Example #1
0
        protected override async Task <Socket> ClientCommandReceived(NetworkStream client, byte[] buffer, int length, CancellationToken cancellationToken)
        {
            Socket        server = null;
            Socks4Command cmd;

            byte[]    response;
            IPAddress ip;
            string    user;
            int       port;

            if (TryParse(buffer, length, out cmd, out port, out ip, out user))
            {
                var host = ip.ToString();

                try {
                    server = await SocketUtils.ConnectAsync(host, port, null, true, cancellationToken).ConfigureAwait(false);

                    var remote = (IPEndPoint)server.RemoteEndPoint;

                    response = GetResponse(Socks4Reply.RequestGranted, remote);
                } catch {
                    response = GetResponse(Socks4Reply.RequestRejected, null);
                }
            }
            else
            {
                response = GetResponse(Socks4Reply.RequestRejected, null);
            }

            await client.WriteAsync(response, 0, response.Length, cancellationToken).ConfigureAwait(false);

            return(server);
        }
Example #2
0
        async Task <Socket> ConnectAsync(string host, int port, bool doAsync, CancellationToken cancellationToken)
        {
            ValidateArguments(host, port);

            cancellationToken.ThrowIfCancellationRequested();

            var socket = await SocketUtils.ConnectAsync(ProxyHost, ProxyPort, LocalEndPoint, doAsync, cancellationToken).ConfigureAwait(false);

            var builder = new StringBuilder();

            builder.AppendFormat("CONNECT {0}:{1} HTTP/1.1\r\n", host, port);
            builder.AppendFormat("Host: {0}:{1}\r\n", host, port);
            if (ProxyCredentials != null)
            {
                var token  = Encoding.UTF8.GetBytes(string.Format("{0}:{1}", ProxyCredentials.UserName, ProxyCredentials.Password));
                var base64 = Convert.ToBase64String(token);
                builder.AppendFormat("Proxy-Authorization: Basic {0}\r\n", base64);
            }
            builder.Append("\r\n");

            var command = Encoding.UTF8.GetBytes(builder.ToString());

            try {
                await SendAsync(socket, command, 0, command.Length, doAsync, cancellationToken).ConfigureAwait(false);

                var buffer       = new byte[1024];
                var endOfHeaders = false;
                var newline      = false;

                builder.Clear();

                // read until we consume the end of the headers (it's ok if we read some of the content)
                do
                {
                    int nread = await ReceiveAsync(socket, buffer, 0, buffer.Length, doAsync, cancellationToken).ConfigureAwait(false);

                    if (nread > 0)
                    {
                        int n = nread;

                        for (int i = 0; i < nread && !endOfHeaders; i++)
                        {
                            switch ((char)buffer[i])
                            {
                            case '\r':
                                break;

                            case '\n':
                                endOfHeaders = newline;
                                newline      = true;

                                if (endOfHeaders)
                                {
                                    n = i + 1;
                                }
                                break;

                            default:
                                newline = false;
                                break;
                            }
                        }

                        builder.Append(Encoding.UTF8.GetString(buffer, 0, n));
                    }
                } while (!endOfHeaders);

                int index = 0;

                while (builder[index] != '\n')
                {
                    index++;
                }

                if (index > 0 && builder[index - 1] == '\r')
                {
                    index--;
                }

                // trim everything beyond the "HTTP/1.1 200 ..." part of the response
                builder.Length = index;

                var response = builder.ToString();

                if (response.Length >= 16 && response.StartsWith("HTTP/1.", StringComparison.OrdinalIgnoreCase) &&
                    (response[7] == '1' || response[7] == '0') && response[8] == ' ' &&
                    response[9] == '2' && response[10] == '0' && response[11] == '0' &&
                    response[12] == ' ')
                {
                    return(socket);
                }

                throw new ProxyProtocolException(string.Format("Failed to connect to {0}:{1}: {2}", host, port, response));
            } catch {
#if NETSTANDARD_2_0 || NET_4_5 || __MOBILE__
                if (socket.Connected)
                {
                    socket.Disconnect(false);
                }
#endif
                socket.Dispose();
                throw;
            }
        }
