示例#1
0
        private void EstablishSocks4Connection(Socket client)
        {
            var userNameBytes = Encoding.ASCII.GetBytes(_userName);
            var addressBytes  = _remoteEndpoint.Address.GetAddressBytes();
            var portBytes     = BitConverter.GetBytes((ushort)_remoteEndpoint.Port).Reverse().ToArray();

            _client.Connect(_endpoint);

            // send SOCKS version
            client.Send(new byte[] { 0x04 }, 0, 1, SocketFlags.None);
            // send command byte
            client.Send(new byte[] { 0x00 }, 0, 1, SocketFlags.None);
            // send port
            client.Send(portBytes, 0, portBytes.Length, SocketFlags.None);
            // send address
            client.Send(addressBytes, 0, addressBytes.Length, SocketFlags.None);
            // send user name
            client.Send(userNameBytes, 0, userNameBytes.Length, SocketFlags.None);
            // terminate user name with null
            client.Send(new byte[] { 0x00 }, 0, 1, SocketFlags.None);

            var buffer    = new byte[8];
            var bytesRead = SocketAbstraction.Read(client, buffer, 0, buffer.Length, TimeSpan.FromMilliseconds(500));

            Assert.AreEqual(buffer.Length, bytesRead);

            // wait until SOCKS client is bound to channel
            Assert.IsTrue(_channelBindStarted.WaitOne(TimeSpan.FromMilliseconds(200)));
        }
示例#2
0
        /// <summary>
        /// Reads a null terminated string from a socket.
        /// </summary>
        /// <param name="socket">The <see cref="Socket"/> to read from.</param>
        /// <param name="timeout">The timeout to apply to individual reads.</param>
        /// <returns>
        /// The <see cref="string"/> read, or <c>null</c> when the socket was closed.
        /// </returns>
        private static string ReadString(Socket socket, TimeSpan timeout)
        {
            var text   = new StringBuilder();
            var buffer = new byte[1];

            while (true)
            {
                if (SocketAbstraction.Read(socket, buffer, 0, 1, timeout) == 0)
                {
                    // SOCKS client closed connection
                    return(null);
                }

                var byteRead = buffer[0];
                if (byteRead == 0)
                {
                    // end of the string
                    break;
                }

                var c = (char)byteRead;
                text.Append(c);
            }
            return(text.ToString());
        }
示例#3
0
        private bool HandleSocks4(Socket socket, IChannelDirectTcpip channel, TimeSpan timeout)
        {
            var commandCode = SocketAbstraction.ReadByte(socket, timeout);

            if (commandCode == 0)
            {
                // SOCKS client closed connection
                return(false);
            }

            //  TODO:   See what need to be done depends on the code

            var portBuffer = new byte[2];

            if (SocketAbstraction.Read(socket, portBuffer, 0, portBuffer.Length, timeout) == 0)
            {
                // SOCKS client closed connection
                return(false);
            }

            var port = (uint)(portBuffer[0] * 256 + portBuffer[1]);

            var ipBuffer = new byte[4];

            if (SocketAbstraction.Read(socket, ipBuffer, 0, ipBuffer.Length, timeout) == 0)
            {
                // SOCKS client closed connection
                return(false);
            }

            var ipAddress = new IPAddress(ipBuffer);

            var username = ReadString(socket, timeout);

            if (username == null)
            {
                // SOCKS client closed connection
                return(false);
            }

            var host = ipAddress.ToString();

            RaiseRequestReceived(host, port);

            channel.Open(host, port, this, socket);

            SocketAbstraction.SendByte(socket, 0x00);

            if (channel.IsOpen)
            {
                SocketAbstraction.SendByte(socket, 0x5a);
                SocketAbstraction.Send(socket, portBuffer, 0, portBuffer.Length);
                SocketAbstraction.Send(socket, ipBuffer, 0, ipBuffer.Length);
                return(true);
            }

            // signal that request was rejected or failed
            SocketAbstraction.SendByte(socket, 0x5b);
            return(false);
        }
