Exemple #1
0
/**
 * Function to apply regexp-based edit to a header in a list.
 *
 * @param[in] list   the list to add to
 * @param[in] entry  the name of the entry to edit
 * @param[in] val    string value of the regexp, if rx is null
 * @param[in] tx     The ironbee tx rec
 * @param[in] rx     The compiled regex operation, or NULL to use val
 */
static ib_status_t list_edit(ngx_list_t *list, const char *entry,
                             const char *val, ib_tx_t *tx, ib_rx_t *rx)
{
    ngx_list_part_t *part;
    ngx_table_elt_t *elt;
    unsigned int i;
    char *repl;
    char *oldval;
    ngxib_req_ctx *ctx = tx->sctx;

    ngx_log_t *prev_log = ngxib_log(ctx->r->connection->log);
    ngx_regex_malloc_init(ctx->r->pool);

    /* Check we were passed something valid */
    if (rx == NULL) {
        if (rx = ib_rx_compile(tx->mp, val), rx == NULL) {
            ngx_log_error(NGX_LOG_ERR, ctx->r->connection->log, 0,
                          "Failed to compile %s as regexp", val);
            cleanup_return(prev_log) IB_EINVAL;
        }
    }
    for (part = &list->part; part; part = part->next) {
        elt = part->elts;
        for (i = 0; i < part->nelts; ++i) {
            if (elt[i].key.len == strlen(entry)
                && !strncasecmp((const char*)elt[i].key.data, entry, elt[i].key.len)) {
                /* we need a null-terminated string for ib_rx_exec */
                oldval = strndup((const char*)elt[i].value.data, elt[i].value.len);
                ib_rx_exec(tx->mp, rx, oldval, &repl, NULL);
                free(oldval);
                if (repl != NULL) {
                    elt[i].value.data = (unsigned char*)repl;
                    elt[i].value.len = strlen(repl);
                }
            }
        }
        if (part == list->last)
            break;
    }
    cleanup_return(prev_log) IB_OK;
}
Exemple #2
0
static ngx_int_t
ngx_regex_module_init(ngx_cycle_t *cycle)
{
    int               opt;
    const char       *errstr;
    ngx_uint_t        i;
    ngx_list_part_t  *part;
    ngx_regex_elt_t  *elts;

    opt = 0;

#if (NGX_HAVE_PCRE_JIT)
    {
    ngx_regex_conf_t    *rcf;
    ngx_pool_cleanup_t  *cln;

    rcf = (ngx_regex_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_regex_module);

    if (rcf->pcre_jit) {
        opt = PCRE_STUDY_JIT_COMPILE;

        /*
         * The PCRE JIT compiler uses mmap for its executable codes, so we
         * have to explicitly call the pcre_free_study() function to free
         * this memory.
         */

        cln = ngx_pool_cleanup_add(cycle->pool, 0);
        if (cln == NULL) {
            return NGX_ERROR;
        }

        cln->handler = ngx_pcre_free_studies;
        cln->data = ngx_pcre_studies;
    }
    }
#endif

    ngx_regex_malloc_init(cycle->pool);

    part = &ngx_pcre_studies->part;
    elts = part->elts;

    for (i = 0 ; /* void */ ; i++) {

        if (i >= part->nelts) {
            if (part->next == NULL) {
                break;
            }

            part = part->next;
            elts = part->elts;
            i = 0;
        }

        elts[i].regex->extra = pcre_study(elts[i].regex->code, opt, &errstr);

        if (errstr != NULL) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
                          "pcre_study() failed: %s in \"%s\"",
                          errstr, elts[i].name);
        }

#if (NGX_HAVE_PCRE_JIT)
        if (opt & PCRE_STUDY_JIT_COMPILE) {
            int jit, n;

            jit = 0;
            n = pcre_fullinfo(elts[i].regex->code, elts[i].regex->extra,
                              PCRE_INFO_JIT, &jit);

            if (n != 0 || jit != 1) {
                ngx_log_error(NGX_LOG_INFO, cycle->log, 0,
                              "JIT compiler does not support pattern: \"%s\"",
                              elts[i].name);
            }
        }
#endif
    }

    ngx_regex_malloc_done();

    ngx_pcre_studies = NULL;

    return NGX_OK;
}
Exemple #3
0
ngx_int_t
ngx_regex_compile(ngx_regex_compile_t *rc)
{
    int               n, erroff;
    char             *p;
    pcre             *re;
    const char       *errstr;
    ngx_regex_elt_t  *elt;

    ngx_regex_malloc_init(rc->pool);

    re = pcre_compile((const char *) rc->pattern.data, (int) rc->options,
                      &errstr, &erroff, NULL);

    /* ensure that there is no current pool */
    ngx_regex_malloc_done();

    if (re == NULL) {
        if ((size_t) erroff == rc->pattern.len) {
           rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
                              "pcre_compile() failed: %s in \"%V\"",
                               errstr, &rc->pattern)
                      - rc->err.data;

        } else {
           rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
                              "pcre_compile() failed: %s in \"%V\" at \"%s\"",
                               errstr, &rc->pattern, rc->pattern.data + erroff)
                      - rc->err.data;
        }

        return NGX_ERROR;
    }

    rc->regex = ngx_pcalloc(rc->pool, sizeof(ngx_regex_t));
    if (rc->regex == NULL) {
        goto nomem;
    }

    rc->regex->code = re;

    /* do not study at runtime */

    if (ngx_pcre_studies != NULL) {
        elt = ngx_list_push(ngx_pcre_studies);
        if (elt == NULL) {
            goto nomem;
        }

        elt->regex = rc->regex;
        elt->name = rc->pattern.data;
    }

    n = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &rc->captures);
    if (n < 0) {
        p = "pcre_fullinfo(\"%V\", PCRE_INFO_CAPTURECOUNT) failed: %d";
        goto failed;
    }

    if (rc->captures == 0) {
        return NGX_OK;
    }

    n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMECOUNT, &rc->named_captures);
    if (n < 0) {
        p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMECOUNT) failed: %d";
        goto failed;
    }

    if (rc->named_captures == 0) {
        return NGX_OK;
    }

    n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMEENTRYSIZE, &rc->name_size);
    if (n < 0) {
        p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMEENTRYSIZE) failed: %d";
        goto failed;
    }

    n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMETABLE, &rc->names);
    if (n < 0) {
        p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMETABLE) failed: %d";
        goto failed;
    }

    return NGX_OK;