Example #3
0
        async Task <Socket> ConnectAsync(string host, int port, bool doAsync, CancellationToken cancellationToken)
        {
            Socks5AddressType addrType;
            IPAddress         ip;

            ValidateArguments(host, port);

            addrType = GetAddressType(host, out ip);

            cancellationToken.ThrowIfCancellationRequested();

            var socket = await SocketUtils.ConnectAsync(ProxyHost, ProxyPort, LocalEndPoint, doAsync, cancellationToken).ConfigureAwait(false);

            byte[]    domain     = null, addr = null;
            const int bufferSize = 6 + 257;

            if (addrType == Socks5AddressType.Domain)
            {
                domain = Encoding.UTF8.GetBytes(host);
            }

            try {
                Socks5AuthMethod method;

                if (ProxyCredentials != null)
                {
                    method = await NegotiateAuthMethodAsync(socket, doAsync, cancellationToken, Socks5AuthMethod.UserPassword, Socks5AuthMethod.Anonymous).ConfigureAwait(false);
                }
                else
                {
                    method = await NegotiateAuthMethodAsync(socket, doAsync, cancellationToken, Socks5AuthMethod.Anonymous).ConfigureAwait(false);
                }

                switch (method)
                {
                case Socks5AuthMethod.UserPassword:
                    await AuthenticateAsync(socket, doAsync, cancellationToken).ConfigureAwait(false);

                    break;

                case Socks5AuthMethod.Anonymous:
                    break;

                default:
                    throw new ProxyProtocolException("Failed to negotiate authentication method with the proxy server.");
                }

                // +----+-----+-------+------+----------+----------+
                // |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
                // +----+-----+-------+------+----------+----------+
                // | 1  |  1  | X'00' |  1   | Variable |    2     |
                // +----+-----+-------+------+----------+----------+
                var buffer = new byte[bufferSize];
                int nread, n = 0;

                buffer[n++] = (byte)SocksVersion;
                buffer[n++] = (byte)Socks5Command.Connect;
                buffer[n++] = 0x00;
                buffer[n++] = (byte)addrType;
                switch (addrType)
                {
                case Socks5AddressType.Domain:
                    buffer[n++] = (byte)domain.Length;
                    Buffer.BlockCopy(domain, 0, buffer, n, domain.Length);
                    n += domain.Length;
                    break;

                case Socks5AddressType.IPv6:
                    addr = ip.GetAddressBytes();
                    Buffer.BlockCopy(addr, 0, buffer, n, addr.Length);
                    n += 16;
                    break;

                case Socks5AddressType.IPv4:
                    addr = ip.GetAddressBytes();
                    Buffer.BlockCopy(addr, 0, buffer, n, addr.Length);
                    n += 4;
                    break;
                }
                buffer[n++] = (byte)(port >> 8);
                buffer[n++] = (byte)port;

                await SendAsync(socket, buffer, 0, n, doAsync, cancellationToken).ConfigureAwait(false);

                // +-----+-----+-------+------+----------+----------+
                // | VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
                // +-----+-----+-------+------+----------+----------+
                // |  1  |  1  | X'00' |  1   | Variable |    2     |
                // +-----+-----+-------+------+----------+----------+

                // Note: We know we'll need at least 4 bytes of header + a minimum of 1 byte
                // to determine the length of the BND.ADDR field if ATYP is a domain.
                int need = 5;
                n = 0;

                do
                {
                    if ((nread = await ReceiveAsync(socket, buffer, 0 + n, need - n, doAsync, cancellationToken).ConfigureAwait(false)) > 0)
                    {
                        n += nread;
                    }
                } while (n < need);

                VerifySocksVersion(buffer[0]);

                if (buffer[1] != (byte)Socks5Reply.Success)
                {
                    throw new ProxyProtocolException(string.Format("Failed to connect to {0}:{1}: {2}", host, port, GetFailureReason(buffer[1])));
                }

                addrType = (Socks5AddressType)buffer[3];

                switch (addrType)
                {
                case Socks5AddressType.Domain: need += buffer[4] + 2; break;

                case Socks5AddressType.IPv6: need += (16 - 1) + 2; break;

                case Socks5AddressType.IPv4: need += (4 - 1) + 2; break;

                default: throw new ProxyProtocolException("Proxy server returned unknown address type.");
                }

                do
                {
                    if ((nread = await ReceiveAsync(socket, buffer, 0 + n, need - n, doAsync, cancellationToken).ConfigureAwait(false)) > 0)
                    {
                        n += nread;
                    }
                } while (n < need);

                // TODO: do we care about BND.ADDR and BND.PORT?

                return(socket);
            } catch {
#if !NETSTANDARD1_3 && !NETSTANDARD1_6
                if (socket.Connected)
                {
                    socket.Disconnect(false);
                }
#endif
                socket.Dispose();
                throw;
            }
        }
