Пример #1
0
 const std::string &ResultStr(ib_flags_t result, std::string &s) const
 {
     int n = 0;
     if (result == IB_STRFLAG_NONE) {
         s = "<None>";
         return s;
     }
     s = "<";
     if (ib_flags_all(result, IB_STRFLAG_MODIFIED)) {
         if (n++ > 0) {
             s += ",";
         }
         s += "MODIFIED";
     }
     if (ib_flags_all(result, IB_STRFLAG_NEWBUF)) {
         if (n++ > 0) {
             s += ",";
         }
         s += "NEWBUF";
     }
     if (ib_flags_all(result, IB_STRFLAG_ALIAS)) {
         if (n++ > 0) {
             s += ",";
         }
         s += "ALIAS";
     }
     s += ">";
     return s;
 }
Пример #2
0
static void tx_finish(ib_tx_t *tx)
{
    if (!ib_flags_all(tx->flags, IB_TX_FREQ_FINISHED) ) {
        ib_state_notify_request_finished(tx->ib, tx);
    }
    if (!ib_flags_all(tx->flags, IB_TX_FRES_FINISHED) ) {
        ib_state_notify_response_finished(tx->ib, tx);
    }
    if (!ib_flags_all(tx->flags, IB_TX_FPOSTPROCESS)) {
        ib_state_notify_postprocess(tx->ib, tx);
    }
    if (!ib_flags_all(tx->flags, IB_TX_FLOGGING)) {
        ib_state_notify_logging(tx->ib, tx);
    }
}
Пример #3
0
    void CheckResult(int lineno,
                     const BaseTestDatum &test,
                     ib_status_t rc,
                     ib_flags_t exresult,
                     ib_flags_t result)
    {
        std::string s1, s2;
        EXPECT_EQ(IB_OK, rc)
            << "Line " << lineno << ": "
            << Stringize(test) << " returned " << rc;
        if (rc != IB_OK) {
            return;
        }

        // NEWBUF and ALIAS result flags should never both be set
        bool both =
            ib_flags_all(result, IB_STRFLAG_NEWBUF|IB_STRFLAG_ALIAS);
        ASSERT_FALSE(both)
            << "Line " << lineno << ": " << Stringize(test)
            << " both NEWBUF and ALIAS result flags are set!"
            << ResultStr(result, s1);

        // Build the expected output
        EXPECT_EQ(exresult, result)
            << "Line " << lineno << ": " << Stringize(test)
            << " expected result=" << ResultStr(exresult, s1) << exresult
            << " actual=" << ResultStr(result, s2) << result;
    }
Пример #4
0
static ib_status_t ib_errbody_callback(
    ib_tx_t *tx,
    const char *data,
    size_t dlen,
    void *cbdata)
{
    uint8_t *err_body;
    tsib_txn_ctx *txndata = (tsib_txn_ctx *)tx->sctx;

    /* Handle No Data as zero length data. */
    if (data == NULL || dlen == 0) {
        return IB_OK;
    }

    /* We can't return an error after the response has started */
    if (ib_flags_all(tx->flags, IB_TX_FCLIENTRES_STARTED)) {
        return IB_DECLINED;
    }

    /* This alloc will be freed within TSHttpTxnErrorBodySet
     * so we have to use TSmalloc for it.
     */
    err_body = TSmalloc(dlen);
    if (err_body == NULL) {
        return IB_EALLOC;
    }

    txndata->err_body = memcpy(err_body, data, dlen);
    txndata->err_body_len = dlen;
    return IB_OK;
}
Пример #5
0
/* ASCII lowercase function (string version); See string.h */
ib_status_t ib_strlower(ib_strop_t op,
                        ib_mpool_t *mp,
                        char *str_in,
                        char **str_out,
                        ib_flags_t *result)
{
    IB_FTRACE_INIT();
    size_t len;
    ib_status_t rc = IB_OK;
    char *out = NULL;

    assert(mp != NULL);
    assert(str_in != NULL);
    assert(str_out != NULL);
    assert(result != NULL);

    len = strlen(str_in);
    switch(op) {
    case IB_STROP_INPLACE:
        out = str_in;
        rc = inplace(IB_STRFLAG_ALIAS, (uint8_t*)str_in, len, result);
        break;

    case IB_STROP_COPY:
        out = ib_mpool_strdup(mp, str_in);
        if (out == NULL) {
            IB_FTRACE_RET_STATUS(IB_EALLOC);
        }
        rc = inplace(IB_STRFLAG_NEWBUF, (uint8_t*)out, len, result);
        break;

    case IB_STROP_COW:
    {
#if ((__GNUC__==4) && (__GNUC_MINOR__<3))
        uint8_t *uint8ptr;
        rc = copy_on_write(mp,
                           (uint8_t *)str_in, len+1,
                           &uint8ptr, &len, result);
        out = (char *)uint8ptr;
#else
        rc = copy_on_write(mp,
                           (uint8_t *)str_in, len+1,
                           (uint8_t **)&out, &len, result);
#endif
        break;
    }

    default:
        IB_FTRACE_RET_STATUS(IB_EINVAL);
    }

    if (rc == IB_OK) {
        if (ib_flags_all(*result, IB_STRFLAG_MODIFIED)) {
            *(out+len) = '\0';
        }
        *str_out = out;
    }
    IB_FTRACE_RET_STATUS(rc);
}
Пример #6
0
static void ironbee_plugin_send_response_hdr(TSCont contp, TSHttpTxn txnp)
{
    assert(contp != NULL);
    assert(txnp != NULL);

    tsib_txn_ctx *txndata;

    txndata = TSContDataGet(contp);
    if (txndata == NULL) {
        /* Ironbee is unavailable to help with our response. */
        /* This contp is not ours, so we leave it. */
        internal_error_response(txnp);
        TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
        return;
    }

    /* If ironbee has sent us into an error response then
     * we came here in our error path, with nonzero status.
     */
    if (txndata->status != 0) {
        error_response(txnp, txndata);
    }

    /* Feed ironbee the headers if not done already. */
    if (!ib_flags_all(txndata->tx->flags, IB_TX_FRES_STARTED)) {
        if (process_hdr(txndata, txnp, &tsib_direction_client_resp) != HDR_OK) {
            /* I think this is a shouldn't happen event, and that
             * if it does we have an ironbee bug or misconfiguration.
             * Log an error to catch if it happens in practice.
             */
            ib_log_error_tx(txndata->tx, "process_hdr returned error in send_response_hdr event");
        }
    }

    /* If there is an ironbee-generated response body, notify ironbee.
     *
     * NOTE: I do not see anywhere else to put this as the error body is
     *       just a buffer and not delivered via normal IO channels, so
     *       the error body will never get caught by an event.
     */
    if ((txndata->status != 0) && (txndata->err_body != NULL)) {
        const char *data = txndata->err_body;
        size_t data_length = txndata->err_body_len;
        ib_log_debug_tx(txndata->tx,
                "error_response: calling ib_state_notify_response_body_data() %s:%d",
                __FILE__, __LINE__);
        ib_state_notify_response_body_data(txndata->tx->ib,
                                           txndata->tx,
                                           data, data_length);
    }

    TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
}
Пример #7
0
/**
 * Callback functions for IronBee to signal to us
 */
