/** * Set the matches from a multi-match dfa as a list in the CAPTURE * collection (all with "0" key). * * @param[in] tx Current transaction. * @param[in] capture Collection to capture to. * @param[in] ovector The vector of integer pairs of matches from PCRE. * @param[in] matches The number of matches. * @param[in] subject The matched-against string data. * * @returns IB_OK or IB_EALLOC. */ static ib_status_t pcre_dfa_set_match( ib_tx_t *tx, ib_field_t *capture, int *ovector, int matches, const char *subject ) { assert(tx != NULL); assert(tx->ib != NULL); assert(capture != NULL); assert(ovector != NULL); int i; /* We have a match! Now populate TX:0-9 in tx->data. */ ib_log_debug2_tx(tx, "DFA populating %d matches", matches); for (i = 0; i < matches; ++i) { size_t match_len; const char *match_start; const char *name; ib_bytestr_t *bs; ib_field_t *field; ib_status_t rc; /* Readability. Mark the start and length of the string. */ match_start = subject+ovector[i * 2]; match_len = ovector[i * 2 + 1] - ovector[i * 2]; /* Create a byte-string representation */ rc = ib_bytestr_dup_mem(&bs, tx->mp, (const uint8_t*)match_start, match_len); if (rc != IB_OK) { return rc; } /* Create a field to hold the byte-string */ name = ib_capture_name(0); rc = ib_field_create(&field, tx->mp, name, strlen(name), IB_FTYPE_BYTESTR, ib_ftype_bytestr_in(bs)); if (rc != IB_OK) { return rc; } /* Add it to the capture collection */ rc = ib_capture_add_item(capture, field); if (rc != IB_OK) { return rc; } } return IB_OK; }
/** * Get or create an ib_hash_t inside of @c tx for storing dfa rule data. * * The hash is stored at the key @c HASH_NAME_STR. * * @param[in] m PCRE module. * @param[in] tx The transaction containing @c tx->data which holds * the @a operator_data object. * @param[out] hash The fetched or created rule data hash. This is set * to NULL on failure. * * @return * - IB_OK on success. * - IB_EALLOC on allocation failure */ static ib_status_t get_or_create_operator_data_hash( const ib_module_t *m, ib_tx_t *tx, ib_hash_t **hash ) { assert(tx); assert(tx->mp); ib_status_t rc; /* Get or create the hash that contains the rule data. */ rc = ib_tx_get_module_data(tx, m, hash); if ( (rc == IB_OK) && (*hash != NULL) ) { ib_log_debug2_tx(tx, "Found rule data hash in tx."); return IB_OK; } ib_log_debug2_tx(tx, "Rule data hash did not exist in tx."); rc = ib_hash_create(hash, tx->mp); if (rc != IB_OK) { ib_log_debug2_tx(tx, "Failed to create hash: %s", ib_status_to_string(rc)); return rc; } rc = ib_tx_set_module_data(tx, m, *hash); if (rc != IB_OK) { ib_log_debug2_tx(tx, "Failed to store hash: %s", ib_status_to_string(rc)); *hash = NULL; } ib_log_debug2_tx(tx, "Returning rule hash at %p.", *hash); return rc; }
/** * Get or create an ib_hash_t inside of @c tx->data for storing dfa rule data. * * The hash is stored at the key @c MODULE_DATA_STR. * * @param[in] tx The transaction containing @c tx->data which holds * the @a rule_data object. * @param[out] rule_data The fetched or created rule data hash. This is set * to NULL on failure. * * @return * - IB_OK on success. * - IB_EALLOC on allocation failure */ static ib_status_t get_or_create_rule_data_hash(ib_tx_t *tx, ib_hash_t **rule_data) { IB_FTRACE_INIT(); assert(tx); assert(tx->mp); ib_status_t rc; /* Get or create the hash that contains the rule data. */ rc = ib_hash_get(tx->data, rule_data, MODULE_DATA_STR); if (rc == IB_OK && *rule_data != NULL) { ib_log_debug2_tx(tx, "Found rule data hash in tx data named " MODULE_DATA_STR); IB_FTRACE_RET_STATUS(IB_OK); } ib_log_debug2_tx(tx, "Rule data hash did not exist in tx data."); ib_log_debug2_tx(tx, "Creating rule data hash " MODULE_DATA_STR); rc = ib_hash_create(rule_data, tx->mp); if (rc != IB_OK) { ib_log_debug2_tx(tx, "Failed to create hash " MODULE_DATA_STR ": %d", rc); IB_FTRACE_RET_STATUS(rc); } rc = ib_hash_set(tx->data, MODULE_DATA_STR, *rule_data); if (rc != IB_OK) { ib_log_debug2_tx(tx, "Failed to store hash " MODULE_DATA_STR ": %d", rc); *rule_data = NULL; } ib_log_debug2_tx(tx, "Returning rule hash " MODULE_DATA_STR " at %p.", *rule_data); IB_FTRACE_RET_STATUS(rc); }
/** * @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); }
/** * Set the matches into the given field name as .0, .1, .2 ... .9. * * @param[in] ib The IronBee engine to log to. * @param[in] tx The transaction to store the values into (tx->dpi). * @param[in] field_name The field to populate with Regex matches. * @param[in] ovector The vector of integer pairs of matches from PCRE. * @param[in] matches The number of matches. * @param[in] subject The matched-against string data. * * @returns IB_OK or IB_EALLOC. */ static ib_status_t pcre_set_matches(ib_engine_t *ib, ib_tx_t *tx, int *ovector, int matches, const char *subject) { IB_FTRACE_INIT(); /* IronBee status. */ ib_status_t rc; /* Iterator. */ int i; rc = ib_data_capture_clear(tx); if (rc != IB_OK) { ib_log_error_tx(tx, "Error clearing captures: %s", ib_status_to_string(rc)); } /* We have a match! Now populate TX:0-9 in tx->dpi. */ ib_log_debug2_tx(tx, "REGEX populating %d matches", matches); for (i=0; i<matches; i++) { /* The length of the match. */ size_t match_len; /* The first character in the match. */ const char *match_start; /* Field name */ const char *name; /* Holder for a copy of the field value when creating a new field. */ ib_bytestr_t *bs; /* Field holder. */ ib_field_t *field; /* Readability. Mark the start and length of the string. */ match_start = subject+ovector[i*2]; match_len = ovector[i*2+1] - ovector[i*2]; /* If debugging this, copy the string value out and print it to the * log. This could be dangerous as there could be non-character * values in the match. */ ib_log_debug2_tx(tx, "REGEX Setting #%d=%.*s", i, (int)match_len, match_start); /* Create a byte-string representation */ rc = ib_bytestr_dup_mem(&bs, tx->mp, (const uint8_t*)match_start, match_len); if (rc != IB_OK) { IB_FTRACE_RET_STATUS(rc); } /* Create a field to hold the byte-string */ name = ib_data_capture_name(i); rc = ib_field_create(&field, tx->mp, name, strlen(name), IB_FTYPE_BYTESTR, ib_ftype_bytestr_in(bs)); if (rc != IB_OK) { IB_FTRACE_RET_STATUS(rc); } /* Add it to the capture collection */ rc = ib_data_capture_set_item(tx, i, field); if (rc != IB_OK) { IB_FTRACE_RET_STATUS(rc); } } IB_FTRACE_RET_STATUS(IB_OK); }
/** * @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 dfa_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(tx); assert(data); int matches; ib_status_t ib_rc; const int ovecsize = 3 * MATCH_MAX; dfa_rule_data_t *rule_data; int *ovector; const char* subject; size_t subject_len; const ib_bytestr_t* bytestr; dfa_workspace_t *dfa_workspace; int options; /* dfa exec options. */ ovector = (int *)malloc(ovecsize*sizeof(*ovector)); if (ovector==NULL) { IB_FTRACE_RET_STATUS(IB_EALLOC); } /* Pull out the rule data. */ rule_data = (dfa_rule_data_t *)data; 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); } 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); } subject_len = ib_bytestr_length(bytestr); subject = (const char *) ib_bytestr_const_ptr(bytestr); } else { free(ovector); IB_FTRACE_RET_STATUS(IB_EINVAL); } /* 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 ); } } /* Get the per-tx workspace data for this rule data id. */ ib_rc = get_dfa_tx_data(tx, rule_data->id, &dfa_workspace); if (ib_rc == IB_ENOENT) { options = PCRE_PARTIAL_SOFT; ib_rc = alloc_dfa_tx_data(tx, rule_data->id, &dfa_workspace); if (ib_rc != IB_OK) { free(ovector); ib_log_error_tx(tx, "Unexpected error creating tx storage " "for dfa operator %s", rule_data->id); IB_FTRACE_RET_STATUS(ib_rc); } ib_log_debug_tx(tx, "Created DFA workspace at %p for id %s.", dfa_workspace, rule_data->id); } else if (ib_rc == IB_OK) { options = PCRE_PARTIAL_SOFT | PCRE_DFA_RESTART; ib_log_debug_tx(tx, "Reusing existing DFA workspace %p for id %s.", dfa_workspace, rule_data->id); } else { free(ovector); ib_log_error_tx(tx, "Unexpected error fetching dfa data " "for dfa operator %s", rule_data->id); IB_FTRACE_RET_STATUS(ib_rc); } /* Actually do the DFA match. */ matches = pcre_dfa_exec(rule_data->cpatt, rule_data->edata, subject, subject_len, 0, /* Starting offset. */ options, ovector, ovecsize, dfa_workspace->workspace, dfa_workspace->wscount); if (matches >= 0) { ib_rc = IB_OK; *result = 1; } else if (matches == PCRE_ERROR_PARTIAL) { ib_log_debug2_tx(tx, "Partial match found, but not a full match."); ib_rc = IB_OK; *result = 0; } 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); }
/** * 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; }
/** * Process data from ATS. * * Process data from one of the ATS events. * * @param[in,out] contp - the continuation * @param[in,out] ibd - the filter descriptor */ static void process_data(TSCont contp, ibd_ctx *ibd) { int64_t ntodo; int64_t navail; TSIOBufferReader input_reader, output_reader; TSIOBufferBlock block; const char *buf; int64_t nbytes; ib_status_t rc; tsib_filter_ctx *fctx = ibd->data; tsib_txn_ctx *txndata = TSContDataGet(contp); TSVIO input_vio = TSVConnWriteVIOGet(contp); TSIOBuffer in_buf = TSVIOBufferGet(input_vio); /* Test whether we're going into an errordoc */ if (HTTP_CODE(txndata->status)) { /* We're going to an error document, * so we discard all this data */ ib_log_debug2_tx(txndata->tx, "Status is %d, discarding", txndata->status); ibd->data->buffering = IOBUF_DISCARD; } /* Test for EOS */ if (in_buf == NULL) { if (fctx->output_buffer != NULL) { /* flush anything we have buffered. This is final! */ rc = flush_data(fctx, -1, 1); switch(rc) { case IB_OK: break; case IB_EBADVAL: ib_log_error_tx(txndata->tx, "Bad/Inconsistent stream edit(s) ignored."); break; default: /* Can't happen unless a new status is introduced */ ib_log_error_tx(txndata->tx, "Unhandled return value %d", rc); break; } } else { /* I guess NULL input may mean something other than EOS. * This appears to be possible when * processing an HTTP error from the backend. */ ib_log_debug2_tx(txndata->tx, "Filter input was null. No filtering."); /* RNS-1268: seems we may have to go through all the motions * of creating and enabling an output_vio with no data. */ fctx->output_buffer = TSIOBufferCreate(); ib_mm_register_cleanup(txndata->tx->mm, (ib_mm_cleanup_fn_t) TSIOBufferDestroy, (void*) fctx->output_buffer); output_reader = TSIOBufferReaderAlloc(fctx->output_buffer); fctx->output_vio = TSVConnWrite(TSTransformOutputVConnGet(contp), contp, output_reader, 0); TSVIOReenable(fctx->output_vio); } return; } /* Test for first time, and initialise. */ if (!fctx->output_buffer) { // FIXME - What to choose here and why? int64_t output_vio_sz = TSVIONBytesGet(input_vio); // NOTE: Using INT64_MAX asserts on 4.2.2: InkAPI.cc:6261: failed assert `sdk_sanity_check_iocore_structure(connp) == TS_SUCCESS` //int64_t output_vio_sz = INT64_MAX; // NOTE: Does it matter that this is only INT32_MAX as in the examples? // int64_t output_vio_sz = INT32_MAX; //int64_t output_vio_sz = fctx->have_edits // ? INT64_MAX // : TSVIONBytesGet(input_vio); fctx->output_buffer = TSIOBufferCreate(); ib_mm_register_cleanup(txndata->tx->mm, (ib_mm_cleanup_fn_t) TSIOBufferDestroy, (void*) fctx->output_buffer); // FIXME - Where is TSIOBufferReaderFree()? output_reader = TSIOBufferReaderAlloc(fctx->output_buffer); fctx->output_vio = TSVConnWrite(TSTransformOutputVConnGet(contp), contp, output_reader, output_vio_sz); fctx->buffer = TSIOBufferCreate(); ib_mm_register_cleanup(txndata->tx->mm, (ib_mm_cleanup_fn_t) TSIOBufferDestroy, (void*) fctx->buffer); // FIXME - Where is TSIOBufferReaderFree()? fctx->reader = TSIOBufferReaderAlloc(fctx->buffer); /* Get the buffering config */ if (!HTTP_CODE(txndata->status)) { buffer_init(ibd, txndata->tx); } } /* Get any unprocessed bytes. */ ntodo = TSVIONTodoGet(input_vio); /* Test for EOS */ if (ntodo == 0) { ib_log_debug2_tx(txndata->tx, "ntodo zero before consuming data"); flush_data(fctx, -1, 1); /* Call back the input VIO continuation to let it know that we * have completed the write operation. */ TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_COMPLETE, input_vio); return; } /* OK, there's some input awaiting our attention */ input_reader = TSVIOReaderGet(input_vio); while (navail = TSIOBufferReaderAvail(input_reader), navail > 0) { block = TSIOBufferReaderStart(input_reader); buf = TSIOBufferBlockReadStart(block, input_reader, &nbytes); rc = (*ibd->ibd->ib_notify_body)(txndata->tx->ib, txndata->tx, buf, nbytes); if (rc != IB_OK) { ib_log_error_tx(txndata->tx, "Error %d notifying body data.", rc); } rc = buffer_data_chunk(fctx, input_reader, nbytes); switch (rc) { case IB_EAGAIN: case IB_OK: break; case IB_EBADVAL: ib_log_error_tx(txndata->tx, "Bad/Inconsistent stream edit(s) ignored."); break; default: /* Can't happen unless a new status is introduced */ ib_log_error_tx(txndata->tx, "Unhandled return value %d", rc); break; } TSIOBufferReaderConsume(input_reader, nbytes); TSVIONDoneSet(input_vio, TSVIONDoneGet(input_vio) + nbytes); } ntodo = TSVIONTodoGet(input_vio); if (ntodo == 0) { ib_log_debug2_tx(txndata->tx, "ntodo zero after consuming data"); flush_data(fctx, -1, 1); /* Call back the input VIO continuation to let it know that we * have completed the write operation. */ TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_COMPLETE, input_vio); } else { /* Call back the input VIO continuation to let it know that we * are ready for more data. */ TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_READY, input_vio); } }
/** * 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"); } } } }
/** * Set the matches into the given field name as .0, .1, .2 ... .9. * * @param[in] tx Current transaction. * @param[in] capture Collection to capture to. * @param[in] ovector The vector of integer pairs of matches from PCRE. * @param[in] matches The number of matches. * @param[in] subject The matched-against string data. * * @returns IB_OK or IB_EALLOC. */ static ib_status_t pcre_set_matches( const ib_tx_t *tx, ib_field_t *capture, int *ovector, int matches, const char *subject ) { assert(tx != NULL); assert(tx->ib != NULL); assert(capture != NULL); assert(ovector != NULL); ib_status_t rc; int i; rc = ib_capture_clear(capture); if (rc != IB_OK) { ib_log_error_tx(tx, "Error clearing captures: %s", ib_status_to_string(rc)); } /* We have a match! Now populate TX:0-9 in tx->data. */ ib_log_debug2_tx(tx, "REGEX populating %d matches", matches); for (i = 0; i < matches; ++i) { /* The length of the match. */ size_t match_len; /* The first character in the match. */ const char *match_start; /* Field name */ const char *name; /* Holder for a copy of the field value when creating a new field. */ ib_bytestr_t *bs; /* Field holder. */ ib_field_t *field; /* Readability. Mark the start and length of the string. */ match_start = subject+ovector[i*2]; match_len = ovector[i*2+1] - ovector[i*2]; /* Create a byte-string representation */ rc = ib_bytestr_dup_mem(&bs, tx->mp, (const uint8_t*)match_start, match_len); if (rc != IB_OK) { return rc; } /* Create a field to hold the byte-string */ name = ib_capture_name(i); rc = ib_field_create(&field, tx->mp, name, strlen(name), IB_FTYPE_BYTESTR, ib_ftype_bytestr_in(bs)); if (rc != IB_OK) { return rc; } /* Add it to the capture collection */ rc = ib_capture_set_item(capture, i, tx->mp, field); if (rc != IB_OK) { return rc; } } return IB_OK; }
/** * @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[in] 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, 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; size_t subject_len; const ib_bytestr_t* bytestr; pcre_rule_data_t *rule_data = (pcre_rule_data_t *)data; pcre *regex; pcre_extra *regex_extra = 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) { IB_FTRACE_RET_STATUS(ib_rc); } 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) { IB_FTRACE_RET_STATUS(ib_rc); } subject_len = ib_bytestr_length(bytestr); subject = (const char *) ib_bytestr_const_ptr(bytestr); } else { free(ovector); IB_FTRACE_RET_STATUS(IB_EALLOC); } /* 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 ); } } /* Alloc space to copy regex. */ regex = (pcre *)malloc(rule_data->cpatt_sz); if (regex == NULL ) { free(ovector); IB_FTRACE_RET_STATUS(IB_EALLOC); } memcpy(regex, rule_data->cpatt, rule_data->cpatt_sz); if (rule_data->study_data_sz == 0 ) { regex_extra = NULL; } else { regex_extra = (pcre_extra *) malloc(sizeof(*regex_extra)); if (regex_extra == NULL ) { free(ovector); free(regex); IB_FTRACE_RET_STATUS(IB_EALLOC); } *regex_extra = *rule_data->edata; if ( rule_data->study_data_sz == 0 ) { regex_extra->study_data = NULL; } else { regex_extra->study_data = malloc(rule_data->study_data_sz); if (regex_extra->study_data == NULL ) { free(ovector); if (regex_extra != NULL) { free(regex_extra); } free(regex); IB_FTRACE_RET_STATUS(IB_EALLOC); } memcpy(regex_extra->study_data, rule_data->edata->study_data, rule_data->study_data_sz); } /* Put some modest limits on our regex. */ regex_extra->match_limit = 1000; regex_extra->match_limit_recursion = 1000; regex_extra->flags = regex_extra->flags | PCRE_EXTRA_MATCH_LIMIT | PCRE_EXTRA_MATCH_LIMIT_RECURSION; } #ifdef PCRE_JIT_STACK if (jit_stack == NULL) { if ( regex_extra != NULL ) { if ( regex_extra->study_data != NULL ) { free(regex_extra->study_data); } free(regex_extra); } free(ovector); free(regex); IB_FTRACE_RET_STATUS(IB_EALLOC); } pcre_assign_jit_stack(regex_extra, NULL, jit_stack); #endif matches = pcre_exec(regex, regex_extra, subject, subject_len, 0, /* Starting offset. */ 0, /* Options. */ ovector, ovecsize); #ifdef PCRE_JIT_STACK pcre_jit_stack_free(jit_stack); #endif if (matches > 0) { pcre_set_matches(ib, tx, "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; } if ( regex_extra != NULL ) { if ( regex_extra->study_data != NULL ) { free(regex_extra->study_data); } free(regex_extra); } free(ovector); free(regex); IB_FTRACE_RET_STATUS(ib_rc); }
/** * Set the matches into the given field name as .0, .1, .2 ... .9. * * @param[in] ib The IronBee engine to log to. * @param[in] tx The transaction to store the values into (tx->dpi). * @param[in] field_name The field to populate with Regex matches. * @param[in] ovector The vector of integer pairs of matches from PCRE. * @param[in] matches The number of matches. * @param[in] subject The matched-against string data. * * @returns IB_OK or IB_EALLOC. */ static ib_status_t pcre_set_matches(ib_engine_t *ib, ib_tx_t *tx, const char* field_name, int *ovector, int matches, const char *subject) { IB_FTRACE_INIT(); /* IronBee status. */ ib_status_t rc; /* Iterator. */ int i; /* Length of field_name. */ const int field_name_sz = strlen(field_name); /* The length of the match. */ size_t match_len; /* The first character in the match. */ const char* match_start; /* +3 = '.', [digit], and \0. */ char *full_field_name = malloc(field_name_sz+3); /* Holder to build an optional debug message in. */ char *debug_msg; /* Holder for a copy of the field value when creating a new field. */ ib_bytestr_t *field_value; /* Field holder. */ ib_field_t *ib_field; /* Ensure the above allocations happened. */ if (full_field_name==NULL) { IB_FTRACE_RET_STATUS(IB_EALLOC); } rc = ensure_field_exists(ib, tx, field_name); if (rc != IB_OK) { ib_log_alert_tx(tx, "Could not ensure that field %s was a list.", field_name); free(full_field_name); IB_FTRACE_RET_STATUS(IB_EINVAL); } /* We have a match! Now populate TX.0-9 in tx->dpi. */ for (i=0; i<matches; i++) { /* Build the field name. Typically TX.0, TX.1 ... TX.9 */ sprintf(full_field_name, "%s.%d", field_name, i); /* Readability. Mark the start and length of the string. */ match_start = subject+ovector[i*2]; match_len = ovector[i*2+1] - ovector[i*2]; /* If debugging this, copy the string value out and print it to the * log. This could be dangerous as there could be non-character * values in the match. */ if (ib_log_get_level(ib) >= 7) { debug_msg = malloc(match_len+1); /* Notice: Don't provoke a crash if malloc fails. */ if (debug_msg != NULL) { memcpy(debug_msg, match_start, match_len); debug_msg[match_len] = '\0'; ib_log_debug2_tx(tx, "REGEX Setting %s=%s", full_field_name, debug_msg); free(debug_msg); } } ib_data_get(tx->dpi, full_field_name, &ib_field); if (ib_field == NULL) { ib_data_add_bytestr(tx->dpi, full_field_name, (uint8_t*)subject+ovector[i*2], match_len, NULL); } else { ib_bytestr_dup_mem(&field_value, tx->mp, (const uint8_t*)match_start, match_len); ib_field_setv_no_copy( ib_field, ib_ftype_bytestr_mutable_in(field_value) ); } } IB_FTRACE_RET_STATUS(IB_OK); }