Example #4
0
        async Task <Stream> ConnectAsync(string host, int port, bool doAsync, CancellationToken cancellationToken)
        {
            ValidateArguments(host, port);

            cancellationToken.ThrowIfCancellationRequested();

            var socket = await SocketUtils.ConnectAsync(ProxyHost, ProxyPort, LocalEndPoint, doAsync, cancellationToken).ConfigureAwait(false);

            var ssl = new SslStream(new NetworkStream(socket, true), false, ValidateRemoteCertificate);

            try {
                if (doAsync)
                {
#if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
                    await ssl.AuthenticateAsClientAsync(GetSslClientAuthenticationOptions (host, ValidateRemoteCertificate), cancellationToken).ConfigureAwait(false);
#else
                    await ssl.AuthenticateAsClientAsync(host, ClientCertificates, SslProtocols, CheckCertificateRevocation).ConfigureAwait(false);
#endif
                }
                else
                {
#if NETSTANDARD1_3 || NETSTANDARD1_6
                    ssl.AuthenticateAsClientAsync(host, ClientCertificates, SslProtocols, CheckCertificateRevocation).GetAwaiter().GetResult();
#elif NET5_0_OR_GREATER
                    ssl.AuthenticateAsClient(GetSslClientAuthenticationOptions(host, ValidateRemoteCertificate));
#else
                    ssl.AuthenticateAsClient(host, ClientCertificates, SslProtocols, CheckCertificateRevocation);
#endif
                }
            } catch (Exception ex) {
                ssl.Dispose();

                throw SslHandshakeException.Create(ref sslValidationInfo, ex, false, "HTTP", host, port, 443, 80);
            }

            var command = HttpProxyClient.GetConnectCommand(host, port, ProxyCredentials);

            try {
                if (doAsync)
                {
                    await ssl.WriteAsync(command, 0, command.Length, cancellationToken).ConfigureAwait(false);
                }
                else
                {
                    ssl.Write(command, 0, command.Length);
                }

                var buffer = ArrayPool <byte> .Shared.Rent(BufferSize);

                var builder = new StringBuilder();

                try {
                    var endOfHeaders = false;
                    var newline      = false;

                    // read until we consume the end of the headers (it's ok if we read some of the content)
                    do
                    {
                        int nread;

                        if (doAsync)
                        {
                            nread = await ssl.ReadAsync(buffer, 0, BufferSize, cancellationToken).ConfigureAwait(false);
                        }
                        else
                        {
                            nread = ssl.Read(buffer, 0, BufferSize);
                        }

                        if (nread > 0)
                        {
                            int n = nread;

                            for (int i = 0; i < nread && !endOfHeaders; i++)
                            {
                                switch ((char)buffer[i])
                                {
                                case '\r':
                                    break;

                                case '\n':
                                    endOfHeaders = newline;
                                    newline      = true;

                                    if (endOfHeaders)
                                    {
                                        n = i + 1;
                                    }
                                    break;

                                default:
                                    newline = false;
                                    break;
                                }
                            }

                            builder.Append(Encoding.UTF8.GetString(buffer, 0, n));
                        }
                    } while (!endOfHeaders);
                } finally {
                    ArrayPool <byte> .Shared.Return(buffer);
                }

                int index = 0;

                while (builder[index] != '\n')
                {
                    index++;
                }

                if (index > 0 && builder[index - 1] == '\r')
                {
                    index--;
                }

                // trim everything beyond the "HTTP/1.1 200 ..." part of the response
                builder.Length = index;

                var response = builder.ToString();

                if (response.Length >= 15 && response.StartsWith("HTTP/1.", StringComparison.OrdinalIgnoreCase) &&
                    (response[7] == '1' || response[7] == '0') && response[8] == ' ' &&
                    response[9] == '2' && response[10] == '0' && response[11] == '0' &&
                    response[12] == ' ')
                {
                    return(ssl);
                }

                throw new ProxyProtocolException(string.Format(CultureInfo.InvariantCulture, "Failed to connect to {0}:{1}: {2}", host, port, response));
            } catch {
                ssl.Dispose();
                throw;
            }
        }