static
ib_status_t ib_header_callback(
    ib_tx_t                   *tx,
    ib_server_direction_t      dir,
    ib_server_header_action_t  action,
    const char                *name,
    size_t                     name_length,
    const char                *value,
    size_t                     value_length,
    void                      *cbdata
)
{
    tsib_txn_ctx *txndata = (tsib_txn_ctx *)tx->sctx;
    hdr_action_t *header;
    /* Logic for whether we're in time for the requested action */
    /* Output headers can change any time before they're sent */
    /* Input headers can only be touched during their read */

    if (ib_flags_all(tx->flags, IB_TX_FCLIENTRES_STARTED) ||
        (ib_flags_all(tx->flags, IB_TX_FSERVERREQ_STARTED)
                  && dir == IB_SERVER_REQUEST))
    {
        ib_log_debug_tx(tx, "Too late to change headers.");
        return IB_DECLINED;  /* too late for requested op */
    }

    header = ib_mm_alloc(tx->mm, sizeof(*header));
    header->next = txndata->hdr_actions;
    txndata->hdr_actions = header;
    header->dir = dir;
    /* FIXME: deferring merge support - implementing append instead */
    header->action = action = action == IB_HDR_MERGE ? IB_HDR_APPEND : action;
    header->hdr = ib_mm_memdup_to_str(tx->mm, name, name_length);
    header->value = ib_mm_memdup_to_str(tx->mm, value, value_length);

    return IB_OK;
}
Пример #8
0
static void ironbee_plugin_send_request_hdr(TSCont contp, TSHttpTxn txnp)
{
    assert(contp != NULL);
    assert(txnp != NULL);

    tsib_txn_ctx *txndata;

    txndata = TSContDataGet(contp);

    /* This event is about as late as we can possibly block
     * body attacks against the server.
     */

    /* If we are not yet blocked, ask IronBee if we should block. */
    if (!HTTP_CODE(txndata->status)) {
        if (!ib_flags_all(txndata->tx->flags, IB_TX_FREQ_FINISHED)) {
            ib_log_debug_tx(
                txndata->tx,
                "data_event: calling ib_state_notify_request_finished()"
            );
            (tsib_direction_client_req.ib_notify_end)(txndata->tx->ib, txndata->tx);
        }

        /* We we've transitioned to blocking,
         * - Add a handler for response header.
         * - Reenable with an error.
         * - Return (so we don't double-reenable).
         */
        if (HTTP_CODE(txndata->status)) {
            TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, contp);
            TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
            return;
        }
    }

    TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
}
Пример #9
0
static
ib_status_t ib_errhdr_callback(
    ib_tx_t    *tx,
    const char *name,
    size_t      name_length,
    const char *value,
    size_t      value_length,
    void       *cbdata
)
{
    tsib_txn_ctx *txndata = (tsib_txn_ctx *)tx->sctx;
    hdr_list *hdrs;
    /* We can't return an error after the response has started */
    if (ib_flags_all(tx->flags, IB_TX_FCLIENTRES_STARTED))
        return IB_DECLINED;
    if (!name || !value)
        return IB_EINVAL;
    hdrs = ib_mm_alloc(tx->mm, sizeof(*hdrs));
    hdrs->hdr = ib_mm_memdup_to_str(tx->mm, name, name_length);
    hdrs->value = ib_mm_memdup_to_str(tx->mm, value, value_length);
    hdrs->next = txndata->err_hdrs;
    txndata->err_hdrs = hdrs;
    return IB_OK;
}
Пример #10
0
static ib_status_t ib_error_callback(ib_tx_t *tx, int status, void *cbdata)
{
    tsib_txn_ctx *txndata = (tsib_txn_ctx *)tx->sctx;
    ib_log_debug_tx(tx, "ib_error_callback with status=%d", status);
    if ( is_error_status(status) ) {
        if (is_error_status(txndata->status) ) {
            ib_log_debug_tx(tx, "Ignoring: status already set to %d", txndata->status);
            return IB_OK;
        }
        /* We can't return an error after the response has started */
        if (ib_flags_all(tx->flags, IB_TX_FCLIENTRES_STARTED)) {
            ib_log_debug_tx(tx, "Too late to change status=%d", status);
            return IB_DECLINED;
        }
        /* ironbee wants to return an HTTP status.  We'll oblige */
        /* FIXME: would the semantics work for 1xx?  Do we care? */
        /* No, we don't care unless a use case arises for the proxy
         * to initiate a 1xx response independently of the backend.
         */
        txndata->status = status;
        return IB_OK;
    }
    return IB_ENOTIMPL;
}
Пример #11
0
/**
 * Determine buffering policy from config settings
 *
 * @param[in] ibd - the filter descriptor
 * @param[in] tx - the transaction
 */
