From 7ef618d745b713e1ce6dc370ed185e12bf5c6b6f Mon Sep 17 00:00:00 2001 From: Jeremiah Mackey Date: Mon, 11 May 2026 22:32:38 +0000 Subject: [PATCH 1/4] Unlock mutex on event update failure paths --- src/events.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/events.c b/src/events.c index e6479c9..6ecb262 100644 --- a/src/events.c +++ b/src/events.c @@ -363,7 +363,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_update_config(WOLFSENTRY_CO WOLFSENTRY_MUTEX_OR_RETURN(); ret = wolfsentry_event_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, label, label_len, &event); - WOLFSENTRY_RERETURN_IF_ERROR(ret); + WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); if (event->config == NULL) { if ((event->config = (struct wolfsentry_eventconfig_internal *)WOLFSENTRY_MALLOC(sizeof *event->config)) == NULL) @@ -618,7 +618,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_set_aux_event( WOLFSENTRY_MUTEX_OR_RETURN(); ret = wolfsentry_event_get_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, event_label, event_label_len, &event); - WOLFSENTRY_RERETURN_IF_ERROR(ret); + WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret); if (WOLFSENTRY_CHECK_BITS(event->flags, WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT)) { ret = WOLFSENTRY_ERROR_ENCODE(INCOMPATIBLE_STATE); goto out; From dfabee14e9207614d7238ba086e928c9136fa15f Mon Sep 17 00:00:00 2001 From: Jeremiah Mackey Date: Mon, 11 May 2026 22:32:38 +0000 Subject: [PATCH 2/4] Bound JSON prefix-bits and validate eventconfig flags --- src/json/load_config.c | 13 ++++++++++++- src/wolfsentry_util.c | 4 ++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/json/load_config.c b/src/json/load_config.c index eb33d9c..01f4706 100644 --- a/src/json/load_config.c +++ b/src/json/load_config.c @@ -876,6 +876,8 @@ static wolfsentry_errcode_t handle_route_endpoint_clause(struct wolfsentry_json_ } #endif else if (! strcmp(jps->cur_keyname, "prefix-bits")) { + wolfsentry_addr_bits_t max_bits; + wolfsentry_errcode_t ret; if (sa->sa_family == WOLFSENTRY_AF_UNSPEC) WOLFSENTRY_ERROR_RETURN(CONFIG_OUT_OF_SEQUENCE); #ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING @@ -888,7 +890,16 @@ static wolfsentry_errcode_t handle_route_endpoint_clause(struct wolfsentry_json_ WOLFSENTRY_ERROR_RETURN(CONFIG_MISPLACED_KEY); } #endif - WOLFSENTRY_ERROR_RERETURN(convert_uint16(type, data, data_size, &sa->addr_len)); + ret = convert_uint16(type, data, data_size, &sa->addr_len); + WOLFSENTRY_RERETURN_IF_ERROR(ret); + ret = wolfsentry_addr_family_max_addr_bits( + JPS_WOLFSENTRY_CONTEXT_ARGS_OUT, + sa->sa_family, + &max_bits); + WOLFSENTRY_RERETURN_IF_ERROR(ret); + if (sa->addr_len > max_bits) + WOLFSENTRY_ERROR_RETURN(NUMERIC_ARG_TOO_BIG); + WOLFSENTRY_RETURN_OK; } else if (! strcmp(jps->cur_keyname, "interface")) { WOLFSENTRY_CLEAR_BITS(jps->o_u_c.route.flags, diff --git a/src/wolfsentry_util.c b/src/wolfsentry_util.c index 2327250..34e81e2 100644 --- a/src/wolfsentry_util.c +++ b/src/wolfsentry_util.c @@ -3606,6 +3606,10 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_eventconfig_check( WOLFSENTRY_ERROR_RETURN(INVALID_ARG); if (config->derogatory_threshold_for_penaltybox > MAX_UINT_OF(instance_of_field(struct wolfsentry_route, meta.derogatory_count))) WOLFSENTRY_ERROR_RETURN(INVALID_ARG); + if (config->route_flags_to_add_on_insert & WOLFSENTRY_ROUTE_FLAG_PENALTYBOXED) + WOLFSENTRY_ERROR_RETURN(INVALID_ARG); + if (config->route_flags_to_clear_on_insert & WOLFSENTRY_ROUTE_FLAG_PENALTYBOXED) + WOLFSENTRY_ERROR_RETURN(INVALID_ARG); ret = wolfsentry_route_check_flags_sensical(config->route_flags_to_add_on_insert); WOLFSENTRY_RERETURN_IF_ERROR(ret); From 0bcbef39c8e36c98d1304f3ae0cc893c2fa50e58 Mon Sep 17 00:00:00 2001 From: Jeremiah Mackey Date: Mon, 11 May 2026 22:32:38 +0000 Subject: [PATCH 3/4] Fail closed in lwIP and wolfIP dispatch and filter callbacks --- src/lwip/packet_filter_glue.c | 50 ++++++++++++++++----------------- src/wolfip/packet_filter_glue.c | 22 ++++++++------- 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/src/lwip/packet_filter_glue.c b/src/lwip/packet_filter_glue.c index 7b71bc6..2b6a062 100644 --- a/src/lwip/packet_filter_glue.c +++ b/src/lwip/packet_filter_glue.c @@ -101,7 +101,7 @@ static err_t ethernet_filter_with_wolfsentry( #endif if (wolfsentry == NULL) - WOLFSENTRY_RETURN_VALUE(ERR_OK); + WOLFSENTRY_RETURN_VALUE(ERR_ABRT); switch(event->reason) { case FILT_RECEIVING: @@ -132,7 +132,7 @@ static err_t ethernet_filter_with_wolfsentry( case FILT_LISTENING: case FILT_STOP_LISTENING: /* can't happen. */ - WOLFSENTRY_RETURN_VALUE(ERR_OK); + WOLFSENTRY_RETURN_VALUE(ERR_ABRT); } remote.remote.sa_family = WOLFSENTRY_AF_LINK; @@ -182,7 +182,7 @@ static err_t ethernet_filter_with_wolfsentry( WOLFSENTRY_WARN_ON_FAILURE(ws_ret); - if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) + if (WOLFSENTRY_IS_FAILURE(ws_ret) || WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) ret = ERR_ABRT; else ret = ERR_OK; @@ -238,7 +238,7 @@ static err_t ip4_filter_with_wolfsentry( #endif if (wolfsentry == NULL) - WOLFSENTRY_RETURN_VALUE(ERR_OK); + WOLFSENTRY_RETURN_VALUE(ERR_ABRT); switch(event->reason) { case FILT_RECEIVING: @@ -272,7 +272,7 @@ static err_t ip4_filter_with_wolfsentry( case FILT_LISTENING: case FILT_STOP_LISTENING: /* can't happen. */ - WOLFSENTRY_RETURN_VALUE(ERR_OK); + WOLFSENTRY_RETURN_VALUE(ERR_ABRT); } remote.remote.sa_family = WOLFSENTRY_AF_INET; @@ -322,7 +322,7 @@ static err_t ip4_filter_with_wolfsentry( WOLFSENTRY_WARN_ON_FAILURE(ws_ret); - if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) + if (WOLFSENTRY_IS_FAILURE(ws_ret) || WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) ret = ERR_ABRT; else ret = ERR_OK; @@ -373,7 +373,7 @@ static err_t ip6_filter_with_wolfsentry( #endif if (wolfsentry == NULL) - WOLFSENTRY_RETURN_VALUE(ERR_OK); + WOLFSENTRY_RETURN_VALUE(ERR_ABRT); switch(event->reason) { case FILT_RECEIVING: @@ -407,7 +407,7 @@ static err_t ip6_filter_with_wolfsentry( case FILT_LISTENING: case FILT_STOP_LISTENING: /* can't happen. */ - WOLFSENTRY_RETURN_VALUE(ERR_OK); + WOLFSENTRY_RETURN_VALUE(ERR_ABRT); } remote.remote.sa_family = WOLFSENTRY_AF_INET6; @@ -457,7 +457,7 @@ static err_t ip6_filter_with_wolfsentry( WOLFSENTRY_WARN_ON_FAILURE(ws_ret); - if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) + if (WOLFSENTRY_IS_FAILURE(ws_ret) || WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) ret = ERR_ABRT; else ret = ERR_OK; @@ -511,7 +511,7 @@ static err_t tcp_filter_with_wolfsentry( #endif if (wolfsentry == NULL) - WOLFSENTRY_RETURN_VALUE(ERR_OK); + WOLFSENTRY_RETURN_VALUE(ERR_ABRT); switch(event->reason) { case FILT_ACCEPTING: @@ -589,7 +589,7 @@ static err_t tcp_filter_with_wolfsentry( case FILT_DISSOCIATE: case FILT_ADDR_UNREACHABLE: /* can't happen. */ - WOLFSENTRY_RETURN_VALUE(ERR_OK); + WOLFSENTRY_RETURN_VALUE(ERR_ABRT); } #if LWIP_IPV6 @@ -658,10 +658,10 @@ static err_t tcp_filter_with_wolfsentry( WOLFSENTRY_WARN_ON_FAILURE(ws_ret); - if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_PORT_RESET)) - ret = ERR_RST; - else if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) + if (WOLFSENTRY_IS_FAILURE(ws_ret) || WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) ret = ERR_ABRT; + else if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_PORT_RESET)) + ret = ERR_RST; else ret = ERR_OK; @@ -723,7 +723,7 @@ static err_t udp_filter_with_wolfsentry( #endif if (wolfsentry == NULL) - WOLFSENTRY_RETURN_VALUE(ERR_OK); + WOLFSENTRY_RETURN_VALUE(ERR_ABRT); switch(event->reason) { case FILT_BINDING: @@ -778,7 +778,7 @@ static err_t udp_filter_with_wolfsentry( case FILT_ADDR_UNREACHABLE: case FILT_CLOSE_WAIT: /* can't happen. */ - WOLFSENTRY_RETURN_VALUE(ERR_OK); + WOLFSENTRY_RETURN_VALUE(ERR_ABRT); } #if LWIP_IPV6 @@ -847,10 +847,10 @@ static err_t udp_filter_with_wolfsentry( WOLFSENTRY_WARN_ON_FAILURE(ws_ret); - if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_PORT_RESET)) - ret = ERR_RST; - else if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) + if (WOLFSENTRY_IS_FAILURE(ws_ret) || WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) ret = ERR_ABRT; + else if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_PORT_RESET)) + ret = ERR_RST; else ret = ERR_OK; @@ -910,7 +910,7 @@ static err_t icmp4_filter_with_wolfsentry( #endif if (wolfsentry == NULL) - WOLFSENTRY_RETURN_VALUE(ERR_OK); + WOLFSENTRY_RETURN_VALUE(ERR_ABRT); switch(event->reason) { case FILT_RECEIVING: @@ -945,7 +945,7 @@ static err_t icmp4_filter_with_wolfsentry( case FILT_STOP_LISTENING: case FILT_CLOSE_WAIT: /* can't happen. */ - WOLFSENTRY_RETURN_VALUE(ERR_OK); + WOLFSENTRY_RETURN_VALUE(ERR_ABRT); } remote.remote.sa_family = WOLFSENTRY_AF_INET; @@ -995,7 +995,7 @@ static err_t icmp4_filter_with_wolfsentry( WOLFSENTRY_WARN_ON_FAILURE(ws_ret); - if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) + if (WOLFSENTRY_IS_FAILURE(ws_ret) || WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) ret = ERR_ABRT; else ret = ERR_OK; @@ -1046,7 +1046,7 @@ static err_t icmp6_filter_with_wolfsentry( #endif if (wolfsentry == NULL) - WOLFSENTRY_RETURN_VALUE(ERR_OK); + WOLFSENTRY_RETURN_VALUE(ERR_ABRT); switch(event->reason) { case FILT_RECEIVING: @@ -1081,7 +1081,7 @@ static err_t icmp6_filter_with_wolfsentry( case FILT_STOP_LISTENING: case FILT_CLOSE_WAIT: /* can't happen. */ - WOLFSENTRY_RETURN_VALUE(ERR_OK); + WOLFSENTRY_RETURN_VALUE(ERR_ABRT); } remote.remote.sa_family = WOLFSENTRY_AF_INET6; @@ -1131,7 +1131,7 @@ static err_t icmp6_filter_with_wolfsentry( WOLFSENTRY_WARN_ON_FAILURE(ws_ret); - if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) + if (WOLFSENTRY_IS_FAILURE(ws_ret) || WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) ret = ERR_ABRT; else ret = ERR_OK; diff --git a/src/wolfip/packet_filter_glue.c b/src/wolfip/packet_filter_glue.c index 2dcaec6..8ee7542 100644 --- a/src/wolfip/packet_filter_glue.c +++ b/src/wolfip/packet_filter_glue.c @@ -141,7 +141,7 @@ static int wolfip_dispatch_event( WOLFSENTRY_THREAD_HEADER_DECLS if (wolfsentry == NULL) - return 0; + return -WOLFIP_EACCES; if (WOLFSENTRY_THREAD_HEADER_INIT(WOLFSENTRY_THREAD_FLAG_NONE) < 0) return -WOLFIP_EACCES; @@ -163,7 +163,7 @@ static int wolfip_dispatch_event( if (WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE) < 0) return -WOLFIP_EACCES; - if (wolfip_action_rejects(*action_results)) + if (WOLFSENTRY_IS_FAILURE(ws_ret) || wolfip_action_rejects(*action_results)) return -WOLFIP_EACCES; return 0; @@ -209,7 +209,7 @@ static int wolfip_filter_ethernet( action_results = WOLFSENTRY_ACTION_RES_SOCK_ERROR; break; default: - return 0; + return -WOLFIP_EACCES; } wolfip_set_link_sockaddrs(&remote.remote, &local.local, event, outbound); @@ -261,7 +261,7 @@ static int wolfip_filter_ipv4( action_results = WOLFSENTRY_ACTION_RES_SOCK_ERROR; break; default: - return 0; + return -WOLFIP_EACCES; } wolfip_set_ipv4_sockaddrs(&remote.remote, &local.local, event, outbound); @@ -360,7 +360,7 @@ static int wolfip_filter_tcp( action_results = WOLFSENTRY_ACTION_RES_DEROGATORY; break; default: - return 0; + return -WOLFIP_EACCES; } wolfip_set_ipv4_sockaddrs(&remote.remote, &local.local, event, outbound); @@ -442,7 +442,7 @@ static int wolfip_filter_udp( WOLFSENTRY_ACTION_RES_EXCLUDE_REJECT_ROUTES; break; default: - return 0; + return -WOLFIP_EACCES; } wolfip_set_ipv4_sockaddrs(&remote.remote, &local.local, event, outbound); @@ -504,7 +504,7 @@ static int wolfip_filter_icmp( action_results = WOLFSENTRY_ACTION_RES_SOCK_ERROR; break; default: - return 0; + return -WOLFIP_EACCES; } wolfip_set_ipv4_sockaddrs(&remote.remote, &local.local, event, outbound); @@ -518,8 +518,10 @@ static int wolfip_filter_with_wolfsentry(void *arg, const struct wolfIP_filter_e { struct wolfsentry_context *wolfsentry = (struct wolfsentry_context *)arg; - if ((wolfsentry == NULL) || (event == NULL)) - return 0; + if (wolfsentry == NULL) + return -WOLFIP_EACCES; + if (event == NULL) + return -WOLFIP_EACCES; switch (event->meta.ip_proto) { case WOLFIP_FILTER_PROTO_ETH: @@ -533,7 +535,7 @@ static int wolfip_filter_with_wolfsentry(void *arg, const struct wolfIP_filter_e case WOLFIP_FILTER_PROTO_ICMP: return wolfip_filter_icmp(wolfsentry, event); default: - return 0; + return -WOLFIP_EACCES; } } From 451b10604876e624fdb7614eed2595ddf7ea4b25 Mon Sep 17 00:00:00 2001 From: Jeremiah Mackey Date: Mon, 11 May 2026 22:32:38 +0000 Subject: [PATCH 4/4] Add mutation-verified test coverage for behavioral guards --- tests/unittests.c | 709 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 709 insertions(+) diff --git a/tests/unittests.c b/tests/unittests.c index 1817b60..f5dcbe5 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -1389,6 +1389,48 @@ TEST_SKIP(test_rw_locks) #ifdef TEST_STATIC_ROUTES +#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS +/* mock clock for exact-boundary subtests. single-threaded use only. */ +static wolfsentry_time_t mock_now; +static wolfsentry_errcode_t mock_get_time(void *context, wolfsentry_time_t *now) { + (void)context; + *now = mock_now; + WOLFSENTRY_RETURN_OK; +} +static wolfsentry_time_t mock_diff_time(wolfsentry_time_t later, wolfsentry_time_t earlier) { + return later - earlier; +} +static wolfsentry_time_t mock_add_time(wolfsentry_time_t a, wolfsentry_time_t b) { + return a + b; +} +static wolfsentry_errcode_t mock_to_epoch_time(wolfsentry_time_t when, time_t *epoch_secs, long *epoch_nsecs) { + *epoch_secs = (time_t)(when / 1000000); + *epoch_nsecs = (long)((when % 1000000) * 1000); + WOLFSENTRY_RETURN_OK; +} +static wolfsentry_errcode_t mock_from_epoch_time(time_t epoch_secs, long epoch_nsecs, wolfsentry_time_t *when) { + *when = ((wolfsentry_time_t)epoch_secs * 1000000) + ((wolfsentry_time_t)epoch_nsecs / 1000); + WOLFSENTRY_RETURN_OK; +} +/* allocator and semcbs left zero: wolfsentry_init_ex falls back to defaults. */ +static const struct wolfsentry_host_platform_interface mock_clock_hpi = { + .caller_build_settings = { + .version = WOLFSENTRY_VERSION, + .config = WOLFSENTRY_CONFIG_SIGNATURE + }, + .timecbs = { + .context = NULL, + .get_time = mock_get_time, + .diff_time = mock_diff_time, + .add_time = mock_add_time, + .to_epoch_time = mock_to_epoch_time, + .from_epoch_time = mock_from_epoch_time, + .interval_to_seconds = mock_to_epoch_time, + .interval_from_seconds = mock_from_epoch_time + } +}; +#endif /* WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS */ + static wolfsentry_errcode_t replace_rule_transactionally( WOLFSENTRY_CONTEXT_ARGS_IN, const char *event_label, @@ -2715,6 +2757,498 @@ static int test_static_routes(void) { WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + /* derogatory_threshold_for_penaltybox == 0 disables auto-penaltybox. */ + { + struct wolfsentry_eventconfig sub_config = { +#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS + .route_private_data_size = PRIVATE_DATA_SIZE, + .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, + .max_connection_count = 10, + .derogatory_threshold_for_penaltybox = 0, /* disabled */ + .penaltybox_duration = 300, + .route_idle_time_for_purge = 0, + .flags = WOLFSENTRY_EVENTCONFIG_FLAG_NONE +#else + PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 0, 300, 0, + WOLFSENTRY_EVENTCONFIG_FLAG_NONE, 0, 0, 0, 0, 0, 0 +#endif + }; + struct { + struct wolfsentry_sockaddr sa; + byte addr_buf[4]; + } sub_remote, sub_local; + wolfsentry_action_res_t sub_ar; + wolfsentry_route_flags_t sub_flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS + | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN + | WOLFSENTRY_ROUTE_FLAG_GREENLISTED; + wolfsentry_route_flags_t sub_im; + wolfsentry_ent_id_t sub_id, sub_rid; + unsigned int i; + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + &sub_config, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + sub_remote.sa.sa_family = sub_local.sa.sa_family = AF_INET; + sub_remote.sa.sa_proto = sub_local.sa.sa_proto = IPPROTO_TCP; + sub_remote.sa.sa_port = 12345; + sub_local.sa.sa_port = 443; + sub_remote.sa.addr_len = sub_local.sa.addr_len = sizeof sub_remote.addr_buf * BITS_PER_BYTE; + sub_remote.sa.interface = sub_local.sa.interface = 1; + memcpy(sub_remote.sa.addr,"\3\4\5\6",sizeof sub_remote.addr_buf); + memcpy(sub_local.sa.addr,"\373\372\371\370",sizeof sub_local.addr_buf); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, &sub_remote.sa, &sub_local.sa, + sub_flags, 0, 0, &sub_id, &sub_ar)); + + for (i = 0; i < 8; ++i) { + WOLFSENTRY_CLEAR_ALL_BITS(sub_ar); + WOLFSENTRY_SET_BITS(sub_ar, WOLFSENTRY_ACTION_RES_DEROGATORY); + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_event_dispatch_with_inited_result( + WOLFSENTRY_CONTEXT_ARGS_OUT, &sub_remote.sa, &sub_local.sa, sub_flags, + NULL, 0, NULL, &sub_rid, &sub_im, &sub_ar)); + WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(sub_ar, WOLFSENTRY_ACTION_RES_REJECT)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(sub_ar, WOLFSENTRY_ACTION_RES_ACCEPT)); + } + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + } + + /* penaltybox_duration == 0 means permanent penaltybox. */ + { + struct wolfsentry_eventconfig sub_config = { +#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS + .route_private_data_size = PRIVATE_DATA_SIZE, + .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, + .max_connection_count = 10, + .derogatory_threshold_for_penaltybox = 4, + .penaltybox_duration = 0, /* permanent */ + .route_idle_time_for_purge = 0, + .flags = WOLFSENTRY_EVENTCONFIG_FLAG_NONE +#else + PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 4, 0, 0, + WOLFSENTRY_EVENTCONFIG_FLAG_NONE, 0, 0, 0, 0, 0, 0 +#endif + }; + struct { + struct wolfsentry_sockaddr sa; + byte addr_buf[4]; + } sub_remote, sub_local; + wolfsentry_action_res_t sub_ar; + wolfsentry_route_flags_t sub_flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS + | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN + | WOLFSENTRY_ROUTE_FLAG_GREENLISTED; + wolfsentry_route_flags_t sub_im; + wolfsentry_ent_id_t sub_id, sub_rid; + unsigned int i; + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + &sub_config, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + sub_remote.sa.sa_family = sub_local.sa.sa_family = AF_INET; + sub_remote.sa.sa_proto = sub_local.sa.sa_proto = IPPROTO_TCP; + sub_remote.sa.sa_port = 12345; + sub_local.sa.sa_port = 443; + sub_remote.sa.addr_len = sub_local.sa.addr_len = sizeof sub_remote.addr_buf * BITS_PER_BYTE; + sub_remote.sa.interface = sub_local.sa.interface = 1; + memcpy(sub_remote.sa.addr,"\7\10\11\12",sizeof sub_remote.addr_buf); + memcpy(sub_local.sa.addr,"\373\372\371\370",sizeof sub_local.addr_buf); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, &sub_remote.sa, &sub_local.sa, + sub_flags, 0, 0, &sub_id, &sub_ar)); + + /* trigger penaltybox. */ + for (i = 1; i <= (unsigned int)sub_config.derogatory_threshold_for_penaltybox; ++i) { + WOLFSENTRY_CLEAR_ALL_BITS(sub_ar); + WOLFSENTRY_SET_BITS(sub_ar, WOLFSENTRY_ACTION_RES_DEROGATORY); + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_event_dispatch_with_inited_result( + WOLFSENTRY_CONTEXT_ARGS_OUT, &sub_remote.sa, &sub_local.sa, sub_flags, + NULL, 0, NULL, &sub_rid, &sub_im, &sub_ar)); + } + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(sub_ar, WOLFSENTRY_ACTION_RES_REJECT)); + + /* advance time well past any nonzero duration. */ + sleep(1); + + WOLFSENTRY_CLEAR_ALL_BITS(sub_ar); + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_event_dispatch_with_inited_result( + WOLFSENTRY_CONTEXT_ARGS_OUT, &sub_remote.sa, &sub_local.sa, sub_flags, + NULL, 0, NULL, &sub_rid, &sub_im, &sub_ar)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(sub_ar, WOLFSENTRY_ACTION_RES_REJECT)); + WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(sub_ar, WOLFSENTRY_ACTION_RES_ACCEPT)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + } + +#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS + /* At elapsed time exactly equal to penaltybox_duration, the route must + * still be penaltyboxed. Mock clock pins elapsed to the boundary value. + */ + { + struct wolfsentry_eventconfig sub_config = { +#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS + .route_private_data_size = PRIVATE_DATA_SIZE, + .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, + .max_connection_count = 10, + .derogatory_threshold_for_penaltybox = 4, + .penaltybox_duration = 1, /* 1 second */ + .route_idle_time_for_purge = 0, + .flags = WOLFSENTRY_EVENTCONFIG_FLAG_NONE +#else + PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 4, 1, 0, + WOLFSENTRY_EVENTCONFIG_FLAG_NONE, 0, 0, 0, 0, 0, 0 +#endif + }; + struct { + struct wolfsentry_sockaddr sa; + byte addr_buf[4]; + } sub_remote, sub_local; + wolfsentry_action_res_t sub_ar; + wolfsentry_route_flags_t sub_flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS + | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN + | WOLFSENTRY_ROUTE_FLAG_GREENLISTED; + wolfsentry_route_flags_t sub_im; + wolfsentry_ent_id_t sub_id, sub_rid; + unsigned int i; + + mock_now = 1000000000; + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&mock_clock_hpi), + &sub_config, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + sub_remote.sa.sa_family = sub_local.sa.sa_family = AF_INET; + sub_remote.sa.sa_proto = sub_local.sa.sa_proto = IPPROTO_TCP; + sub_remote.sa.sa_port = 12345; + sub_local.sa.sa_port = 443; + sub_remote.sa.addr_len = sub_local.sa.addr_len = sizeof sub_remote.addr_buf * BITS_PER_BYTE; + sub_remote.sa.interface = sub_local.sa.interface = 1; + memcpy(sub_remote.sa.addr,"\13\14\15\16",sizeof sub_remote.addr_buf); + memcpy(sub_local.sa.addr,"\373\372\371\370",sizeof sub_local.addr_buf); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, &sub_remote.sa, &sub_local.sa, + sub_flags, 0, 0, &sub_id, &sub_ar)); + + /* trigger penaltybox at mock_now = T0. */ + for (i = 1; i <= (unsigned int)sub_config.derogatory_threshold_for_penaltybox; ++i) { + WOLFSENTRY_CLEAR_ALL_BITS(sub_ar); + WOLFSENTRY_SET_BITS(sub_ar, WOLFSENTRY_ACTION_RES_DEROGATORY); + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_event_dispatch_with_inited_result( + WOLFSENTRY_CONTEXT_ARGS_OUT, &sub_remote.sa, &sub_local.sa, sub_flags, + NULL, 0, NULL, &sub_rid, &sub_im, &sub_ar)); + } + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(sub_ar, WOLFSENTRY_ACTION_RES_REJECT)); + + /* elapsed time exactly equal to penaltybox_duration (1s = 1e6 us). */ + mock_now += 1000000; + + WOLFSENTRY_CLEAR_ALL_BITS(sub_ar); + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_event_dispatch_with_inited_result( + WOLFSENTRY_CONTEXT_ARGS_OUT, &sub_remote.sa, &sub_local.sa, sub_flags, + NULL, 0, NULL, &sub_rid, &sub_im, &sub_ar)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(sub_ar, WOLFSENTRY_ACTION_RES_REJECT)); + WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(sub_ar, WOLFSENTRY_ACTION_RES_ACCEPT)); + + /* one tick past the boundary clears the flag. */ + mock_now += 1; + WOLFSENTRY_CLEAR_ALL_BITS(sub_ar); + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_event_dispatch_with_inited_result( + WOLFSENTRY_CONTEXT_ARGS_OUT, &sub_remote.sa, &sub_local.sa, sub_flags, + NULL, 0, NULL, &sub_rid, &sub_im, &sub_ar)); + WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(sub_ar, WOLFSENTRY_ACTION_RES_REJECT)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(sub_ar, WOLFSENTRY_ACTION_RES_ACCEPT)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + } +#endif /* WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS */ + + /* WOLFSENTRY_ROUTE_FLAG_DONT_COUNT_CURRENT_CONNECTIONS bypasses the + * connection-count cap. + */ + { + struct wolfsentry_eventconfig sub_config = { +#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS + .route_private_data_size = PRIVATE_DATA_SIZE, + .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, + .max_connection_count = 2, + .derogatory_threshold_for_penaltybox = 4, + .penaltybox_duration = 300, + .route_idle_time_for_purge = 0, + .flags = WOLFSENTRY_EVENTCONFIG_FLAG_NONE +#else + PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 2, 4, 300, 0, + WOLFSENTRY_EVENTCONFIG_FLAG_NONE, 0, 0, 0, 0, 0, 0 +#endif + }; + struct { + struct wolfsentry_sockaddr sa; + byte addr_buf[4]; + } sub_remote, sub_local; + wolfsentry_action_res_t sub_ar; + wolfsentry_route_flags_t sub_flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS + | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN + | WOLFSENTRY_ROUTE_FLAG_GREENLISTED + | WOLFSENTRY_ROUTE_FLAG_DONT_COUNT_CURRENT_CONNECTIONS; + wolfsentry_route_flags_t sub_im; + wolfsentry_ent_id_t sub_id, sub_rid; + unsigned int i; + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + &sub_config, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + sub_remote.sa.sa_family = sub_local.sa.sa_family = AF_INET; + sub_remote.sa.sa_proto = sub_local.sa.sa_proto = IPPROTO_TCP; + sub_remote.sa.sa_port = 12345; + sub_local.sa.sa_port = 443; + sub_remote.sa.addr_len = sub_local.sa.addr_len = sizeof sub_remote.addr_buf * BITS_PER_BYTE; + sub_remote.sa.interface = sub_local.sa.interface = 1; + memcpy(sub_remote.sa.addr,"\17\20\21\22",sizeof sub_remote.addr_buf); + memcpy(sub_local.sa.addr,"\373\372\371\370",sizeof sub_local.addr_buf); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, &sub_remote.sa, &sub_local.sa, + sub_flags, 0, 0, &sub_id, &sub_ar)); + + for (i = 0; i < 5; ++i) { + WOLFSENTRY_CLEAR_ALL_BITS(sub_ar); + WOLFSENTRY_SET_BITS(sub_ar, WOLFSENTRY_ACTION_RES_CONNECT); + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_event_dispatch_with_inited_result( + WOLFSENTRY_CONTEXT_ARGS_OUT, &sub_remote.sa, &sub_local.sa, sub_flags, + NULL, 0, NULL, &sub_rid, &sub_im, &sub_ar)); + WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(sub_ar, WOLFSENTRY_ACTION_RES_REJECT)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(sub_ar, WOLFSENTRY_ACTION_RES_ACCEPT)); + } + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + } + + /* action_res_filter_bits_unset: route matches only when all listed bits + * are absent from action_results. + */ + { + struct wolfsentry_eventconfig sub_base = { +#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS + .route_private_data_size = PRIVATE_DATA_SIZE, + .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, + .max_connection_count = 10, + .derogatory_threshold_for_penaltybox = 4, + .penaltybox_duration = 300, + .route_idle_time_for_purge = 0, + .flags = WOLFSENTRY_EVENTCONFIG_FLAG_NONE +#else + PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 4, 300, 0, + WOLFSENTRY_EVENTCONFIG_FLAG_NONE, 0, 0, 0, 0, 0, 0 +#endif + }; + struct wolfsentry_eventconfig sub_event_config = sub_base; + struct { + struct wolfsentry_sockaddr sa; + byte addr_buf[4]; + } sub_remote, sub_local; + wolfsentry_action_res_t sub_ar; + wolfsentry_route_flags_t sub_flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS + | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN + | WOLFSENTRY_ROUTE_FLAG_GREENLISTED; + wolfsentry_route_flags_t sub_im; + wolfsentry_ent_id_t sub_id, sub_rid; + + sub_event_config.action_res_filter_bits_unset = WOLFSENTRY_ACTION_RES_USER0; + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + &sub_base, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_REJECT)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "evt_filter", -1, + 10, &sub_event_config, + WOLFSENTRY_EVENT_FLAG_NONE, + &sub_id)); + + sub_remote.sa.sa_family = sub_local.sa.sa_family = AF_INET; + sub_remote.sa.sa_proto = sub_local.sa.sa_proto = IPPROTO_TCP; + sub_remote.sa.sa_port = 12345; + sub_local.sa.sa_port = 443; + sub_remote.sa.addr_len = sub_local.sa.addr_len = sizeof sub_remote.addr_buf * BITS_PER_BYTE; + sub_remote.sa.interface = sub_local.sa.interface = 1; + memcpy(sub_remote.sa.addr,"\27\30\31\32",sizeof sub_remote.addr_buf); + memcpy(sub_local.sa.addr,"\373\372\371\370",sizeof sub_local.addr_buf); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, &sub_remote.sa, &sub_local.sa, + sub_flags, "evt_filter", (int)strlen("evt_filter"), &sub_id, &sub_ar)); + + WOLFSENTRY_CLEAR_ALL_BITS(sub_ar); + WOLFSENTRY_SET_BITS(sub_ar, WOLFSENTRY_ACTION_RES_USER0); + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_event_dispatch_with_inited_result( + WOLFSENTRY_CONTEXT_ARGS_OUT, &sub_remote.sa, &sub_local.sa, sub_flags, + NULL, 0, NULL, &sub_rid, &sub_im, &sub_ar)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(sub_ar, WOLFSENTRY_ACTION_RES_REJECT)); + WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(sub_ar, WOLFSENTRY_ACTION_RES_ACCEPT)); + + WOLFSENTRY_CLEAR_ALL_BITS(sub_ar); + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_event_dispatch_with_inited_result( + WOLFSENTRY_CONTEXT_ARGS_OUT, &sub_remote.sa, &sub_local.sa, sub_flags, + NULL, 0, NULL, &sub_rid, &sub_im, &sub_ar)); + WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(sub_ar, WOLFSENTRY_ACTION_RES_ACCEPT)); + WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(sub_ar, WOLFSENTRY_ACTION_RES_REJECT)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + } + +#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS + /* Stale-purge: a route whose purge_after equals the current time is purged. */ + { + struct wolfsentry_eventconfig sub_config = { +#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS + .route_private_data_size = PRIVATE_DATA_SIZE, + .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, + .max_connection_count = 10, + .derogatory_threshold_for_penaltybox = 4, + .penaltybox_duration = 300, + .route_idle_time_for_purge = 0, + .flags = WOLFSENTRY_EVENTCONFIG_FLAG_NONE +#else + PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 4, 300, 0, + WOLFSENTRY_EVENTCONFIG_FLAG_NONE, 0, 0, 0, 0, 0, 0 +#endif + }; + struct { + struct wolfsentry_sockaddr sa; + byte addr_buf[4]; + } sub_remote, sub_local; + wolfsentry_action_res_t sub_ar; + wolfsentry_route_flags_t sub_flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS + | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN; + wolfsentry_ent_id_t sub_id; + struct wolfsentry_route *sub_route; + + mock_now = 1000000000; + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&mock_clock_hpi), + &sub_config, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + sub_remote.sa.sa_family = sub_local.sa.sa_family = AF_INET; + sub_remote.sa.sa_proto = sub_local.sa.sa_proto = IPPROTO_TCP; + sub_remote.sa.sa_port = 12345; + sub_local.sa.sa_port = 443; + sub_remote.sa.addr_len = sub_local.sa.addr_len = sizeof sub_remote.addr_buf * BITS_PER_BYTE; + sub_remote.sa.interface = sub_local.sa.interface = 1; + memcpy(sub_remote.sa.addr,"\33\34\35\36",sizeof sub_remote.addr_buf); + memcpy(sub_local.sa.addr,"\373\372\371\370",sizeof sub_local.addr_buf); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, &sub_remote.sa, &sub_local.sa, + sub_flags, 0, 0, &sub_id, &sub_ar)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT)); + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, sub_id, + (struct wolfsentry_table_ent_header **)&sub_route)); + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_purge_time_set(WOLFSENTRY_CONTEXT_ARGS_OUT, sub_route, mock_now)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_stale_purge(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, NULL)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT)); + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE( + ITEM_NOT_FOUND, + wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, sub_id, + (struct wolfsentry_table_ent_header **)&sub_route)); + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT)); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + } +#endif /* WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS */ + + /* wolfsentry_eventconfig_check rejects PENALTYBOXED in either + * route_flags_to_add_on_insert or route_flags_to_clear_on_insert. + */ + { + struct wolfsentry_eventconfig sub_config = { +#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS + .route_private_data_size = PRIVATE_DATA_SIZE, + .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, + .max_connection_count = 10, + .derogatory_threshold_for_penaltybox = 4, + .penaltybox_duration = 300, + .route_idle_time_for_purge = 0, + .flags = WOLFSENTRY_EVENTCONFIG_FLAG_NONE +#else + PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 4, 300, 0, + WOLFSENTRY_EVENTCONFIG_FLAG_NONE, 0, 0, 0, 0, 0, 0 +#endif + }; + wolfsentry_errcode_t sub_ret; + + sub_config.route_flags_to_add_on_insert = WOLFSENTRY_ROUTE_FLAG_PENALTYBOXED; + WOLFSENTRY_EXIT_ON_SUCCESS( + sub_ret = wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + &sub_config, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(INVALID_ARG, sub_ret); + sub_config.route_flags_to_add_on_insert = WOLFSENTRY_ROUTE_FLAG_NONE; + + sub_config.route_flags_to_clear_on_insert = WOLFSENTRY_ROUTE_FLAG_PENALTYBOXED; + WOLFSENTRY_EXIT_ON_SUCCESS( + sub_ret = wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + &sub_config, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(INVALID_ARG, sub_ret); + sub_config.route_flags_to_clear_on_insert = WOLFSENTRY_ROUTE_FLAG_NONE; + } + WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE)); WOLFSENTRY_RETURN_OK; @@ -2724,6 +3258,33 @@ static int test_static_routes(void) { #ifdef TEST_DYNAMIC_RULES +static unsigned int action_invocation_count; +static wolfsentry_errcode_t counting_action_handler( + WOLFSENTRY_CONTEXT_ARGS_IN, + const struct wolfsentry_action *action, + void *handler_arg, + void *caller_arg, + const struct wolfsentry_event *trigger_event, + wolfsentry_action_type_t action_type, + const struct wolfsentry_route *target_route, + struct wolfsentry_route_table *route_table, + struct wolfsentry_route *rule_route, + wolfsentry_action_res_t *action_results) +{ + WOLFSENTRY_CONTEXT_ARGS_NOT_USED; + (void)action; + (void)handler_arg; + (void)caller_arg; + (void)trigger_event; + (void)action_type; + (void)target_route; + (void)route_table; + (void)rule_route; + (void)action_results; + ++action_invocation_count; + WOLFSENTRY_RETURN_OK; +} + static wolfsentry_errcode_t wolfsentry_action_dummy_callback( WOLFSENTRY_CONTEXT_ARGS_IN, const struct wolfsentry_action *action, @@ -3352,6 +3913,143 @@ static int test_dynamic_rules(void) { WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + /* WOLFSENTRY_EVENTCONFIG_FLAG_INHIBIT_ACTIONS suppresses action dispatch. */ + { + struct wolfsentry_eventconfig sub_config = { +#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS + .route_private_data_size = PRIVATE_DATA_SIZE, + .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, + .max_connection_count = 10, + .derogatory_threshold_for_penaltybox = 4, + .penaltybox_duration = 300, + .route_idle_time_for_purge = 0, + .flags = WOLFSENTRY_EVENTCONFIG_FLAG_INHIBIT_ACTIONS +#else + PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 4, 300, 0, + WOLFSENTRY_EVENTCONFIG_FLAG_INHIBIT_ACTIONS, 0, 0, 0, 0, 0, 0 +#endif + }; + struct { + struct wolfsentry_sockaddr sa; + byte addr_buf[4]; + } sub_remote, sub_local; + wolfsentry_action_res_t sub_ar; + wolfsentry_route_flags_t sub_flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS + | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN + | WOLFSENTRY_ROUTE_FLAG_GREENLISTED; + wolfsentry_route_flags_t sub_im; + wolfsentry_ent_id_t sub_id, sub_rid; + + action_invocation_count = 0; + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + &sub_config, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_action_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "count_action", -1, + WOLFSENTRY_ACTION_FLAG_NONE, + counting_action_handler, + NULL, + &sub_id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "evt_inhibit", -1, + 10, NULL, + WOLFSENTRY_EVENT_FLAG_NONE, + &sub_id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_action_append( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "evt_inhibit", -1, + WOLFSENTRY_ACTION_TYPE_DECISION, + "count_action", -1)); + + sub_remote.sa.sa_family = sub_local.sa.sa_family = AF_INET; + sub_remote.sa.sa_proto = sub_local.sa.sa_proto = IPPROTO_TCP; + sub_remote.sa.sa_port = 12345; + sub_local.sa.sa_port = 443; + sub_remote.sa.addr_len = sub_local.sa.addr_len = sizeof sub_remote.addr_buf * BITS_PER_BYTE; + sub_remote.sa.interface = sub_local.sa.interface = 1; + memcpy(sub_remote.sa.addr,"\23\24\25\26",sizeof sub_remote.addr_buf); + memcpy(sub_local.sa.addr,"\373\372\371\370",sizeof sub_local.addr_buf); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, &sub_remote.sa, &sub_local.sa, + sub_flags, "evt_inhibit", (int)strlen("evt_inhibit"), &sub_id, &sub_ar)); + + WOLFSENTRY_CLEAR_ALL_BITS(sub_ar); + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_event_dispatch_with_inited_result( + WOLFSENTRY_CONTEXT_ARGS_OUT, &sub_remote.sa, &sub_local.sa, sub_flags, + NULL, 0, NULL, &sub_rid, &sub_im, &sub_ar)); + + WOLFSENTRY_EXIT_ON_FALSE(action_invocation_count == 0); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + + /* companion run with the flag cleared confirms the action plumbing + * fires, so the zero count above can't be a wiring artefact. + */ + action_invocation_count = 0; + sub_config.flags = WOLFSENTRY_EVENTCONFIG_FLAG_NONE; + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_init_ex( + wolfsentry_build_settings, + WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI), + &sub_config, + &wolfsentry, + WOLFSENTRY_INIT_FLAG_NONE)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_action_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "count_action", -1, + WOLFSENTRY_ACTION_FLAG_NONE, + counting_action_handler, + NULL, + &sub_id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_insert( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "evt_inhibit", -1, + 10, NULL, + WOLFSENTRY_EVENT_FLAG_NONE, + &sub_id)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_event_action_append( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "evt_inhibit", -1, + WOLFSENTRY_ACTION_TYPE_DECISION, + "count_action", -1)); + + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, &sub_remote.sa, &sub_local.sa, + sub_flags, "evt_inhibit", (int)strlen("evt_inhibit"), &sub_id, &sub_ar)); + + WOLFSENTRY_CLEAR_ALL_BITS(sub_ar); + WOLFSENTRY_EXIT_ON_FAILURE( + wolfsentry_route_event_dispatch_with_inited_result( + WOLFSENTRY_CONTEXT_ARGS_OUT, &sub_remote.sa, &sub_local.sa, sub_flags, + NULL, 0, NULL, &sub_rid, &sub_im, &sub_ar)); + + WOLFSENTRY_EXIT_ON_FALSE(action_invocation_count >= 1); + + WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry))); + } + WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE)); WOLFSENTRY_RETURN_OK; @@ -3648,6 +4346,17 @@ static int test_user_values(void) { WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(NOT_PERMITTED, ret); + WOLFSENTRY_EXIT_ON_SUCCESS( + ret = wolfsentry_user_value_store_string( + WOLFSENTRY_CONTEXT_ARGS_OUT, + "test_string", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + "different", + WOLFSENTRY_LENGTH_NULL_TERMINATED, + 1 /* overwrite_p */)); + + WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(NOT_PERMITTED, ret); + WOLFSENTRY_EXIT_ON_FAILURE( wolfsentry_user_value_get_string( WOLFSENTRY_CONTEXT_ARGS_OUT,