1 /***************************************************************************
2  * Copyright (c) 2024 Microsoft Corporation
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the MIT License which is available at
6  * https://opensource.org/licenses/MIT.
7  *
8  * SPDX-License-Identifier: MIT
9  **************************************************************************/
10 
11 
12 /**************************************************************************/
13 /**************************************************************************/
14 /**                                                                       */
15 /** NetX Component                                                        */
16 /**                                                                       */
17 /**   Transmission Control Protocol (TCP)                                 */
18 /**                                                                       */
19 /**************************************************************************/
20 /**************************************************************************/
21 
22 #define NX_SOURCE_CODE
23 
24 
25 /* Include necessary system files.  */
26 
27 #include "nx_api.h"
28 #include "nx_tcp.h"
29 #include "tx_thread.h"
30 #include "nx_ipv6.h"
31 #include "nx_ipv4.h"
32 #include "nx_ip.h"
33 #ifdef NX_ENABLE_HTTP_PROXY
34 #include "nx_http_proxy_client.h"
35 #endif /* NX_ENABLE_HTTP_PROXY */
36 
37 
38 #ifdef NX_ENABLE_TCPIP_OFFLOAD
39 /**************************************************************************/
40 /*                                                                        */
41 /*  FUNCTION                                               RELEASE        */
42 /*                                                                        */
43 /*    _nxd_tcp_client_socket_driver_connect               PORTABLE C      */
44 /*                                                           6.1.8        */
45 /*  AUTHOR                                                                */
46 /*                                                                        */
47 /*    Yuxin Zhou, Microsoft Corporation                                   */
48 /*                                                                        */
49 /*  DESCRIPTION                                                           */
50 /*                                                                        */
51 /*    This function handles the connect request for TCP/IP offload        */
52 /*    interface.                                                          */
53 /*                                                                        */
54 /*  INPUT                                                                 */
55 /*                                                                        */
56 /*    socket_ptr                            Pointer to TCP client socket  */
57 /*    server_ip                             IP address of server          */
58 /*    server_port                           Port number of server         */
59 /*    wait_option                           Suspension option             */
60 /*                                                                        */
61 /*  OUTPUT                                                                */
62 /*                                                                        */
63 /*    status                                Completion status             */
64 /*                                                                        */
65 /*  CALLS                                                                 */
66 /*                                                                        */
67 /*    tx_mutex_get                          Obtain protection             */
68 /*    tx_mutex_put                          Release protection            */
69 /*                                                                        */
70 /*  CALLED BY                                                             */
71 /*                                                                        */
72 /*    _nxd_tcp_client_socket_connect                                      */
73 /*                                                                        */
74 /*  RELEASE HISTORY                                                       */
75 /*                                                                        */
76 /*    DATE              NAME                      DESCRIPTION             */
77 /*                                                                        */
78 /*  08-02-2021     Yuxin Zhou               Initial Version 6.1.8         */
79 /*                                                                        */
80 /**************************************************************************/
_nxd_tcp_client_socket_driver_connect(NX_TCP_SOCKET * socket_ptr,NXD_ADDRESS * server_ip,UINT server_port,ULONG wait_option)81 static UINT _nxd_tcp_client_socket_driver_connect(NX_TCP_SOCKET *socket_ptr,
82                                                   NXD_ADDRESS *server_ip,
83                                                   UINT server_port,
84                                                   ULONG wait_option)
85 {
86 UINT          status;
87 NX_INTERFACE *interface_ptr;
88 NX_IP        *ip_ptr;
89 
90 
91     /* Setup the pointer to the associated IP instance.  */
92     ip_ptr =  socket_ptr -> nx_tcp_socket_ip_ptr;
93 
94     /* Clear the socket timeout.  */
95     socket_ptr -> nx_tcp_socket_timeout =  0;
96 
97     /* Let TCP/IP offload interface make the connection.  */
98     interface_ptr = socket_ptr -> nx_tcp_socket_connect_interface;
99     status = interface_ptr -> nx_interface_tcpip_offload_handler(ip_ptr, interface_ptr, socket_ptr,
100                                                                  NX_TCPIP_OFFLOAD_TCP_CLIENT_SOCKET_CONNECT,
101                                                                  NX_NULL, NX_NULL, server_ip,
102                                                                  socket_ptr -> nx_tcp_socket_port,
103                                                                  &server_port, wait_option);
104     if ((status == NX_SUCCESS) || (status == NX_IN_PROGRESS))
105     {
106 
107         /* Set MSS.  */
108 #ifndef NX_DISABLE_IPV4
109         if (server_ip -> nxd_ip_version == NX_IP_VERSION_V4)
110         {
111             socket_ptr -> nx_tcp_socket_mss =
112                 (ULONG)((interface_ptr -> nx_interface_ip_mtu_size - sizeof(NX_IPV4_HEADER)) - sizeof(NX_TCP_HEADER));
113         }
114 #endif /* !NX_DISABLE_IPV4  */
115 #ifdef FEATURE_NX_IPV6
116         if (server_ip -> nxd_ip_version == NX_IP_VERSION_V6)
117         {
118             socket_ptr -> nx_tcp_socket_mss =
119                 (ULONG)((interface_ptr -> nx_interface_ip_mtu_size - sizeof(NX_IPV6_HEADER)) - sizeof(NX_TCP_HEADER));
120         }
121 #endif /* FEATURE_NX_IPV6 */
122         socket_ptr -> nx_tcp_socket_connect_mss = socket_ptr -> nx_tcp_socket_mss;
123         socket_ptr -> nx_tcp_socket_peer_mss = socket_ptr -> nx_tcp_socket_mss;
124 
125         if (status == NX_SUCCESS)
126         {
127 
128             /* Connected to server.  */
129             socket_ptr -> nx_tcp_socket_state =  NX_TCP_ESTABLISHED;
130 #ifndef NX_DISABLE_EXTENDED_NOTIFY_SUPPORT
131 
132             /* Is a connection completion callback registered with the TCP socket?  */
133             if (socket_ptr -> nx_tcp_establish_notify)
134             {
135 
136                 /* Call the application's establish callback function.    */
137                 (socket_ptr -> nx_tcp_establish_notify)(socket_ptr);
138             }
139 #endif
140 
141             /* Release the IP protection.  */
142             tx_mutex_put(&(ip_ptr -> nx_ip_protection));
143             return(NX_SUCCESS);
144         }
145         else
146         {
147 
148             /* Connected to server.  */
149             socket_ptr -> nx_tcp_socket_state =  NX_TCP_SYN_SENT;
150         }
151     }
152     else
153     {
154 
155         /* Unable to connect to server.  */
156 
157 #ifndef NX_DISABLE_TCP_INFO
158 
159         /* Reduce the active connections count.  */
160         ip_ptr -> nx_ip_tcp_active_connections--;
161 
162         /* Reduce the TCP connections count.  */
163         ip_ptr -> nx_ip_tcp_connections--;
164 #endif
165 
166         /* Restore the socket state. */
167         socket_ptr -> nx_tcp_socket_state = NX_TCP_CLOSED;
168 
169         /* Reset server port and server IP address. */
170         memset(&socket_ptr -> nx_tcp_socket_connect_ip, 0, sizeof(NXD_ADDRESS));
171         socket_ptr -> nx_tcp_socket_connect_port = 0;
172 
173         /* Reset the next_hop_address information. */
174         socket_ptr -> nx_tcp_socket_next_hop_address = 0;
175 
176         /* Release the IP protection.  */
177         tx_mutex_put(&(ip_ptr -> nx_ip_protection));
178         return(NX_TCPIP_OFFLOAD_ERROR);
179     }
180 
181     /* Release the IP protection.  */
182     tx_mutex_put(&(ip_ptr -> nx_ip_protection));
183     return(NX_SUCCESS);
184 }
185 #endif /* NX_ENABLE_TCPIP_OFFLOAD */
186 
187 
188 /**************************************************************************/
189 /*                                                                        */
190 /*  FUNCTION                                               RELEASE        */
191 /*                                                                        */
192 /*    _nxd_tcp_client_socket_connect                     PORTABLE C       */
193 /*                                                           6.2.0        */
194 /*  AUTHOR                                                                */
195 /*                                                                        */
196 /*    Yuxin Zhou, Microsoft Corporation                                   */
197 /*                                                                        */
198 /*  DESCRIPTION                                                           */
199 /*                                                                        */
200 /*    This function handles the connect request for the supplied socket.  */
201 /*    If bound and not connected, this function will send the first SYN   */
202 /*    message to the specified server to initiate the connection process. */
203 /*                                                                        */
204 /*  INPUT                                                                 */
205 /*                                                                        */
206 /*    socket_ptr                            Pointer to TCP client socket  */
207 /*    server_ip                             IP address of server          */
208 /*    server_port                           Port number of server         */
209 /*    wait_option                           Suspension option             */
210 /*                                                                        */
211 /*  OUTPUT                                                                */
212 /*                                                                        */
213 /*    status                                Completion status             */
214 /*                                                                        */
215 /*  CALLS                                                                 */
216 /*                                                                        */
217 /*    _nx_tcp_socket_thread_suspend         Suspend thread for connection */
218 /*    _nx_tcp_packet_send_syn               Send SYN packet               */
219 /*    _nx_ip_route_find                     Find a suitable outgoing      */
220 /*                                            interface.                  */
221 /*    tx_mutex_get                          Obtain protection             */
222 /*    tx_mutex_put                          Release protection            */
223 /*    _nx_http_proxy_client_initialize      Initialize the HTTP Proxy     */
224 /*                                                                        */
225 /*  CALLED BY                                                             */
226 /*                                                                        */
227 /*    Application Code                                                    */
228 /*                                                                        */
229 /*  RELEASE HISTORY                                                       */
230 /*                                                                        */
231 /*    DATE              NAME                      DESCRIPTION             */
232 /*                                                                        */
233 /*  05-19-2020     Yuxin Zhou               Initial Version 6.0           */
234 /*  09-30-2020     Yuxin Zhou               Modified comment(s),          */
235 /*                                            resulting in version 6.1    */
236 /*  08-02-2021     Yuxin Zhou               Modified comment(s), and      */
237 /*                                            supported TCP/IP offload,   */
238 /*                                            resulting in version 6.1.8  */
239 /*  10-31-2022     Wenhui Xie               Modified comment(s), and      */
240 /*                                            supported HTTP Proxy,       */
241 /*                                            resulting in version 6.2.0  */
242 /*                                                                        */
243 /**************************************************************************/
_nxd_tcp_client_socket_connect(NX_TCP_SOCKET * socket_ptr,NXD_ADDRESS * server_ip,UINT server_port,ULONG wait_option)244 UINT  _nxd_tcp_client_socket_connect(NX_TCP_SOCKET *socket_ptr,
245                                      NXD_ADDRESS *server_ip,
246                                      UINT server_port,
247                                      ULONG wait_option)
248 {
249 
250 UINT          ip_header_size = 0;
251 NX_IP        *ip_ptr;
252 NX_INTERFACE *outgoing_interface = NX_NULL;
253 
254 #ifdef FEATURE_NX_IPV6
255 UINT          status;
256 #endif /* FEATURE_NX_IPV6 */
257 
258 #ifdef TX_ENABLE_EVENT_TRACE
259 ULONG         ip_address_log = 0;
260 #endif /* TX_ENABLE_EVENT_TRACE */
261 
262     /* Setup IP pointer.  */
263     ip_ptr =  socket_ptr -> nx_tcp_socket_ip_ptr;
264 
265 #ifdef NX_ENABLE_HTTP_PROXY
266     if (ip_ptr -> nx_ip_http_proxy_enable)
267     {
268 
269         /* Initialize the HTTP Proxy info and replace the peer IP and port with HTTP proxy server's IP and port.  */
270         _nx_http_proxy_client_initialize(socket_ptr, &server_ip, &server_port);
271     }
272 #endif /* NX_ENABLE_HTTP_PROXY */
273 
274     /* Make sure the server IP address is accesible. */
275 #ifndef NX_DISABLE_IPV4
276     if (server_ip -> nxd_ip_version == NX_IP_VERSION_V4)
277     {
278         if (_nx_ip_route_find(ip_ptr, server_ip -> nxd_ip_address.v4, &outgoing_interface, &socket_ptr -> nx_tcp_socket_next_hop_address) != NX_SUCCESS)
279         {
280             /* Return an IP address error code.  */
281             return(NX_IP_ADDRESS_ERROR);
282         }
283     }
284 #endif /* !NX_DISABLE_IPV4  */
285 
286 #ifdef FEATURE_NX_IPV6
287     /* For IPv6 connections, find a suitable outgoing interface, based on the TCP peer address. */
288     if (server_ip -> nxd_ip_version == NX_IP_VERSION_V6)
289     {
290 
291         status = _nxd_ipv6_interface_find(ip_ptr, server_ip -> nxd_ip_address.v6,
292                                           &socket_ptr -> nx_tcp_socket_ipv6_addr,
293                                           NX_NULL);
294 
295         if (status != NX_SUCCESS)
296         {
297             return(status);
298         }
299 
300         outgoing_interface = socket_ptr -> nx_tcp_socket_ipv6_addr -> nxd_ipv6_address_attached;
301     }
302 #endif /* FEATURE_NX_IPV6 */
303 
304 #ifdef TX_ENABLE_EVENT_TRACE
305 #ifndef NX_DISABLE_IPV4
306     if (server_ip -> nxd_ip_version == NX_IP_VERSION_V4)
307     {
308         ip_address_log = server_ip -> nxd_ip_address.v4;
309     }
310 #endif /* !NX_DISABLE_IPV4  */
311 
312 #ifdef FEATURE_NX_IPV6
313     if (server_ip -> nxd_ip_version == NX_IP_VERSION_V6)
314     {
315         ip_address_log = server_ip -> nxd_ip_address.v6[3];
316     }
317 #endif /* FEATURE_NX_IPV6 */
318 
319     /* If trace is enabled, insert this event into the trace buffer.  */
320     NX_TRACE_IN_LINE_INSERT(NX_TRACE_TCP_CLIENT_SOCKET_CONNECT, ip_ptr, socket_ptr, ip_address_log, server_port, NX_TRACE_TCP_EVENTS, 0, 0);
321 #endif /* TX_ENABLE_EVENT_TRACE */
322 
323     /* Obtain the IP mutex so we initiate the connect.  */
324     tx_mutex_get(&(ip_ptr -> nx_ip_protection), TX_WAIT_FOREVER);
325 
326     /* Determine if the socket has already been bound to port or if a socket bind is
327        already pending from another thread.  */
328     if (!socket_ptr -> nx_tcp_socket_bound_next)
329     {
330 
331         /* Release protection.  */
332         tx_mutex_put(&(ip_ptr -> nx_ip_protection));
333 
334         /* Return a not bound error code.  */
335         return(NX_NOT_BOUND);
336     }
337 
338     /* Determine if the socket is in a pre-connection state.  */
339     if ((socket_ptr -> nx_tcp_socket_state != NX_TCP_CLOSED) && (socket_ptr -> nx_tcp_socket_state != NX_TCP_TIMED_WAIT))
340     {
341 
342         /* Release protection.  */
343         tx_mutex_put(&(ip_ptr -> nx_ip_protection));
344 
345         /* Return a not closed error code.  */
346         return(NX_NOT_CLOSED);
347     }
348 
349 #ifndef NX_DISABLE_TCP_INFO
350 
351     /* Increment the active connections count.  */
352     ip_ptr -> nx_ip_tcp_active_connections++;
353 
354     /* Increment the TCP connections count.  */
355     ip_ptr -> nx_ip_tcp_connections++;
356 #endif
357 
358     /* If trace is enabled, insert this event into the trace buffer.  */
359     NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_TCP_STATE_CHANGE, ip_ptr, socket_ptr, socket_ptr -> nx_tcp_socket_state, NX_TCP_SYN_SENT, NX_TRACE_INTERNAL_EVENTS, 0, 0);
360 
361     /* Move the TCP state to Sequence Sent, the next state of an active open.  */
362     socket_ptr -> nx_tcp_socket_state =  NX_TCP_SYN_SENT;
363 
364     /* Save the server port and server IP address.  */
365     socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version       = server_ip -> nxd_ip_version;
366 
367 #ifndef NX_DISABLE_IPV4
368     if (server_ip -> nxd_ip_version == NX_IP_VERSION_V4)
369     {
370 
371         /* copy the IPv4 address */
372         socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_address.v4 = server_ip -> nxd_ip_address.v4;
373 
374         ip_header_size = (UINT)sizeof(NX_IPV4_HEADER);
375     }
376 #endif /* !NX_DISABLE_IPV4  */
377 
378 #ifdef FEATURE_NX_IPV6
379     if (server_ip -> nxd_ip_version == NX_IP_VERSION_V6)
380     {
381         COPY_IPV6_ADDRESS(server_ip -> nxd_ip_address.v6,
382                           socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_address.v6);
383 
384         ip_header_size = (UINT)sizeof(NX_IPV6_HEADER);
385     }
386 #endif /* FEATURE_NX_IPV6 */
387 
388     socket_ptr -> nx_tcp_socket_connect_port = server_port;
389 
390     /* Outgoing interface must not be null. */
391     NX_ASSERT(outgoing_interface != NX_NULL);
392 
393     /* Initialize the maximum segment size based on the interface MTU. */
394     /*lint -e{644} suppress variable might not be initialized, since "outgoing_interface" was initialized by _nx_ip_route_find or _nxd_ipv6_interface_find. */
395     if (outgoing_interface -> nx_interface_ip_mtu_size < (ip_header_size + NX_TCP_SYN_SIZE))
396     {
397 
398         /* Interface MTU size is smaller than IP and TCP header size.  Invalid interface! */
399 
400 #ifndef NX_DISABLE_TCP_INFO
401 
402         /* Reduce the active connections count.  */
403         ip_ptr -> nx_ip_tcp_active_connections--;
404 
405         /* Reduce the TCP connections count.  */
406         ip_ptr -> nx_ip_tcp_connections--;
407 #endif
408 
409         /* Restore the socket state. */
410         socket_ptr -> nx_tcp_socket_state = NX_TCP_CLOSED;
411 
412         /* Reset server port and server IP address. */
413         memset(&socket_ptr -> nx_tcp_socket_connect_ip, 0, sizeof(NXD_ADDRESS));
414         socket_ptr -> nx_tcp_socket_connect_port = 0;
415 
416         /* Reset the next_hop_address information. */
417         socket_ptr -> nx_tcp_socket_next_hop_address = 0;
418 
419         /* Release protection.  */
420         tx_mutex_put(&(ip_ptr -> nx_ip_protection));
421 
422 
423         /* Return an IP address error code.  */
424         return(NX_INVALID_INTERFACE);
425     }
426 
427     socket_ptr -> nx_tcp_socket_connect_interface = outgoing_interface;
428 
429     /* Setup the initial sequence number.  */
430     if (socket_ptr -> nx_tcp_socket_tx_sequence == 0)
431     {
432         socket_ptr -> nx_tcp_socket_tx_sequence =  (((ULONG)NX_RAND()) << NX_SHIFT_BY_16) & 0xFFFFFFFF;
433         socket_ptr -> nx_tcp_socket_tx_sequence |= (ULONG)NX_RAND();
434     }
435     else
436     {
437         socket_ptr -> nx_tcp_socket_tx_sequence =  socket_ptr -> nx_tcp_socket_tx_sequence + ((ULONG)(((ULONG)0x10000))) + ((ULONG)NX_RAND());
438     }
439 
440     /* Ensure the rx window size logic is reset.  */
441     socket_ptr -> nx_tcp_socket_rx_window_current =    socket_ptr -> nx_tcp_socket_rx_window_default;
442     socket_ptr -> nx_tcp_socket_rx_window_last_sent =  socket_ptr -> nx_tcp_socket_rx_window_default;
443 
444     /* Clear the FIN received flag.  */
445     socket_ptr -> nx_tcp_socket_fin_received =  NX_FALSE;
446     socket_ptr -> nx_tcp_socket_fin_acked =  NX_FALSE;
447 
448     /* Increment the sequence number.  */
449     socket_ptr -> nx_tcp_socket_tx_sequence++;
450 
451     /* Setup a timeout so the connection attempt can be sent again.  */
452     socket_ptr -> nx_tcp_socket_timeout =          socket_ptr -> nx_tcp_socket_timeout_rate;
453     socket_ptr -> nx_tcp_socket_timeout_retries =  0;
454 
455     /* CLEANUP: In case any existing packets on socket's receive queue.  */
456     if (socket_ptr -> nx_tcp_socket_receive_queue_count)
457     {
458 
459         /* Remove all packets on the socket's receive queue.  */
460         _nx_tcp_socket_receive_queue_flush(socket_ptr);
461     }
462 
463     /* CLEANUP: Clean up any existing socket data before making a new connection. */
464     socket_ptr -> nx_tcp_socket_tx_window_congestion = 0;
465     socket_ptr -> nx_tcp_socket_tx_outstanding_bytes = 0;
466     socket_ptr -> nx_tcp_socket_packets_sent         = 0;
467     socket_ptr -> nx_tcp_socket_bytes_sent           = 0;
468     socket_ptr -> nx_tcp_socket_packets_received     = 0;
469     socket_ptr -> nx_tcp_socket_bytes_received       = 0;
470     socket_ptr -> nx_tcp_socket_retransmit_packets   = 0;
471     socket_ptr -> nx_tcp_socket_checksum_errors      = 0;
472     socket_ptr -> nx_tcp_socket_transmit_sent_head   = NX_NULL;
473     socket_ptr -> nx_tcp_socket_transmit_sent_tail   = NX_NULL;
474     socket_ptr -> nx_tcp_socket_transmit_sent_count  = 0;
475     socket_ptr -> nx_tcp_socket_receive_queue_count  = 0;
476     socket_ptr -> nx_tcp_socket_receive_queue_head   = NX_NULL;
477     socket_ptr -> nx_tcp_socket_receive_queue_tail   = NX_NULL;
478 
479 #ifdef NX_ENABLE_TCPIP_OFFLOAD
480     if ((outgoing_interface -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_TCPIP_OFFLOAD) &&
481         (outgoing_interface -> nx_interface_tcpip_offload_handler))
482     {
483 
484         /* This interface supports TCP/IP offload.  */
485         return(_nxd_tcp_client_socket_driver_connect(socket_ptr, server_ip, server_port, wait_option));
486     }
487     else
488 #endif /* NX_ENABLE_TCPIP_OFFLOAD */
489     {
490 
491         /* Send the SYN message.  */
492         _nx_tcp_packet_send_syn(socket_ptr, (socket_ptr -> nx_tcp_socket_tx_sequence - 1));
493     }
494 
495 #ifdef NX_ENABLE_HTTP_PROXY
496     if (ip_ptr -> nx_ip_http_proxy_enable)
497     {
498 
499         /* Set HTTP Proxy state as waiting for TCP connection.  */
500         socket_ptr -> nx_tcp_socket_http_proxy_state = NX_HTTP_PROXY_STATE_WAITING;
501     }
502 #endif /* NX_ENABLE_HTTP_PROXY */
503 
504     /* Optionally suspend the thread.  If timeout occurs, return a connection timeout status.  If
505        immediate response is selected, return a connection in progress status.  Only on a real
506        connection should success be returned.  */
507     if ((wait_option) && (_tx_thread_current_ptr != &(ip_ptr -> nx_ip_thread)))
508     {
509 
510         /* Suspend the thread on this socket's connection attempt.  */
511         /* Note: the IP protection mutex is released inside _nx_tcp_socket_thread_suspend().  */
512 
513         _nx_tcp_socket_thread_suspend(&(socket_ptr -> nx_tcp_socket_connect_suspended_thread), _nx_tcp_connect_cleanup, socket_ptr, &(ip_ptr -> nx_ip_protection), wait_option);
514 
515         /* Just return the completion code.  */
516         return(_tx_thread_current_ptr -> tx_thread_suspend_status);
517     }
518     else
519     {
520 
521         /* No suspension is request, just release protection and return to the caller.  */
522 
523         /* Release the IP protection.  */
524         tx_mutex_put(&(ip_ptr -> nx_ip_protection));
525 
526         /* Return in-progress completion status.  */
527         return(NX_IN_PROGRESS);
528     }
529 }
530 
531