Example #5
0
        async Task <Socket> ConnectAsync(string host, int port, bool doAsync, CancellationToken cancellationToken)
        {
            byte[]    addr, domain = null;
            IPAddress ip;

            ValidateArguments(host, port);

            if (!IPAddress.TryParse(host, out ip))
            {
                if (IsSocks4a)
                {
                    domain = Encoding.UTF8.GetBytes(host);
                    addr   = InvalidIPAddress;
                }
                else
                {
                    ip = await ResolveAsync(host, doAsync, cancellationToken).ConfigureAwait(false);

                    addr = ip.GetAddressBytes();
                }
            }
            else
            {
                if (ip.AddressFamily != AddressFamily.InterNetwork)
                {
                    throw new ArgumentException("The specified host address must be IPv4.", nameof(host));
                }

                addr = ip.GetAddressBytes();
            }

            cancellationToken.ThrowIfCancellationRequested();

            var socket = await SocketUtils.ConnectAsync(ProxyHost, ProxyPort, LocalEndPoint, doAsync, cancellationToken).ConfigureAwait(false);

            var user = ProxyCredentials != null?Encoding.UTF8.GetBytes(ProxyCredentials.UserName) : new byte[0];

            try {
                // +----+-----+----------+----------+----------+-------+--------------+-------+
                // |VER | CMD | DST.PORT | DST.ADDR |  USERID  | NULL  |  DST.DOMAIN  | NULL  |
                // +----+-----+----------+----------+----------+-------+--------------+-------+
                // | 1  |  1  |    2     |    4     | VARIABLE | X'00' |   VARIABLE   | X'00' |
                // +----+-----+----------+----------+----------+-------+--------------+-------+
                int bufferSize = 9 + user.Length + (domain != null ? domain.Length + 1 : 0);
                var buffer = new byte[bufferSize];
                int nread, n = 0;

                buffer[n++] = (byte)SocksVersion;
                buffer[n++] = (byte)Socks4Command.Connect;
                buffer[n++] = (byte)(port >> 8);
                buffer[n++] = (byte)port;
                Buffer.BlockCopy(addr, 0, buffer, n, 4);
                n += 4;
                Buffer.BlockCopy(user, 0, buffer, n, user.Length);
                n          += user.Length;
                buffer[n++] = 0x00;
                if (domain != null)
                {
                    Buffer.BlockCopy(domain, 0, buffer, n, domain.Length);
                    n          += domain.Length;
                    buffer[n++] = 0x00;
                }

                await SendAsync(socket, buffer, 0, n, doAsync, cancellationToken).ConfigureAwait(false);

                // +-----+-----+----------+----------+
                // | VER | REP | BND.PORT | BND.ADDR |
                // +-----+-----+----------+----------+
                // |  1  |  1  |    2     |    4     |
                // +-----+-----+----------+----------+
                n = 0;

                do
                {
                    if ((nread = await ReceiveAsync(socket, buffer, 0 + n, 8 - n, doAsync, cancellationToken).ConfigureAwait(false)) > 0)
                    {
                        n += nread;
                    }
                } while (n < 8);

                if (buffer[1] != (byte)Socks4Reply.RequestGranted)
                {
                    throw new ProxyProtocolException(string.Format("Failed to connect to {0}:{1}: {2}", host, port, GetFailureReason(buffer[1])));
                }

                // TODO: do we care about BND.ADDR and BND.PORT?

                return(socket);
            } catch {
#if !NETSTANDARD1_3 && !NETSTANDARD1_6
                if (socket.Connected)
                {
                    socket.Disconnect(false);
                }
#endif
                socket.Dispose();
                throw;
            }
        }
