|  | @@ -1,6 +1,4 @@
 | 
	
		
			
				|  |  |  using System;
 | 
	
		
			
				|  |  | -using System.Collections.Generic;
 | 
	
		
			
				|  |  | -using System.Net;
 | 
	
		
			
				|  |  |  using System.Net.Sockets;
 | 
	
		
			
				|  |  |  using System.Security.Cryptography;
 | 
	
		
			
				|  |  |  using System.Text;
 | 
	
	
		
			
				|  | @@ -18,6 +16,7 @@ using System.Globalization;
 | 
	
		
			
				|  |  |  using System.Linq;
 | 
	
		
			
				|  |  |  using Renci.SshNet.Abstractions;
 | 
	
		
			
				|  |  |  using Renci.SshNet.Security.Cryptography;
 | 
	
		
			
				|  |  | +using System.Collections.Generic;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  namespace Renci.SshNet
 | 
	
		
			
				|  |  |  {
 | 
	
	
		
			
				|  | @@ -27,7 +26,7 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |      public class Session : ISession
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  |          private const byte Null = 0x00;
 | 
	
		
			
				|  |  | -        private const byte CarriageReturn = 0x0d;
 | 
	
		
			
				|  |  | +        internal const byte CarriageReturn = 0x0d;
 | 
	
		
			
				|  |  |          internal const byte LineFeed = 0x0a;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
	
		
			
				|  | @@ -52,7 +51,7 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |          /// <value>
 | 
	
		
			
				|  |  |          /// 68536 (64 KB + 3000 bytes).
 | 
	
		
			
				|  |  |          /// </value>
 | 
	
		
			
				|  |  | -        private const int MaximumSshPacketSize = LocalChannelDataPacketSize + 3000;
 | 
	
		
			
				|  |  | +        internal const int MaximumSshPacketSize = LocalChannelDataPacketSize + 3000;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// Holds the initial local window size for the channels.
 | 
	
	
		
			
				|  | @@ -587,24 +586,8 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |                      // Build list of available messages while connecting
 | 
	
		
			
				|  |  |                      _sshMessageFactory = new SshMessageFactory();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                    switch (ConnectionInfo.ProxyType)
 | 
	
		
			
				|  |  | -                    {
 | 
	
		
			
				|  |  | -                        case ProxyTypes.None:
 | 
	
		
			
				|  |  | -                            SocketConnect(ConnectionInfo.Host, ConnectionInfo.Port);
 | 
	
		
			
				|  |  | -                            break;
 | 
	
		
			
				|  |  | -                        case ProxyTypes.Socks4:
 | 
	
		
			
				|  |  | -                            SocketConnect(ConnectionInfo.ProxyHost, ConnectionInfo.ProxyPort);
 | 
	
		
			
				|  |  | -                            ConnectSocks4(_socket, ConnectionInfo);
 | 
	
		
			
				|  |  | -                            break;
 | 
	
		
			
				|  |  | -                        case ProxyTypes.Socks5:
 | 
	
		
			
				|  |  | -                            SocketConnect(ConnectionInfo.ProxyHost, ConnectionInfo.ProxyPort);
 | 
	
		
			
				|  |  | -                            ConnectSocks5(_socket, ConnectionInfo);
 | 
	
		
			
				|  |  | -                            break;
 | 
	
		
			
				|  |  | -                        case ProxyTypes.Http:
 | 
	
		
			
				|  |  | -                            SocketConnect(ConnectionInfo.ProxyHost, ConnectionInfo.ProxyPort);
 | 
	
		
			
				|  |  | -                            ConnectHttp(_socket, ConnectionInfo);
 | 
	
		
			
				|  |  | -                            break;
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | +                    _socket = _serviceFactory.CreateConnector(ConnectionInfo)
 | 
	
		
			
				|  |  | +                                             .Connect(ConnectionInfo);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                      // Immediately send the identification string since the spec states both sides MUST send an identification string
 | 
	
		
			
				|  |  |                      // when the connection has been established
 | 
	
	
		
			
				|  | @@ -1698,54 +1681,6 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          #endregion
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Establishes a socket connection to the specified host and port.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <param name="host">The host name of the server to connect to.</param>
 | 
	
		
			
				|  |  | -        /// <param name="port">The port to connect to.</param>
 | 
	
		
			
				|  |  | -        /// <exception cref="SshOperationTimeoutException">The connection failed to establish within the configured <see cref="Renci.SshNet.ConnectionInfo.Timeout"/>.</exception>
 | 
	
		
			
				|  |  | -        /// <exception cref="SocketException">An error occurred trying to establish the connection.</exception>
 | 
	
		
			
				|  |  | -        private void SocketConnect(string host, int port)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            var ipAddress = DnsAbstraction.GetHostAddresses(host)[0];
 | 
	
		
			
				|  |  | -            var ep = new IPEndPoint(ipAddress, port);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            DiagnosticAbstraction.Log(string.Format("Initiating connection to '{0}:{1}'.", host, port));
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            _socket = SocketAbstraction.Connect(ep, ConnectionInfo.Timeout);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            const int socketBufferSize = 2 * MaximumSshPacketSize;
 | 
	
		
			
				|  |  | -            _socket.SendBufferSize = socketBufferSize;
 | 
	
		
			
				|  |  | -            _socket.ReceiveBufferSize = socketBufferSize;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Performs a blocking read on the socket until <paramref name="length"/> bytes are received.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <param name="socket">The <see cref="Socket"/> to read from.</param>
 | 
	
		
			
				|  |  | -        /// <param name="buffer">An array of type <see cref="byte"/> that is the storage location for the received data.</param>
 | 
	
		
			
				|  |  | -        /// <param name="offset">The position in <paramref name="buffer"/> parameter to store the received data.</param>
 | 
	
		
			
				|  |  | -        /// <param name="length">The number of bytes to read.</param>
 | 
	
		
			
				|  |  | -        /// <returns>
 | 
	
		
			
				|  |  | -        /// The number of bytes read.
 | 
	
		
			
				|  |  | -        /// </returns>
 | 
	
		
			
				|  |  | -        /// <exception cref="SshConnectionException">The socket is closed.</exception>
 | 
	
		
			
				|  |  | -        /// <exception cref="SshOperationTimeoutException">The read has timed-out.</exception>
 | 
	
		
			
				|  |  | -        /// <exception cref="SocketException">The read failed.</exception>
 | 
	
		
			
				|  |  | -        private static int SocketRead(Socket socket, byte[] buffer, int offset, int length)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            var bytesRead = SocketAbstraction.Read(socket, buffer, offset, length, InfiniteTimeSpan);
 | 
	
		
			
				|  |  | -            if (bytesRead == 0)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                // when we're in the disconnecting state (either triggered by client or server), then the
 | 
	
		
			
				|  |  | -                // SshConnectionException will interrupt the message listener loop (if not already interrupted)
 | 
	
		
			
				|  |  | -                // and the exception itself will be ignored (in RaiseError)
 | 
	
		
			
				|  |  | -                throw new SshConnectionException("An established connection was aborted by the server.",
 | 
	
		
			
				|  |  | -                                                 DisconnectReason.ConnectionLost);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            return bytesRead;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  #if FEATURE_SOCKET_POLL
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// Gets a value indicating whether the socket is connected.
 | 
	
	
		
			
				|  | @@ -1896,13 +1831,16 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |                              {
 | 
	
		
			
				|  |  |                                  DiagnosticAbstraction.Log(string.Format("[{0}] Shutting down socket.", ToHex(SessionId)));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                                // interrupt any pending reads; should be done outside of socket read lock as we
 | 
	
		
			
				|  |  | -                                // actually want shutdown the socket to make sure blocking reads are interrupted
 | 
	
		
			
				|  |  | +                                // Interrupt any pending reads; should be done outside of socket read lock as we
 | 
	
		
			
				|  |  | +                                // actually want shutdown the socket to make sure blocking reads are interrupted.
 | 
	
		
			
				|  |  |                                  //
 | 
	
		
			
				|  |  | -                                // this may result in a SocketException (eg. An existing connection was forcibly
 | 
	
		
			
				|  |  | +                                // This may result in a SocketException (eg. An existing connection was forcibly
 | 
	
		
			
				|  |  |                                  // closed by the remote host) which we'll log and ignore as it means the socket
 | 
	
		
			
				|  |  | -                                // was already shut down
 | 
	
		
			
				|  |  | -                                _socket.Shutdown(SocketShutdown.Send);
 | 
	
		
			
				|  |  | +                                // was already shut down.
 | 
	
		
			
				|  |  | +                                //
 | 
	
		
			
				|  |  | +                                // We use SocketShutdown.Both instead of SocketShutdown.Send as a workaround to a
 | 
	
		
			
				|  |  | +                                // .NET Core issue on Linux & Mac OS X.
 | 
	
		
			
				|  |  | +                                _socket.Shutdown(SocketShutdown.Both);
 | 
	
		
			
				|  |  |                              }
 | 
	
		
			
				|  |  |                              catch (SocketException ex)
 | 
	
		
			
				|  |  |                              {
 | 
	
	
		
			
				|  | @@ -2026,392 +1964,6 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        private static byte SocketReadByte(Socket socket)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            var buffer = new byte[1];
 | 
	
		
			
				|  |  | -            SocketRead(socket, buffer, 0, 1);
 | 
	
		
			
				|  |  | -            return buffer[0];
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        private static void ConnectSocks4(Socket socket, ConnectionInfo connectionInfo)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            var connectionRequest = CreateSocks4ConnectionRequest(connectionInfo.Host, (ushort)connectionInfo.Port, connectionInfo.ProxyUsername);
 | 
	
		
			
				|  |  | -            SocketAbstraction.Send(socket, connectionRequest);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            //  Read null byte
 | 
	
		
			
				|  |  | -            if (SocketReadByte(socket) != 0)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                throw new ProxyException("SOCKS4: Null is expected.");
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            //  Read response code
 | 
	
		
			
				|  |  | -            var code = SocketReadByte(socket);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            switch (code)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                case 0x5a:
 | 
	
		
			
				|  |  | -                    break;
 | 
	
		
			
				|  |  | -                case 0x5b:
 | 
	
		
			
				|  |  | -                    throw new ProxyException("SOCKS4: Connection rejected.");
 | 
	
		
			
				|  |  | -                case 0x5c:
 | 
	
		
			
				|  |  | -                    throw new ProxyException("SOCKS4: Client is not running identd or not reachable from the server.");
 | 
	
		
			
				|  |  | -                case 0x5d:
 | 
	
		
			
				|  |  | -                    throw new ProxyException("SOCKS4: Client's identd could not confirm the user ID string in the request.");
 | 
	
		
			
				|  |  | -                default:
 | 
	
		
			
				|  |  | -                    throw new ProxyException("SOCKS4: Not valid response.");
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            var dummyBuffer = new byte[6]; // field 3 (2 bytes) and field 4 (4) should be ignored
 | 
	
		
			
				|  |  | -            SocketRead(socket, dummyBuffer, 0, 6);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        private static void ConnectSocks5(Socket socket, ConnectionInfo connectionInfo)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            var greeting = new byte[]
 | 
	
		
			
				|  |  | -                {
 | 
	
		
			
				|  |  | -                    // SOCKS version number
 | 
	
		
			
				|  |  | -                    0x05,
 | 
	
		
			
				|  |  | -                    // Number of supported authentication methods
 | 
	
		
			
				|  |  | -                    0x02,
 | 
	
		
			
				|  |  | -                    // No authentication
 | 
	
		
			
				|  |  | -                    0x00,
 | 
	
		
			
				|  |  | -                    // Username/Password authentication
 | 
	
		
			
				|  |  | -                    0x02
 | 
	
		
			
				|  |  | -                };
 | 
	
		
			
				|  |  | -            SocketAbstraction.Send(socket, greeting);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            var socksVersion = SocketReadByte(socket);
 | 
	
		
			
				|  |  | -            if (socksVersion != 0x05)
 | 
	
		
			
				|  |  | -                throw new ProxyException(string.Format("SOCKS Version '{0}' is not supported.", socksVersion));
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            var authenticationMethod = SocketReadByte(socket);
 | 
	
		
			
				|  |  | -            switch (authenticationMethod)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                case 0x00:
 | 
	
		
			
				|  |  | -                    break;
 | 
	
		
			
				|  |  | -                case 0x02:
 | 
	
		
			
				|  |  | -                    // Create username/password authentication request
 | 
	
		
			
				|  |  | -                    var authenticationRequest = CreateSocks5UserNameAndPasswordAuthenticationRequest(connectionInfo.ProxyUsername, connectionInfo.ProxyPassword);
 | 
	
		
			
				|  |  | -                    // Send authentication request
 | 
	
		
			
				|  |  | -                    SocketAbstraction.Send(socket, authenticationRequest);
 | 
	
		
			
				|  |  | -                    // Read authentication result
 | 
	
		
			
				|  |  | -                    var authenticationResult = SocketAbstraction.Read(socket, 2, connectionInfo.Timeout);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                    if (authenticationResult[0] != 0x01)
 | 
	
		
			
				|  |  | -                        throw new ProxyException("SOCKS5: Server authentication version is not valid.");
 | 
	
		
			
				|  |  | -                    if (authenticationResult[1] != 0x00)
 | 
	
		
			
				|  |  | -                        throw new ProxyException("SOCKS5: Username/Password authentication failed.");
 | 
	
		
			
				|  |  | -                    break;
 | 
	
		
			
				|  |  | -                case 0xFF:
 | 
	
		
			
				|  |  | -                    throw new ProxyException("SOCKS5: No acceptable authentication methods were offered.");
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            var connectionRequest = CreateSocks5ConnectionRequest(connectionInfo.Host, (ushort)connectionInfo.Port);
 | 
	
		
			
				|  |  | -            SocketAbstraction.Send(socket, connectionRequest);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            //  Read Server SOCKS5 version
 | 
	
		
			
				|  |  | -            if (SocketReadByte(socket) != 5)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                throw new ProxyException("SOCKS5: Version 5 is expected.");
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            //  Read response code
 | 
	
		
			
				|  |  | -            var status = SocketReadByte(socket);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            switch (status)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                case 0x00:
 | 
	
		
			
				|  |  | -                    break;
 | 
	
		
			
				|  |  | -                case 0x01:
 | 
	
		
			
				|  |  | -                    throw new ProxyException("SOCKS5: General failure.");
 | 
	
		
			
				|  |  | -                case 0x02:
 | 
	
		
			
				|  |  | -                    throw new ProxyException("SOCKS5: Connection not allowed by ruleset.");
 | 
	
		
			
				|  |  | -                case 0x03:
 | 
	
		
			
				|  |  | -                    throw new ProxyException("SOCKS5: Network unreachable.");
 | 
	
		
			
				|  |  | -                case 0x04:
 | 
	
		
			
				|  |  | -                    throw new ProxyException("SOCKS5: Host unreachable.");
 | 
	
		
			
				|  |  | -                case 0x05:
 | 
	
		
			
				|  |  | -                    throw new ProxyException("SOCKS5: Connection refused by destination host.");
 | 
	
		
			
				|  |  | -                case 0x06:
 | 
	
		
			
				|  |  | -                    throw new ProxyException("SOCKS5: TTL expired.");
 | 
	
		
			
				|  |  | -                case 0x07:
 | 
	
		
			
				|  |  | -                    throw new ProxyException("SOCKS5: Command not supported or protocol error.");
 | 
	
		
			
				|  |  | -                case 0x08:
 | 
	
		
			
				|  |  | -                    throw new ProxyException("SOCKS5: Address type not supported.");
 | 
	
		
			
				|  |  | -                default:
 | 
	
		
			
				|  |  | -                    throw new ProxyException("SOCKS5: Not valid response.");
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            //  Read reserved byte
 | 
	
		
			
				|  |  | -            if (SocketReadByte(socket) != 0)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                throw new ProxyException("SOCKS5: 0 byte is expected.");
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            var addressType = SocketReadByte(socket);
 | 
	
		
			
				|  |  | -            switch (addressType)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                case 0x01:
 | 
	
		
			
				|  |  | -                    var ipv4 = new byte[4];
 | 
	
		
			
				|  |  | -                    SocketRead(socket, ipv4, 0, 4);
 | 
	
		
			
				|  |  | -                    break;
 | 
	
		
			
				|  |  | -                case 0x04:
 | 
	
		
			
				|  |  | -                    var ipv6 = new byte[16];
 | 
	
		
			
				|  |  | -                    SocketRead(socket, ipv6, 0, 16);
 | 
	
		
			
				|  |  | -                    break;
 | 
	
		
			
				|  |  | -                default:
 | 
	
		
			
				|  |  | -                    throw new ProxyException(string.Format("Address type '{0}' is not supported.", addressType));
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            var port = new byte[2];
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            //  Read 2 bytes to be ignored
 | 
	
		
			
				|  |  | -            SocketRead(socket, port, 0, 2);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// https://tools.ietf.org/html/rfc1929
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        private static byte[] CreateSocks5UserNameAndPasswordAuthenticationRequest(string username, string password)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            if (username.Length > byte.MaxValue)
 | 
	
		
			
				|  |  | -                throw new ProxyException("Proxy username is too long.");
 | 
	
		
			
				|  |  | -            if (password.Length > byte.MaxValue)
 | 
	
		
			
				|  |  | -                throw new ProxyException("Proxy password is too long.");
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            var authenticationRequest = new byte
 | 
	
		
			
				|  |  | -                [
 | 
	
		
			
				|  |  | -                    // Version of the negotiation
 | 
	
		
			
				|  |  | -                    1 +
 | 
	
		
			
				|  |  | -                    // Length of the username
 | 
	
		
			
				|  |  | -                    1 +
 | 
	
		
			
				|  |  | -                    // Username
 | 
	
		
			
				|  |  | -                    username.Length +
 | 
	
		
			
				|  |  | -                    // Length of the password
 | 
	
		
			
				|  |  | -                    1 +
 | 
	
		
			
				|  |  | -                    // Password
 | 
	
		
			
				|  |  | -                    password.Length
 | 
	
		
			
				|  |  | -                ];
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            var index = 0;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            // Version of the negiotiation
 | 
	
		
			
				|  |  | -            authenticationRequest[index++] = 0x01;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            // Length of the username
 | 
	
		
			
				|  |  | -            authenticationRequest[index++] = (byte) username.Length;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            // Username
 | 
	
		
			
				|  |  | -            SshData.Ascii.GetBytes(username, 0, username.Length, authenticationRequest, index);
 | 
	
		
			
				|  |  | -            index += username.Length;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            // Length of the password
 | 
	
		
			
				|  |  | -            authenticationRequest[index++] = (byte) password.Length;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            // Password
 | 
	
		
			
				|  |  | -            SshData.Ascii.GetBytes(password, 0, password.Length, authenticationRequest, index);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            return authenticationRequest;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        private static byte[] CreateSocks4ConnectionRequest(string hostname, ushort port, string username)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            var addressBytes = GetSocks4DestinationAddress(hostname);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            var connectionRequest = new byte
 | 
	
		
			
				|  |  | -                [
 | 
	
		
			
				|  |  | -                    // SOCKS version number
 | 
	
		
			
				|  |  | -                    1 +
 | 
	
		
			
				|  |  | -                    // Command code
 | 
	
		
			
				|  |  | -                    1 +
 | 
	
		
			
				|  |  | -                    // Port number
 | 
	
		
			
				|  |  | -                    2 +
 | 
	
		
			
				|  |  | -                    // IP address
 | 
	
		
			
				|  |  | -                    addressBytes.Length +
 | 
	
		
			
				|  |  | -                    // Username
 | 
	
		
			
				|  |  | -                    username.Length +
 | 
	
		
			
				|  |  | -                    // Null terminator
 | 
	
		
			
				|  |  | -                    1
 | 
	
		
			
				|  |  | -                ];
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            var index = 0;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            // SOCKS version number
 | 
	
		
			
				|  |  | -            connectionRequest[index++] = 0x04;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            // Command code
 | 
	
		
			
				|  |  | -            connectionRequest[index++] = 0x01; // establish a TCP/IP stream connection
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            // Port number
 | 
	
		
			
				|  |  | -            Pack.UInt16ToBigEndian(port, connectionRequest, index);
 | 
	
		
			
				|  |  | -            index += 2;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            // Address
 | 
	
		
			
				|  |  | -            Buffer.BlockCopy(addressBytes, 0, connectionRequest, index, addressBytes.Length);
 | 
	
		
			
				|  |  | -            index += addressBytes.Length;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            connectionRequest[index] = 0x00;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            return connectionRequest;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        private static byte[] CreateSocks5ConnectionRequest(string hostname, ushort port)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            byte addressType;
 | 
	
		
			
				|  |  | -            var addressBytes = GetSocks5DestinationAddress(hostname, out addressType);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            var connectionRequest = new byte
 | 
	
		
			
				|  |  | -                [
 | 
	
		
			
				|  |  | -                    // SOCKS version number
 | 
	
		
			
				|  |  | -                    1 +
 | 
	
		
			
				|  |  | -                    // Command code
 | 
	
		
			
				|  |  | -                    1 +
 | 
	
		
			
				|  |  | -                    // Reserved
 | 
	
		
			
				|  |  | -                    1 +
 | 
	
		
			
				|  |  | -                    // Address type
 | 
	
		
			
				|  |  | -                    1 +
 | 
	
		
			
				|  |  | -                    // Address
 | 
	
		
			
				|  |  | -                    addressBytes.Length +
 | 
	
		
			
				|  |  | -                    // Port number
 | 
	
		
			
				|  |  | -                    2
 | 
	
		
			
				|  |  | -                ];
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            var index = 0;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            // SOCKS version number
 | 
	
		
			
				|  |  | -            connectionRequest[index++] = 0x05;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            // Command code
 | 
	
		
			
				|  |  | -            connectionRequest[index++] = 0x01; // establish a TCP/IP stream connection
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            // Reserved
 | 
	
		
			
				|  |  | -            connectionRequest[index++] = 0x00;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            // Address type
 | 
	
		
			
				|  |  | -            connectionRequest[index++] = addressType;
 | 
	
		
			
				|  |  | -            
 | 
	
		
			
				|  |  | -            // Address
 | 
	
		
			
				|  |  | -            Buffer.BlockCopy(addressBytes, 0, connectionRequest, index, addressBytes.Length);
 | 
	
		
			
				|  |  | -            index += addressBytes.Length;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            // Port number
 | 
	
		
			
				|  |  | -            Pack.UInt16ToBigEndian(port, connectionRequest, index);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            return connectionRequest;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        private static byte[] GetSocks4DestinationAddress(string hostname)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            var addresses = DnsAbstraction.GetHostAddresses(hostname);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            for (var i = 0; i < addresses.Length; i++)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                var address = addresses[i];
 | 
	
		
			
				|  |  | -                if (address.AddressFamily == AddressFamily.InterNetwork)
 | 
	
		
			
				|  |  | -                    return address.GetAddressBytes();
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            throw new ProxyException(string.Format("SOCKS4 only supports IPv4. No such address found for '{0}'.", hostname));
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        private static byte[] GetSocks5DestinationAddress(string hostname, out byte addressType)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            var ip = DnsAbstraction.GetHostAddresses(hostname)[0];
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            byte[] address;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            switch (ip.AddressFamily)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                case AddressFamily.InterNetwork:
 | 
	
		
			
				|  |  | -                    addressType = 0x01; // IPv4
 | 
	
		
			
				|  |  | -                    address = ip.GetAddressBytes();
 | 
	
		
			
				|  |  | -                    break;
 | 
	
		
			
				|  |  | -                case AddressFamily.InterNetworkV6:
 | 
	
		
			
				|  |  | -                    addressType = 0x04; // IPv6
 | 
	
		
			
				|  |  | -                    address = ip.GetAddressBytes();
 | 
	
		
			
				|  |  | -                    break;
 | 
	
		
			
				|  |  | -                default:
 | 
	
		
			
				|  |  | -                    throw new ProxyException(string.Format("SOCKS5: IP address '{0}' is not supported.", ip));
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            return address;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        private static void ConnectHttp(Socket socket, ConnectionInfo connectionInfo)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            var httpResponseRe = new Regex(@"HTTP/(?<version>\d[.]\d) (?<statusCode>\d{3}) (?<reasonPhrase>.+)$");
 | 
	
		
			
				|  |  | -            var httpHeaderRe = new Regex(@"(?<fieldName>[^\[\]()<>@,;:\""/?={} \t]+):(?<fieldValue>.+)?");
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            SocketAbstraction.Send(socket, SshData.Ascii.GetBytes(string.Format("CONNECT {0}:{1} HTTP/1.0\r\n", connectionInfo.Host, connectionInfo.Port)));
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            //  Sent proxy authorization is specified
 | 
	
		
			
				|  |  | -            if (!string.IsNullOrEmpty(connectionInfo.ProxyUsername))
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                var authorization = string.Format("Proxy-Authorization: Basic {0}\r\n",
 | 
	
		
			
				|  |  | -                                                  Convert.ToBase64String(SshData.Ascii.GetBytes(string.Format("{0}:{1}", connectionInfo.ProxyUsername, connectionInfo.ProxyPassword)))
 | 
	
		
			
				|  |  | -                                                  );
 | 
	
		
			
				|  |  | -                SocketAbstraction.Send(socket, SshData.Ascii.GetBytes(authorization));
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            SocketAbstraction.Send(socket, SshData.Ascii.GetBytes("\r\n"));
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            HttpStatusCode? statusCode = null;
 | 
	
		
			
				|  |  | -            var contentLength = 0;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            while (true)
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                var response = SocketReadLine(socket, connectionInfo.Timeout);
 | 
	
		
			
				|  |  | -                if (response == null)
 | 
	
		
			
				|  |  | -                    // server shut down socket
 | 
	
		
			
				|  |  | -                    break;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                if (statusCode == null)
 | 
	
		
			
				|  |  | -                {
 | 
	
		
			
				|  |  | -                    var statusMatch = httpResponseRe.Match(response);
 | 
	
		
			
				|  |  | -                    if (statusMatch.Success)
 | 
	
		
			
				|  |  | -                    {
 | 
	
		
			
				|  |  | -                        var httpStatusCode = statusMatch.Result("${statusCode}");
 | 
	
		
			
				|  |  | -                        statusCode = (HttpStatusCode) int.Parse(httpStatusCode);
 | 
	
		
			
				|  |  | -                        if (statusCode != HttpStatusCode.OK)
 | 
	
		
			
				|  |  | -                        {
 | 
	
		
			
				|  |  | -                            var reasonPhrase = statusMatch.Result("${reasonPhrase}");
 | 
	
		
			
				|  |  | -                            throw new ProxyException(string.Format("HTTP: Status code {0}, \"{1}\"", httpStatusCode,
 | 
	
		
			
				|  |  | -                                reasonPhrase));
 | 
	
		
			
				|  |  | -                        }
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                    continue;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                // continue on parsing message headers coming from the server
 | 
	
		
			
				|  |  | -                var headerMatch = httpHeaderRe.Match(response);
 | 
	
		
			
				|  |  | -                if (headerMatch.Success)
 | 
	
		
			
				|  |  | -                {
 | 
	
		
			
				|  |  | -                    var fieldName = headerMatch.Result("${fieldName}");
 | 
	
		
			
				|  |  | -                    if (fieldName.Equals("Content-Length", StringComparison.OrdinalIgnoreCase))
 | 
	
		
			
				|  |  | -                    {
 | 
	
		
			
				|  |  | -                        contentLength = int.Parse(headerMatch.Result("${fieldValue}"));
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                    continue;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                // check if we've reached the CRLF which separates request line and headers from the message body
 | 
	
		
			
				|  |  | -                if (response.Length == 0)
 | 
	
		
			
				|  |  | -                {
 | 
	
		
			
				|  |  | -                    //  read response body if specified
 | 
	
		
			
				|  |  | -                    if (contentLength > 0)
 | 
	
		
			
				|  |  | -                    {
 | 
	
		
			
				|  |  | -                        var contentBody = new byte[contentLength];
 | 
	
		
			
				|  |  | -                        SocketRead(socket, contentBody, 0, contentLength);
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                    break;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            if (statusCode == null)
 | 
	
		
			
				|  |  | -                throw new ProxyException("HTTP response does not contain status line.");
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// Raises the <see cref="ErrorOccured"/> event.
 | 
	
		
			
				|  |  |          /// </summary>
 |