/***************************************************************************/ /* Copyright (c) 2024 Microsoft Corporation */ /* 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. */ /* */ /* SPDX-License-Identifier: MIT */ /***************************************************************************/ /* This test checks for the Telnet Server to negotiate with a Telnet Client for three common Telnet options. It offers to ECHO, Suppress Go Ahead and asks the peer not to ECHO (Don't Echo). The Telnet Client is programmed to acknowledge the ECHO and SGA, and Won't Echo. Required configuration settings: NX_TELNET_SERVER_OPTION_DISABLE not defined NX_TELNET_SERVER_USER_CREATE_PACKET_POOL defined */ #include "tx_api.h" #include "nx_api.h" extern void test_control_return(UINT); #if defined(NX_TELNET_SERVER_USER_CREATE_PACKET_POOL) && !defined(NX_DISABLE_IPV4) #include "nxd_telnet_client.h" #include "nxd_telnet_server.h" #define DEMO_STACK_SIZE 4096 /* Define the ThreadX and NetX object control blocks... */ static TX_THREAD server_thread; static TX_THREAD client_thread; static NX_PACKET_POOL pool_server; static NX_PACKET_POOL pool_client; static NX_IP ip_server; static NX_IP ip_client; /* Define TELNET server object. */ static NX_TELNET_SERVER my_server; static NX_TELNET_CLIENT my_client; #define SERVER_ADDRESS IP_ADDRESS(1,2,3,4) #define CLIENT_ADDRESS IP_ADDRESS(1,2,3,5) /* Define the counters and flags used in the demo application... */ static ULONG error_counter; static UINT server_option_negotiation; /* Define timeout in ticks for connecting and sending/receiving data. */ #define TELNET_TIMEOUT 200 void thread_server_entry(ULONG thread_input); void thread_client_entry(ULONG thread_input); extern void _nx_ram_network_driver_512(struct NX_IP_DRIVER_STRUCT *driver_req); static UCHAR send_buff[256]; /* Define the application's TELNET Server callback routines. */ void telnet_new_connection(NX_TELNET_SERVER *server_ptr, UINT logical_connection); void telnet_receive_data(NX_TELNET_SERVER *server_ptr, UINT logical_connection, NX_PACKET *packet_ptr); void telnet_connection_end(NX_TELNET_SERVER *server_ptr, UINT logical_connection); /* Define what the initial system looks like. */ #ifdef CTEST VOID test_application_define(void *first_unused_memory) #else void netx_telnet_server_options_negotiate_test_application_define(void *first_unused_memory) #endif { UINT status; CHAR *pointer; error_counter = 0; /* Setup the working pointer. */ pointer = (CHAR *) first_unused_memory; /* Create the server thread. */ status = tx_thread_create(&server_thread, "Telnet Server thread", thread_server_entry, 0, pointer, DEMO_STACK_SIZE, 12, 12, TX_NO_TIME_SLICE, TX_AUTO_START); pointer = pointer + DEMO_STACK_SIZE; if (status) { printf("Error creating Server thread 0x%x\n", status); } /* Initialize the NetX system. */ nx_system_initialize(); /* Create the server packet pool. */ nx_packet_pool_create(&pool_server, "Server NetX Packet Pool", 600, pointer, 8192); pointer = pointer + 8192; /* Create the Server IP instance. */ nx_ip_create(&ip_server, "Server NetX IP Instance", SERVER_ADDRESS, 0xFFFFFF00UL, &pool_server, _nx_ram_network_driver_512, pointer, 4096, 1); pointer = pointer + 4096; /* Enable ARP and supply ARP cache memory for the server. */ nx_arp_enable(&ip_server, (void *) pointer, 1024); pointer = pointer + 1024; /* Enable TCP processing for both IP instances. */ nx_tcp_enable(&ip_server); nx_icmp_enable(&ip_server); /* Create the NetX Duo TELNET Server. */ status = nx_telnet_server_create(&my_server, "Telnet Server", &ip_server, pointer, 2048, telnet_new_connection, telnet_receive_data, telnet_connection_end); /* Check for errors. */ if (status) error_counter++; pointer = pointer + 2048; /* Create the Client thread. */ status = tx_thread_create(&client_thread, "Telnet Client thread", thread_client_entry, 0, pointer, DEMO_STACK_SIZE, 12, 12, TX_NO_TIME_SLICE, TX_AUTO_START); if (status) { error_counter++; } pointer = pointer + DEMO_STACK_SIZE; /* Create the client packet pool. */ status = nx_packet_pool_create(&pool_client, "Client NetX Packet Pool", 600, pointer, 8192); if (status) { error_counter++; } pointer = pointer + 8192; /* Create the Client IP instance. */ status = nx_ip_create(&ip_client, "Client NetX IP Instance", CLIENT_ADDRESS, 0xFFFFFF00UL, &pool_client, _nx_ram_network_driver_512, pointer, 4096, 1); if (status) { error_counter++; } pointer = pointer + 4096; /* Enable ARP and supply ARP cache memory for the client. */ nx_arp_enable(&ip_client, (void *) pointer, 1024); pointer = pointer + 1024; /* Enable TCP and ICMP processing for client IP instance. */ nx_tcp_enable(&ip_client); nx_icmp_enable(&ip_client); return; } void thread_client_entry(ULONG thread_input) { UINT status; NX_PACKET *my_packet, *my_packet2, *rcv_packet; tx_thread_sleep(100); /* Create a TELENT client instance. */ status = nx_telnet_client_create(&my_client, "TELNET Client", &ip_client, 6 * NX_IP_PERIODIC_RATE); if (status) error_counter++; /* Connect the TELNET client to the TELNET Server at port 23 over IPv4. */ status = nx_telnet_client_connect(&my_client, SERVER_ADDRESS, NX_TELNET_SERVER_PORT, 200); if (status) error_counter++; /* This should be the welcome message. */ status = nx_telnet_client_packet_receive(&my_client, &rcv_packet, TELNET_TIMEOUT); if (status) error_counter++; /* To do verify this is just a hello message. */ nx_packet_release(rcv_packet); /* Server should be negotiating options. */ status = nx_telnet_client_packet_receive(&my_client, &rcv_packet, TELNET_TIMEOUT); if (status) error_counter++; /* Parse the options. Should be will echo don't echo and will suppress go ahead */ nx_packet_release(rcv_packet); /* Prepare a Do ECHO reply */ send_buff[0] = 0xFF; send_buff[1] = 0xFD; send_buff[2] = 0x01; /* Allocate a packet. */ status = nx_packet_allocate(&pool_client, &my_packet, NX_TCP_PACKET, NX_WAIT_FOREVER); if (status) error_counter++; /* Build a simple 1-byte message. */ nx_packet_data_append(my_packet, send_buff, sizeof(send_buff), &pool_client, NX_WAIT_FOREVER); /* Send the packet to the TELNET Server. */ status = nx_telnet_client_packet_send(&my_client, my_packet, TELNET_TIMEOUT); if (status) error_counter++; /* Prepare a second reply: WONT ECHO, DO SUPPRESS GO AHEAD */ send_buff[0] = 0xFF; send_buff[1] = 0xFC; send_buff[2] = 0x01; send_buff[0] = 0xFF; send_buff[1] = 0xFD; send_buff[2] = 0x03; /* Allocate a packet. */ status = nx_packet_allocate(&pool_client, &my_packet2, NX_TCP_PACKET, NX_WAIT_FOREVER); if (status) error_counter++; /* Load the reply in to the packet. */ nx_packet_data_append(my_packet2, send_buff, sizeof(send_buff), &pool_client, NX_WAIT_FOREVER); /* Send the packet to the TELNET Server. */ status = nx_telnet_client_packet_send(&my_client, my_packet2, TELNET_TIMEOUT); if (status) error_counter++; tx_thread_sleep(200); /* Now disconnect form the TELNET Server. */ nx_telnet_client_disconnect(&my_client, 100); /* Delete the TELNET Client. */ status = nx_telnet_client_delete(&my_client); if (status) error_counter++; } /* Define the Telnet Server thread. */ void thread_server_entry(ULONG thread_input) { UINT status; tx_thread_sleep(20); /* Print out test information banner. */ printf("NetX Test: Telnet Server Negotiation Test............................"); /* Check for earlier error. */ if(error_counter) { printf("ERROR!\n"); test_control_return(1); } /* We have defined the NX_TELNET_CREATE_PACKET_POOL option for sending Telnet options (not disabled). So set the telnet packet pool. */ status = nx_telnet_server_packet_pool_set(&my_server, &pool_server); if (status != NX_SUCCESS) { error_counter++; } server_option_negotiation = NX_FALSE; /* Start the TELNET Server. */ status = nx_telnet_server_start(&my_server); /* Check for errors. */ if (status != NX_SUCCESS) { error_counter++; } do { /* Check on the result of optio negotiations while the client connection is live. */ NX_TELNET_CLIENT_REQUEST *client_req_ptr = &(my_server.nx_telnet_server_client_list[0]); if ((client_req_ptr -> nx_telnet_client_agree_server_will_SGA_success == NX_TRUE) && (client_req_ptr -> nx_telnet_client_agree_server_will_echo_success == NX_TRUE)) { /* negotiation successful. */ server_option_negotiation = NX_TRUE; } tx_thread_sleep(100); }while (my_server.nx_telnet_server_open_connections == 1); status = nx_telnet_server_stop(&my_server); /* Check for errors. */ if (status != NX_SUCCESS) { error_counter++; } if (error_counter || !server_option_negotiation) { printf("ERROR!\n"); test_control_return(1); } else { printf("SUCCESS!\n"); test_control_return(0); } return; } /* This routine is called by the NetX Telnet Server whenever a new Telnet client connection is established. */ void telnet_new_connection(NX_TELNET_SERVER *server_ptr, UINT logical_connection) { UINT status; NX_PACKET *packet_ptr; /* Allocate a packet for client greeting. */ status = nx_packet_allocate(&pool_server, &packet_ptr, NX_TCP_PACKET, NX_NO_WAIT); if (status != NX_SUCCESS) { error_counter++; return; } if (pool_server.nx_packet_pool_available < 2) { printf("Packet pool getting low...\n"); } /* Build a banner message and a prompt. */ nx_packet_data_append(packet_ptr, "**** Welcome to NetX TELNET Server ****\r\n\r\n\r\n", 45, &pool_server, NX_NO_WAIT); /* Send the packet to the client. */ status = nx_telnet_server_packet_send(server_ptr, logical_connection, packet_ptr, TELNET_TIMEOUT); if (status != NX_SUCCESS) { error_counter++; nx_packet_release(packet_ptr); } return; } /* This routine is called by the NetX Telnet Server whenever data is present on a Telnet client connection. */ void telnet_receive_data(NX_TELNET_SERVER *server_ptr, UINT logical_connection, NX_PACKET *packet_ptr) { UINT status; UCHAR alpha; /* This demo just echoes the character back and on sends a new prompt back to the client. A real system would most likely buffer the character(s) received in a buffer associated with the supplied logical connection and process according to it. */ /* Just throw away carriage returns. */ if ((packet_ptr -> nx_packet_prepend_ptr[0] == '\r') && (packet_ptr -> nx_packet_length == 1)) { nx_packet_release(packet_ptr); return; } /* Setup new line on line feed. */ if ((packet_ptr -> nx_packet_prepend_ptr[0] == '\n') || (packet_ptr -> nx_packet_prepend_ptr[1] == '\n')) { /* Clean up the packet. */ packet_ptr -> nx_packet_length = 0; packet_ptr -> nx_packet_prepend_ptr = packet_ptr -> nx_packet_data_start + NX_TCP_PACKET; packet_ptr -> nx_packet_append_ptr = packet_ptr -> nx_packet_data_start + NX_TCP_PACKET; /* Build the next prompt. */ nx_packet_data_append(packet_ptr, "\r\nNETX> ", 8, &pool_server, NX_NO_WAIT); /* Send the packet to the client. */ status = nx_telnet_server_packet_send(server_ptr, logical_connection, packet_ptr, TELNET_TIMEOUT); if (status) { nx_packet_release(packet_ptr); error_counter++; } return; } /* Pickup first character (usually only one from client). */ alpha = packet_ptr -> nx_packet_prepend_ptr[0]; /* Echo character. */ status = nx_telnet_server_packet_send(server_ptr, logical_connection, packet_ptr, TELNET_TIMEOUT); if (status != NX_SUCCESS) { error_counter++; nx_packet_release(packet_ptr); } /* Check for a disconnection. */ if (alpha == 'q') { /* Initiate server disconnection. */ nx_telnet_server_disconnect(server_ptr, logical_connection); } } /* This routine is called by the NetX Telnet Server whenever the client disconnects. */ void telnet_connection_end(NX_TELNET_SERVER *server_ptr, UINT logical_connection) { /* Cleanup any application specific connection or buffer information. */ } #else /* NX_TELNET_SERVER_USER_CREATE_PACKET_POOL */ #ifdef CTEST VOID test_application_define(void *first_unused_memory) #else void netx_telnet_server_options_negotiate_test_application_define(void *first_unused_memory) #endif { printf("NetX Test: Telnet Server Negotiation Test............................N/A\n"); test_control_return(3); } #endif /* NX_TELNET_SERVER_USER_CREATE_PACKET_POOL */