static void buffer_init(ibd_ctx *ibd, ib_tx_t *tx)
{
    ib_core_cfg_t *corecfg = NULL;
    ib_status_t rc;

    tsib_filter_ctx *fctx = ibd->data;
    ib_server_direction_t dir = ibd->ibd->dir;

    if (tx == NULL) {
        fctx->buffering = IOBUF_NOBUF;
        return;
    }
    rc = ib_core_context_config(ib_context_main(tx->ib), &corecfg);
    if (rc != IB_OK) {
        ib_log_error_tx(tx, "Error determining buffering configuration.");
    }
    else {
        if (dir == IBD_REQ) {
            fctx->buffering = (corecfg->buffer_req == 0)
                ? IOBUF_NOBUF :
                (corecfg->limits.request_body_buffer_limit < 0)
                    ? IOBUF_BUFFER_ALL :
                    (corecfg->limits.request_body_buffer_limit_action == IB_BUFFER_LIMIT_ACTION_FLUSH_ALL)
                        ? IOBUF_BUFFER_FLUSHALL
                        : IOBUF_BUFFER_FLUSHPART;
            fctx->buf_limit = (size_t) corecfg->limits.request_body_buffer_limit;
        }
        else {
            fctx->buffering = (corecfg->buffer_res == 0)
                ? IOBUF_NOBUF :
                (corecfg->limits.response_body_buffer_limit < 0)
                    ? IOBUF_BUFFER_ALL :
                    (corecfg->limits.response_body_buffer_limit_action == IB_BUFFER_LIMIT_ACTION_FLUSH_ALL)
                        ? IOBUF_BUFFER_FLUSHALL
                        : IOBUF_BUFFER_FLUSHPART;
            fctx->buf_limit = (size_t) corecfg->limits.response_body_buffer_limit;
        }
    }

    /* Override buffering based on flags */
    if (fctx->buffering != IOBUF_NOBUF) {
        if (dir == IBD_REQ) {
            if (ib_flags_any(tx->flags, IB_TX_FALLOW_ALL | IB_TX_FALLOW_REQUEST) ||
                (!ib_flags_all(tx->flags, IB_TX_FINSPECT_REQBODY) 
&&
                 !ib_flags_all(tx->flags, IB_TX_FINSPECT_REQHDR)) 
)
            {
                fctx->buffering = IOBUF_NOBUF;
                ib_log_debug2_tx(tx, "\tDisable request buffering");
            }
        } else if (dir == IBD_RESP) {
            if (ib_flags_any(tx->flags, IB_TX_FALLOW_ALL) ||
                (!ib_flags_all(tx->flags, IB_TX_FINSPECT_RESBODY) &&
                 !ib_flags_all(tx->flags, IB_TX_FINSPECT_RESHDR)) )
            {
                fctx->buffering = IOBUF_NOBUF;
                ib_log_debug2_tx(tx, "\tDisable response buffering");
            }
        }
    }
}
Пример #12
0
/**
 * String modification transformation core
 *
 * @param[in] ib IronBee engine
 * @param[in] mp Memory pool to use for allocations.
 * @param[in] str_fn NUL-terminated string transformation function
 * @param[in] ex_fn EX (string/length) transformation function
 * @param[in] fin Input field.
 * @param[out] fout Output field.
 * @param[out] pflags Transformation flags.
 *
 * @returns IB_OK if successful.
 */
