Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/port/stm32h563/ssh_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ int ssh_server_init(struct wolfIP *stack, uint16_t port, ssh_debug_cb debug);
* Returns 0 on success */
int ssh_server_poll(void);

/* Get SSH server uptime in seconds (for status display) */
/* Get SSH server uptime in seconds (for status display).
* Currently a placeholder: returns 0 until a main-loop tick source is
* integrated, so the "uptime" SSH command always reports zero. */
uint32_t ssh_server_get_uptime(void);

#endif /* SSH_SERVER_H */
6 changes: 6 additions & 0 deletions src/test/unit/unit.c
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_utils, test_tcp_fin_wait_1_to_closing);
tcase_add_test(tc_utils, test_tcp_last_ack_closes_socket);
tcase_add_test(tc_utils, test_tcp_last_ack_closes_socket_delivers_closed_event);
tcase_add_test(tc_utils, test_handle_socket_callbacks_keeps_recreated_socket);
tcase_add_test(tc_utils, test_tcp_last_ack_partial_ack_keeps_socket_and_timer);
tcase_add_test(tc_utils, test_tcp_ack_acks_data_and_sets_writable);
tcase_add_test(tc_utils, test_tcp_ack_duplicate_resend_clears_sent);
Expand Down Expand Up @@ -921,6 +922,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_proto, test_regression_icmp_echo_request_non_local_dst_no_reply);
tcase_add_test(tc_proto, test_tcp_listen_rejects_wrong_interface);
tcase_add_test(tc_proto, test_tcp_listen_accepts_bound_interface);
tcase_add_test(tc_proto, test_tcp_listen_requires_matching_local_ip);
tcase_add_test(tc_proto, test_tcp_listen_accepts_any_interface);
tcase_add_test(tc_proto, test_sock_connect_selects_local_ip_multi_if);
tcase_add_test(tc_proto, test_icmp_socket_send_recv);
Expand Down Expand Up @@ -1148,6 +1150,9 @@ Suite *wolf_suite(void)
tcase_add_test(tc_core, test_icmp_input_echo_reply_path_filter_at_eth);
tcase_add_test(tc_core, test_ip_recv_with_options_oversize_dropped);
tcase_add_test(tc_core, test_wolfip_recv_on_null_stack_returns);
#if WOLFIP_RAWSOCKETS
tcase_add_test(tc_core, test_raw_sendto_rejects_oversized_len_before_narrowing);
#endif

/* Socket API arms: TCP, RAW, PACKET */
tcase_add_test(tc_core, test_register_callback_tcp_stores_handle);
Expand Down Expand Up @@ -1529,6 +1534,7 @@ Suite *wolf_suite(void)
tcase_add_test(tc_core, test_dns_callback_rcode_nonzero_aborts_query);
tcase_add_test(tc_core, test_dns_callback_zero_ancount_no_delivery);
tcase_add_test(tc_core, test_dns_callback_aaaa_answer_skipped_for_a_query);
tcase_add_test(tc_core, test_dns_callback_qr_without_rd_is_accepted);
tcase_add_test(tc_core, test_dns_callback_rr_rdlen_truncated_aborts_query);
tcase_add_test(tc_core, test_dns_callback_bad_question_name_aborts_query);
tcase_add_test(tc_core, test_dns_callback_answer_forward_ptr_aborts_query);
Expand Down
34 changes: 34 additions & 0 deletions src/test/unit/unit_tests_branches.c
Original file line number Diff line number Diff line change
Expand Up @@ -2607,3 +2607,37 @@ START_TEST(test_wolfip_recv_on_null_stack_returns)
wolfIP_recv_on(NULL, TEST_PRIMARY_IF, buf, sizeof(buf));
}
END_TEST

#if WOLFIP_RAWSOCKETS
/* F-8525: the raw-socket sendto path must reject a payload that cannot fit
* in a frame before narrowing len to uint32_t. A size_t len above the
* LINK_MTU-derived bound wraps in the total_len computation, slips past the
* LINK_MTU guard, and lets the payload memcpy overflow the fixed-size frame
* buffer. len = UINT32_MAX + 100 narrows to 100 (which would pass the MTU
* guard) but must be rejected before any memcpy. The overflow itself cannot
* be exercised in a unit test (it needs a >4GB buffer), so this pins the
* new size_t bound: the oversized length is refused, not narrowed. */
START_TEST(test_raw_sendto_rejects_oversized_len_before_narrowing)
{
struct wolfIP s;
int fd;
uint8_t buf[8];
struct wolfIP_sockaddr_in sin;

wolfIP_init(&s);
mock_link_init(&s);
wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0);

