Exemplo n.º 1
0
        public void Start(Configuration configuration)
        {
            if (_service is not null)
            {
                return;
            }

            var ip = configuration.ShareOverLan ? Global.IpAny : Global.IpLocal;

            RunningPort = GetFreePort(ip);
            var ipe    = new IPEndPoint(ip, RunningPort);
            var option = new Socks5CreateOption
            {
                Address          = Global.IpLocal,
                Port             = (ushort)configuration.LocalPort,
                UsernamePassword = new UsernamePassword
                {
                    UserName = configuration.AuthUser,
                    Password = configuration.AuthPass
                }
            };

            _service = new HttpSocks5Service(ipe, new HttpToSocks5(), option);
            _service.StartAsync().Forget();
        }
Exemplo n.º 2
0
    public Socks5Client(Socks5CreateOption option)
    {
        Requires.NotNull(option, nameof(option));
        Requires.NotNullAllowStructs(option.Address, nameof(option.Address));

        _option    = option;
        _tcpClient = new TcpClient(option.Address.AddressFamily);
    }
Exemplo n.º 3
0
    public HttpSocks5Service(IPEndPoint bindEndPoint, HttpToSocks5 httpToSocks5, Socks5CreateOption socks5CreateOption)
    {
        Requires.NotNullAllowStructs(socks5CreateOption.Address, nameof(socks5CreateOption.Address));

        TcpListener         = new TcpListener(bindEndPoint);
        _httpToSocks5       = httpToSocks5;
        _socks5CreateOption = socks5CreateOption;

        _cts = new CancellationTokenSource();
    }
Exemplo n.º 4
0
        public Socks5UdpProxy(IPEndPoint localEndPoint, Socks5CreateOption socks5Options)
        {
            _socks5Options = socks5Options;
            Requires.NotNull(localEndPoint, nameof(localEndPoint));
            Requires.NotNull(socks5Options, nameof(socks5Options));
            Requires.Argument(socks5Options.Address is not null, nameof(socks5Options), @"SOCKS5 address is null");

            _localEndPoint = localEndPoint;
            _socks5Options = socks5Options;
        }
Exemplo n.º 5
0
    public static async Task <NatTypeTestResult> DiscoveryNatTypeAsync(Socks5Server socks5, CancellationToken ctx = default)
    {
        var stunServer = Global.Settings.STUN_Server;
        var port       = (ushort)Global.Settings.STUN_Server_Port;
        var local      = new IPEndPoint(IPAddress.Any, 0);

        var socks5Option = new Socks5CreateOption
        {
            Address          = await DnsUtils.LookupAsync(socks5.Hostname),
            Port             = socks5.Port,
            UsernamePassword = new UsernamePassword
            {
                UserName = socks5.Username,
                Password = socks5.Password
            }
        };

        var ip = await DnsUtils.LookupAsync(stunServer);

        if (ip == null)
        {
            return(new NatTypeTestResult {
                Result = "Wrong STUN Server!"
            });
        }

        using IUdpProxy proxy = ProxyFactory.CreateProxy(ProxyType.Socks5, new IPEndPoint(IPAddress.Loopback, 0), socks5Option);
        using var client      = new StunClient5389UDP(new IPEndPoint(ip, port), local, proxy);

        await client.ConnectProxyAsync(ctx);

        try
        {
            await client.QueryAsync(ctx);
        }
        finally
        {
            await client.CloseProxyAsync(ctx);
        }

        var res    = client.State;
        var result = GetSimpleResult(res);

        return(new NatTypeTestResult
        {
            Result = result,
            LocalEnd = res.LocalEndPoint?.ToString(),
            PublicEnd = res.PublicEndPoint?.ToString()
        });
    }
