Skip to content
2 changes: 1 addition & 1 deletion src/http/httpd.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ struct httpd;
struct http_request {
char method[HTTP_METHOD_LEN]; // "GET", "POST", etc.
char path[HTTP_PATH_LEN]; // URL path
char query[HTTP_QUERY_LEN]; // URL query string (for GET requests)
char query[HTTP_QUERY_LEN]; // URL query string, if present in the target
char headers[HTTP_HEADERS_LEN]; // HTTP headers
char body[HTTP_BODY_LEN]; // HTTP body (for POST requests)
size_t body_len;
Expand Down
9 changes: 9 additions & 0 deletions src/test/unit/unit.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ Suite *wolf_suite(void)
tcase_add_test(tc_core, test_fifo_wrap_full_pop_then_refill_keeps_order_without_drops);
tcase_add_test(tc_core, test_fifo_wrap_flag_transitions_push_pop_around_boundary);
tcase_add_test(tc_core, test_fifo_wrap_flag_repeated_flips_keep_data_consistent);
tcase_add_test(tc_core, test_fifo_push_align_wrap_tail0_rejects_not_clobbers);
tcase_add_test(tc_core, test_fifo_push_align_wrap_keeps_nonempty_state);
tcase_add_test(tc_core, test_fifo_wrap_flag_transitions_with_odd_payload_sizes);
tcase_add_test(tc_core, test_fifo_wrap_flag_repeated_flips_with_odd_payload_sizes);

Expand Down Expand Up @@ -851,6 +853,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_proto, test_raw_socket_send_hdrincl_respected);
tcase_add_test(tc_proto, test_raw_socket_send_builds_ip_header);
tcase_add_test(tc_proto, test_regression_raw_socket_send_ip_id_network_byte_order);
tcase_add_test(tc_proto, test_regression_raw_socket_tx_eagain_keeps_descriptor);
tcase_add_test(tc_proto, test_raw_socket_sendto_short_addrlen_returns_einval);
tcase_add_test(tc_proto, test_raw_socket_sendto_wrong_family_returns_einval);
tcase_add_test(tc_proto, test_raw_socket_sendto_payload_too_large_for_ip_header_returns_einval);
Expand All @@ -860,6 +863,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_proto, test_getsockopt_unsupported_option_returns_einval);
tcase_add_test(tc_proto, test_packet_socket_recv_frame);
tcase_add_test(tc_proto, test_packet_socket_send_frame);
tcase_add_test(tc_proto, test_regression_packet_socket_tx_eagain_keeps_descriptor);
#if WOLFIP_PACKET_SOCKETS
tcase_add_test(tc_proto, test_packet_socket_tx_filter_block_does_not_resend);
#endif
Expand Down Expand Up @@ -974,6 +978,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_proto, test_regression_udp_len_exceeds_ip_len_dropped);
tcase_add_test(tc_proto, test_regression_udp_len_below_header_discards_and_unblocks);
tcase_add_test(tc_proto, test_regression_udp_payload_exceeds_buffer_discards_and_unblocks);
tcase_add_test(tc_proto, test_udp_dhcp_relaxation_scoped_to_dhcp_socket);
tcase_add_test(tc_proto, test_regression_icmp_payload_exceeds_buffer_discards_and_unblocks);
tcase_add_test(tc_proto, test_regression_tcp_ip_len_below_ip_header);
tcase_add_test(tc_proto, test_regression_syn_on_established_not_silently_processed);
Expand Down Expand Up @@ -1360,9 +1365,11 @@ Suite *wolf_suite(void)
tcase_add_test(tc_core, test_poll_udp_socket_callback_dispatched);
#if WOLFIP_RAWSOCKETS
tcase_add_test(tc_core, test_poll_raw_socket_callback_dispatched);
tcase_add_test(tc_core, test_poll_raw_socket_callback_reraised_event_survives);
#endif /* WOLFIP_RAWSOCKETS */
#if WOLFIP_PACKET_SOCKETS
tcase_add_test(tc_core, test_poll_packet_socket_callback_dispatched);
tcase_add_test(tc_core, test_poll_packet_socket_callback_reraised_event_survives);
#endif /* WOLFIP_PACKET_SOCKETS */
tcase_add_test(tc_core, test_poll_tx_tcp_pkt_flag_sent_desc_skipped);
tcase_add_test(tc_core, test_poll_tx_tcp_arp_miss_emits_arp_request);
Expand Down Expand Up @@ -1546,6 +1553,8 @@ Suite *wolf_suite(void)
tcase_add_test(tc_core, test_dns_skip_name_label_past_end);
tcase_add_test(tc_core, test_dns_copy_name_second_label_separator_and_label_fit);
tcase_add_test(tc_core, test_dns_callback_ptr_bad_copy_name_stays_pending);
tcase_add_test(tc_core, test_dns_callback_ptr_name_beyond_rdata_rejected);
tcase_add_test(tc_core, test_dns_callback_ptr_rdata_ends_in_pointer_ok);
tcase_add_test(tc_core, test_dns_copy_name_jumped_no_pos_increment);
tcase_add_test(tc_core, test_dns_send_query_socket_alloc_failure);
/* --- unit_tests_misc_edges.c (75 tests) --- */
Expand Down
6 changes: 3 additions & 3 deletions src/test/unit/unit_tests_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -1098,14 +1098,14 @@ START_TEST(test_dns_skip_and_copy_name)
ret = dns_skip_name(buf, sizeof(buf), 0);
ck_assert_int_eq(ret, pos);

