@@ -59,11 +59,14 @@ static struct {
5959 int rx_len ;
6060 uint32_t start_tick ;
6161 int channel_open ;
62+ uint16_t port ; /* saved for listen socket recovery */
63+ uint8_t session_established ; /* 1 if SSH channel opened (CONNECTED reached) */
6264} server ;
6365
6466/* External functions from wolfssh_io.c */
6567extern void wolfSSH_CTX_SetIO_wolfIP (WOLFSSH_CTX * ctx );
6668extern int wolfSSH_SetIO_wolfIP (WOLFSSH * ssh , struct wolfIP * stack , int fd );
69+ extern void wolfSSH_CleanupIO_wolfIP (WOLFSSH * ssh );
6770
6871#ifdef DEBUG_WOLFSSH
6972/* wolfSSH logging callback */
@@ -185,9 +188,9 @@ static int handle_command(const char *cmd, char *response, int max_len)
185188 const char * bye = "\r\nGoodbye!\r\n" ;
186189 len = strlen (bye );
187190 if (len < max_len ) {
188- memcpy (response , bye , len );
191+ memcpy (response , bye , len + 1 ); /* include null terminator */
189192 }
190- return -1 ; /* Signal to close connection */
193+ return - len ; /* Negative = close, magnitude = byte count to send */
191194 }
192195 else if (cmd [0 ] != '\0' && cmd [0 ] != '\r' && cmd [0 ] != '\n' ) {
193196 const char * unknown = "\r\nUnknown command. Type 'help' for available commands.\r\n\r\n" ;
@@ -215,6 +218,7 @@ int ssh_server_init(struct wolfIP *stack, uint16_t port, ssh_debug_cb debug)
215218 server .listen_fd = -1 ;
216219 server .client_fd = -1 ;
217220 server .state = SSH_STATE_LISTENING ;
221+ server .port = port ;
218222
219223 debug_print ("SSH: Initializing wolfSSH\n" );
220224
@@ -295,6 +299,56 @@ int ssh_server_init(struct wolfIP *stack, uint16_t port, ssh_debug_cb debug)
295299 return 0 ;
296300}
297301
302+ /* Re-open the listen socket when wolfIP has corrupted it (e.g. listen socket
303+ * got stuck in TCP_ESTABLISHED after the RTO fired and the client ACK arrived
304+ * before wolfIP_sock_accept() was called). */
305+ static int ssh_reinit_listen (void )
306+ {
307+ struct wolfIP_sockaddr_in addr ;
308+ int ret ;
309+
310+ debug_print ("SSH: Reinitializing listen socket\n" );
311+
312+ /* Close the broken listen socket (ignore errors) */
313+ if (server .listen_fd >= 0 ) {
314+ wolfIP_sock_close (server .stack , server .listen_fd );
315+ server .listen_fd = -1 ;
316+ }
317+
318+ /* Create a new listen socket */
319+ server .listen_fd = wolfIP_sock_socket (server .stack ,
320+ AF_INET , IPSTACK_SOCK_STREAM , 0 );
321+ if (server .listen_fd < 0 ) {
322+ debug_print ("SSH: reinit socket() failed\n" );
323+ return -1 ;
324+ }
325+
326+ memset (& addr , 0 , sizeof (addr ));
327+ addr .sin_family = AF_INET ;
328+ addr .sin_port = ee16 (server .port );
329+ addr .sin_addr .s_addr = 0 ;
330+
331+ ret = wolfIP_sock_bind (server .stack , server .listen_fd ,
332+ (struct wolfIP_sockaddr * )& addr , sizeof (addr ));
333+ if (ret < 0 ) {
334+ debug_print ("SSH: reinit bind() failed\n" );
335+ wolfIP_sock_close (server .stack , server .listen_fd );
336+ server .listen_fd = -1 ;
337+ return -1 ;
338+ }
339+
340+ ret = wolfIP_sock_listen (server .stack , server .listen_fd , 1 );
341+ if (ret < 0 ) {
342+ debug_print ("SSH: reinit listen() failed\n" );
343+ wolfIP_sock_close (server .stack , server .listen_fd );
344+ server .listen_fd = -1 ;
345+ return -1 ;
346+ }
347+
348+ debug_print ("SSH: Listen socket recovered\n" );
349+ return 0 ;
350+ }
351+
298352int ssh_server_poll (void )
299353{
300354 int ret ;
@@ -308,8 +362,16 @@ int ssh_server_poll(void)
308362 (struct wolfIP_sockaddr * )& client_addr , & addr_len );
309363 if (ret >= 0 ) {
310364 server .client_fd = ret ;
365+ server .session_established = 0 ;
311366 debug_print ("SSH: Client connected\n" );
312367 server .state = SSH_STATE_ACCEPTING ;
368+ } else if (ret == -1 ) {
369+ /* Listen socket in unexpected state (e.g. stuck in
370+ * TCP_ESTABLISHED after wolfIP's RTO mechanism fired and the
371+ * client ACK arrived before wolfIP_sock_accept() was called,
372+ * or destroyed after TCP_CTRL_RTO_MAXRTX retries).
373+ * Reinitialize to recover. */
374+ ssh_reinit_listen ();
313375 }
314376 break ;
315377
@@ -339,6 +401,7 @@ int ssh_server_poll(void)
339401 ret = wolfSSH_accept (server .ssh );
340402 if (ret == WS_SUCCESS ) {
341403 debug_print ("SSH: Handshake complete\n" );
404+ server .session_established = 1 ;
342405 server .state = SSH_STATE_CONNECTED ;
343406 server .rx_len = 0 ;
344407
@@ -350,7 +413,10 @@ int ssh_server_poll(void)
350413 wolfSSH_stream_send (server .ssh , (byte * )welcome , strlen (welcome ));
351414 } else {
352415 int err = wolfSSH_get_error (server .ssh );
353- if (err != WS_WANT_READ && err != WS_WANT_WRITE ) {
416+ /* Check both ret and err: wolfSSH may return WANT_READ/WRITE
417+ * either as the return value or stored via wolfSSH_get_error() */
418+ if (err != WS_WANT_READ && err != WS_WANT_WRITE &&
419+ ret != WS_WANT_READ && ret != WS_WANT_WRITE ) {
354420 debug_print ("SSH: Handshake failed\n" );
355421 server .state = SSH_STATE_CLOSING ;
356422 }
@@ -384,8 +450,8 @@ int ssh_server_poll(void)
384450 response , sizeof (response ));
385451
386452 if (resp_len < 0 ) {
387- /* Exit requested */
388- wolfSSH_stream_send (server .ssh , (byte * )response , strlen ( response ));
453+ /* Exit requested: magnitude encodes byte count */
454+ wolfSSH_stream_send (server .ssh , (byte * )response , ( word32 )( - resp_len ));
389455 server .state = SSH_STATE_CLOSING ;
390456 break ;
391457 }
@@ -404,7 +470,8 @@ int ssh_server_poll(void)
404470 }
405471 } else if (ret < 0 ) {
406472 int err = wolfSSH_get_error (server .ssh );
407- if (err != WS_WANT_READ && err != WS_WANT_WRITE ) {
473+ if (err != WS_WANT_READ && err != WS_WANT_WRITE &&
474+ ret != WS_WANT_READ && ret != WS_WANT_WRITE ) {
408475 debug_print ("SSH: Connection closed\n" );
409476 server .state = SSH_STATE_CLOSING ;
410477 }
@@ -413,10 +480,19 @@ int ssh_server_poll(void)
413480
414481 case SSH_STATE_CLOSING :
415482 if (server .ssh ) {
416- wolfSSH_shutdown (server .ssh );
483+ /* Only send SSH_MSG_DISCONNECT if the session was fully
484+ * established; calling wolfSSH_shutdown() on a half-open
485+ * session (e.g. auth failure) can trigger unexpected I/O
486+ * that delays the return to LISTENING and widens the window
487+ * in which a new SYN can corrupt the listen socket state. */
488+ if (server .session_established ) {
489+ wolfSSH_shutdown (server .ssh );
490+ }
491+ wolfSSH_CleanupIO_wolfIP (server .ssh );
417492 wolfSSH_free (server .ssh );
418493 server .ssh = NULL ;
419494 }
495+ server .session_established = 0 ;
420496 if (server .client_fd >= 0 ) {
421497 wolfIP_sock_close (server .stack , server .client_fd );
422498 server .client_fd = -1 ;
0 commit comments