|  | @@ -63,7 +63,9 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |          /// </value>
 | 
	
		
			
				|  |  |          private const int LocalChannelDataPacketSize = 1024*64;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +#if !TUNING
 | 
	
		
			
				|  |  |          private static readonly RNGCryptoServiceProvider Randomizer = new RNGCryptoServiceProvider();
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #if SILVERLIGHT
 | 
	
		
			
				|  |  |          private static readonly Regex ServerVersionRe = new Regex("^SSH-(?<protoversion>[^-]+)-(?<softwareversion>.+)( SP.+)?$");
 | 
	
	
		
			
				|  | @@ -149,7 +151,7 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
		
			
				|  |  |          private bool _isDisconnecting;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        private KeyExchange _keyExchange;
 | 
	
		
			
				|  |  | +        private IKeyExchange _keyExchange;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          private HashAlgorithm _serverMac;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -165,6 +167,11 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          private SemaphoreLight _sessionSemaphore;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        /// <summary>
 | 
	
		
			
				|  |  | +        /// Holds the factory to use for creating new services.
 | 
	
		
			
				|  |  | +        /// </summary>
 | 
	
		
			
				|  |  | +        private readonly IServiceFactory _serviceFactory;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// Gets the session semaphore that controls session channels.
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
	
		
			
				|  | @@ -225,6 +232,9 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |          /// This methods returns true in all but the following cases:
 | 
	
		
			
				|  |  |          /// <list type="bullet">
 | 
	
		
			
				|  |  |          ///     <item>
 | 
	
		
			
				|  |  | +        ///         <description>The <see cref="Session"/> is disposed.</description>
 | 
	
		
			
				|  |  | +        ///     </item>
 | 
	
		
			
				|  |  | +        ///     <item>
 | 
	
		
			
				|  |  |          ///         <description>The SSH_MSG_DISCONNECT message - which is used to disconnect from the server - has been sent.</description>
 | 
	
		
			
				|  |  |          ///     </item>
 | 
	
		
			
				|  |  |          ///     <item>
 | 
	
	
		
			
				|  | @@ -242,7 +252,7 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              get
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                if (_isDisconnectMessageSent || !_isAuthenticated)
 | 
	
		
			
				|  |  | +                if (_disposed || _isDisconnectMessageSent || !_isAuthenticated)
 | 
	
		
			
				|  |  |                      return false;
 | 
	
		
			
				|  |  |                  if (_messageListenerCompleted == null || _messageListenerCompleted.WaitOne(0))
 | 
	
		
			
				|  |  |                      return false;
 | 
	
	
		
			
				|  | @@ -468,15 +478,20 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |          /// Initializes a new instance of the <see cref="Session"/> class.
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
		
			
				|  |  |          /// <param name="connectionInfo">The connection info.</param>
 | 
	
		
			
				|  |  | -        /// <exception cref="ArgumentNullException"><paramref name="connectionInfo"/> is <c>null</c>.</exception>
 | 
	
		
			
				|  |  | -        internal Session(ConnectionInfo connectionInfo)
 | 
	
		
			
				|  |  | +        /// <param name="serviceFactory">The factory to use for creating new services.</param>
 | 
	
		
			
				|  |  | +        /// <exception cref="ArgumentNullException"><paramref name="serviceFactory"/> is <c>null</c>.</exception>
 | 
	
		
			
				|  |  | +        internal Session(ConnectionInfo connectionInfo, IServiceFactory serviceFactory)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              if (connectionInfo == null)
 | 
	
		
			
				|  |  |                  throw new ArgumentNullException("connectionInfo");
 | 
	
		
			
				|  |  | +            if (serviceFactory == null)
 | 
	
		
			
				|  |  | +                throw new ArgumentNullException("serviceFactory");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              ConnectionInfo = connectionInfo;
 | 
	
		
			
				|  |  |              //this.ClientVersion = string.Format(CultureInfo.CurrentCulture, "SSH-2.0-Renci.SshNet.SshClient.{0}", this.GetType().Assembly.GetName().Version);
 | 
	
		
			
				|  |  | -            ClientVersion = string.Format(CultureInfo.CurrentCulture, "SSH-2.0-Renci.SshNet.SshClient.0.0.1");
 | 
	
		
			
				|  |  | +            ClientVersion = "SSH-2.0-Renci.SshNet.SshClient.0.0.1";
 | 
	
		
			
				|  |  | +            _serviceFactory = serviceFactory;
 | 
	
		
			
				|  |  | +            _messageListenerCompleted = new ManualResetEvent(true);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
	
		
			
				|  | @@ -538,7 +553,7 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |                          var serverVersion = string.Empty;
 | 
	
		
			
				|  |  |                          SocketReadLine(ref serverVersion, ConnectionInfo.Timeout);
 | 
	
		
			
				|  |  |                          if (serverVersion == null)
 | 
	
		
			
				|  |  | -                            throw new SshConnectionException("Server response does not contain SSH protocol identification.");
 | 
	
		
			
				|  |  | +                            throw new SshConnectionException("Server response does not contain SSH protocol identification.", DisconnectReason.ProtocolError);
 | 
	
		
			
				|  |  |                          versionMatch = ServerVersionRe.Match(serverVersion);
 | 
	
		
			
				|  |  |                          if (versionMatch.Success)
 | 
	
		
			
				|  |  |                          {
 | 
	
	
		
			
				|  | @@ -577,20 +592,11 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |                      //  Some server implementations might sent this message first, prior establishing encryption algorithm
 | 
	
		
			
				|  |  |                      RegisterMessage("SSH_MSG_USERAUTH_BANNER");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                    //  Start incoming request listener
 | 
	
		
			
				|  |  | -                    _messageListenerCompleted = new ManualResetEvent(false);
 | 
	
		
			
				|  |  | +                    // mark the message listener threads as started
 | 
	
		
			
				|  |  | +                    _messageListenerCompleted.Reset();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                    ExecuteThread(() =>
 | 
	
		
			
				|  |  | -                    {
 | 
	
		
			
				|  |  | -                        try
 | 
	
		
			
				|  |  | -                        {
 | 
	
		
			
				|  |  | -                            MessageListener();
 | 
	
		
			
				|  |  | -                        }
 | 
	
		
			
				|  |  | -                        finally
 | 
	
		
			
				|  |  | -                        {
 | 
	
		
			
				|  |  | -                            _messageListenerCompleted.Set();
 | 
	
		
			
				|  |  | -                        }
 | 
	
		
			
				|  |  | -                    });
 | 
	
		
			
				|  |  | +                    //  Start incoming request listener
 | 
	
		
			
				|  |  | +                    ExecuteThread(MessageListener);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                      //  Wait for key exchange to be completed
 | 
	
		
			
				|  |  |                      WaitOnHandle(_keyExchangeCompletedWaitHandle);
 | 
	
	
		
			
				|  | @@ -613,7 +619,7 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |                          throw new SshException("Username is not specified.");
 | 
	
		
			
				|  |  |                      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                    ConnectionInfo.Authenticate(this);
 | 
	
		
			
				|  |  | +                    ConnectionInfo.Authenticate(this, _serviceFactory);
 | 
	
		
			
				|  |  |                      _isAuthenticated = true;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                      //  Register Connection messages
 | 
	
	
		
			
				|  | @@ -650,6 +656,14 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |          public void Disconnect()
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              Disconnect(DisconnectReason.ByApplication, "Connection terminated by the client.");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // at this point, we are sure that the listener thread will stop as we've
 | 
	
		
			
				|  |  | +            // disconnected the socket, so lets wait until the message listener thread
 | 
	
		
			
				|  |  | +            // has completed
 | 
	
		
			
				|  |  | +            if (_messageListenerCompleted != null)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                _messageListenerCompleted.WaitOne();
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          private void Disconnect(DisconnectReason reason, string message)
 | 
	
	
		
			
				|  | @@ -661,19 +675,13 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |              //
 | 
	
		
			
				|  |  |              // note that this should also cause the listener thread to be stopped as
 | 
	
		
			
				|  |  |              // the server should respond by closing the socket
 | 
	
		
			
				|  |  | -            SendDisconnect(reason, message);
 | 
	
		
			
				|  |  | +            if (reason == DisconnectReason.ByApplication)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                SendDisconnect(reason, message);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              // disconnect socket, and dispose it
 | 
	
		
			
				|  |  |              SocketDisconnectAndDispose();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            if (_messageListenerCompleted != null)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                // at this point, we are sure that the listener thread will stop
 | 
	
		
			
				|  |  | -                // as we've disconnected the socket
 | 
	
		
			
				|  |  | -                _messageListenerCompleted.WaitOne();
 | 
	
		
			
				|  |  | -                _messageListenerCompleted.Dispose();
 | 
	
		
			
				|  |  | -                _messageListenerCompleted = null;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
	
		
			
				|  | @@ -693,24 +701,6 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |              WaitOnHandle(waitHandle, ConnectionInfo.Timeout);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Waits for the specified handle or the exception handle for the receive thread
 | 
	
		
			
				|  |  | -        /// to signal within the specified timeout.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <param name="waitHandle">The wait handle.</param>
 | 
	
		
			
				|  |  | -        /// <param name="timeout">The time to wait for any of the handles to become signaled.</param>
 | 
	
		
			
				|  |  | -        /// <exception cref="SshConnectionException">A received package was invalid or failed the message integrity check.</exception>
 | 
	
		
			
				|  |  | -        /// <exception cref="SshOperationTimeoutException">None of the handles are signaled in time and the session is not disconnecting.</exception>
 | 
	
		
			
				|  |  | -        /// <exception cref="SocketException">A socket error was signaled while receiving messages from the server.</exception>
 | 
	
		
			
				|  |  | -        /// <remarks>
 | 
	
		
			
				|  |  | -        /// When neither handles are signaled in time and the session is not closing, then the
 | 
	
		
			
				|  |  | -        /// session is disconnected.
 | 
	
		
			
				|  |  | -        /// </remarks>
 | 
	
		
			
				|  |  | -        void ISession.WaitOnHandle(WaitHandle waitHandle, TimeSpan timeout)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            WaitOnHandle(waitHandle, timeout);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// Waits for the specified handle or the exception handle for the receive thread
 | 
	
		
			
				|  |  |          /// to signal within the connection timeout.
 | 
	
	
		
			
				|  | @@ -754,17 +744,7 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |                  case 0:
 | 
	
		
			
				|  |  |                      throw _exception;
 | 
	
		
			
				|  |  |                  case 1:
 | 
	
		
			
				|  |  | -                    // when the session is NOT disconnecting, the listener should actually
 | 
	
		
			
				|  |  | -                    // never complete without setting the exception wait handle and should
 | 
	
		
			
				|  |  | -                    // end up in case 0... 
 | 
	
		
			
				|  |  | -                    //
 | 
	
		
			
				|  |  | -                    // when the session is disconnecting, the completion of the listener
 | 
	
		
			
				|  |  | -                    // should not be considered an error (quite the oppposite actually)
 | 
	
		
			
				|  |  | -                    if (!_isDisconnecting)
 | 
	
		
			
				|  |  | -                    {
 | 
	
		
			
				|  |  | -                        throw new SshConnectionException("Client not connected.");
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                    break;
 | 
	
		
			
				|  |  | +                    throw new SshConnectionException("Client not connected.");
 | 
	
		
			
				|  |  |                  case WaitHandle.WaitTimeout:
 | 
	
		
			
				|  |  |                      // when the session is disconnecting, a timeout is likely when no
 | 
	
		
			
				|  |  |                      // network connectivity is available; depending on the configured
 | 
	
	
		
			
				|  | @@ -1013,7 +993,7 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              //  Test packet minimum and maximum boundaries
 | 
	
		
			
				|  |  |              if (packetLength < Math.Max((byte)16, blockSize) - 4 || packetLength > MaximumSshPacketSize - 4)
 | 
	
		
			
				|  |  | -                throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Bad packet length {0}", packetLength), DisconnectReason.ProtocolError);
 | 
	
		
			
				|  |  | +                throw new SshConnectionException(string.Format(CultureInfo.CurrentCulture, "Bad packet length: {0}.", packetLength), DisconnectReason.ProtocolError);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              //  Read rest of the packet data
 | 
	
		
			
				|  |  |              var bytesToRead = (int)(packetLength - (blockSize - 4));
 | 
	
	
		
			
				|  | @@ -1145,9 +1125,7 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |          private void HandleMessage(DisconnectMessage message)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              OnDisconnectReceived(message);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            //  disconnect from the socket, and dispose it
 | 
	
		
			
				|  |  | -            SocketDisconnectAndDispose();
 | 
	
		
			
				|  |  | +            Disconnect(message.ReasonCode, message.Description);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          private void HandleMessage(IgnoreMessage message)
 | 
	
	
		
			
				|  | @@ -1298,6 +1276,9 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              Log(string.Format("Disconnect received: {0} {1}", message.ReasonCode, message.Description));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +            _exception = new SshConnectionException(string.Format(CultureInfo.InvariantCulture, "The connection was closed by the server: {0} ({1}).", message.Description, message.ReasonCode), message.ReasonCode);
 | 
	
		
			
				|  |  | +            _exceptionWaitHandle.Set();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |              var disconnectReceived = DisconnectReceived;
 | 
	
		
			
				|  |  |              if (disconnectReceived != null)
 | 
	
		
			
				|  |  |                  disconnectReceived(this, new MessageEventArgs<DisconnectMessage>(message));
 | 
	
	
		
			
				|  | @@ -1379,20 +1360,10 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |                      messageMetadata.Enabled = false;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            var keyExchangeAlgorithmName = (from c in ConnectionInfo.KeyExchangeAlgorithms.Keys
 | 
	
		
			
				|  |  | -                                            from s in message.KeyExchangeAlgorithms
 | 
	
		
			
				|  |  | -                                            where s == c
 | 
	
		
			
				|  |  | -                                            select c).FirstOrDefault();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            if (keyExchangeAlgorithmName == null)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                throw new SshConnectionException("Failed to negotiate key exchange algorithm.", DisconnectReason.KeyExchangeFailed);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | +            _keyExchange = _serviceFactory.CreateKeyExchange(ConnectionInfo.KeyExchangeAlgorithms,
 | 
	
		
			
				|  |  | +                                                             message.KeyExchangeAlgorithms);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            //  Create instance of key exchange algorithm that will be used
 | 
	
		
			
				|  |  | -            _keyExchange = ConnectionInfo.KeyExchangeAlgorithms[keyExchangeAlgorithmName].CreateInstance<KeyExchange>();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            ConnectionInfo.CurrentKeyExchangeAlgorithm = keyExchangeAlgorithmName;
 | 
	
		
			
				|  |  | +            ConnectionInfo.CurrentKeyExchangeAlgorithm = _keyExchange.Name;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              _keyExchange.HostKeyReceived += KeyExchange_HostKeyReceived;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1872,6 +1843,11 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  RaiseError(exp);
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | +            finally
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                // signal that the message listener thread has stopped
 | 
	
		
			
				|  |  | +                _messageListenerCompleted.Set();
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          private byte SocketReadByte()
 | 
	
	
		
			
				|  | @@ -2206,7 +2182,7 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |              if (errorOccured != null)
 | 
	
		
			
				|  |  |                  errorOccured(this, new ExceptionEventArgs(exp));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            if (connectionException != null && connectionException.DisconnectReason != DisconnectReason.ConnectionLost)
 | 
	
		
			
				|  |  | +            if (connectionException != null)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  Disconnect(connectionException.DisconnectReason, exp.ToString());
 | 
	
		
			
				|  |  |              }
 | 
	
	
		
			
				|  | @@ -2223,7 +2199,7 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |              if (_keyExchangeCompletedWaitHandle != null)
 | 
	
		
			
				|  |  |                  _keyExchangeCompletedWaitHandle.Reset();
 | 
	
		
			
				|  |  |              if (_messageListenerCompleted != null)
 | 
	
		
			
				|  |  | -                _messageListenerCompleted.Reset();
 | 
	
		
			
				|  |  | +                _messageListenerCompleted.Set();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              SessionId = null;
 | 
	
		
			
				|  |  |              _isDisconnectMessageSent = false;
 | 
	
	
		
			
				|  | @@ -2303,6 +2279,12 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |                          _bytesReadFromSocket.Dispose();
 | 
	
		
			
				|  |  |                          _bytesReadFromSocket = null;
 | 
	
		
			
				|  |  |                      }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                    if (_messageListenerCompleted != null)
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        _messageListenerCompleted.Dispose();
 | 
	
		
			
				|  |  | +                        _messageListenerCompleted = null;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                  // Note disposing has been done.
 |