ret = dns_copy_name(buf, sizeof(buf), 0, out, sizeof(out));
ret = dns_copy_name(buf, sizeof(buf), 0, out, sizeof(out), sizeof(buf));
ck_assert_int_eq(ret, 0);
ck_assert_str_eq(out, "www.example.com");

/* add a pointer to the name at offset 0 */
buf[pos++] = 0xC0;
buf[pos++] = 0x00;
ret = dns_copy_name(buf, sizeof(buf), pos - 2, out, sizeof(out));
ret = dns_copy_name(buf, sizeof(buf), pos - 2, out, sizeof(out), sizeof(buf));
ck_assert_int_eq(ret, 0);
ck_assert_str_eq(out, "www.example.com");

Expand All @@ -1115,7 +1115,7 @@ START_TEST(test_dns_skip_and_copy_name)
buf[pos++] = (uint8_t)(ptr_pos + 2);
buf[pos++] = 3; memcpy(&buf[pos], "bad", 3); pos += 3;
buf[pos++] = 0;
ret = dns_copy_name(buf, pos, ptr_pos, out, sizeof(out));
ret = dns_copy_name(buf, pos, ptr_pos, out, sizeof(out), pos);
ck_assert_int_eq(ret, -1);
}
END_TEST
Expand Down
4 changes: 4 additions & 0 deletions src/test/unit/unit_tests_dns_dhcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -6457,6 +6457,10 @@ START_TEST(test_udp_try_recv_dhcp_running_local_zero)
ck_assert_ptr_nonnull(ts);
ts->src_port = 1234;
ts->local_ip = 0;
/* F-10280: the local_ip==0 relaxation is scoped to the DHCP client
* socket, so this socket must be the DHCP socket to receive before it
* owns an address. */
s.dhcp_udp_sd = (int)(MARK_UDP_SOCKET | (uint32_t)(ts - s.udpsockets));

memset(udp_buf, 0, sizeof(udp_buf));
udp->ip.dst = ee32(local_ip);
Expand Down
145 changes: 136 additions & 9 deletions src/test/unit/unit_tests_dns_edges.c
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ START_TEST(test_dns_copy_name_label_too_big_for_output)
int ret;

/* out_len == 2: 0 + 2 >= 2 → label-bound guard fires → -1 */
ret = dns_copy_name(buf, (int)sizeof(buf), 0, out, sizeof(out));
ret = dns_copy_name(buf, (int)sizeof(buf), 0, out, sizeof(out), (int)sizeof(buf));
ck_assert_int_eq(ret, -1);
}
END_TEST
Expand All @@ -431,7 +431,7 @@ START_TEST(test_dns_copy_name_zero_out_len_rejects_terminator_write)
char out[1]; /* not written; placeholder */
int ret;