fd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_RAW, 0);
ck_assert_int_ge(fd, 0);

memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = ee32(0x0A000002U);

ck_assert_int_eq(wolfIP_sock_sendto(&s, fd, buf, (size_t)UINT32_MAX + 100, 0,
(struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EINVAL);
Comment on lines +2638 to +2639
}
END_TEST
#endif /* WOLFIP_RAWSOCKETS */

51 changes: 51 additions & 0 deletions src/test/unit/unit_tests_dns_edges.c
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,57 @@ START_TEST(test_dns_callback_aaaa_answer_skipped_for_a_query)
}
END_TEST

/* ------------------------------------------------------------------ *
* F-6211: response detection must key on the QR bit alone (RFC 1035
* s4.1.1). A conformant server that does not echo the RD bit must still
* have its reply parsed. Requiring RD as well silently drops such a
* response and lets the query time out and retransmit. Flags here are
* 0x8000 (QR set, RD clear).
* ------------------------------------------------------------------ */
START_TEST(test_dns_callback_qr_without_rd_is_accepted)
{
struct wolfIP s;
uint8_t response[128];
int pos;
struct dns_rr *rr;
uint8_t a_rdata[4] = {0x0A, 0x00, 0x00, 0x02};

wolfIP_init(&s);
mock_link_init(&s);
s.dns_server = 0x0A000001U;
arm_dns_query(&s, 0x3333, dns_qname_example_com,
(int)sizeof(dns_qname_example_com), DNS_A);
dns_lookup_calls = 0;
dns_lookup_ip = 0;
s.dns_lookup_cb = test_dns_lookup_cb;
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);

/* QR set, RD clear, RCODE 0, TC clear. */
pos = build_dns_a_response_header(response, sizeof(response),
s.dns_id, 0x8000, 1, 1, NULL);
/* Answer NAME: compressed pointer to the question name. */
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(DNS_CLASS_IN);
rr->ttl = ee32(60);
rr->rdlength = ee16((uint16_t)sizeof(a_rdata));
pos += (int)sizeof(struct dns_rr);
memcpy(&response[pos], a_rdata, sizeof(a_rdata));
pos += (int)sizeof(a_rdata);

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 QR-only response must be parsed and the lookup delivered. */
ck_assert_int_eq(dns_lookup_calls, 1);
ck_assert_uint_eq(dns_lookup_ip, 0x0A000002U);
}
END_TEST

/* ------------------------------------------------------------------ *
* dns_callback: answer rdlen advertised larger than remaining buffer
* → abort query (line 8997-8999)
Expand Down
47 changes: 47 additions & 0 deletions src/test/unit/unit_tests_proto.c
Original file line number Diff line number Diff line change
Expand Up @@ -4811,6 +4811,53 @@ START_TEST(test_tcp_listen_accepts_bound_interface)
}
END_TEST

/* F-10281: a listener bound to a specific local address must only match
* segments addressed to that address. The SYN path already validates
* bound_local_ip, but a non-SYN segment for the same port and a different
* local address on the same host overwrites the listener's
* if_idx/last_pkt_ttl/peer_rwnd and sets matched (suppressing the RFC 793
* unmatched RST) before any address validation. A segment for the bound
* address must still match, so the check is not over-restricting. */
START_TEST(test_tcp_listen_requires_matching_local_ip)
{
struct wolfIP s;
const ip4 primary_ip = 0xC0A80002U;
const ip4 secondary_ip = 0xC0A80101U;
const uint16_t listen_port = 23456;
int listen_fd;
struct wolfIP_sockaddr_in addr;
struct tsocket *listener;
uint8_t ttl_before;

setup_stack_with_two_ifaces(&s, primary_ip, secondary_ip);

listen_fd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, 0);
ck_assert_int_ge(listen_fd, 0);
listener = &s.tcpsockets[SOCKET_UNMARK(listen_fd)];

memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = ee16(listen_port);
addr.sin_addr.s_addr = ee32(secondary_ip);
ck_assert_int_eq(wolfIP_sock_bind(&s, listen_fd, (struct wolfIP_sockaddr *)&addr, sizeof(addr)), 0);
ck_assert_int_eq(wolfIP_sock_listen(&s, listen_fd, 1), 0);
ck_assert_uint_eq(listener->bound_local_ip, secondary_ip);
ck_assert_int_eq(listener->sock.tcp.state, TCP_LISTEN);
ttl_before = listener->last_pkt_ttl;

