抽象tcp拦截器

This commit is contained in:
陈国伟 2021-09-22 14:03:44 +08:00
parent d0dcd990fb
commit e5dcab793f
16 changed files with 224 additions and 253 deletions

View File

@ -10,6 +10,11 @@ namespace FastGithub.Configuration
/// </summary> /// </summary>
public static class ReverseProxyPort public static class ReverseProxyPort
{ {
/// <summary>
/// ssh端口
/// </summary>
public static int Ssh { get; } = GetAvailableTcpPort(22);
/// <summary> /// <summary>
/// http端口 /// http端口
/// </summary> /// </summary>

View File

@ -55,11 +55,12 @@ namespace FastGithub
/// <param name="kestrel"></param> /// <param name="kestrel"></param>
public static void ListenSshReverseProxy(this KestrelServerOptions kestrel) public static void ListenSshReverseProxy(this KestrelServerOptions kestrel)
{ {
const int SSH_PORT = 22; var sshPort = ReverseProxyPort.Ssh;
if (CanListenTcp(SSH_PORT) == true) kestrel.Listen(IPAddress.Loopback, sshPort, listen => listen.UseConnectionHandler<SshReverseProxyHandler>());
if (OperatingSystem.IsWindows())
{ {
kestrel.Listen(IPAddress.Loopback, SSH_PORT, listen => listen.UseConnectionHandler<SshReverseProxyHandler>()); kestrel.GetLogger().LogInformation($"已监听ssh://{IPAddress.Loopback}:{sshPort}github的ssh反向代理服务启动完成");
kestrel.GetLogger().LogInformation($"已监听ssh://{IPAddress.Loopback}:{SSH_PORT}github的ssh反向代理服务启动完成");
} }
} }
@ -70,9 +71,10 @@ namespace FastGithub
public static void ListenHttpReverseProxy(this KestrelServerOptions kestrel) public static void ListenHttpReverseProxy(this KestrelServerOptions kestrel)
{ {
var httpPort = ReverseProxyPort.Http; var httpPort = ReverseProxyPort.Http;
if (CanListenTcp(httpPort) == true) kestrel.Listen(IPAddress.Loopback, httpPort);
if (OperatingSystem.IsWindows())
{ {
kestrel.Listen(IPAddress.Loopback, httpPort);
kestrel.GetLogger().LogInformation($"已监听http://{IPAddress.Loopback}:{httpPort}http反向代理服务启动完成"); kestrel.GetLogger().LogInformation($"已监听http://{IPAddress.Loopback}:{httpPort}http反向代理服务启动完成");
} }
} }
@ -82,19 +84,13 @@ namespace FastGithub
/// </summary> /// </summary>
/// <param name="kestrel"></param> /// <param name="kestrel"></param>
/// <exception cref="FastGithubException"></exception> /// <exception cref="FastGithubException"></exception>
/// <returns></returns> public static void ListenHttpsReverseProxy(this KestrelServerOptions kestrel)
public static int ListenHttpsReverseProxy(this KestrelServerOptions kestrel)
{ {
var httpsPort = ReverseProxyPort.Https;
if (CanListenTcp(httpsPort) == false)
{
throw new FastGithubException($"tcp端口{httpsPort}已经被其它进程占用");
}
var certService = kestrel.ApplicationServices.GetRequiredService<CertService>(); var certService = kestrel.ApplicationServices.GetRequiredService<CertService>();
certService.CreateCaCertIfNotExists(); certService.CreateCaCertIfNotExists();
certService.InstallAndTrustCaCert(); certService.InstallAndTrustCaCert();
var httpsPort = ReverseProxyPort.Https;
kestrel.Listen(IPAddress.Loopback, httpsPort, kestrel.Listen(IPAddress.Loopback, httpsPort,
listen => listen.UseHttps(https => listen => listen.UseHttps(https =>
https.ServerCertificateSelector = (ctx, domain) => https.ServerCertificateSelector = (ctx, domain) =>
@ -105,8 +101,6 @@ namespace FastGithub
var logger = kestrel.GetLogger(); var logger = kestrel.GetLogger();
logger.LogInformation($"已监听https://{IPAddress.Loopback}:{httpsPort}https反向代理服务启动完成"); logger.LogInformation($"已监听https://{IPAddress.Loopback}:{httpsPort}https反向代理服务启动完成");
} }
return httpsPort;
} }
/// <summary> /// <summary>

View File

@ -4,7 +4,7 @@ using System.Runtime.Versioning;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace FastGithub.Dns namespace FastGithub.PacketIntercept
{ {
/// <summary> /// <summary>
/// dns拦截后台服务 /// dns拦截后台服务

View File

@ -13,7 +13,7 @@ using System.Runtime.Versioning;
using System.Threading; using System.Threading;
using WinDivertSharp; using WinDivertSharp;
namespace FastGithub.Dns namespace FastGithub.PacketIntercept
{ {
/// <summary> /// <summary>
/// dns拦截器 /// dns拦截器
@ -165,7 +165,7 @@ namespace FastGithub.Dns
} }
WinDivert.WinDivertHelperCalcChecksums(winDivertBuffer, packetLength, ref winDivertAddress, WinDivertChecksumHelperParam.All); WinDivert.WinDivertHelperCalcChecksums(winDivertBuffer, packetLength, ref winDivertAddress, WinDivertChecksumHelperParam.All);
this.logger.LogInformation($"已拦截dns查询{domain}并伪造解析结果为{IPAddress.Loopback}"); this.logger.LogInformation($"{domain} => {IPAddress.Loopback}");
} }

View File

@ -6,7 +6,7 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace FastGithub.Dns namespace FastGithub.PacketIntercept
{ {
/// <summary> /// <summary>
/// host文件冲解决者 /// host文件冲解决者

View File

@ -1,36 +0,0 @@
using Microsoft.Extensions.Hosting;
using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;
namespace FastGithub.Dns
{
/// <summary>
/// http拦截后台服务
/// </summary>
[SupportedOSPlatform("windows")]
sealed class HttpInterceptHostedService : BackgroundService
{
private readonly HttpInterceptor httpsInterceptor;
/// <summary>
/// http拦截后台服务
/// </summary>
/// <param name="httpInterceptor"></param>
public HttpInterceptHostedService(HttpInterceptor httpInterceptor)
{
this.httpsInterceptor = httpInterceptor;
}
/// <summary>
/// https后台
/// </summary>
/// <param name="stoppingToken"></param>
/// <returns></returns>
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await Task.Yield();
this.httpsInterceptor.Intercept(stoppingToken);
}
}
}

View File

@ -1,95 +1,22 @@
using FastGithub.Configuration; using FastGithub.Configuration;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System;
using System.Buffers.Binary;
using System.Runtime.Versioning; using System.Runtime.Versioning;
using System.Threading;
using WinDivertSharp;
namespace FastGithub.Dns namespace FastGithub.PacketIntercept
{ {
/// <summary> /// <summary>
/// http拦截器 /// http拦截器
/// </summary> /// </summary>
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
sealed class HttpInterceptor sealed class HttpInterceptor : TcpInterceptor
{ {
private readonly ILogger<HttpInterceptor> logger;
private readonly ushort http80Port = BinaryPrimitives.ReverseEndianness((ushort)80);
private readonly ushort httpReverseProxyPort = BinaryPrimitives.ReverseEndianness((ushort)ReverseProxyPort.Http);
/// <summary> /// <summary>
/// http拦截器 /// http拦截器
/// </summary> /// </summary>
/// <param name="logger"></param> /// <param name="logger"></param>
public HttpInterceptor(ILogger<HttpInterceptor> logger) public HttpInterceptor(ILogger<HttpInterceptor> logger)
: base(80, ReverseProxyPort.Http, logger)
{ {
this.logger = logger;
}
/// <summary>
/// 拦截80端口的数据包
/// </summary>
/// <param name="cancellationToken"></param>
public void Intercept(CancellationToken cancellationToken)
{
if (ReverseProxyPort.Http == 80)
{
return;
}
var filter = $"loopback and (tcp.DstPort == 80 or tcp.SrcPort == {ReverseProxyPort.Http})";
var handle = WinDivert.WinDivertOpen(filter, WinDivertLayer.Network, 0, WinDivertOpenFlags.None);
if (handle == IntPtr.Zero)
{
return;
}
cancellationToken.Register(hwnd => WinDivert.WinDivertClose((IntPtr)hwnd!), handle);
var packetLength = 0U;
using var winDivertBuffer = new WinDivertBuffer();
var winDivertAddress = new WinDivertAddress();
while (cancellationToken.IsCancellationRequested == false)
{
if (WinDivert.WinDivertRecv(handle, winDivertBuffer, ref winDivertAddress, ref packetLength))
{
try
{
this.ModifyHttpsPacket(winDivertBuffer, ref winDivertAddress, ref packetLength);
}
catch (Exception ex)
{
this.logger.LogWarning(ex.Message);
}
finally
{
WinDivert.WinDivertSend(handle, winDivertBuffer, packetLength, ref winDivertAddress);
}
}
}
}
/// <summary>
/// 443端口转发到https反向代理端口
/// </summary>
/// <param name="winDivertBuffer"></param>
/// <param name="winDivertAddress"></param>
/// <param name="packetLength"></param>
unsafe private void ModifyHttpsPacket(WinDivertBuffer winDivertBuffer, ref WinDivertAddress winDivertAddress, ref uint packetLength)
{
var packet = WinDivert.WinDivertHelperParsePacket(winDivertBuffer, packetLength);
if (packet.TcpHeader->DstPort == http80Port)
{
packet.TcpHeader->DstPort = this.httpReverseProxyPort;
}
else
{
packet.TcpHeader->SrcPort = http80Port;
}
winDivertAddress.Impostor = true;
WinDivert.WinDivertHelperCalcChecksums(winDivertBuffer, packetLength, ref winDivertAddress, WinDivertChecksumHelperParam.All);
} }
} }
} }

View File

@ -1,36 +0,0 @@
using Microsoft.Extensions.Hosting;
using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;
namespace FastGithub.Dns
{
/// <summary>
/// https拦截后台服务
/// </summary>
[SupportedOSPlatform("windows")]
sealed class HttpsInterceptHostedService : BackgroundService
{
private readonly HttpsInterceptor httpsInterceptor;
/// <summary>
/// https拦截后台服务
/// </summary>
/// <param name="httpsInterceptor"></param>
public HttpsInterceptHostedService(HttpsInterceptor httpsInterceptor)
{
this.httpsInterceptor = httpsInterceptor;
}
/// <summary>
/// https后台
/// </summary>
/// <param name="stoppingToken"></param>
/// <returns></returns>
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await Task.Yield();
this.httpsInterceptor.Intercept(stoppingToken);
}
}
}

View File

@ -1,95 +1,22 @@
using FastGithub.Configuration; using FastGithub.Configuration;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System;
using System.Buffers.Binary;
using System.Runtime.Versioning; using System.Runtime.Versioning;
using System.Threading;
using WinDivertSharp;
namespace FastGithub.Dns namespace FastGithub.PacketIntercept
{ {
/// <summary> /// <summary>
/// https拦截器 /// https拦截器
/// </summary> /// </summary>
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
sealed class HttpsInterceptor sealed class HttpsInterceptor : TcpInterceptor
{ {
private readonly ILogger<HttpsInterceptor> logger;
private readonly ushort https443Port = BinaryPrimitives.ReverseEndianness((ushort)443);
private readonly ushort httpsReverseProxyPort = BinaryPrimitives.ReverseEndianness((ushort)ReverseProxyPort.Https);
/// <summary> /// <summary>
/// https拦截器 /// https拦截器
/// </summary> /// </summary>
/// <param name="logger"></param> /// <param name="logger"></param>
public HttpsInterceptor(ILogger<HttpsInterceptor> logger) public HttpsInterceptor(ILogger<HttpsInterceptor> logger)
: base(443, ReverseProxyPort.Https, logger)
{ {
this.logger = logger;
}
/// <summary>
/// 拦截443端口的数据包
/// </summary>
/// <param name="cancellationToken"></param>
public void Intercept(CancellationToken cancellationToken)
{
if (ReverseProxyPort.Https == 443)
{
return;
}
var filter = $"loopback and (tcp.DstPort == 443 or tcp.SrcPort == {ReverseProxyPort.Https})";
var handle = WinDivert.WinDivertOpen(filter, WinDivertLayer.Network, 0, WinDivertOpenFlags.None);
if (handle == IntPtr.Zero)
{
return;
}
cancellationToken.Register(hwnd => WinDivert.WinDivertClose((IntPtr)hwnd!), handle);
var packetLength = 0U;
using var winDivertBuffer = new WinDivertBuffer();
var winDivertAddress = new WinDivertAddress();
while (cancellationToken.IsCancellationRequested == false)
{
if (WinDivert.WinDivertRecv(handle, winDivertBuffer, ref winDivertAddress, ref packetLength))
{
try
{
this.ModifyHttpsPacket(winDivertBuffer, ref winDivertAddress, ref packetLength);
}
catch (Exception ex)
{
this.logger.LogWarning(ex.Message);
}
finally
{
WinDivert.WinDivertSend(handle, winDivertBuffer, packetLength, ref winDivertAddress);
}
}
}
}
/// <summary>
/// 443端口转发到https反向代理端口
/// </summary>
/// <param name="winDivertBuffer"></param>
/// <param name="winDivertAddress"></param>
/// <param name="packetLength"></param>
unsafe private void ModifyHttpsPacket(WinDivertBuffer winDivertBuffer, ref WinDivertAddress winDivertAddress, ref uint packetLength)
{
var packet = WinDivert.WinDivertHelperParsePacket(winDivertBuffer, packetLength);
if (packet.TcpHeader->DstPort == https443Port)
{
packet.TcpHeader->DstPort = this.httpsReverseProxyPort;
}
else
{
packet.TcpHeader->SrcPort = https443Port;
}
winDivertAddress.Impostor = true;
WinDivert.WinDivertHelperCalcChecksums(winDivertBuffer, packetLength, ref winDivertAddress, WinDivertChecksumHelperParam.All);
} }
} }
} }

View File

@ -1,7 +1,7 @@
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace FastGithub.Dns namespace FastGithub.PacketIntercept
{ {
/// <summary> /// <summary>
/// Dns冲突解决者 /// Dns冲突解决者

View File

@ -0,0 +1,16 @@
using System.Threading;
namespace FastGithub.PacketIntercept
{
/// <summary>
/// tcp拦截器接口
/// </summary>
interface ITcpInterceptor
{
/// <summary>
/// 拦截tcp
/// </summary>
/// <param name="cancellationToken"></param>
void Intercept(CancellationToken cancellationToken);
}
}

View File

@ -11,7 +11,7 @@ using System.Runtime.Versioning;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace FastGithub.Dns namespace FastGithub.PacketIntercept
{ {
/// <summary> /// <summary>
/// 代理冲突解决者 /// 代理冲突解决者

View File

@ -1,4 +1,4 @@
using FastGithub.Dns; using FastGithub.PacketIntercept;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using System.Runtime.Versioning; using System.Runtime.Versioning;
@ -20,14 +20,15 @@ namespace FastGithub
{ {
services.AddSingleton<IConflictSolver, HostsConflictSolver>(); services.AddSingleton<IConflictSolver, HostsConflictSolver>();
services.AddSingleton<IConflictSolver, ProxyConflictSolver>(); services.AddSingleton<IConflictSolver, ProxyConflictSolver>();
services.TryAddSingleton<DnsInterceptor>(); services.TryAddSingleton<DnsInterceptor>();
services.TryAddSingleton<HttpInterceptor>();
services.TryAddSingleton<HttpsInterceptor>();
services.AddHostedService<DnsInterceptHostedService>(); services.AddHostedService<DnsInterceptHostedService>();
services.AddHostedService<HttpInterceptHostedService>();
return services.AddHostedService<HttpsInterceptHostedService>(); services.AddSingleton<ITcpInterceptor, SshInterceptor>();
services.AddSingleton<ITcpInterceptor, HttpInterceptor>();
services.AddSingleton<ITcpInterceptor, HttpsInterceptor>();
services.AddHostedService<TcpInterceptHostedService>();
return services;
} }
} }
} }

View File

@ -0,0 +1,22 @@
using FastGithub.Configuration;
using Microsoft.Extensions.Logging;
using System.Runtime.Versioning;
namespace FastGithub.PacketIntercept
{
/// <summary>
/// ssh拦截器
/// </summary>
[SupportedOSPlatform("windows")]
sealed class SshInterceptor : TcpInterceptor
{
/// <summary>
/// ssh拦截器
/// </summary>
/// <param name="logger"></param>
public SshInterceptor(ILogger<HttpInterceptor> logger)
: base(22, ReverseProxyPort.Ssh, logger)
{
}
}
}

View File

@ -0,0 +1,50 @@
using Microsoft.Extensions.Hosting;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;
namespace FastGithub.PacketIntercept
{
/// <summary>
/// tcp拦截后台服务
/// </summary>
[SupportedOSPlatform("windows")]
sealed class TcpInterceptHostedService : BackgroundService
{
private readonly IEnumerable<ITcpInterceptor> tcpInterceptors;
/// <summary>
/// tcp拦截后台服务
/// </summary>
/// <param name="tcpInterceptors"></param>
public TcpInterceptHostedService(IEnumerable<ITcpInterceptor> tcpInterceptors)
{
this.tcpInterceptors = tcpInterceptors;
}
/// <summary>
/// https后台
/// </summary>
/// <param name="stoppingToken"></param>
/// <returns></returns>
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
var tasks = this.tcpInterceptors.Select(item => this.InterceptAsync(item, stoppingToken));
return Task.WhenAll(tasks);
}
/// <summary>
/// 拦截
/// </summary>
/// <param name="interceptor"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private async Task InterceptAsync(ITcpInterceptor interceptor, CancellationToken cancellationToken)
{
await Task.Yield();
interceptor.Intercept(cancellationToken);
}
}
}

View File

@ -0,0 +1,101 @@
using Microsoft.Extensions.Logging;
using System;
using System.Buffers.Binary;
using System.Net;
using System.Runtime.Versioning;
using System.Threading;
using WinDivertSharp;
namespace FastGithub.PacketIntercept
{
/// <summary>
/// tcp拦截器
/// </summary>
[SupportedOSPlatform("windows")]
abstract class TcpInterceptor : ITcpInterceptor
{
private readonly string filter;
private readonly ushort oldServerPort;
private readonly ushort newServerPort;
private readonly ILogger logger;
/// <summary>
/// tcp拦截器
/// </summary>
/// <param name="oldServerPort">修改前的服务器端口</param>
/// <param name="newServerPort">修改后的服务器端口</param>
/// <param name="logger"></param>
public TcpInterceptor(int oldServerPort, int newServerPort, ILogger logger)
{
this.filter = $"loopback and (tcp.DstPort == {oldServerPort} or tcp.SrcPort == {newServerPort})";
this.oldServerPort = BinaryPrimitives.ReverseEndianness((ushort)oldServerPort);
this.newServerPort = BinaryPrimitives.ReverseEndianness((ushort)newServerPort);
this.logger = logger;
}
/// <summary>
/// 拦截指定端口的数据包
/// </summary>
/// <param name="cancellationToken"></param>
public void Intercept(CancellationToken cancellationToken)
{
if (this.oldServerPort == this.newServerPort)
{
return;
}
var handle = WinDivert.WinDivertOpen(this.filter, WinDivertLayer.Network, 0, WinDivertOpenFlags.None);
if (handle == IntPtr.Zero)
{
return;
}
this.logger.LogInformation($"tcp://{IPAddress.Loopback}:{BinaryPrimitives.ReverseEndianness(this.oldServerPort)} => tcp://{IPAddress.Loopback}:{BinaryPrimitives.ReverseEndianness(this.newServerPort)}");
cancellationToken.Register(hwnd => WinDivert.WinDivertClose((IntPtr)hwnd!), handle);
var packetLength = 0U;
using var winDivertBuffer = new WinDivertBuffer();
var winDivertAddress = new WinDivertAddress();
while (cancellationToken.IsCancellationRequested == false)
{
if (WinDivert.WinDivertRecv(handle, winDivertBuffer, ref winDivertAddress, ref packetLength))
{
try
{
this.ModifyTcpPacket(winDivertBuffer, ref winDivertAddress, ref packetLength);
}
catch (Exception ex)
{
this.logger.LogWarning(ex.Message);
}
finally
{
WinDivert.WinDivertSend(handle, winDivertBuffer, packetLength, ref winDivertAddress);
}
}
}
}
/// <summary>
/// 修改tcp数据端口的端口
/// </summary>
/// <param name="winDivertBuffer"></param>
/// <param name="winDivertAddress"></param>
/// <param name="packetLength"></param>
unsafe private void ModifyTcpPacket(WinDivertBuffer winDivertBuffer, ref WinDivertAddress winDivertAddress, ref uint packetLength)
{
var packet = WinDivert.WinDivertHelperParsePacket(winDivertBuffer, packetLength);
if (packet.TcpHeader->DstPort == oldServerPort)
{
packet.TcpHeader->DstPort = this.newServerPort;
}
else
{
packet.TcpHeader->SrcPort = oldServerPort;
}
winDivertAddress.Impostor = true;
WinDivert.WinDivertHelperCalcChecksums(winDivertBuffer, packetLength, ref winDivertAddress, WinDivertChecksumHelperParam.All);
}
}
}