summaryrefslogtreecommitdiff
path: root/test/regression/ftp_test/netx_ftp_server_activity_timeout_test.c
diff options
context:
space:
mode:
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.c363
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 */