@@ -160,12 +160,7 @@ public class Session : ISession
160160 /// <summary>
161161 /// WaitHandle to signal that key exchange was completed.
162162 /// </summary>
163- private EventWaitHandle _keyExchangeCompletedWaitHandle = new ManualResetEvent ( initialState : false ) ;
164-
165- /// <summary>
166- /// WaitHandle to signal that key exchange is in progress.
167- /// </summary>
168- private bool _keyExchangeInProgress ;
163+ private ManualResetEventSlim _keyExchangeCompletedWaitHandle = new ManualResetEventSlim ( initialState : false ) ;
169164
170165 /// <summary>
171166 /// Exception that need to be thrown by waiting thread.
@@ -643,6 +638,11 @@ public void Connect()
643638 // Some server implementations might sent this message first, prior to establishing encryption algorithm
644639 RegisterMessage ( "SSH_MSG_USERAUTH_BANNER" ) ;
645640
641+ // Send our key exchange init.
642+ // We need to do this before starting the message listener to avoid the case where we receive the server
643+ // key exchange init and we continue the key exchange before having sent our own init.
644+ SendMessage ( ClientInitMessage ) ;
645+
646646 // Mark the message listener threads as started
647647 _ = _messageListenerCompleted . Reset ( ) ;
648648
@@ -651,7 +651,7 @@ public void Connect()
651651 _ = ThreadAbstraction . ExecuteThreadLongRunning ( MessageListener ) ;
652652
653653 // Wait for key exchange to be completed
654- WaitOnHandle ( _keyExchangeCompletedWaitHandle ) ;
654+ WaitOnHandle ( _keyExchangeCompletedWaitHandle . WaitHandle ) ;
655655
656656 // If sessionId is not set then its not connected
657657 if ( SessionId is null )
@@ -757,6 +757,11 @@ public async Task ConnectAsync(CancellationToken cancellationToken)
757757 // Some server implementations might sent this message first, prior to establishing encryption algorithm
758758 RegisterMessage ( "SSH_MSG_USERAUTH_BANNER" ) ;
759759
760+ // Send our key exchange init.
761+ // We need to do this before starting the message listener to avoid the case where we receive the server
762+ // key exchange init and we continue the key exchange before having sent our own init.
763+ SendMessage ( ClientInitMessage ) ;
764+
760765 // Mark the message listener threads as started
761766 _ = _messageListenerCompleted . Reset ( ) ;
762767
@@ -765,7 +770,7 @@ public async Task ConnectAsync(CancellationToken cancellationToken)
765770 _ = ThreadAbstraction . ExecuteThreadLongRunning ( MessageListener ) ;
766771
767772 // Wait for key exchange to be completed
768- WaitOnHandle ( _keyExchangeCompletedWaitHandle ) ;
773+ WaitOnHandle ( _keyExchangeCompletedWaitHandle . WaitHandle ) ;
769774
770775 // If sessionId is not set then its not connected
771776 if ( SessionId is null )
@@ -1046,10 +1051,10 @@ internal void SendMessage(Message message)
10461051 throw new SshConnectionException ( "Client not connected." ) ;
10471052 }
10481053
1049- if ( _keyExchangeInProgress && message is not IKeyExchangedAllowed )
1054+ if ( ! _keyExchangeCompletedWaitHandle . IsSet && message is not IKeyExchangedAllowed )
10501055 {
10511056 // Wait for key exchange to be completed
1052- WaitOnHandle ( _keyExchangeCompletedWaitHandle ) ;
1057+ WaitOnHandle ( _keyExchangeCompletedWaitHandle . WaitHandle ) ;
10531058 }
10541059
10551060 DiagnosticAbstraction . Log ( string . Format ( "[{0}] Sending message '{1}' to server: '{2}'." , ToHex ( SessionId ) , message . GetType ( ) . Name , message ) ) ;
@@ -1394,9 +1399,15 @@ internal void OnKeyExchangeDhGroupExchangeReplyReceived(KeyExchangeDhGroupExchan
13941399 /// <param name="message"><see cref="KeyExchangeInitMessage"/> message.</param>
13951400 internal void OnKeyExchangeInitReceived ( KeyExchangeInitMessage message )
13961401 {
1397- _keyExchangeInProgress = true ;
1402+ // If _keyExchangeCompletedWaitHandle is already set, then this is a key
1403+ // re-exchange initiated by the server, and we need to send our own init
1404+ // message.
1405+ // Otherwise, the wait handle is not set and this received init is part of the
1406+ // initial connection for which we have already sent our init, so we shouldn't
1407+ // send another one.
1408+ var sendClientInitMessage = _keyExchangeCompletedWaitHandle . IsSet ;
13981409
1399- _ = _keyExchangeCompletedWaitHandle . Reset ( ) ;
1410+ _keyExchangeCompletedWaitHandle . Reset ( ) ;
14001411
14011412 // Disable messages that are not key exchange related
14021413 _sshMessageFactory . DisableNonKeyExchangeMessages ( ) ;
@@ -1411,7 +1422,7 @@ internal void OnKeyExchangeInitReceived(KeyExchangeInitMessage message)
14111422 _keyExchange . HostKeyReceived += KeyExchange_HostKeyReceived ;
14121423
14131424 // Start the algorithm implementation
1414- _keyExchange . Start ( this , message ) ;
1425+ _keyExchange . Start ( this , message , sendClientInitMessage ) ;
14151426
14161427 KeyExchangeInitReceived ? . Invoke ( this , new MessageEventArgs < KeyExchangeInitMessage > ( message ) ) ;
14171428 }
@@ -1477,9 +1488,7 @@ internal void OnNewKeysReceived(NewKeysMessage message)
14771488 NewKeysReceived ? . Invoke ( this , new MessageEventArgs < NewKeysMessage > ( message ) ) ;
14781489
14791490 // Signal that key exchange completed
1480- _ = _keyExchangeCompletedWaitHandle . Set ( ) ;
1481-
1482- _keyExchangeInProgress = false ;
1491+ _keyExchangeCompletedWaitHandle . Set ( ) ;
14831492 }
14841493
14851494 /// <summary>
@@ -1967,15 +1976,14 @@ private void RaiseError(Exception exp)
19671976 private void Reset ( )
19681977 {
19691978 _ = _exceptionWaitHandle ? . Reset ( ) ;
1970- _ = _keyExchangeCompletedWaitHandle ? . Reset ( ) ;
1979+ _keyExchangeCompletedWaitHandle ? . Reset ( ) ;
19711980 _ = _messageListenerCompleted ? . Set ( ) ;
19721981
19731982 SessionId = null ;
19741983 _isDisconnectMessageSent = false ;
19751984 _isDisconnecting = false ;
19761985 _isAuthenticated = false ;
19771986 _exception = null ;
1978- _keyExchangeInProgress = false ;
19791987 }
19801988
19811989 private static SshConnectionException CreateConnectionAbortedByServerException ( )
0 commit comments