static ib_status_t tfn_strmod(ib_engine_t *ib,
                              ib_mpool_t *mp,
                              ib_strmod_fn_t str_fn,
                              ib_strmod_ex_fn_t ex_fn,
                              const ib_field_t *fin,
                              ib_field_t **fout,
                              ib_flags_t *pflags)
{
    ib_status_t rc;
    ib_flags_t result;

    assert(ib != NULL);
    assert(mp != NULL);
    assert(str_fn != NULL);
    assert(ex_fn != NULL);
    assert(fin != NULL);
    assert(fout != NULL);
    assert(pflags != NULL);

    /* Initialize the output field pointer */
    *fout = NULL;

    switch(fin->type) {
    case IB_FTYPE_NULSTR :
    {
        const char *in;
        char *out;
        rc = ib_field_value(fin, ib_ftype_nulstr_out(&in));
        if (rc != IB_OK) {
            return rc;
        }
        if (in == NULL) {
            return IB_EINVAL;
        }
        rc = str_fn(IB_STROP_COW, mp, (char *)in, &out, &result);
        if (rc != IB_OK) {
            return rc;
        }
        rc = ib_field_create(fout, mp,
                             fin->name, fin->nlen,
                             IB_FTYPE_NULSTR,
                             ib_ftype_nulstr_in(out));
        if (rc != IB_OK) {
            return rc;
        }
        break;
    }

    case IB_FTYPE_BYTESTR:
    {
        const ib_bytestr_t *bs;
        const uint8_t *din;
        uint8_t *dout;
        size_t dlen;
        rc = ib_field_value(fin, ib_ftype_bytestr_out(&bs));
        if (rc != IB_OK) {
            return rc;
        }
        if (bs == NULL) {
            return IB_EINVAL;
        }
        din = ib_bytestr_const_ptr(bs);
        if (din == NULL) {
            return IB_EINVAL;
        }
        dlen = ib_bytestr_length(bs);
        rc = ex_fn(IB_STROP_COW, mp,
                   (uint8_t *)din, dlen,
                   &dout, &dlen,
                   &result);
        if (rc != IB_OK) {
            return rc;
        }
        rc = ib_field_create_bytestr_alias(fout, mp,
                                           fin->name, fin->nlen,
                                           dout, dlen);
        if (rc != IB_OK) {
            return rc;
        }
        break;
    }

    default:
        return IB_EINVAL;
    } /* switch(fin->type) */

    /* Check the flags */
    if (ib_flags_all(result, IB_STRFLAG_MODIFIED)) {
        *pflags = IB_TFN_FMODIFIED;
    }
    else {
        *pflags = IB_TFN_NONE;
    }

    return IB_OK;
}
Пример #13
0
static void ironbee_plugin_pre_remap(TSCont contp, TSHttpTxn txnp)
{
    assert(contp != NULL);
    assert(txnp != NULL);

    tsib_txn_ctx *txndata;
    tsib_hdr_outcome status;

    int request_inspection_finished = 0;
    txndata = TSContDataGet(contp);
    assert ((txndata != NULL) && (txndata->tx != NULL));
    status = process_hdr(txndata, txnp, &tsib_direction_client_req);
    if (HDR_OUTCOME_IS_HTTP_OR_ERROR(status, txndata)) {
        if (status == HDR_HTTP_STATUS) {
            ib_log_debug_tx(txndata->tx,
                            "HTTP code %d contp=%p", txndata->status, contp);
         }
         else {
            /* Ironbee set a status we don't handle.
             * We returned EINVAL, but we also need housekeeping to
             * avoid a crash in modhtp and log something bad.
             */
            ib_log_debug_tx(txndata->tx,
                            "Internal error %d contp=%p", txndata->status, contp);
            /* Ugly hack: notifications to stop modhtp bombing out */
            request_inspection_finished = 1;
        }
    }
    else {
        /* Other nonzero statuses not supported */
        switch(status) {
          case HDR_OK:
            /* If we're not inspecting the Request body,
             * we can bring forward notification of end-request
             * so any header-only tests run on Request phase
             * can abort the tx before opening a backend connection.
             */
            if (!ib_flags_all(txndata->tx->flags, IB_TX_FINSPECT_REQBODY)) {
                request_inspection_finished = 1;
            }
            break;	/* All's well */
          case HDR_HTTP_STATUS:
            // FIXME: should we take the initiative here and return 500?
            ib_log_error_tx(txndata->tx,
                            "Internal error: ts-ironbee requested error but no error response set.");
            break;
          case HDR_HTTP_100:
            /* This can't actually happen with current Trafficserver
             * versions, as TS will generate a 400 error without
             * reference to us.  But in case that changes in future ...
             */
            ib_log_error_tx(txndata->tx,
                            "No request headers found.");
            break;
          default:
            ib_log_error_tx(txndata->tx,
                            "Unhandled state arose in handling request headers.");
            break;
        }
    }
    if (request_inspection_finished) {
        if (!ib_flags_all(txndata->tx->flags, IB_TX_FREQ_STARTED) ) {
            ib_state_notify_request_started(txndata->tx->ib, txndata->tx, NULL);
        }
        if (!ib_flags_all(txndata->tx->flags, IB_TX_FREQ_FINISHED) ) {
            ib_state_notify_request_finished(txndata->tx->ib, txndata->tx);
        }
    }
    else {
        /* hook an input filter to watch data */
        TSHttpTxnHookAdd(txnp, TS_HTTP_REQUEST_TRANSFORM_HOOK,
                         txndata->in_data_cont);
    }
    /* Flag that we can no longer prevent a request going to backend */
    ib_tx_flags_set(txndata->tx, IB_TX_FSERVERREQ_STARTED);

    /* Check whether Ironbee told us to block the request.
     * This could now come not just from process_hdr, but also
     * from a brought-forward notification if we aren't inspecting
     * a request body and notified request_finished.
     */
    if (HTTP_CODE(txndata->status)) {
        TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, contp);
        TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
    }
    else {
        TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
    }
}
Пример #14
0
static void ironbee_plugin_read_response_hdr(TSCont contp, TSHttpTxn txnp)
{
    assert(contp != NULL);
    assert(txnp != NULL);

    tsib_txn_ctx *txndata;
    tsib_hdr_outcome status;

    txndata = TSContDataGet(contp);

    if (txndata->tx == NULL) {
        TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
        return;
    }

    /* Feed ironbee the headers if not done already. */
    if (!ib_flags_all(txndata->tx->flags, IB_TX_FRES_STARTED)) {
        status = process_hdr(txndata, txnp, &tsib_direction_server_resp);

        /* OK, if this was an HTTP 100 response, it's not the
         * response we're interested in.  No headers have been
         * sent yet, and no data will be sent until we've
         * reached here again with the final response.
         */
        if (status == HDR_HTTP_100) {
            TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
            return;
        }
        // FIXME: Need to know if this fails as it (I think) means
        //        that the response did not come from the server and
        //        that ironbee should ignore it.
        /* I've not seen a fail here.  AFAICT if either the origin
         * isn't responding or we're responding from cache. we
         * never reach here in the first place.
         */
    }

    /* If ironbee signalled an error while processing request body data,
     * this is the first opportunity to divert to an errordoc
     */
    if (HTTP_CODE(txndata->status)) {
        ib_log_debug_tx(txndata->tx,
                        "HTTP code %d contp=%p", txndata->status, contp);
        TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, contp);
        TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
        return;
    }

    /* If we're not going to inspect response body data
     * we can bring forward notification of response-end
     * so we're in time to respond with an errordoc if Ironbee
     * wants to block in the response phase.
     *
     * This currently fails.  However, that appears to be because I
     * can't unset IB_TX_FINSPECT_RESBODY with InspectionEngineOptions
     */
    if (!ib_flags_all(txndata->tx->flags, IB_TX_FINSPECT_RESBODY)) {
        if (!ib_flags_all(txndata->tx->flags, IB_TX_FRES_STARTED) ) {
            ib_state_notify_response_started(txndata->tx->ib, txndata->tx, NULL);
        }
        if (!ib_flags_all(txndata->tx->flags, IB_TX_FRES_FINISHED) ) {
            ib_state_notify_response_finished(txndata->tx->ib, txndata->tx);
        }
        /* Test again for Ironbee telling us to block */
        if (HTTP_CODE(txndata->status)) {
            TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, contp);
            TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
            return;
        }
    }

    /* Flag that we're too late to divert to an error response */
    ib_tx_flags_set(txndata->tx, IB_TX_FCLIENTRES_STARTED);

    /* Normal execution.  Add output filter to inspect response. */
    TSHttpTxnHookAdd(txnp, TS_HTTP_RESPONSE_TRANSFORM_HOOK,
                     txndata->out_data_cont);
    TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
}
Пример #15
0
/**
 * Handle a data event from ATS.
 *
 * Handles all data events from ATS, uses process_data to handle the data
 * itself.
 *
 * @param[in,out] contp Pointer to the continuation
 * @param[in,out] event Event from ATS
 * @param[in,out] ibd unknown
 *
 * @returns status
 */