Example #6
0
        protected override async Task <Socket> ClientCommandReceived(NetworkStream client, byte[] buffer, int length, CancellationToken cancellationToken)
        {
            byte[] response = null;
            Socket server   = null;

            Buffer.BlockCopy(buffer, 0, request, requestLength, length);
            requestLength += length;

            switch (state)
            {
            case Socks5ListenerState.NegotiateAuthMethod:
                response = NegotiateAuthMethod();
                break;

            case Socks5ListenerState.Authenticate:
                response = Authenticate();
                break;

            case Socks5ListenerState.Command:
                var result = Parse(request, requestLength, out Socks5Command cmd, out string host, out int port);
                switch (result)
                {
                case Socks5ParseResult.Success:
                    try {
                        server = await SocketUtils.ConnectAsync(host, port, null, true, cancellationToken).ConfigureAwait(false);

                        var remote = (IPEndPoint)server.RemoteEndPoint;

                        response = GetCommandResponse(Socks5Reply.Success, remote);
                    } catch (OperationCanceledException) {
                        throw;
                    } catch (SocketException ex) {
                        response = GetCommandResponse(ex.SocketErrorCode);
                    } catch {
                        response = GetCommandResponse(Socks5Reply.GeneralServerFailure, null);
                    }
                    break;

                case Socks5ParseResult.InvalidAddrType:
                    response = GetCommandResponse(Socks5Reply.AddressTypeNotSupported, null);
                    break;

                case Socks5ParseResult.InvalidCommand:
                    response = GetCommandResponse(Socks5Reply.CommandNotSupported, null);
                    break;

                case Socks5ParseResult.NotEnoughData:
                    response = null;
                    break;

                case Socks5ParseResult.InvalidRequest:
                default:
                    response = GetCommandResponse(Socks5Reply.GeneralServerFailure, null);
                    break;
                }
                break;
            }

            if (response == null)
            {
                return(null);
            }

            await client.WriteAsync(response, 0, response.Length, cancellationToken).ConfigureAwait(false);

            requestLength = 0;

            return(server);
        }
