前言

之前做了个轮子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... }, 减轻了很多大家负担

如何基于 Kestrel 实现 socks5 代理的更多相关文章

  1. Socks5代理Socks5 Proxy

    Socks5代理Socks5 Proxy   Socks5代理是基于Socks协议的一种代理模式.其中,5表示该协议的版本号.它可以让局域网的计算机通过socks5代理服务器,访问外网的内容.由于它工 ...

  2. 使用 EW 作Socks5代理

    简介: EarthWorm是一款用于开启 SOCKS v5 代理服务的工具,基于标准 C 开发,可提供多平台间的转接通讯,用于复杂网络环境下的数据转发. 主页: http://rootkiter.co ...

  3. paip.基于urlrewrite的反向代理以及内容改写

    paip.基于urlrewrite的反向代理以及内容改写 ---------反向代理 RewriteCond %{REQUEST_URI} !=/process.php RewriteRule  ^( ...

  4. sshuttle基于VPN的透明代理,安全连接

    sshuttle基于VPN的透明代理, 通过 ssh 创建一条从你电脑连接到任何远程服务器的 VPN 连 sudo sshuttle -r username@sshserver 0.0.0.0/0 - ...

  5. 利用proxychains在终端使用socks5代理

    最近用各种脚本下载东西的时候发现有的站点需要当地IP才能下,比如.....nico, youtube等: 所以就找了下能在终端用socks5代理的工具,最后找到了proxychains,从此再无压力= ...

  6. http,socks4,socks5代理的区别

    HTTP代理 能够代理客户机的HTTP访问,主要是代理浏览器访问网页,它的端口一般为80.8080.3128等: SOCKS代理 SOCKS代理与其他类型的代理不同,它只是简单地传递数据包,而并不关心 ...

  7. 浏览器插件使用socks5代理

    服务端测试,经常会遇到需要通过代理访问的情景,比如公司内网不能访问测试环境,这时可以通过socks5代理来解决. 一.使用Chrome浏览器访问   1. 下载并安装SwitchyOmega插件   ...

  8. 为 pip install 设置 socks5 代理

    参考 How to use pip with socks proxy? 为 pip install 设置 socks5 代理 设置方法: pip install pysocks pip install ...

  9. 让终端走socks5代理

    (2017.9.17更新) 方法1: 在终端中直接运行命令 1 export http_proxy=http://proxyAddress:port 这个办法的好处是简单直接,并且影响面很小(只对当前 ...

  10. 配置Linux客户端使用socks5代理上网

    配置Linux客户端使用socks5代理上网   背景 有访问google或者其他海外网站需求的同学可能大都用过或者听过ss,在Windows.Mac.Android.IOS都有现成可用的客户端来协助 ...

随机推荐

  1. 5. Docker 本地镜像发布到阿里云

    5. Docker 本地镜像发布到阿里云 @ 目录 5. Docker 本地镜像发布到阿里云 1. 本地镜像发布到阿里云流程 最后: 1. 本地镜像发布到阿里云流程 镜像的生成方法: 基于当前容器创建 ...

  2. flutter-全局监听路由

    main.dart navigatorObservers: [ MyApp.routeObserver, GLObserver(),//全局监听路由 ], router_listener.dart c ...

  3. [PA2021] Od deski do deski 题解

    好题好题,难者不会会者不难,我是前者. 实际上加入就可以合法的数是很好计算的.考虑现在所有前缀合法串后的字符实际上都可以满足条件. 容易想到根据是否合法设置状态.设 \(f_{i,j}/g_{i,j} ...

  4. Powershell实现圆缩小放大 (实时刷新窗口)

    使用Powershell,创建实时刷新的窗口,绘制图形,这里以圆作为例子,做缩小放大动画. [分析] Powershell是windows内置的自动部署平台,功能强大在于可以调取.net框架,因此,即 ...

  5. curl使用总结

    1.请求源码 curl http://wttr.in/ 天气网站  2.文件下载 -o :保存文件 -s:禁用进度表 --progress-bar:让进度显示为进度条 -C - :断点连续下载 --l ...

  6. linux安装lspci

    点击查看代码 `lspci` 是一个用于在Linux系统中显示所有PCI总线以及已连接设备信息的命令.这个工具通常包含在 `pciutils` 包里.如果你需要在你的Linux系统上安装 `lspci ...

  7. 解密prompt系列50. RL用于优化Agent行为路径的一些思路

    OpenAI新推出的Deep Research功能,属实有些惊艳,也验证了去年的一些观点,之后的大模型工作流会呈现一些截然不同的形态,有敏捷型的例如语音端到端的及时对话,也会有异步长流程的复杂任务,去 ...

  8. gorm插入报错Error 1292 (22007): Incorrect datetime value: ‘0000-00-00‘ for column ‘xxx‘ at row 1

    在MySQL中,'0000-00-00 00:00:00'不是一个合法的DATETIME值.从MySQL 5.7.5开始,默认情况下不允许插入零日期或零时间值到DATETIME或 TIMESTAMP列 ...

  9. MySql 主从(备)部署 | 冷备份

    前言 MySQL 主从复制(Master-Slave Replication)是一种常见的数据库架构设计,用于提高数据可用性.实现读写分离以及支持备份策略.冷备份是指在数据库关闭状态下进行的数据备份方 ...

  10. [Qt基础-06] QButtonGroup

    QButtonGroup 本文主要根据QT官方帮助文档以及日常使用,简单的介绍一下QButtonGroup的功能以及基本使用 文章目录 QButtonGroup 简介 信号和槽 简介 有的时候,我们会 ...