static int data_event(TSCont contp, TSEvent event, ibd_ctx *ibd)
{
    /* Check to see if the transformation has been closed by a call to
     * TSVConnClose.
     */
    tsib_txn_ctx *txndata = TSContDataGet(contp);
    ib_log_debug2_tx(txndata->tx, "Entering out_data for %s", ibd->ibd->dir_label);

    if (TSVConnClosedGet(contp)) {
        ib_log_debug2_tx(txndata->tx, "\tVConn is closed");
        return 0;
    }

    switch (event) {
        case TS_EVENT_ERROR:
        {
            TSVIO input_vio;

            ib_log_debug2_tx(txndata->tx, "\tEvent is TS_EVENT_ERROR");
            /* Get the write VIO for the write operation that was
             * performed on ourself. This VIO contains the continuation of
             * our parent transformation. This is the input VIO.
             */
            input_vio = TSVConnWriteVIOGet(contp);

            /* Call back the write VIO continuation to let it know that we
             * have completed the write operation.
             */
            TSContCall(TSVIOContGet(input_vio), TS_EVENT_ERROR, input_vio);
        }
        break;
        case TS_EVENT_VCONN_WRITE_COMPLETE:
            ib_log_debug2_tx(txndata->tx, "\tEvent is TS_EVENT_VCONN_WRITE_COMPLETE");
            /* When our output connection says that it has finished
             * reading all the txndata we've written to it then we should
             * shutdown the write portion of its connection to
             * indicate that we don't want to hear about it anymore.
             */
            TSVConnShutdown(TSTransformOutputVConnGet(contp), 0, 1);

            if (ibd->ibd->dir == IBD_REQ) {
                if (!ib_flags_all(txndata->tx->flags, IB_TX_FREQ_FINISHED)) {
                    ib_log_debug2_tx(txndata->tx, "data_event: calling ib_state_notify_request_finished()");
                    (*ibd->ibd->ib_notify_end)(txndata->tx->ib, txndata->tx);
                }
            }
            else {
                if (!ib_flags_all(txndata->tx->flags, IB_TX_FRES_FINISHED)) {
                    ib_log_debug2_tx(txndata->tx, "data_event: calling ib_state_notify_response_finished()");
                    (*ibd->ibd->ib_notify_end)(txndata->tx->ib, txndata->tx);
                }
            }
            if ( (ibd->ibd->ib_notify_post != NULL) &&
                 (!ib_flags_all(txndata->tx->flags, IB_TX_FPOSTPROCESS)) )
            {
                (*ibd->ibd->ib_notify_post)(txndata->tx->ib, txndata->tx);
            }
            if ( (ibd->ibd->ib_notify_log != NULL) &&
                 (!ib_flags_all(txndata->tx->flags, IB_TX_FLOGGING)) )
            {
                (*ibd->ibd->ib_notify_log)(txndata->tx->ib, txndata->tx);
            }
            break;
        case TS_EVENT_VCONN_WRITE_READY:
            ib_log_debug2_tx(txndata->tx, "\tEvent is TS_EVENT_VCONN_WRITE_READY");
            /* fall through */
        default:
            ib_log_debug2_tx(txndata->tx, "\t(event is %d)", event);
            /* If we get a WRITE_READY event or any other type of
             * event (sent, perhaps, because we were re-enabled) then
             * we'll attempt to transform more data.
             */
            process_data(contp, ibd);
            break;
    }

    return 0;
}
Пример #16
0
/**
 * @brief Execute the rule.
 *
 * @param[in] ib Ironbee engine
 * @param[in] tx The transaction.
 * @param[in,out] User data. A @c pcre_rule_data_t.
 * @param[in] flags Operator instance flags
 * @param[in] field The field content.
 * @param[out] result The result.
 * @returns IB_OK most times. IB_EALLOC when a memory allocation error handles.
 */
static ib_status_t pcre_operator_execute(ib_engine_t *ib,
                                         ib_tx_t *tx,
                                         const ib_rule_t *rule,
                                         void *data,
                                         ib_flags_t flags,
                                         ib_field_t *field,
                                         ib_num_t *result)
{
    IB_FTRACE_INIT();

    assert(ib!=NULL);
    assert(tx!=NULL);
    assert(tx->dpi!=NULL);
    assert(data!=NULL);

    int matches;
    ib_status_t ib_rc;
    const int ovecsize = 3 * MATCH_MAX;
    int *ovector = (int *)malloc(ovecsize*sizeof(*ovector));
    const char* subject = NULL;
    size_t subject_len = 0;
    const ib_bytestr_t* bytestr;
    pcre_rule_data_t *rule_data = (pcre_rule_data_t *)data;
    pcre_extra *edata = NULL;
#ifdef PCRE_JIT_STACK
    pcre_jit_stack *jit_stack = pcre_jit_stack_alloc(PCRE_JIT_MIN_STACK_SZ,
                                                     PCRE_JIT_MAX_STACK_SZ);
#endif

    if (ovector==NULL) {
        IB_FTRACE_RET_STATUS(IB_EALLOC);
    }

    if (field->type == IB_FTYPE_NULSTR) {
        ib_rc = ib_field_value(field, ib_ftype_nulstr_out(&subject));
        if (ib_rc != IB_OK) {
            free(ovector);
            IB_FTRACE_RET_STATUS(ib_rc);
        }

        if (subject != NULL) {
            subject_len = strlen(subject);
        }
    }
    else if (field->type == IB_FTYPE_BYTESTR) {
        ib_rc = ib_field_value(field, ib_ftype_bytestr_out(&bytestr));
        if (ib_rc != IB_OK) {
            free(ovector);
            IB_FTRACE_RET_STATUS(ib_rc);
        }

        if (bytestr != NULL) {
            subject_len = ib_bytestr_length(bytestr);
            subject = (const char *) ib_bytestr_const_ptr(bytestr);
        }
    }
    else {
        free(ovector);
        IB_FTRACE_RET_STATUS(IB_EINVAL);
    }

    if (subject == NULL) {
        subject     = "";
    }

    /* Debug block. Escapes a string and prints it to the log.
     * Memory is freed. */
    if (ib_log_get_level(ib) >= 9) {

        /* Worst case, we can have a string that is 4x larger.
         * Consider if a string of 0xF7 is passed.  That single character
         * will expand to a string of 4 printed characters +1 for the \0
         * character. */
        char *debug_str = ib_util_hex_escape(subject, subject_len);

        if ( debug_str != NULL ) {
            ib_log_debug3_tx(tx, "Matching against: %s", debug_str);
            free( debug_str );
        }
    }

#ifdef PCRE_JIT_STACK
    /* Log if we expected jit, but did not get it. */
    if (rule_data->is_jit && jit_stack == NULL) {
        ib_log_debug(ib,
                     "Failed to allocate a jit stack for a jit-compiled rule. "
                     "Not using jit for this call.");
        edata = NULL;
    }

    /* If the study data is NULL or size zero, don't use it. */
    else if (rule_data->edata == NULL || rule_data->study_data_sz <= 0) {
        edata = NULL;
    }

    /* Only if we get here do we use the study data (edata) in the rule_data. */
    else {
        edata = rule_data->edata;
        pcre_assign_jit_stack(rule_data->edata, NULL, jit_stack);
    }

#endif

    matches = pcre_exec(rule_data->cpatt,
                        edata,
                        subject,
                        subject_len,
                        0, /* Starting offset. */
                        0, /* Options. */
                        ovector,
                        ovecsize);

#ifdef PCRE_JIT_STACK
    if (jit_stack != NULL) {
        pcre_jit_stack_free(jit_stack);
    }
#endif

    if (matches > 0) {
        if (ib_flags_all(rule->flags, IB_RULE_FLAG_CAPTURE) == true) {
            pcre_set_matches(ib, tx, ovector, matches, subject);
        }
        ib_rc = IB_OK;
        *result = 1;
    }
    else if (matches == PCRE_ERROR_NOMATCH) {

        if (ib_log_get_level(ib) >= 7) {
            char* tmp_c = malloc(subject_len+1);
            memcpy(tmp_c, subject, subject_len);
            tmp_c[subject_len] = '\0';
            /* No match. Return false to the caller (*result = 0). */
            ib_log_debug2_tx(tx, "No match for [%s] using pattern [%s].",
                        tmp_c,
                        rule_data->patt);
            free(tmp_c);
        }


        ib_rc = IB_OK;
        *result = 0;
    }
    else {
        /* Some other error occurred. Set the status to false and
        report the error. */
        ib_rc = IB_EUNKNOWN;
        *result = 0;
    }

    free(ovector);
    IB_FTRACE_RET_STATUS(ib_rc);
}
Пример #17
0
/**
 * Plugin for the IronBee ATS.
 *
 * Handles some ATS events.
 *
 * @param[in,out] contp Pointer to the continuation
 * @param[in,out] event Event from ATS
 * @param[in,out] edata Event data
 *
 * @returns status
 */
