diff options
Diffstat (limited to 'test/regression/ftp_test/netx_ftp_server_activity_timeout_test.c')
| -rw-r--r-- | test/regression/ftp_test/netx_ftp_server_activity_timeout_test.c | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/test/regression/ftp_test/netx_ftp_server_activity_timeout_test.c b/test/regression/ftp_test/netx_ftp_server_activity_timeout_test.c new file mode 100644 index 00000000..2ef076b9 --- /dev/null +++ b/test/regression/ftp_test/netx_ftp_server_activity_timeout_test.c @@ -0,0 +1,363 @@ +/*************************************************************************** + * 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 + **************************************************************************/ + +/* FTP server activity timeout regression test. + * + * Verifies that when the FTP server inactivity timer fires it: + * 1. Calls the logout callback. + * 2. Resets the authenticated flag so that the next connection on the same + * socket slot cannot issue commands without a fresh USER/PASS exchange. + * 3. Allows a new connection to log in and operate normally afterwards. + */ + +#include "tx_api.h" +#include "fx_api.h" +#include "nx_api.h" +#include "nxd_ftp_client.h" +#include "nxd_ftp_server.h" + +extern void test_control_return(UINT); + +#if !defined(NX_DISABLE_IPV4) + +#define DEMO_STACK_SIZE 4096 +#define FTP_SERVER_ADDRESS IP_ADDRESS(1,2,3,4) +#define FTP_CLIENT_ADDRESS IP_ADDRESS(1,2,3,5) + +/* ThreadX / NetX / FileX control blocks. */ +static TX_THREAD server_thread; +static TX_THREAD client_thread; +static NX_PACKET_POOL server_pool; +static NX_IP server_ip; +static NX_PACKET_POOL client_pool; +static NX_IP client_ip; +static FX_MEDIA ram_disk; +static NX_FTP_CLIENT ftp_client; +static NX_FTP_SERVER ftp_server; + +/* Raw TCP socket used to probe auth-state after timeout. */ +static NX_TCP_SOCKET raw_socket; + +/* Test state. */ +static ULONG error_counter = 0; +static ULONG logout_counter = 0; +static UINT test_done = NX_FALSE; + +/* RAM disk memory. */ +static UCHAR ram_disk_memory[32000]; +static UCHAR ram_disk_sector_cache[512]; + +extern UINT _fx_media_format(FX_MEDIA *media_ptr, + VOID (*driver)(FX_MEDIA *media), + VOID *driver_info_ptr, + UCHAR *memory_ptr, UINT memory_size, + CHAR *volume_name, + UINT number_of_fats, + UINT directory_entries, + UINT hidden_sectors, + ULONG total_sectors, + UINT bytes_per_sector, + UINT sectors_per_cluster, + UINT heads, UINT sectors_per_track); + +extern void _fx_ram_driver(FX_MEDIA *media_ptr); +extern void _nx_ram_network_driver_512(NX_IP_DRIVER *driver_req_ptr); + +static void client_thread_entry(ULONG thread_input); +static void thread_server_entry(ULONG thread_input); + +static UINT server_login(struct NX_FTP_SERVER_STRUCT *ftp_server_ptr, + ULONG client_ip_address, UINT client_port, + CHAR *name, CHAR *password, CHAR *extra_info); +static UINT server_logout(struct NX_FTP_SERVER_STRUCT *ftp_server_ptr, + ULONG client_ip_address, UINT client_port, + CHAR *name, CHAR *password, CHAR *extra_info); + +#ifdef CTEST +VOID test_application_define(void *first_unused_memory) +#else +void netx_ftp_server_activity_timeout_test_application_define(void *first_unused_memory) +#endif +{ +UINT status; +UCHAR *pointer = (UCHAR *)first_unused_memory; + + tx_thread_create(&server_thread, "FTP Server thread", thread_server_entry, 0, + pointer, DEMO_STACK_SIZE, 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer += DEMO_STACK_SIZE; + + nx_system_initialize(); + + status = nx_packet_pool_create(&server_pool, "NetX Server Packet Pool", 512, pointer, 8192); + pointer += 8192; + if (status) error_counter++; + + status = nx_ip_create(&server_ip, "NetX Server IP Instance", FTP_SERVER_ADDRESS, 0xFFFFFF00UL, + &server_pool, _nx_ram_network_driver_512, pointer, 2048, 1); + pointer += 2048; + if (status) error_counter++; + + status = nx_arp_enable(&server_ip, (void *)pointer, 1024); + pointer += 1024; + if (status) error_counter++; + + status = nx_tcp_enable(&server_ip); + if (status) error_counter++; + + status = nx_ftp_server_create(&ftp_server, "FTP Server Instance", &server_ip, + &ram_disk, pointer, DEMO_STACK_SIZE, &server_pool, + server_login, server_logout); + pointer += DEMO_STACK_SIZE; + if (status) error_counter++; + + status = tx_thread_create(&client_thread, "FTP Client thread", client_thread_entry, 0, + pointer, DEMO_STACK_SIZE, 6, 6, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer += DEMO_STACK_SIZE; + if (status) error_counter++; + + status = nx_packet_pool_create(&client_pool, "NetX Client Packet Pool", 256, pointer, 8192); + pointer += 8192; + if (status) error_counter++; + + status = nx_ip_create(&client_ip, "NetX Client IP Instance", FTP_CLIENT_ADDRESS, 0xFFFFFF00UL, + &client_pool, _nx_ram_network_driver_512, pointer, 2048, 1); + pointer += 2048; + if (status) error_counter++; + + status = nx_arp_enable(&client_ip, (void *)pointer, 1024); + pointer += 1024; + if (status) error_counter++; + + status = nx_tcp_enable(&client_ip); + if (status) error_counter++; +} + +void client_thread_entry(ULONG thread_input) +{ +UINT status; +UINT i; +NX_PACKET *packet_ptr; + + /* Format and open the RAM disk. */ + status = _fx_media_format(&ram_disk, _fx_ram_driver, ram_disk_memory, + ram_disk_sector_cache, sizeof(ram_disk_sector_cache), + "MY_RAM_DISK", 1, 32, 0, 256, 128, 1, 1, 1); + if (status) error_counter++; + + status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, + ram_disk_memory, ram_disk_sector_cache, + sizeof(ram_disk_sector_cache)); + if (status) error_counter++; + + /* --- Phase 1: connect and log in via the FTP client API. --- */ + + status = nx_ftp_client_create(&ftp_client, "FTP Client", &client_ip, 2000, &client_pool); + if (status) error_counter++; + + status = nx_ftp_client_connect(&ftp_client, FTP_SERVER_ADDRESS, "name", "password", + NX_IP_PERIODIC_RATE); + if (status) error_counter++; + + /* --- Phase 2: force the inactivity timeout on the server. --- + * + * Write 1 directly into the server's activity-timeout counter for this + * client slot so the next timer tick decrements it to zero. This is a + * deliberate white-box test hook – see the precedent in + * netx_ftp_access_control_commands_03_test.c. + */ + ftp_server.nx_ftp_server_client_list[0].nx_ftp_client_request_activity_timeout = 1; + + /* Poll until the server's timeout processing runs. Bound the loop to + * avoid an infinite hang; each iteration advances simulation time by one + * second. Two full NX_FTP_TIMEOUT_PERIOD intervals is a safe upper bound. + */ + for (i = 0; i < (UINT)(NX_FTP_TIMEOUT_PERIOD * 2 + 10); i++) + { + if (ftp_server.nx_ftp_server_activity_timeouts > 0) + break; + tx_thread_sleep(NX_IP_PERIODIC_RATE); + } + + if (ftp_server.nx_ftp_server_activity_timeouts == 0) + error_counter++; /* Timeout never fired. */ + + /* Verify the logout callback was invoked. */ + if (logout_counter != 1) + error_counter++; + + /* Clean up the FTP client – the server already closed its end. */ + nx_ftp_client_disconnect(&ftp_client, NX_IP_PERIODIC_RATE); + nx_ftp_client_delete(&ftp_client); + + /* Give the server a moment to relisten. */ + tx_thread_sleep(NX_IP_PERIODIC_RATE); + + /* --- Phase 3: raw TCP connection to verify stale-auth is cleared. + * + * Connect without sending USER/PASS and send a NOOP command. If the + * authenticated flag was left set (the pre-fix bug), the server would + * accept the command and return "200". With the fix it must return "530". + */ + status = nx_tcp_socket_create(&client_ip, &raw_socket, "Raw Client Socket", + NX_IP_NORMAL, NX_FRAGMENT_OKAY, + NX_IP_TIME_TO_LIVE, 512, NX_NULL, NX_NULL); + if (status) error_counter++; + + status = nx_tcp_client_socket_bind(&raw_socket, NX_ANY_PORT, NX_IP_PERIODIC_RATE); + if (status) error_counter++; + + status = nx_tcp_client_socket_connect(&raw_socket, FTP_SERVER_ADDRESS, + NX_FTP_SERVER_CONTROL_PORT, + 5 * NX_IP_PERIODIC_RATE); + if (status) error_counter++; + + /* Read and discard the "220 Connection Ready" banner. */ + status = nx_tcp_socket_receive(&raw_socket, &packet_ptr, 5 * NX_IP_PERIODIC_RATE); + if (status) + { + error_counter++; + } + else + { + nx_packet_release(packet_ptr); + } + + /* Send "NOOP\r\n" without USER/PASS. */ + status = nx_packet_allocate(&client_pool, &packet_ptr, NX_TCP_PACKET, + NX_IP_PERIODIC_RATE); + if (status) error_counter++; + + if (!status) + { + (VOID)memcpy(packet_ptr -> nx_packet_prepend_ptr, "NOOP\r\n", 6); /* MISRA 21.17: bounded */ + packet_ptr -> nx_packet_length = 6; + packet_ptr -> nx_packet_append_ptr = packet_ptr -> nx_packet_prepend_ptr + 6; + status = nx_tcp_socket_send(&raw_socket, packet_ptr, NX_IP_PERIODIC_RATE); + if (status) error_counter++; + } + + /* Read the server response and verify it is "530" (not logged in). */ + status = nx_tcp_socket_receive(&raw_socket, &packet_ptr, 5 * NX_IP_PERIODIC_RATE); + if (status) + { + error_counter++; + } + else + { + /* Response must start with "530". */ + if (memcmp(packet_ptr -> nx_packet_prepend_ptr, "530", 3) != 0) + error_counter++; + nx_packet_release(packet_ptr); + } + + nx_tcp_socket_disconnect(&raw_socket, NX_IP_PERIODIC_RATE); + nx_tcp_client_socket_unbind(&raw_socket); + nx_tcp_socket_delete(&raw_socket); + + /* --- Phase 4: fresh login must succeed and commands must work. --- */ + + status = nx_ftp_client_create(&ftp_client, "FTP Client 2", &client_ip, 2000, &client_pool); + if (status) error_counter++; + + status = nx_ftp_client_connect(&ftp_client, FTP_SERVER_ADDRESS, "name", "password", + 5 * NX_IP_PERIODIC_RATE); + if (status) error_counter++; + + /* nx_ftp_client_connect success means USER/PASS was accepted, verifying + * that the server correctly resets authentication on timeout. + */ + + status = nx_ftp_client_disconnect(&ftp_client, NX_IP_PERIODIC_RATE); + if (status) error_counter++; + + status = nx_ftp_client_delete(&ftp_client); + if (status) error_counter++; + + test_done = NX_TRUE; +} + +void thread_server_entry(ULONG thread_input) +{ +UINT status; + + printf("NetX Test: FTP Server Activity Timeout Test.........................."); + + if (error_counter) + { + printf("ERROR!\n"); + test_control_return(1); + } + + status = nx_ftp_server_start(&ftp_server); + if (status) error_counter++; + + while (test_done == NX_FALSE) + tx_thread_sleep(NX_IP_PERIODIC_RATE); + + status = nx_ftp_server_delete(&ftp_server); + if (status) error_counter++; + + if (error_counter != 0) + { + printf("ERROR!\n"); + test_control_return(1); + } + else + { + printf("SUCCESS!\n"); + test_control_return(0); + } +} + +static UINT server_login(struct NX_FTP_SERVER_STRUCT *ftp_server_ptr, + ULONG client_ip_address, UINT client_port, + CHAR *name, CHAR *password, CHAR *extra_info) +{ + NX_PARAMETER_NOT_USED(ftp_server_ptr); + NX_PARAMETER_NOT_USED(client_ip_address); + NX_PARAMETER_NOT_USED(client_port); + NX_PARAMETER_NOT_USED(name); + NX_PARAMETER_NOT_USED(password); + NX_PARAMETER_NOT_USED(extra_info); + return(NX_SUCCESS); +} + +static UINT server_logout(struct NX_FTP_SERVER_STRUCT *ftp_server_ptr, + ULONG client_ip_address, UINT client_port, + CHAR *name, CHAR *password, CHAR *extra_info) +{ + NX_PARAMETER_NOT_USED(ftp_server_ptr); + NX_PARAMETER_NOT_USED(client_ip_address); + NX_PARAMETER_NOT_USED(client_port); + NX_PARAMETER_NOT_USED(name); + NX_PARAMETER_NOT_USED(password); + NX_PARAMETER_NOT_USED(extra_info); + logout_counter++; + return(NX_SUCCESS); +} + +#else + +#ifdef CTEST +VOID test_application_define(void *first_unused_memory) +#else +void netx_ftp_server_activity_timeout_test_application_define(void *first_unused_memory) +#endif +{ + printf("NetX Test: FTP Server Activity Timeout Test..........................N/A\n"); + test_control_return(3); +} + +#endif /* NX_DISABLE_IPV4 */ |
