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(); }
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; }
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; } } } }
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; } }