failed:

    rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, p, &rc->pattern, n)
                  - rc->err.data;
    return NGX_ERROR;

nomem:

    rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
                               "regex \"%V\" compilation failed: no memory",
                               &rc->pattern)
                  - rc->err.data;
    return NGX_ERROR;
}
Exemple #4
0
ngx_int_t
ngx_regex_compile(ngx_regex_compile_t *rc)
{
    int           n, erroff;
    char         *p;
    const char   *errstr;
    ngx_regex_t  *re;

    ngx_regex_malloc_init(rc->pool);

    re = pcre_compile((const char *) rc->pattern.data, (int) rc->options,
                      &errstr, &erroff, NULL);

    /* ensure that there is no current pool */
    ngx_regex_malloc_done();

    if (re == NULL) {
        if ((size_t) erroff == rc->pattern.len) {
           rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
                              "pcre_compile() failed: %s in \"%V\"",
                               errstr, &rc->pattern)
                      - rc->err.data;

        } else {
           rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
                              "pcre_compile() failed: %s in \"%V\" at \"%s\"",
                               errstr, &rc->pattern, rc->pattern.data + erroff)
                      - rc->err.data;
        }

        return NGX_ERROR;
    }

    rc->regex = re;

    n = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &rc->captures);
    if (n < 0) {
        p = "pcre_fullinfo(\"%V\", PCRE_INFO_CAPTURECOUNT) failed: %d";
        goto failed;
    }

    if (rc->captures == 0) {
        return NGX_OK;
    }

    n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMECOUNT, &rc->named_captures);
    if (n < 0) {
        p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMECOUNT) failed: %d";
        goto failed;
    }

    if (rc->named_captures == 0) {
        return NGX_OK;
    }

    n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMEENTRYSIZE, &rc->name_size);
    if (n < 0) {
        p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMEENTRYSIZE) failed: %d";
        goto failed;
    }

    n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMETABLE, &rc->names);
    if (n < 0) {
        p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMETABLE) failed: %d";
        goto failed;
    }

    return NGX_OK;

failed:

    rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, p, &rc->pattern, n)
                  - rc->err.data;
    return NGX_OK;
}
Exemple #5
0
/**
 * nginx handler to feed request body (if any) to IronBee
 *
 * @param[in] r   the nginx request object
 * @return    NGX_DECLINED for normal operation
 * @return    NGX_DONE if body is not yet available (processing will resume
 *            on new data)
 * @return    Error status if set by IronBee on sight of request data.
 */
