Beispiel #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;
}
Beispiel #2
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);
}
Beispiel #3
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;
}
Beispiel #4
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);
}
Beispiel #5
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;
}
Beispiel #6
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_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;

    /* We already completed handling of no-body requests
     * when we looked at headers
     */
    if (!ngxib_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) {
        ib_log_error_tx(ctx->tx, "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 */
        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];
        ib_log_debug_tx(ctx->tx, "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;
            ib_log_debug_tx(ctx->tx, "Feeding %d bytes request data to ironbee",
                            (int)itxdata.dlen);
            ib_state_notify_request_body_data(ngxib_engine(), ctx->tx, &itxdata);
            count += itxdata.dlen;
        }
        if ((int)itxdata.dlen == NGX_ERROR) {
            ib_log_error_tx(ctx->tx, "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);
        ib_log_debug_tx(ctx->tx, "Feeding %d bytes request data to ironbee",
                        (int)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;
        ctx->internal_errordoc = 1;
        ib_log_error_tx(ctx->tx, "Ironbee set %d reading request body", (int)rv);
    }

    cleanup_return(prev_log) rv;
}