int ironbee_plugin(TSCont contp, TSEvent event, void *edata)
{
    ib_status_t rc;
    TSCont mycont;
    TSHttpTxn txnp = (TSHttpTxn) edata;
    TSHttpSsn ssnp = (TSHttpSsn) edata;
    tsib_txn_ctx *txndata;
    tsib_ssn_ctx *ssndata;
    tsib_hdr_outcome status;
    TSMutex ts_mutex = NULL;

    TSDebug("ironbee", "Entering ironbee_plugin with %d", event);
    switch (event) {

        /* CONNECTION */
        case TS_EVENT_HTTP_SSN_START:
            /* start of connection */
            /* But we can't initialize conn stuff here, because there's
             * no API to get the connection stuff required by ironbee
             * at this point.  So instead, intercept the first TXN
             *
             * what we can and must do: create a new contp whose
             * lifetime is our ssn
             */
            ts_mutex = TSMutexCreate();
            mycont = TSContCreate(ironbee_plugin, ts_mutex);
            TSHttpSsnHookAdd (ssnp, TS_HTTP_TXN_START_HOOK, mycont);
            ssndata = TSmalloc(sizeof(*ssndata));
            memset(ssndata, 0, sizeof(*ssndata));
            /* The only failure here is EALLOC, and if that happens
             * we're ****ed anyway
             */
            rc = ib_lock_create_malloc(&(ssndata->mutex));
            assert(rc == IB_OK);
            ssndata->contp = mycont;
            ssndata->ts_mutex = ts_mutex;
            TSContDataSet(mycont, ssndata);

            TSHttpSsnHookAdd (ssnp, TS_HTTP_SSN_CLOSE_HOOK, mycont);

            TSHttpSsnReenable (ssnp, TS_EVENT_HTTP_CONTINUE);
            break;

        case TS_EVENT_HTTP_TXN_START:
        {
            /* start of Request */
            /* First req on a connection, we set up conn stuff */
            ib_status_t  rc;
            ib_engine_t *ib = NULL;

            ssndata = TSContDataGet(contp);
            ib_lock_lock(ssndata->mutex);

            if (ssndata->iconn == NULL) {
                rc = tsib_manager_engine_acquire(&ib);
                if (rc == IB_DECLINED) {
                    /* OK, this means the manager is disabled deliberately,
                     * but otherwise all's well.  So this TXN
                     * gets processed without intervention from Ironbee
                     * and is invisble when our SSN_CLOSE hook runs.
                     */
                    ib_lock_unlock(ssndata->mutex);
                    TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
                    TSDebug("ironbee", "Decline from engine manager");
                    break;
                }
                else if (rc != IB_OK) {
                    TSError("[ironbee] Failed to acquire engine: %s",
                            ib_status_to_string(rc));
                    goto noib_error;
                }
                if (ib != NULL) {
                    rc = ib_conn_create(ib, &ssndata->iconn, contp);
                    if (rc != IB_OK) {
                        TSError("[ironbee] ib_conn_create: %s",
                                ib_status_to_string(rc));
                        tsib_manager_engine_release(ib);
                        goto noib_error;
                    }

                    /* In the normal case, release the engine when the
                     * connection's memory pool is destroyed */
                    rc = ib_mm_register_cleanup(ssndata->iconn->mm,
                                                cleanup_ib_connection,
                                                ib);
                    if (rc != IB_OK) {
                        TSError("[ironbee] ib_mm_register_cleanup: %s",
                                ib_status_to_string(rc));
                        tsib_manager_engine_release(ib);
                        goto noib_error;
                    }

                    TSDebug("ironbee", "CONN CREATE: conn=%p", ssndata->iconn);
                    ssndata->txnp = txnp;
                    ssndata->txn_count = ssndata->closing = 0;

                    rc = ironbee_conn_init(ssndata);
                    if (rc != IB_OK) {
                        TSError("[ironbee] ironbee_conn_init: %s",
                                ib_status_to_string(rc));
                        goto noib_error;
                    }

                    TSContDataSet(contp, ssndata);
                    TSDebug("ironbee",
                            "ironbee_plugin: ib_state_notify_conn_opened()");
                    rc = ib_state_notify_conn_opened(ib, ssndata->iconn);
                    if (rc != IB_OK) {
                        TSError("[ironbee] Failed to notify connection opened: %s",
                                ib_status_to_string(rc));
                    }
                }
                else {
                    /* Use TSError where there's no ib or tx */
                    TSError("Ironbee: No ironbee engine!");
                    goto noib_error;
                }
            }

            /* create a txn cont (request ctx) and tx */
            txndata = TSmalloc(sizeof(*txndata));
            memset(txndata, 0, sizeof(*txndata));
            txndata->ssn = ssndata;
            txndata->txnp = txnp;

            rc = ib_tx_create(&txndata->tx, ssndata->iconn, txndata);
            if (rc != IB_OK) {
                TSError("[ironbee] Failed to create tx: %d", rc);
                tsib_manager_engine_release(ib);
                TSfree(txndata);
                goto noib_error;
            }

            ++ssndata->txn_count;
            ib_lock_unlock(ssndata->mutex);

            ib_log_debug_tx(txndata->tx,
                            "TX CREATE: conn=%p tx=%p id=%s txn_count=%d",
                            ssndata->iconn, txndata->tx, txndata->tx->id,
                            txndata->ssn->txn_count);

            mycont = TSContCreate(ironbee_plugin, ssndata->ts_mutex);
            TSContDataSet(mycont, txndata);

            TSHttpTxnHookAdd(txnp, TS_HTTP_TXN_CLOSE_HOOK, mycont);

            /* Hook to process responses */
            TSHttpTxnHookAdd(txnp, TS_HTTP_READ_RESPONSE_HDR_HOOK, mycont);

            /* Hook to process requests */
            TSHttpTxnHookAdd(txnp, TS_HTTP_READ_REQUEST_HDR_HOOK, mycont);

            /* Create continuations for input and output filtering
             * to give them txn lifetime.
             */
            txndata->in_data_cont = TSTransformCreate(in_data_event, txnp);
            TSContDataSet(txndata->in_data_cont, txndata);

            txndata->out_data_cont = TSTransformCreate(out_data_event, txnp);
            TSContDataSet(txndata->out_data_cont, txndata);

            TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
            break;

noib_error:
            ib_lock_unlock(ssndata->mutex);

            /* NULL txndata signals this to SEND_RESPONSE */
            mycont = TSContCreate(ironbee_plugin, ssndata->ts_mutex);
            TSContDataSet(mycont, NULL);

            TSError("[ironbee] Internal error initialising for transaction");
            TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, mycont);
            TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
            break;
        }

        /* HTTP RESPONSE */
        case TS_EVENT_HTTP_READ_RESPONSE_HDR:
            txndata = TSContDataGet(contp);
            if (txndata->tx == NULL) {
                TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
                break;
            }

            /* Feed ironbee the headers if not done already. */
            if (!ib_flags_all(txndata->tx->flags, IB_TX_FRES_STARTED)) {
                status = process_hdr(txndata, txnp, &tsib_direction_server_resp);

                /* OK, if this was an HTTP 100 response, it's not the
                 * response we're interested in.  No headers have been
                 * sent yet, and no data will be sent until we've
                 * reached here again with the final response.
                 */
                if (status == HDR_HTTP_100) {
                    TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
                    break;
                }
                // FIXME: Need to know if this fails as it (I think) means
                //        that the response did not come from the server and
                //        that ironbee should ignore it.
                /* I've not seen a fail here.  AFAICT if either the origin
                 * isn't responding or we're responding from cache. we
                 * never reach here in the first place.
                 */
            }

            /* If ironbee signalled an error while processing request body data,
             * this is the first opportunity to divert to an errordoc
             */
            if (HTTP_CODE(txndata->status)) {
                ib_log_debug_tx(txndata->tx,
                                "HTTP code %d contp=%p", txndata->status, contp);
                TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, contp);
                TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
                break;
            }

            /* If we're not going to inspect response body data 
             * we can bring forward notification of response-end
             * so we're in time to respond with an errordoc if Ironbee
             * wants to block in the response phase.
             *
             * This currently fails.  However, that appears to be because I
             * can't unset IB_TX_FINSPECT_RESBODY with InspectionEngineOptions
             */
            if (!ib_flags_all(txndata->tx->flags, IB_TX_FINSPECT_RESBODY)) {
                if (!ib_flags_all(txndata->tx->flags, IB_TX_FRES_STARTED) ) {
                    ib_state_notify_response_started(txndata->tx->ib, txndata->tx, NULL);
                }
                if (!ib_flags_all(txndata->tx->flags, IB_TX_FRES_FINISHED) ) {
                    ib_state_notify_response_finished(txndata->tx->ib, txndata->tx);
                }
                /* Test again for Ironbee telling us to block */
                if (HTTP_CODE(txndata->status)) {
                    TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, contp);
                    TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
                    break;
                }
            }

            /* Flag that we're too late to divert to an error response */
            ib_tx_flags_set(txndata->tx, IB_TX_FCLIENTRES_STARTED);

            /* Normal execution.  Add output filter to inspect response. */
            TSHttpTxnHookAdd(txnp, TS_HTTP_RESPONSE_TRANSFORM_HOOK,
                             txndata->out_data_cont);
            TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);

            break;

        /* Hook for processing response headers. */
        case TS_EVENT_HTTP_SEND_RESPONSE_HDR:
            txndata = TSContDataGet(contp);
            if (txndata == NULL) {
                /* Ironbee is unavailable to help with our response. */
                internal_error_response(txnp);
                /* This contp isn't going through the normal flow. */
                TSContDestroy(contp);
                TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
                break;
            }

            /* If ironbee has sent us into an error response then
             * we came here in our error path, with nonzero status.
             */
            if (txndata->status != 0) {
                error_response(txnp, txndata);
            }

            /* Feed ironbee the headers if not done already. */
            if (!ib_flags_all(txndata->tx->flags, IB_TX_FRES_STARTED)) {
                if (process_hdr(txndata, txnp, &tsib_direction_client_resp) != HDR_OK) {
                    /* I think this is a shouldn't happen event, and that
                     * if it does we have an ironbee bug or misconfiguration.
                     * Log an error to catch if it happens in practice.
                     */
                    ib_log_error_tx(txndata->tx, "process_hdr returned error in send_response_hdr event");
                }
            }

            /* If there is an ironbee-generated response body, notify ironbee.
             *
             * NOTE: I do not see anywhere else to put this as the error body is
             *       just a buffer and not delivered via normal IO channels, so
             *       the error body will never get caught by an event.
             */
            if ((txndata->status != 0) && (txndata->err_body != NULL)) {
                const char *data = txndata->err_body;
                size_t data_length = txndata->err_body_len;
                ib_log_debug_tx(txndata->tx,
                        "error_response: calling ib_state_notify_response_body_data() %s:%d",
                        __FILE__, __LINE__);
                ib_state_notify_response_body_data(txndata->tx->ib,
                                                   txndata->tx,
                                                   data, data_length);
            }

            TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
            break;

        /* HTTP REQUEST */
        case TS_EVENT_HTTP_READ_REQUEST_HDR:
            /* hook to examine output headers.  They're not available yet */
            TSHttpTxnHookAdd(txnp, TS_HTTP_PRE_REMAP_HOOK, contp);

            TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
            break;

        /* hook for processing incoming request/headers
         * The OS_DNS hook is an alternative here.
         */
        case TS_EVENT_HTTP_PRE_REMAP:
        {
            int request_inspection_finished = 0;
            txndata = TSContDataGet(contp);
            assert ((txndata != NULL) && (txndata->tx != NULL));
            status = process_hdr(txndata, txnp, &tsib_direction_client_req);
            if (HDR_OUTCOME_IS_HTTP_OR_ERROR(status, txndata)) {
                if (status == HDR_HTTP_STATUS) {
                    ib_log_debug_tx(txndata->tx,
                                    "HTTP code %d contp=%p", txndata->status, contp);
                 }
                 else {
                    /* Ironbee set a status we don't handle.
                     * We returned EINVAL, but we also need housekeeping to
                     * avoid a crash in modhtp and log something bad.
                     */
                    ib_log_debug_tx(txndata->tx,
                                    "Internal error %d contp=%p", txndata->status, contp);
                    /* Ugly hack: notifications to stop modhtp bombing out */
                    request_inspection_finished = 1;
                }
            }
            else {
                /* Other nonzero statuses not supported */
                switch(status) {
                  case HDR_OK:
                    /* If we're not inspecting the Request body,
                     * we can bring forward notification of end-request
                     * so any header-only tests run on Request phase
                     * can abort the tx before opening a backend connection.
                     */
                    if (!ib_flags_all(txndata->tx->flags, IB_TX_FINSPECT_REQBODY)) {
                        request_inspection_finished = 1;
                    }
                    break;	/* All's well */
                  case HDR_HTTP_STATUS:
                    // FIXME: should we take the initiative here and return 500?
                    ib_log_error_tx(txndata->tx,
                                    "Internal error: ts-ironbee requested error but no error response set.");
                    break;
                  case HDR_HTTP_100:
                    /* This can't actually happen with current Trafficserver
                     * versions, as TS will generate a 400 error without
                     * reference to us.  But in case that changes in future ...
                     */
                    ib_log_error_tx(txndata->tx,
                                    "No request headers found.");
                    break;
                  default:
                    ib_log_error_tx(txndata->tx,
                                    "Unhandled state arose in handling request headers.");
                    break;
                }
            }
            if (request_inspection_finished) {
                if (!ib_flags_all(txndata->tx->flags, IB_TX_FREQ_STARTED) ) {
                    ib_state_notify_request_started(txndata->tx->ib, txndata->tx, NULL);
                }
                if (!ib_flags_all(txndata->tx->flags, IB_TX_FREQ_FINISHED) ) {
                    ib_state_notify_request_finished(txndata->tx->ib, txndata->tx);
                }
            }
            else {
                /* hook an input filter to watch data */
                TSHttpTxnHookAdd(txnp, TS_HTTP_REQUEST_TRANSFORM_HOOK,
                                 txndata->in_data_cont);
            }
            /* Flag that we can no longer prevent a request going to backend */
            ib_tx_flags_set(txndata->tx, IB_TX_FSERVERREQ_STARTED);

            /* Check whether Ironbee told us to block the request.
             * This could now come not just from process_hdr, but also
             * from a brought-forward notification if we aren't inspecting
             * a request body and notified request_finished.
             */
            if (HTTP_CODE(txndata->status)) {
                TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, contp);
                TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
            }
            else {
                TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
            }
            break;
        }


        /* CLEANUP EVENTS */
        case TS_EVENT_HTTP_TXN_CLOSE:
        {
            txndata = TSContDataGet(contp);

            TSContDestroy(txndata->out_data_cont);
            TSContDestroy(txndata->in_data_cont);
            TSContDataSet(contp, NULL);
            TSContDestroy(contp);
            if ( (txndata != NULL) && (txndata->tx != NULL) ) {
                ib_log_debug_tx(txndata->tx,
                                "TXN Close: %p", (void *)contp);
                tsib_txn_ctx_destroy(txndata);
            }
            TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
            break;
        }

        case TS_EVENT_HTTP_SSN_CLOSE:
            TSDebug("ironbee", "SSN Close: %p", (void *)contp);
            tsib_ssn_ctx_destroy(TSContDataGet(contp));
            tsib_manager_engine_cleanup();
            TSHttpSsnReenable(ssnp, TS_EVENT_HTTP_CONTINUE);
            break;

        case TS_EVENT_MGMT_UPDATE:
        {
            TSDebug("ironbee", "Management update");
            ib_status_t  rc;
            rc = tsib_manager_engine_create();
            if (rc != IB_OK) {
                TSError("[ironbee] Error creating new engine: %s",
                        ib_status_to_string(rc));
            }
            break;
        }

        /* if we get here we've got a bug */
        default:
            TSError("[ironbee] *** Unhandled event %d in ironbee_plugin.", event);
            break;
    }

    return 0;
}