@@ -88,6 +88,7 @@ static void ResetSession(WOLFSSH* ssh)
8888 ssh -> connectState = CONNECT_BEGIN ;
8989 ssh -> acceptState = ACCEPT_BEGIN ;
9090 ssh -> error = 0 ;
91+ ssh -> disconnected = 0 ;
9192}
9293
9394
@@ -2571,6 +2572,7 @@ static void TestDisconnectSetsDisconnectError(void)
25712572 MemIo io ;
25722573 byte in [128 ];
25732574 byte out [32 ];
2575+ byte data [8 ];
25742576 word32 inSz ;
25752577 int ret ;
25762578
@@ -2594,6 +2596,129 @@ static void TestDisconnectSetsDisconnectError(void)
25942596 AssertIntEQ (wolfSSH_get_error (ssh ), WS_DISCONNECT );
25952597 AssertIntEQ (io .inOff , io .inSz );
25962598
2599+ /* The disconnect is terminal, not just this call's error. Later stream
2600+ * calls must report it rather than clearing the error and reading or
2601+ * writing more. */
2602+ AssertTrue (ssh -> disconnected );
2603+
2604+ WMEMSET (data , 0 , sizeof (data ));
2605+ ret = wolfSSH_stream_read (ssh , data , sizeof (data ));
2606+ AssertIntEQ (ret , WS_FATAL_ERROR );
2607+ AssertIntEQ (wolfSSH_get_error (ssh ), WS_DISCONNECT );
2608+
2609+ ret = wolfSSH_stream_send (ssh , data , sizeof (data ));
2610+ AssertIntEQ (ret , WS_FATAL_ERROR );
2611+ AssertIntEQ (wolfSSH_get_error (ssh ), WS_DISCONNECT );
2612+
2613+ wolfSSH_free (ssh );
2614+ wolfSSH_CTX_free (ctx );
2615+ }
2616+
2617+
2618+ /* Append a bare session channel so the stream calls have a channel to work
2619+ * on, the state a disconnect actually arrives in. */
2620+ static void AddSessionChannel (WOLFSSH * ssh )
2621+ {
2622+ WOLFSSH_CHANNEL * ch ;
2623+
2624+ ch = ChannelNew (ssh , ID_CHANTYPE_SESSION , 1024 , 1024 );
2625+ AssertNotNull (ch );
2626+ AssertIntEQ (ChannelAppend (ssh , ch ), WS_SUCCESS );
2627+ ch -> openConfirmed = 1 ;
2628+ }
2629+
2630+
2631+ /* The same received disconnect on an established session. Without a channel
2632+ * the stream calls bail out on the NULL channel list before they reach
2633+ * anything, so this is the case that shows the gate doing work. */
2634+ static void TestDisconnectTerminalWithChannel (void )
2635+ {
2636+ WOLFSSH_CTX * ctx ;
2637+ WOLFSSH * ssh ;
2638+ MemIo io ;
2639+ byte in [128 ];
2640+ byte out [128 ];
2641+ byte data [8 ];
2642+ word32 inSz ;
2643+ int ret ;
2644+
2645+ ctx = wolfSSH_CTX_new (WOLFSSH_ENDPOINT_CLIENT , NULL );
2646+ AssertNotNull (ctx );
2647+
2648+ wolfSSH_SetIORecv (ctx , MemRecv );
2649+ wolfSSH_SetIOSend (ctx , MemSend );
2650+
2651+ ssh = wolfSSH_new (ctx );
2652+ AssertNotNull (ssh );
2653+ AddSessionChannel (ssh );
2654+
2655+ inSz = BuildDisconnectPacket (WOLFSSH_DISCONNECT_BY_APPLICATION ,
2656+ in , sizeof (in ));
2657+ MemIoInit (& io , in , inSz , out , sizeof (out ));
2658+ wolfSSH_SetIOReadCtx (ssh , & io );
2659+ wolfSSH_SetIOWriteCtx (ssh , & io );
2660+
2661+ ret = DoReceive (ssh );
2662+ AssertIntEQ (ret , WS_FATAL_ERROR );
2663+ AssertIntEQ (wolfSSH_get_error (ssh ), WS_DISCONNECT );
2664+ AssertTrue (ssh -> disconnected );
2665+
2666+ WMEMSET (data , 0 , sizeof (data ));
2667+ ret = wolfSSH_stream_read (ssh , data , sizeof (data ));
2668+ AssertIntEQ (ret , WS_FATAL_ERROR );
2669+ AssertIntEQ (wolfSSH_get_error (ssh ), WS_DISCONNECT );
2670+
2671+ /* Nothing may go out on the channel either. */
2672+ ret = wolfSSH_stream_send (ssh , data , sizeof (data ));
2673+ AssertIntEQ (ret , WS_FATAL_ERROR );
2674+ AssertIntEQ (wolfSSH_get_error (ssh ), WS_DISCONNECT );
2675+ AssertIntEQ (io .outSz , 0 );
2676+
2677+ wolfSSH_free (ssh );
2678+ wolfSSH_CTX_free (ctx );
2679+ }
2680+
2681+
2682+ /* Sending SSH_MSG_DISCONNECT ends the session the same way receiving one
2683+ * does: RFC 4253 section 11.1 says the connection is over once the message
2684+ * goes out, so the stream calls must refuse afterwards. */
2685+ static void TestSendDisconnectIsTerminal (void )
2686+ {
2687+ WOLFSSH_CTX * ctx ;
2688+ WOLFSSH * ssh ;
2689+ MemIo io ;
2690+ byte out [128 ];
2691+ byte data [8 ];
2692+ int ret ;
2693+
2694+ ctx = wolfSSH_CTX_new (WOLFSSH_ENDPOINT_CLIENT , NULL );
2695+ AssertNotNull (ctx );
2696+
2697+ wolfSSH_SetIORecv (ctx , MemRecv );
2698+ wolfSSH_SetIOSend (ctx , MemSend );
2699+
2700+ ssh = wolfSSH_new (ctx );
2701+ AssertNotNull (ssh );
2702+ AddSessionChannel (ssh );
2703+
2704+ MemIoInit (& io , NULL , 0 , out , sizeof (out ));
2705+ wolfSSH_SetIOReadCtx (ssh , & io );
2706+ wolfSSH_SetIOWriteCtx (ssh , & io );
2707+
2708+ ret = wolfSSH_SendDisconnect (ssh , WOLFSSH_DISCONNECT_BY_APPLICATION );
2709+ AssertIntEQ (ret , WS_SUCCESS );
2710+ AssertTrue (ssh -> disconnected );
2711+ AssertTrue (io .outSz > 0 );
2712+
2713+ WMEMSET (data , 0 , sizeof (data ));
2714+ ret = wolfSSH_stream_send (ssh , data , sizeof (data ));
2715+ AssertIntEQ (ret , WS_FATAL_ERROR );
2716+ AssertIntEQ (wolfSSH_get_error (ssh ), WS_DISCONNECT );
2717+
2718+ ret = wolfSSH_stream_read (ssh , data , sizeof (data ));
2719+ AssertIntEQ (ret , WS_FATAL_ERROR );
2720+ AssertIntEQ (wolfSSH_get_error (ssh ), WS_DISCONNECT );
2721+
25972722 wolfSSH_free (ssh );
25982723 wolfSSH_CTX_free (ctx );
25992724}
@@ -6328,6 +6453,8 @@ int main(int argc, char** argv)
63286453 TestDoNewKeys ();
63296454#endif
63306455 TestDisconnectSetsDisconnectError ();
6456+ TestDisconnectTerminalWithChannel ();
6457+ TestSendDisconnectIsTerminal ();
63316458#if !(defined(WOLFSSH_NO_RSA ) && defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256 ))
63326459 TestClientBuffersIdempotent ();
63336460#endif
0 commit comments