示例#4
0
        /// <summary>
        /// Performs a blocking read on the socket until a line is read.
        /// </summary>
        /// <param name="socket">The <see cref="Socket"/> to read from.</param>
        /// <param name="readTimeout">A <see cref="TimeSpan"/> that represents the time to wait until a line is read.</param>
        /// <exception cref="SshOperationTimeoutException">The read has timed-out.</exception>
        /// <exception cref="SocketException">An error occurred when trying to access the socket.</exception>
        /// <returns>
        /// The line read from the socket, or <c>null</c> when the remote server has shutdown and all data has been received.
        /// </returns>
        private static string SocketReadLine(Socket socket, TimeSpan readTimeout)
        {
            var encoding = SshData.Ascii;
            var buffer   = new List <byte>();
            var data     = new byte[1];

            // read data one byte at a time to find end of line and leave any unhandled information in the buffer
            // to be processed by subsequent invocations
            do
            {
                var bytesRead = SocketAbstraction.Read(socket, data, 0, data.Length, readTimeout);
                if (bytesRead == 0)
                {
                    // the remote server shut down the socket
                    break;
                }

                var b = data[0];
                buffer.Add(b);

                if (b == Session.LineFeed && buffer.Count > 1 && buffer[buffer.Count - 2] == Session.CarriageReturn)
                {
                    // Return line without CRLF
                    return(encoding.GetString(buffer.ToArray(), 0, buffer.Count - 2));
                }
            }while (true);

            if (buffer.Count == 0)
            {
                return(null);
            }

            return(encoding.GetString(buffer.ToArray(), 0, buffer.Count));
        }
示例#5
0
        /// <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>
        /// <param name="readTimeout">The maximum time to wait until <paramref name="length"/> bytes have been received.</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>
        protected static int SocketRead(Socket socket, byte[] buffer, int offset, int length, TimeSpan readTimeout)
        {
            var bytesRead = SocketAbstraction.Read(socket, buffer, offset, length, readTimeout);

            if (bytesRead == 0)
            {
                throw new SshConnectionException("An established connection was aborted by the server.",
                                                 DisconnectReason.ConnectionLost);
            }
            return(bytesRead);
        }
        private static string GetSocks5Host(int addressType, Socket socket, TimeSpan timeout)
        {
            switch (addressType)
            {
            case 0x01:     // IPv4
            {
                var addressBuffer = new byte[4];
                if (SocketAbstraction.Read(socket, addressBuffer, 0, 4, timeout) == 0)
                {
                    // SOCKS client closed connection
                    return(null);
                }

                var ipv4 = new IPAddress(addressBuffer);
                return(ipv4.ToString());
            }

            case 0x03:     // Domain name
            {
                var length = SocketAbstraction.ReadByte(socket, timeout);
                if (length == -1)
                {
                    // SOCKS client closed connection
                    return(null);
                }
                var addressBuffer = new byte[length];
                if (SocketAbstraction.Read(socket, addressBuffer, 0, addressBuffer.Length, timeout) == 0)
                {
                    // SOCKS client closed connection
                    return(null);
                }

                var hostName = SshData.Ascii.GetString(addressBuffer, 0, addressBuffer.Length);
                return(hostName);
            }

            case 0x04:     // IPv6
            {
                var addressBuffer = new byte[16];
                if (SocketAbstraction.Read(socket, addressBuffer, 0, 16, timeout) == 0)
                {
                    // SOCKS client closed connection
                    return(null);
                }

                var ipv6 = new IPAddress(addressBuffer);
                return(ipv6.ToString());
            }

            default:
                throw new ProxyException(string.Format("SOCKS5: Address type '{0}' is not supported.", addressType));
            }
        }
