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(); }
public Socks5Client(Socks5CreateOption option) { Requires.NotNull(option, nameof(option)); Requires.NotNullAllowStructs(option.Address, nameof(option.Address)); _option = option; _tcpClient = new TcpClient(option.Address.AddressFamily); }
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(); }
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; }
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() }); }
/// <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));
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(); } } }
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; } }
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(); } }