ngx_int_t ngxib_handler(ngx_http_request_t *r)
{
    ngx_chain_t *link;
    ngxib_req_ctx *ctx;
    ngx_int_t rv = NGX_DECLINED;
    ngx_http_request_body_t *rb;
    /* Don't process internal requests */
    if (r->internal)
        return rv;

    ctx = ngx_http_get_module_ctx(r, ngx_ironbee_module);
    if (ctx->body_done)
        return rv;

    /* We already completed handling of no-body requests
     * when we looked at headers
     */
    if (!ngxib_has_request_body(r, ctx))
        return rv;

    ngx_regex_malloc_init(r->pool);

    /* We can now read the body.
     * This may come asynchronously in many chunks, so we need
     * to check for AGAIN and return DONE if waiting.
     * We pass it a handler to go round again while waiting.
     *
     * TODO: figure out how to pass data to ironbee asynchronously
     */
    rv = ngx_http_read_client_request_body(r, ngxib_post_handler);
    if (rv == NGX_AGAIN) {
        ctx->body_wait = 1;
        cleanup_return NGX_DONE;
    }

    /* We now have the request body.  Feed it to ironbee */
    rb = r->request_body;
    if (!rb) {
        ib_log_error_tx(ctx->tx, "Failed to read request body.");
        cleanup_return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    if (!rb->bufs) {
        /* I think this shouldn't happen */
        /* But rethink if this turns up in logs when all is fine */
        ib_log_error_tx(ctx->tx, "Probable error reading request body.");
    }

    if (rb->temp_file && (rb->temp_file->file.fd != NGX_INVALID_FILE)) {
        /* Reader has put request body in temp file */
        off_t count = 0;
        u_char buf[BUFSIZE];
        size_t buf_len;
        ib_log_debug_tx(ctx->tx, "Reading request body in temp file.");
        while (buf_len = ngx_read_file(&rb->temp_file->file,
                         buf, BUFSIZE, count),
               buf_len > 0) {
            ib_log_debug_tx(ctx->tx, "Feeding %zd bytes request data to ironbee.",
                            buf_len);
            ib_state_notify_request_body_data(ctx->tx->ib, ctx->tx,
                                              (const char*)buf, buf_len);
            count += buf_len;
        }
        if ((int)buf_len == NGX_ERROR) {
            ib_log_error_tx(ctx->tx, "Failed to read request body in temp file.");
        }
    }

    for (link = rb->bufs; link != NULL; link = link->next) {
        size_t len = (link->buf->last - link->buf->pos);
        ib_log_debug_tx(ctx->tx, "Feeding %zd bytes request data to ironbee.",
                        len);
        if (len > 0) {
            ib_state_notify_request_body_data(ctx->tx->ib, ctx->tx,
                                              (const char*)link->buf->pos, len);
        }
    }
    ctx->body_done = 1;
    ib_state_notify_request_finished(ctx->tx->ib, ctx->tx);

    /* If IronBee signaled an error, we can return it */
    if (STATUS_IS_ERROR(ctx->status)) {
        rv = ctx->status;
        ctx->internal_errordoc = 1;
        ib_log_error_tx(ctx->tx, "IronBee set %d reading request body.", (int)rv);
    }

    cleanup_return rv;
}
//PCRE使用参考http://blog.chinaunix.net/uid-26575352-id-3517146.html    http://blog.csdn.net/kofiory/article/details/5829697
ngx_int_t
ngx_regex_compile(ngx_regex_compile_t *rc)
{
    int               n, erroff;
    char             *p;
    pcre             *re;
    const char       *errstr;
    ngx_regex_elt_t  *elt;
    
    ngx_regex_malloc_init(rc->pool);

    //正则表达式在使用之前要经过编译。编译的目的是将正则表达式的pattern转换成PCRE引擎能够识别的结构(struct real_pcre)。
    re = pcre_compile((const char *) rc->pattern.data, (int) rc->options,
                      &errstr, &erroff, NULL);

    /* ensure that there is no current pool */
    ngx_regex_malloc_done();

    if (re == NULL) {
        if ((size_t) erroff == rc->pattern.len) {
           rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
                              "pcre_compile() failed: %s in \"%V\"",
                               errstr, &rc->pattern)
                      - rc->err.data;

        } else {
           rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
                              "pcre_compile() failed: %s in \"%V\" at \"%s\"",
                               errstr, &rc->pattern, rc->pattern.data + erroff)
                      - rc->err.data;
        }

        return NGX_ERROR;
    }

    rc->regex = ngx_pcalloc(rc->pool, sizeof(ngx_regex_t));
    if (rc->regex == NULL) {
        goto nomem;
    }

    rc->regex->code = re;

    /* do not study at runtime */

    if (ngx_pcre_studies != NULL) {
        elt = ngx_list_push(ngx_pcre_studies);
        if (elt == NULL) {
            goto nomem;
        }

        elt->regex = rc->regex;
        elt->name = rc->pattern.data;
    }

    //PCRE_INFO_CAPTURECOUNT: 得到的是所有子模式的个数,包含命名子模式和非命名子模式
    n = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &rc->captures);
    if (n < 0) {
        p = "pcre_fullinfo(\"%V\", PCRE_INFO_CAPTURECOUNT) failed: %d";
        goto failed;
    }

    if (rc->captures == 0) {
        return NGX_OK;
    }
    
    /* See if there are any named substrings, and if so, show them by name. First
    we have to extract the count of named parentheses from the pattern. */   
    n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMECOUNT, &rc->named_captures);
    if (n < 0) {
        p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMECOUNT) failed: %d";
        goto failed;
    }

    if (rc->named_captures == 0) {
        return NGX_OK;
    }

    /* Before we can access the substrings, we must extract the table for
    translating names to numbers, and the size of each entry in the table. */


    n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMEENTRYSIZE, &rc->name_size);   /* where to put the answer */
    
    if (n < 0) {
        p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMEENTRYSIZE) failed: %d";
        goto failed;
    }


    n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMETABLE, &rc->names);  /* where to put the answer */
    
    if (n < 0) {
        p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMETABLE) failed: %d";
        goto failed;
    }

    return NGX_OK;

failed:

    rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, p, &rc->pattern, n)
                  - rc->err.data;
    return NGX_ERROR;