Exemplo n.º 6
0
    /// <summary>
    /// 使用 HTTP1.1 204 测试 SOCKS5 CONNECT
    /// <para>Example:</para>
    /// <para>http://www.google.com/generate_204</para>
    /// <para>http://connectivitycheck.gstatic.com/generate_204</para>
    /// <para>http://connect.rom.miui.com/generate_204</para>
    /// <para>http://cp.cloudflare.com</para>
    /// </summary>
    public static async ValueTask <bool> Socks5ConnectAsync(
        Socks5CreateOption option,
        string target           = @"http://cp.cloudflare.com",
        string targetHost       = @"cp.cloudflare.com",
        ushort targetPort       = 80,
        CancellationToken token = default)
    {
        string sendString = $"GET {target} HTTP/1.1\r\nHost: {targetHost}\r\n\r\n";

        using Socks5Client client = new(option);

        ServerBound bound = await client.ConnectAsync(targetHost, targetPort, token);

        Debug.WriteLine($@"TCP: Supported, {bound.Type} {(bound.Type == AddressType.Domain ? bound.Domain : bound.Address)}:{bound.Port}");

        IDuplexPipe pipe = client.GetPipe();

        await pipe.Output.WriteAsync(sendString, token);

        // Response

        string?content;

        while (true)
        {
            ReadResult result = await pipe.Input.ReadAsync(token);

            ReadOnlySequence <byte> buffer = result.Buffer;
            try
            {
                if (TryReadLine(ref buffer, out content))
                {
                    break;
                }

                if (result.IsCompleted)
                {
                    break;
                }
            }
            finally
            {
                pipe.Input.AdvanceTo(buffer.Start, buffer.End);
            }
        }

        return(content is not null && content.Equals(@"HTTP/1.1 204 No Content", StringComparison.OrdinalIgnoreCase));
Exemplo n.º 7
0
        public static IUdpProxy CreateProxy(ProxyType type, IPEndPoint local, Socks5CreateOption option)
        {
            switch (type)
            {
            case ProxyType.Plain:
            {
                return(new NoneUdpProxy(local));
            }

            case ProxyType.Socks5:
            {
                Requires.NotNull(option, nameof(option));
                Requires.Argument(option.Address is not null, nameof(option), @"Proxy server is null");
                return(new Socks5UdpProxy(local, option));
            }

            default:
            {
                throw Assumes.NotReachable();
            }
            }
        }
Exemplo n.º 8
0
    public async ValueTask ForwardToSocks5Async(IDuplexPipe incomingPipe, Socks5CreateOption socks5CreateOption, CancellationToken cancellationToken = default)
    {
        try
        {
            string headers = await ReadHttpHeadersAsync(incomingPipe.Input, cancellationToken);

            _logger.LogDebug("Client headers received: \n{Headers}", headers);

            if (!TryParseHeader(headers, out HttpHeaders? httpHeaders))
            {
                await SendErrorAsync(incomingPipe.Output, ConnectionErrorResult.InvalidRequest, token: cancellationToken);

                return;
            }
            _logger.LogDebug("New request headers: \n{Headers}", httpHeaders);

            if (httpHeaders.Hostname is null || !httpHeaders.IsConnect && httpHeaders.Request is null)
            {
                await SendErrorAsync(incomingPipe.Output, ConnectionErrorResult.InvalidRequest, httpHeaders.HttpVersion, cancellationToken);

                return;
            }

            using Socks5Client socks5Client = new(socks5CreateOption);
            try
            {
                await socks5Client.ConnectAsync(httpHeaders.Hostname, httpHeaders.Port, cancellationToken);
            }
            catch (Socks5ProtocolErrorException ex) when(ex.Socks5Reply == Socks5Reply.ConnectionNotAllowed)
            {
                await SendErrorAsync(incomingPipe.Output, ConnectionErrorResult.AuthenticationError, httpHeaders.HttpVersion, cancellationToken);

                return;
            }
            catch (Socks5ProtocolErrorException ex) when(ex.Socks5Reply == Socks5Reply.HostUnreachable)
            {
                await SendErrorAsync(incomingPipe.Output, ConnectionErrorResult.HostUnreachable, httpHeaders.HttpVersion, cancellationToken);

                return;
            }
            catch (Socks5ProtocolErrorException ex) when(ex.Socks5Reply == Socks5Reply.ConnectionRefused)
            {
                await SendErrorAsync(incomingPipe.Output, ConnectionErrorResult.ConnectionRefused, httpHeaders.HttpVersion, cancellationToken);

                return;
            }
            catch (Socks5ProtocolErrorException)
            {
                await SendErrorAsync(incomingPipe.Output, ConnectionErrorResult.InvalidRequest, httpHeaders.HttpVersion, cancellationToken);

                return;
            }

            if (httpHeaders.IsConnect)
            {
                await SendConnectSuccessAsync(incomingPipe.Output, httpHeaders.HttpVersion, cancellationToken);

                IDuplexPipe socks5Pipe = socks5Client.GetPipe();

                await socks5Pipe.LinkToAsync(incomingPipe, cancellationToken);
            }
            else
            {
                IDuplexPipe socks5Pipe = socks5Client.GetPipe();

                await socks5Pipe.Output.WriteAsync(httpHeaders.Request !, cancellationToken);

                if (httpHeaders.ContentLength > 0)
                {
                    _logger.LogDebug(@"Waiting for up to {ContentLength} bytes from client", httpHeaders.ContentLength);

                    long readLength = await incomingPipe.Input.CopyToAsync(socks5Pipe.Output, httpHeaders.ContentLength, cancellationToken);

                    _logger.LogDebug(@"client sent {ClientSentLength} bytes to server", readLength);
                }

                //Read response
                string responseHeadersStr = await ReadHttpHeadersAsync(socks5Pipe.Input, cancellationToken);

                _logger.LogDebug("server headers received: \n{Headers}", responseHeadersStr);
                Dictionary <string, string> responseHeaders = ReadHeaders(responseHeadersStr);

                incomingPipe.Output.Write(responseHeadersStr);
                incomingPipe.Output.Write(HttpHeaderEnd);
                await incomingPipe.Output.FlushAsync(cancellationToken);

                if (IsChunked(responseHeaders))
                {
                    await CopyChunksAsync(socks5Pipe.Input, incomingPipe.Output, cancellationToken);
                }
                else if (TryReadContentLength(responseHeaders, out long serverResponseContentLength))
                {
                    if (serverResponseContentLength > 0)
                    {
                        _logger.LogDebug(@"Waiting for up to {ContentLength} bytes from server", serverResponseContentLength);

                        long readLength = await socks5Pipe.Input.CopyToAsync(incomingPipe.Output, serverResponseContentLength, cancellationToken);

                        _logger.LogDebug(@"server sent {ServerSentLength} bytes to client", readLength);
                    }
                }
            }
        }
        catch (Exception)
        {
            await SendErrorAsync(incomingPipe.Output, ConnectionErrorResult.UnknownError, token : cancellationToken);

            throw;
        }
    }
Exemplo n.º 9
0
    public async Task TestAsync()
    {
        IPEndPoint       serverEndpoint = new(IPAddress.Loopback, 0);
        UsernamePassword userPass       = new()
        {
            UserName = @"114514!",
            Password = @"1919810¥"
        };
        SimpleSocks5Server server = new(serverEndpoint, userPass);

        server.StartAsync().Forget();
        try
        {
            ushort             port = (ushort)((IPEndPoint)server.TcpListener.LocalEndpoint).Port;
            Socks5CreateOption socks5CreateOption = new()
            {
                Address          = IPAddress.Loopback,
                Port             = port,
                UsernamePassword = userPass
            };
            HttpSocks5Service httpServer = new(serverEndpoint, new HttpToSocks5(), socks5CreateOption);
            httpServer.StartAsync().Forget();
            try
            {
                IPAddress          httpAddress = ((IPEndPoint)httpServer.TcpListener.LocalEndpoint).Address;
                ushort             httpPort    = (ushort)((IPEndPoint)httpServer.TcpListener.LocalEndpoint).Port;
                SocketsHttpHandler handler     = new()
                {
                    UseProxy = true,
                    Proxy    = new WebProxy(httpAddress.ToString(), httpPort)
                };
                HttpClient httpClient = new(handler);

                // CONNECT
                string httpsStr = await httpClient.GetStringAsync(@"https://api.ip.sb/ip");

                Assert.IsFalse(string.IsNullOrWhiteSpace(httpsStr));

                // HTTP chunk
                string httpChunkStr = await httpClient.GetStringAsync(@"http://api.ip.sb/ip");

                Assert.IsFalse(string.IsNullOrWhiteSpace(httpChunkStr));
                Assert.AreEqual(httpsStr, httpChunkStr);

                // HTTP Content-Length
                httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(@"curl/7.55.1");
                string httpStr = await httpClient.GetStringAsync(@"http://ip.sb");

                Assert.IsFalse(string.IsNullOrWhiteSpace(httpStr));
                Assert.AreEqual(httpChunkStr, httpStr);

                // HTTP no body
                HttpResponseMessage response = await httpClient.GetAsync(@"http://cp.cloudflare.com");

                Assert.AreEqual(HttpStatusCode.NoContent, response.StatusCode);

                // Forward to SOCKS5
                socks5CreateOption = new Socks5CreateOption
                {
                    Address          = httpAddress,
                    Port             = httpPort,
                    UsernamePassword = userPass
                };
                Assert.IsTrue(await Socks5TestUtils.Socks5ConnectAsync(socks5CreateOption));
            }
            finally
            {
                httpServer.Stop();
            }
        }
        finally
        {
            server.Stop();
        }
    }