示例#7
0
        /// <summary>
        /// Performs a blocking read on the socket until a line is read.
        /// </summary>
        /// <param name="socket">The <see cref="Socket"/> to read from.</param>
        /// <param name="timeout">A <see cref="TimeSpan"/> that represents the time to wait until a line is read.</param>
        /// <param name="buffer">A <see cref="List{Byte}"/> to which read bytes will be added.</param>
        /// <exception cref="SshOperationTimeoutException">The read has timed-out.</exception>
        /// <exception cref="SocketException">An error occurred when trying to access the socket.</exception>
        /// <returns>
        /// The line read from the socket, or <c>null</c> when the remote server has shutdown and all data has been received.
        /// </returns>
        private static string SocketReadLine(Socket socket, TimeSpan timeout, List <byte> buffer)
        {
            var data = new byte[1];

            var startPosition = buffer.Count;

            // Read data one byte at a time to find end of line and leave any unhandled information in the buffer
            // to be processed by subsequent invocations.
            while (true)
            {
                var bytesRead = SocketAbstraction.Read(socket, data, 0, data.Length, timeout);
                if (bytesRead == 0)
                {
                    // The remote server shut down the socket.
                    break;
                }

                var byteRead = data[0];
                buffer.Add(byteRead);

                // The null character MUST NOT be sent
                if (byteRead == Null)
                {
                    throw new SshConnectionException(string.Format(CultureInfo.InvariantCulture,
                                                                   "The server response contains a null character at position 0x{0:X8}:{1}{1}{2}{1}{1}" +
                                                                   "A server must not send a null character before the Protocol Version Exchange is complete.{1}{1}" +
                                                                   "More information is available here:{1}" +
                                                                   "https://tools.ietf.org/html/rfc4253#section-4.2",
                                                                   buffer.Count,
                                                                   Environment.NewLine,
                                                                   PacketDump.Create(buffer.ToArray(), 2)));
                }

                if (byteRead == Session.LineFeed)
                {
                    if (buffer.Count > startPosition + 1 && buffer[buffer.Count - 2] == Session.CarriageReturn)
                    {
                        // Return current line without CRLF
                        return(Encoding.UTF8.GetString(buffer.ToArray(), startPosition, buffer.Count - (startPosition + 2)));
                    }
                    else
                    {
                        // Even though RFC4253 clearly indicates that the identification string should be terminated
                        // by a CR LF we also support banners and identification strings that are terminated by a LF

                        // Return current line without LF
                        return(Encoding.UTF8.GetString(buffer.ToArray(), startPosition, buffer.Count - (startPosition + 1)));
                    }
                }
            }

            return(null);
        }
示例#8
0
        private bool HandleSocks5(Socket socket, IChannelDirectTcpip channel, TimeSpan timeout)
        {
#if DEBUG_GERT
            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " |  Handling Socks5: " + socket.LocalEndPoint + " | " + socket.RemoteEndPoint + " | " + DateTime.Now.ToString("hh:mm:ss.fff"));
#endif // DEBUG_GERT

            var authenticationMethodsCount = SocketAbstraction.ReadByte(socket, timeout);
            if (authenticationMethodsCount == -1)
            {
                // SOCKS client closed connection
                return(false);
            }

#if DEBUG_GERT
            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " |  After ReadByte for authenticationMethodsCount | " + DateTime.Now.ToString("hh:mm:ss.fff"));
#endif // DEBUG_GERT

            var authenticationMethods = new byte[authenticationMethodsCount];
            if (SocketAbstraction.Read(socket, authenticationMethods, 0, authenticationMethods.Length, timeout) == 0)
            {
                // SOCKS client closed connection
                return(false);
            }

#if DEBUG_GERT
            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " |  After Read for authenticationMethods | " + DateTime.Now.ToString("hh:mm:ss.fff"));
#endif // DEBUG_GERT

            if (authenticationMethods.Min() == 0)
            {
                // no user authentication is one of the authentication methods supported
                // by the SOCKS client
                SocketAbstraction.Send(socket, new byte[] { 0x05, 0x00 }, 0, 2);

#if DEBUG_GERT
                Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " |  After Send for authenticationMethods 0 | " + DateTime.Now.ToString("hh:mm:ss.fff"));
#endif // DEBUG_GERT
            }
            else
            {
                // the SOCKS client requires authentication, which we currently do not support
                SocketAbstraction.Send(socket, new byte[] { 0x05, 0xFF }, 0, 2);

                // we continue business as usual but expect the client to close the connection
                // so one of the subsequent reads should return -1 signaling that the client
                // has effectively closed the connection
#if DEBUG_GERT
                Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " |  After Send for authenticationMethods 2 | " + DateTime.Now.ToString("hh:mm:ss.fff"));
