/*************************************************************************** * Copyright (C) 2026 Eclipse ThreadX contributors * * This program and the accompanying materials are made available under the * terms of the MIT License which is available at * https://opensource.org/licenses/MIT. * * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). * The AI-generated portions may be considered public domain (CC0-1.0) * and not subject to the project's licence. The human contributor has * reviewed and verified that the code is correct. * * SPDX-License-Identifier: MIT and CC0-1.0 **************************************************************************/ /* This test covers the recvfromto() API from PR #376: * * 1. recvfromto() populates fromAddr with the sender's address/port and * toAddr with the destination (local) address/port of the UDP packet. * * 2. recvfromto() with NULL toAddr/toAddrLen works like recvfrom(). * * 3. inet_aton() still accepts abbreviated IPv4 forms such as a.b.c * and a.b (regression for the inet_aton revert). */ #include "tx_api.h" #include "nx_api.h" #if defined(NX_BSD_ENABLE) && !defined(NX_DISABLE_IPV4) && defined(__PRODUCT_NETXDUO__) #include "nxd_bsd.h" #define DEMO_STACK_SIZE 4096 #define BSD_THREAD_PRIORITY 2 #define TEST_PORT 4444 static TX_THREAD ntest_0; static TX_THREAD ntest_1; static NX_PACKET_POOL pool_0; static NX_IP ip_0; /* BSD receiver: 1.2.3.4 */ static NX_IP ip_1; /* NX sender: 1.2.3.5 */ static TX_SEMAPHORE sema_rx_ready; static TX_SEMAPHORE sema_done; static ULONG error_counter; static ULONG packet_pool_area[(512 + sizeof(NX_PACKET)) * 32 / 4]; static ULONG bsd_thread_area[DEMO_STACK_SIZE / sizeof(ULONG)]; static void ntest_0_entry(ULONG thread_input); static void ntest_1_entry(ULONG thread_input); extern void test_control_return(UINT status); extern void _nx_ram_network_driver_256(struct NX_IP_DRIVER_STRUCT *driver_req); #ifdef CTEST VOID test_application_define(void *first_unused_memory) #else void netx_bsd_recvfromto_test_application_define(void *first_unused_memory) #endif { CHAR *pointer = (CHAR *)first_unused_memory; UINT status; error_counter = 0; tx_thread_create(&ntest_0, "thread 0", ntest_0_entry, 0, pointer, DEMO_STACK_SIZE, 3, 3, TX_NO_TIME_SLICE, TX_AUTO_START); pointer += DEMO_STACK_SIZE; tx_thread_create(&ntest_1, "thread 1", ntest_1_entry, 0, pointer, DEMO_STACK_SIZE, 3, 3, TX_NO_TIME_SLICE, TX_AUTO_START); pointer += DEMO_STACK_SIZE; nx_system_initialize(); status = nx_packet_pool_create(&pool_0, "NetX Main Packet Pool", 512, packet_pool_area, sizeof(packet_pool_area)); if (status) error_counter++; status = nx_ip_create(&ip_0, "NetX IP Instance 0", IP_ADDRESS(1, 2, 3, 4), 0xFFFFFF00UL, &pool_0, _nx_ram_network_driver_256, pointer, 2048, 1); pointer += 2048; if (status) error_counter++; status = nx_ip_create(&ip_1, "NetX IP Instance 1", IP_ADDRESS(1, 2, 3, 5), 0xFFFFFF00UL, &pool_0, _nx_ram_network_driver_256, pointer, 2048, 1); pointer += 2048; if (status) error_counter++; status = nx_arp_enable(&ip_0, (void *)pointer, 1024); pointer += 1024; if (status) error_counter++; status = nx_arp_enable(&ip_1, (void *)pointer, 1024); pointer += 1024; if (status) error_counter++; status = nx_udp_enable(&ip_0); status += nx_udp_enable(&ip_1); if (status) error_counter++; status = bsd_initialize(&ip_0, &pool_0, (CHAR *)&bsd_thread_area[0], sizeof(bsd_thread_area), BSD_THREAD_PRIORITY); if (status) error_counter++; status = tx_semaphore_create(&sema_rx_ready, "RX READY", 0); if (status) error_counter++; status = tx_semaphore_create(&sema_done, "DONE", 0); if (status) error_counter++; } /* Thread 0: BSD receiver */ static void ntest_0_entry(ULONG thread_input) { INT sock; struct nx_bsd_sockaddr_in local_addr; struct nx_bsd_sockaddr_in from_addr; struct nx_bsd_sockaddr_in to_addr; INT from_len; INT to_len; CHAR buf[64]; INT ret; UINT actual_status; /* Wait for IP stack initialisation. */ nx_ip_status_check(&ip_0, NX_IP_INITIALIZE_DONE, &actual_status, NX_IP_PERIODIC_RATE); /* --- Test 1: recvfromto() populates fromAddr and toAddr --- */ sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) { error_counter++; test_control_return(1); return; } memset(&local_addr, 0, sizeof(local_addr)); local_addr.sin_family = AF_INET; local_addr.sin_port = htons(TEST_PORT); local_addr.sin_addr.s_addr = htonl(IP_ADDRESS(1, 2, 3, 4)); if (bind(sock, (struct nx_bsd_sockaddr *)&local_addr, sizeof(local_addr)) < 0) { error_counter++; soc_close(sock); test_control_return(1); return; } /* Signal sender that receiver is ready. */ tx_semaphore_put(&sema_rx_ready); memset(&from_addr, 0, sizeof(from_addr)); memset(&to_addr, 0, sizeof(to_addr)); from_len = sizeof(from_addr); to_len = sizeof(to_addr); ret = recvfromto(sock, buf, sizeof(buf), 0, (struct nx_bsd_sockaddr *)&from_addr, &from_len, (struct nx_bsd_sockaddr *)&to_addr, &to_len); if (ret <= 0) { error_counter++; } else { /* Validate fromAddr: must be 1.2.3.5 : any port */ if (from_addr.sin_family != AF_INET) error_counter++; if (ntohl(from_addr.sin_addr.s_addr) != IP_ADDRESS(1, 2, 3, 5)) error_counter++; /* Validate toAddr: must be 1.2.3.4 : TEST_PORT */ if (to_addr.sin_family != AF_INET) error_counter++; if (ntohl(to_addr.sin_addr.s_addr) != IP_ADDRESS(1, 2, 3, 4)) error_counter++; if (ntohs(to_addr.sin_port) != TEST_PORT) error_counter++; /* toAddrLen must be trimmed to sizeof(sockaddr_in). */ if (to_len != (INT)sizeof(struct nx_bsd_sockaddr_in)) error_counter++; } soc_close(sock); /* --- Test 2: recvfromto() with NULL toAddr works like recvfrom() --- */ sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (sock < 0) { error_counter++; test_control_return(1); return; } memset(&local_addr, 0, sizeof(local_addr)); local_addr.sin_family = AF_INET; local_addr.sin_port = htons(TEST_PORT); local_addr.sin_addr.s_addr = htonl(IP_ADDRESS(1, 2, 3, 4)); if (bind(sock, (struct nx_bsd_sockaddr *)&local_addr, sizeof(local_addr)) < 0) { error_counter++; soc_close(sock); test_control_return(1); return; } tx_semaphore_put(&sema_rx_ready); memset(&from_addr, 0, sizeof(from_addr)); from_len = sizeof(from_addr); ret = recvfromto(sock, buf, sizeof(buf), 0, (struct nx_bsd_sockaddr *)&from_addr, &from_len, NX_NULL, NX_NULL); if (ret <= 0) error_counter++; soc_close(sock); /* --- Test 3: inet_aton() still accepts abbreviated IPv4 forms --- */ { struct in_addr in_val; INT rc; /* a.b format: 11.657930 == 11.0x0a0a0a == 11.10.10.10 */ rc = inet_aton("11.657930", &in_val); if (rc == 0) error_counter++; /* a.b.c format: 11.10.2570 == 11.10.0x0a0a == 11.10.10.10 */ rc = inet_aton("11.10.2570", &in_val); if (rc == 0) error_counter++; /* standard dotted-decimal must still work */ rc = inet_aton("11.10.10.10", &in_val); if (rc == 0) error_counter++; } tx_semaphore_put(&sema_done); } /* Thread 1: NX UDP sender */ static void ntest_1_entry(ULONG thread_input) { NX_UDP_SOCKET udp_socket; NX_PACKET *packet_ptr; NXD_ADDRESS dest_addr; UINT actual_status; nx_ip_status_check(&ip_1, NX_IP_INITIALIZE_DONE, &actual_status, NX_IP_PERIODIC_RATE); nx_udp_socket_create(&ip_1, &udp_socket, "UDP sender", NX_IP_NORMAL, NX_FRAGMENT_OKAY, NX_IP_TIME_TO_LIVE, 8); nx_udp_socket_bind(&udp_socket, NX_ANY_PORT, NX_WAIT_FOREVER); memset(&dest_addr, 0, sizeof(dest_addr)); dest_addr.nxd_ip_version = NX_IP_VERSION_V4; dest_addr.nxd_ip_address.v4 = IP_ADDRESS(1, 2, 3, 4); /* Send for Test 1 */ tx_semaphore_get(&sema_rx_ready, NX_IP_PERIODIC_RATE); nx_packet_allocate(&pool_0, &packet_ptr, NX_UDP_PACKET, NX_WAIT_FOREVER); nx_packet_data_append(packet_ptr, "hello", 5, &pool_0, NX_WAIT_FOREVER); nxd_udp_socket_send(&udp_socket, packet_ptr, &dest_addr, TEST_PORT); /* Send for Test 2 */ tx_semaphore_get(&sema_rx_ready, NX_IP_PERIODIC_RATE); nx_packet_allocate(&pool_0, &packet_ptr, NX_UDP_PACKET, NX_WAIT_FOREVER); nx_packet_data_append(packet_ptr, "world", 5, &pool_0, NX_WAIT_FOREVER); nxd_udp_socket_send(&udp_socket, packet_ptr, &dest_addr, TEST_PORT); nx_udp_socket_unbind(&udp_socket); nx_udp_socket_delete(&udp_socket); /* Wait for receiver to finish then report result. */ tx_semaphore_get(&sema_done, 5 * NX_IP_PERIODIC_RATE); test_control_return(error_counter ? 1 : 0); } #else /* !(NX_BSD_ENABLE && !NX_DISABLE_IPV4) */ #ifdef CTEST VOID test_application_define(void *first_unused_memory) #else void netx_bsd_recvfromto_test_application_define(void *first_unused_memory) #endif { NX_PARAMETER_NOT_USED(first_unused_memory); test_control_return(3); /* skip */ } #endif /* NX_BSD_ENABLE && !NX_DISABLE_IPV4 */