From 21d1e1f4a64c555091f821e413f09f69e160e7a9 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 10 Mar 2026 16:05:13 +0100 Subject: [PATCH 1/6] Fix byteorder in DNS returned IP address F/477 --- src/test/unit/unit.c | 2 +- src/wolfip.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 7e57893..ba038a2 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -2255,7 +2255,7 @@ START_TEST(test_dns_query_and_callback_a) dns_callback(s.dns_udp_sd, CB_EVENT_READABLE, &s); ck_assert_int_eq(dns_lookup_calls, 1); - ck_assert_uint_eq(dns_lookup_ip, ee32(0x0A000042U)); + ck_assert_uint_eq(dns_lookup_ip, 0x0A000042U); ck_assert_int_eq(s.dns_id, 0); ck_assert_int_eq(s.dns_query_type, DNS_QUERY_TYPE_NONE); } diff --git a/src/wolfip.c b/src/wolfip.c index e4bd22b..3434e3b 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -5912,7 +5912,7 @@ void dns_callback(int dns_sd, uint16_t ev, void *arg) ((buf[pos + 1] & 0xFF) << 16) | ((buf[pos + 0] & 0xFF) << 24); if (s->dns_lookup_cb) - s->dns_lookup_cb(ee32(ip)); + s->dns_lookup_cb(ip); s->dns_id = 0; s->dns_query_type = DNS_QUERY_TYPE_NONE; return; From 7cda225987dc825ffcc177889c168e253051c661 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 10 Mar 2026 16:07:28 +0100 Subject: [PATCH 2/6] Bind ICMP sockets to an IP address / interface F/478 --- src/test/unit/unit.c | 1 + src/wolfip.c | 1 + 2 files changed, 2 insertions(+) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index ba038a2..936a99c 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -2092,6 +2092,7 @@ START_TEST(test_sock_bind_icmp_success) ck_assert_int_eq(wolfIP_sock_bind(&s, icmp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); ck_assert_uint_eq(ts->src_port, 7U); + ck_assert_uint_eq(ts->bound_local_ip, 0x0A000001U); } END_TEST diff --git a/src/wolfip.c b/src/wolfip.c index 3434e3b..52bcf2b 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -4598,6 +4598,7 @@ int wolfIP_sock_bind(struct wolfIP *s, int sockfd, const struct wolfIP_sockaddr return -1; } } + ts->bound_local_ip = bind_ip; return 0; } else return -1; From ec81ea53ebc77314208a84c8aaf376e4a932f5f8 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 10 Mar 2026 16:11:23 +0100 Subject: [PATCH 3/6] TCP: Fix ACK processing in CLOSE_WAIT state F/479 --- src/test/unit/unit.c | 63 ++++++++++++++++++++++++++++++++++++++++++++ src/wolfip.c | 1 + 2 files changed, 64 insertions(+) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 936a99c..7dc2102 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -15293,6 +15293,68 @@ START_TEST(test_tcp_input_established_ack_only_returns) } END_TEST +START_TEST(test_tcp_input_close_wait_processes_ack) +{ + struct wolfIP s; + struct tsocket *ts; + struct tcp_seg_buf segbuf; + struct wolfIP_tcp_seg *queued; + struct wolfIP_tcp_seg ackseg; + struct pkt_desc *desc; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + ts = &s.tcpsockets[0]; + memset(ts, 0, sizeof(*ts)); + ts->proto = WI_IPPROTO_TCP; + ts->S = &s; + ts->sock.tcp.state = TCP_CLOSE_WAIT; + ts->src_port = 1234; + ts->dst_port = 4321; + ts->local_ip = 0x0A000001U; + ts->remote_ip = 0x0A000002U; + ts->sock.tcp.snd_una = 100; + ts->sock.tcp.seq = 101; + ts->sock.tcp.bytes_in_flight = 1; + fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); + + memset(&segbuf, 0, sizeof(segbuf)); + queued = &segbuf.seg; + queued->ip.len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN + 1); + queued->hlen = TCP_HEADER_LEN << 2; + queued->seq = ee32(100); + ck_assert_int_eq(fifo_push(&ts->sock.tcp.txbuf, &segbuf, sizeof(segbuf)), 0); + desc = fifo_peek(&ts->sock.tcp.txbuf); + ck_assert_ptr_nonnull(desc); + desc->flags |= PKT_FLAG_SENT; + + memset(&ackseg, 0, sizeof(ackseg)); + ackseg.ip.ver_ihl = 0x45; + ackseg.ip.ttl = 64; + ackseg.ip.proto = WI_IPPROTO_TCP; + ackseg.ip.len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN); + ackseg.ip.src = ee32(ts->remote_ip); + ackseg.ip.dst = ee32(ts->local_ip); + ackseg.src_port = ee16(ts->dst_port); + ackseg.dst_port = ee16(ts->src_port); + ackseg.hlen = TCP_HEADER_LEN << 2; + ackseg.flags = TCP_FLAG_ACK; + ackseg.ack = ee32(101); + ackseg.win = ee16(32); + fix_tcp_checksums(&ackseg); + + tcp_input(&s, TEST_PRIMARY_IF, &ackseg, + (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + TCP_HEADER_LEN)); + + ck_assert_int_eq(ts->sock.tcp.state, TCP_CLOSE_WAIT); + ck_assert_uint_eq(ts->sock.tcp.snd_una, 101U); + ck_assert_uint_eq(ts->sock.tcp.bytes_in_flight, 0U); + ck_assert_ptr_eq(fifo_peek(&ts->sock.tcp.txbuf), NULL); +} +END_TEST + START_TEST(test_tcp_sock_close_state_transitions) { struct wolfIP s; @@ -18717,6 +18779,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_tcp_input_port_mismatch_skips_socket); tcase_add_test(tc_utils, test_tcp_input_syn_bound_ip_mismatch); tcase_add_test(tc_utils, test_tcp_input_syn_rcvd_ack_wrong_flags); + tcase_add_test(tc_utils, test_tcp_input_close_wait_processes_ack); tcase_add_test(tc_utils, test_tcp_input_established_ack_only_returns); tcase_add_test(tc_utils, test_tcp_input_syn_dst_not_local); tcase_add_test(tc_utils, test_tcp_input_syn_dst_outside_subnet); diff --git a/src/wolfip.c b/src/wolfip.c index 52bcf2b..fe2bab0 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -3412,6 +3412,7 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, } } else if ((t->sock.tcp.state == TCP_ESTABLISHED) || + (t->sock.tcp.state == TCP_CLOSE_WAIT) || (t->sock.tcp.state == TCP_FIN_WAIT_1) || (t->sock.tcp.state == TCP_FIN_WAIT_2)) { From 2575ee5f4e671f96fb00aba9dfc4f811a44da9b0 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 10 Mar 2026 16:15:34 +0100 Subject: [PATCH 4/6] DNS client: properly validate XID before accepting record F/480 --- src/test/unit/unit.c | 55 ++++++++++++++++++++++++++++++++++++++++++++ src/wolfip.c | 2 ++ 2 files changed, 57 insertions(+) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 7dc2102..5671d5c 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -7766,6 +7766,60 @@ START_TEST(test_dns_callback_short_header_ignored) } END_TEST +START_TEST(test_dns_callback_wrong_id_ignored) +{ + struct wolfIP s; + uint8_t response[128]; + int pos; + struct dns_header *hdr = (struct dns_header *)response; + struct dns_question *q; + struct dns_rr *rr; + const uint8_t ip_bytes[4] = {0x0A, 0x00, 0x00, 0x42}; + + wolfIP_init(&s); + mock_link_init(&s); + s.dns_server = 0x0A000001U; + s.dns_query_type = DNS_QUERY_TYPE_A; + s.dns_id = 0x1234; + s.dns_lookup_cb = test_dns_lookup_cb; + dns_lookup_calls = 0; + dns_lookup_ip = 0; + s.dns_udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(s.dns_udp_sd, 0); + + memset(response, 0, sizeof(response)); + hdr->id = ee16(0x4321); + hdr->flags = ee16(0x8100); + hdr->qdcount = ee16(1); + hdr->ancount = ee16(1); + pos = sizeof(struct dns_header); + response[pos++] = 7; memcpy(&response[pos], "example", 7); pos += 7; + response[pos++] = 3; memcpy(&response[pos], "com", 3); pos += 3; + response[pos++] = 0; + q = (struct dns_question *)(response + pos); + q->qtype = ee16(DNS_A); + q->qclass = ee16(1); + pos += sizeof(struct dns_question); + response[pos++] = 0xC0; + response[pos++] = (uint8_t)sizeof(struct dns_header); + rr = (struct dns_rr *)(response + pos); + rr->type = ee16(DNS_A); + rr->class = ee16(1); + rr->ttl = ee32(60); + rr->rdlength = ee16(4); + pos += sizeof(struct dns_rr); + memcpy(&response[pos], ip_bytes, sizeof(ip_bytes)); + pos += sizeof(ip_bytes); + + enqueue_udp_rx(&s.udpsockets[SOCKET_UNMARK(s.dns_udp_sd)], response, (uint16_t)pos, DNS_PORT); + dns_callback(s.dns_udp_sd, CB_EVENT_READABLE, &s); + ck_assert_int_eq(dns_lookup_calls, 0); + ck_assert_uint_eq(dns_lookup_ip, 0U); + ck_assert_uint_eq(s.dns_id, 0x1234); + ck_assert_int_eq(s.dns_query_type, DNS_QUERY_TYPE_A); +} +END_TEST + START_TEST(test_tcp_input_ttl_zero_sends_icmp) { struct wolfIP s; @@ -18693,6 +18747,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_dns_callback_bad_flags); tcase_add_test(tc_utils, test_dns_callback_bad_name); tcase_add_test(tc_utils, test_dns_callback_short_header_ignored); + tcase_add_test(tc_utils, test_dns_callback_wrong_id_ignored); tcase_add_test(tc_utils, test_tcp_input_ttl_zero_sends_icmp); tcase_add_test(tc_utils, test_dns_callback_bad_rr_rdlen); tcase_add_test(tc_utils, test_dhcp_parse_offer_no_match); diff --git a/src/wolfip.c b/src/wolfip.c index fe2bab0..48b6e52 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -5875,6 +5875,8 @@ void dns_callback(int dns_sd, uint16_t ev, void *arg) } if (dns_len < (int)sizeof(struct dns_header)) return; + if (ee16(hdr->id) != s->dns_id) + return; /* Parse DNS response */ if ((ee16(hdr->flags) & 0x8100) == 0x8100) { int pos = sizeof(struct dns_header); From 8abbfed05d66d807f69b0662ed1abd0aae43d4d1 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 10 Mar 2026 16:20:50 +0100 Subject: [PATCH 5/6] Add comment to clarify RTT wrapping logic. Rejects F/481 --- src/wolfip.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/wolfip.c b/src/wolfip.c index 48b6e52..0caf123 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -3110,7 +3110,10 @@ static void tcp_ack(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) ack_frame_len = ETH_HEADER_LEN + ack_ip_len; /* Prefer timestamp-based RTT sample from the incoming ACK. */ if (ack_frame_len == 0 || tcp_process_ts(t, tcp, ack_frame_len) < 0) { - /* No usable TS echo; use coarse RTT sample from send timestamp. */ + /* No usable TS echo; use coarse RTT sample from send timestamp. + * time_sent is stored modulo 2^32, so this subtraction remains + * correct across a single tick wrap as long as the RTT sample + * itself fits in 32 bits. */ if (t->S->last_tick >= fresh_desc->time_sent) { uint32_t rtt = (uint32_t)(t->S->last_tick - fresh_desc->time_sent); tcp_rto_update_from_sample(t, rtt); From bf7f39ee108ad8b6e5f83393e9adb45588422588 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 10 Mar 2026 16:44:37 +0100 Subject: [PATCH 6/6] Addressed copilot's comments --- src/test/unit/unit.c | 182 +++++++++++++++++++++++++++++++++++++++++++ src/wolfip.c | 82 ++++++++++++------- 2 files changed, 236 insertions(+), 28 deletions(-) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 5671d5c..1c152d0 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -2742,6 +2742,33 @@ START_TEST(test_sock_connect_icmp_primary_ip_fallback) } END_TEST +START_TEST(test_sock_connect_icmp_bound_local_ip_match) +{ + struct wolfIP s; + const ip4 primary_ip = 0xC0A80009U; + const ip4 secondary_ip = 0xC0A80109U; + const ip4 remote_secondary = 0xC0A801A1U; + int icmp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + + setup_stack_with_two_ifaces(&s, primary_ip, secondary_ip); + + icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); + ck_assert_int_gt(icmp_sd, 0); + ts = &s.icmpsockets[SOCKET_UNMARK(icmp_sd)]; + ts->bound_local_ip = primary_ip; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = ee32(remote_secondary); + + ck_assert_int_eq(wolfIP_sock_connect(&s, icmp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + ck_assert_uint_eq(ts->local_ip, primary_ip); + ck_assert_uint_eq(ts->if_idx, TEST_PRIMARY_IF); +} +END_TEST + START_TEST(test_sock_connect_tcp_established_returns_zero) { struct wolfIP s; @@ -7820,6 +7847,82 @@ START_TEST(test_dns_callback_wrong_id_ignored) } END_TEST +START_TEST(test_dns_callback_abort_clears_query_state) +{ + struct wolfIP s; + uint8_t bad_response[64]; + uint8_t good_response[128]; + int pos; + struct dns_header *hdr = (struct dns_header *)bad_response; + struct dns_header *good_hdr = (struct dns_header *)good_response; + struct dns_question *q; + struct dns_rr *rr; + const uint8_t ip_bytes[4] = {0x0A, 0x00, 0x00, 0x42}; + + wolfIP_init(&s); + mock_link_init(&s); + s.dns_server = 0x0A000001U; + s.dns_query_type = DNS_QUERY_TYPE_A; + s.dns_id = 0x1234; + s.dns_lookup_cb = test_dns_lookup_cb; + dns_lookup_calls = 0; + dns_lookup_ip = 0; + s.dns_udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(s.dns_udp_sd, 0); + + memset(bad_response, 0, sizeof(bad_response)); + hdr->id = ee16(s.dns_id); + hdr->flags = ee16(0x8100); + hdr->qdcount = ee16(1); + hdr->ancount = ee16(0); + pos = sizeof(struct dns_header); + bad_response[pos++] = 60; + memset(&bad_response[pos], 'a', 5); + pos += 5; + + enqueue_udp_rx(&s.udpsockets[SOCKET_UNMARK(s.dns_udp_sd)], bad_response, (uint16_t)pos, DNS_PORT); + dns_callback(s.dns_udp_sd, CB_EVENT_READABLE, &s); + ck_assert_uint_eq(s.dns_id, 0); + + memset(good_response, 0, sizeof(good_response)); + good_hdr->id = 0; + good_hdr->flags = ee16(0x8100); + good_hdr->qdcount = ee16(1); + good_hdr->ancount = ee16(1); + pos = sizeof(struct dns_header); + good_response[pos++] = 7; memcpy(&good_response[pos], "example", 7); pos += 7; + good_response[pos++] = 3; memcpy(&good_response[pos], "com", 3); pos += 3; + good_response[pos++] = 0; + q = (struct dns_question *)(good_response + pos); + q->qtype = ee16(DNS_A); + q->qclass = ee16(1); + pos += sizeof(struct dns_question); + good_response[pos++] = 0xC0; + good_response[pos++] = (uint8_t)sizeof(struct dns_header); + rr = (struct dns_rr *)(good_response + pos); + rr->type = ee16(DNS_A); + rr->class = ee16(1); + rr->ttl = ee32(60); + rr->rdlength = ee16(4); + pos += sizeof(struct dns_rr); + memcpy(&good_response[pos], ip_bytes, sizeof(ip_bytes)); + pos += sizeof(ip_bytes); + + enqueue_udp_rx(&s.udpsockets[SOCKET_UNMARK(s.dns_udp_sd)], good_response, (uint16_t)pos, DNS_PORT); + dns_callback(s.dns_udp_sd, CB_EVENT_READABLE, &s); + ck_assert_int_eq(dns_lookup_calls, 0); + ck_assert_uint_eq(dns_lookup_ip, 0U); + ck_assert_int_eq(s.dns_query_type, DNS_QUERY_TYPE_NONE); + ck_assert_ptr_eq(s.dns_lookup_cb, NULL); +} +END_TEST + +START_TEST(test_dns_abort_query_null_noop) +{ + dns_abort_query(NULL); +} +END_TEST + START_TEST(test_tcp_input_ttl_zero_sends_icmp) { struct wolfIP s; @@ -14366,6 +14469,48 @@ START_TEST(test_tcp_ack_coarse_rtt_sets_writable) } END_TEST +START_TEST(test_tcp_ack_coarse_rtt_across_low32_wrap) +{ + struct wolfIP s; + struct tsocket *ts; + struct tcp_seg_buf segbuf; + struct wolfIP_tcp_seg *seg; + struct wolfIP_tcp_seg ackseg; + struct pkt_desc *desc; + + wolfIP_init(&s); + ts = &s.tcpsockets[0]; + memset(ts, 0, sizeof(*ts)); + ts->proto = WI_IPPROTO_TCP; + ts->S = &s; + ts->sock.tcp.state = TCP_ESTABLISHED; + ts->sock.tcp.cwnd = TCP_MSS; + ts->sock.tcp.ssthresh = TCP_MSS * 4; + fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); + s.last_tick = (1ULL << 32) + 100U; + + memset(&segbuf, 0, sizeof(segbuf)); + seg = &segbuf.seg; + seg->ip.len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN + 1); + seg->hlen = TCP_HEADER_LEN << 2; + seg->seq = ee32(100); + ck_assert_int_eq(fifo_push(&ts->sock.tcp.txbuf, &segbuf, sizeof(segbuf)), 0); + desc = fifo_peek(&ts->sock.tcp.txbuf); + ck_assert_ptr_nonnull(desc); + desc->flags |= PKT_FLAG_SENT; + desc->time_sent = 0xFFFFFFF0U; + + memset(&ackseg, 0, sizeof(ackseg)); + ackseg.ack = ee32(101); + ackseg.hlen = TCP_HEADER_LEN << 2; + ackseg.flags = TCP_FLAG_ACK; + + tcp_ack(ts, &ackseg); + ck_assert_uint_eq(ts->sock.tcp.rtt, 116U); + ck_assert_uint_eq(ts->events & CB_EVENT_WRITABLE, CB_EVENT_WRITABLE); +} +END_TEST + START_TEST(test_tcp_ack_duplicate_clears_sent_large_seg_len) { struct wolfIP s; @@ -18120,6 +18265,38 @@ START_TEST(test_icmp_socket_send_recv) } END_TEST +START_TEST(test_icmp_sendto_respects_bound_local_ip_interface) +{ + struct wolfIP s; + const ip4 primary_ip = 0xC0A80009U; + const ip4 secondary_ip = 0xC0A80109U; + const ip4 remote_secondary = 0xC0A801A1U; + int sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + uint8_t payload[ICMP_HEADER_LEN] = {0}; + + setup_stack_with_two_ifaces(&s, primary_ip, secondary_ip); + + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); + ck_assert_int_gt(sd, 0); + ts = &s.icmpsockets[SOCKET_UNMARK(sd)]; + fifo_init(&ts->sock.udp.txbuf, ts->txmem, TXBUF_SIZE); + ts->bound_local_ip = primary_ip; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = ee32(remote_secondary); + + payload[0] = ICMP_ECHO_REQUEST; + + ck_assert_int_eq(wolfIP_sock_sendto(&s, sd, payload, sizeof(payload), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(payload)); + ck_assert_uint_eq(ts->local_ip, primary_ip); + ck_assert_uint_eq(ts->if_idx, TEST_PRIMARY_IF); +} +END_TEST + START_TEST(test_regression_snd_una_initialized_on_syn_rcvd) { struct wolfIP s; @@ -18615,6 +18792,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_sock_connect_udp_bound_local_ip_no_match); tcase_add_test(tc_utils, test_sock_connect_udp_bound_local_ip_match); tcase_add_test(tc_utils, test_sock_connect_icmp_sets_local_ip_from_conf); + tcase_add_test(tc_utils, test_sock_connect_icmp_bound_local_ip_match); tcase_add_test(tc_utils, test_sock_connect_icmp_wrong_family); tcase_add_test(tc_utils, test_sock_connect_icmp_local_ip_pre_set); tcase_add_test(tc_utils, test_sock_connect_icmp_conf_null); @@ -18748,6 +18926,8 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_dns_callback_bad_name); tcase_add_test(tc_utils, test_dns_callback_short_header_ignored); tcase_add_test(tc_utils, test_dns_callback_wrong_id_ignored); + tcase_add_test(tc_utils, test_dns_callback_abort_clears_query_state); + tcase_add_test(tc_utils, test_dns_abort_query_null_noop); tcase_add_test(tc_utils, test_tcp_input_ttl_zero_sends_icmp); tcase_add_test(tc_utils, test_dns_callback_bad_rr_rdlen); tcase_add_test(tc_utils, test_dhcp_parse_offer_no_match); @@ -18865,6 +19045,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_tcp_ack_last_seq_not_last_ack_state); tcase_add_test(tc_utils, test_tcp_ack_no_progress_when_ack_far_ahead); tcase_add_test(tc_utils, test_tcp_ack_coarse_rtt_sets_writable); + tcase_add_test(tc_utils, test_tcp_ack_coarse_rtt_across_low32_wrap); tcase_add_test(tc_utils, test_tcp_ack_duplicate_clears_sent_large_seg_len); tcase_add_test(tc_utils, test_tcp_ack_duplicate_discards_zero_len_segment_far_ack); tcase_add_test(tc_utils, test_tcp_ack_duplicate_ssthresh_min); @@ -18890,6 +19071,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_tcp_send_syn_advertises_sack_permitted); tcase_add_test(tc_utils, test_tcp_build_ack_options_does_not_write_past_returned_len); tcase_add_test(tc_utils, test_tcp_build_ack_options_omits_ts_when_not_negotiated); + tcase_add_test(tc_utils, test_icmp_sendto_respects_bound_local_ip_interface); tcase_add_test(tc_utils, test_tcp_sort_sack_blocks_swaps_out_of_order); tcase_add_test(tc_utils, test_tcp_sort_sack_blocks_wrap_order); tcase_add_test(tc_utils, test_tcp_merge_sack_blocks_adjacent_and_disjoint); diff --git a/src/wolfip.c b/src/wolfip.c index 0caf123..e59bef8 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -3796,15 +3796,24 @@ int wolfIP_sock_connect(struct wolfIP *s, int sockfd, const struct wolfIP_sockad if ((sin->sin_family != AF_INET) || (addrlen < sizeof(struct wolfIP_sockaddr_in))) return -WOLFIP_EINVAL; ts->remote_ip = ee32(sin->sin_addr.s_addr); - if_idx = wolfIP_route_for_ip(s, ts->remote_ip); - conf = wolfIP_ipconf_at(s, if_idx); - ts->if_idx = (uint8_t)if_idx; - if (ts->local_ip == 0 && conf && conf->ip != IPADDR_ANY) - ts->local_ip = conf->ip; - else if (ts->local_ip == 0) { - struct ipconf *primary = wolfIP_primary_ipconf(s); - if (primary && primary->ip != IPADDR_ANY) - ts->local_ip = primary->ip; + if (ts->bound_local_ip != IPADDR_ANY) { + int bound_match = 0; + unsigned int bound_if = wolfIP_if_for_local_ip(s, ts->bound_local_ip, &bound_match); + if (!bound_match) + return -WOLFIP_EINVAL; + ts->if_idx = (uint8_t)bound_if; + ts->local_ip = ts->bound_local_ip; + } else { + if_idx = wolfIP_route_for_ip(s, ts->remote_ip); + conf = wolfIP_ipconf_at(s, if_idx); + ts->if_idx = (uint8_t)if_idx; + if (ts->local_ip == 0 && conf && conf->ip != IPADDR_ANY) + ts->local_ip = conf->ip; + else if (ts->local_ip == 0) { + struct ipconf *primary = wolfIP_primary_ipconf(s); + if (primary && primary->ip != IPADDR_ANY) + ts->local_ip = primary->ip; + } } return 0; } @@ -4124,16 +4133,25 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len if (ts->src_port == 0) ts->src_port = 1; } - if_idx = wolfIP_route_for_ip(s, ts->remote_ip); - conf = wolfIP_ipconf_at(s, if_idx); - ts->if_idx = (uint8_t)if_idx; - if (ts->local_ip == 0) { - if (conf && conf->ip != IPADDR_ANY) - ts->local_ip = conf->ip; - else { - struct ipconf *primary = wolfIP_primary_ipconf(s); - if (primary && primary->ip != IPADDR_ANY) - ts->local_ip = primary->ip; + if (ts->bound_local_ip != IPADDR_ANY) { + int bound_match = 0; + unsigned int bound_if = wolfIP_if_for_local_ip(s, ts->bound_local_ip, &bound_match); + if (!bound_match) + return -WOLFIP_EINVAL; + ts->if_idx = (uint8_t)bound_if; + ts->local_ip = ts->bound_local_ip; + } else { + if_idx = wolfIP_route_for_ip(s, ts->remote_ip); + conf = wolfIP_ipconf_at(s, if_idx); + ts->if_idx = (uint8_t)if_idx; + if (ts->local_ip == 0) { + if (conf && conf->ip != IPADDR_ANY) + ts->local_ip = conf->ip; + else { + struct ipconf *primary = wolfIP_primary_ipconf(s); + if (primary && primary->ip != IPADDR_ANY) + ts->local_ip = primary->ip; + } } } if (sizeof(struct wolfIP_ip_packet) + payload_len > sizeof(frame)) @@ -5860,6 +5878,16 @@ static int dns_copy_name(const uint8_t *buf, int len, int offset, char *out, return -1; } +static void dns_abort_query(struct wolfIP *s) +{ + if (!s) + return; + s->dns_id = 0; + s->dns_query_type = DNS_QUERY_TYPE_NONE; + s->dns_lookup_cb = NULL; + s->dns_ptr_cb = NULL; +} + void dns_callback(int dns_sd, uint16_t ev, void *arg) { struct wolfIP *s = (struct wolfIP *)arg; @@ -5873,7 +5901,7 @@ void dns_callback(int dns_sd, uint16_t ev, void *arg) if (dns_len < 0) { wolfIP_sock_close(s, dns_sd); s->dns_udp_sd = -1; - s->dns_id = 0; + dns_abort_query(s); return; } if (dns_len < (int)sizeof(struct dns_header)) @@ -5888,7 +5916,7 @@ void dns_callback(int dns_sd, uint16_t ev, void *arg) while (qcount-- > 0) { pos = dns_skip_name((const uint8_t *)buf, dns_len, pos); if (pos < 0 || pos + (int)sizeof(struct dns_question) > dns_len) { - s->dns_id = 0; + dns_abort_query(s); return; } pos += sizeof(struct dns_question); @@ -5898,20 +5926,20 @@ void dns_callback(int dns_sd, uint16_t ev, void *arg) uint16_t rdlen; pos = dns_skip_name((const uint8_t *)buf, dns_len, pos); if (pos < 0 || pos + (int)sizeof(struct dns_rr) > dns_len) { - s->dns_id = 0; + dns_abort_query(s); return; } rr = (struct dns_rr *)(buf + pos); pos += sizeof(struct dns_rr); rdlen = ee16(rr->rdlength); if (pos + rdlen > dns_len) { - s->dns_id = 0; + dns_abort_query(s); return; } if (s->dns_query_type == DNS_QUERY_TYPE_A && ee16(rr->type) == DNS_A && rdlen >= 4) { uint32_t ip; if (pos + 4 > dns_len) { - s->dns_id = 0; + dns_abort_query(s); return; } ip = (buf[pos + 3] & 0xFF) | @@ -5920,16 +5948,14 @@ void dns_callback(int dns_sd, uint16_t ev, void *arg) ((buf[pos + 0] & 0xFF) << 24); if (s->dns_lookup_cb) s->dns_lookup_cb(ip); - s->dns_id = 0; - s->dns_query_type = DNS_QUERY_TYPE_NONE; + dns_abort_query(s); return; } else if (s->dns_query_type == DNS_QUERY_TYPE_PTR && ee16(rr->type) == DNS_PTR) { if (dns_copy_name((const uint8_t *)buf, dns_len, pos, s->dns_ptr_name, sizeof(s->dns_ptr_name)) == 0) { if (s->dns_ptr_cb) s->dns_ptr_cb(s->dns_ptr_name); - s->dns_id = 0; - s->dns_query_type = DNS_QUERY_TYPE_NONE; + dns_abort_query(s); return; } }