nomem:

    rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
                               "regex \"%V\" compilation failed: no memory",
                               &rc->pattern)
                  - rc->err.data;
    return NGX_ERROR;
}
static ngx_int_t
ngx_regex_module_init(ngx_cycle_t *cycle)
{
    int               opt;
    const char       *errstr;
    ngx_uint_t        i;
    ngx_list_part_t  *part;
    ngx_regex_elt_t  *elts;

    opt = 0;

#if (NGX_HAVE_PCRE_JIT)
    {
    ngx_regex_conf_t    *rcf;
    ngx_pool_cleanup_t  *cln;

    rcf = (ngx_regex_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_regex_module);

    if (rcf->pcre_jit) {
        opt = PCRE_STUDY_JIT_COMPILE;

        /*
         * The PCRE JIT compiler uses mmap for its executable codes, so we
         * have to explicitly call the pcre_free_study() function to free
         * this memory.
         */

        cln = ngx_pool_cleanup_add(cycle->pool, 0);
        if (cln == NULL) {
            return NGX_ERROR;
        }

        cln->handler = ngx_pcre_free_studies;
        cln->data = ngx_pcre_studies;
    }
    }
#endif

    ngx_regex_malloc_init(cycle->pool);

    part = &ngx_pcre_studies->part;
    elts = part->elts;

    for (i = 0 ; /* void */ ; i++) {

        if (i >= part->nelts) {
            if (part->next == NULL) {
                break;
            }

            part = part->next;
            elts = part->elts;
            i = 0;
        }

        /* 
          对编译后的正则表达式结构(struct real_pcre)进行分析和学习,学习的结果是一个数据结构(struct pcre_extra),这个数据结构连同编译
          后的规则(struct real_pcre)可以一起送给pcre_exec单元进行匹配.

          pcre_study()的引入主要是为了加速正则表达式匹配的速度.(为什么学习后就能加速呢?)这个还是比较有用的,可以将正则表达式编译,
          学习后保存到一个文件或内存中,这样进行匹配的时候效率比较搞.snort中就是这样做的.
          */
        elts[i].regex->extra = pcre_study(elts[i].regex->code, opt, &errstr);

        if (errstr != NULL) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
                          "pcre_study() failed: %s in \"%s\"",
                          errstr, elts[i].name);
        }

#if (NGX_HAVE_PCRE_JIT)
        if (opt & PCRE_STUDY_JIT_COMPILE) {
            int jit, n;

            jit = 0;
            n = pcre_fullinfo(elts[i].regex->code, elts[i].regex->extra,
                              PCRE_INFO_JIT, &jit);

            if (n != 0 || jit != 1) {
                ngx_log_error(NGX_LOG_INFO, cycle->log, 0,
                              "JIT compiler does not support pattern: \"%s\"",
                              elts[i].name);
            }
        }
#endif
    }

    ngx_regex_malloc_done();

    ngx_pcre_studies = NULL;

    return NGX_OK;
}
Exemple #8
0
ib_conn_t *ngxib_conn_get(ngxib_req_ctx *rctx)
{
    ngx_pool_cleanup_t *cln;
    ib_status_t rc;
    ib_engine_t *ib;

    /* Suggested by Maxim Dounin on dev list:
     * Look through pool cleanups for our conn
     * No race condition because no threads!
     */
    for (cln = rctx->r->connection->pool->cleanup;
         cln != NULL; cln = cln->next) {
        if (cln->handler == conn_end) {
            /* Our connection is already initialized and it's here */
            rctx->conn = cln->data;
            return rctx->conn->iconn;
        }
    }

    /* This connection is new, so initialize our conn struct
     * and notify IronBee.
     *
     * No threads, so no race condition here
     */

    /* Acquire an IronBee engine */
    /* n.b. pool cleanup for this and the ib_conn is conn_end, added below */
    rc = ngxib_acquire_engine(&ib, rctx->r->connection->log);
    if (rc != IB_OK) {
        cleanup_return NULL;
    }

    ngx_regex_malloc_init(rctx->r->connection->pool);

    rctx->conn = ngx_palloc(rctx->r->connection->pool, sizeof(ngxib_conn_t));
    if (rctx->conn == NULL) {
        ngxib_release_engine(ib, rctx->r->connection->log);
        cleanup_return NULL;
    }
    rctx->conn->engine = ib;
    rctx->conn->log = rctx->r->connection->log;

    rc = ib_conn_create(rctx->conn->engine, &rctx->conn->iconn, rctx->r->connection);
    if (rc != IB_OK) {
        ngxib_release_engine(ib, rctx->r->connection->log);
        cleanup_return NULL;
    }

    /* Initialize the connection */
    rc = conn_init(rctx->conn->iconn);
    if (rc != IB_OK) {
        ngxib_release_engine(ib, rctx->r->connection->log);
        cleanup_return NULL;
    }

    ib_state_notify_conn_opened(rctx->conn->engine, rctx->conn->iconn);

    cln = ngx_pool_cleanup_add(rctx->r->connection->pool, 0);
    if (cln != NULL) {
        cln->handler = conn_end;
        cln->data = rctx->conn;
    }

    cleanup_return rctx->conn->iconn;
}
Exemple #9
0
/**
 * Ironbee initialisation function.  Sets up engine and logging,
 * and reads Ironbee config.
 *
 * @param[in]  cf     Configuration rec
 * @return     NGX_OK or error
 */