#endif // DEBUG_GERT
            }

            var version = SocketAbstraction.ReadByte(socket, timeout);
            if (version == -1)
            {
                // SOCKS client closed connection
                return(false);
            }

            if (version != 5)
            {
                throw new ProxyException("SOCKS5: Version 5 is expected.");
            }

            var commandCode = SocketAbstraction.ReadByte(socket, timeout);
            if (commandCode == -1)
            {
                // SOCKS client closed connection
                return(false);
            }

            var reserved = SocketAbstraction.ReadByte(socket, timeout);
            if (reserved == -1)
            {
                // SOCKS client closed connection
                return(false);
            }

            if (reserved != 0)
            {
                throw new ProxyException("SOCKS5: 0 is expected for reserved byte.");
            }

            var addressType = SocketAbstraction.ReadByte(socket, timeout);
            if (addressType == -1)
            {
                // SOCKS client closed connection
                return(false);
            }

            IPAddress ipAddress;
            byte[]    addressBuffer;
            switch (addressType)
            {
            case 0x01:
            {
                addressBuffer = new byte[4];
                if (SocketAbstraction.Read(socket, addressBuffer, 0, 4, timeout) == 0)
                {
                    // SOCKS client closed connection
                    return(false);
                }

                ipAddress = new IPAddress(addressBuffer);
            }
            break;

            case 0x03:
            {
                var length = SocketAbstraction.ReadByte(socket, timeout);
                addressBuffer = new byte[length];
                if (SocketAbstraction.Read(socket, addressBuffer, 0, addressBuffer.Length, timeout) == 0)
                {
                    // SOCKS client closed connection
                    return(false);
                }

                ipAddress = IPAddress.Parse(SshData.Ascii.GetString(addressBuffer));

                //var hostName = new Common.ASCIIEncoding().GetString(addressBuffer);

                //ipAddress = Dns.GetHostEntry(hostName).AddressList[0];
            }
            break;

            case 0x04:
            {
                addressBuffer = new byte[16];
                if (SocketAbstraction.Read(socket, addressBuffer, 0, 16, timeout) == 0)
                {
                    // SOCKS client closed connection
                    return(false);
                }

                ipAddress = new IPAddress(addressBuffer);
            }
            break;

            default:
                throw new ProxyException(string.Format("SOCKS5: Address type '{0}' is not supported.", addressType));
            }

            var portBuffer = new byte[2];
            if (SocketAbstraction.Read(socket, portBuffer, 0, portBuffer.Length, timeout) == 0)
            {
                // SOCKS client closed connection
                return(false);
            }

            var port = (uint)(portBuffer[0] * 256 + portBuffer[1]);
            var host = ipAddress.ToString();

            RaiseRequestReceived(host, port);

#if DEBUG_GERT
            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " |  Before channel open | " + DateTime.Now.ToString("hh:mm:ss.fff"));

            var stopWatch = new Stopwatch();
            stopWatch.Start();
#endif // DEBUG_GERT

            channel.Open(host, port, this, socket);

#if DEBUG_GERT
            stopWatch.Stop();

            Console.WriteLine("ID: " + Thread.CurrentThread.ManagedThreadId + " |  After channel open | " + DateTime.Now.ToString("hh:mm:ss.fff") + " => " + stopWatch.ElapsedMilliseconds);
#endif // DEBUG_GERT

            SocketAbstraction.SendByte(socket, 0x05);


            if (channel.IsOpen)
            {
                SocketAbstraction.SendByte(socket, 0x00);
            }
            else
            {
                SocketAbstraction.SendByte(socket, 0x01);
            }

            // reserved
            SocketAbstraction.SendByte(socket, 0x00);

            if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
            {
                SocketAbstraction.SendByte(socket, 0x01);
            }
            else if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
            {
                SocketAbstraction.SendByte(socket, 0x04);
            }
            else
            {
                throw new NotSupportedException("Not supported address family.");
            }

            var addressBytes = ipAddress.GetAddressBytes();
            SocketAbstraction.Send(socket, addressBytes, 0, addressBytes.Length);
            SocketAbstraction.Send(socket, portBuffer, 0, portBuffer.Length);

            return(true);
        }
