/** * Configuration function to select ident provider * Implements IdentType directive * * @param cp IronBee configuration parser * @param name Unused * @param p1 Select ident provider * @param p2 Optional. If set to "any", ident will be checked by all * available providers if the configured provider doesn't * identify. Expected to be used in "Log" mode. * @param dummy Unused * @return status */ static ib_status_t ident_type(ib_cfgparser_t *cp, const char *name, const char *p1, const char *p2, void *dummy) { ident_cfg_t *cfg; ib_status_t rc; ib_module_t *m; char *p; rc = ib_engine_module_get(cp->ib, MODULE_NAME_STR, &m); assert((rc == IB_OK) && (m != NULL)); rc = ib_context_module_config(ib_context_main(cp->ib), m, &cfg); assert((rc == IB_OK) && (cfg != NULL)); cfg->type = p = ib_mm_strdup(cp->mm, p1); assert(p != NULL); do { if (isupper(*p)) { *p = tolower(*p); } } while (*++p); if (p2 && !strcasecmp(p2, "any")) { cfg->accept_any = 1; } else { cfg->accept_any = 0; } return rc; }
/** * Configuration function to select what ident regime to operate * Implements IdentMode directive * * @param cp IronBee configuration parser * @param name Unused * @param p1 Select whether ident is "Off" (do nothing), * "Log" (Log user id or unidentified) or * "Require" (Log id and issue challenge if unidentified) * @param dummy Unused * @return OK, or EINVAL if p1 is unrecognized */ static ib_status_t ident_mode(ib_cfgparser_t *cp, const char *name, const char *p1, void *dummy) { ident_cfg_t *cfg; ib_status_t rc; ib_module_t *m; rc = ib_engine_module_get(cp->ib, MODULE_NAME_STR, &m); assert((rc == IB_OK) && (m != NULL)); rc = ib_context_module_config(ib_context_main(cp->ib), m, &cfg); assert((rc == IB_OK) && (cfg != NULL)); if (!strcasecmp(p1, "Off")) { cfg->mode = ident_off; } else if (!strcasecmp(p1, "Log")) { cfg->mode = ident_log; } else if (!strcasecmp(p1, "Require")) { cfg->mode = ident_require; } else { rc = IB_EINVAL; } return rc; }
void DLL_PUBLIC ib_vlog_ex(ib_engine_t *ib, int level, const ib_tx_t *tx, const char *prefix, const char *file, int line, const char *fmt, va_list ap) { IB_FTRACE_INIT(); IB_PROVIDER_API_TYPE(logger) *api; ib_core_cfg_t *corecfg; ib_provider_inst_t *pi = NULL; ib_status_t rc; ib_context_t *ctx; ctx = ib_context_main(ib); if (ctx != NULL) { rc = ib_context_module_config(ctx, ib_core_module(), (void *)&corecfg); if (rc == IB_OK) { pi = corecfg->pi.logger; } if (pi != NULL) { api = (IB_PROVIDER_API_TYPE(logger) *)pi->pr->api; api->vlogmsg(pi, ctx, level, tx, prefix, file, line, fmt, ap); IB_FTRACE_RET_VOID(); }
/** * Access configuration data. * * @param[in] ib IronBee engine. * @return * - configuration on success. * - NULL on failure. */ static ee_config_t *ee_get_config( ib_engine_t *ib ) { assert(ib != NULL); ib_module_t *module; ib_context_t *context; ib_status_t rc; ee_config_t *config; rc = ib_engine_module_get(ib, MODULE_NAME_STR, &module); if (rc != IB_OK) { return NULL; } context = ib_context_main(ib); if (context == NULL) { return NULL; } rc = ib_context_module_config(context, module, &config); if (rc != IB_OK) { return NULL; } return config; }
ib_status_t ib_cfgparser_create(ib_cfgparser_t **pcp, ib_engine_t *ib) { IB_FTRACE_INIT(ib_cfgparser_create); ib_mpool_t *pool; ib_status_t rc; /* Create parser memory pool */ rc = ib_mpool_create(&pool, ib->mp); if (rc != IB_OK) { rc = IB_EALLOC; goto failed; } /* Create the main structure in the memory pool */ *pcp = (ib_cfgparser_t *)ib_mpool_calloc(pool, 1, sizeof(**pcp)); if (*pcp == NULL) { rc = IB_EALLOC; goto failed; } (*pcp)->ib = ib; (*pcp)->mp = pool; /* Create the stack */ rc = ib_list_create(&((*pcp)->stack), pool); if (rc != IB_OK) { goto failed; } (*pcp)->cur_ctx = ib_context_main(ib); ib_list_push((*pcp)->stack, (*pcp)->cur_ctx); /* Create the block tracking list */ rc = ib_list_create(&((*pcp)->block), pool); if (rc != IB_OK) { goto failed; } /* Other fields are NULLed via calloc */ ib_log_debug(ib, 9, "Stack: ctx=%p site=%p(%s) loc=%p(%s)", (*pcp)->cur_ctx, (*pcp)->cur_site, (*pcp)->cur_site?(*pcp)->cur_site->name:"NONE", (*pcp)->cur_loc, (*pcp)->cur_loc?(*pcp)->cur_loc->path:"/"); IB_FTRACE_RET_STATUS(rc); failed: /* Make sure everything is cleaned up on failure */ if (pool != NULL) { ib_mpool_destroy(pool); } *pcp = NULL; IB_FTRACE_RET_STATUS(rc); }
TEST_F(TestIronBeeModuleRulesLua, operator_test) { const ib_operator_t *op; void *instance_data; ib_num_t result; ib_field_t* field1; const char* op_name = "test_module_rules_lua.lua"; const char* rule_name = "luarule001"; char* str1 = (char *)ib_mpool_strdup(ib_engine->mp, "string 1"); ASSERT_TRUE(str1); // Create field 1. ASSERT_EQ(IB_OK, ib_field_create( &field1, ib_engine->mp, IB_FIELD_NAME("field1"), IB_FTYPE_NULSTR, ib_ftype_nulstr_in(str1) ) ); /* Configure the operator. */ configureIronBee(); // Ensure that the operator exists. ASSERT_EQ(IB_OK, ib_operator_lookup(ib_engine, op_name, &op) ); ASSERT_EQ(IB_OK, ib_operator_inst_create(op, ib_context_main(ib_engine), IB_OP_CAPABILITY_NON_STREAM, rule_name, &instance_data)); performTx(); // Attempt to match. ASSERT_EQ(IB_OK, ib_operator_inst_execute(op, instance_data, ib_tx, field1, NULL, &result)); // This time we should succeed. ASSERT_TRUE(result); }
/** * Handle on/off directives. * * @param[in] cp Config parser * @param[in] name Directive name * @param[in] onoff on/off flag * @param[in] cbdata Callback data (ignored) * * @returns Status code */ static ib_status_t handle_directive_onoff(ib_cfgparser_t *cp, const char *name, int onoff, void *cbdata) { assert(cp != NULL); assert(name != NULL); assert(cp->ib != NULL); ib_engine_t *ib = cp->ib; ib_status_t rc; ib_module_t *module = NULL; modpcre_cfg_t *config = NULL; ib_context_t *ctx = cp->cur_ctx ? cp->cur_ctx : ib_context_main(ib); const char *pname; /* Get my module object */ rc = ib_engine_module_get(cp->ib, MODULE_NAME_STR, &module); if (rc != IB_OK) { ib_cfg_log_error(cp, "Failed to get %s module object: %s", MODULE_NAME_STR, ib_status_to_string(rc)); return rc; } /* Get my module configuration */ rc = ib_context_module_config(ctx, module, (void *)&config); if (rc != IB_OK) { ib_cfg_log_error(cp, "Failed to get %s module configuration: %s", MODULE_NAME_STR, ib_status_to_string(rc)); return rc; } if (strcasecmp("PcreStudy", name) == 0) { pname = MODULE_NAME_STR ".study"; } else if (strcasecmp("PcreUseJit", name) == 0) { pname = MODULE_NAME_STR ".use_jit"; } else { ib_cfg_log_error(cp, "Unhandled directive \"%s\"", name); return IB_EINVAL; } rc = ib_context_set_num(ctx, pname, onoff); if (rc != IB_OK) { ib_cfg_log_error(cp, "Failed to set \"%s\" to %s for \"%s\": %s", pname, onoff ? "true" : "false", name, ib_status_to_string(rc)); } return IB_OK; }
/** * Function exported to enable a module to register an ident provider * * @param engine Engine to register with * @param name Provider name (referenced in IdentType directive) * @param provider The identity provider * @return status */ ib_status_t ib_ident_provider_register(ib_engine_t *engine, const char *name, ib_ident_provider_t *provider) { ident_cfg_t *cfg; ib_status_t rc; ib_module_t *m; rc = ib_engine_module_get(engine, MODULE_NAME_STR, &m); assert((rc == IB_OK) && (m != NULL)); rc = ib_context_module_config(ib_context_main(engine), m, &cfg); assert((rc == IB_OK) && (cfg != NULL)); if (cfg->providers == NULL) { rc = ib_hash_create(&cfg->providers, ib_engine_mm_main_get(engine)); assert((rc == IB_OK) && (cfg->providers != NULL)); } return ib_hash_set(cfg->providers, name, provider); }
TEST_F(CoreOperatorsTest, ContainsTest) { ib_status_t status; ib_num_t call_result; const ib_operator_t *op; void *instance_data; status = ib_operator_lookup(ib_engine, "contains", &op); ASSERT_EQ(IB_OK, status); status = ib_operator_inst_create(op, ib_context_main(ib_engine), IB_OP_CAPABILITY_NON_STREAM, "needle", &instance_data); ASSERT_EQ(IB_OK, status); // call contains ib_field_t *field; const char *matching = "data with needle in it"; const char *nonmatching = "non matching string"; ib_field_create( &field, ib_engine_pool_main_get(ib_engine), IB_FIELD_NAME("testfield"), IB_FTYPE_NULSTR, NULL ); ib_field_setv(field, ib_ftype_nulstr_in(matching)); status = ib_operator_inst_execute(op, instance_data, ib_tx, field, NULL, &call_result); ASSERT_EQ(IB_OK, status); EXPECT_EQ(1, call_result); ib_field_setv(field, ib_ftype_nulstr_in(nonmatching)); status = ib_operator_inst_execute(op, instance_data, ib_tx, field, NULL, &call_result); ASSERT_EQ(IB_OK, status); EXPECT_EQ(0, call_result); }
TEST_F(CoreOperatorsTest, NeTest) { ib_status_t status; ib_num_t call_result; const ib_operator_t *op; void *instance_data; status = ib_operator_lookup(ib_engine, "ne", &op); ASSERT_EQ(IB_OK, status); status = ib_operator_inst_create(op, ib_context_main(ib_engine), IB_OP_CAPABILITY_NON_STREAM, "1", &instance_data); ASSERT_EQ(IB_OK, status); // call contains ib_field_t *field; const ib_num_t matching = 2; const ib_num_t nonmatching = 1; ib_field_create( &field, ib_engine_pool_main_get(ib_engine), IB_FIELD_NAME("testfield"), IB_FTYPE_NUM, ib_ftype_num_in(&matching) ); ib_field_setv(field, ib_ftype_num_in(&matching)); status = ib_operator_inst_execute(op, instance_data, ib_tx, field, NULL, &call_result); ASSERT_EQ(IB_OK, status); EXPECT_EQ(1, call_result); ib_field_setv(field, ib_ftype_num_in(&nonmatching)); status = ib_operator_inst_execute(op, instance_data, ib_tx, field, NULL, &call_result); ASSERT_EQ(IB_OK, status); EXPECT_EQ(0, call_result); }
/** * @internal * Handle a PocSigTrace directive. * * @param cp Config parser * @param name Directive name * @param p1 First parameter * @param cbdata Callback data (from directive registration) * * @returns Status code */ static ib_status_t pocsig_dir_trace(ib_cfgparser_t *cp, const char *name, const char *p1, void *cbdata) { IB_FTRACE_INIT(pocsig_dir_trace); ib_engine_t *ib = cp->ib; ib_context_t *ctx = cp->cur_ctx ? cp->cur_ctx : ib_context_main(ib); ib_status_t rc; ib_log_debug(ib, 7, "%s: \"%s\" ctx=%p", name, p1, ctx); if (strcasecmp("On", p1) == 0) { rc = ib_context_set_num(ctx, MODULE_NAME_STR ".trace", 1); IB_FTRACE_RET_STATUS(rc); } else if (strcasecmp("Off", p1) == 0) { rc = ib_context_set_num(ctx, MODULE_NAME_STR ".trace", 0); IB_FTRACE_RET_STATUS(rc); } ib_log_error(ib, 1, "Failed to parse directive: %s \"%s\"", name, p1); IB_FTRACE_RET_STATUS(IB_EINVAL); }
Context ConstEngine::main_context() const { return Context(ib_context_main(ib())); }
/** * 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"); } } } }
TEST_F(PcreModuleTest, test_pcre_operator) { ib_field_t *outfield; ib_num_t result; ib_field_t *capture; const ib_operator_t *op; ib_operator_inst_t *opinst; ASSERT_EQ(IB_OK, ib_operator_lookup(ib_engine, IB_S2SL("pcre"), &op)); // Create the operator instance. ASSERT_EQ( IB_OK, ib_operator_inst_create( &opinst, ib_engine_mm_main_get(ib_engine), ib_context_main(ib_engine), op, IB_OP_CAPABILITY_NONE, "string\\s2" ) ); // Attempt to match. ASSERT_EQ( IB_OK, ib_operator_inst_execute( opinst, rule_exec1.tx, field1, NULL, &result ) ); // We should fail. ASSERT_FALSE(result); // Attempt to match again. ASSERT_EQ( IB_OK, ib_operator_inst_execute( opinst, rule_exec1.tx, field2, NULL, &result ) ); // This time we should succeed. ASSERT_TRUE(result); // Should be no capture set */ outfield = getTarget1(IB_TX_CAPTURE":0"); ASSERT_FALSE(outfield); // Create the operator instance. ASSERT_EQ( IB_OK, ib_operator_inst_create( &opinst, ib_engine_mm_main_get(ib_engine), ib_context_main(ib_engine), op, IB_OP_CAPABILITY_NONE, "(string 2)" ) ); // Attempt to match. ASSERT_EQ( IB_OK, ib_operator_inst_execute( opinst, rule_exec1.tx, field1, NULL, &result ) ); // We should fail. ASSERT_FALSE(result); // Attempt to match again. ASSERT_EQ( IB_OK, ib_operator_inst_execute( opinst, rule_exec1.tx, field2, NULL, &result ) ); // This time we should succeed. ASSERT_TRUE(result); // Should be no capture (CAPTURE flag not set for rule 1) outfield = getTarget1(IB_TX_CAPTURE":0"); ASSERT_FALSE(outfield); // Create the operator instance. ASSERT_EQ( IB_OK, ib_operator_inst_create( &opinst, ib_engine_mm_main_get(ib_engine), ib_context_main(ib_engine), op, IB_OP_CAPABILITY_NONE, "(string 2)" ) ); ASSERT_EQ(IB_OK, ib_capture_acquire( rule_exec2.tx, NULL, &capture)); // Attempt to match again. ASSERT_EQ( IB_OK, ib_operator_inst_execute( opinst, rule_exec1.tx, field2, capture, &result ) ); // This time we should succeed. ASSERT_TRUE(result); // Should be a capture (CAPTURE flag is set for rule 2) outfield = getTarget1(IB_TX_CAPTURE":0"); ASSERT_TRUE(outfield); }
/** * Handle single parameter directives. * * @param cp Config parser * @param name Directive name * @param p1 First parameter * @param cbdata Callback data (from directive registration) * * @returns Status code */ static ib_status_t handle_directive_param(ib_cfgparser_t *cp, const char *name, const char *p1, void *cbdata) { assert(cp != NULL); assert(name != NULL); assert(p1 != NULL); assert(cp->ib != NULL); ib_engine_t *ib = cp->ib; ib_status_t rc; ib_module_t *module = NULL; modpcre_cfg_t *config = NULL; ib_context_t *ctx = cp->cur_ctx ? cp->cur_ctx : ib_context_main(ib); const char *pname; ib_num_t value; /* Get my module object */ rc = ib_engine_module_get(cp->ib, MODULE_NAME_STR, &module); if (rc != IB_OK) { ib_cfg_log_error(cp, "Failed to get %s module object: %s", MODULE_NAME_STR, ib_status_to_string(rc)); return rc; } /* Get my module configuration */ rc = ib_context_module_config(ctx, module, (void *)&config); if (rc != IB_OK) { ib_cfg_log_error(cp, "Failed to get %s module configuration: %s", MODULE_NAME_STR, ib_status_to_string(rc)); return rc; } /* p1 should be a number */ rc = ib_string_to_num(p1, 0, &value); if (rc != IB_OK) { ib_cfg_log_error(cp, "Failed to convert \"%s\" to a number for \"%s\": %s", p1, name, ib_status_to_string(rc)); return rc; } if (strcasecmp("PcreMatchLimit", name) == 0) { pname = "pcre.match_limit"; } else if (strcasecmp("PcreMatchLimitRecursion", name) == 0) { pname = "pcre.match_limit_recursion"; } else if (strcasecmp("PcreJitStackStart", name) == 0) { pname = "pcre.jit_stack_start"; } else if (strcasecmp("PcreJitStackMax", name) == 0) { pname = "pcre.jit_stack_max"; } else if (strcasecmp("PcreDfaWorkspaceSize", name) == 0) { pname = "pcre.dfa_workspace_size"; } else { ib_cfg_log_error(cp, "Unhandled directive \"%s\"", name); return IB_EINVAL; } rc = ib_context_set_num(ctx, pname, value); if (rc != IB_OK) { ib_cfg_log_error(cp, "Failed to set \"%s\" to %ld for \"%s\": %s", pname, (long int)value, name, ib_status_to_string(rc)); } return IB_OK; }
/** * @internal * Handle a PocSig directive. * * @param cp Config parser * @param name Directive name * @param args List of directive arguments * @param cbdata Callback data (from directive registration) * * @returns Status code */ static ib_status_t pocsig_dir_signature(ib_cfgparser_t *cp, const char *name, ib_list_t *args, void *cbdata) { IB_FTRACE_INIT(pocsig_dir_signature); ib_engine_t *ib = cp->ib; ib_context_t *ctx = cp->cur_ctx ? cp->cur_ctx : ib_context_main(ib); ib_list_t *list; const char *target; const char *op; const char *action; pocsig_cfg_t *cfg; pocsig_phase_t phase; pocsig_sig_t *sig; const char *errptr; int erroff; ib_status_t rc; /* Get the pocsig configuration for this context. */ rc = ib_context_module_config(ctx, IB_MODULE_STRUCT_PTR, (void *)&cfg); if (rc != IB_OK) { ib_log_error(ib, 1, "Failed to fetch %s config: %d", MODULE_NAME_STR, rc); } /* Setup the PCRE matcher. */ if (cfg->pcre == NULL) { rc = ib_matcher_create(ib, ib_engine_pool_config_get(ib), "pcre", &cfg->pcre); if (rc != IB_OK) { ib_log_error(ib, 2, "Could not create a PCRE matcher: %d", rc); IB_FTRACE_RET_STATUS(rc); } } /* Determine phase and initialize the phase list if required. */ if (strcasecmp("PocSigPreTx", name) == 0) { phase = POCSIG_PRE; if (cfg->phase[phase] == NULL) { rc = ib_list_create(cfg->phase + POCSIG_PRE, ib_engine_pool_config_get(ib)); if (rc != IB_OK) { IB_FTRACE_RET_STATUS(rc); } } } else if (strcasecmp("PocSigReqHead", name) == 0) { phase = POCSIG_REQHEAD; if (cfg->phase[phase] == NULL) { ib_log_debug(ib, 4, "Creating list for phase=%d", phase); rc = ib_list_create(&list, ib_engine_pool_config_get(ib)); if (rc != IB_OK) { IB_FTRACE_RET_STATUS(rc); } ib_log_debug(ib, 4, "List for phase=%d list=%p", phase, list); cfg->phase[phase] = list; } } else if (strcasecmp("PocSigReq", name) == 0) { phase = POCSIG_REQ; if (cfg->phase[phase] == NULL) { rc = ib_list_create(&cfg->phase[phase], ib_engine_pool_config_get(ib)); if (rc != IB_OK) { IB_FTRACE_RET_STATUS(rc); } } } else if (strcasecmp("PocSigResHead", name) == 0) { phase = POCSIG_RESHEAD; if (cfg->phase[phase] == NULL) { rc = ib_list_create(&cfg->phase[phase], ib_engine_pool_config_get(ib)); if (rc != IB_OK) { IB_FTRACE_RET_STATUS(rc); } } } else if (strcasecmp("PocSigRes", name) == 0) { phase = POCSIG_RES; if (cfg->phase[POCSIG_RES] == NULL) { rc = ib_list_create(&cfg->phase[POCSIG_RES], ib_engine_pool_config_get(ib)); if (rc != IB_OK) { IB_FTRACE_RET_STATUS(rc); } } } else if (strcasecmp("PocSigPostTx", name) == 0) { phase = POCSIG_POST; if (cfg->phase[phase] == NULL) { rc = ib_list_create(&cfg->phase[phase], ib_engine_pool_config_get(ib)); if (rc != IB_OK) { IB_FTRACE_RET_STATUS(rc); } } } else { ib_log_error(ib, 2, "Invalid signature: %s", name); IB_FTRACE_RET_STATUS(IB_EINVAL); } /* Target */ rc = ib_list_shift(args, &target); if (rc != IB_OK) { ib_log_error(ib, 1, "No PocSig target"); IB_FTRACE_RET_STATUS(IB_EINVAL); } /* Operator */ rc = ib_list_shift(args, &op); if (rc != IB_OK) { ib_log_error(ib, 1, "No PocSig operator"); IB_FTRACE_RET_STATUS(IB_EINVAL); } /* Action */ rc = ib_list_shift(args, &action); if (rc != IB_OK) { ib_log_debug(ib, 4, "No PocSig action"); action = ""; } /* Signature */ sig = (pocsig_sig_t *)ib_mpool_alloc(ib_engine_pool_config_get(ib), sizeof(*sig)); if (sig == NULL) { IB_FTRACE_RET_STATUS(IB_EALLOC); } sig->target = ib_mpool_strdup(ib_engine_pool_config_get(ib), target); sig->patt = ib_mpool_strdup(ib_engine_pool_config_get(ib), op); sig->emsg = ib_mpool_strdup(ib_engine_pool_config_get(ib), action); /* Compile the PCRE patt. */ if (cfg->pcre == NULL) { ib_log_error(ib, 2, "No PCRE matcher available (load the pcre module?)"); IB_FTRACE_RET_STATUS(IB_EINVAL); } sig->cpatt = ib_matcher_compile(cfg->pcre, sig->patt, &errptr, &erroff); if (sig->cpatt == NULL) { ib_log_error(ib, 2, "Error at offset=%d of PCRE patt=\"%s\": %s", erroff, sig->patt, errptr); IB_FTRACE_RET_STATUS(IB_EINVAL); } ib_log_debug(ib, 4, "POCSIG: \"%s\" \"%s\" \"%s\" phase=%d ctx=%p", target, op, action, phase, ctx); /* Add the signature to the phase list. */ rc = ib_list_push(cfg->phase[phase], sig); if (rc != IB_OK) { ib_log_error(ib, 1, "Failed to add signature"); IB_FTRACE_RET_STATUS(rc); } IB_FTRACE_RET_STATUS(IB_OK); }
/** * @internal * Initialize a context for the perf_stats module. * * This is a hack. * Hook callbacks set here to get end times for hooks. * Currently no other modules register hook call backs. * Because of this it should * ensure that we are the last thing called per event. * * @param[in] ib IronBee object * @param[in] m Module object * @param[in] ctx Context object * @param[in] cbdata Callback data (unused) */ static ib_status_t perf_stats_context_close(ib_engine_t *ib, ib_module_t *m, ib_context_t *ctx, void *cbdata) { IB_FTRACE_INIT(); ib_status_t rc; int event; /* Check that we are in the main ctx otherwise return */ if (ctx != ib_context_main(ib)) { IB_FTRACE_RET_STATUS(IB_OK); } for (event = 0; event < IB_STATE_EVENT_NUM; ++event) { event_info_t *eventp = &event_info[event]; if ((eventp->cbdata_type == IB_CBDATA_NONE) || (eventp->cbdata_type == IB_CBDATA_CONN_DATA_T)) { rc = IB_EINVAL; ib_log_error(ib, "Cannot register handler " "for:%d name:%s cbdata_type: %d", eventp->number, eventp->name, eventp->cbdata_type); } else { switch( ib_state_hook_type( (ib_state_event_type_t)event ) ) { case IB_STATE_HOOK_CONN: rc = ib_hook_conn_register( ib, (ib_state_event_type_t)event, mod_perf_stats_event_stop_conn_callback, (void *)eventp ); break; case IB_STATE_HOOK_CONNDATA: rc = ib_hook_conndata_register( ib, (ib_state_event_type_t)event, mod_perf_stats_event_stop_conndata_callback, (void *)eventp ); break; case IB_STATE_HOOK_TX: rc = ib_hook_tx_register( ib, (ib_state_event_type_t)event, mod_perf_stats_event_stop_tx_callback, (void *)eventp ); break; case IB_STATE_HOOK_TXDATA: rc = ib_hook_txdata_register( ib, (ib_state_event_type_t)event, mod_perf_stats_event_stop_txdata_callback, (void *)eventp ); break; default: rc = IB_EINVAL; ib_log_error(ib, "Event with unknown hook type: %d/%s", eventp->number, eventp->name); } } if (rc != IB_OK) { ib_log_error(ib, "Hook register for " "event:%d name:%s cbdata_type: %d returned %s", eventp->number, eventp->name, eventp->cbdata_type, ib_status_to_string(rc)); } } IB_FTRACE_RET_STATUS(IB_OK); }
/** * Handle request_header events for remote IP extraction. * * Extract the "request_headers" field (a list) from the transactions's * data provider instance, then loop through the list, looking for the * "X-Forwarded-For" field. If found, the first value in the (comma * separated) list replaces the local ip address string in the connection * object. * * @param[in] ib IronBee object * @param[in,out] tx Transaction object * @param[in] event Event type * @param[in] cbdata Callback data (module) * * @returns Status code */ static ib_status_t modua_remoteip(ib_engine_t *ib, ib_tx_t *tx, ib_state_event_type_t event, void *cbdata) { assert(ib != NULL); assert(tx != NULL); assert(tx->var_store != NULL); assert(event == request_header_finished_event); const ib_module_t *m = (const ib_module_t *)cbdata; ib_field_t *field = NULL; ib_status_t rc = IB_OK; const ib_bytestr_t *bs; const uint8_t *data; size_t len; char *buf; uint8_t *comma; const ib_list_t *list; const ib_list_node_t *node; const ib_field_t *forwarded; uint8_t *stripped; size_t num; ib_flags_t flags; const modua_config_t *cfg; rc = ib_context_module_config(ib_context_main(ib), m, &cfg); if (rc != IB_OK) { ib_log_error_tx(tx, "Can't fetch configuration: %s", ib_status_to_string(rc)); return rc; } ib_log_debug3_tx(tx, "Checking for alternate remote address"); /* Extract the X-Forwarded-For header field */ rc = ib_var_target_get_const( cfg->forwarded_for, &list, tx->mp, tx->var_store ); if (rc == IB_ENOENT || ib_list_elements(list) == 0) { ib_log_debug_tx(tx, "No X-Forwarded-For"); return IB_OK; } if (rc != IB_OK) { ib_log_error_tx(tx, "Cannot retrieve request_headers:User-Agent: %d", rc); return rc; } num = ib_list_elements(list); if (num == 0) { ib_log_debug_tx(tx, "No X-Forwarded-For header found"); return rc; } else if (num != 1) { ib_log_debug_tx(tx, "%zd X-Forwarded-For headers found: ignoring", num); return rc; } node = ib_list_last_const(list); if ( (node == NULL) || (node->data == NULL) ) { ib_log_notice_tx(tx, "Invalid X-Forwarded-For header found"); return rc; } forwarded = (const ib_field_t *)node->data; /* Found it: copy the data into a newly allocated string buffer */ rc = ib_field_value_type(forwarded, ib_ftype_bytestr_out(&bs), IB_FTYPE_BYTESTR); if (rc != IB_OK) { ib_log_notice_tx(tx, "Invalid X-Forwarded-For header value"); return rc; } if (bs == NULL) { ib_log_notice_tx(tx, "X-Forwarded-For header not a bytestr"); return IB_EINVAL; } len = ib_bytestr_length(bs); data = ib_bytestr_const_ptr(bs); /* Search for a comma in the buffer */ comma = memchr(data, ',', len); if (comma != NULL) { len = comma - data; } /* Trim whitespace */ stripped = (uint8_t *)data; rc = ib_strtrim_lr_ex(IB_STROP_INPLACE, tx->mp, stripped, len, &stripped, &len, &flags); if (rc != IB_OK) { return rc; } /* Verify that it looks like a valid IP v4/6 address */ rc = ib_ip_validate_ex((const char *)stripped, len); if (rc != IB_OK) { ib_log_error_tx(tx, "X-Forwarded-For \"%.*s\" is not a valid IP address", (int)len, stripped ); return IB_OK; } /* Allocate memory for copy of stripped string */ buf = (char *)ib_mpool_alloc(tx->mp, len+1); if (buf == NULL) { ib_log_error_tx(tx, "Failed to allocate %zd bytes for remote address", len+1); return IB_EALLOC; } /* Copy the string out */ memcpy(buf, stripped, len); buf[len] = '\0'; ib_log_debug_tx(tx, "Remote address changed to \"%s\"", buf); /* This will lose the pointer to the original address * buffer, but it should be cleaned up with the rest * of the memory pool. */ tx->er_ipstr = buf; /* Update the remote address field in the tx collection */ rc = ib_field_create_bytestr_alias( &field, tx->mp, "", 0, (uint8_t *)buf, len ); if (rc != IB_OK) { ib_log_error_tx(tx, "Failed to create field for remote_addr: %s", ib_status_to_string(rc)); return rc; } rc = ib_var_source_set(cfg->remote_addr, tx->var_store, field); if (rc != IB_OK) { ib_log_error_tx(tx, "Failed to set remote address var: %s", ib_status_to_string(rc)); return rc; } return IB_OK; }
/** * 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); }
static ib_status_t sqli_dir_fingerprint_set( ib_cfgparser_t *cp, const char *directive_name, const char *set_name, const char *set_path, void *cbdata ) { assert(cp != NULL); assert(directive_name != NULL); assert(set_name != NULL); assert(set_path != NULL); ib_status_t rc; ib_context_t *ctx = NULL; ib_module_t *m = NULL; sqli_module_config_t *cfg = NULL; sqli_fingerprint_set_t *ps = NULL; ib_mm_t mm; char *abs_set_path = NULL; rc = ib_cfgparser_context_current(cp, &ctx); assert(rc == IB_OK); assert(ctx != NULL); if (ctx != ib_context_main(cp->ib)) { ib_cfg_log_error(cp, "%s: Only valid at main context.", directive_name ); return IB_EINVAL; } if (strcmp("default", set_name) == 0) { ib_cfg_log_error(cp, "%s: default is a reserved set name.", directive_name ); return IB_EINVAL; } mm = ib_engine_mm_main_get(cp->ib); rc = ib_engine_module_get( ib_context_get_engine(ctx), MODULE_NAME_STR, &m ); assert(rc == IB_OK); rc = ib_context_module_config(ctx, m, &cfg); assert(rc == IB_OK); if (cfg->fingerprint_sets == NULL) { rc = ib_hash_create(&cfg->fingerprint_sets, mm); assert(rc == IB_OK); } assert(cfg->fingerprint_sets != NULL); rc = ib_hash_get(cfg->fingerprint_sets, NULL, set_name); if (rc == IB_OK) { ib_cfg_log_error(cp, "%s: Duplicate fingerprint set definition: %s", directive_name, set_name ); return IB_EINVAL; } assert(rc == IB_ENOENT); abs_set_path = ib_util_relative_file( ib_engine_mm_config_get(cp->ib), ib_cfgparser_curr_file(cp), set_path ); if (abs_set_path == NULL) { return IB_EALLOC; } rc = sqli_create_fingerprint_set_from_file(&ps, abs_set_path, mm); if (rc != IB_OK) { ib_cfg_log_error(cp, "%s: Failure to load fingerprint set from file: %s", directive_name, abs_set_path ); return IB_EINVAL; } assert(ps != NULL); rc = ib_hash_set(cfg->fingerprint_sets, ib_mm_strdup(mm, set_name), ps); assert(rc == IB_OK); return IB_OK; }
/** * Main identity handler. Called both on request_header_finished and * request_finished: the configured provider decides which state to * run on, and skips (returns immediately) on the other state. * * If configured mode is "Off", just returns. Otherwise calls provider's * check_id function to check and log user ID. Optionally cycles through * other providers. Finally, if client is not identified and mode is * "Require", calls provider's challenge function to ask client to * identify (e.g. HTTP 401). * * @param ib The engine * @param tx The transaction * @param state State that triggered the call * @param cbdata Unused */ static ib_status_t ident_handler(ib_engine_t *ib, ib_tx_t *tx, ib_state_t state, void *cbdata) { ident_cfg_t *cfg; const char *userid = NULL; ib_ident_provider_t *provider; ib_status_t rc; ib_module_t *m; assert(state == request_header_finished_state || state == request_finished_state); rc = ib_engine_module_get(ib, MODULE_NAME_STR, &m); assert((rc == IB_OK) && (m != NULL)); rc = ib_context_module_config(ib_context_main(ib), m, &cfg); assert((rc == IB_OK) && (cfg != NULL)); if (cfg->mode == ident_off) { return IB_OK; } if (cfg->type != NULL && cfg->providers != NULL) { rc = ib_hash_get(cfg->providers, &provider, cfg->type); if (rc != IB_OK || provider == NULL) { ib_log_error_tx(tx, "Identifier '%s' configured but not available", cfg->type); provider = &ident_dummy_provider; } } else { ib_log_error_tx(tx, "Ident module loaded but not configured!"); provider = &ident_dummy_provider; } if (provider->state != state) { /* This provider doesn't check now */ return IB_OK; } /* OK, ident is on. Verify if there is a user ID */ userid = provider->check_id(tx); if (userid == NULL && cfg->accept_any && cfg->providers != NULL) { ib_hash_iterator_t *iterator = ib_hash_iterator_create(tx->mm); ib_ident_provider_t *p; for (ib_hash_iterator_first(iterator, cfg->providers); !userid && !ib_hash_iterator_at_end(iterator); ib_hash_iterator_next(iterator)) { ib_hash_iterator_fetch(NULL, NULL, &p, iterator); /* configured provider already checked - so skip it now */ if (p->check_id != provider->check_id) { userid = p->check_id(tx); } } } if (userid != NULL) { ib_log_info(ib, "User identified as %s", userid); return IB_OK; } /* If we haven't configured an ident type, don't enforce */ if (cfg->type == NULL) { return IB_OK; } /* If we're enforcing ident, send a challenge */ return provider->challenge(tx); }
static int ironbee_init(const char *configfile, const char *logfile) { /* grab from httpd module's post-config */ ib_status_t rc; // ib_provider_t *lpr; ib_cfgparser_t *cp; ib_context_t *ctx; int rv; rc = ib_initialize(); if (rc != IB_OK) { return rc; } ib_util_log_level(9); ib_trace_init(TRACEFILE); rc = ib_engine_create(&ironbee, &ibplugin); if (rc != IB_OK) { return rc; } rc = ib_provider_register(ironbee, IB_PROVIDER_TYPE_LOGGER, "ironbee-ts", NULL, &ironbee_logger_iface, NULL); if (rc != IB_OK) { return rc; } ib_context_set_string(ib_context_engine(ironbee), IB_PROVIDER_TYPE_LOGGER, "ironbee-ts"); ib_context_set_num(ib_context_engine(ironbee), IB_PROVIDER_TYPE_LOGGER ".log_level", 4); rc = ib_engine_init(ironbee); if (rc != IB_OK) { return rc; } /* success is documented as TS_LOG_ERROR_NO_ERROR but that's undefined. * It's actually a TS_SUCCESS (proxy/InkAPI.cc line 6641). */ rv = TSTextLogObjectCreate(logfile, TS_LOG_MODE_ADD_TIMESTAMP, &ironbee_log); if (rv != TS_SUCCESS) { return IB_OK + rv; } rc = atexit(ibexit); if (rc != 0) { return IB_OK + rv; } ib_hook_register(ironbee, conn_opened_event, (ib_void_fn_t)ironbee_conn_init, NULL); ib_state_notify_cfg_started(ironbee); ctx = ib_context_main(ironbee); ib_context_set_string(ctx, IB_PROVIDER_TYPE_LOGGER, "ironbee-ts"); ib_context_set_num(ctx, "logger.log_level", 4); rc = ib_cfgparser_create(&cp, ironbee); if (rc != IB_OK) { return rc; } if (cp != NULL) { // huh? ib_cfgparser_parse(cp, configfile); ib_cfgparser_destroy(cp); } ib_state_notify_cfg_finished(ironbee); return IB_OK; }
TEST_F(OperatorTest, OperatorCallTest) { ib_status_t status; ib_num_t call_result; void *instance_data; ib_operator_t *op; const ib_operator_t *cop; status = ib_operator_create_and_register( &op, ib_engine, "test_op", IB_OP_CAPABILITY_NON_STREAM, test_create_fn, NULL, NULL, NULL, test_execute_fn, NULL ); ASSERT_EQ(IB_OK, status); status = ib_operator_lookup(ib_engine, "test_op", &cop); ASSERT_EQ(IB_OK, status); status = ib_operator_inst_create(op, ib_context_main(ib_engine), IB_OP_CAPABILITY_NON_STREAM, "INVALID", &instance_data); ASSERT_EQ(IB_EINVAL, status); status = ib_operator_inst_create(op, ib_context_main(ib_engine), IB_OP_CAPABILITY_NON_STREAM, "data", &instance_data); ASSERT_EQ(IB_OK, status); ib_field_t *field; const char *matching = "data matching string"; const char *nonmatching = "non matching string"; ib_field_create( &field, ib_engine_pool_main_get(ib_engine), IB_FIELD_NAME("testfield"), IB_FTYPE_NULSTR, NULL ); ib_field_setv(field, ib_ftype_nulstr_in(matching)); status = ib_operator_inst_execute(op, instance_data, ib_tx, field, NULL, &call_result); ASSERT_EQ(IB_OK, status); EXPECT_EQ(1, call_result); ib_field_setv(field, ib_ftype_nulstr_in(nonmatching)); status = ib_operator_inst_execute(op, instance_data, ib_tx, field, NULL, &call_result); ASSERT_EQ(IB_OK, status); EXPECT_EQ(0, call_result); status = ib_operator_inst_destroy(op, instance_data); ASSERT_EQ(IB_OK, status); }
/** * Initializes and configures the ironbee engine. */ static int ironbee_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptmp, server_rec *s) { ironbee_config_t *modcfg = (ironbee_config_t *)ap_get_module_config(s->module_config, &ironbee_module); ib_cfgparser_t *cp; ib_provider_t *lpr; void *init = NULL; ib_status_t rc; /* Init IB library. */ rc = ib_initialize(); if (rc != IB_OK) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, IB_PRODUCT_NAME ": Error initializing ib library"); return OK; } ib_util_log_level(4); /* Detect first (validation) run vs real config run. */ apr_pool_userdata_get(&init, "ironbee-init", s->process->pool); if (init == NULL) { ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, MODULE_NAME_FULL " loading."); apr_pool_userdata_set((const void *)1, "ironbee-init", apr_pool_cleanup_null, s->process->pool); return OK; } /// @todo Tracefile needs removed //ib_trace_init("/tmp/ironbee.trace"); ib_trace_init(NULL); /* Create the engine handle. */ rc = ib_engine_create(&ironbee, &ibplugin); if (rc != IB_OK) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, IB_PRODUCT_NAME ": Error creating engine: %d", rc); return OK; } /* Register the logger. */ rc = ib_provider_register(ironbee, IB_PROVIDER_TYPE_LOGGER, MODULE_NAME_STR, &lpr, &ironbee_logger_iface, NULL); if (rc != IB_OK) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, IB_PRODUCT_NAME ": Error registering log provider: %d", rc); return OK; } ib_provider_data_set(lpr, (void *)s); /* Default logger */ /// @todo Need to add a post set hook in core for this to work correctly ib_context_set_string(ib_context_engine(ironbee), IB_PROVIDER_TYPE_LOGGER, MODULE_NAME_STR); ib_context_set_num(ib_context_engine(ironbee), IB_PROVIDER_TYPE_LOGGER ".log_level", 4); rc = ib_engine_init(ironbee); if (rc != IB_OK) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, IB_PRODUCT_NAME ": Error initializing engine: %d", rc); return OK; } /* Register module cleanup. */ apr_pool_cleanup_register(p, (void *)s, ironbee_module_cleanup, apr_pool_cleanup_null); /* Register conn/tx init hooks. */ ib_hook_register(ironbee, conn_opened_event, (ib_void_fn_t)ironbee_conn_init, s); /* Configure the engine. */ if (modcfg->config != NULL) { ib_context_t *ctx; /* Notify the engine that the config process has started. This * will also create a main configuration context. */ ib_state_notify_cfg_started(ironbee); /* Get the main configuration context. */ ctx = ib_context_main(ironbee); /* Set some defaults */ ib_context_set_string(ctx, IB_PROVIDER_TYPE_LOGGER, MODULE_NAME_STR); ib_context_set_num(ctx, "logger.log_level", 4); /* Parse the config file. */ rc = ib_cfgparser_create(&cp, ironbee); if ((rc == IB_OK) && (cp != NULL)) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, IB_PRODUCT_NAME ": Parsing config: %s", modcfg->config); ib_cfgparser_parse(cp, modcfg->config); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, IB_PRODUCT_NAME ": Destroying config parser"); ib_cfgparser_destroy(cp); } /* Notify the engine that the config process has finished. This * will also close out the main configuration context. */ ib_state_notify_cfg_finished(ironbee); } else { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, IB_PRODUCT_NAME ": No config specified with IronBeeConfig directive"); } ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, MODULE_NAME_FULL " configured."); return OK; }
/** * Handle request_header events for user agent extraction. * * Extract the "request_headers" field (a list) from the transactions's * data provider instance, then loop through the list, looking for the * "User-Agent" field. If found, the value is parsed and used to update the * connection object fields. * * @param[in] ib IronBee object * @param[in,out] tx Transaction. * @param[in] event Event type * @param[in] data Callback data (module) * * @returns Status code */ static ib_status_t modua_user_agent(ib_engine_t *ib, ib_tx_t *tx, ib_state_event_type_t event, void *data) { assert(ib != NULL); assert(tx != NULL); assert(tx->var_store != NULL); assert(event == request_header_finished_event); assert(data != NULL); const ib_module_t *m = (const ib_module_t *)data; const ib_field_t *req_agent = NULL; ib_status_t rc = IB_OK; const ib_list_t *bs_list; const ib_bytestr_t *bs; const modua_config_t *cfg; rc = ib_context_module_config(ib_context_main(ib), m, &cfg); if (rc != IB_OK) { ib_log_error_tx(tx, "Can't fetch configuration: %s", ib_status_to_string(rc)); return rc; } /* Extract the User-Agent header field */ rc = ib_var_target_get_const( cfg->user_agent, &bs_list, tx->mp, tx->var_store ); if (rc == IB_ENOENT || ib_list_elements(bs_list) == 0) { ib_log_debug_tx(tx, "request_header_finished_event: No user agent"); return IB_OK; } if (rc != IB_OK) { ib_log_error_tx(tx, "Cannot retrieve request_headers:User-Agent: %d", rc); return rc; } if (IB_LIST_ELEMENTS(bs_list) == 0) { ib_log_debug_tx(tx, "request_header_finished_event: No user agent"); return IB_OK; } req_agent = (ib_field_t *)IB_LIST_NODE_DATA(IB_LIST_LAST(bs_list)); /* Found it: copy the data into a newly allocated string buffer */ rc = ib_field_value_type(req_agent, ib_ftype_bytestr_out(&bs), IB_FTYPE_BYTESTR); if (rc != IB_OK) { ib_log_error_tx(tx, "Request user agent is not a BYTESTR: %s", ib_status_to_string(rc)); return rc; } /* Finally, split it up & store the components */ rc = modua_agent_fields(ib, tx, bs); return rc; }