static ngx_int_t ironbee_init(ngx_conf_t *cf)
{
    ngx_log_t *prev_log;
    ib_context_t *ctx;
    ib_cfgparser_t *cp;
    ironbee_proc_t *proc;
    ib_status_t rc, rc1;

    prev_log = ngxib_log(cf->log);
    ngx_regex_malloc_init(cf->pool);

    ngx_log_error(NGX_LOG_NOTICE, cf->log, 0, "ironbee_init %d", getpid());

    proc = ngx_http_conf_get_module_main_conf(cf, ngx_ironbee_module);
    if (proc->loglevel == NGX_CONF_UNSET_UINT)
        proc->loglevel = 4; /* default */

    rc = ib_initialize();
    if (rc != IB_OK)
        cleanup_return(prev_log) IB2NG(rc);

    ib_util_log_level(proc->loglevel);

    rc = ib_engine_create(&ironbee, ngxib_server());
    if (rc != IB_OK)
        cleanup_return(prev_log) IB2NG(rc);

    if (proc->use_ngxib_logger)
        ib_log_set_logger_fn(ironbee, ngxib_logger, NULL);
    /* Using default log level function. */

    rc = ib_engine_init(ironbee);
    if (rc != IB_OK)
        cleanup_return(prev_log) IB2NG(rc);

    /* TODO: TS creates logfile at this point */

    ib_hook_conn_register(ironbee, conn_opened_event, ngxib_conn_init, NULL);

    rc = ib_cfgparser_create(&cp, ironbee);
    assert((cp != NULL) || (rc != IB_OK));
    if (rc != IB_OK)
        cleanup_return(prev_log) IB2NG(rc);

    rc = ib_engine_config_started(ironbee, cp);
    if (rc != IB_OK)
        cleanup_return(prev_log) IB2NG(rc);

    /* Get the main context, set some defaults */
    ctx = ib_context_main(ironbee);
    ib_context_set_num(ctx, "logger.log_level", proc->loglevel);

    /* FIXME - use the temp pool operation for this */
    char *buf = strndup((char*)proc->config_file.data, proc->config_file.len);
    rc = ib_cfgparser_parse(cp, buf);
    free(buf);
    rc1 = ib_engine_config_finished(ironbee);
    ib_cfgparser_destroy(cp);

    cleanup_return(prev_log) rc == IB_OK ? rc1 == IB_OK ? NGX_OK : IB2NG(rc1) : IB2NG(rc);
}
Exemple #10
0
/**
 * nginx post-read-request handler to feed request line and headers to Ironbee.
 *
 * @param[in]  r     The nginx request object.
 * @return     Declined (ignoreme) or error status.
 */
static ngx_int_t ironbee_post_read_request(ngx_http_request_t *r)
{
    ngx_log_t *prev_log;
    ngxib_req_ctx *ctx;
    ib_conn_t *iconn;
    ib_parsed_req_line_t *rline;
    ib_parsed_header_wrapper_t *ibhdrs;
    ib_status_t rc;

    ngx_list_part_t *part;
    ngx_table_elt_t *hdr;
    unsigned int i;

    /* Don't process internal requests */
    if (r->internal)
        return NGX_DECLINED;

    prev_log = ngxib_log(r->connection->log);
    ngx_regex_malloc_init(r->pool);

    ctx = ngx_pcalloc(r->pool, sizeof(ngxib_req_ctx));
    ctx->r = r;
    ngx_http_set_ctx(r, ctx, ngx_ironbee_module);

    iconn = ngxib_conn_get(ctx, ironbee);

    ib_tx_create(&ctx->tx, iconn, ctx);

    /* Notify Ironbee of request line and headers */
    rc = ib_parsed_req_line_create(ctx->tx, &rline,
                                   (const char*)r->request_line.data,
                                   r->request_line.len,
                                   (const char*)r->method_name.data,
                                   r->method_name.len,
                                   (const char*)r->unparsed_uri.data,
                                   r->unparsed_uri.len,
                                   (const char*)r->http_protocol.data,
                                   r->http_protocol.len);
    if (rc != IB_OK)
        cleanup_return(prev_log) NGX_ERROR;

    ib_state_notify_request_started(ironbee, ctx->tx, rline);

    rc = ib_parsed_name_value_pair_list_wrapper_create(&ibhdrs, ctx->tx);
    if (rc != IB_OK)
        cleanup_return(prev_log) NGX_ERROR;

    for (part = &r->headers_in.headers.part; part != NULL; part = part->next) {
        hdr = part->elts;
        for (i = 0; i < part->nelts; ++i) {
            ib_parsed_name_value_pair_list_add(ibhdrs,
                                               (const char*)hdr->key.data,
                                               hdr->key.len,
                                               (const char*)hdr->value.data,
                                               hdr->value.len);
            ++hdr;
        }
    }

    rc = ib_state_notify_request_header_data(ironbee, ctx->tx, ibhdrs);
    if (rc != IB_OK)
        cleanup_return(prev_log) NGX_ERROR;

    rc = ib_state_notify_request_header_finished(ironbee, ctx->tx);
    if (rc != IB_OK)
        cleanup_return(prev_log) NGX_ERROR;

    if (!ngxib_has_request_body(r, ctx)) {
        rc = ib_state_notify_request_finished(ironbee, ctx->tx);
        if (rc != IB_OK)
            cleanup_return(prev_log) NGX_ERROR;
    }
    ctx->hdrs_in = 1;
    if (STATUS_IS_ERROR(ctx->status)) {
        ctx->internal_errordoc = 1;
        cleanup_return(prev_log) ctx->status;
    }

    cleanup_return(prev_log) NGX_DECLINED;
}
Exemple #11
0
/**
 * A header filter to intercept response line and headers and feed to Ironbee.
 *
 * @param[in]  r     The nginx request object.
 * @return     status propagated from next filter, or Error
 */
