如何基于 Kestrel 实现 socks5 代理

前言

之前做了个轮子NZOrz, 本来打算慢慢参照KestrelYarp长久地写着玩

奈何川普上台,关税,订婚案,自身和钱包等等各种乐子层出不穷,无暇慢悠悠地写轮子玩

还有有些盆友也想知道能否直接使用 Kestrel 来实现L4的处理,

所以为了2025年轻松一些,重新基于 Kestrel 实现了 L4/L7的代理 VKProxy (有兴趣的同学点个赞呗),并简单实现 socks5 为大家展示一下

(PS:叠甲 本人认知和能力有限,永远搞不懂/也不知道什么Txxxrojan/Sxxxhadowsocks等等这些东西,所以请不要咨询本人,本人不会不懂)

如何释放 Kestrel 的能力

众所周知 Kestrel 是 Aspnetcore 为了跨平台而实现的web server,只提供 http 1/2/3 的 L7层的能力

但看过源码的同学都知道,其实其本身从L4层(socket)实现的Http协议处理,只是OnBind只有http相关实现以及没有提供相关公开扩展的api,所以限制了其能力

但是既然代码是开源的,并且我们也知道dotnet有虽然麻烦但是能跨越访问限制的能力(Reflection),所以它是不能阻挡我们的魔爪