Example #7
0
        async Task <Stream> ConnectAsync(string host, int port, bool doAsync, CancellationToken cancellationToken)
        {
            ValidateArguments(host, port);

            cancellationToken.ThrowIfCancellationRequested();

            var socket = await SocketUtils.ConnectAsync(ProxyHost, ProxyPort, LocalEndPoint, doAsync, cancellationToken).ConfigureAwait(false);

            var command = GetConnectCommand(host, port, ProxyCredentials);

            try {
                await SendAsync(socket, command, 0, command.Length, doAsync, cancellationToken).ConfigureAwait(false);

                var buffer = ArrayPool <byte> .Shared.Rent(BufferSize);

                var builder = new StringBuilder();

                try {
                    var endOfHeaders = false;
                    var newline      = false;

                    // read until we consume the end of the headers (it's ok if we read some of the content)
                    do
                    {
                        int nread = await ReceiveAsync(socket, buffer, 0, BufferSize, doAsync, cancellationToken).ConfigureAwait(false);

                        if (nread > 0)
                        {
                            int n = nread;

                            for (int i = 0; i < nread && !endOfHeaders; i++)
                            {
                                switch ((char)buffer[i])
                                {
                                case '\r':
                                    break;

                                case '\n':
                                    endOfHeaders = newline;
                                    newline      = true;

                                    if (endOfHeaders)
                                    {
                                        n = i + 1;
                                    }
                                    break;

                                default:
                                    newline = false;
                                    break;
                                }
                            }

                            builder.Append(Encoding.UTF8.GetString(buffer, 0, n));
                        }
                    } while (!endOfHeaders);
                } finally {
                    ArrayPool <byte> .Shared.Return(buffer);
                }

                int index = 0;

                while (builder[index] != '\n')
                {
                    index++;
                }

                if (index > 0 && builder[index - 1] == '\r')
                {
                    index--;
                }

                // trim everything beyond the "HTTP/1.1 200 ..." part of the response
                builder.Length = index;

                var response = builder.ToString();

                if (response.Length >= 15 && response.StartsWith("HTTP/1.", StringComparison.OrdinalIgnoreCase) &&
                    (response[7] == '1' || response[7] == '0') && response[8] == ' ' &&
                    response[9] == '2' && response[10] == '0' && response[11] == '0' &&
                    response[12] == ' ')
                {
                    return(new NetworkStream(socket, true));
                }

                throw new ProxyProtocolException(string.Format(CultureInfo.InvariantCulture, "Failed to connect to {0}:{1}: {2}", host, port, response));
            } catch {
#if !NETSTANDARD1_3 && !NETSTANDARD1_6
                if (socket.Connected)
                {
                    socket.Disconnect(false);
                }
#endif
                socket.Dispose();
                throw;
            }
        }
Example #8
0
        protected override async Task <Socket> ClientCommandReceived(Stream client, byte[] buffer, int length, CancellationToken cancellationToken)
        {
            byte[] response = null;
            Socket server   = null;

            using (var stream = new MemoryStream(buffer, 0, length, false)) {
                using (var reader = new StreamReader(stream)) {
                    string line;

                    while ((line = reader.ReadLine()) != null)
                    {
                        if (string.IsNullOrEmpty(line))
                        {
                            break;
                        }

                        if (line.StartsWith("CONNECT ", StringComparison.OrdinalIgnoreCase))
                        {
                            int startIndex = "CONNECT ".Length;
                            int index      = startIndex;

                            while (index < line.Length && line[index] != ':')
                            {
                                index++;
                            }

                            var host = line.Substring(startIndex, index - startIndex);
                            startIndex = ++index;

                            while (index < line.Length && line[index] != ' ')
                            {
                                index++;
                            }

                            var portStr = line.Substring(startIndex, index - startIndex);
                            int port    = int.Parse(portStr, CultureInfo.InvariantCulture);
                            index++;

                            // HTTP/X.Y
                            var httpVersion = line.Substring(index);

                            try {
                                server = await SocketUtils.ConnectAsync(host, port, null, true, cancellationToken).ConfigureAwait(false);

                                var remote = (IPEndPoint)server.RemoteEndPoint;

                                response = Encoding.ASCII.GetBytes($"HTTP/1.1 200 Connected to {remote}\r\n\r\n");
                            } catch {
                                response = Encoding.ASCII.GetBytes("HTTP/1.1 404 Not Found\r\n\r\n");
                            }

                            break;
                        }
                    }
                }
            }

            if (response == null)
            {
                return(null);
            }

            await client.WriteAsync(response, 0, response.Length, cancellationToken).ConfigureAwait(false);

            return(server);
        }