static ngx_int_t ironbee_headers_out(ngx_http_request_t *r)
{
    ngx_log_t *prev_log;
    ngxib_req_ctx *ctx;
    ib_parsed_resp_line_t *rline;
    ib_parsed_header_wrapper_t *ibhdrs;
    ib_status_t rc;

    ngx_list_part_t *part;
    ngx_table_elt_t *hdr;
    unsigned int i;
    char proto[12];
    char *status;
    const char *reason;
    int status_len, reason_len;

    /* FIXME: needs more logic here to catch error pages */
    if (r->internal)
        return ngx_http_next_header_filter(r);

    ctx = ngx_http_get_module_ctx(r, ngx_ironbee_module);
    assert((ctx != NULL) && (ctx->tx != NULL));

    prev_log = ngxib_log(r->connection->log);
    ngx_regex_malloc_init(r->pool);

    /* Notify Ironbee of request line and headers */
    sprintf(proto, "HTTP/%d.%d", r->http_major, r->http_minor);
    if (r->headers_out.status_line.len) {
        status = (char*)r->headers_out.status_line.data;
        status_len = strcspn(status, " \t");
        for (reason = status+status_len; isspace(*reason); ++reason);
        reason_len = r->headers_out.status_line.len - (reason-status);
    }
    else if (r->headers_out.status >= 100 && r->headers_out.status < 600) {
        status = ngx_palloc(r->pool, 4);
        /* cast to int, because ngx_int_t requires different format args
         * on different platforms.  We're already limited to 3-digit numbers.
         */
        sprintf(status, "%d", (int)r->headers_out.status);
        status_len = 3;
        reason = "";
        reason_len = 0;
    }
    else {
        ib_log_error_tx(ctx->tx, "Ironbee: bogus response status %d",
                        (int)r->headers_out.status);
        cleanup_return(prev_log) NGX_ERROR;
    }
    rc = ib_parsed_resp_line_create(ctx->tx, &rline, NULL, 0,
                                    proto, strlen(proto),
                                    status, status_len,
                                    reason, reason_len);
    if (rc != IB_OK)
        cleanup_return(prev_log) NGX_ERROR;

    ib_state_notify_response_started(ironbee, ctx->tx, rline);

    rc = ib_parsed_name_value_pair_list_wrapper_create(&ibhdrs, ctx->tx);
    if (rc != IB_OK)
        cleanup_return(prev_log) NGX_ERROR;

    for (part = &r->headers_out.headers.part; part != NULL; part = part->next) {
        hdr = part->elts;
        for (i = 0; i < part->nelts; ++i) {
            ib_parsed_name_value_pair_list_add(ibhdrs,
                                               (const char*)hdr->key.data,
                                               hdr->key.len,
                                               (const char*)hdr->value.data,
                                               hdr->value.len);
            ++hdr;
        }
    }

    /* Ironbee currently crashes if called here with no headers,
     * even perfectly correctly on a 204/304 response.
     */
    if (ibhdrs->size > 0) {
        rc = ib_state_notify_response_header_data(ironbee, ctx->tx, ibhdrs);
        if (rc != IB_OK)
            cleanup_return(prev_log) NGX_ERROR;
    }

    rc = ib_state_notify_response_header_finished(ironbee, ctx->tx);
    if (rc != IB_OK)
        cleanup_return(prev_log) NGX_ERROR;

    ctx->hdrs_out = 1;

    cleanup_return(prev_log) ngx_http_next_header_filter(r);
}
Exemple #12
0
/**
 * A body filter to intercept response body and feed it to Ironbee,
 * and to buffer the data if required by Ironbee configuration.
 *
 * @param[in]  r     The nginx request object.
 * @param[in]  in    The data to filter.
 * @return     status propagated from next filter, or OK/Error
 */