(ps
1. 不过这样绕过限制可能会在Native AOT相关场景存在问题,目前暂时没有做具体相关测试
2. 在不同版本Kestrel 可能会存在api变动,目前为了省事,不适配各版本差异,暂时以net9.0为准,net10正式发布后迁移升级到net10,此后不再适配net9.0之前版本

示例

首先我们先来看完成效果监听并处理 tcp/udp/http1/http2/http3,以便大家能理解我们的目的

VKProxy.Core 单纯封装释放Kestrel的能力以及简单的udp处理能力,所以大家单纯想使用 Kestrel处理相关内容就可以只使用VKProxy.Core

安装
dotnet add package VKProxy.Core --version 0.0.0.1 
程序入口
using CoreDemo; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using VKProxy.Core.Hosting;  var app = Host.CreateDefaultBuilder(args).UseVKProxyCore()     .ConfigureServices(i =>     {         // 已通过 IListenHandler 解耦监听和处理, 大家可以实现其而做任意自己想做的事情         i.AddSingleton<IListenHandler, TcpListenHandler>();         i.AddSingleton<IListenHandler, UdpListenHandler>();         i.AddSingleton<IListenHandler, HttpListenHandler>();     })     .Build();  await app.RunAsync(); 
如何处理 tcp
internal class TcpListenHandler : ListenHandlerBase {     private readonly List<EndPointOptions> endPointOptions = new List<EndPointOptions>();     private readonly ILogger<TcpListenHandler> logger;     private readonly IConnectionFactory connectionFactory;      public TcpListenHandler(ILogger<TcpListenHandler> logger, IConnectionFactory connectionFactory)     {         this.logger = logger;         this.connectionFactory = connectionFactory;     }      /// 程序初次启动时,可以在此实现相关的初始化操作     public override Task InitAsync(CancellationToken cancellationToken)     {         endPointOptions.Add(new EndPointOptions()         {             EndPoint = IPEndPoint.Parse("127.0.0.1:5000"),             Key = "tcpXXX"         });         return Task.CompletedTask;     }      /// 可在此方法通过 transportManager.BindAsync 实现监听哪些端口以及如何处理,如果需要运行时监听端口变动等,可通过 GetReloadToken 和 RebindAsync 实现,这里为了简单不再举例     public override async Task BindAsync(ITransportManager transportManager, CancellationToken cancellationToken)     {         foreach (var item in endPointOptions)         {             try             {                 await transportManager.BindAsync(item, Proxy, cancellationToken);                 logger.LogInformation($"listen {item.EndPoint}");             }             catch (Exception ex)             {                 logger.LogError(ex.Message, ex);             }         }     }      /// 处理的委托方法,这里的例子为简单的 tcp 代理     private async Task Proxy(ConnectionContext connection)     {         logger.LogInformation($"begin tcp {DateTime.Now} {connection.LocalEndPoint.ToString()} ");         var upstream = await connectionFactory.ConnectAsync(new IPEndPoint(IPAddress.Parse("14.215.177.38"), 80));         var task1 = connection.Transport.Input.CopyToAsync(upstream.Transport.Output);         var task2 = upstream.Transport.Input.CopyToAsync(connection.Transport.Output);         await Task.WhenAny(task1, task2);         upstream.Abort();         connection.Abort();         logger.LogInformation($"end tcp {DateTime.Now} {connection.LocalEndPoint.ToString()} ");     } } 
如何处理 udp

默认已提供简单的udp 处理,所以无需大家自己实现监听循环, 当然由于实现过于简单,复杂场景可能需要大家自己实现 IConnectionListenerFactory 或者 IMultiplexedConnectionListenerFactory

internal class UdpListenHandler : ListenHandlerBase {     private readonly ILogger<UdpListenHandler> logger;     private readonly IUdpConnectionFactory udp;     private readonly IPEndPoint proxyServer = new(IPAddress.Parse("127.0.0.1"), 11000);      public UdpListenHandler(ILogger<UdpListenHandler> logger, IUdpConnectionFactory udp)     {         this.logger = logger;         this.udp = udp;     }      public override async Task BindAsync(ITransportManager transportManager, CancellationToken cancellationToken)     {         var ip = new EndPointOptions()         {             EndPoint = UdpEndPoint.Parse("127.0.0.1:5000"), // 为了区别 Kestrel 默认的tcp实现,所以必须通过 UdpEndPoint 屏蔽默认的tcp监听             Key = "udpXXX"         };         await transportManager.BindAsync(ip, Proxy, cancellationToken);         logger.LogInformation($"listen {ip.EndPoint}");     }      /// 处理的委托方法,这里的例子为简单的 UDP 代理     private async Task Proxy(ConnectionContext connection)     {         if (connection is UdpConnectionContext context)         {             Console.WriteLine($"{context.LocalEndPoint} received {context.ReceivedBytesCount} from {context.RemoteEndPoint}");             var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);             await udp.SendToAsync(socket, proxyServer, context.ReceivedBytes, CancellationToken.None);             var r = await udp.ReceiveAsync(socket, CancellationToken.None);             await udp.SendToAsync(context.Socket, context.RemoteEndPoint, r.GetReceivedBytes(), CancellationToken.None);         }     } } 
如何处理 http
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Https; using Microsoft.Extensions.Logging; using System.Net; using VKProxy.Core.Adapters; using VKProxy.Core.Config; using VKProxy.Core.Hosting;  namespace CoreDemo;  public class HttpListenHandler : ListenHandlerBase {    private readonly ILogger<HttpListenHandler> logger;    private readonly ICertificateLoader certificateLoader;     public HttpListenHandler(ILogger<HttpListenHandler> logger, ICertificateLoader certificateLoader)    {        this.logger = logger;        this.certificateLoader = certificateLoader;    }     private async Task Proxy(HttpContext context)    {        var resp = context.Response;        resp.StatusCode = 404;        await resp.WriteAsJsonAsync(new { context.Request.Protocol });        await resp.CompleteAsync().ConfigureAwait(false);    }     public override async Task BindAsync(ITransportManager transportManager, CancellationToken cancellationToken)    {        try        {            // http  (http2和http3都需要证书,所以这里监听会忽略,只监听http1)            var ip = new EndPointOptions()            {                EndPoint = IPEndPoint.Parse("127.0.0.1:4000"),                Key = "http"            };            await transportManager.BindHttpAsync(ip, Proxy, cancellationToken);            logger.LogInformation($"listen {ip.EndPoint}");             // https            ip = new EndPointOptions()            {                EndPoint = IPEndPoint.Parse("127.0.0.1:4001"),                Key = "https"            };             var (c, f) = certificateLoader.LoadCertificate(new CertificateConfig() { Path = "testCert.pfx", Password = "testPassword" });  //读取证书            await transportManager.BindHttpAsync(ip, Proxy, cancellationToken, HttpProtocols.Http1AndHttp2AndHttp3, callbackOptions: new HttpsConnectionAdapterOptions()            {                //ServerCertificateSelector = (context, host) => c   http3 由于底层 quic 实现,无法支持动态ServerCertificate                ServerCertificate = c,                CheckCertificateRevocation = false,                ClientCertificateMode = ClientCertificateMode.AllowCertificate            });            logger.LogInformation($"listen {ip.EndPoint}");        }        catch (Exception ex)        {            logger.LogError(ex.Message, ex);        }    } } 

适配Kestrel 的核心点

核心重点在暴露TransportManager api, 这样大家就有了L4层的处理能力

TransportManagerAdapter 实现

public class TransportManagerAdapter : ITransportManager, IHeartbeat {     private static MethodInfo StopAsyncMethod;     private static MethodInfo StopEndpointsAsyncMethod;     private static MethodInfo MultiplexedBindAsyncMethod;     private static MethodInfo BindAsyncMethod;     private static MethodInfo StartHeartbeatMethod;     private object transportManager;     private object heartbeat;     private object serviceContext;     private object metrics;     private int multiplexedTransportCount;     private int transportCount;     internal readonly IServiceProvider serviceProvider;      IServiceProvider ITransportManager.ServiceProvider => serviceProvider;      public TransportManagerAdapter(IServiceProvider serviceProvider, IEnumerable<IConnectionListenerFactory> transportFactories, IEnumerable<IMultiplexedConnectionListenerFactory> multiplexedConnectionListenerFactories)     {         (transportManager, heartbeat, serviceContext, metrics) = CreateTransportManager(serviceProvider);         multiplexedTransportCount = multiplexedConnectionListenerFactories.Count();         transportCount = transportFactories.Count();         this.serviceProvider = serviceProvider;     }      private static (object, object, object, object) CreateTransportManager(IServiceProvider serviceProvider)     {         foreach (var item in KestrelExtensions.TransportManagerType.GetTypeInfo().DeclaredMethods)         {             if (item.Name == "StopAsync")             {                 StopAsyncMethod = item;             }             else if (item.Name == "StopEndpointsAsync")             {                 StopEndpointsAsyncMethod = item;             }             else if (item.Name == "BindAsync")             {                 if (item.GetParameters().Any(i => i.ParameterType == typeof(ConnectionDelegate)))                 {                     BindAsyncMethod = item;                 }                 else                 {                     MultiplexedBindAsyncMethod = item;                 }             }         }          var s = CreateServiceContext(serviceProvider);         var r = Activator.CreateInstance(KestrelExtensions.TransportManagerType,                     Enumerable.Reverse(serviceProvider.GetServices<IConnectionListenerFactory>()).ToList(),                     Enumerable.Reverse(serviceProvider.GetServices<IMultiplexedConnectionListenerFactory>()).ToList(),                     CreateHttpsConfigurationService(serviceProvider),                     s.context                     );         return (r, s.heartbeat, s.context, s.metrics);          static object CreateHttpsConfigurationService(IServiceProvider serviceProvider)         {             var CreateLogger = typeof(LoggerFactoryExtensions).GetTypeInfo().DeclaredMethods.First(i => i.Name == "CreateLogger" && i.ContainsGenericParameters);             var r = Activator.CreateInstance(KestrelExtensions.HttpsConfigurationServiceType);             var m = KestrelExtensions.HttpsConfigurationServiceType.GetMethod("Initialize");             var log = serviceProvider.GetRequiredService<ILoggerFactory>();             var l = CreateLogger.MakeGenericMethod(KestrelExtensions.HttpsConnectionMiddlewareType).Invoke(null, new object[] { log });             m.Invoke(r, new object[] { serviceProvider.GetRequiredService<IHostEnvironment>(), log.CreateLogger<KestrelServer>(), l });             return r;         }          static (object context, object heartbeat, object metrics) CreateServiceContext(IServiceProvider serviceProvider)         {             var m = CreateKestrelMetrics();             var KestrelCreateServiceContext = KestrelExtensions.KestrelServerImplType.GetMethod("CreateServiceContext", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);             var r = KestrelCreateServiceContext.Invoke(null, new object[]             {                 serviceProvider.GetRequiredService<IOptions<KestrelServerOptions>>(),                 serviceProvider.GetRequiredService<ILoggerFactory>(),                 null,                 m             });             var h = KestrelExtensions.ServiceContextType.GetTypeInfo().DeclaredProperties.First(i => i.Name == "Heartbeat");             StartHeartbeatMethod = KestrelExtensions.HeartbeatType.GetTypeInfo().DeclaredMethods.First(i => i.Name == "Start");             return (r, h.GetGetMethod().Invoke(r, null), m);         }          static object CreateKestrelMetrics()         {             return Activator.CreateInstance(KestrelExtensions.KestrelMetricsType, Activator.CreateInstance(KestrelExtensions.DummyMeterFactoryType));         }     }      public Task<EndPoint> BindAsync(EndPointOptions endpointConfig, ConnectionDelegate connectionDelegate, CancellationToken cancellationToken)     {         return BindAsyncMethod.Invoke(transportManager, new object[] { endpointConfig.EndPoint, connectionDelegate, endpointConfig.Init(), cancellationToken }) as Task<EndPoint>;     }      public Task<EndPoint> BindAsync(EndPointOptions endpointConfig, MultiplexedConnectionDelegate multiplexedConnectionDelegate, CancellationToken cancellationToken)     {         return MultiplexedBindAsyncMethod.Invoke(transportManager, new object[] { endpointConfig.EndPoint, multiplexedConnectionDelegate, endpointConfig.GetListenOptions(), cancellationToken }) as Task<EndPoint>;     }      public Task StopEndpointsAsync(List<EndPointOptions> endpointsToStop, CancellationToken cancellationToken)     {         return StopEndpointsAsyncMethod.Invoke(transportManager, new object[] { EndPointOptions.Init(endpointsToStop), cancellationToken }) as Task;     }      public Task StopAsync(CancellationToken cancellationToken)     {         return StopAsyncMethod.Invoke(transportManager, new object[] { cancellationToken }) as Task;     }      public void StartHeartbeat()     {         if (heartbeat != null)         {             StartHeartbeatMethod.Invoke(heartbeat, null);         }     }      public void StopHeartbeat()     {         if (heartbeat is IDisposable disposable)         {             disposable.Dispose();         }     }      public IConnectionBuilder UseHttpServer(IConnectionBuilder builder, IHttpApplication<HttpApplication.Context> application, HttpProtocols protocols, bool addAltSvcHeader)     {         KestrelExtensions.UseHttpServerMethod.Invoke(null, new object[] { builder, serviceContext, application, protocols, addAltSvcHeader });         return builder;     }      public IMultiplexedConnectionBuilder UseHttp3Server(IMultiplexedConnectionBuilder builder, IHttpApplication<HttpApplication.Context> application, HttpProtocols protocols, bool addAltSvcHeader)     {         KestrelExtensions.UseHttp3ServerMethod.Invoke(null, new object[] { builder, serviceContext, application, protocols, addAltSvcHeader });         return builder;     }      public ConnectionDelegate UseHttps(ConnectionDelegate next, HttpsConnectionAdapterOptions tlsCallbackOptions, HttpProtocols protocols)     {         if (tlsCallbackOptions == null)             return next;         var o = KestrelExtensions.HttpsConnectionMiddlewareInitMethod.Invoke(new object[] { next, tlsCallbackOptions, protocols, serviceProvider.GetRequiredService<ILoggerFactory>(), metrics });         return KestrelExtensions.HttpsConnectionMiddlewareOnConnectionAsyncMethod.CreateDelegate<ConnectionDelegate>(o);     }      public async Task BindHttpApplicationAsync(EndPointOptions options, IHttpApplication<HttpApplication.Context> application, CancellationToken cancellationToken, HttpProtocols protocols = HttpProtocols.Http1AndHttp2AndHttp3, bool addAltSvcHeader = true, Action<IConnectionBuilder> config = null         , Action<IMultiplexedConnectionBuilder> configMultiplexed = null, HttpsConnectionAdapterOptions callbackOptions = null)     {         var hasHttp1 = protocols.HasFlag(HttpProtocols.Http1);         var hasHttp2 = protocols.HasFlag(HttpProtocols.Http2);         var hasHttp3 = protocols.HasFlag(HttpProtocols.Http3);         var hasTls = callbackOptions is not null;          if (hasTls)         {             if (hasHttp3)             {                 options.GetListenOptions().Protocols = protocols;                 options.SetHttpsOptions(callbackOptions);             }             //callbackOptions.SetHttpProtocols(protocols);             //if (hasHttp3)             //{             //    HttpsConnectionAdapterOptions             //    options.SetHttpsCallbackOptions(callbackOptions);             //}         }         else         {             // Http/1 without TLS, no-op HTTP/2 and 3.             if (hasHttp1)             {                 hasHttp2 = false;                 hasHttp3 = false;             }             // Http/3 requires TLS. Note we only let it fall back to HTTP/1, not HTTP/2             else if (hasHttp3)             {                 throw new InvalidOperationException("HTTP/3 requires HTTPS.");             }         }          // Quic isn't registered if it's not supported, throw if we can't fall back to 1 or 2         if (hasHttp3 && multiplexedTransportCount == 0 && !(hasHttp1 || hasHttp2))         {             throw new InvalidOperationException("Unable to bind an HTTP/3 endpoint. This could be because QUIC has not been configured using UseQuic, or the platform doesn't support QUIC or HTTP/3.");         }          addAltSvcHeader = addAltSvcHeader && multiplexedTransportCount > 0;          // Add the HTTP middleware as the terminal connection middleware         if (hasHttp1 || hasHttp2             || protocols == HttpProtocols.None)         {             if (transportCount == 0)             {                 throw new InvalidOperationException($"Cannot start HTTP/1.x or HTTP/2 server if no {nameof(IConnectionListenerFactory)} is registered.");             }              var builder = new ConnectionBuilder(serviceProvider);             config?.Invoke(builder);             UseHttpServer(builder, application, protocols, addAltSvcHeader);             var connectionDelegate = UseHttps(builder.Build(), callbackOptions, protocols);              options.EndPoint = await BindAsync(options, connectionDelegate, cancellationToken).ConfigureAwait(false);         }          if (hasHttp3 && multiplexedTransportCount > 0)         {             var builder = new MultiplexedConnectionBuilder(serviceProvider);             configMultiplexed?.Invoke(builder);             UseHttp3Server(builder, application, protocols, addAltSvcHeader);             var multiplexedConnectionDelegate = builder.Build();              options.EndPoint = await BindAsync(options, multiplexedConnectionDelegate, cancellationToken).ConfigureAwait(false);         }     } } 

其次通过重写 VKServer 从而去除 OnBind 方法的影响,达到大家可以使用 ITransportManager 做任意 L4/L7的处理

public class VKServer : IServer {     private readonly ITransportManager transportManager;     private readonly IHeartbeat heartbeat;     private readonly IListenHandler listenHandler;     private readonly GeneralLogger logger;     private bool _hasStarted;     private int _stopping;     private readonly SemaphoreSlim _bindSemaphore = new SemaphoreSlim(initialCount: 1);     private readonly CancellationTokenSource _stopCts = new CancellationTokenSource();     private readonly TaskCompletionSource _stoppedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);     private IDisposable? _configChangedRegistration;      public VKServer(ITransportManager transportManager, IHeartbeat heartbeat, IListenHandler listenHandler, GeneralLogger logger)     {         this.transportManager = transportManager;         this.heartbeat = heartbeat;         this.listenHandler = listenHandler;         this.logger = logger;     }      public async Task StartAsync(CancellationToken cancellationToken)     {         try         {             if (_hasStarted)             {                 throw new InvalidOperationException("Server already started");             }             _hasStarted = true;             await listenHandler.InitAsync(cancellationToken);             heartbeat.StartHeartbeat();             await BindAsync(cancellationToken).ConfigureAwait(false);         }         catch         {             Dispose();             throw;         }     }      private async Task BindAsync(CancellationToken cancellationToken)     {         await _bindSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);          try         {             if (_stopping == 1)             {                 throw new InvalidOperationException("Server has already been stopped.");             }              IChangeToken? reloadToken = listenHandler.GetReloadToken();             await listenHandler.BindAsync(transportManager, _stopCts.Token).ConfigureAwait(false);             _configChangedRegistration = reloadToken?.RegisterChangeCallback(TriggerRebind, this);         }         finally         {             _bindSemaphore.Release();         }     }      private void TriggerRebind(object? state)     {         if (state is VKServer server)         {             _ = server.RebindAsync();         }     }      private async Task RebindAsync()     {         await _bindSemaphore.WaitAsync();          IChangeToken? reloadToken = null;         try         {             if (_stopping == 1)             {                 return;             }              reloadToken = listenHandler.GetReloadToken();             await listenHandler.RebindAsync(transportManager, _stopCts.Token).ConfigureAwait(false);         }         catch (Exception ex)         {             logger.UnexpectedException("Unable to reload configuration", ex);         }         finally         {             _configChangedRegistration = reloadToken?.RegisterChangeCallback(TriggerRebind, this);             _bindSemaphore.Release();         }     }      public async Task StopAsync(CancellationToken cancellationToken)     {         if (Interlocked.Exchange(ref _stopping, 1) == 1)         {             await _stoppedTcs.Task.ConfigureAwait(false);             return;         }          heartbeat.StopHeartbeat();          _stopCts.Cancel();          await _bindSemaphore.WaitAsync().ConfigureAwait(false);          try         {             await listenHandler.StopAsync(transportManager, cancellationToken).ConfigureAwait(false);             await transportManager.StopAsync(cancellationToken).ConfigureAwait(false);         }         catch (Exception ex)         {             _stoppedTcs.TrySetException(ex);             throw;         }         finally         {             _configChangedRegistration?.Dispose();             _stopCts.Dispose();             _bindSemaphore.Release();         }          _stoppedTcs.TrySetResult();     }      public void Dispose()     {         StopAsync(new CancellationToken(canceled: true)).GetAwaiter().GetResult();     } } 

如何实现 socks5

socks5 代理协议已经有很多文章说明,这里不再赘述,想了解的可以参见https://zh.wikipedia.org/wiki/SOCKS

这里列举一下核心实现

internal class Socks5Middleware : ITcpProxyMiddleware {     private readonly IDictionary<byte, ISocks5Auth> auths;     private readonly IConnectionFactory tcp;     private readonly IHostResolver hostResolver;     private readonly ITransportManager transport;     private readonly IUdpConnectionFactory udp;      public Socks5Middleware(IEnumerable<ISocks5Auth> socks5Auths, IConnectionFactory tcp, IHostResolver hostResolver, ITransportManager transport, IUdpConnectionFactory udp)     {         this.auths = socks5Auths.ToFrozenDictionary(i => i.AuthType);         this.tcp = tcp;         this.hostResolver = hostResolver;         this.transport = transport;         this.udp = udp;     }      public Task InitAsync(ConnectionContext context, CancellationToken token, TcpDelegate next)     {        // 识别是否为 socks5 路由         var feature = context.Features.Get<IL4ReverseProxyFeature>();         if (feature is not null)         {             var route = feature.Route;             if (route is not null && route.Metadata is not null                 && route.Metadata.TryGetValue("socks5", out var b) && bool.TryParse(b, out var isSocks5) && isSocks5)             {                 feature.IsDone = true;                 return Proxy(context, feature, token);             }         }         return next(context, token);     }      public Task<ReadOnlyMemory<byte>> OnRequestAsync(ConnectionContext context, ReadOnlyMemory<byte> source, CancellationToken token, TcpProxyDelegate next)     {         return next(context, source, token);     }      public Task<ReadOnlyMemory<byte>> OnResponseAsync(ConnectionContext context, ReadOnlyMemory<byte> source, CancellationToken token, TcpProxyDelegate next)     {         return next(context, source, token);     }      private async Task Proxy(ConnectionContext context, IL4ReverseProxyFeature feature, CancellationToken token)     {         var input = context.Transport.Input;         var output = context.Transport.Output;         // 1. socks5 认证         if (!await Socks5Parser.AuthAsync(input, auths, context, token))         {             context.Abort();         }         // 2. 获取 socks5 命令请求         var cmd = await Socks5Parser.GetCmdRequestAsync(input, token);         IPEndPoint ip = await ResolveIpAsync(context, cmd, token);         switch (cmd.Cmd)         {             case Socks5Cmd.Connect:             case Socks5Cmd.Bind:                 // 3. 如果为tcp代理,则会在此分支处理,以命令请求中的地址建立tcp链接                 ConnectionContext upstream;                 try                 {                     upstream = await tcp.ConnectAsync(ip, token);                 }                 catch                 {  // 为了简单,这里异常没有详细分区各种情况                     await Socks5Parser.ResponeAsync(output, Socks5CmdResponseType.ConnectFail, token);                     throw;                 }                 // 4. 服务tcp建立成功,通知 client                 await Socks5Parser.ResponeAsync(output, Socks5CmdResponseType.Success, token);                 var task = await Task.WhenAny(                                context.Transport.Input.CopyToAsync(upstream.Transport.Output, token)                                , upstream.Transport.Input.CopyToAsync(context.Transport.Output, token));                 if (task.IsCanceled)                 {                     context.Abort();                 }                 break;              case Socks5Cmd.UdpAssociate:                 // 3. 如果为udp代理,则会在此分支处理,建立临时 udp 代理服务地址                 var local = context.LocalEndPoint as IPEndPoint;                 var op = new EndPointOptions()                 {                     EndPoint = new UdpEndPoint(local.Address, 0),                     Key = Guid.NewGuid().ToString(),                 };                 try                 {                     var remote = context.RemoteEndPoint;                     var timeout = feature.Route.Timeout;                     op.EndPoint = await transport.BindAsync(op, c => ProxyUdp(c as UdpConnectionContext, remote, timeout), token);                     // 5. tcp 关闭时 需要关闭临时 udp 服务                     context.ConnectionClosed.Register(state => transport.StopEndpointsAsync(new List<EndPointOptions>() { state as EndPointOptions }, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult(), op);                 }                 catch                 {                     await Socks5Parser.ResponeAsync(output, Socks5CmdResponseType.ConnectFail, token);                     throw;                 }                  // 4. 服务udp建立成功,通知 client 临时udp地址                 await Socks5Parser.ResponeAsync(output, op.EndPoint as IPEndPoint, Socks5CmdResponseType.Success, token);                 break;         }     }      private async Task ProxyUdp(UdpConnectionContext context, EndPoint remote, TimeSpan timeout)     {         using var cts = CancellationTokenSourcePool.Default.Rent(timeout);         var token = cts.Token;         // 这里用为了简单 同一个临时地址即监听client 也处理 服务端 response,通过端口比较区分, 当然这样存在一定安全问题          if (context.RemoteEndPoint.GetHashCode() == remote.GetHashCode())         {             var req = Socks5Parser.GetUdpRequest(context.ReceivedBytes);             IPEndPoint ip = await ResolveIpAsync(req, token);             // 请求服务,解包原始请求             await udp.SendToAsync(context.Socket, ip, req.Data, token);         }         else         {                         // 服务response,封包             await Socks5Parser.UdpResponeAsync(udp, context, remote as IPEndPoint, token);         }     }      private async Task<IPEndPoint> ResolveIpAsync(ConnectionContext context, Socks5Common cmd, CancellationToken token)     {         IPEndPoint ip = await ResolveIpAsync(cmd, token);         if (ip is null)         {             await Socks5Parser.ResponeAsync(context.Transport.Output, Socks5CmdResponseType.AddressNotAllow, token);             context.Abort();         }          return ip;     }      private async Task<IPEndPoint> ResolveIpAsync(Socks5Common cmd, CancellationToken token)     {         IPEndPoint ip;         if (cmd.Domain is not null)         {             var ips = await hostResolver.HostResolveAsync(cmd.Domain, token);             if (ips.Length > 0)             {                 ip = new IPEndPoint(ips.First(), cmd.Port);             }             else                 ip = null;         }         else if (cmd.Ip is not null)         {             ip = new IPEndPoint(cmd.Ip, cmd.Port);         }         else         {             ip = null;         }          return ip;     } } 

如此大家可以看到大家无需疯狂 while(true) { await socket.Receive... }, 减轻了很多大家负担

发表评论

评论已关闭。

相关文章