@@ -4194,6 +4194,222 @@ static void TestDisconnectOutranksRekey(void)
41944194}
41954195
41964196
4197+
4198+ #ifndef NO_WOLFSSH_SERVER
4199+
4200+ /* wolfSSH_accept() drives the handshake, so a session that is already over
4201+ * must stop it the way it stops every other sender. The short-send case is
4202+ * the sharp one: the pending-send block at the top would push out a
4203+ * disconnect left queued by a short send and then count it as the handshake
4204+ * message the state machine was waiting for. RFC 4253 section 11.1. */
4205+ static void TestDisconnectGatesAccept (void )
4206+ {
4207+ WOLFSSH_CTX * ctx ;
4208+ WOLFSSH * ssh ;
4209+ MemIo io ;
4210+ byte in [128 ];
4211+ byte out [512 ];
4212+ word32 inSz ;
4213+ word32 quietSz ;
4214+ byte state ;
4215+ int ret ;
4216+
4217+ /* A local disconnect leaves ssh->error clear, so the "in error state"
4218+ * test in wolfSSH_accept() never sees it. */
4219+ ctx = wolfSSH_CTX_new (WOLFSSH_ENDPOINT_SERVER , NULL );
4220+ AssertNotNull (ctx );
4221+ wolfSSH_SetIORecv (ctx , MemRecv );
4222+ wolfSSH_SetIOSend (ctx , MemSend );
4223+
4224+ ssh = wolfSSH_new (ctx );
4225+ AssertNotNull (ssh );
4226+ AddSessionChannel (ssh );
4227+ /* Not one of the states wolfSSH_accept() holds back, so an unwanted
4228+ * advance shows up in the assertions below. */
4229+ ssh -> acceptState = ACCEPT_SERVER_USERAUTH_SENT ;
4230+
4231+ MemIoInit (& io , NULL , 0 , out , sizeof (out ));
4232+ wolfSSH_SetIOReadCtx (ssh , & io );
4233+ wolfSSH_SetIOWriteCtx (ssh , & io );
4234+
4235+ AssertIntEQ (wolfSSH_SendDisconnect (ssh , WOLFSSH_DISCONNECT_BY_APPLICATION ),
4236+ WS_SUCCESS );
4237+ AssertTrue (ssh -> disconnected );
4238+ AssertIntEQ (wolfSSH_get_error (ssh ), 0 );
4239+ quietSz = io .outSz ;
4240+ state = ssh -> acceptState ;
4241+
4242+ ret = wolfSSH_accept (ssh );
4243+ AssertIntEQ (ret , WS_FATAL_ERROR );
4244+ AssertIntEQ (wolfSSH_get_error (ssh ), WS_DISCONNECT );
4245+ /* No handshake packet, and the state machine did not move. */
4246+ AssertIntEQ (io .outSz , quietSz );
4247+ AssertIntEQ (ssh -> acceptState , state );
4248+
4249+ wolfSSH_free (ssh );
4250+ wolfSSH_CTX_free (ctx );
4251+
4252+ /* Our disconnect short-sends, so it is sitting in the output buffer
4253+ * with a flush owed. wolfSSH_shutdown() and wolfSSH_SendDisconnect()
4254+ * own that flush; wolfSSH_accept() must leave it alone. */
4255+ ctx = wolfSSH_CTX_new (WOLFSSH_ENDPOINT_SERVER , NULL );
4256+ AssertNotNull (ctx );
4257+ wolfSSH_SetIORecv (ctx , MemRecv );
4258+ wolfSSH_SetIOSend (ctx , MemSendWantWrite );
4259+
4260+ ssh = wolfSSH_new (ctx );
4261+ AssertNotNull (ssh );
4262+ AddSessionChannel (ssh );
4263+ /* Not one of the states wolfSSH_accept() holds back, so an unwanted
4264+ * advance shows up in the assertions below. */
4265+ ssh -> acceptState = ACCEPT_SERVER_USERAUTH_SENT ;
4266+
4267+ MemIoInit (& io , NULL , 0 , out , sizeof (out ));
4268+ wolfSSH_SetIOReadCtx (ssh , & io );
4269+ wolfSSH_SetIOWriteCtx (ssh , & io );
4270+
4271+ MemSendWantWriteCount = 1 ;
4272+ AssertIntEQ (wolfSSH_SendDisconnect (ssh , WOLFSSH_DISCONNECT_BY_APPLICATION ),
4273+ WS_WANT_WRITE );
4274+ AssertTrue (ssh -> disconnected );
4275+ AssertTrue (ssh -> disconnectTxd );
4276+ AssertTrue (wolfSSH_OutputPending (ssh ));
4277+ AssertIntEQ (io .outSz , 0 );
4278+ state = ssh -> acceptState ;
4279+
4280+ ret = wolfSSH_accept (ssh );
4281+ AssertIntEQ (ret , WS_FATAL_ERROR );
4282+ AssertIntEQ (wolfSSH_get_error (ssh ), WS_DISCONNECT );
4283+ /* Still queued, and not mistaken for the awaited handshake message. */
4284+ AssertIntEQ (io .outSz , 0 );
4285+ AssertTrue (wolfSSH_OutputPending (ssh ));
4286+ AssertIntEQ (ssh -> acceptState , state );
4287+
4288+ wolfSSH_free (ssh );
4289+ wolfSSH_CTX_free (ctx );
4290+
4291+ /* The peer's disconnect latches WS_DISCONNECT, which the error-state
4292+ * test below the gate used to answer with WS_INVALID_STATE_E. */
4293+ ctx = wolfSSH_CTX_new (WOLFSSH_ENDPOINT_SERVER , NULL );
4294+ AssertNotNull (ctx );
4295+ wolfSSH_SetIORecv (ctx , MemRecv );
4296+ wolfSSH_SetIOSend (ctx , MemSend );
4297+
4298+ ssh = wolfSSH_new (ctx );
4299+ AssertNotNull (ssh );
4300+ AddSessionChannel (ssh );
4301+ ssh -> acceptState = ACCEPT_SERVER_USERAUTH_SENT ;
4302+
4303+ inSz = BuildDisconnectPacket (WOLFSSH_DISCONNECT_BY_APPLICATION ,
4304+ in , sizeof (in ));
4305+ MemIoInit (& io , in , inSz , out , sizeof (out ));
4306+ wolfSSH_SetIOReadCtx (ssh , & io );
4307+ wolfSSH_SetIOWriteCtx (ssh , & io );
4308+
4309+ AssertIntEQ (DoReceive (ssh ), WS_FATAL_ERROR );
4310+ AssertTrue (ssh -> disconnected );
4311+ AssertIntEQ (wolfSSH_get_error (ssh ), WS_DISCONNECT );
4312+ quietSz = io .outSz ;
4313+ state = ssh -> acceptState ;
4314+
4315+ ret = wolfSSH_accept (ssh );
4316+ AssertIntEQ (ret , WS_FATAL_ERROR );
4317+ AssertIntEQ (wolfSSH_get_error (ssh ), WS_DISCONNECT );
4318+ AssertIntEQ (io .outSz , quietSz );
4319+ AssertIntEQ (ssh -> acceptState , state );
4320+
4321+ wolfSSH_free (ssh );
4322+ wolfSSH_CTX_free (ctx );
4323+ }
4324+
4325+ #endif /* !NO_WOLFSSH_SERVER */
4326+
4327+
4328+ #ifndef NO_WOLFSSH_CLIENT
4329+
4330+ /* The same gate on the client's driver, which has no error-state test of
4331+ * its own. Split from the accept test so a single-sided build keeps the
4332+ * coverage that applies to it. */
4333+ static void TestDisconnectGatesConnect (void )
4334+ {
4335+ WOLFSSH_CTX * ctx ;
4336+ WOLFSSH * ssh ;
4337+ MemIo io ;
4338+ byte in [128 ];
4339+ byte out [512 ];
4340+ word32 inSz ;
4341+ byte state ;
4342+ int ret ;
4343+
4344+ /* wolfSSH_connect() has no error-state test of its own, so it reaches
4345+ * the state machine after a disconnect from either side. */
4346+ ctx = wolfSSH_CTX_new (WOLFSSH_ENDPOINT_CLIENT , NULL );
4347+ AssertNotNull (ctx );
4348+ wolfSSH_SetIORecv (ctx , MemRecv );
4349+ wolfSSH_SetIOSend (ctx , MemSendWantWrite );
4350+
4351+ ssh = wolfSSH_new (ctx );
4352+ AssertNotNull (ssh );
4353+ AddSessionChannel (ssh );
4354+ ssh -> connectState = CONNECT_SERVER_USERAUTH_ACCEPT_DONE ;
4355+
4356+ MemIoInit (& io , NULL , 0 , out , sizeof (out ));
4357+ wolfSSH_SetIOReadCtx (ssh , & io );
4358+ wolfSSH_SetIOWriteCtx (ssh , & io );
4359+
4360+ MemSendWantWriteCount = 1 ;
4361+ AssertIntEQ (wolfSSH_SendDisconnect (ssh , WOLFSSH_DISCONNECT_BY_APPLICATION ),
4362+ WS_WANT_WRITE );
4363+ AssertTrue (ssh -> disconnected );
4364+ AssertTrue (wolfSSH_OutputPending (ssh ));
4365+ AssertIntEQ (io .outSz , 0 );
4366+ state = ssh -> connectState ;
4367+
4368+ ret = wolfSSH_connect (ssh );
4369+ AssertIntEQ (ret , WS_FATAL_ERROR );
4370+ AssertIntEQ (wolfSSH_get_error (ssh ), WS_DISCONNECT );
4371+ AssertIntEQ (io .outSz , 0 );
4372+ AssertTrue (wolfSSH_OutputPending (ssh ));
4373+ AssertIntEQ (ssh -> connectState , state );
4374+
4375+ wolfSSH_free (ssh );
4376+ wolfSSH_CTX_free (ctx );
4377+
4378+ /* The peer's disconnect reaches the same gate. */
4379+ ctx = wolfSSH_CTX_new (WOLFSSH_ENDPOINT_CLIENT , NULL );
4380+ AssertNotNull (ctx );
4381+ wolfSSH_SetIORecv (ctx , MemRecv );
4382+ wolfSSH_SetIOSend (ctx , MemSend );
4383+
4384+ ssh = wolfSSH_new (ctx );
4385+ AssertNotNull (ssh );
4386+ AddSessionChannel (ssh );
4387+ ssh -> connectState = CONNECT_SERVER_USERAUTH_ACCEPT_DONE ;
4388+
4389+ inSz = BuildDisconnectPacket (WOLFSSH_DISCONNECT_BY_APPLICATION ,
4390+ in , sizeof (in ));
4391+ MemIoInit (& io , in , inSz , out , sizeof (out ));
4392+ wolfSSH_SetIOReadCtx (ssh , & io );
4393+ wolfSSH_SetIOWriteCtx (ssh , & io );
4394+
4395+ AssertIntEQ (DoReceive (ssh ), WS_FATAL_ERROR );
4396+ AssertTrue (ssh -> disconnected );
4397+ AssertIntEQ (wolfSSH_get_error (ssh ), WS_DISCONNECT );
4398+ state = ssh -> connectState ;
4399+
4400+ ret = wolfSSH_connect (ssh );
4401+ AssertIntEQ (ret , WS_FATAL_ERROR );
4402+ AssertIntEQ (wolfSSH_get_error (ssh ), WS_DISCONNECT );
4403+ AssertIntEQ (io .outSz , 0 );
4404+ AssertIntEQ (ssh -> connectState , state );
4405+
4406+ wolfSSH_free (ssh );
4407+ wolfSSH_CTX_free (ctx );
4408+ }
4409+
4410+ #endif /* !NO_WOLFSSH_CLIENT */
4411+
4412+
41974413/* disconnectTxd means "a flush is owed", not "a disconnect was sent". Once
41984414 * ours has gone out, a teardown call must not push whatever the internal
41994415 * senders queued behind it. */
@@ -8079,6 +8295,12 @@ int main(int argc, char** argv)
80798295 TestDisconnectDrainsBufferedData ();
80808296 TestDisconnectBlocksEverySend ();
80818297 TestSendDisconnectIsTerminal ();
8298+ #ifndef NO_WOLFSSH_SERVER
8299+ TestDisconnectGatesAccept ();
8300+ #endif
8301+ #ifndef NO_WOLFSSH_CLIENT
8302+ TestDisconnectGatesConnect ();
8303+ #endif
80828304 TestDisconnectQuietWindowAdjust ();
80838305 TestDisconnectBlocksChannelAndFwdSends ();
80848306 TestStreamExitReportsDisconnect ();
0 commit comments