static ngx_int_t ironbee_body_out(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_log_t *prev_log;
    ngxib_req_ctx *ctx;
    ngx_chain_t *link;
    ib_status_t rc;
    ib_num_t num;
    ngx_int_t rv = NGX_OK;
    ib_txdata_t itxdata;

    if (r->internal)
        return ngx_http_next_body_filter(r, in);

    prev_log = ngxib_log(r->connection->log);

    ctx = ngx_http_get_module_ctx(r, ngx_ironbee_module);
    assert((ctx != NULL) && (ctx->tx != NULL));
    ib_log_debug_tx(ctx->tx, "ironbee_body_out");
    if (in == NULL) {
        /* FIXME: could this happen in circumstances when we should
         * notify Ironbee of end-of-response ?
         */
        ib_log_debug_tx(ctx->tx, "ironbee_body_out: input was null");
        cleanup_return(prev_log) ngx_http_next_body_filter(r, in);
    }
    ctx = ngx_http_get_module_ctx(r, ngx_ironbee_module);
    if (ctx->output_filter_done) {
        ib_log_debug_tx(ctx->tx, "ironbee_body_out: already done");
        cleanup_return(prev_log) ngx_http_next_body_filter(r, in);
    }
    if (!ctx->output_filter_init) {
        ctx->output_filter_init = 1;

        if (ctx->internal_errordoc) {
            /* If it's our own errordoc, pass it straight through */
            /* Should we log anything here?  The error will already
             * have been logged.
             */
            ctx->output_buffering = IOBUF_NOBUF;
            ctx->response_buf = NULL;
            ib_log_debug_tx(ctx->tx, "ironbee_body_out: in internal errordoc");
        }
        else {
            /* Determine whether we're configured to buffer */
            rc = ib_context_get(ctx->tx->ctx, "buffer_res",
                                ib_ftype_num_out(&num), NULL);
            ib_log_debug_tx(ctx->tx, "ironbee_body_out: buffer_res is %d", (int)num);
            if (rc != IB_OK)
                ib_log_error_tx(ctx->tx,
                                "Can't determine output buffer configuration!");
            if (num == 0) {
                ib_log_debug_tx(ctx->tx, "ironbee_body_out: NOBUF");
                ctx->output_buffering = IOBUF_NOBUF;
                ctx->response_buf = NULL;
            }
            else {
                /* If we're buffering, initialise the buffer */
                ib_log_debug_tx(ctx->tx, "ironbee_body_out: BUFFER");
                ctx->output_buffering = IOBUF_BUFFER;
            }
        }
    }

    ngx_regex_malloc_init(r->pool);

    for (link = in; link != NULL; link = link->next) {
        /* Feed the data to ironbee */
        itxdata.data = link->buf->pos;
        itxdata.dlen = link->buf->last - link->buf->pos;
        ib_log_debug_tx(ctx->tx, "ironbee_body_out: %d bytes",
                        (int)itxdata.dlen);
        if (itxdata.dlen > 0) {
            ib_state_notify_response_body_data(ironbee, ctx->tx, &itxdata);
        }

        /* If Ironbee just signalled an error, switch to discard data mode,
         * and dump anything we already have buffered,
         */
        if ( (STATUS_IS_ERROR(ctx->status)) &&
             !ctx->internal_errordoc &&
             (ctx->output_buffering != IOBUF_DISCARD) ) {
            ib_log_debug_tx(ctx->tx, "ironbee_body_out: error %d", ctx->status);
            free_chain(r->pool, ctx->response_buf);
            ctx->response_buf = NULL;
            ctx->output_buffering = IOBUF_DISCARD;
        }
        else if (ctx->output_buffering == IOBUF_BUFFER) {
            /* Copy any data to our buffer */
            if (ctx->response_buf == NULL) {
                ctx->response_buf = ngx_pcalloc(r->pool, sizeof(ngx_chain_t));
                ctx->response_ptr = ctx->response_buf;
            }
            else {
                ctx->response_ptr->next = ngx_pcalloc(r->pool, sizeof(ngx_chain_t));
                ctx->response_ptr = ctx->response_ptr->next;
            }
            /* Not sure if any data types need setaside, but let's be safe */
#if NO_COPY_REQUIRED
            ctx->response_ptr->buf = link->buf;
#else
            if (itxdata.dlen > 0) {
                ctx->response_ptr->buf = ngx_create_temp_buf(r->pool, itxdata.dlen);
                memcpy(ctx->response_ptr->buf->pos, link->buf->pos, itxdata.dlen);
                ctx->response_ptr->buf->last += itxdata.dlen;
            }
            else {
                ctx->response_ptr->buf = ngx_palloc(r->pool, sizeof(ngx_buf_t));
                memcpy(ctx->response_ptr->buf, link->buf, sizeof(ngx_buf_t));
            }
#endif
        }

        if (link->buf->last_buf) {
            ib_log_debug_tx(ctx->tx, "ironbee_body_out: last_buf");
            ctx->output_filter_done = 1;
        }
    }

    if (ctx->output_buffering == IOBUF_NOBUF) {
        /* Normal operation - pass it down the chain */
        ib_log_debug_tx(ctx->tx, "ironbee_body_out: passing on");
        ctx->start_response = 1;
        rv = ngx_http_next_body_filter(r, in);
    }
    else if (ctx->output_buffering == IOBUF_BUFFER) {
        ib_log_debug_tx(ctx->tx, "ironbee_body_out: buffering");
        if (ctx->output_filter_done) {
            /* We can pass on the buffered data all at once */
            ib_log_debug_tx(ctx->tx, "ironbee_body_out: passing buffer");
            ctx->start_response = 1;
            rv = ngx_http_next_body_filter(r, ctx->response_buf);
        }
    }
    else if (ctx->output_buffering == IOBUF_DISCARD) {
        ib_log_debug_tx(ctx->tx, "ironbee_body_out: discarding");
        if (ctx->output_filter_done) {
            /* Pass a last bucket with no data */
            //ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,
            //              "ironbee_body_out: passing NULL last-buffer");
            //ctx->response_buf = ngx_pcalloc(r->pool, sizeof(ngx_chain_t));
            //ctx->response_buf->buf = ngx_calloc_buf(r->pool);
            //ctx->response_buf->buf->last_buf = ctx->response_buf->buf->last_in_chain = 1;
            //rv = ngx_http_next_body_filter(r, ctx->response_buf);
            /* FIXME: Is setting rv enough to serve error page */
            rv = ctx->status;
        }
    }
    if (ctx->output_filter_done) {
        ib_log_debug_tx(ctx->tx, "ironbee_body_out: notify_postprocess");
        rc = ib_state_notify_postprocess(ironbee, ctx->tx);
        if ((rv == NGX_OK) && (rc != IB_OK)) {
            rv = NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
        rc = ib_state_notify_logging(ironbee, ctx->tx);
        if ((rv == NGX_OK) && (rc != IB_OK)) {
            rv = NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    }
    cleanup_return(prev_log) rv;
}
Exemple #13
0
/**
 * IronBee initialization function.  Sets up engine and logging,
 * and reads IronBee config.
 *
 * @param[in]  cf     Configuration rec
 * @return     NGX_OK or error
 */
static ngx_int_t ironbee_init(ngx_conf_t *cf)
{
    ironbee_proc_t *proc;
    ib_status_t rc;
    module_data_t *mod_data = &module_data;
    char *buf;

    /* We still use the global-log hack to initialise */
    ngx_regex_malloc_init(cf->pool);

    ngx_log_error(NGX_LOG_NOTICE, cf->log, 0, "ironbee_init %d", getpid());

    proc = ngx_http_conf_get_module_main_conf(cf, ngx_ironbee_module);
    if (proc->log_level == NGX_CONF_UNSET_UINT) {
        proc->log_level = IB_LOG_NOTICE;
    }
    if (proc->max_engines == NGX_CONF_UNSET_UINT) {
        proc->max_engines = IB_MANAGER_DEFAULT_MAX_ENGINES;
    }
    if (proc->use_ngxib_logger == NGX_CONF_UNSET) {
        proc->use_ngxib_logger =1;
    }

    /* initialise fields in mod_data */
    mod_data->ib_log_active = proc->use_ngxib_logger;
    mod_data->log = cf->log;
    mod_data->log_level = proc->log_level;

    rc = ib_initialize();
    if (rc != IB_OK) {
        cleanup_return IB2NG(rc);
    }

    /* Create the IronBee engine manager */
    rc = ib_manager_create(&(mod_data->manager),  /* Engine Manager */
                           ngxib_server(),        /* Server object */
                           proc->max_engines);    /* Max engines */
    if (rc != IB_OK) {
        cleanup_return IB2NG(rc);
    }

    rc = ib_manager_register_module_fn(
        mod_data->manager,
        ngxib_module,
        mod_data);
    if (rc != IB_OK) {
        cleanup_return IB2NG(rc);
    }

    /* Null manager here would be a bug, as per RNS-CR-143 comments */
    assert(mod_data->manager != NULL);

    /* FIXME - use the temp pool operation for this */
    buf = strndup((char*)proc->config_file.data, proc->config_file.len);

    /* Create the initial engine */
    rc = ib_manager_engine_create(mod_data->manager, buf);
    if (rc != IB_OK) {
        free(buf);
        cleanup_return IB2NG(rc);
    }
    free(buf);

    cleanup_return rc == IB_OK ? NGX_OK : IB2NG(rc);
}
Exemple #14
0
/**
 * nginx handler to feed request body (if any) to Ironbee
 *
 * @param[in] r   the nginx request object
 * @return    NGX_DECLINED for normal operation
 * @return    NGX_DONE if body is not yet available (processing will restart
 *            on new data)
 * @return    Error status if set by Ironbee on sight of request data.
 */
ngx_int_t ngxib_handler(ngx_http_request_t *r)
{
    ngx_log_t *prev_log;
    ib_txdata_t itxdata;
    ngx_chain_t *link;
    ngxib_req_ctx *ctx;
    ngx_int_t rv = NGX_DECLINED;
    ngx_http_request_body_t *rb;
    /* Don't process internal requests */
    if (r->internal)
        return rv;

    ctx = ngx_http_get_module_ctx(r, ngx_ironbee_module);
    if (ctx->body_done)
        return rv;

    if (!has_request_body(r, ctx))
        return rv;

    prev_log = ngxib_log(r->connection->log);
    ngx_regex_malloc_init(r->pool);

    /* We can now read the body.
     * This may come asynchronously in many chunks, so we need
     * to check for AGAIN and return DONE if waiting.
     * We pass it a handler to go round again while waiting.
     *
     * TODO: figure out how to pass data to ironbee asynchronously
     */
    rv = ngx_http_read_client_request_body(r, ngxib_post_handler);
    if (rv == NGX_AGAIN) {
        ctx->body_wait = 1;
        cleanup_return(prev_log) NGX_DONE;
    }

    /* We now have the request body.  Feed it to ironbee */
    rb = r->request_body;
    if (!rb) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "Error reading request body");
        cleanup_return(prev_log) NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    if (!rb->bufs) {
        /* I think this shouldn't happen */
        /* But rethink if this turns up in logs when all is fine */
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "Probable error reading request body");
    }

    if (rb->temp_file && (rb->temp_file->file.fd != NGX_INVALID_FILE)) {
        /* Reader has put request body in temp file */
        off_t count = 0;
        u_char buf[BUFSIZE];
        ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,
                      "Reading request body in temp file");
        while (itxdata.dlen = ngx_read_file(&rb->temp_file->file,
                                            buf, BUFSIZE, count),
               (int)itxdata.dlen > 0) {
            itxdata.data = buf;
            ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,
                          "Feeding %d bytes request data to ironbee", itxdata.dlen);
            ib_state_notify_request_body_data(ngxib_engine(), ctx->tx, &itxdata);
            count += itxdata.dlen;
        }
        if ((int)itxdata.dlen == NGX_ERROR) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "Error reading request body in temp file");
        }
    }

    for (link = rb->bufs; link != NULL; link = link->next) {
        itxdata.data = link->buf->pos;
        itxdata.dlen = (link->buf->last - link->buf->pos);
        ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,
                      "Feeding %d bytes request data to ironbee", itxdata.dlen);
        if (itxdata.dlen > 0) {
            ib_state_notify_request_body_data(ngxib_engine(), ctx->tx, &itxdata);
        }
    }
    ctx->body_done = 1;
    ib_state_notify_request_finished(ngxib_engine(), ctx->tx);

    /* If Ironbee signalled an error, we can return it */
    if (STATUS_IS_ERROR(ctx->status)) {
        rv = ctx->status;
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "Ironbee set %d reading request body", rv);
    }

    cleanup_return(prev_log) rv;
}