ret = dns_copy_name(buf, (int)sizeof(buf), 0, out, 0);
ret = dns_copy_name(buf, (int)sizeof(buf), 0, out, 0, (int)sizeof(buf));
ck_assert_int_eq(ret, -1);
}
END_TEST
Expand All @@ -448,7 +448,7 @@ START_TEST(test_dns_copy_name_ptr_at_end_of_buffer)
char out[32];
int ret;

ret = dns_copy_name(buf, 3, 2, out, sizeof(out));
ret = dns_copy_name(buf, 3, 2, out, sizeof(out), 3);
ck_assert_int_eq(ret, -1);
}
END_TEST
Expand All @@ -463,7 +463,7 @@ START_TEST(test_dns_copy_name_label_past_end)
char out[32];
int ret;

ret = dns_copy_name(buf, (int)sizeof(buf), 0, out, sizeof(out));
ret = dns_copy_name(buf, (int)sizeof(buf), 0, out, sizeof(out), (int)sizeof(buf));
ck_assert_int_eq(ret, -1);
}
END_TEST
Expand All @@ -484,7 +484,7 @@ START_TEST(test_dns_copy_name_separator_overflow)
char out[3];
int ret;

ret = dns_copy_name(buf, (int)sizeof(buf), 0, out, sizeof(out));
ret = dns_copy_name(buf, (int)sizeof(buf), 0, out, sizeof(out), (int)sizeof(buf));
ck_assert_int_eq(ret, -1);
}
END_TEST
Expand All @@ -501,7 +501,7 @@ START_TEST(test_dns_copy_name_label_overflow_output)
char out[3];
int ret;

ret = dns_copy_name(buf, (int)sizeof(buf), 0, out, sizeof(out));
ret = dns_copy_name(buf, (int)sizeof(buf), 0, out, sizeof(out), (int)sizeof(buf));
ck_assert_int_eq(ret, -1);
}
END_TEST
Expand Down Expand Up @@ -536,13 +536,13 @@ START_TEST(test_dns_copy_name_second_label_separator_and_label_fit)
int ret;

/* Should succeed with enough room */
ret = dns_copy_name(buf, (int)sizeof(buf), 0, out_ok, sizeof(out_ok));
ret = dns_copy_name(buf, (int)sizeof(buf), 0, out_ok, sizeof(out_ok), (int)sizeof(buf));
ck_assert_int_eq(ret, 0);
ck_assert_str_eq(out_ok, "ab.cd");

/* Should fail: out_len == 5, after "ab" o=2, need o+1 < 5 (ok),
* then o+c = 2+1+2 = 5 >= 5 → overflow at label copy */
ret = dns_copy_name(buf, (int)sizeof(buf), 0, out_small, sizeof(out_small));
ret = dns_copy_name(buf, (int)sizeof(buf), 0, out_small, sizeof(out_small), (int)sizeof(buf));
ck_assert_int_eq(ret, -1);
}
END_TEST
Expand Down Expand Up @@ -611,6 +611,133 @@ START_TEST(test_dns_callback_ptr_bad_copy_name_stays_pending)
}
END_TEST

/* ------------------------------------------------------------------ *
* F-10260: a PTR RDATA whose name encoding does not fit in the declared
* rdlength must be rejected. The old code bounded dns_copy_name by the
* full message length, so an inline label could continue past the RDATA
* into the following record and dns_ptr_cb was invoked with a name the
* RDATA never contained. The bytes after the 1-byte RDATA spell "foo."
* but the RDATA itself holds only the label-length byte 3.
* ------------------------------------------------------------------ */
START_TEST(test_dns_callback_ptr_name_beyond_rdata_rejected)
{
struct wolfIP s;
uint8_t response[128];
struct dns_header *hdr = (struct dns_header *)response;
struct dns_question *q;
struct dns_rr *rr;
int pos;

wolfIP_init(&s);
mock_link_init(&s);
s.dns_server = 0x0A000001U;
arm_dns_query(&s, 0xBBBB, dns_qname_a, (int)sizeof(dns_qname_a), DNS_PTR);
s.dns_ptr_cb = test_dns_ptr_cb;
s.dns_lookup_cb = NULL;
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(s.dns_id);
hdr->flags = ee16(0x8100);
hdr->qdcount = ee16(1);
hdr->ancount = ee16(1);
pos = (int)sizeof(struct dns_header);
response[pos++] = 1; response[pos++] = 'a'; response[pos++] = 0;
q = (struct dns_question *)(response + pos);
q->qtype = ee16(DNS_PTR);
q->qclass = ee16(DNS_CLASS_IN);
pos += (int)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_PTR);
rr->class = ee16(DNS_CLASS_IN);
rr->ttl = ee32(60);
rr->rdlength = ee16(1);
pos += (int)sizeof(struct dns_rr);

