|  | @@ -11,11 +11,13 @@ namespace Renci.SshNet.Channels
 | 
											
												
													
														|  |      /// <summary>
 |  |      /// <summary>
 | 
											
												
													
														|  |      /// Implements "direct-tcpip" SSH channel.
 |  |      /// Implements "direct-tcpip" SSH channel.
 | 
											
												
													
														|  |      /// </summary>
 |  |      /// </summary>
 | 
											
												
													
														|  | -    internal partial class ChannelDirectTcpip : ClientChannel
 |  | 
 | 
											
												
													
														|  | 
 |  | +    internal partial class ChannelDirectTcpip : ClientChannel, IChannelDirectTcpip
 | 
											
												
													
														|  |      {
 |  |      {
 | 
											
												
													
														|  |          private EventWaitHandle _channelEof = new AutoResetEvent(false);
 |  |          private EventWaitHandle _channelEof = new AutoResetEvent(false);
 | 
											
												
													
														|  |          private EventWaitHandle _channelOpen = new AutoResetEvent(false);
 |  |          private EventWaitHandle _channelOpen = new AutoResetEvent(false);
 | 
											
												
													
														|  |          private EventWaitHandle _channelData = new AutoResetEvent(false);
 |  |          private EventWaitHandle _channelData = new AutoResetEvent(false);
 | 
											
												
													
														|  | 
 |  | +        private EventWaitHandle _channelInterrupted = new ManualResetEvent(false);
 | 
											
												
													
														|  | 
 |  | +        private IForwardedPort _forwardedPort;
 | 
											
												
													
														|  |          private Socket _socket;
 |  |          private Socket _socket;
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |          /// <summary>
 |  |          /// <summary>
 | 
											
										
											
												
													
														|  | @@ -29,24 +31,19 @@ namespace Renci.SshNet.Channels
 | 
											
												
													
														|  |              get { return ChannelTypes.DirectTcpip; }
 |  |              get { return ChannelTypes.DirectTcpip; }
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -        /// <summary>
 |  | 
 | 
											
												
													
														|  | -        /// Initializes a new instance of the <see cref="ChannelDirectTcpip"/> class.
 |  | 
 | 
											
												
													
														|  | -        /// </summary>
 |  | 
 | 
											
												
													
														|  | -        public ChannelDirectTcpip()
 |  | 
 | 
											
												
													
														|  | 
 |  | +        public void Open(string remoteHost, uint port, IForwardedPort forwardedPort, Socket socket)
 | 
											
												
													
														|  |          {
 |  |          {
 | 
											
												
													
														|  | -        }
 |  | 
 | 
											
												
													
														|  | 
 |  | +            if (IsOpen)
 | 
											
												
													
														|  | 
 |  | +                throw new SshException("Channel is already open.");
 | 
											
												
													
														|  | 
 |  | +            if (!this.IsConnected)
 | 
											
												
													
														|  | 
 |  | +                throw new SshException("Session is not connected.");
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -        public void Open(string remoteHost, uint port, Socket socket)
 |  | 
 | 
											
												
													
														|  | -        {
 |  | 
 | 
											
												
													
														|  | -            this._socket = socket;
 |  | 
 | 
											
												
													
														|  | 
 |  | +            _socket = socket;
 | 
											
												
													
														|  | 
 |  | +            _forwardedPort = forwardedPort;
 | 
											
												
													
														|  | 
 |  | +            _forwardedPort.Closing += ForwardedPort_Closing;
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |              var ep = socket.RemoteEndPoint as IPEndPoint;
 |  |              var ep = socket.RemoteEndPoint as IPEndPoint;
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -            if (!this.IsConnected)
 |  | 
 | 
											
												
													
														|  | -            {
 |  | 
 | 
											
												
													
														|  | -                throw new SshException("Session is not connected.");
 |  | 
 | 
											
												
													
														|  | -            }
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  |              //  Open channel
 |  |              //  Open channel
 | 
											
												
													
														|  |              this.SendMessage(new ChannelOpenMessage(this.LocalChannelNumber, this.LocalWindowSize, this.LocalPacketSize,
 |  |              this.SendMessage(new ChannelOpenMessage(this.LocalChannelNumber, this.LocalWindowSize, this.LocalPacketSize,
 | 
											
												
													
														|  |                                                          new DirectTcpipChannelInfo(remoteHost, port, ep.Address.ToString(), (uint)ep.Port)));
 |  |                                                          new DirectTcpipChannelInfo(remoteHost, port, ep.Address.ToString(), (uint)ep.Port)));
 | 
											
										
											
												
													
														|  | @@ -55,6 +52,13 @@ namespace Renci.SshNet.Channels
 | 
											
												
													
														|  |              this.WaitOnHandle(this._channelOpen);
 |  |              this.WaitOnHandle(this._channelOpen);
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +        private void ForwardedPort_Closing(object sender, EventArgs eventArgs)
 | 
											
												
													
														|  | 
 |  | +        {
 | 
											
												
													
														|  | 
 |  | +            // close the socket, hereby interrupting the blocking receive in Bind()
 | 
											
												
													
														|  | 
 |  | +            if (_socket != null)
 | 
											
												
													
														|  | 
 |  | +                CloseSocket();
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |          /// <summary>
 |  |          /// <summary>
 | 
											
												
													
														|  |          /// Binds channel to remote host.
 |  |          /// Binds channel to remote host.
 | 
											
												
													
														|  |          /// </summary>
 |  |          /// </summary>
 | 
											
										
											
												
													
														|  | @@ -64,76 +68,79 @@ namespace Renci.SshNet.Channels
 | 
											
												
													
														|  |              if (!this.IsOpen)
 |  |              if (!this.IsOpen)
 | 
											
												
													
														|  |                  return;
 |  |                  return;
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -            //  Start reading data from the port and send to channel
 |  | 
 | 
											
												
													
														|  | -            Exception exception = null;
 |  | 
 | 
											
												
													
														|  | 
 |  | +            var buffer = new byte[this.RemotePacketSize];
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -            try
 |  | 
 | 
											
												
													
														|  | 
 |  | +            while (this._socket != null && _socket.Connected)
 | 
											
												
													
														|  |              {
 |  |              {
 | 
											
												
													
														|  | -                var buffer = new byte[this.RemotePacketSize];
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -                while (this._socket != null && this._socket.CanRead())
 |  | 
 | 
											
												
													
														|  | 
 |  | +                try
 | 
											
												
													
														|  |                  {
 |  |                  {
 | 
											
												
													
														|  | -                    try
 |  | 
 | 
											
												
													
														|  | 
 |  | +                    var read = 0;
 | 
											
												
													
														|  | 
 |  | +                    this.InternalSocketReceive(buffer, ref read);
 | 
											
												
													
														|  | 
 |  | +                    if (read > 0)
 | 
											
												
													
														|  |                      {
 |  |                      {
 | 
											
												
													
														|  | -                        var read = 0;
 |  | 
 | 
											
												
													
														|  | -                        this.InternalSocketReceive(buffer, ref read);
 |  | 
 | 
											
												
													
														|  | -                        if (read > 0)
 |  | 
 | 
											
												
													
														|  | -                        {
 |  | 
 | 
											
												
													
														|  | -                            this.SendMessage(new ChannelDataMessage(this.RemoteChannelNumber, buffer.Take(read).ToArray()));
 |  | 
 | 
											
												
													
														|  | -                        }
 |  | 
 | 
											
												
													
														|  | -                        else
 |  | 
 | 
											
												
													
														|  | -                        {
 |  | 
 | 
											
												
													
														|  | -                            break;
 |  | 
 | 
											
												
													
														|  | -                        }
 |  | 
 | 
											
												
													
														|  | 
 |  | +                        this.SendMessage(new ChannelDataMessage(this.RemoteChannelNumber, buffer.Take(read).ToArray()));
 | 
											
												
													
														|  |                      }
 |  |                      }
 | 
											
												
													
														|  | -                    catch (SocketException exp)
 |  | 
 | 
											
												
													
														|  | 
 |  | +                    else
 | 
											
												
													
														|  |                      {
 |  |                      {
 | 
											
												
													
														|  | -                        if (exp.SocketErrorCode == SocketError.WouldBlock ||
 |  | 
 | 
											
												
													
														|  | -                            exp.SocketErrorCode == SocketError.IOPending ||
 |  | 
 | 
											
												
													
														|  | -                            exp.SocketErrorCode == SocketError.NoBufferSpaceAvailable)
 |  | 
 | 
											
												
													
														|  | -                        {
 |  | 
 | 
											
												
													
														|  | 
 |  | +                        // client quit sending
 | 
											
												
													
														|  | 
 |  | +                        break;
 | 
											
												
													
														|  | 
 |  | +                    }
 | 
											
												
													
														|  | 
 |  | +                }
 | 
											
												
													
														|  | 
 |  | +                catch (SocketException exp)
 | 
											
												
													
														|  | 
 |  | +                {
 | 
											
												
													
														|  | 
 |  | +                    switch (exp.SocketErrorCode)
 | 
											
												
													
														|  | 
 |  | +                    {
 | 
											
												
													
														|  | 
 |  | +                        case SocketError.WouldBlock:
 | 
											
												
													
														|  | 
 |  | +                        case SocketError.IOPending:
 | 
											
												
													
														|  | 
 |  | +                        case SocketError.NoBufferSpaceAvailable:
 | 
											
												
													
														|  |                              // socket buffer is probably empty, wait and try again
 |  |                              // socket buffer is probably empty, wait and try again
 | 
											
												
													
														|  |                              Thread.Sleep(30);
 |  |                              Thread.Sleep(30);
 | 
											
												
													
														|  | -                        }
 |  | 
 | 
											
												
													
														|  | -                        else if (exp.SocketErrorCode == SocketError.ConnectionAborted || exp.SocketErrorCode == SocketError.ConnectionReset)
 |  | 
 | 
											
												
													
														|  | -                        {
 |  | 
 | 
											
												
													
														|  |                              break;
 |  |                              break;
 | 
											
												
													
														|  | -                        }
 |  | 
 | 
											
												
													
														|  | -                        else
 |  | 
 | 
											
												
													
														|  | -                            throw;  // throw any other error
 |  | 
 | 
											
												
													
														|  | 
 |  | +                        case SocketError.ConnectionAborted:
 | 
											
												
													
														|  | 
 |  | +                        case SocketError.ConnectionReset:
 | 
											
												
													
														|  | 
 |  | +                            // connection was closed after receiving SSH_MSG_CHANNEL_CLOSE message
 | 
											
												
													
														|  | 
 |  | +                            // in which case the _channelEof waithandle is also set
 | 
											
												
													
														|  | 
 |  | +                            break;
 | 
											
												
													
														|  | 
 |  | +                        case SocketError.Interrupted:
 | 
											
												
													
														|  | 
 |  | +                            // connection was interrupted as part of closing the forwarded port
 | 
											
												
													
														|  | 
 |  | +                            _channelInterrupted.Set();
 | 
											
												
													
														|  | 
 |  | +                            break;
 | 
											
												
													
														|  | 
 |  | +                        default:
 | 
											
												
													
														|  | 
 |  | +                            throw; // throw any other error
 | 
											
												
													
														|  |                      }
 |  |                      }
 | 
											
												
													
														|  |                  }
 |  |                  }
 | 
											
												
													
														|  |              }
 |  |              }
 | 
											
												
													
														|  | -            catch (Exception exp)
 |  | 
 | 
											
												
													
														|  | -            {
 |  | 
 | 
											
												
													
														|  | -                exception = exp;
 |  | 
 | 
											
												
													
														|  | -            }
 |  | 
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -            //  Channel was open and we MUST receive EOF notification,
 |  | 
 | 
											
												
													
														|  | -            //  data transfer can take longer than connection specified timeout
 |  | 
 | 
											
												
													
														|  | -            //  If listener thread is finished then socket was closed
 |  | 
 | 
											
												
													
														|  | -            WaitHandle.WaitAny(new WaitHandle[] {_channelEof});
 |  | 
 | 
											
												
													
														|  | 
 |  | +            WaitHandle.WaitAny(new WaitHandle[] { _channelEof, _channelInterrupted });
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -            //  Close socket if still open
 |  | 
 | 
											
												
													
														|  | -            if (this._socket != null)
 |  | 
 | 
											
												
													
														|  | -            {
 |  | 
 | 
											
												
													
														|  | -                this._socket.Dispose();
 |  | 
 | 
											
												
													
														|  | -                this._socket = null;
 |  | 
 | 
											
												
													
														|  | -            }
 |  | 
 | 
											
												
													
														|  | 
 |  | +        /// <summary>
 | 
											
												
													
														|  | 
 |  | +        /// Closes the socket, hereby interrupting the blocking receive in <see cref="Bind()"/>.
 | 
											
												
													
														|  | 
 |  | +        /// </summary>
 | 
											
												
													
														|  | 
 |  | +        private void CloseSocket()
 | 
											
												
													
														|  | 
 |  | +        {
 | 
											
												
													
														|  | 
 |  | +            if (!_socket.Connected)
 | 
											
												
													
														|  | 
 |  | +                return;
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -            if (exception != null)
 |  | 
 | 
											
												
													
														|  | -                throw exception;
 |  | 
 | 
											
												
													
														|  | 
 |  | +            _socket.Shutdown(SocketShutdown.Both);
 | 
											
												
													
														|  | 
 |  | +            _socket.Close();
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +        /// <summary>
 | 
											
												
													
														|  | 
 |  | +        /// Closes the channel.
 | 
											
												
													
														|  | 
 |  | +        /// </summary>
 | 
											
												
													
														|  |          public override void Close()
 |  |          public override void Close()
 | 
											
												
													
														|  |          {
 |  |          {
 | 
											
												
													
														|  | -            //  Close socket if still open
 |  | 
 | 
											
												
													
														|  | -            if (this._socket != null)
 |  | 
 | 
											
												
													
														|  | 
 |  | +            if (_forwardedPort != null)
 | 
											
												
													
														|  |              {
 |  |              {
 | 
											
												
													
														|  | -                this._socket.Dispose();
 |  | 
 | 
											
												
													
														|  | -                this._socket = null;
 |  | 
 | 
											
												
													
														|  | 
 |  | +                _forwardedPort.Closing -= ForwardedPort_Closing;
 | 
											
												
													
														|  | 
 |  | +                _forwardedPort = null;
 | 
											
												
													
														|  |              }
 |  |              }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +            // close the socket, hereby interrupting the blocking receive in Bind()
 | 
											
												
													
														|  | 
 |  | +            if (this._socket != null)
 | 
											
												
													
														|  | 
 |  | +                CloseSocket();
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |              //  Send EOF message first when channel need to be closed
 |  |              //  Send EOF message first when channel need to be closed
 | 
											
												
													
														|  |              this.SendMessage(new ChannelEofMessage(this.RemoteChannelNumber));
 |  |              this.SendMessage(new ChannelEofMessage(this.RemoteChannelNumber));
 | 
											
												
													
														|  |  
 |  |  
 | 
											
										
											
												
													
														|  | @@ -174,8 +181,14 @@ namespace Renci.SshNet.Channels
 | 
											
												
													
														|  |          /// <summary>
 |  |          /// <summary>
 | 
											
												
													
														|  |          /// Called when channel has no more data to receive.
 |  |          /// Called when channel has no more data to receive.
 | 
											
												
													
														|  |          /// </summary>
 |  |          /// </summary>
 | 
											
												
													
														|  | -        protected override void OnEof() {
 |  | 
 | 
											
												
													
														|  | -	        base.OnEof();
 |  | 
 | 
											
												
													
														|  | 
 |  | +        protected override void OnEof()
 | 
											
												
													
														|  | 
 |  | +        {
 | 
											
												
													
														|  | 
 |  | +            base.OnEof();
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +            // the channel will send no more data, so signal to the client that
 | 
											
												
													
														|  | 
 |  | +            // we won't be sending anything anymore
 | 
											
												
													
														|  | 
 |  | +            if (_socket != null && _socket.Connected)
 | 
											
												
													
														|  | 
 |  | +                _socket.Shutdown(SocketShutdown.Send);
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |              var channelEof = this._channelEof;
 |  |              var channelEof = this._channelEof;
 | 
											
												
													
														|  |              if (channelEof != null)
 |  |              if (channelEof != null)
 | 
											
										
											
												
													
														|  | @@ -191,34 +204,57 @@ namespace Renci.SshNet.Channels
 | 
											
												
													
														|  |                  channelEof.Set();
 |  |                  channelEof.Set();
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +        /// <summary>
 | 
											
												
													
														|  | 
 |  | +        /// Called whenever an unhandled <see cref="Exception"/> occurs in <see cref="Session"/> causing
 | 
											
												
													
														|  | 
 |  | +        /// the message loop to be interrupted.
 | 
											
												
													
														|  | 
 |  | +        /// </summary>
 | 
											
												
													
														|  |          protected override void OnErrorOccured(Exception exp)
 |  |          protected override void OnErrorOccured(Exception exp)
 | 
											
												
													
														|  |          {
 |  |          {
 | 
											
												
													
														|  |              base.OnErrorOccured(exp);
 |  |              base.OnErrorOccured(exp);
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -            //  If error occured, no more data can be received
 |  | 
 | 
											
												
													
														|  | 
 |  | +            // close the socket, hereby interrupting the blocking receive in Bind()
 | 
											
												
													
														|  | 
 |  | +            if (_socket != null)
 | 
											
												
													
														|  | 
 |  | +                CloseSocket();
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +            //  if error occured, no more data can be received
 | 
											
												
													
														|  |              var channelEof = this._channelEof;
 |  |              var channelEof = this._channelEof;
 | 
											
												
													
														|  |              if (channelEof != null)
 |  |              if (channelEof != null)
 | 
											
												
													
														|  |                  channelEof.Set();
 |  |                  channelEof.Set();
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +        /// <summary>
 | 
											
												
													
														|  | 
 |  | +        /// Called when the server wants to terminate the connection immmediately.
 | 
											
												
													
														|  | 
 |  | +        /// </summary>
 | 
											
												
													
														|  | 
 |  | +        /// <remarks>
 | 
											
												
													
														|  | 
 |  | +        /// The sender MUST NOT send or receive any data after this message, and
 | 
											
												
													
														|  | 
 |  | +        /// the recipient MUST NOT accept any data after receiving this message.
 | 
											
												
													
														|  | 
 |  | +        /// </remarks>
 | 
											
												
													
														|  |          protected override void OnDisconnected()
 |  |          protected override void OnDisconnected()
 | 
											
												
													
														|  |          {
 |  |          {
 | 
											
												
													
														|  |              base.OnDisconnected();
 |  |              base.OnDisconnected();
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +            // close the socket, hereby interrupting the blocking receive in Bind()
 | 
											
												
													
														|  | 
 |  | +            if (_socket != null)
 | 
											
												
													
														|  | 
 |  | +                CloseSocket();
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |              //  If disconnected, no more data can be received
 |  |              //  If disconnected, no more data can be received
 | 
											
												
													
														|  |              var channelEof = this._channelEof;
 |  |              var channelEof = this._channelEof;
 | 
											
												
													
														|  |              if (channelEof != null)
 |  |              if (channelEof != null)
 | 
											
												
													
														|  |                  channelEof.Set();
 |  |                  channelEof.Set();
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -        partial void ExecuteThread(Action action);
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  |          partial void InternalSocketReceive(byte[] buffer, ref int read);
 |  |          partial void InternalSocketReceive(byte[] buffer, ref int read);
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |          partial void InternalSocketSend(byte[] data);
 |  |          partial void InternalSocketSend(byte[] data);
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |          protected override void Dispose(bool disposing)
 |  |          protected override void Dispose(bool disposing)
 | 
											
												
													
														|  |          {
 |  |          {
 | 
											
												
													
														|  | 
 |  | +            if (_forwardedPort != null)
 | 
											
												
													
														|  | 
 |  | +            {
 | 
											
												
													
														|  | 
 |  | +                _forwardedPort.Closing -= ForwardedPort_Closing;
 | 
											
												
													
														|  | 
 |  | +                _forwardedPort = null;
 | 
											
												
													
														|  | 
 |  | +            }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |              if (this._socket != null)
 |  |              if (this._socket != null)
 | 
											
												
													
														|  |              {
 |  |              {
 | 
											
												
													
														|  |                  this._socket.Dispose();
 |  |                  this._socket.Dispose();
 | 
											
										
											
												
													
														|  | @@ -243,6 +279,12 @@ namespace Renci.SshNet.Channels
 | 
											
												
													
														|  |                  this._channelData = null;
 |  |                  this._channelData = null;
 | 
											
												
													
														|  |              }
 |  |              }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +            if (_channelInterrupted != null)
 | 
											
												
													
														|  | 
 |  | +            {
 | 
											
												
													
														|  | 
 |  | +                _channelInterrupted.Dispose();
 | 
											
												
													
														|  | 
 |  | +                _channelInterrupted = null;
 | 
											
												
													
														|  | 
 |  | +            }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |              base.Dispose(disposing);
 |  |              base.Dispose(disposing);
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  |      }
 |  |      }
 |