示例#9
0
        private bool HandleSocks5(Socket socket, IChannelDirectTcpip channel, TimeSpan timeout)
        {
            var authenticationMethodsCount = SocketAbstraction.ReadByte(socket, timeout);

            if (authenticationMethodsCount == -1)
            {
                // SOCKS client closed connection
                return(false);
            }

            var authenticationMethods = new byte[authenticationMethodsCount];

            if (SocketAbstraction.Read(socket, authenticationMethods, 0, authenticationMethods.Length, timeout) == 0)
            {
                // SOCKS client closed connection
                return(false);
            }

            if (authenticationMethods.Min() == 0)
            {
                // no user authentication is one of the authentication methods supported
                // by the SOCKS client
                SocketAbstraction.Send(socket, new byte[] { 0x05, 0x00 }, 0, 2);
            }
            else
            {
                // the SOCKS client requires authentication, which we currently do not support
                SocketAbstraction.Send(socket, new byte[] { 0x05, 0xFF }, 0, 2);

                // we continue business as usual but expect the client to close the connection
                // so one of the subsequent reads should return -1 signaling that the client
                // has effectively closed the connection
            }

            var version = SocketAbstraction.ReadByte(socket, timeout);

            if (version == -1)
            {
                // SOCKS client closed connection
                return(false);
            }

            if (version != 5)
            {
                throw new ProxyException("SOCKS5: Version 5 is expected.");
            }

            var commandCode = SocketAbstraction.ReadByte(socket, timeout);

            if (commandCode == -1)
            {
                // SOCKS client closed connection
                return(false);
            }

            var reserved = SocketAbstraction.ReadByte(socket, timeout);

            if (reserved == -1)
            {
                // SOCKS client closed connection
                return(false);
            }

            if (reserved != 0)
            {
                throw new ProxyException("SOCKS5: 0 is expected for reserved byte.");
            }

            var addressType = SocketAbstraction.ReadByte(socket, timeout);

            if (addressType == -1)
            {
                // SOCKS client closed connection
                return(false);
            }

            IPAddress ipAddress;

            byte[] addressBuffer;
            switch (addressType)
            {
            case 0x01:
            {
                addressBuffer = new byte[4];
                if (SocketAbstraction.Read(socket, addressBuffer, 0, 4, timeout) == 0)
                {
                    // SOCKS client closed connection
                    return(false);
                }

                ipAddress = new IPAddress(addressBuffer);
            }
            break;

            case 0x03:
            {
                var length = SocketAbstraction.ReadByte(socket, timeout);
                if (length == -1)
                {
                    // SOCKS client closed connection
                    return(false);
                }
                addressBuffer = new byte[length];
                if (SocketAbstraction.Read(socket, addressBuffer, 0, addressBuffer.Length, timeout) == 0)
                {
                    // SOCKS client closed connection
                    return(false);
                }

                ipAddress = IPAddress.Parse(SshData.Ascii.GetString(addressBuffer, 0, addressBuffer.Length));

                //var hostName = new Common.ASCIIEncoding().GetString(addressBuffer);

                //ipAddress = Dns.GetHostEntry(hostName).AddressList[0];
            }
            break;

            case 0x04:
            {
                addressBuffer = new byte[16];
                if (SocketAbstraction.Read(socket, addressBuffer, 0, 16, timeout) == 0)
                {
                    // SOCKS client closed connection
                    return(false);
                }

                ipAddress = new IPAddress(addressBuffer);
            }
            break;

            default:
                throw new ProxyException(string.Format("SOCKS5: Address type '{0}' is not supported.", addressType));
            }

            var portBuffer = new byte[2];

            if (SocketAbstraction.Read(socket, portBuffer, 0, portBuffer.Length, timeout) == 0)
            {
                // SOCKS client closed connection
                return(false);
            }

            var port = (uint)(portBuffer[0] * 256 + portBuffer[1]);
            var host = ipAddress.ToString();

            RaiseRequestReceived(host, port);

            channel.Open(host, port, this, socket);

            SocketAbstraction.SendByte(socket, 0x05);

            if (channel.IsOpen)
            {
                SocketAbstraction.SendByte(socket, 0x00);
            }
            else
            {
                SocketAbstraction.SendByte(socket, 0x01);
            }

            // reserved
            SocketAbstraction.SendByte(socket, 0x00);

            if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
            {
                SocketAbstraction.SendByte(socket, 0x01);
            }
            else if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
            {
                SocketAbstraction.SendByte(socket, 0x04);
            }
            else
            {
                throw new NotSupportedException("Not supported address family.");
            }

            var addressBytes = ipAddress.GetAddressBytes();

            SocketAbstraction.Send(socket, addressBytes, 0, addressBytes.Length);
            SocketAbstraction.Send(socket, portBuffer, 0, portBuffer.Length);

            return(true);
        }
        private bool HandleSocks5(Socket socket, IChannelDirectTcpip channel, TimeSpan timeout)
        {
            var authenticationMethodsCount = SocketAbstraction.ReadByte(socket, timeout);

            if (authenticationMethodsCount == -1)
            {
                // SOCKS client closed connection
                return(false);
            }

            var authenticationMethods = new byte[authenticationMethodsCount];

            if (SocketAbstraction.Read(socket, authenticationMethods, 0, authenticationMethods.Length, timeout) == 0)
            {
                // SOCKS client closed connection
                return(false);
            }

            if (authenticationMethods.Min() == 0)
            {
                // no user authentication is one of the authentication methods supported
                // by the SOCKS client
                SocketAbstraction.Send(socket, new byte[] { 0x05, 0x00 }, 0, 2);
            }
            else
            {
                // the SOCKS client requires authentication, which we currently do not support
                SocketAbstraction.Send(socket, new byte[] { 0x05, 0xFF }, 0, 2);

                // we continue business as usual but expect the client to close the connection
                // so one of the subsequent reads should return -1 signaling that the client
                // has effectively closed the connection
            }

            var version = SocketAbstraction.ReadByte(socket, timeout);

            if (version == -1)
            {
                // SOCKS client closed connection
                return(false);
            }

            if (version != 5)
            {
                throw new ProxyException("SOCKS5: Version 5 is expected.");
            }

            var commandCode = SocketAbstraction.ReadByte(socket, timeout);

            if (commandCode == -1)
            {
                // SOCKS client closed connection
                return(false);
            }

            var reserved = SocketAbstraction.ReadByte(socket, timeout);

            if (reserved == -1)
            {
                // SOCKS client closed connection
                return(false);
            }

            if (reserved != 0)
            {
                throw new ProxyException("SOCKS5: 0 is expected for reserved byte.");
            }

            var addressType = SocketAbstraction.ReadByte(socket, timeout);

            if (addressType == -1)
            {
                // SOCKS client closed connection
                return(false);
            }

            var host = GetSocks5Host(addressType, socket, timeout);

            if (host == null)
            {
                // SOCKS client closed connection
                return(false);
            }

            var portBuffer = new byte[2];

            if (SocketAbstraction.Read(socket, portBuffer, 0, portBuffer.Length, timeout) == 0)
            {
                // SOCKS client closed connection
                return(false);
            }

            var port = Pack.BigEndianToUInt16(portBuffer);

            RaiseRequestReceived(host, port);

            channel.Open(host, port, this, socket);

            var socksReply = CreateSocks5Reply(channel.IsOpen);

            SocketAbstraction.Send(socket, socksReply, 0, socksReply.Length);

            return(true);
        }
示例#11
0
        /// <summary>
        /// Establishes a connection to the server via a SOCKS5 proxy.
        /// </summary>
        /// <param name="connectionInfo">The connection information.</param>
        /// <param name="socket">The <see cref="Socket"/>.</param>
        private void HandleProxyConnect(IConnectionInfo connectionInfo, Socket socket)
        {
            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);
        }