/* RDATA is a single byte claiming a 3-char label; "foo" + terminator
* sit in the bytes that follow the RDATA (outside it). */
response[pos++] = 3;
response[pos++] = 'f';
response[pos++] = 'o';
response[pos++] = 'o';
response[pos++] = 0;

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);

/* The name does not fit the RDATA → copy fails → the bogus name must
* not be delivered and the query stays pending. */
ck_assert_uint_eq(s.dns_id, 0xBBBB);
ck_assert_int_eq(s.dns_query_type, DNS_QUERY_TYPE_PTR);
}
END_TEST

/* ------------------------------------------------------------------ *
* F-10260 companion: a PTR RDATA that legitimately ends in a compression
* pointer (RFC 1035 s4.1.4 allows the pointer to reference any offset in
* the message) must still parse. Guards the rdata_end bound from being
* applied past the pointer jump.
* ------------------------------------------------------------------ */
START_TEST(test_dns_callback_ptr_rdata_ends_in_pointer_ok)
{
struct wolfIP s;
uint8_t response[128];
struct dns_header *hdr = (struct dns_header *)response;
struct dns_question *q;
struct dns_rr *rr;
int pos;

wolfIP_init(&s);
mock_link_init(&s);
s.dns_server = 0x0A000001U;
arm_dns_query(&s, 0xBBBB, dns_qname_a, (int)sizeof(dns_qname_a), DNS_PTR);
s.dns_ptr_cb = test_dns_ptr_cb;
s.dns_lookup_cb = NULL;
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(s.dns_id);
hdr->flags = ee16(0x8100);
hdr->qdcount = ee16(1);
hdr->ancount = ee16(1);
pos = (int)sizeof(struct dns_header);
response[pos++] = 1; response[pos++] = 'a'; response[pos++] = 0;
q = (struct dns_question *)(response + pos);
q->qtype = ee16(DNS_PTR);
q->qclass = ee16(DNS_CLASS_IN);
pos += (int)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_PTR);
rr->class = ee16(DNS_CLASS_IN);
rr->ttl = ee32(60);
/* RDATA: label "x" then a pointer to the question name ("a") → "x.a" */
rr->rdlength = ee16(4);
pos += (int)sizeof(struct dns_rr);
response[pos++] = 1;
response[pos++] = 'x';
response[pos++] = 0xC0;
response[pos++] = (uint8_t)sizeof(struct dns_header);

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);

/* Valid name → ptr_cb called → query aborted */
ck_assert_uint_eq(s.dns_id, 0U);
}
END_TEST

/* ------------------------------------------------------------------ *
* dns_copy_name: jumped == 1, so pos is NOT incremented after reading
* the NUL terminator (line 8813-8814 true branch).
Expand All @@ -630,7 +757,7 @@ START_TEST(test_dns_copy_name_jumped_no_pos_increment)
/* Start at the compression pointer (offset 1).
* The pointer lands at offset 0 which is '\0', so jumped == 1 and
* the NUL-terminator branch sets out[0]='\0' without touching pos. */
ret = dns_copy_name(buf, (int)sizeof(buf), 1, out, sizeof(out));
ret = dns_copy_name(buf, (int)sizeof(buf), 1, out, sizeof(out), (int)sizeof(buf));
ck_assert_int_eq(ret, 0);
ck_assert_uint_eq((uint8_t)out[0], 0);
}
Expand Down
Loading
Loading