private async Task HandleAsync(Socket socket, CancellationToken token)
    {
        try
        {
            IDuplexPipe pipe   = socket.AsDuplexPipe();
            ReadResult  result = await pipe.Input.ReadAsync(token);

            ReadOnlySequence <byte> buffer = result.Buffer;

            if (IsSocks5Header(buffer))
            {
                using TcpClient socks5 = new();
                await socks5.ConnectAsync(_socks5CreateOption.Address !, _socks5CreateOption.Port, token);

                IDuplexPipe socks5Pipe = socks5.Client.AsDuplexPipe();

                await socks5Pipe.Output.WriteAsync(buffer, token);

                pipe.Input.AdvanceTo(buffer.End);

                await socks5Pipe.LinkToAsync(pipe, token);
            }
            else
            {
                pipe.Input.AdvanceTo(buffer.Start, buffer.End);
                pipe.Input.CancelPendingRead();

                await _httpToSocks5.ForwardToSocks5Async(pipe, _socks5CreateOption, token);
            }
        }
        finally
        {
            socket.FullClose();
        }
Beispiel #2
0
    private async ValueTask HandleAsync(Socket socket, CancellationToken token)
    {
        try
        {
            IDuplexPipe            pipe    = socket.AsDuplexPipe();
            Socks5ServerConnection service = new(pipe, _credential);
            await service.AcceptClientAsync(token);

            switch (service.Command)
            {
            case Command.Connect:
            {
                using TcpClient tcp = new();
                if (service.Target.Type is AddressType.Domain)
                {
                    Assumes.NotNull(service.Target.Domain);
                    await tcp.ConnectAsync(service.Target.Domain, service.Target.Port, token);
                }
                else
                {
                    Assumes.NotNull(service.Target.Address);
                    await tcp.ConnectAsync(service.Target.Address, service.Target.Port, token);
                }

                await service.SendReplyAsync(Socks5Reply.Succeeded, ReplyTcpBound, token);

                IDuplexPipe tcpPipe = tcp.Client.AsDuplexPipe();

                await tcpPipe.LinkToAsync(pipe, token);

                break;
            }

            case Command.Bind:
            {
                await service.SendReplyAsync(Socks5Reply.CommandNotSupported, ReplyTcpBound, token);

                break;
            }

            case Command.UdpAssociate:
            {
                IPEndPoint remote = (IPEndPoint)socket.RemoteEndPoint !;
                IPEndPoint local  = new(((IPEndPoint)TcpListener.LocalEndpoint).Address, IPEndPoint.MinPort);
                using SimpleSocks5UdpServer udpServer = new(local, remote);
                udpServer.StartAsync().Forget();

                ServerBound replyUdpBound = new()
                {
                    Type    = AddressType.IPv4,
                    Address = local.Address,
                    Domain  = default,
                    Port    = (ushort)((IPEndPoint)udpServer.UdpListener.Client.LocalEndPoint !).Port,
                };

                await service.SendReplyAsync(Socks5Reply.Succeeded, replyUdpBound, token);

                // wait remote close
                ReadResult result = await pipe.Input.ReadAsync(token);

                Report.IfNot(result.IsCompleted);

                break;
            }
Beispiel #3
0
    public async ValueTask HandleAsync(IDuplexPipe pipe, CancellationToken token = default)
    {
        Verify.Operation(Socks5CreateOption is not null, @"You must set {0}", nameof(Socks5CreateOption));
        Verify.Operation(Socks5CreateOption.Address is not null, @"You must set socks5 address");

        Socks5ServerConnection socks5 = new(pipe, Socks5CreateOption.UsernamePassword);

        await socks5.AcceptClientAsync(token);

        AddressType outType = Socks5CreateOption.Address.AddressFamily is AddressFamily.InterNetwork ? AddressType.IPv4 : AddressType.IPv6;

        switch (socks5.Command)
        {
        case Command.Connect:
        {
            _logger.LogDebug(@"SOCKS5 Connect");

            string target = socks5.Target.Type switch
            {
                AddressType.Domain => socks5.Target.Domain !,
                _ => socks5.Target.Address !.ToString()
            };
            ushort targetPort = socks5.Target.Port;

            IPipeClient?tmpClient = null;
            try
            {
                tmpClient = await _serversController.GetServerAsync(target, targetPort);
            }
            catch (Exception)
            {
                tmpClient?.Dispose();
                ServerBound failBound = new()
                {
                    Type    = outType,
                    Address = Socks5CreateOption.Address,
                    Port    = IPEndPoint.MinPort
                };
                await socks5.SendReplyAsync(Socks5Reply.HostUnreachable, failBound, token);

                throw;
            }

            using IPipeClient client = tmpClient;
            if (client is ConnectionRefusedTcpClient)
            {
                _logger.LogInformation(@"SOCKS5 Connect to {Target}:{Port} Refused", target, targetPort);
                ServerBound failBound = new()
                {
                    Type    = outType,
                    Address = Socks5CreateOption.Address,
                    Port    = IPEndPoint.MinPort
                };
                await socks5.SendReplyAsync(Socks5Reply.ConnectionRefused, failBound, token);

                break;
            }

            _logger.LogInformation(@"SOCKS5 Connect to {Target}:{Port} via {Client}", target, targetPort, client);

            ServerBound bound = new()
            {
                Type    = outType,
                Address = Socks5CreateOption.Address,
                Port    = IPEndPoint.MinPort
            };
            await socks5.SendReplyAsync(Socks5Reply.Succeeded, bound, token);

            IDuplexPipe clientPipe = client.GetPipe();

            await clientPipe.LinkToAsync(pipe, token);

            break;
        }

        case Command.UdpAssociate:
        {
            _logger.LogDebug(@"SOCKS5 UdpAssociate");

            ServerBound bound = new()
            {
                Type    = outType,
                Address = Socks5CreateOption.Address,
                Port    = Socks5CreateOption.Port
            };
            await socks5.SendReplyAsync(Socks5Reply.Succeeded, bound, token);

            // wait remote close
            ReadResult result = await pipe.Input.ReadAsync(token);

            Report.IfNot(result.IsCompleted);
            break;
        }

        default:
        {
            ServerBound bound = new()
            {
                Type    = outType,
                Address = Socks5CreateOption.Address,
                Port    = IPEndPoint.MinPort
            };
            await socks5.SendReplyAsync(Socks5Reply.CommandNotSupported, bound, token);

            break;
        }
        }
    }
}
Beispiel #4
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;
        }
    }