/* (1) A non-SYN segment for the same port but a different local address
* must not mutate the listener's bookkeeping. */
inject_tcp_segment(&s, TEST_PRIMARY_IF, 0x0A0000A1U, primary_ip, 40000,
listen_port, 100, 0, 0);
ck_assert_uint_eq(listener->last_pkt_ttl, ttl_before);

/* (2) A segment for the bound address still matches (not over-restricted). */
inject_tcp_segment(&s, TEST_SECOND_IF, 0x0A0000A2U, secondary_ip, 40000,
listen_port, 200, 0, 0);
ck_assert_uint_eq(listener->last_pkt_ttl, 64);
}
END_TEST

START_TEST(test_tcp_listen_accepts_any_interface)
{
struct wolfIP s;
Expand Down
54 changes: 54 additions & 0 deletions src/test/unit/unit_tests_tcp_ack.c
Original file line number Diff line number Diff line change
Expand Up @@ -4029,6 +4029,60 @@ START_TEST(test_tcp_last_ack_closes_socket_delivers_closed_event)
}
END_TEST

/* F-8523: the post-callback reap in handle_socket_callbacks must not destroy
* a socket that the close callback closed and re-created in the same slot.
* The reap historically keyed on the slot's TCP_CLOSED state, which a fresh
* socket has by design, so it must be gated on the slot still holding the
* dispatched socket (same callback pair). */
static int test_f8523_recreated_fd;
static void test_f8523_close_recreate_cb(int fd, uint16_t events, void *arg)
{
struct wolfIP *s = (struct wolfIP *)arg;
(void)events;
/* Close the socket (frees its slot) and create a fresh one in its place. */
(void)wolfIP_sock_close(s, fd);
test_f8523_recreated_fd = wolfIP_sock_socket(s, AF_INET, IPSTACK_SOCK_STREAM, 0);
}

START_TEST(test_handle_socket_callbacks_keeps_recreated_socket)
{
struct wolfIP s;
struct tsocket *ts;
ip4 local_ip = 0x0A000001U;
uint16_t local_port = 6669;

wolfIP_init(&s);
mock_link_init(&s);
wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 0);

/* Put slot 0 in the RX-deferred close state: TCP_CLOSED with a pending
* CB_EVENT_CLOSED and an armed callback, no close_notify_pending. */
ts = &s.tcpsockets[0];
memset(ts, 0, sizeof(*ts));
ts->proto = WI_IPPROTO_TCP;
ts->S = &s;
ts->sock.tcp.state = TCP_CLOSED;
ts->local_ip = local_ip;
ts->src_port = local_port;
ts->callback = test_f8523_close_recreate_cb;
ts->callback_arg = &s;
ts->events = CB_EVENT_CLOSED;
queue_init(&ts->sock.tcp.rxbuf, ts->rxmem, RXBUF_SIZE, ts->sock.tcp.ack);

test_f8523_recreated_fd = -1;

/* poll Step 3 dispatches the deferred CB_EVENT_CLOSED; the callback closes
* the socket and re-creates a fresh one in the same slot. */
(void)wolfIP_poll(&s, 1);

/* The callback ran and created a fresh socket. */
ck_assert_int_ge(test_f8523_recreated_fd, 0);
/* The fresh socket must have survived the dispatcher's post-callback reap. */
ck_assert_uint_eq(s.tcpsockets[SOCKET_UNMARK(test_f8523_recreated_fd)].proto,
(uint8_t)WI_IPPROTO_TCP);
}
END_TEST

START_TEST(test_tcp_last_ack_partial_ack_keeps_socket_and_timer)
{
struct wolfIP s;
Expand Down
8 changes: 5 additions & 3 deletions src/tftp/wolftftp.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,11 @@

/* Worst-case RRQ/WRQ on the wire:
* opcode(2) + filename(MAX_FILENAME, null-terminated) + "octet\0"(6)
* + blksize/value(13) + timeout/value(12) + windowsize/value(13)
* + tsize/value(17) = 63 + MAX_FILENAME. The constant below adds a
* generous margin so future options do not silently truncate. */
* + blksize/value(13) + timeout/value(14) + windowsize/value(13)
* + tsize/value(17) = 65 + MAX_FILENAME. The constant below adds a
* generous margin so future options do not silently truncate. The timeout
* value is the widest: timeout_s is an unclamped uint16_t, so 65535 serializes
* as "65535\0" (6 bytes) behind "timeout\0" (8 bytes). */
#define WOLFTFTP_REQ_BUF_MAX (WOLFTFTP_MAX_FILENAME + 128U)

#define WOLFTFTP_ERR_IO (-1000)
Expand Down
Loading
Loading