/// @test Test util list library - IB_LIST_LOOP() TEST_F(TestIBUtilList, test_list_loop) { ib_list_t *list; ib_list_node_t *node; ib_status_t rc; int init[] = { 0, 1, 2, 3, 4 }; int *val; int i; rc = ib_list_create(&list, MemPool()); ASSERT_EQ(IB_OK, rc); ASSERT_TRUE(list); ASSERT_EQ(0UL, ib_list_elements(list)); for (i = 0; i < 5; i++) { rc = ib_list_push(list, &init[i]); ASSERT_EQ(IB_OK, rc); } ASSERT_EQ(5UL, ib_list_elements(list)); i = 0; IB_LIST_LOOP(list, node) { val = (int *)ib_list_node_data(node); ASSERT_EQ(init[i], *val); i++; }
TEST_F(XRulesTest, SetFlag) { ib_field_t *field; ib_num_t num; ib_var_target_t *target; const ib_list_t *list; std::string config = std::string( "LogLevel DEBUG\n" "LoadModule \"ibmod_persistence_framework.so\"\n" "LoadModule \"ibmod_init_collection.so\"\n" "LoadModule \"ibmod_xrules.so\"\n" "InitCollection GeoIP vars: country_code=US\n" "SensorId B9C1B52B-C24A-4309-B9F9-0EF4CD577A3E\n" "SensorName UnitTesting\n" "SensorHostname unit-testing.sensor.tld\n" /* Note that both rules should fire and result in a single entry. */ "XRulePath / EnableRequestParamInspection priority=1\n" "XRuleGeo US EnableRequestParamInspection priority=1\n" "<Site test-site>\n" " SiteId AAAABBBB-1111-2222-3333-000000000000\n" " Hostname somesite.com\n" "</Site>\n" ); configureIronBeeByString(config.c_str()); performTx(); ASSERT_TRUE(ib_tx); ASSERT_TRUE(ib_tx->flags & IB_TX_FINSPECT_REQPARAMS); ASSERT_EQ( IB_OK, ib_var_target_acquire_from_string( &target, ib_tx->mp, ib_var_store_config(ib_tx->var_store), "FLAGS:inspectRequestParams", strlen("FLAGS:inspectRequestParams"), NULL, NULL) ); ASSERT_EQ( IB_OK, ib_var_target_get_const( target, &list, ib_tx->mp, ib_tx->var_store) ); ASSERT_EQ(1U, ib_list_elements(list)); field = (ib_field_t *)ib_list_node_data_const(ib_list_first_const(list)); ASSERT_EQ(IB_FTYPE_NUM, field->type); ASSERT_EQ( IB_OK, ib_field_value(field, ib_ftype_num_out(&num)) ); ASSERT_EQ(1, num); }
/// @test Test util list library - ib_htree_list() and ib_list_destroy() TEST_F(TestIBUtilList, test_list_create_and_destroy) { ib_list_t *list; ib_status_t rc; rc = ib_list_create(&list, MemPool()); ASSERT_EQ(IB_OK, rc); ASSERT_TRUE(list); ASSERT_EQ(0UL, ib_list_elements(list)); }
TEST_F(TestIBUtilLogformat, test_parse_custom3) { ib_status_t rc; ib_logformat_t *lf = NULL; size_t len; static char linebuf[buflen + 1]; static const char *formatted = \ "Start" SITE_ID " \\ " SENSOR_ID " " HOST_NAME " \t" LOG_FILE " %End"; rc = ib_logformat_create(MM(), &lf); ASSERT_EQ(IB_OK, rc); rc = ib_logformat_parse(lf, "Start%s \\\\ %S %h\\n\\r\\t%f %%End"); ASSERT_EQ(IB_OK, rc); ASSERT_EQ(9U, ib_list_elements(lf->items)); rc = ib_logformat_format(lf, linebuf, buflen, &len, format_field, NULL); ASSERT_EQ(IB_OK, rc); ASSERT_STREQ(formatted, linebuf); }
/// @test Test util logformat library - ib_logformat_parse() TEST_F(TestIBUtilLogformat, test_parse_custom1) { ib_status_t rc; ib_logformat_t *lf = NULL; size_t len; static char linebuf[buflen + 1]; static const char *formatted = \ "MyFormat " SITE_ID " " SENSOR_ID " " HOST_NAME " " LOG_FILE " END"; rc = ib_logformat_create(MM(), &lf); ASSERT_EQ(IB_OK, rc); rc = ib_logformat_parse(lf, "MyFormat %s %S %h %f END"); ASSERT_EQ(IB_OK, rc); ASSERT_EQ(9U, ib_list_elements(lf->items)); rc = ib_logformat_format(lf, linebuf, buflen, &len, format_field, NULL); ASSERT_EQ(IB_OK, rc); ASSERT_STREQ(formatted, linebuf); }
TEST_F(TestIBUtilLogformat, test_parse_default) { ib_status_t rc; ib_logformat_t *lf = NULL; const ib_list_node_t *node; const ib_logformat_item_t *item; static char linebuf[buflen + 1]; static const char *formatted = \ TIME_STAMP " " HOST_NAME " " REMOTE_IP " " SENSOR_ID " " SITE_ID " " \ TX_ID " " LOG_FILE; size_t len; rc = ib_logformat_create(MM(), &lf); ASSERT_EQ(IB_OK, rc); rc = ib_logformat_parse(lf, IB_LOGFORMAT_DEFAULT); ASSERT_EQ(IB_OK, rc); ASSERT_STREQ(IB_LOGFORMAT_DEFAULT, lf->format); ASSERT_EQ(13U, ib_list_elements(lf->items)); node = ib_list_first_const(lf->items); ASSERT_TRUE(node); item = (const ib_logformat_item_t *)node->data; ASSERT_EQ(item_type_format, item->itype); ASSERT_EQ('T', item->item.field.fchar); node = ib_list_node_next_const(node); ASSERT_TRUE(node); item = (const ib_logformat_item_t *)node->data; ASSERT_EQ(item_type_literal, item->itype); ASSERT_STREQ(" ", item->item.literal.buf.short_str); node = ib_list_node_next_const(node); ASSERT_TRUE(node); item = (const ib_logformat_item_t *)node->data; ASSERT_EQ(item_type_format, item->itype); ASSERT_EQ('h', item->item.field.fchar); node = ib_list_node_next_const(node); ASSERT_TRUE(node); item = (const ib_logformat_item_t *)node->data; ASSERT_EQ(item_type_literal, item->itype); ASSERT_STREQ(" ", item->item.literal.buf.short_str); node = ib_list_node_next_const(node); ASSERT_TRUE(node); item = (const ib_logformat_item_t *)node->data; ASSERT_EQ(item_type_format, item->itype); ASSERT_EQ('a', item->item.field.fchar); node = ib_list_node_next_const(node); ASSERT_TRUE(node); item = (const ib_logformat_item_t *)node->data; ASSERT_EQ(item_type_literal, item->itype); ASSERT_STREQ(" ", item->item.literal.buf.short_str); node = ib_list_node_next_const(node); ASSERT_TRUE(node); item = (const ib_logformat_item_t *)node->data; ASSERT_EQ(item_type_format, item->itype); ASSERT_EQ('S', item->item.field.fchar); node = ib_list_node_next_const(node); ASSERT_TRUE(node); item = (const ib_logformat_item_t *)node->data; ASSERT_EQ(item_type_literal, item->itype); ASSERT_STREQ(" ", item->item.literal.buf.short_str); node = ib_list_node_next_const(node); ASSERT_TRUE(node); item = (const ib_logformat_item_t *)node->data; ASSERT_EQ(item_type_format, item->itype); ASSERT_EQ('s', item->item.field.fchar); node = ib_list_node_next_const(node); ASSERT_TRUE(node); item = (const ib_logformat_item_t *)node->data; ASSERT_EQ(item_type_literal, item->itype); ASSERT_STREQ(" ", item->item.literal.buf.short_str); node = ib_list_node_next_const(node); ASSERT_TRUE(node); item = (const ib_logformat_item_t *)node->data; ASSERT_EQ(item_type_format, item->itype); ASSERT_EQ('t', item->item.field.fchar); node = ib_list_node_next_const(node); ASSERT_TRUE(node); item = (const ib_logformat_item_t *)node->data; ASSERT_EQ(item_type_literal, item->itype); ASSERT_STREQ(" ", item->item.literal.buf.short_str); node = ib_list_node_next_const(node); ASSERT_TRUE(node); item = (const ib_logformat_item_t *)node->data; ASSERT_EQ(item_type_format, item->itype); ASSERT_EQ('f', item->item.field.fchar); rc = ib_logformat_format(lf, linebuf, buflen, &len, format_field, NULL); ASSERT_EQ(IB_OK, rc); ASSERT_STREQ(formatted, linebuf); /* Verify that truncation is handled correctly */ rc = ib_logformat_format(lf, linebuf, trunclen, &len, format_field, NULL); ASSERT_EQ(IB_ETRUNC, rc); ASSERT_EQ(len, trunclen-1); char trunc_buf[trunclen]; strncpy(trunc_buf, formatted, trunclen-1); trunc_buf[trunclen-1] = '\0'; ASSERT_STREQ(trunc_buf, linebuf); }
/** * 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; }
/** * Handle managed collection registration for vars "name=value" parameters * * @param[in] ib Engine * @param[in] module Collection manager's module object (unused) * @param[in] manager The collection manager object to register with (unused) * @param[in] mp Memory pool to use for allocations * @param[in] collection_name Name of the collection * @param[in] uri Full collection URI (unused) * @param[in] uri_scheme URI scheme (unused) * @param[in] uri_data Hierarchical/data part of the URI * @param[in] params List of parameter strings * @param[in] register_data Register callback data (unused) * @param[out] pmanager_inst_data Pointer to manager specific collection data * * @returns Status code: * - IB_OK All OK * - IB_Exxx Other error */ static ib_status_t core_managed_collection_vars_register_fn( const ib_engine_t *ib, const ib_module_t *module, const ib_collection_manager_t *manager, ib_mpool_t *mp, const char *collection_name, const char *uri, const char *uri_scheme, const char *uri_data, const ib_list_t *params, void *register_data, void **pmanager_inst_data) { assert(ib != NULL); assert(module != NULL); assert(mp != NULL); assert(collection_name != NULL); assert(params != NULL); assert(pmanager_inst_data != NULL); const ib_list_node_t *node; ib_list_t *vars_list; ib_list_t *field_list; ib_mpool_t *tmp = ib_engine_pool_temp_get(ib); ib_status_t rc; if (strlen(uri_data) != 0) { return IB_DECLINED; } if (ib_list_elements(params) < 1) { return IB_EINVAL; } /* Create a temporary list */ rc = ib_list_create(&vars_list, tmp); if (rc != IB_OK) { return rc; } /* First pass; walk through all params, look for "a=b" type syntax */ IB_LIST_LOOP_CONST(params, node) { static const int ovecsize = 9; int ovector[ovecsize]; const char *param = (const char *)node->data; core_vars_t *vars; int pcre_rc; pcre_rc = pcre_exec(core_vars_manager.pattern, NULL, param, strlen(param), 0, 0, ovector, ovecsize); if (pcre_rc < 0) { return IB_DECLINED; } vars = ib_mpool_alloc(tmp, sizeof(*vars)); if (vars == NULL) { return IB_EALLOC; } vars->name = ib_mpool_memdup_to_str(tmp, param + ovector[2], ovector[3] - ovector[2]); vars->value = ib_mpool_memdup_to_str(tmp, param + ovector[4], ovector[5] - ovector[4]); if ( (vars->name == NULL) || (vars->value == NULL) ) { return IB_EALLOC; } rc = ib_list_push(vars_list, vars); if (rc != IB_OK) { return rc; } } /* Build the list of fields */ rc = ib_list_create(&field_list, mp); if (rc != IB_OK) { return rc; } /* Now walk though the list, creating a field for each one */ IB_LIST_LOOP_CONST(vars_list, node) { const core_vars_t *vars = (const core_vars_t *)node->data; ib_field_t *field; ib_field_val_union_t fval; rc = ib_field_from_string(mp, IB_FIELD_NAME(vars->name), vars->value, &field, &fval); if (rc != IB_OK) { ib_log_error(ib, "Error creating field (\"%s\", \"%s\"): %s", vars->name, vars->value, ib_status_to_string(rc)); return rc; } rc = ib_list_push(field_list, field); if (rc != IB_OK) { return rc; } if (field->type == IB_FTYPE_NUM) { ib_log_trace(ib, "Created numeric field \"%s\" %"PRId64" in \"%s\"", vars->name, fval.num, collection_name); } else if (field->type == IB_FTYPE_FLOAT) { ib_log_trace(ib, "Created float field \"%s\" %f in \"%s\"", vars->name, (double)fval.fnum, collection_name); } else { ib_log_trace(ib, "Created string field \"%s\" \"%s\" in \"%s\"", vars->name, fval.nulstr, collection_name); } } /* Finally, store the list as the manager specific collection data */ *pmanager_inst_data = field_list; return IB_OK; }
//! Number of elements in list. O(1) size_type size() const { return ib_list_elements(ib()); }
/// @test Test ironbee library - data provider TEST(TestIronBee, test_data_dynf) { ib_engine_t *ib; ib_data_config_t *dataconfig; ib_data_t *data; ib_field_t *dynf; ib_field_t *f; ib_field_t *f2; ib_status_t rc; ib_num_t n; ib_list_t* l; ibtest_engine_create(&ib); ASSERT_EQ(IB_OK, ib_data_config_create(ib_engine_pool_main_get(ib), & dataconfig)); ASSERT_EQ(IB_OK, ib_data_create(dataconfig, ib_engine_pool_main_get(ib), &data)); ASSERT_TRUE(data); /* Create a field with no initial value. */ ASSERT_EQ( IB_OK, ib_field_create_dynamic( &dynf, ib_engine_pool_main_get(ib), IB_FIELD_NAME("test_dynf"), IB_FTYPE_LIST, dyn_get, (void *)ib_engine_pool_main_get(ib), NULL, NULL ) ); ASSERT_TRUE(dynf); ASSERT_EQ(9UL, dynf->nlen); ASSERT_MEMEQ("test_dynf", dynf->name, 9); /* Add the field to the data store. */ ASSERT_EQ(IB_OK, ib_data_add(data, dynf)); /* Fetch the field from the data store */ ASSERT_EQ(IB_OK, ib_data_get(data, "test_dynf", &f)); ASSERT_TRUE(f); ASSERT_EQ(dynf, f); /* Fetch a dynamic field from the data store */ ASSERT_EQ(IB_OK, ib_data_get(data, "test_dynf:dyn_subkey", &f)); ASSERT_TRUE(f); ASSERT_EQ(9UL, f->nlen); /* Get the list value from the dynamic field. */ rc = ib_field_mutable_value(f, ib_ftype_list_mutable_out(&l)); ASSERT_EQ(IB_OK, rc); ASSERT_EQ(1UL, ib_list_elements(l)); /* Get the single value from the list. */ f2 = (ib_field_t *)ib_list_node_data(ib_list_first(l)); ASSERT_TRUE(f2); ASSERT_EQ(10UL, f2->nlen); rc = ib_field_value(f2, ib_ftype_num_out(&n)); ASSERT_EQ(IB_OK, rc); ASSERT_EQ(5, n); /* Fetch a another subkey */ ASSERT_EQ(IB_OK, ib_data_get(data, "test_dynf:dyn_subkey2", &f)); ASSERT_TRUE(f); ASSERT_EQ(9UL, f->nlen); /* Get the list value from the dynamic field. */ rc = ib_field_mutable_value(f, ib_ftype_list_mutable_out(&l)); ASSERT_EQ(IB_OK, rc); ASSERT_EQ(1UL, ib_list_elements(l)); /* Get the single value from the list. */ f2 = (ib_field_t *)ib_list_node_data(ib_list_first(l)); ASSERT_TRUE(f2); ASSERT_EQ(11UL, f2->nlen); rc = ib_field_value(f2, ib_ftype_num_out(&n)); ASSERT_EQ(IB_OK, rc); ASSERT_EQ(5, n); ibtest_engine_destroy(ib); }
ASSERT_TRUE(list); ASSERT_EQ(0UL, ib_list_elements(list)); for (i = 0; i < 5; i++) { rc = ib_list_push(list, &init[i]); ASSERT_EQ(IB_OK, rc); } ASSERT_EQ(5UL, ib_list_elements(list)); i = 0; IB_LIST_LOOP(list, node) { val = (int *)ib_list_node_data(node); ASSERT_EQ(init[i], *val); i++; } ASSERT_EQ(5UL, ib_list_elements(list)); } /// @test Test util list library - IB_LIST_LOOP_SAFE() TEST_F(TestIBUtilList, test_list_loop_safe) { ib_list_t *list; ib_list_node_t *node; ib_list_node_t *node_next; ib_status_t rc; int init[] = { 0, 1, 2, 3, 4 }; int *val; int i; rc = ib_util_initialize(); ASSERT_EQ(IB_OK, rc);
/* * Function that return a list of all the datas with a prefix that start like * the provided prefix arg * * Example: insert data in 192.168.1.27, as well as 192.168.1.28, * as well as 192.168.10.0/24 and 10.0.0.0/8 and then search 192.168.0.0/16 * it should return a list containing all the datas except the associated to * 10.0.0.0/8 * * @param node the node to check * @param prefix the prefix we are searching * @param offset, the number of bits already compared +1 (cur possition) * @param rlist reference to the pointer that will be linked to the list, if any * @param mp pool where we should allocate the list * * @returns Status code */ ib_status_t ib_radix_match_all_data(ib_radix_t *radix, ib_radix_prefix_t *prefix, ib_list_t **rlist, ib_mpool_t *mp) { IB_FTRACE_INIT(ib_radix_match_all_data); ib_status_t ret; if (rlist == NULL) { IB_FTRACE_RET_STATUS(IB_EINVAL); } if (radix->start == NULL) { IB_FTRACE_RET_STATUS(IB_ENOENT); } if (radix->start->data != NULL && prefix->prefixlen == 0) { if (*rlist == NULL) { ret = ib_list_create(rlist, mp); if (ret != IB_OK) { IB_FTRACE_RET_STATUS(ret); } } ret = ib_list_push(*rlist, radix->start->data); if (ret != IB_OK) { IB_FTRACE_RET_STATUS(ret); } } if (prefix->prefixlen == 0) { // Append data of both branches ret = ib_radix_match_all(radix->start->zero, prefix, 0, rlist, mp); if (ret != IB_OK && ret != IB_ENOENT) { IB_FTRACE_RET_STATUS(ret); } ret = ib_radix_match_all(radix->start->one, prefix, 0, rlist, mp); if (ret != IB_OK && ret != IB_ENOENT) { IB_FTRACE_RET_STATUS(ret); } } else { // Follow it's path until we reach the correct offset if (IB_GET_DIR(prefix->rawbits[0]) == 0) { ret = ib_radix_match_all(radix->start->zero, prefix, 0, rlist, mp); } else { ret = ib_radix_match_all(radix->start->one, prefix, 0, rlist, mp); } } if (ret == IB_ENOENT && *rlist != NULL && ib_list_elements(*rlist) > 0) { IB_FTRACE_RET_STATUS(IB_OK); } IB_FTRACE_RET_STATUS(ret); }
ib_status_t ib_string_join( const char *join_string, ib_list_t *list, ib_mm_t mm, const char **pout, size_t *pout_len ) { assert(join_string != NULL); assert(list != NULL); assert(pout != NULL); assert(pout_len != NULL); const ib_list_node_t *node; char *end; /* Points to the \0 in out. */ const char *out; size_t out_len = 1; /* Size to hold an empty string, '\0' */ size_t *str_len; /* Array of lengths of char*s in list. */ size_t str_idx; /* Index into str_len. */ size_t join_string_len; join_string_len = strlen(join_string); /* Handle the base-case and avoid asking for size 0 memory segments. */ if (ib_list_elements(list) == 0) { *pout = ""; *pout_len = 0; return IB_OK; } /* First, build a place to cache string lengths. */ str_len = ib_mm_alloc(mm, sizeof(*str_len) * ib_list_elements(list)); if (str_len == NULL) { return IB_EALLOC; } /* Record each string length and sum those lengths into out_len. * Recall that out_len starts equal to 1 for the '\0'. */ str_idx = 0; IB_LIST_LOOP_CONST(list, node) { const size_t len = strlen((const char *)ib_list_node_data_const(node)); out_len += len; str_len[str_idx] = len; ++str_idx; } /* Increase out_len by the join string length * (n-1) elements. */ out_len += (ib_list_elements(list) - 1)* join_string_len; /* Allocate the final string. */ end = ib_mm_alloc(mm, out_len); if (end == NULL) { return IB_EALLOC; } /* Setup vars for joining the strings into out. */ out = end; str_idx = 0; node = ib_list_first(list); /* Copy the first string. We know the list is > 0 elements long. */ strcpy(end, (const char *)ib_list_node_data_const(node)); end += str_len[str_idx]; ++str_idx; /* Having copied the first string, now copy the join string, then the * the next string until the end of the list. */ for ( node = ib_list_node_next_const(node); node != NULL; node = ib_list_node_next_const(node) ) { /* Copy the join string first. */ strcpy(end, join_string); end += join_string_len; /* Copy the lagging string. */ strcpy(end, (const char *)ib_list_node_data_const(node)); end += str_len[str_idx]; ++str_idx; } /* Commit work back to the caller. */ *pout = out; *pout_len = out_len-1; return IB_OK; }
ib_status_t ib_config_directive_process(ib_cfgparser_t *cp, const char *name, ib_list_t *args) { IB_FTRACE_INIT(ib_config_directive_process); ib_engine_t *ib = cp->ib; ib_dirmap_init_t *rec; ib_list_node_t *node; size_t nargs = ib_list_elements(args); const char *p1; const char *p2; ib_flags_t flags; ib_flags_t fmask; ib_status_t rc; int i; rc = ib_hash_get(ib->dirmap, name, (void *)&rec); if (rc != IB_OK) { IB_FTRACE_RET_STATUS(rc); } switch (rec->type) { case IB_DIRTYPE_ONOFF: if (nargs != 1) { ib_log_error(ib, 1, "OnOff directive \"%s\" takes one parameter, not %d", name, nargs); rc = IB_EINVAL; break; } ib_list_shift(args, &p1); if ( (strcasecmp("on", p1) == 0) || (strcasecmp("yes", p1) == 0) || (strcasecmp("true", p1) == 0)) { rc = rec->cb.fn_onoff(cp, name, 1, rec->cbdata); } else { rc = rec->cb.fn_onoff(cp, name, 0, rec->cbdata); } break; case IB_DIRTYPE_PARAM1: if (nargs != 1) { ib_log_error(ib, 1, "Param1 directive \"%s\" takes one parameter, not %d", name, nargs); rc = IB_EINVAL; break; } ib_list_shift(args, &p1); rc = rec->cb.fn_param1(cp, name, p1, rec->cbdata); break; case IB_DIRTYPE_PARAM2: if (nargs != 2) { ib_log_error(ib, 1, "Param2 directive \"%s\" takes two parameters, not %d", name, nargs); rc = IB_EINVAL; break; } ib_list_shift(args, &p1); ib_list_shift(args, &p2); rc = rec->cb.fn_param2(cp, name, p1, p2, rec->cbdata); break; case IB_DIRTYPE_LIST: rc = rec->cb.fn_list(cp, name, args, rec->cbdata); break; case IB_DIRTYPE_OPFLAGS: i = 0; flags = 0; fmask = 0; IB_LIST_LOOP(args, node) { const char *opname = (const char *)ib_list_node_data(node); int oper = (*opname == '-') ? -1 : ((*opname == '+') ? 1 : 0); ib_num_t val; /* If the first option does not use an operator, then * this is setting all flags so set all the mask bits. */ if ((i == 0) && (oper == 0)) { fmask = ~0; } ib_log_debug(ib, 9, "Processing %s option: %s", name, opname); /* Remove the operator from the name if required. * and determine the numeric value of the option * by using the value map. */ if (oper != 0) { opname++; } rc = cfgp_opval(opname, rec->valmap, &val); if (rc != IB_OK) { ib_log_error(ib, 3, "Invalid %s option: %s", name, opname); IB_FTRACE_RET_STATUS(rc); } /* Mark which bit(s) we are setting. */ fmask |= val; /* Set/Unset the appropriate bits. */ if (oper == -1) { flags = flags & ~val; } else { flags |= val; } i++; } rc = rec->cb.fn_opflags(cp, name, flags, fmask, rec->cbdata); break; case IB_DIRTYPE_SBLK1: if (nargs != 1) { ib_log_error(ib, 1, "SBlk1 directive \"%s\" takes one parameter, not %d", name, nargs); rc = IB_EINVAL; break; } ib_list_shift(args, &p1); rc = rec->cb.fn_sblk1(cp, name, p1, rec->cbdata); break; default: rc = IB_EINVAL; } IB_FTRACE_RET_STATUS(rc); }
/********************************* * Helper Functions *********************************/ static ib_status_t sqli_create_fingerprint_set_from_file( sqli_fingerprint_set_t **out_ps, const char *path, ib_mm_t mm ) { assert(out_ps != NULL); assert(path != NULL); ib_status_t rc; FILE *fp = NULL; char *buffer = NULL; size_t buffer_size = 0; ib_list_t *items = NULL; ib_list_node_t *n = NULL; ib_mpool_lite_t *tmp = NULL; ib_mm_t tmp_mm; sqli_fingerprint_set_t *ps = NULL; size_t i = 0; /* Temporary memory pool for this function only. */ rc = ib_mpool_lite_create(&tmp); assert(rc == IB_OK); assert(tmp != NULL); tmp_mm = ib_mm_mpool_lite(tmp); fp = fopen(path, "r"); if (fp == NULL) { goto fail; } rc = ib_list_create(&items, tmp_mm); assert(rc == IB_OK); assert(items != NULL); for (;;) { char *buffer_copy; int read = getline(&buffer, &buffer_size, fp); char *space = NULL; ib_num_t confidence = 0; sqli_fingerprint_entry_t *entry = ib_mm_alloc(tmp_mm, sizeof(*entry)); if (read == -1) { if (! feof(fp)) { fclose(fp); goto fail; } else { break; } } while (buffer[read-1] == '\n' || buffer[read-1] == '\r') { buffer[read-1] = '\0'; --read; } space = strstr(buffer, " "); if (space != NULL) { rc = ib_type_atoi(space + 1, 10, &confidence); if (rc != IB_OK || confidence > 100) { return IB_EINVAL; } *space = '\0'; } buffer_copy = ib_mm_strdup(mm, buffer); assert(buffer_copy != NULL); entry->confidence = confidence; entry->fingerprint = buffer_copy; rc = ib_list_push(items, (void *)entry); assert(rc == IB_OK); } fclose(fp); ps = ib_mm_alloc(mm, sizeof(*ps)); assert(ps != NULL); ps->num_fingerprints = ib_list_elements(items); ps->fingerprints = ib_mm_alloc(mm, ps->num_fingerprints * sizeof(*ps->fingerprints)); assert(ps->fingerprints != NULL); i = 0; IB_LIST_LOOP(items, n) { const sqli_fingerprint_entry_t *entry = (const sqli_fingerprint_entry_t *)ib_list_node_data(n); ps->fingerprints[i] = *entry; ++i; } assert(i == ps->num_fingerprints); ib_mpool_lite_destroy(tmp); qsort( ps->fingerprints, ps->num_fingerprints, sizeof(*ps->fingerprints), &sqli_cmp ); *out_ps = ps; return IB_OK; fail: ib_mpool_lite_destroy(tmp); return IB_EINVAL; }
/** * @internal * Handle signature execution. * * @param ib Engine * @param tx Transaction * @param cbdata Phase passed as pointer value * * @return Status code */ static ib_status_t pocsig_handle_sigs(ib_engine_t *ib, ib_tx_t *tx, void *cbdata) { IB_FTRACE_INIT(pocsig_handle_post); pocsig_cfg_t *cfg; pocsig_phase_t phase = (pocsig_phase_t)(uintptr_t)cbdata; ib_list_t *sigs; ib_list_node_t *node; int dbglvl; ib_status_t rc; /* Get the pocsig configuration for this context. */ rc = ib_context_module_config(tx->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); } /* If tracing is enabled, lower the log level. */ dbglvl = cfg->trace ? 4 : 9; /* Get the list of sigs for this phase. */ sigs = cfg->phase[phase]; if (sigs == NULL) { ib_log_debug(ib, dbglvl, "No signatures for phase=%d ctx=%p", phase, tx->ctx); IB_FTRACE_RET_STATUS(IB_OK); } ib_log_debug(ib, dbglvl, "Executing %d signatures for phase=%d ctx=%p", ib_list_elements(sigs), phase, tx->ctx); /* Run all the sigs for this phase. */ IB_LIST_LOOP(sigs, node) { pocsig_sig_t *s = (pocsig_sig_t *)ib_list_node_data(node); ib_field_t *f; /* Fetch the field. */ rc = ib_data_get(tx->dpi, s->target, &f); if (rc != IB_OK) { ib_log_error(ib, 4, "PocSig: No field named \"%s\"", s->target); continue; } /* Perform the match. */ ib_log_debug(ib, dbglvl, "PocSig: Matching \"%s\" against field \"%s\"", s->patt, s->target); rc = ib_matcher_match_field(cfg->pcre, s->cpatt, 0, f, NULL); if (rc == IB_OK) { ib_logevent_t *e; ib_log_debug(ib, dbglvl, "PocSig MATCH: %s at %s", s->patt, s->target); /* Create the event. */ rc = ib_logevent_create( &e, tx->mp, "-", IB_LEVENT_TYPE_ALERT, IB_LEVENT_ACT_UNKNOWN, IB_LEVENT_PCLASS_UNKNOWN, IB_LEVENT_SCLASS_UNKNOWN, 90, 80, IB_LEVENT_SYS_UNKNOWN, IB_LEVENT_ACTION_IGNORE, IB_LEVENT_ACTION_IGNORE, s->emsg ); if (rc != IB_OK) { ib_log_error(ib, 3, "PocSig: Error generating event: %d", rc); continue; } /* Log the event. */ ib_clog_event(tx->ctx, e); } else { ib_log_debug(ib, dbglvl, "PocSig NOMATCH"); } }
/********************************* * Helper Functions *********************************/ static ib_status_t sqli_create_pattern_set_from_file( sqli_pattern_set_t **out_ps, const char *path, ib_mpool_t *mp ) { assert(out_ps != NULL); assert(path != NULL); assert(mp != NULL); ib_status_t rc; FILE *fp = NULL; char *buffer = NULL; size_t buffer_size = 0; ib_list_t *items = NULL; ib_list_node_t *n = NULL; ib_mpool_t *tmp = NULL; sqli_pattern_set_t *ps = NULL; size_t i = 0; /* Temporary memory pool for this function only. */ rc = ib_mpool_create(&tmp, "sqli tmp", NULL); assert(rc == IB_OK); assert(tmp != NULL); fp = fopen(path, "r"); if (fp == NULL) { goto fail; } rc = ib_list_create(&items, tmp); assert(rc == IB_OK); assert(items != NULL); for (;;) { char *buffer_copy; int read = getline(&buffer, &buffer_size, fp); if (read == -1) { if (! feof(fp)) { fclose(fp); goto fail; } else { break; } } buffer_copy = ib_mpool_memdup(mp, buffer, read); assert(buffer_copy != NULL); while (buffer_copy[read-1] == '\n' || buffer_copy[read-1] == '\r') { buffer_copy[read-1] = '\0'; --read; } rc = ib_list_push(items, (void *)buffer_copy); assert(rc == IB_OK); } fclose(fp); ps = ib_mpool_alloc(mp, sizeof(*ps)); assert(ps != NULL); ps->num_patterns = ib_list_elements(items); ps->patterns = ib_mpool_alloc(mp, ps->num_patterns * sizeof(*ps->patterns)); assert(ps->patterns != NULL); i = 0; IB_LIST_LOOP(items, n) { ps->patterns[i] = ib_list_node_data(n); ++i; }
/// @test Test util list library - ib_list_push() and ib_list_pop() TEST_F(TestIBUtilList, test_list_push_and_pop) { ib_list_t *list; ib_status_t rc; int v0 = 0; int v1 = 1; int v2 = 2; int v3 = 3; int v4 = 4; int *val; rc = ib_list_create(&list, MemPool()); ASSERT_EQ(IB_OK, rc); ASSERT_TRUE(list); ASSERT_EQ(0UL, ib_list_elements(list)); /* Pop invalid. */ rc = ib_list_pop(list,(void *)&val); ASSERT_EQ(IB_ENOENT, rc); ASSERT_FALSE(val); ASSERT_EQ(0UL, ib_list_elements(list)); /* Simple pushes followed by pops. */ rc = ib_list_push(list, &v0); ASSERT_EQ(IB_OK, rc); ASSERT_EQ(1UL, ib_list_elements(list)); rc = ib_list_push(list, &v1); ASSERT_EQ(IB_OK, rc); ASSERT_EQ(2UL, ib_list_elements(list)); rc = ib_list_push(list, &v2); ASSERT_EQ(IB_OK, rc); ASSERT_EQ(3UL, ib_list_elements(list)); rc = ib_list_push(list, &v3); ASSERT_EQ(IB_OK, rc); ASSERT_EQ(4UL, ib_list_elements(list)); rc = ib_list_push(list, &v4); ASSERT_EQ(IB_OK, rc); ASSERT_EQ(5UL, ib_list_elements(list)); ASSERT_EQ(v0, *(int *)(ib_list_node_data(ib_list_first(list)))); ASSERT_EQ(v4, *(int *)(ib_list_node_data(ib_list_last(list)))); rc = ib_list_pop(list, (void *)&val); ASSERT_EQ(IB_OK, rc); ASSERT_TRUE(val); ASSERT_EQ(v4, *val); ASSERT_EQ(4UL, ib_list_elements(list)); rc = ib_list_pop(list, (void *)&val); ASSERT_EQ(IB_OK, rc); ASSERT_TRUE(val); ASSERT_EQ(v3, *val); ASSERT_EQ(3UL, ib_list_elements(list)); rc = ib_list_pop(list, (void *)&val); ASSERT_EQ(IB_OK, rc); ASSERT_TRUE(val); ASSERT_EQ(v2, *val); ASSERT_EQ(2UL, ib_list_elements(list)); rc = ib_list_pop(list, (void *)&val); ASSERT_EQ(IB_OK, rc); ASSERT_TRUE(val); ASSERT_EQ(v1, *val); ASSERT_EQ(1UL, ib_list_elements(list)); rc = ib_list_pop(list, (void *)&val); ASSERT_EQ(IB_OK, rc); ASSERT_TRUE(val); ASSERT_EQ(v0, *val); ASSERT_EQ(0UL, ib_list_elements(list)); }
/** * Constructs fail links of branches (the failure transition function) * * @param ac_tree the ac tree matcher * * @return ib_status_t status of the operation */ static ib_status_t ib_ac_link_fail_states(ib_ac_t *ac_tree) { ib_status_t rc; ib_ac_state_t *child = NULL; ib_ac_state_t *state = NULL; ib_ac_state_t *goto_state = NULL; ib_list_t *iter_queue = NULL; if (ac_tree->flags & IB_AC_FLAG_PARSER_COMPILED) { return IB_OK; } ac_tree->root->pattern = 0; rc = ib_list_create(&iter_queue, ac_tree->mp); if (rc != IB_OK) { return rc; } ac_tree->root->fail = ac_tree->root; /* All first-level children will fail back to root state */ for (child = ac_tree->root->child; child != NULL; child = child->sibling) { child->fail = ac_tree->root; rc = ib_list_enqueue(iter_queue, (void *) child); if (rc != IB_OK) { return rc; } } while (ib_list_elements(iter_queue) > 0) { rc = ib_list_dequeue(iter_queue, (void *) &state); if (rc != IB_OK) { return rc; } state->fail = ac_tree->root; if (state->parent != ac_tree->root) { goto_state = ib_ac_child_for_code(state->parent->fail, state->letter); if (goto_state != NULL) { state->fail = goto_state; } } for (child = state->child; child != NULL; child = child->sibling) { rc = ib_list_enqueue(iter_queue, (void *) child); if (rc != IB_OK) { return rc; } } } /* Link common outputs of subpatterns present in the branch*/ ib_ac_link_outputs(ac_tree, ac_tree->root); /* Unlink invalid fail transitions. This guarantees that there will * be at least one letter with transition in each fail state*/ ib_ac_unlink_unuseful(ac_tree, ac_tree->root); if (ac_tree->root->child != NULL) { ib_ac_build_bintree(ac_tree, ac_tree->root); } ac_tree->flags |= IB_AC_FLAG_PARSER_COMPILED; return IB_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; }
/** * 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 (not used) * * @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->data != NULL); assert(event == request_header_finished_event); 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; ib_log_debug3_tx(tx, "Checking for alternate remote address"); /* Extract the X-Forwarded-For from the provider instance */ rc = ib_data_get(tx->data, "request_headers:X-Forwarded-For", &field); if ( (field == NULL) || (rc != IB_OK) ) { ib_log_debug_tx(tx, "No X-Forwarded-For field"); return IB_OK; } /* Because we asked for a filtered item, what we get back is a list */ rc = ib_field_value(field, ib_ftype_list_out(&list)); if (rc != IB_OK) { ib_log_debug_tx(tx, "No request header collection"); 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_data_add_bytestr(tx->data, "remote_addr", (uint8_t*)buf, len, NULL); if (rc != IB_OK) { ib_log_error_tx(tx, "Failed to create remote address TX field: %s", ib_status_to_string(rc)); return rc; } return IB_OK; }
/** * Handle managed collection: register for JSON file * * Examines the incoming parameters; if if it looks like a JSON file, * take it; otherwise do nothing (decline) * * @param[in] ib Engine * @param[in] module Collection manager's module object * @param[in] mp Memory pool to use for allocations * @param[in] collection_name Name of the collection * @param[in] uri Full collection URI * @param[in] uri_scheme URI scheme (unused) * @param[in] uri_data Hierarchical/data part of the URI (typically a path) * @param[in] params List of parameter strings * @param[in] register_data Selection callback data * @param[out] pmanager_inst_data Pointer to manager specific data * * @returns Status code: * - IB_DECLINED Parameters not recognized * - IB_OK All OK, parameters recognized * - IB_Exxx Other error */ static ib_status_t core_managed_collection_jsonfile_register_fn( const ib_engine_t *ib, const ib_module_t *module, const ib_collection_manager_t *manager, ib_mpool_t *mp, const char *collection_name, const char *uri, const char *uri_scheme, const char *uri_data, const ib_list_t *params, void *register_data, void **pmanager_inst_data) { assert(ib != NULL); assert(module != NULL); assert(mp != NULL); assert(collection_name != NULL); assert(params != NULL); assert(pmanager_inst_data != NULL); const ib_list_node_t *node; const char *path; const char *param; const char *path_start = uri_data; bool persist = false; core_json_file_t *json_file; /* Get the first element in the list */ if (ib_list_elements(params) > 1) { return IB_EINVAL; } /* Look at the first param (if it exists) */ node = ib_list_first_const(params); if (node != NULL) { param = (const char *)node->data; if (strcasecmp(param, "persist") == 0) { persist = true; } else { ib_log_warning(ib, "JSON file: \"%s\"; unknown parameter \"%s\"", uri, param); return IB_EINVAL; } } /* Try to stat the file */ if (!persist) { struct stat sbuf; if (stat(path_start, &sbuf) < 0) { ib_log_warning(ib, "JSON file: Declining \"%s\"; " "stat(\"%s\") failed: %s", uri, path_start, strerror(errno)); return IB_DECLINED; } if (! S_ISREG(sbuf.st_mode)) { ib_log_warning(ib, "JSON file: Declining \"%s\"; \"%s\" is not a file", uri, path_start); return IB_DECLINED; } } /* Happy now, copy the file name, be done */ path = ib_mpool_strdup(mp, path_start); if (path == NULL) { return IB_EALLOC; } json_file = ib_mpool_alloc(mp, sizeof(*json_file)); if (json_file == NULL) { return IB_EALLOC; } json_file->path = path; json_file->persist = persist; /* Store the file object as the manager specific collection data */ *pmanager_inst_data = json_file; return IB_OK; }