mirror of
https://github.com/snltty/linker.git
synced 2025-09-27 05:25:57 +08:00
代理的DNAT和webapi
This commit is contained in:
@@ -63,7 +63,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "linker.ics", "src\linker.ic
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "linker.messenger.plan", "src\linker.messenger.plan\linker.messenger.plan.csproj", "{5649D02E-200B-45E0-A82F-8EBE76CF96C6}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "linker.snat", "src\linker.snat\linker.snat.csproj", "{A1EA64AA-8C30-4616-B65D-8AF07641807E}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "linker.nat", "src\linker.nat\linker.nat.csproj", "{A1EA64AA-8C30-4616-B65D-8AF07641807E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "linker.messenger.firewall", "src\linker.messenger.firewall\linker.messenger.firewall.csproj", "{F97DB5A9-3807-4441-A520-7B1211C1CE8A}"
|
||||
EndProject
|
||||
|
@@ -16,7 +16,7 @@ using linker.libs.web;
|
||||
using linker.messenger.entry;
|
||||
using linker.messenger.tuntap;
|
||||
using linker.messenger.updater;
|
||||
using linker.tun;
|
||||
using linker.tun.device;
|
||||
using linker.tunnel.connection;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
1
src/linker.app/public/web/css/182.ad831e91.css
Normal file
1
src/linker.app/public/web/css/182.ad831e91.css
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>linker.web</title><link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/><script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script><script defer="defer" src="js/chunk-vendors.e9b4f2f4.js"></script><script defer="defer" src="js/app.166579fa.js"></script><link href="css/chunk-vendors.d8267b33.css" rel="stylesheet"><link href="css/app.e16885f2.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but linker.web doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
|
||||
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>linker.web</title><link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/><script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script><script defer="defer" src="js/chunk-vendors.e9b4f2f4.js"></script><script defer="defer" src="js/app.a3656579.js"></script><link href="css/chunk-vendors.d8267b33.css" rel="stylesheet"><link href="css/app.e16885f2.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but linker.web doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
|
1
src/linker.app/public/web/js/182.a9bc4709.js
Normal file
1
src/linker.app/public/web/js/182.a9bc4709.js
Normal file
File diff suppressed because one or more lines are too long
1
src/linker.app/public/web/js/app.a3656579.js
Normal file
1
src/linker.app/public/web/js/app.a3656579.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1,15 +1,93 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace linker.libs
|
||||
{
|
||||
public sealed class ChecksumHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 清空IP包的校验和
|
||||
/// </summary>
|
||||
/// <param name="ptr"></param>
|
||||
/// <param name="ipHeader">是否情况IP头校验和</param>
|
||||
/// <param name="payload">是否清空荷载协议校验和</param>
|
||||
public static unsafe void ClearChecksum(byte* ptr, bool ipHeader = true, bool payload = true)
|
||||
{
|
||||
byte ipHeaderLength = (byte)((*ptr & 0b1111) * 4);
|
||||
byte* packetPtr = ptr + ipHeaderLength;
|
||||
|
||||
if (ipHeader)
|
||||
{
|
||||
*(ushort*)(ptr + 10) = 0;
|
||||
}
|
||||
if (payload)
|
||||
{
|
||||
int index = (ProtocolType)(*(ptr + 9)) switch
|
||||
{
|
||||
ProtocolType.Icmp => 2,
|
||||
ProtocolType.Tcp => 16,
|
||||
ProtocolType.Udp => 6,
|
||||
_ => -1,
|
||||
};
|
||||
if (index > 0)
|
||||
{
|
||||
*(ushort*)(packetPtr + index) = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 计算IP包的校验和,当校验和为0时才计算
|
||||
/// </summary>
|
||||
/// <param name="packet">一个完整的IP包</param>
|
||||
/// <param name="ipHeader">是否计算IP头校验和</param>
|
||||
/// <param name="payload">是否计算荷载协议校验和</param>
|
||||
public static unsafe void ChecksumWithZero(ReadOnlyMemory<byte> packet, bool ipHeader = true, bool payload = true)
|
||||
{
|
||||
ChecksumWithZero(packet.Span, ipHeader, payload);
|
||||
}
|
||||
/// <summary>
|
||||
/// 计算IP包的校验和,当校验和为0时才计算
|
||||
/// </summary>
|
||||
/// <param name="packet">一个完整的IP包</param>
|
||||
/// <param name="ipHeader">是否计算IP头校验和</param>
|
||||
/// <param name="payload">是否计算荷载协议校验和</param>
|
||||
public static unsafe void ChecksumWithZero(ReadOnlySpan<byte> packet, bool ipHeader = true, bool payload = true)
|
||||
{
|
||||
fixed (byte* ptr = packet)
|
||||
{
|
||||
ChecksumWithZero(ptr, ipHeader, payload);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 计算IP包的校验和,当校验和为0时才计算
|
||||
/// </summary>
|
||||
/// <param name="ptr">IP包指针</param>
|
||||
/// <param name="ipHeader">是否计算IP头校验和</param>
|
||||
/// <param name="payload">是否计算荷载协议校验和</param>
|
||||
public static unsafe void ChecksumWithZero(byte* ptr, bool ipHeader = true, bool payload = true)
|
||||
{
|
||||
byte ipHeaderLength = (byte)((*ptr & 0b1111) * 4);
|
||||
byte* packetPtr = ptr + ipHeaderLength;
|
||||
|
||||
ipHeader = ipHeader && *(ushort*)(ptr + 10) == 0;
|
||||
payload = payload && ((ProtocolType)(*(ptr + 9)) switch
|
||||
{
|
||||
ProtocolType.Icmp => *(ushort*)(packetPtr + 2) == 0,
|
||||
ProtocolType.Tcp => *(ushort*)(packetPtr + 16) == 0,
|
||||
ProtocolType.Udp => *(ushort*)(packetPtr + 6) == 0,
|
||||
_ => false,
|
||||
});
|
||||
if (ipHeader || payload)
|
||||
Checksum(ptr, ipHeader, payload);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算IP包的校验和
|
||||
/// </summary>
|
||||
/// <param name="packet">一个完整的IP包</param>
|
||||
/// <param name="ipHeader">是否计算IP头校验和</param>
|
||||
/// <param name="payload">是否计算荷载协议校验和</param>
|
||||
public static unsafe void Checksum(ReadOnlyMemory<byte> packet, bool ipHeader = true, bool payload = true)
|
||||
{
|
||||
@@ -19,6 +97,7 @@ namespace linker.libs
|
||||
/// 计算IP包的校验和
|
||||
/// </summary>
|
||||
/// <param name="packet">一个完整的IP包</param>
|
||||
/// <param name="ipHeader">是否计算IP头校验和</param>
|
||||
/// <param name="payload">是否计算荷载协议校验和</param>
|
||||
public static unsafe void Checksum(ReadOnlySpan<byte> packet, bool ipHeader = true, bool payload = true)
|
||||
{
|
||||
@@ -31,7 +110,7 @@ namespace linker.libs
|
||||
/// 计算IP包的校验和
|
||||
/// </summary>
|
||||
/// <param name="ptr">IP包指针</param>
|
||||
/// <param name="ipHeader">是否计算IP校验和</param>
|
||||
/// <param name="ipHeader">是否计算IP头校验和</param>
|
||||
/// <param name="payload">是否计算荷载协议校验和</param>
|
||||
public static unsafe void Checksum(byte* ptr, bool ipHeader = true, bool payload = true)
|
||||
{
|
||||
|
27
src/linker.libs/web/IWebApiServer.cs
Normal file
27
src/linker.libs/web/IWebApiServer.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace linker.libs.web
|
||||
{
|
||||
/// <summary>
|
||||
/// web服务
|
||||
/// </summary>
|
||||
public interface IWebApiServer
|
||||
{
|
||||
/// <summary>
|
||||
/// 开始
|
||||
/// </summary>
|
||||
public void Start(int port);
|
||||
|
||||
public void AddController(IWebApiController controller);
|
||||
public void AddControllers(List<IWebApiController> controllers);
|
||||
}
|
||||
|
||||
public interface IWebApiController
|
||||
{
|
||||
public string Path { get; }
|
||||
public Memory<byte> Handle(string query);
|
||||
public void Free();
|
||||
}
|
||||
|
||||
}
|
119
src/linker.libs/web/WebApiServer.cs
Normal file
119
src/linker.libs/web/WebApiServer.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace linker.libs.web
|
||||
{
|
||||
/// <summary>
|
||||
/// 本地web api 服务器
|
||||
/// </summary>
|
||||
public class WebApiServer : IWebApiServer
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, IWebApiController> dic = new();
|
||||
|
||||
public WebApiServer()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开启web api
|
||||
/// </summary>
|
||||
public void Start(int port)
|
||||
{
|
||||
Task.Factory.StartNew(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
HttpListener http = new HttpListener();
|
||||
http.IgnoreWriteExceptions = true;
|
||||
http.Prefixes.Add($"http://+:{port}/");
|
||||
http.Start();
|
||||
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
HttpListenerContext context = await http.GetContextAsync();
|
||||
HandleWeb(context);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.Instance.Error(ex);
|
||||
}
|
||||
}, TaskCreationOptions.LongRunning);
|
||||
}
|
||||
|
||||
public void AddController(IWebApiController controller)
|
||||
{
|
||||
if (controller == null || string.IsNullOrWhiteSpace(controller.Path)) return;
|
||||
|
||||
dic.TryAdd(controller.Path.ToLower(), controller);
|
||||
}
|
||||
public void AddControllers(List<IWebApiController> controllers)
|
||||
{
|
||||
foreach (var controller in controllers)
|
||||
{
|
||||
if (controller == null || string.IsNullOrWhiteSpace(controller.Path)) continue;
|
||||
|
||||
dic.TryAdd(controller.Path.ToLower(), controller);
|
||||
}
|
||||
}
|
||||
private void HandleWeb(HttpListenerContext context)
|
||||
{
|
||||
HttpListenerRequest request = context.Request;
|
||||
HttpListenerResponse response = context.Response;
|
||||
try
|
||||
{
|
||||
response.Headers.Set("Server", Helper.GlobalString);
|
||||
|
||||
string path = request.Url.AbsolutePath.ToLower();
|
||||
string query = request.Url.Query;
|
||||
//默认页面
|
||||
if (path == "/") path = "online.json";
|
||||
|
||||
try
|
||||
{
|
||||
if (dic.TryGetValue(path, out IWebApiController controller))
|
||||
{
|
||||
Memory<byte> memory = controller.Handle(query);
|
||||
response.ContentLength64 = memory.Length;
|
||||
response.ContentType = "application/json";
|
||||
response.OutputStream.Write(memory.Span);
|
||||
response.OutputStream.Flush();
|
||||
response.OutputStream.Close();
|
||||
|
||||
controller.Free();
|
||||
}
|
||||
else
|
||||
{
|
||||
response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(ex + $"");
|
||||
response.ContentLength64 = bytes.Length;
|
||||
response.ContentType = "text/plain; charset=utf-8";
|
||||
response.OutputStream.Write(bytes, 0, bytes.Length);
|
||||
response.OutputStream.Flush();
|
||||
response.OutputStream.Close();
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
response.StatusCode = (int)HttpStatusCode.BadRequest;
|
||||
}
|
||||
|
||||
response.Close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -4,7 +4,8 @@ using linker.messenger.firewall.hooks;
|
||||
using linker.messenger.forward.proxy;
|
||||
using linker.messenger.socks5;
|
||||
using linker.messenger.sync;
|
||||
using linker.snat;
|
||||
using linker.nat;
|
||||
using linker.tun.hook;
|
||||
using linker.tun;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
using linker.libs;
|
||||
using linker.messenger.signin;
|
||||
using linker.snat;
|
||||
using linker.nat;
|
||||
|
||||
namespace linker.messenger.firewall
|
||||
{
|
||||
|
@@ -1,5 +1,5 @@
|
||||
using linker.messenger.signin;
|
||||
using linker.snat;
|
||||
using linker.nat;
|
||||
|
||||
namespace linker.messenger.firewall
|
||||
{
|
||||
|
@@ -1,4 +1,4 @@
|
||||
using linker.snat;
|
||||
using linker.nat;
|
||||
|
||||
namespace linker.messenger.firewall
|
||||
{
|
||||
@@ -17,7 +17,7 @@ namespace linker.messenger.firewall
|
||||
public bool Check(FirewallCheckInfo info);
|
||||
}
|
||||
|
||||
public sealed class FirewallRuleInfo : linker.snat.LinkerFirewallRuleInfo
|
||||
public sealed class FirewallRuleInfo : linker.nat.LinkerFirewallRuleInfo
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string GroupId { get; set; }
|
||||
|
@@ -1,6 +1,6 @@
|
||||
|
||||
using linker.messenger.forward.proxy;
|
||||
using linker.snat;
|
||||
using linker.nat;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
using linker.messenger.socks5;
|
||||
using linker.snat;
|
||||
using linker.nat;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
using linker.snat;
|
||||
using linker.tun;
|
||||
using linker.nat;
|
||||
using linker.tun.hook;
|
||||
|
||||
namespace linker.messenger.firewall.hooks
|
||||
{
|
||||
@@ -13,13 +13,13 @@ namespace linker.messenger.firewall.hooks
|
||||
this.linkerFirewall = linkerFirewall;
|
||||
}
|
||||
|
||||
public unsafe bool ReadAfter(ReadOnlyMemory<byte> packet)
|
||||
public unsafe bool Read(ReadOnlyMemory<byte> packet)
|
||||
{
|
||||
linkerFirewall.AddAllow(packet);
|
||||
return true;
|
||||
}
|
||||
|
||||
public unsafe bool WriteBefore(string srcId, ReadOnlyMemory<byte> packet)
|
||||
public unsafe bool Write(string srcId, ReadOnlyMemory<byte> packet)
|
||||
{
|
||||
return linkerFirewall.Check(srcId, packet);
|
||||
}
|
||||
|
@@ -44,7 +44,8 @@
|
||||
<ProjectReference Include="..\linker.messenger.socks5\linker.messenger.socks5.csproj" />
|
||||
<ProjectReference Include="..\linker.messenger.sync\linker.messenger.sync.csproj" />
|
||||
<ProjectReference Include="..\linker.messenger\linker.messenger.csproj" />
|
||||
<ProjectReference Include="..\linker.snat\linker.snat.csproj" />
|
||||
<ProjectReference Include="..\linker.nat\linker.nat.csproj" />
|
||||
<ProjectReference Include="..\linker.snat\linker.nat.csproj" />
|
||||
<ProjectReference Include="..\linker.tun\linker.tun.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
using linker.libs.web;
|
||||
using linker.messenger.flow.history;
|
||||
using linker.messenger.flow.messenger;
|
||||
using linker.messenger.flow.webapi;
|
||||
using linker.messenger.forward.proxy;
|
||||
using linker.messenger.relay.server;
|
||||
using linker.messenger.socks5;
|
||||
@@ -85,6 +86,10 @@ namespace linker.messenger.flow
|
||||
serviceCollection.AddSingleton<FlowHistoryTransfer>();
|
||||
|
||||
|
||||
serviceCollection.AddSingleton<WebApiOnlineController>();
|
||||
serviceCollection.AddSingleton<WebApiCitysController>();
|
||||
|
||||
|
||||
return serviceCollection;
|
||||
}
|
||||
public static ServiceProvider UseFlowServer(this ServiceProvider serviceProvider)
|
||||
@@ -108,6 +113,11 @@ namespace linker.messenger.flow
|
||||
resolverTransfer.AddResolvers(new List<IResolver> { serviceProvider.GetService<FlowResolver>() });
|
||||
|
||||
//FlowHistoryTransfer flowHistoryTransfer = serviceProvider.GetService<FlowHistoryTransfer>();
|
||||
IWebApiServer webApiServer = serviceProvider.GetService<IWebApiServer>();
|
||||
webApiServer.AddControllers(new List<IWebApiController> {
|
||||
serviceProvider.GetService<WebApiOnlineController>(),
|
||||
serviceProvider.GetService<WebApiCitysController>(),
|
||||
});
|
||||
|
||||
return serviceProvider;
|
||||
}
|
||||
|
@@ -5,7 +5,7 @@ using linker.messenger.relay.client;
|
||||
using linker.messenger.signin;
|
||||
using linker.messenger.tuntap;
|
||||
using linker.messenger.tuntap.cidr;
|
||||
using linker.tun;
|
||||
using linker.nat;
|
||||
using linker.tunnel;
|
||||
using linker.tunnel.connection;
|
||||
using System.Collections.Concurrent;
|
||||
|
@@ -47,5 +47,6 @@
|
||||
<ProjectReference Include="..\linker.messenger.tunnel\linker.messenger.tunnel.csproj" />
|
||||
<ProjectReference Include="..\linker.messenger.tuntap\linker.messenger.tuntap.csproj" />
|
||||
<ProjectReference Include="..\linker.messenger\linker.messenger.csproj" />
|
||||
<ProjectReference Include="..\linker.nat\linker.nat.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
24
src/linker.messenger.flow/webapi/WebApiCitysController.cs
Normal file
24
src/linker.messenger.flow/webapi/WebApiCitysController.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using linker.libs.extends;
|
||||
using linker.libs.web;
|
||||
using linker.messenger.signin;
|
||||
namespace linker.messenger.flow.webapi
|
||||
{
|
||||
public sealed class WebApiCitysController : IWebApiController
|
||||
{
|
||||
public string Path => "/flow/citys.json";
|
||||
|
||||
private readonly FlowResolver flowResolver;
|
||||
public WebApiCitysController(FlowResolver flowResolver)
|
||||
{
|
||||
this.flowResolver = flowResolver;
|
||||
}
|
||||
public Memory<byte> Handle(string query)
|
||||
{
|
||||
return flowResolver.GetCitys().ToJson().ToBytes();
|
||||
}
|
||||
|
||||
public void Free()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
40
src/linker.messenger.flow/webapi/WebApiOnlineController.cs
Normal file
40
src/linker.messenger.flow/webapi/WebApiOnlineController.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using linker.libs.extends;
|
||||
using linker.libs.web;
|
||||
using linker.messenger.signin;
|
||||
namespace linker.messenger.flow.webapi
|
||||
{
|
||||
public sealed class WebApiOnlineController : IWebApiController
|
||||
{
|
||||
public string Path => "/flow/online.json";
|
||||
|
||||
private readonly SignInServerCaching signCaching;
|
||||
private readonly FlowResolver flowResolver;
|
||||
public WebApiOnlineController(SignInServerCaching signCaching, FlowResolver flowResolver)
|
||||
{
|
||||
this.signCaching = signCaching;
|
||||
this.flowResolver = flowResolver;
|
||||
}
|
||||
public Memory<byte> Handle(string query)
|
||||
{
|
||||
signCaching.GetOnline(out int all, out int online);
|
||||
return new
|
||||
{
|
||||
CurrentServer = new
|
||||
{
|
||||
Online7day = all,
|
||||
Online = online,
|
||||
},
|
||||
AllServer = new
|
||||
{
|
||||
Online7day = flowResolver.ReceiveBytes & 0xffffffff,
|
||||
Online = flowResolver.ReceiveBytes >> 32,
|
||||
Server = flowResolver.SendtBytes,
|
||||
}
|
||||
}.ToJson().ToBytes();
|
||||
}
|
||||
|
||||
public void Free()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
using linker.libs;
|
||||
using linker.libs.web;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
namespace linker.messenger.listen
|
||||
{
|
||||
@@ -7,11 +8,13 @@ namespace linker.messenger.listen
|
||||
public static ServiceCollection AddListen(this ServiceCollection serviceCollection)
|
||||
{
|
||||
serviceCollection.AddSingleton<TcpServer>();
|
||||
serviceCollection.AddSingleton<IWebApiServer,WebApiServer>();
|
||||
return serviceCollection;
|
||||
}
|
||||
public static ServiceProvider UseListen(this ServiceProvider serviceProvider)
|
||||
{
|
||||
TcpServer tcpServer = serviceProvider.GetService<TcpServer>();
|
||||
IWebApiServer webapiServer = serviceProvider.GetService<IWebApiServer>();
|
||||
IListenStore listenStore = serviceProvider.GetService<IListenStore>();
|
||||
|
||||
LoggerHelper.Instance.Info($"start server");
|
||||
@@ -25,6 +28,20 @@ namespace linker.messenger.listen
|
||||
}
|
||||
LoggerHelper.Instance.Warning($"server listen:{listenStore.Port}");
|
||||
|
||||
if (listenStore.ApiPort > 0)
|
||||
{
|
||||
LoggerHelper.Instance.Info($"start server web api");
|
||||
try
|
||||
{
|
||||
webapiServer.Start(listenStore.ApiPort);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.Instance.Error(ex);
|
||||
}
|
||||
LoggerHelper.Instance.Warning($"server web api listen:{listenStore.Port}");
|
||||
}
|
||||
|
||||
return serviceProvider;
|
||||
}
|
||||
}
|
||||
|
@@ -6,12 +6,14 @@
|
||||
/// 端口
|
||||
/// </summary>
|
||||
public int Port { get; }
|
||||
public int ApiPort { get; }
|
||||
/// <summary>
|
||||
/// 设置端口
|
||||
/// </summary>
|
||||
/// <param name="port"></param>
|
||||
/// <returns></returns>
|
||||
public bool SetPort(int port);
|
||||
public bool SetApiPort(int port);
|
||||
/// <summary>
|
||||
/// 提交
|
||||
/// </summary>
|
||||
|
@@ -3,6 +3,7 @@ using linker.messenger.relay.client;
|
||||
using linker.messenger.relay.messenger;
|
||||
using linker.messenger.relay.server;
|
||||
using linker.messenger.relay.server.validator;
|
||||
using linker.messenger.relay.webapi;
|
||||
using linker.messenger.sync;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
namespace linker.messenger.relay
|
||||
@@ -54,6 +55,8 @@ namespace linker.messenger.relay
|
||||
serviceCollection.AddSingleton<IRelayServerWhiteListStore, RelayServerWhiteListStore>();
|
||||
serviceCollection.AddSingleton<IRelayServerCdkeyStore, RelayServerCdkeyStore>();
|
||||
|
||||
|
||||
serviceCollection.AddSingleton<WebApiRelayNodesController>();
|
||||
return serviceCollection;
|
||||
}
|
||||
public static ServiceProvider UseRelayServer(this ServiceProvider serviceProvider)
|
||||
@@ -70,6 +73,10 @@ namespace linker.messenger.relay
|
||||
|
||||
RelayServerNodeTransfer relayServerNodeTransfer = serviceProvider.GetService<RelayServerNodeTransfer>();
|
||||
RelayServerMasterTransfer relayServerMasterTransfer = serviceProvider.GetService<RelayServerMasterTransfer>();
|
||||
|
||||
IWebApiServer webApiServer = serviceProvider.GetService<IWebApiServer>();
|
||||
webApiServer.AddController(serviceProvider.GetService<WebApiRelayNodesController>());
|
||||
|
||||
return serviceProvider;
|
||||
}
|
||||
}
|
||||
|
@@ -199,6 +199,22 @@ namespace linker.messenger.relay.server
|
||||
.ThenByDescending(x => x.MaxGbTotalLastBytes == 0 ? long.MaxValue : x.MaxGbTotalLastBytes)
|
||||
.ToList();
|
||||
}
|
||||
public List<RelayServerNodeReportInfo188> GetPublicNodes()
|
||||
{
|
||||
var result = reports.Values
|
||||
.Where(c => Environment.TickCount64 - c.LastTicks < 15000)
|
||||
.Where(c => c.Public)
|
||||
.OrderByDescending(c => c.LastTicks);
|
||||
|
||||
return result.OrderByDescending(x => x.MaxConnection == 0 ? int.MaxValue : x.MaxConnection)
|
||||
.ThenBy(x => x.ConnectionRatio)
|
||||
.ThenBy(x => x.BandwidthRatio)
|
||||
.ThenByDescending(x => x.MaxBandwidth == 0 ? double.MaxValue : x.MaxBandwidth)
|
||||
.ThenByDescending(x => x.MaxBandwidthTotal == 0 ? double.MaxValue : x.MaxBandwidthTotal)
|
||||
.ThenByDescending(x => x.MaxGbTotal == 0 ? double.MaxValue : x.MaxGbTotal)
|
||||
.ThenByDescending(x => x.MaxGbTotalLastBytes == 0 ? long.MaxValue : x.MaxGbTotalLastBytes)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@@ -0,0 +1,48 @@
|
||||
using linker.libs.extends;
|
||||
using linker.libs.web;
|
||||
using linker.messenger.relay.server;
|
||||
|
||||
namespace linker.messenger.relay.webapi
|
||||
{
|
||||
public sealed class WebApiRelayNodesController : IWebApiController
|
||||
{
|
||||
public string Path => "/relay/nodes.json";
|
||||
|
||||
private readonly RelayServerMasterTransfer relayServerMasterTransfer;
|
||||
public WebApiRelayNodesController(RelayServerMasterTransfer relayServerMasterTransfer)
|
||||
{
|
||||
this.relayServerMasterTransfer = relayServerMasterTransfer;
|
||||
}
|
||||
public Memory<byte> Handle(string query)
|
||||
{
|
||||
return relayServerMasterTransfer.GetPublicNodes().Select(c =>
|
||||
{
|
||||
return new
|
||||
{
|
||||
AllowProtocol = c.AllowProtocol,
|
||||
Name = c.Name,
|
||||
Version = c.Version,
|
||||
|
||||
BandwidthMaxMbps = c.MaxBandwidthTotal,
|
||||
BandwidthConnMbps = c.MaxBandwidth,
|
||||
BandwidthCurrentMbps = c.BandwidthRatio,
|
||||
|
||||
BandwidthGbMonth = c.MaxGbTotal,
|
||||
BandwidthByteAvailable = c.MaxGbTotalLastBytes,
|
||||
|
||||
ConnectionMaxNum = c.MaxConnection,
|
||||
ConnectionCurrentNum = c.ConnectionRatio,
|
||||
|
||||
EndPoint = c.EndPoint,
|
||||
Url = c.Url,
|
||||
|
||||
};
|
||||
}).ToJson().ToBytes();
|
||||
}
|
||||
|
||||
public void Free()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
using linker.messenger.firewall;
|
||||
using linker.snat;
|
||||
using linker.nat;
|
||||
using MemoryPack;
|
||||
|
||||
namespace linker.messenger.serializer.memorypack
|
||||
@@ -29,10 +29,10 @@ namespace linker.messenger.serializer.memorypack
|
||||
string DstPort => info.DstPort;
|
||||
|
||||
[MemoryPackInclude]
|
||||
snat.LinkerFirewallProtocolType Protocol => info.Protocol;
|
||||
nat.LinkerFirewallProtocolType Protocol => info.Protocol;
|
||||
|
||||
[MemoryPackInclude]
|
||||
snat.LinkerFirewallAction Action => info.Action;
|
||||
nat.LinkerFirewallAction Action => info.Action;
|
||||
|
||||
[MemoryPackInclude]
|
||||
bool Disabled => info.Disabled;
|
||||
@@ -45,7 +45,7 @@ namespace linker.messenger.serializer.memorypack
|
||||
|
||||
[MemoryPackConstructor]
|
||||
SerializableFirewallRuleInfo(string id, string srcId, string srcName, string groupId, string dstCIDR, string dstPort,
|
||||
snat.LinkerFirewallProtocolType protocol, snat.LinkerFirewallAction action, bool disabled, int orderby, string remark)
|
||||
nat.LinkerFirewallProtocolType protocol, nat.LinkerFirewallAction action, bool disabled, int orderby, string remark)
|
||||
{
|
||||
var info = new FirewallRuleInfo
|
||||
{
|
||||
@@ -115,14 +115,14 @@ namespace linker.messenger.serializer.memorypack
|
||||
int Disabled => info.Disabled;
|
||||
|
||||
[MemoryPackInclude]
|
||||
snat.LinkerFirewallProtocolType Protocol => info.Protocol;
|
||||
nat.LinkerFirewallProtocolType Protocol => info.Protocol;
|
||||
|
||||
[MemoryPackInclude]
|
||||
snat.LinkerFirewallAction Action => info.Action;
|
||||
nat.LinkerFirewallAction Action => info.Action;
|
||||
|
||||
[MemoryPackConstructor]
|
||||
SerializableFirewallSearchInfo(string groupId, string str, snat.LinkerFirewallProtocolType protocol,
|
||||
snat.LinkerFirewallAction action, int disabled)
|
||||
SerializableFirewallSearchInfo(string groupId, string str, nat.LinkerFirewallProtocolType protocol,
|
||||
nat.LinkerFirewallAction action, int disabled)
|
||||
{
|
||||
var info = new FirewallSearchInfo
|
||||
{
|
||||
|
@@ -1,9 +1,9 @@
|
||||
using linker.libs;
|
||||
using linker.messenger.exroute;
|
||||
using linker.messenger.signin;
|
||||
using linker.snat;
|
||||
using linker.nat;
|
||||
using System.Net;
|
||||
using static linker.snat.LinkerDstMapping;
|
||||
using static linker.nat.LinkerDstMapping;
|
||||
|
||||
namespace linker.messenger.socks5
|
||||
{
|
||||
|
@@ -10,7 +10,6 @@ using linker.messenger.relay.client;
|
||||
using linker.messenger.channel;
|
||||
using linker.messenger.signin;
|
||||
using linker.messenger.pcp;
|
||||
using static linker.snat.WinDivert;
|
||||
using System.Buffers;
|
||||
|
||||
namespace linker.messenger.socks5
|
||||
|
@@ -43,7 +43,8 @@
|
||||
<ProjectReference Include="..\linker.messenger.exroute\linker.messenger.exroute.csproj" />
|
||||
<ProjectReference Include="..\linker.messenger.signin\linker.messenger.signin.csproj" />
|
||||
<ProjectReference Include="..\linker.messenger\linker.messenger.csproj" />
|
||||
<ProjectReference Include="..\linker.snat\linker.snat.csproj" />
|
||||
<ProjectReference Include="..\linker.nat\linker.nat.csproj" />
|
||||
<ProjectReference Include="..\linker.snat\linker.nat.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
using linker.snat;
|
||||
using linker.nat;
|
||||
|
||||
namespace linker.messenger.store.file
|
||||
{
|
||||
|
@@ -16,8 +16,8 @@ namespace linker.messenger.store.file.firewall
|
||||
this.runningConfig = runningConfig;
|
||||
}
|
||||
|
||||
public snat.LinkerFirewallState State => runningConfig.Data.Firewall.State;
|
||||
public void SetState(snat.LinkerFirewallState state)
|
||||
public nat.LinkerFirewallState State => runningConfig.Data.Firewall.State;
|
||||
public void SetState(nat.LinkerFirewallState state)
|
||||
{
|
||||
runningConfig.Data.Firewall.State = state;
|
||||
runningConfig.Data.Update();
|
||||
|
@@ -1,8 +1,8 @@
|
||||
using linker.libs.extends;
|
||||
namespace linker.messenger.store.file
|
||||
namespace linker.messenger.store.file
|
||||
{
|
||||
public sealed partial class ConfigServerInfo
|
||||
{
|
||||
public int ServicePort { get; set; } = 1802;
|
||||
public int ApiPort { get; set; } = 1803;
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ namespace linker.messenger.store.file.server
|
||||
public sealed class ListenStore: IListenStore
|
||||
{
|
||||
public int Port => config.Data.Server.ServicePort;
|
||||
public int ApiPort => config.Data.Server.ApiPort;
|
||||
|
||||
private readonly FileConfig config;
|
||||
public ListenStore(FileConfig config)
|
||||
@@ -17,6 +18,11 @@ namespace linker.messenger.store.file.server
|
||||
config.Data.Server.ServicePort = port;
|
||||
return true;
|
||||
}
|
||||
public bool SetApiPort(int port)
|
||||
{
|
||||
config.Data.Server.ApiPort = port;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Confirm()
|
||||
{
|
||||
|
@@ -8,6 +8,7 @@ using linker.messenger.tunnel;
|
||||
using linker.messenger.tuntap.cidr;
|
||||
using linker.messenger.tuntap.lease;
|
||||
using linker.messenger.tuntap.messenger;
|
||||
using linker.nat;
|
||||
using linker.tun;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Net;
|
||||
@@ -44,6 +45,7 @@ namespace linker.messenger.tuntap
|
||||
serviceCollection.AddSingleton<TuntapCidrMapfileManager>();
|
||||
|
||||
|
||||
serviceCollection.AddSingleton<LinkerFakeAckTransfer>();
|
||||
serviceCollection.AddSingleton<FakeAckTransfer>();
|
||||
|
||||
|
||||
|
@@ -3,10 +3,10 @@ using linker.libs.extends;
|
||||
using linker.libs.timer;
|
||||
using linker.messenger.exroute;
|
||||
using linker.messenger.signin;
|
||||
using linker.tun;
|
||||
using linker.tun.device;
|
||||
using linker.tunnel.connection;
|
||||
using System.Net;
|
||||
using static linker.snat.LinkerDstMapping;
|
||||
using static linker.nat.LinkerDstMapping;
|
||||
|
||||
namespace linker.messenger.tuntap
|
||||
{
|
||||
|
@@ -1,12 +1,13 @@
|
||||
using linker.tunnel;
|
||||
using linker.tunnel.connection;
|
||||
using linker.libs;
|
||||
using linker.tun;
|
||||
using System.Buffers.Binary;
|
||||
using linker.messenger.relay.client;
|
||||
using linker.messenger.signin;
|
||||
using linker.messenger.pcp;
|
||||
using linker.messenger.tuntap.cidr;
|
||||
using linker.nat;
|
||||
using linker.tun.device;
|
||||
|
||||
namespace linker.messenger.tuntap
|
||||
{
|
||||
@@ -103,15 +104,15 @@ namespace linker.messenger.tuntap
|
||||
uint ip = BinaryPrimitives.ReadUInt32BigEndian(packet.DistIPAddress.Span[^4..]);
|
||||
if (tuntapCidrConnectionManager.TryGet(ip, out ITunnelConnection connection) && connection.Connected)
|
||||
{
|
||||
/*
|
||||
if (connection.PacketBuffer.Length > 0)
|
||||
{
|
||||
fakeAckTransfer.Read(packet.IPPacket);
|
||||
}
|
||||
*/
|
||||
await connection.SendAsync(packet.Buffer, packet.Offset, packet.Length).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
if (connection.PacketBuffer.Length > 0)
|
||||
{
|
||||
fakeAckTransfer.Read(packet.IPPacket);
|
||||
}
|
||||
*/
|
||||
|
||||
//开始操作,开始失败直接丢包
|
||||
if (operatingMultipleManager.StartOperation(ip) == false)
|
||||
|
@@ -2,7 +2,8 @@
|
||||
using System.Net;
|
||||
using linker.tun;
|
||||
using linker.libs.timer;
|
||||
using static linker.snat.LinkerDstMapping;
|
||||
using static linker.nat.LinkerDstMapping;
|
||||
using linker.tun.device;
|
||||
|
||||
namespace linker.messenger.tuntap
|
||||
{
|
||||
|
@@ -1,7 +1,7 @@
|
||||
using linker.libs;
|
||||
using linker.messenger.exroute;
|
||||
using linker.messenger.signin;
|
||||
using linker.tun;
|
||||
using linker.tun.device;
|
||||
using System.Net;
|
||||
|
||||
namespace linker.messenger.tuntap.cidr
|
||||
|
@@ -5,7 +5,7 @@ using System.Collections.Concurrent;
|
||||
using System.Collections.Frozen;
|
||||
using System.Net;
|
||||
|
||||
namespace linker.snat
|
||||
namespace linker.nat
|
||||
{
|
||||
/// <summary>
|
||||
/// 网段映射
|
||||
@@ -14,8 +14,8 @@ namespace linker.snat
|
||||
public sealed class LinkerDstMapping
|
||||
{
|
||||
private FrozenDictionary<uint, uint> mapDic = new Dictionary<uint, uint>().ToFrozenDictionary();
|
||||
private uint[] masks = Array.Empty<uint>();
|
||||
private ConcurrentDictionary<uint, uint> natDic = new ConcurrentDictionary<uint, uint>();
|
||||
private uint[] masks = [];
|
||||
private readonly ConcurrentDictionary<uint, uint> natDic = new ConcurrentDictionary<uint, uint>();
|
||||
|
||||
/// <summary>
|
||||
/// 设置映射目标
|
||||
@@ -26,7 +26,7 @@ namespace linker.snat
|
||||
if (maps == null || maps.Length == 0)
|
||||
{
|
||||
mapDic = new Dictionary<uint, uint>().ToFrozenDictionary();
|
||||
masks = Array.Empty<uint>();
|
||||
masks = [];
|
||||
natDic.Clear();
|
||||
return;
|
||||
}
|
||||
@@ -83,7 +83,7 @@ namespace linker.snat
|
||||
/// </summary>
|
||||
/// <param name="packet">TCP/IP</param>
|
||||
/// <param name="checksum">是否计算校验和,如果使用了应用层NAT,可以交给应用层NAT去计算校验和</param>
|
||||
public void ToRealDst(ReadOnlyMemory<byte> packet, bool checksum = true)
|
||||
public void ToRealDst(ReadOnlyMemory<byte> packet)
|
||||
{
|
||||
//只支持映射IPV4
|
||||
if ((byte)(packet.Span[0] >> 4 & 0b1111) != 4) return;
|
||||
@@ -101,8 +101,8 @@ namespace linker.snat
|
||||
{
|
||||
uint realDist = realNetwork | (fakeDist & ~masks[i]);
|
||||
//修改目标IP
|
||||
ReWriteIP(packet, realDist, 16, checksum);
|
||||
if(natDic.TryGetValue(realDist,out uint value) == false || value != fakeDist)
|
||||
ReWriteIP(packet, realDist, 16);
|
||||
if (natDic.TryGetValue(realDist, out uint value) == false || value != fakeDist)
|
||||
{
|
||||
natDic.AddOrUpdate(realDist, fakeDist, (a, b) => fakeDist);
|
||||
}
|
||||
@@ -116,18 +116,14 @@ namespace linker.snat
|
||||
/// <param name="packet">IP包</param>
|
||||
/// <param name="newIP">大端IP</param>
|
||||
/// <param name="pos">写入位置,源12,目的16</param>
|
||||
/// <param name="checksum">是否计算校验和,当windows使用应用层NAT后,会计算一次,这样可以减少一次计算</param>
|
||||
private unsafe void ReWriteIP(ReadOnlyMemory<byte> packet, uint newIP, int pos, bool checksum = true)
|
||||
private unsafe void ReWriteIP(ReadOnlyMemory<byte> packet, uint newIP, int pos)
|
||||
{
|
||||
fixed (byte* ptr = packet.Span)
|
||||
{
|
||||
//修改目标IP,需要小端写入,IP计算都是按大端的,操作是小端的,所以转换一下
|
||||
*(uint*)(ptr + pos) = BinaryPrimitives.ReverseEndianness(newIP);
|
||||
if (checksum)
|
||||
{
|
||||
//计算校验和
|
||||
ChecksumHelper.Checksum(ptr);
|
||||
}
|
||||
//清空校验和,等待重新计算
|
||||
*(ushort*)(ptr + 10) = 0;
|
||||
}
|
||||
}
|
||||
|
531
src/linker.nat/LinkerDstProxy.cs
Normal file
531
src/linker.nat/LinkerDstProxy.cs
Normal file
@@ -0,0 +1,531 @@
|
||||
using linker.libs;
|
||||
using linker.libs.extends;
|
||||
using linker.libs.timer;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace linker.nat
|
||||
{
|
||||
/// <summary>
|
||||
/// 目标代理,实现类似DNAT的功能
|
||||
/// </summary>
|
||||
public sealed class LinkerDstProxy
|
||||
{
|
||||
public bool Running => listenSocketTcp != null;
|
||||
|
||||
private Socket listenSocketTcp;
|
||||
private Socket listenSocketUdp;
|
||||
|
||||
ushort proxyPort = 0;
|
||||
uint tunIp = 0;
|
||||
|
||||
private ValueTuple<uint, uint>[] lans = [];
|
||||
private readonly ConcurrentDictionary<(uint srcIp, ushort srcPort), DstCacheInfo> dic = new();
|
||||
private readonly ConcurrentDictionary<(uint srcIp, ushort srcPort, uint dstIp, ushort dstPort), UdpState> udpMap = new();
|
||||
public LinkerDstProxy()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
public bool Setup(IPAddress dstAddr, ValueTuple<IPAddress, byte>[] dsts, ref string error)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (dsts == null || dsts.Length == 0)
|
||||
{
|
||||
lans = [];
|
||||
dic.Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
lans = dsts.Select(c => new ValueTuple<uint, uint>(NetworkHelper.ToNetworkValue(c.Item1, c.Item2), NetworkHelper.ToBroadcastValue(c.Item1, c.Item2))).ToArray();
|
||||
|
||||
listenSocketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
listenSocketTcp.Bind(new IPEndPoint(IPAddress.Any, 0));
|
||||
listenSocketTcp.Listen(int.MaxValue);
|
||||
_ = ReceiveTcp();
|
||||
|
||||
listenSocketUdp = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
||||
listenSocketUdp.Bind(new IPEndPoint(IPAddress.Any, (listenSocketTcp.LocalEndPoint as IPEndPoint).Port));
|
||||
_ = ReceiveUdp();
|
||||
|
||||
tunIp = NetworkHelper.ToValue(dstAddr);
|
||||
|
||||
proxyPort = (ushort)(listenSocketTcp.LocalEndPoint as IPEndPoint).Port;
|
||||
|
||||
error = string.Empty;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.Message;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
private async Task ReceiveTcp()
|
||||
{
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
Socket source = await listenSocketTcp.AcceptAsync();
|
||||
IPEndPoint ep = source.RemoteEndPoint as IPEndPoint;
|
||||
(uint srcIp, ushort srcPort) key = (NetworkHelper.ToValue(ep.Address), (ushort)ep.Port);
|
||||
if (dic.TryGetValue(key, out DstCacheInfo cache) == false)
|
||||
{
|
||||
source.SafeClose();
|
||||
continue;
|
||||
}
|
||||
|
||||
Socket dst = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
dst.BeginConnect(new IPEndPoint(NetworkHelper.ToIP(cache.IP), cache.Port), ConnectCallback, new TcpState { Source = source, Target = dst });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.Instance.Error(ex);
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
private async void ConnectCallback(IAsyncResult result)
|
||||
{
|
||||
TcpState state = result.AsyncState as TcpState;
|
||||
try
|
||||
{
|
||||
state.Target.EndConnect(result);
|
||||
state.Target.KeepAlive();
|
||||
state.Source.KeepAlive();
|
||||
|
||||
using IMemoryOwner<byte> buffer1 = MemoryPool<byte>.Shared.Rent(8192);
|
||||
using IMemoryOwner<byte> buffer2 = MemoryPool<byte>.Shared.Rent(8192);
|
||||
|
||||
await Task.WhenAll(
|
||||
CopyToAsync(buffer1.Memory, state.Source, state.Target),
|
||||
CopyToAsync(buffer2.Memory, state.Target, state.Source)
|
||||
).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.Instance.Error(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
state.Source.SafeClose();
|
||||
state.Target.SafeClose();
|
||||
}
|
||||
}
|
||||
private async Task CopyToAsync(Memory<byte> buffer, Socket source, Socket target)
|
||||
{
|
||||
try
|
||||
{
|
||||
int bytesRead;
|
||||
while ((bytesRead = await source.ReceiveAsync(buffer, SocketFlags.None).ConfigureAwait(false)) != 0)
|
||||
{
|
||||
await target.SendAsync(buffer.Slice(0, bytesRead), SocketFlags.None).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
||||
{
|
||||
LoggerHelper.Instance.Error(ex);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
source.SafeClose();
|
||||
target.SafeClose();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReceiveUdp()
|
||||
{
|
||||
try
|
||||
{
|
||||
using IMemoryOwner<byte> memory = MemoryPool<byte>.Shared.Rent(65535);
|
||||
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
|
||||
while (true)
|
||||
{
|
||||
SocketReceiveFromResult result = await listenSocketUdp.ReceiveFromAsync(memory.Memory, remoteEndPoint);
|
||||
if (result.ReceivedBytes == 0) continue;
|
||||
|
||||
IPEndPoint ep = result.RemoteEndPoint as IPEndPoint;
|
||||
(uint srcIp, ushort srcPort) key = (NetworkHelper.ToValue(ep.Address), (ushort)ep.Port);
|
||||
if (dic.TryGetValue(key, out DstCacheInfo cache) == false) continue;
|
||||
|
||||
(uint srcIp, ushort srcPort, uint dstIp, ushort dstPort) keyUdp = (NetworkHelper.ToValue(ep.Address), (ushort)ep.Port, cache.IP, cache.Port);
|
||||
if (udpMap.TryGetValue(keyUdp, out UdpState state) == false)
|
||||
{
|
||||
state = new UdpState
|
||||
{
|
||||
Source = listenSocketUdp,
|
||||
SourceEP = new IPEndPoint(ep.Address, ep.Port),
|
||||
Target = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp),
|
||||
TargetEP = new IPEndPoint(NetworkHelper.ToIP(cache.IP), cache.Port)
|
||||
};
|
||||
udpMap.AddOrUpdate(keyUdp, state, (a, b) => state);
|
||||
|
||||
await state.Target.SendToAsync(memory.Memory.Slice(0, result.ReceivedBytes), state.TargetEP);
|
||||
ConnectCallback(keyUdp, state);
|
||||
}
|
||||
else
|
||||
{
|
||||
await state.Target.SendToAsync(memory.Memory.Slice(0, result.ReceivedBytes), state.TargetEP);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.Instance.Error(ex);
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
private async void ConnectCallback((uint srcIp, ushort srcPort, uint dstIp, ushort dstPort) keyUdp, UdpState state)
|
||||
{
|
||||
try
|
||||
{
|
||||
using IMemoryOwner<byte> memory = MemoryPool<byte>.Shared.Rent(65535);
|
||||
while (true)
|
||||
{
|
||||
SocketReceiveFromResult result = await state.Target.ReceiveFromAsync(memory.Memory, state.TargetEP);
|
||||
if (result.ReceivedBytes == 0) continue;
|
||||
|
||||
await state.Source.SendToAsync(memory.Memory.Slice(0, result.ReceivedBytes), state.SourceEP);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.Instance.Error(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
state.Target.SafeClose();
|
||||
udpMap.TryRemove(keyUdp, out _);
|
||||
}
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
listenSocketTcp?.SafeClose();
|
||||
listenSocketTcp = null;
|
||||
listenSocketUdp?.SafeClose();
|
||||
listenSocketUdp = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从网卡读取之后
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
/// <param name="needChecksum"></param>
|
||||
public unsafe void Read(ReadOnlyMemory<byte> packet)
|
||||
{
|
||||
if (listenSocketTcp == null) return;
|
||||
fixed (byte* ptr = packet.Span)
|
||||
{
|
||||
DstProxyPacket p = new DstProxyPacket(ptr);
|
||||
if (p.Version != 4) return;
|
||||
|
||||
if (p.Protocol == ProtocolType.Tcp || p.Protocol == ProtocolType.Udp)
|
||||
{
|
||||
if (dic.TryGetValue((p.DstAddr, p.DstPort), out DstCacheInfo cache))
|
||||
{
|
||||
cache.LastTime = Environment.TickCount64;
|
||||
p.SrcAddr = cache.IP;
|
||||
p.SrcPort = cache.Port;
|
||||
p.IPChecksum = 0;
|
||||
p.PayloadChecksum = 0;
|
||||
|
||||
if (p.Protocol == ProtocolType.Tcp && (p.TcpFlagFin || p.TcpFlagRst))
|
||||
{
|
||||
cache.Fin = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 写如网卡之前
|
||||
/// </summary>
|
||||
/// <param name="packet">单个完整TCP/IP包</param>
|
||||
/// <returns></returns>
|
||||
public unsafe bool Write(ReadOnlyMemory<byte> packet)
|
||||
{
|
||||
if (listenSocketTcp == null) return true;
|
||||
fixed (byte* ptr = packet.Span)
|
||||
{
|
||||
DstProxyPacket p = new DstProxyPacket(ptr);
|
||||
if (p.Version != 4 || p.DstAddr == tunIp || p.DstAddrSpan.IsCast()) return true;
|
||||
|
||||
if (lans.Any(c => p.DstAddr >= c.Item1 && p.DstAddr <= c.Item2) == false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return p.Protocol switch
|
||||
{
|
||||
ProtocolType.Tcp => WriteTcp(p),
|
||||
ProtocolType.Udp => WriteUdp(p),
|
||||
ProtocolType.Icmp => WriteIcmp(p),
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
}
|
||||
private bool WriteTcp(DstProxyPacket p)
|
||||
{
|
||||
(uint srcIp, ushort srcPort) key = (p.SrcAddr, p.SrcPort);
|
||||
if (dic.TryGetValue(key, out DstCacheInfo cache) == false || cache.IP != p.DstAddr || cache.Port != p.DstPort)
|
||||
{
|
||||
if (p.IsOnlySyn == false) return true;
|
||||
cache = new DstCacheInfo { IP = p.DstAddr, Port = p.DstPort };
|
||||
dic.AddOrUpdate(key, cache, (a, b) => cache);
|
||||
}
|
||||
cache.LastTime = Environment.TickCount64;
|
||||
|
||||
if (p.TcpFlagFin || p.TcpFlagRst)
|
||||
{
|
||||
cache.Fin = true;
|
||||
}
|
||||
|
||||
//改为代理地址
|
||||
p.DstPort = proxyPort;
|
||||
p.DstAddr = tunIp;
|
||||
//重新计算校验和
|
||||
p.IPChecksum = 0;
|
||||
p.PayloadChecksum = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
private bool WriteUdp(DstProxyPacket p)
|
||||
{
|
||||
(uint srcIp, ushort srcPort) key = (p.SrcAddr, p.SrcPort);
|
||||
if (dic.TryGetValue(key, out DstCacheInfo cache) == false || cache.IP != p.DstAddr || cache.Port != p.DstPort)
|
||||
{
|
||||
cache = new DstCacheInfo { IP = p.DstAddr, Port = p.DstPort, Fin = true };
|
||||
dic.AddOrUpdate(key, cache, (a, b) => cache);
|
||||
}
|
||||
cache.LastTime = Environment.TickCount64;
|
||||
|
||||
//改为代理地址
|
||||
p.DstPort = proxyPort;
|
||||
p.DstAddr = tunIp;
|
||||
//重新计算校验和
|
||||
p.IPChecksum = 0;
|
||||
p.PayloadChecksum = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
private bool WriteIcmp(DstProxyPacket p)
|
||||
{
|
||||
if (p.IcmpType == 8)
|
||||
{
|
||||
using Ping ping = new Ping();
|
||||
PingReply reply = ping.Send(NetworkHelper.ToIP(p.DstAddr), 1000);
|
||||
if (reply.Status != IPStatus.Success)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
//交换IP
|
||||
(p.DstAddr, p.SrcAddr) = (p.SrcAddr, p.DstAddr);
|
||||
//改为response
|
||||
p.IcmpType = 0;
|
||||
//重新计算校验和
|
||||
p.IPChecksum = 0;
|
||||
p.PayloadChecksum = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Clear()
|
||||
{
|
||||
TimerHelper.SetIntervalLong(() =>
|
||||
{
|
||||
foreach (var item in dic.Where(c => c.Value.Fin && Environment.TickCount64 - c.Value.LastTime > 5 * 60 * 1000).Select(c => c.Key).ToList())
|
||||
{
|
||||
dic.TryRemove(item, out _);
|
||||
}
|
||||
}, 30000);
|
||||
}
|
||||
|
||||
sealed class DstCacheInfo
|
||||
{
|
||||
public long LastTime { get; set; } = Environment.TickCount64;
|
||||
public uint IP { get; set; }
|
||||
public ushort Port { get; set; }
|
||||
|
||||
public bool Fin { get; set; }
|
||||
}
|
||||
|
||||
sealed class TcpState
|
||||
{
|
||||
public Socket Source { get; set; }
|
||||
public Socket Target { get; set; }
|
||||
}
|
||||
sealed class UdpState
|
||||
{
|
||||
public Socket Source { get; set; }
|
||||
public IPEndPoint SourceEP { get; set; }
|
||||
public Socket Target { get; set; }
|
||||
public IPEndPoint TargetEP { get; set; }
|
||||
}
|
||||
readonly unsafe struct DstProxyPacket
|
||||
{
|
||||
private readonly byte* ptr;
|
||||
|
||||
/// <summary>
|
||||
/// 协议版本
|
||||
/// </summary>
|
||||
public readonly byte Version => (byte)((*ptr >> 4) & 0b1111);
|
||||
public readonly ProtocolType Protocol => (ProtocolType)(*(ptr + 9));
|
||||
|
||||
public readonly byte IcmpType
|
||||
{
|
||||
get
|
||||
{
|
||||
return *PayloadPtr;
|
||||
}
|
||||
set
|
||||
{
|
||||
*PayloadPtr = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IP头长度
|
||||
/// </summary>
|
||||
public readonly int IPHeadLength => (*ptr & 0b1111) * 4;
|
||||
/// <summary>
|
||||
/// IP包荷载数据指针,也就是TCP/UDP头指针
|
||||
/// </summary>
|
||||
public readonly byte* PayloadPtr => ptr + IPHeadLength;
|
||||
|
||||
/// <summary>
|
||||
/// 源地址
|
||||
/// </summary>
|
||||
public readonly uint SrcAddr
|
||||
{
|
||||
get
|
||||
{
|
||||
return BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 12));
|
||||
}
|
||||
set
|
||||
{
|
||||
*(uint*)(ptr + 12) = BinaryPrimitives.ReverseEndianness(value);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 源端口
|
||||
/// </summary>
|
||||
public readonly ushort SrcPort
|
||||
{
|
||||
get
|
||||
{
|
||||
return BinaryPrimitives.ReverseEndianness(*(ushort*)(PayloadPtr));
|
||||
}
|
||||
set
|
||||
{
|
||||
*(ushort*)(PayloadPtr) = BinaryPrimitives.ReverseEndianness(value);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 目的地址
|
||||
/// </summary>
|
||||
public readonly uint DstAddr
|
||||
{
|
||||
get
|
||||
{
|
||||
return BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 16));
|
||||
}
|
||||
set
|
||||
{
|
||||
*(uint*)(ptr + 16) = BinaryPrimitives.ReverseEndianness(value);
|
||||
}
|
||||
}
|
||||
public ReadOnlySpan<byte> DstAddrSpan => new Span<byte>((ptr + 16), 4);
|
||||
|
||||
|
||||
public readonly byte TcpFlag => *(ptr + IPHeadLength + 13);
|
||||
public readonly bool TcpFlagFin => (TcpFlag & 0b000001) != 0;
|
||||
public readonly bool TcpFlagSyn => (TcpFlag & 0b000010) != 0;
|
||||
public readonly bool TcpFlagRst => (TcpFlag & 0b000100) != 0;
|
||||
public readonly bool TcpFlagPsh => (TcpFlag & 0b001000) != 0;
|
||||
public readonly bool TcpFlagAck => (TcpFlag & 0b010000) != 0;
|
||||
public readonly bool TcpFlagUrg => (TcpFlag & 0b100000) != 0;
|
||||
|
||||
public readonly bool IsPshAck => TcpFlagPsh && TcpFlagAck;
|
||||
public readonly bool IsOnlyAck => TcpFlag == 0b00010000;
|
||||
public readonly bool IsOnlySyn => TcpFlag == 0b00000010;
|
||||
public readonly bool IsSynAck => TcpFlag == 0b00010010;
|
||||
|
||||
/// <summary>
|
||||
/// 目标端口
|
||||
/// </summary>
|
||||
public readonly ushort DstPort
|
||||
{
|
||||
get
|
||||
{
|
||||
return BinaryPrimitives.ReverseEndianness(*(ushort*)(PayloadPtr + 2));
|
||||
}
|
||||
set
|
||||
{
|
||||
*(ushort*)(PayloadPtr + 2) = BinaryPrimitives.ReverseEndianness(value);
|
||||
}
|
||||
}
|
||||
|
||||
public readonly ushort IPChecksum
|
||||
{
|
||||
get
|
||||
{
|
||||
return BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + 10));
|
||||
}
|
||||
set
|
||||
{
|
||||
*(ushort*)(ptr + 10) = BinaryPrimitives.ReverseEndianness(value);
|
||||
}
|
||||
}
|
||||
public readonly ushort PayloadChecksum
|
||||
{
|
||||
get
|
||||
{
|
||||
return Protocol switch
|
||||
{
|
||||
ProtocolType.Icmp => BinaryPrimitives.ReverseEndianness(*(ushort*)(PayloadPtr + 2)),
|
||||
ProtocolType.Tcp => BinaryPrimitives.ReverseEndianness(*(ushort*)(PayloadPtr + 16)),
|
||||
ProtocolType.Udp => BinaryPrimitives.ReverseEndianness(*(ushort*)(PayloadPtr + 6)),
|
||||
_ => (ushort)0,
|
||||
};
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (Protocol)
|
||||
{
|
||||
case ProtocolType.Icmp:
|
||||
*(ushort*)(PayloadPtr + 2) = BinaryPrimitives.ReverseEndianness(value);
|
||||
break;
|
||||
case ProtocolType.Tcp:
|
||||
*(ushort*)(PayloadPtr + 16) = BinaryPrimitives.ReverseEndianness(value);
|
||||
break;
|
||||
case ProtocolType.Udp:
|
||||
*(ushort*)(PayloadPtr + 6) = BinaryPrimitives.ReverseEndianness(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载TCP/IP包,必须是一个完整的TCP/IP包
|
||||
/// </summary>
|
||||
/// <param name="ptr">一个完整的TCP/IP包</param>
|
||||
public DstProxyPacket(byte* ptr)
|
||||
{
|
||||
this.ptr = ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
563
src/linker.nat/LinkerFakeAck.cs
Normal file
563
src/linker.nat/LinkerFakeAck.cs
Normal file
@@ -0,0 +1,563 @@
|
||||
using linker.libs;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace linker.nat
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 伪造ACK操作类
|
||||
/// </summary>
|
||||
public unsafe sealed class LinkerFakeAckTransfer
|
||||
{
|
||||
private readonly ConcurrentDictionary<FaceAckKey, FackAckState> dic = new(new FackAckKeyComparer());
|
||||
|
||||
/// <summary>
|
||||
/// 发起方
|
||||
/// </summary>
|
||||
/// <param name="packet">一个完整的TCP/IP包</param>
|
||||
/// <param name="fakeBuffer">一个能容纳ACK包的缓冲区,如果需要伪造ACK则写入到这里</param>
|
||||
/// <param name="bufferFree">缓冲区可用字节数,会根据这个来计算ack的窗口大小</param>
|
||||
/// <param name="fakeLength">ack包长度</param>
|
||||
/// <returns>是否丢包</returns>
|
||||
public bool Read(ReadOnlyMemory<byte> packet, ReadOnlyMemory<byte> fakeBuffer, long bufferFree, out ushort fakeLength)
|
||||
{
|
||||
fakeLength = 0;
|
||||
|
||||
fixed (byte* ptr = packet.Span)
|
||||
{
|
||||
FakeAckPacket originPacket = new(ptr);
|
||||
if (originPacket.Version != 4 || originPacket.Protocol != ProtocolType.Tcp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FaceAckKey key = new() { srcAddr = originPacket.SrcAddr, srcPort = originPacket.SrcPort, dstAddr = originPacket.DstAddr, dstPort = originPacket.DstPort };
|
||||
|
||||
if ((originPacket.IsOnlyAck || originPacket.IsPshAck) && dic.TryGetValue(key, out FackAckState state))
|
||||
{
|
||||
if (originPacket.TcpPayloadLength == 0)
|
||||
{
|
||||
return state.Ack++ > 0;
|
||||
}
|
||||
|
||||
fixed (byte* pptr = fakeBuffer.Span)
|
||||
{
|
||||
ushort win = (ushort)Math.Max(Math.Min(bufferFree * 0.8 / state.WindowScale, 32 * 1024), 4);
|
||||
fakeLength = originPacket.ToAck(state.Seq, win, pptr);
|
||||
if (new FakeAckPacket(pptr).Cq <= state.Cq)
|
||||
{
|
||||
fakeLength = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (originPacket.TcpFlagFin || originPacket.TcpFlagRst)
|
||||
{
|
||||
dic.TryRemove(key, out _);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/// <summary>
|
||||
/// 接收方
|
||||
/// </summary>
|
||||
/// <param name="packet">一个完整的TCP/IP包</param>
|
||||
/// <returns></returns>
|
||||
public void Write(ReadOnlyMemory<byte> packet)
|
||||
{
|
||||
fixed (byte* ptr = packet.Span)
|
||||
{
|
||||
FakeAckPacket originPacket = new(ptr);
|
||||
if (originPacket.Version != 4 || originPacket.Protocol != ProtocolType.Tcp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
FaceAckKey key = new() { srcAddr = originPacket.SrcAddr, srcPort = originPacket.SrcPort, dstAddr = originPacket.DstAddr, dstPort = originPacket.DstPort };
|
||||
|
||||
/*
|
||||
//更新序列号
|
||||
if (originPacket.TcpFlagAck && dic.TryGetValue(key, out FackAckState state))
|
||||
{
|
||||
state.Cq = originPacket.Cq;
|
||||
|
||||
if (originPacket.TcpPayloadLength > 0)
|
||||
{
|
||||
state.Seq = originPacket.Seq + (uint)originPacket.TcpPayloadLength;
|
||||
}
|
||||
}
|
||||
else*/
|
||||
if (originPacket.IsOnlySyn || originPacket.IsSynAck)
|
||||
{
|
||||
FackAckState state = new()
|
||||
{
|
||||
Ack = (ulong)(originPacket.IsOnlySyn ? 1 : 0),
|
||||
Seq = originPacket.Seq + 1,
|
||||
WindowScale = originPacket.FindWindowScale(ptr + originPacket.IPHeadLength)
|
||||
};
|
||||
dic.AddOrUpdate(key, state, (a, b) => state);
|
||||
}
|
||||
else if (originPacket.TcpFlagFin || originPacket.TcpFlagRst)
|
||||
{
|
||||
dic.TryRemove(key, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 状态
|
||||
/// </summary>
|
||||
sealed class FackAckState
|
||||
{
|
||||
public ulong Ack { get; set; }
|
||||
public uint Seq { get; set; }
|
||||
public uint Cq { get; set; }
|
||||
public int WindowScale { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 四元组缓存key
|
||||
/// </summary>
|
||||
struct FaceAckKey
|
||||
{
|
||||
public uint srcAddr;
|
||||
public ushort srcPort;
|
||||
public uint dstAddr;
|
||||
public ushort dstPort;
|
||||
}
|
||||
/// <summary>
|
||||
/// 四元组缓存key比较器
|
||||
/// </summary>
|
||||
sealed class FackAckKeyComparer : IEqualityComparer<FaceAckKey>
|
||||
{
|
||||
public bool Equals(FaceAckKey x, FaceAckKey y)
|
||||
{
|
||||
return (x.srcAddr, x.srcPort, x.dstAddr, x.dstPort) == (y.srcAddr, y.srcPort, y.dstAddr, y.dstPort)
|
||||
|| (x.dstAddr, x.dstPort, x.srcAddr, x.srcPort) == (y.srcAddr, y.srcPort, y.dstAddr, y.dstPort);
|
||||
}
|
||||
|
||||
public int GetHashCode(FaceAckKey obj)
|
||||
{
|
||||
return (int)obj.srcAddr ^ obj.srcPort ^ (int)obj.dstAddr ^ obj.dstPort;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 数据包解析
|
||||
/// </summary>
|
||||
readonly unsafe struct FakeAckPacket
|
||||
{
|
||||
private readonly byte* ptr;
|
||||
|
||||
/// <summary>
|
||||
/// 协议版本
|
||||
/// </summary>
|
||||
public readonly byte Version => (byte)((*ptr >> 4) & 0b1111);
|
||||
public readonly ProtocolType Protocol => (ProtocolType)(*(ptr + 9));
|
||||
|
||||
/// <summary>
|
||||
/// 源地址
|
||||
/// </summary>
|
||||
public readonly uint SrcAddr => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 12));
|
||||
/// <summary>
|
||||
/// 源端口
|
||||
/// </summary>
|
||||
public readonly ushort SrcPort => BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + IPHeadLength));
|
||||
/// <summary>
|
||||
/// 目的地址
|
||||
/// </summary>
|
||||
public readonly uint DstAddr => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 16));
|
||||
/// <summary>
|
||||
/// 目标端口
|
||||
/// </summary>
|
||||
public readonly ushort DstPort => BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + IPHeadLength + 2));
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// IP头长度
|
||||
/// </summary>
|
||||
public readonly int IPHeadLength => (*ptr & 0b1111) * 4;
|
||||
|
||||
/// <summary>
|
||||
/// 序列号
|
||||
/// </summary>
|
||||
public readonly uint Seq => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + IPHeadLength + 4));
|
||||
/// <summary>
|
||||
/// 确认号
|
||||
/// </summary>
|
||||
public readonly uint Cq => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + IPHeadLength + 8));
|
||||
|
||||
/// <summary>
|
||||
/// TCP负载长度
|
||||
/// </summary>
|
||||
public readonly int TcpPayloadLength
|
||||
{
|
||||
get
|
||||
{
|
||||
int ipHeadLength = (*ptr & 0b1111) * 4;
|
||||
int tcpHeaderLength = (*(ptr + ipHeadLength + 12) >> 4) * 4;
|
||||
return BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + 2)) - ipHeadLength - tcpHeaderLength;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TCP Flag
|
||||
/// </summary>
|
||||
public readonly byte TcpFlag => *(ptr + IPHeadLength + 13);
|
||||
public readonly bool TcpFlagFin => (TcpFlag & 0b000001) != 0;
|
||||
public readonly bool TcpFlagSyn => (TcpFlag & 0b000010) != 0;
|
||||
public readonly bool TcpFlagRst => (TcpFlag & 0b000100) != 0;
|
||||
public readonly bool TcpFlagPsh => (TcpFlag & 0b001000) != 0;
|
||||
public readonly bool TcpFlagAck => (TcpFlag & 0b010000) != 0;
|
||||
public readonly bool TcpFlagUrg => (TcpFlag & 0b100000) != 0;
|
||||
|
||||
public readonly bool IsPshAck => TcpFlagPsh && TcpFlagAck;
|
||||
public readonly bool IsOnlyAck => TcpFlag == 0b00010000;
|
||||
public readonly bool IsOnlySyn => TcpFlag == 0b00000010;
|
||||
public readonly bool IsSynAck => TcpFlag == 0b00010010;
|
||||
|
||||
/// <summary>
|
||||
/// 加载TCP/IP包,必须是一个完整的TCP/IP包
|
||||
/// </summary>
|
||||
/// <param name="ptr">一个完整的TCP/IP包</param>
|
||||
public FakeAckPacket(byte* ptr)
|
||||
{
|
||||
this.ptr = ptr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 制作一个ACK包
|
||||
/// </summary>
|
||||
/// <param name="seq">给定一个序列号,可以从syn+ack包中+1获得</param>
|
||||
/// <param name="winSize">窗口大小</param>
|
||||
/// <param name="ipPtr">目标内存</param>
|
||||
/// <returns></returns>
|
||||
public readonly unsafe ushort ToAck(uint seq, ushort winSize, byte* ipPtr)
|
||||
{
|
||||
//复制一份IP+TCP头部
|
||||
int ipHeaderLength = (*ptr & 0b1111) * 4;
|
||||
int tcpHeaderLength = (*(ptr + ipHeaderLength + 12) >> 4) * 4;
|
||||
ushort totalLength = BinaryPrimitives.ReverseEndianness(*(ushort*)(ipPtr + 2));
|
||||
uint payloadLength = (uint)(totalLength - ipHeaderLength - tcpHeaderLength);
|
||||
|
||||
new Span<byte>(ptr, ipHeaderLength + tcpHeaderLength).CopyTo(new Span<byte>(ipPtr, ipHeaderLength + tcpHeaderLength));
|
||||
|
||||
//TCP头指针
|
||||
byte* tcpPtr = ipPtr + ipHeaderLength;
|
||||
|
||||
//如果有时间戳,就填充时间戳选项
|
||||
//FullOptionTimestamp(tcpPtr);
|
||||
*(tcpPtr + 12) = 0b01010000;
|
||||
//重新计算头部长度
|
||||
tcpHeaderLength = (*(tcpPtr + 12) >> 4) * 4;
|
||||
totalLength = (ushort)(ipHeaderLength + tcpHeaderLength);
|
||||
|
||||
//交换地址和端口
|
||||
(*(uint*)(ipPtr + 16), *(uint*)(ipPtr + 12)) = (*(uint*)(ipPtr + 12), *(uint*)(ipPtr + 16));
|
||||
(*(ushort*)(tcpPtr + 2), *(ushort*)(tcpPtr)) = (*(ushort*)(tcpPtr), *(ushort*)(tcpPtr + 2));
|
||||
|
||||
//设置总长度
|
||||
*(ushort*)(ipPtr + 2) = BinaryPrimitives.ReverseEndianness(totalLength);
|
||||
|
||||
//重置分片相关信息
|
||||
*(ushort*)(ipPtr + 4) = 0; // 清除分片偏移和标志
|
||||
*(ushort*)(ipPtr + 6) = 0; // 清除更多分片标志
|
||||
|
||||
//源序列号
|
||||
uint _seq = BinaryPrimitives.ReverseEndianness(*(uint*)(tcpPtr + 4));
|
||||
//设置序列号
|
||||
*(uint*)(tcpPtr + 4) = BinaryPrimitives.ReverseEndianness(seq);
|
||||
//设置确认号
|
||||
*(uint*)(tcpPtr + 8) = BinaryPrimitives.ReverseEndianness(_seq + payloadLength);
|
||||
|
||||
//设置TCP标志位为ACK,其他标志位清除
|
||||
*(tcpPtr + 13) = 0b00010000;
|
||||
|
||||
//设置窗口大小
|
||||
*(ushort*)(tcpPtr + 14) = BinaryPrimitives.ReverseEndianness(Math.Max(winSize, (ushort)8));
|
||||
|
||||
//计算校验和
|
||||
ChecksumHelper.Checksum(ipPtr);
|
||||
|
||||
//只需要IP头+TCP头
|
||||
return totalLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从TCP的SYN包或SYN+ACK包中,获取窗口缩放比例
|
||||
/// </summary>
|
||||
/// <param name="ipPtr">一个完整TCP/IP包</param>
|
||||
/// <returns></returns>
|
||||
public int FindWindowScale(byte* ipPtr)
|
||||
{
|
||||
//指针移动到TCP头开始位置
|
||||
byte* tcpPtr = ipPtr + ((*ipPtr & 0b1111) * 4);
|
||||
|
||||
//tcp头固定20,所以option从这里开始
|
||||
int index = 20;
|
||||
//tcp头结束位置,就是option结束位置
|
||||
int end = (*(tcpPtr + 12) >> 4) * 4;
|
||||
while (index < end)
|
||||
{
|
||||
byte kind = *(tcpPtr + index);
|
||||
//EOF结束符
|
||||
if (kind == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
byte length = *(tcpPtr + index + 1);
|
||||
//NOP 空选项
|
||||
if (kind == 1)
|
||||
{
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
//Window Scale 1kind 1length 1shiftCount
|
||||
else if (kind == 3 && length == 3)
|
||||
{
|
||||
byte shiftCount = *(tcpPtr + index + 2);
|
||||
return shiftCount > 14 ? 1 : 1 << shiftCount;
|
||||
}
|
||||
index += length;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public unsafe sealed class FakeAckTransfer
|
||||
{
|
||||
private readonly ConcurrentDictionary<FaceAckKey, int> dic = new(new FackAckKeyComparer());
|
||||
|
||||
/// <summary>
|
||||
/// 发起方
|
||||
/// </summary>
|
||||
/// <param name="packet">一个完整的TCP/IP包</param>
|
||||
/// <returns></returns>
|
||||
public void Read(ReadOnlyMemory<byte> packet)
|
||||
{
|
||||
fixed (byte* ptr = packet.Span)
|
||||
{
|
||||
FakeAckPacket originPacket = new(ptr);
|
||||
if (originPacket.Version != 4 || originPacket.Protocol != ProtocolType.Tcp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (originPacket.TcpFlagFin || originPacket.TcpFlagRst)
|
||||
{
|
||||
FaceAckKey key = new() { srcAddr = originPacket.SrcAddr, srcPort = originPacket.SrcPort, dstAddr = originPacket.DstAddr, dstPort = originPacket.DstPort };
|
||||
dic.TryRemove(key, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 接收方
|
||||
/// </summary>
|
||||
/// <param name="packet">一个完整的TCP/IP包</param>
|
||||
/// <returns></returns>
|
||||
public void Write(ReadOnlyMemory<byte> packet, long bufferFree)
|
||||
{
|
||||
fixed (byte* ptr = packet.Span)
|
||||
{
|
||||
FakeAckPacket originPacket = new(ptr);
|
||||
if (originPacket.Version != 4 || originPacket.Protocol != ProtocolType.Tcp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
FaceAckKey key = new() { srcAddr = originPacket.SrcAddr, srcPort = originPacket.SrcPort, dstAddr = originPacket.DstAddr, dstPort = originPacket.DstPort };
|
||||
|
||||
if (originPacket.IsPshAck || originPacket.IsOnlyAck)
|
||||
{
|
||||
if (dic.TryGetValue(key, out int wins) && originPacket.Window > 0)
|
||||
{
|
||||
ushort win = (ushort)Math.Max(Math.Min(bufferFree / wins, 65535), 4);
|
||||
originPacket.WriteWindow(ptr, win);
|
||||
}
|
||||
|
||||
}
|
||||
else if (originPacket.IsOnlySyn || originPacket.IsSynAck)
|
||||
{
|
||||
int windowScale = originPacket.FindWindowScale(ptr);
|
||||
dic.AddOrUpdate(key, windowScale, (a, b) => windowScale);
|
||||
}
|
||||
else if (originPacket.TcpFlagFin || originPacket.TcpFlagRst)
|
||||
{
|
||||
dic.TryRemove(key, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 四元组缓存key
|
||||
/// </summary>
|
||||
struct FaceAckKey
|
||||
{
|
||||
public uint srcAddr;
|
||||
public ushort srcPort;
|
||||
public uint dstAddr;
|
||||
public ushort dstPort;
|
||||
}
|
||||
/// <summary>
|
||||
/// 四元组缓存key比较器
|
||||
/// </summary>
|
||||
sealed class FackAckKeyComparer : IEqualityComparer<FaceAckKey>
|
||||
{
|
||||
public bool Equals(FaceAckKey x, FaceAckKey y)
|
||||
{
|
||||
return (x.srcAddr, x.srcPort, x.dstAddr, x.dstPort) == (y.srcAddr, y.srcPort, y.dstAddr, y.dstPort)
|
||||
|| (x.dstAddr, x.dstPort, x.srcAddr, x.srcPort) == (y.srcAddr, y.srcPort, y.dstAddr, y.dstPort);
|
||||
}
|
||||
|
||||
public int GetHashCode(FaceAckKey obj)
|
||||
{
|
||||
return (int)obj.srcAddr ^ obj.srcPort ^ (int)obj.dstAddr ^ obj.dstPort;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 数据包解析
|
||||
/// </summary>
|
||||
readonly unsafe struct FakeAckPacket
|
||||
{
|
||||
private readonly byte* ptr;
|
||||
|
||||
/// <summary>
|
||||
/// 协议版本
|
||||
/// </summary>
|
||||
public readonly byte Version => (byte)((*ptr >> 4) & 0b1111);
|
||||
public readonly ProtocolType Protocol => (ProtocolType)(*(ptr + 9));
|
||||
|
||||
/// <summary>
|
||||
/// 源地址
|
||||
/// </summary>
|
||||
public readonly uint SrcAddr => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 12));
|
||||
/// <summary>
|
||||
/// 源端口
|
||||
/// </summary>
|
||||
public readonly ushort SrcPort => BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + IPHeadLength));
|
||||
/// <summary>
|
||||
/// 目的地址
|
||||
/// </summary>
|
||||
public readonly uint DstAddr => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 16));
|
||||
/// <summary>
|
||||
/// 目标端口
|
||||
/// </summary>
|
||||
public readonly ushort DstPort => BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + IPHeadLength + 2));
|
||||
|
||||
/// <summary>
|
||||
/// IP头长度
|
||||
/// </summary>
|
||||
public readonly int IPHeadLength => (*ptr & 0b1111) * 4;
|
||||
/// <summary>
|
||||
/// 窗口大小
|
||||
/// </summary>
|
||||
public readonly ushort Window => BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + IPHeadLength + 14));
|
||||
|
||||
/// <summary>
|
||||
/// TCP Flag
|
||||
/// </summary>
|
||||
public readonly byte TcpFlag => *(ptr + IPHeadLength + 13);
|
||||
public readonly bool TcpFlagFin => (TcpFlag & 0b000001) != 0;
|
||||
public readonly bool TcpFlagSyn => (TcpFlag & 0b000010) != 0;
|
||||
public readonly bool TcpFlagRst => (TcpFlag & 0b000100) != 0;
|
||||
public readonly bool TcpFlagPsh => (TcpFlag & 0b001000) != 0;
|
||||
public readonly bool TcpFlagAck => (TcpFlag & 0b010000) != 0;
|
||||
public readonly bool TcpFlagUrg => (TcpFlag & 0b100000) != 0;
|
||||
|
||||
public readonly bool IsPshAck => TcpFlagPsh && TcpFlagAck;
|
||||
public readonly bool IsOnlyAck => TcpFlag == 0b00010000;
|
||||
public readonly bool IsOnlySyn => TcpFlag == 0b00000010;
|
||||
public readonly bool IsSynAck => TcpFlag == 0b00010010;
|
||||
|
||||
/// <summary>
|
||||
/// 加载TCP/IP包,必须是一个完整的TCP/IP包
|
||||
/// </summary>
|
||||
/// <param name="ptr">一个完整的TCP/IP包</param>
|
||||
public FakeAckPacket(byte* ptr)
|
||||
{
|
||||
this.ptr = ptr;
|
||||
}
|
||||
|
||||
public int FindWindowScale(byte* ipPtr)
|
||||
{
|
||||
//指针移动到TCP头开始位置
|
||||
byte* tcpPtr = ipPtr + ((*ipPtr & 0b1111) * 4);
|
||||
|
||||
//tcp头固定20,所以option从这里开始
|
||||
int index = 20;
|
||||
//tcp头结束位置,就是option结束位置
|
||||
int end = (*(tcpPtr + 12) >> 4) * 4;
|
||||
while (index < end)
|
||||
{
|
||||
byte kind = *(tcpPtr + index);
|
||||
//EOF结束符
|
||||
if (kind == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
byte length = *(tcpPtr + index + 1);
|
||||
//NOP 空选项
|
||||
if (kind == 1)
|
||||
{
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
//Window Scale 1kind 1length 1shiftCount
|
||||
else if (kind == 3 && length == 3)
|
||||
{
|
||||
return *(tcpPtr + index + 2);
|
||||
}
|
||||
index += length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public int WriteWindowScale(byte* ipPtr, byte windowScale = 7)
|
||||
{
|
||||
//指针移动到TCP头开始位置
|
||||
byte* tcpPtr = ipPtr + ((*ipPtr & 0b1111) * 4);
|
||||
|
||||
//tcp头固定20,所以option从这里开始
|
||||
int index = 20;
|
||||
//tcp头结束位置,就是option结束位置
|
||||
int end = (*(tcpPtr + 12) >> 4) * 4;
|
||||
while (index < end)
|
||||
{
|
||||
byte kind = *(tcpPtr + index);
|
||||
//EOF结束符
|
||||
if (kind == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
byte length = *(tcpPtr + index + 1);
|
||||
//NOP 空选项
|
||||
if (kind == 1)
|
||||
{
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
//Window Scale 1kind 1length 1shiftCount
|
||||
else if (kind == 3 && length == 3)
|
||||
{
|
||||
if (*(tcpPtr + index + 2) < windowScale)
|
||||
{
|
||||
*(tcpPtr + index + 2) = windowScale;
|
||||
ChecksumHelper.Checksum(ipPtr, false, true);
|
||||
}
|
||||
return *(tcpPtr + index + 2);
|
||||
}
|
||||
index += length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public void WriteWindow(byte* ipPtr, ushort window)
|
||||
{
|
||||
*(ushort*)(ipPtr + ((*ipPtr & 0b1111) * 4) + 14) = BinaryPrimitives.ReverseEndianness(Math.Max(window, (ushort)8));
|
||||
ChecksumHelper.Checksum(ipPtr, false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,8 +3,8 @@ using linker.libs.timer;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using static linker.snat.LinkerSrcNat;
|
||||
namespace linker.snat
|
||||
using static linker.nat.LinkerSrcNat;
|
||||
namespace linker.nat
|
||||
{
|
||||
/// <summary>
|
||||
/// 开启后默认阻止
|
@@ -9,7 +9,7 @@ using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace linker.snat
|
||||
namespace linker.nat
|
||||
{
|
||||
/// <summary>
|
||||
/// 64位,放x64的WinDivert.dll和WinDivert64.sys
|
||||
@@ -551,8 +551,6 @@ namespace linker.snat
|
||||
public byte Version => (byte)((*ptr >> 4) & 0b1111);
|
||||
public ProtocolType Protocol => (ProtocolType)(*(ptr + 9));
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 源地址
|
||||
/// </summary>
|
@@ -8,7 +8,7 @@ using Microsoft.Win32.SafeHandles;
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
namespace linker.snat
|
||||
namespace linker.nat
|
||||
{
|
||||
/// <summary>
|
||||
/// WinDivert 包装
|
@@ -1,4 +1,5 @@
|
||||
using linker.libs;
|
||||
using linker.tun.device;
|
||||
using System.Buffers.Binary;
|
||||
using System.Net;
|
||||
|
||||
|
@@ -1,245 +0,0 @@
|
||||
using linker.libs;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace linker.tun
|
||||
{
|
||||
/// <summary>
|
||||
/// 伪造ACK操作类
|
||||
/// </summary>
|
||||
public unsafe sealed class FakeAckTransfer
|
||||
{
|
||||
private readonly ConcurrentDictionary<FaceAckKey, int> dic = new(new FackAckKeyComparer());
|
||||
|
||||
/// <summary>
|
||||
/// 发起方
|
||||
/// </summary>
|
||||
/// <param name="packet">一个完整的TCP/IP包</param>
|
||||
/// <returns></returns>
|
||||
public void Read(ReadOnlyMemory<byte> packet)
|
||||
{
|
||||
fixed (byte* ptr = packet.Span)
|
||||
{
|
||||
FakeAckPacket originPacket = new(ptr);
|
||||
if (originPacket.Version != 4 || originPacket.Protocol != ProtocolType.Tcp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (originPacket.TcpFlagFin || originPacket.TcpFlagRst)
|
||||
{
|
||||
FaceAckKey key = new() { srcAddr = originPacket.SrcAddr, srcPort = originPacket.SrcPort, dstAddr = originPacket.DstAddr, dstPort = originPacket.DstPort };
|
||||
dic.TryRemove(key, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 接收方
|
||||
/// </summary>
|
||||
/// <param name="packet">一个完整的TCP/IP包</param>
|
||||
/// <returns></returns>
|
||||
public void Write(ReadOnlyMemory<byte> packet, long bufferFree)
|
||||
{
|
||||
fixed (byte* ptr = packet.Span)
|
||||
{
|
||||
FakeAckPacket originPacket = new(ptr);
|
||||
if (originPacket.Version != 4 || originPacket.Protocol != ProtocolType.Tcp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
FaceAckKey key = new() { srcAddr = originPacket.SrcAddr, srcPort = originPacket.SrcPort, dstAddr = originPacket.DstAddr, dstPort = originPacket.DstPort };
|
||||
|
||||
if (originPacket.IsPshAck || originPacket.IsOnlyAck)
|
||||
{
|
||||
|
||||
if (dic.TryGetValue(key, out int wins) && originPacket.Window > 0)
|
||||
{
|
||||
ushort win = (ushort)Math.Max(Math.Min(bufferFree / wins, 65535), 4);
|
||||
originPacket.WriteWindow(ptr, win);
|
||||
}
|
||||
|
||||
}
|
||||
else if (originPacket.IsOnlySyn || originPacket.IsSynAck)
|
||||
{
|
||||
int windowScale = originPacket.FindWindowScale(ptr);
|
||||
dic.AddOrUpdate(key, windowScale, (a, b) => windowScale);
|
||||
}
|
||||
else if (originPacket.TcpFlagFin || originPacket.TcpFlagRst)
|
||||
{
|
||||
dic.TryRemove(key, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 四元组缓存key
|
||||
/// </summary>
|
||||
struct FaceAckKey
|
||||
{
|
||||
public uint srcAddr;
|
||||
public ushort srcPort;
|
||||
public uint dstAddr;
|
||||
public ushort dstPort;
|
||||
}
|
||||
/// <summary>
|
||||
/// 四元组缓存key比较器
|
||||
/// </summary>
|
||||
sealed class FackAckKeyComparer : IEqualityComparer<FaceAckKey>
|
||||
{
|
||||
public bool Equals(FaceAckKey x, FaceAckKey y)
|
||||
{
|
||||
return (x.srcAddr, x.srcPort, x.dstAddr, x.dstPort) == (y.srcAddr, y.srcPort, y.dstAddr, y.dstPort)
|
||||
|| (x.dstAddr, x.dstPort, x.srcAddr, x.srcPort) == (y.srcAddr, y.srcPort, y.dstAddr, y.dstPort);
|
||||
}
|
||||
|
||||
public int GetHashCode(FaceAckKey obj)
|
||||
{
|
||||
return (int)obj.srcAddr ^ obj.srcPort ^ (int)obj.dstAddr ^ obj.dstPort;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 数据包解析
|
||||
/// </summary>
|
||||
readonly unsafe struct FakeAckPacket
|
||||
{
|
||||
private readonly byte* ptr;
|
||||
|
||||
/// <summary>
|
||||
/// 协议版本
|
||||
/// </summary>
|
||||
public readonly byte Version => (byte)((*ptr >> 4) & 0b1111);
|
||||
public readonly ProtocolType Protocol => (ProtocolType)(*(ptr + 9));
|
||||
|
||||
/// <summary>
|
||||
/// 源地址
|
||||
/// </summary>
|
||||
public readonly uint SrcAddr => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 12));
|
||||
/// <summary>
|
||||
/// 源端口
|
||||
/// </summary>
|
||||
public readonly ushort SrcPort => BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + IPHeadLength));
|
||||
/// <summary>
|
||||
/// 目的地址
|
||||
/// </summary>
|
||||
public readonly uint DstAddr => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 16));
|
||||
/// <summary>
|
||||
/// 目标端口
|
||||
/// </summary>
|
||||
public readonly ushort DstPort => BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + IPHeadLength + 2));
|
||||
|
||||
/// <summary>
|
||||
/// IP头长度
|
||||
/// </summary>
|
||||
public readonly int IPHeadLength => (*ptr & 0b1111) * 4;
|
||||
/// <summary>
|
||||
/// 窗口大小
|
||||
/// </summary>
|
||||
public readonly ushort Window => BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + IPHeadLength + 14));
|
||||
|
||||
/// <summary>
|
||||
/// TCP Flag
|
||||
/// </summary>
|
||||
public readonly byte TcpFlag => *(ptr + IPHeadLength + 13);
|
||||
public readonly bool TcpFlagFin => (TcpFlag & 0b000001) != 0;
|
||||
public readonly bool TcpFlagSyn => (TcpFlag & 0b000010) != 0;
|
||||
public readonly bool TcpFlagRst => (TcpFlag & 0b000100) != 0;
|
||||
public readonly bool TcpFlagPsh => (TcpFlag & 0b001000) != 0;
|
||||
public readonly bool TcpFlagAck => (TcpFlag & 0b010000) != 0;
|
||||
public readonly bool TcpFlagUrg => (TcpFlag & 0b100000) != 0;
|
||||
|
||||
public readonly bool IsPshAck => TcpFlagPsh && TcpFlagAck;
|
||||
public readonly bool IsOnlyAck => TcpFlag == 0b00010000;
|
||||
public readonly bool IsOnlySyn => TcpFlag == 0b00000010;
|
||||
public readonly bool IsSynAck => TcpFlag == 0b00010010;
|
||||
|
||||
/// <summary>
|
||||
/// 加载TCP/IP包,必须是一个完整的TCP/IP包
|
||||
/// </summary>
|
||||
/// <param name="ptr">一个完整的TCP/IP包</param>
|
||||
public FakeAckPacket(byte* ptr)
|
||||
{
|
||||
this.ptr = ptr;
|
||||
}
|
||||
|
||||
public int FindWindowScale(byte* ipPtr)
|
||||
{
|
||||
//指针移动到TCP头开始位置
|
||||
byte* tcpPtr = ipPtr + ((*ipPtr & 0b1111) * 4);
|
||||
|
||||
//tcp头固定20,所以option从这里开始
|
||||
int index = 20;
|
||||
//tcp头结束位置,就是option结束位置
|
||||
int end = (*(tcpPtr + 12) >> 4) * 4;
|
||||
while (index < end)
|
||||
{
|
||||
byte kind = *(tcpPtr + index);
|
||||
//EOF结束符
|
||||
if (kind == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
byte length = *(tcpPtr + index + 1);
|
||||
//NOP 空选项
|
||||
if (kind == 1)
|
||||
{
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
//Window Scale 1kind 1length 1shiftCount
|
||||
else if (kind == 3 && length == 3)
|
||||
{
|
||||
return *(tcpPtr + index + 2);
|
||||
}
|
||||
index += length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public int WriteWindowScale(byte* ipPtr, byte windowScale = 7)
|
||||
{
|
||||
//指针移动到TCP头开始位置
|
||||
byte* tcpPtr = ipPtr + ((*ipPtr & 0b1111) * 4);
|
||||
|
||||
//tcp头固定20,所以option从这里开始
|
||||
int index = 20;
|
||||
//tcp头结束位置,就是option结束位置
|
||||
int end = (*(tcpPtr + 12) >> 4) * 4;
|
||||
while (index < end)
|
||||
{
|
||||
byte kind = *(tcpPtr + index);
|
||||
//EOF结束符
|
||||
if (kind == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
byte length = *(tcpPtr + index + 1);
|
||||
//NOP 空选项
|
||||
if (kind == 1)
|
||||
{
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
//Window Scale 1kind 1length 1shiftCount
|
||||
else if (kind == 3 && length == 3)
|
||||
{
|
||||
if (*(tcpPtr + index + 2) < windowScale)
|
||||
{
|
||||
*(tcpPtr + index + 2) = windowScale;
|
||||
ChecksumHelper.Checksum(ipPtr, false, true);
|
||||
}
|
||||
return *(tcpPtr + index + 2);
|
||||
}
|
||||
index += length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public void WriteWindow(byte* ipPtr, ushort window)
|
||||
{
|
||||
*(ushort*)(ipPtr + ((*ipPtr & 0b1111) * 4) + 14) = BinaryPrimitives.ReverseEndianness(Math.Max(window, (ushort)8));
|
||||
ChecksumHelper.Checksum(ipPtr, false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,31 +0,0 @@
|
||||
using linker.snat;
|
||||
using static linker.snat.LinkerDstMapping;
|
||||
|
||||
namespace linker.tun
|
||||
{
|
||||
internal sealed class LanMap : ILinkerTunPacketHook
|
||||
{
|
||||
public LinkerTunPacketHookLevel Level => LinkerTunPacketHookLevel.Lowest;
|
||||
|
||||
|
||||
private readonly LinkerDstMapping linkerDstMapping = new LinkerDstMapping();
|
||||
private bool checksum = true;
|
||||
public void SetMap(DstMapInfo[] maps,bool checksum = true)
|
||||
{
|
||||
linkerDstMapping.SetDsts(maps);
|
||||
this.checksum = checksum;
|
||||
}
|
||||
|
||||
public bool ReadAfter(ReadOnlyMemory<byte> packet)
|
||||
{
|
||||
linkerDstMapping.ToFakeDst(packet);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool WriteBefore(string srcId, ReadOnlyMemory<byte> packet)
|
||||
{
|
||||
linkerDstMapping.ToRealDst(packet, checksum);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,7 +1,9 @@
|
||||
using linker.libs;
|
||||
using linker.libs.timer;
|
||||
using linker.tun.device;
|
||||
using linker.tun.hook;
|
||||
using System.Net;
|
||||
using static linker.snat.LinkerDstMapping;
|
||||
using static linker.nat.LinkerDstMapping;
|
||||
|
||||
namespace linker.tun
|
||||
{
|
||||
@@ -19,12 +21,12 @@ namespace linker.tun
|
||||
|
||||
private string natError = string.Empty;
|
||||
public string NatError => natError;
|
||||
public bool AppNat => lanSnat.Running;
|
||||
public bool AppNat => lanDnat.Running;
|
||||
|
||||
private IPAddress address;
|
||||
private byte prefixLength;
|
||||
private readonly LanMap lanMap = new LanMap();
|
||||
private readonly LanSnat lanSnat = new LanSnat();
|
||||
private readonly LinkerTunPacketHookLanMap lanMap = new LinkerTunPacketHookLanMap();
|
||||
private readonly LinkerTunPacketHookLanDstProxy lanDnat = new LinkerTunPacketHookLanDstProxy();
|
||||
|
||||
|
||||
private readonly OperatingManager operatingManager = new OperatingManager();
|
||||
@@ -43,13 +45,12 @@ namespace linker.tun
|
||||
}
|
||||
|
||||
private ILinkerTunPacketHook[] hooks = [];
|
||||
private ILinkerTunPacketHook[] hooks1 = [];
|
||||
|
||||
public LinkerTunDeviceAdapter()
|
||||
{
|
||||
hooks = new ILinkerTunPacketHook[]
|
||||
{
|
||||
lanMap,lanSnat
|
||||
};
|
||||
hooks = new ILinkerTunPacketHook[] { lanMap, lanDnat };
|
||||
hooks1 = hooks.OrderByDescending(c => c.Level).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -187,7 +188,7 @@ namespace linker.tun
|
||||
if (linkerTunDevice.Running)
|
||||
{
|
||||
linkerTunDevice.SetNat(out natError);
|
||||
lanSnat.Setup(address, prefixLength, items, ref natError);
|
||||
lanDnat.Setup(address, prefixLength, items, ref natError);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
@@ -201,7 +202,7 @@ namespace linker.tun
|
||||
}
|
||||
natError = string.Empty;
|
||||
linkerTunDevice.RemoveNat(out string error);
|
||||
lanSnat.Shutdown();
|
||||
lanDnat.Shutdown();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -274,6 +275,7 @@ namespace linker.tun
|
||||
list.AddRange(hooks);
|
||||
|
||||
this.hooks = list.Distinct().OrderBy(c => c.Level).ToArray();
|
||||
hooks1 = this.hooks.OrderByDescending(c => c.Level).ToArray();
|
||||
}
|
||||
|
||||
private void Read()
|
||||
@@ -298,7 +300,9 @@ namespace linker.tun
|
||||
packet.Unpacket(buffer, 0, length);
|
||||
if (packet.DistIPAddress.Length == 0) continue;
|
||||
|
||||
for (int i = 0; i < hooks.Length; i++) if (hooks[i].ReadAfter(packet.IPPacket) == false) goto end;
|
||||
for (int i = 0; i < hooks1.Length; i++) if (hooks1[i].Read(packet.IPPacket) == false) goto end;
|
||||
ChecksumHelper.ChecksumWithZero(packet.IPPacket);
|
||||
|
||||
await linkerTunDeviceCallback.Callback(packet).ConfigureAwait(false);
|
||||
|
||||
end:;
|
||||
@@ -322,7 +326,9 @@ namespace linker.tun
|
||||
{
|
||||
if (linkerTunDevice == null || Status != LinkerTunDeviceStatus.Running || new LinkerTunDevicValidatePacket(buffer).IsValid == false) return false;
|
||||
|
||||
for (int i = 0; i < hooks.Length; i++) if (hooks[i].WriteBefore(srcId, buffer) == false) return false;
|
||||
for (int i = 0; i < hooks.Length; i++) if (hooks[i].Write(srcId, buffer) == false) return false;
|
||||
ChecksumHelper.ChecksumWithZero(buffer);
|
||||
|
||||
return linkerTunDevice.Write(buffer);
|
||||
}
|
||||
|
||||
@@ -332,14 +338,14 @@ namespace linker.tun
|
||||
/// <param name="maps"></param>
|
||||
public void SetMap(DstMapInfo[] maps)
|
||||
{
|
||||
lanMap.SetMap(maps, AppNat == false);
|
||||
lanMap.SetMap(maps);
|
||||
}
|
||||
/// <summary>
|
||||
/// 移除映射
|
||||
/// </summary>
|
||||
public void RemoveMap()
|
||||
{
|
||||
lanMap.SetMap([], AppNat == false);
|
||||
lanMap.SetMap([]);
|
||||
}
|
||||
|
||||
public async Task<bool> CheckAvailable(bool order = false)
|
||||
|
@@ -4,7 +4,7 @@ using System.Buffers.Binary;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text.Json.Serialization;
|
||||
namespace linker.tun
|
||||
namespace linker.tun.device
|
||||
{
|
||||
/// <summary>
|
||||
/// 设备接口
|
||||
@@ -136,60 +136,6 @@ namespace linker.tun
|
||||
public Task Callback(LinkerTunDevicPacket packet);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 数据包钩子
|
||||
/// </summary>
|
||||
public interface ILinkerTunPacketHook
|
||||
{
|
||||
public LinkerTunPacketHookLevel Level { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 从网卡读取到数据包后
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
/// <returns></returns>
|
||||
public bool ReadAfter(ReadOnlyMemory<byte> packet);
|
||||
/// <summary>
|
||||
/// 写入网卡前
|
||||
/// </summary>
|
||||
/// <param name="srcId"></param>
|
||||
/// <param name="packet"></param>
|
||||
/// <returns></returns>
|
||||
public bool WriteBefore(string srcId, ReadOnlyMemory<byte> packet);
|
||||
}
|
||||
/// <summary>
|
||||
/// 回调处理级别
|
||||
/// </summary>
|
||||
public enum LinkerTunPacketHookLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// 最低的,也是最早执行的,不要用这个
|
||||
/// </summary>
|
||||
Lowest = int.MinValue,
|
||||
Low9 = -9,
|
||||
Low8 = -8,
|
||||
Low7 = -7,
|
||||
Low6 = -6,
|
||||
Low5 = -5,
|
||||
Low4 = -4,
|
||||
Low3 = -3,
|
||||
Low2 = -2,
|
||||
Low1 = -1,
|
||||
Normal = 0,
|
||||
High1 = 1,
|
||||
High2 = 2,
|
||||
High3 = 3,
|
||||
High4 = 4,
|
||||
High5 = 5,
|
||||
High6 = 6,
|
||||
High7 = 7,
|
||||
High8 = 8,
|
||||
High9 = 9,
|
||||
/// <summary>
|
||||
/// 最高的,也是最晚执行的,不要用这个
|
||||
/// </summary>
|
||||
Highest = int.MaxValue
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 网卡端口转发
|
@@ -1,12 +1,11 @@
|
||||
|
||||
using linker.libs;
|
||||
using linker.libs;
|
||||
using linker.libs.extends;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace linker.tun
|
||||
namespace linker.tun.device
|
||||
{
|
||||
internal sealed class LinkerLinuxTunDevice : ILinkerTunDevice
|
||||
{
|
||||
@@ -30,13 +29,13 @@ namespace linker.tun
|
||||
{
|
||||
error = string.Empty;
|
||||
|
||||
this.name = info.Name;
|
||||
this.address = info.Address;
|
||||
this.prefixLength = info.PrefixLength;
|
||||
name = info.Name;
|
||||
address = info.Address;
|
||||
prefixLength = info.PrefixLength;
|
||||
|
||||
if (Running)
|
||||
{
|
||||
error = ($"Adapter already exists");
|
||||
error = $"Adapter already exists";
|
||||
return false;
|
||||
}
|
||||
if (Create(out error) == false)
|
||||
@@ -71,7 +70,7 @@ namespace linker.tun
|
||||
string str = CommandHelper.Linux(string.Empty, new string[] { $"ifconfig" });
|
||||
if (str.Contains(Name) == false)
|
||||
{
|
||||
CommandHelper.Linux(string.Empty, new string[] { $"ip tuntap add mode tun dev {Name}" },out error);
|
||||
CommandHelper.Linux(string.Empty, new string[] { $"ip tuntap add mode tun dev {Name}" }, out error);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -340,7 +339,7 @@ namespace linker.tun
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if(LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
||||
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
|
||||
{
|
||||
LoggerHelper.Instance.Error(ex.Message);
|
||||
LoggerHelper.Instance.Error(string.Join(",", buffer.ToArray()));
|
@@ -4,7 +4,7 @@ using Microsoft.Win32.SafeHandles;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace linker.tun
|
||||
namespace linker.tun.device
|
||||
{
|
||||
/// <summary>
|
||||
/// osx网卡实现,未测试
|
||||
@@ -31,13 +31,13 @@ namespace linker.tun
|
||||
|
||||
public bool Setup(LinkerTunDeviceSetupInfo info, out string error)
|
||||
{
|
||||
this.name = "utun0";
|
||||
name = "utun0";
|
||||
error = string.Empty;
|
||||
|
||||
this.address = info.Address;
|
||||
this.prefixLength = info.PrefixLength;
|
||||
address = info.Address;
|
||||
prefixLength = info.PrefixLength;
|
||||
|
||||
IntPtr arg = Marshal.AllocHGlobal(4);
|
||||
nint arg = Marshal.AllocHGlobal(4);
|
||||
Marshal.WriteInt32(arg, 0);
|
||||
try
|
||||
{
|
||||
@@ -51,7 +51,7 @@ namespace linker.tun
|
||||
error = $"open utun failed: {Marshal.GetLastWin32Error()}";
|
||||
return false;
|
||||
}
|
||||
this.name = $"utun{Marshal.ReadInt32(arg)}";
|
||||
name = $"utun{Marshal.ReadInt32(arg)}";
|
||||
|
||||
fsRead = new FileStream(safeFileHandle, FileAccess.Read, 65 * 1024, true);
|
||||
fsWrite = new FileStream(safeFileHandle, FileAccess.Write, 65 * 1024, true);
|
||||
@@ -99,7 +99,7 @@ namespace linker.tun
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
IPAddress network = NetworkHelper.ToNetworkIP(address, NetworkHelper.ToPrefixValue(this.prefixLength));
|
||||
IPAddress network = NetworkHelper.ToNetworkIP(address, NetworkHelper.ToPrefixValue(prefixLength));
|
||||
CommandHelper.Osx(string.Empty, new string[] { $"route delete -net {network}/{prefixLength} {address}" });
|
||||
}
|
||||
public void Refresh()
|
@@ -7,7 +7,7 @@ using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace linker.tun
|
||||
namespace linker.tun.device
|
||||
{
|
||||
[SupportedOSPlatform("windows")]
|
||||
internal sealed class LinkerWinTunDevice : ILinkerTunDevice
|
||||
@@ -16,7 +16,7 @@ namespace linker.tun
|
||||
public string Name => name;
|
||||
public bool Running => session != 0;
|
||||
|
||||
private IntPtr waitHandle = IntPtr.Zero, adapter = IntPtr.Zero, session = IntPtr.Zero;
|
||||
private nint waitHandle = nint.Zero, adapter = nint.Zero, session = nint.Zero;
|
||||
private int interfaceNumber = 0;
|
||||
private IPAddress address;
|
||||
private byte prefixLength = 24;
|
||||
@@ -31,15 +31,15 @@ namespace linker.tun
|
||||
|
||||
public bool Setup(LinkerTunDeviceSetupInfo info, out string error)
|
||||
{
|
||||
this.name = info.Name;
|
||||
this.address = info.Address;
|
||||
this.prefixLength = info.PrefixLength;
|
||||
name = info.Name;
|
||||
address = info.Address;
|
||||
prefixLength = info.PrefixLength;
|
||||
|
||||
|
||||
error = string.Empty;
|
||||
if (adapter != 0)
|
||||
{
|
||||
error = ($"Adapter already exists");
|
||||
error = $"Adapter already exists";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -49,10 +49,10 @@ namespace linker.tun
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
if (
|
||||
(
|
||||
|
||||
(adapter = WinTun.WintunCreateAdapter(name, name, ref guid)) == 0
|
||||
&& (adapter = WinTun.WintunOpenAdapter(name)) == 0
|
||||
)
|
||||
|
||||
|| (session = WinTun.WintunStartSession(adapter, 0x400000)) == 0
|
||||
)
|
||||
{
|
||||
@@ -64,12 +64,12 @@ namespace linker.tun
|
||||
}
|
||||
if (adapter == 0)
|
||||
{
|
||||
error = ($"Failed to create adapter {Marshal.GetLastWin32Error()}");
|
||||
error = $"Failed to create adapter {Marshal.GetLastWin32Error()}";
|
||||
return false;
|
||||
}
|
||||
if (session == 0)
|
||||
{
|
||||
error = ($"Failed to start session");
|
||||
error = $"Failed to start session";
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
@@ -92,7 +92,7 @@ namespace linker.tun
|
||||
Thread.Sleep(2000);
|
||||
}
|
||||
}
|
||||
error = ($"Failed to set adapter ip {Marshal.GetLastWin32Error()}");
|
||||
error = $"Failed to set adapter ip {Marshal.GetLastWin32Error()}";
|
||||
Shutdown();
|
||||
return false;
|
||||
|
||||
@@ -167,8 +167,8 @@ namespace linker.tun
|
||||
if (session == 0) return;
|
||||
try
|
||||
{
|
||||
IntPtr oldSession = session;
|
||||
IntPtr oldWaitHandle = waitHandle;
|
||||
nint oldSession = session;
|
||||
nint oldWaitHandle = waitHandle;
|
||||
|
||||
CommandHelper.Windows(string.Empty, new string[] { $"netsh interface set interface {Name} enable" });
|
||||
session = WinTun.WintunStartSession(adapter, 0x400000);
|
||||
@@ -204,7 +204,7 @@ namespace linker.tun
|
||||
}
|
||||
|
||||
SetupNat();
|
||||
IPAddress network = NetworkHelper.ToNetworkIP(this.address, NetworkHelper.ToPrefixValue(prefixLength));
|
||||
IPAddress network = NetworkHelper.ToNetworkIP(address, NetworkHelper.ToPrefixValue(prefixLength));
|
||||
RemoveOldNat($"{network}/{prefixLength}");
|
||||
|
||||
CommandHelper.PowerShell($"New-NetNat -Name {Name} -InternalIPInterfaceAddressPrefix {network}/{prefixLength}", [], out error);
|
||||
@@ -243,7 +243,7 @@ namespace linker.tun
|
||||
.Select(c => c.Split(':')).Where(c => c.Length == 2).Select(c => { c[0] = c[0].Trim(); c[1] = c[1].Trim(); return c; })
|
||||
.ToDictionary(c => c[0], c => c[1]);
|
||||
})
|
||||
.Where(c => (c.TryGetValue("Name", out string name) && name == Name) || (c.TryGetValue("InternalIPInterfaceAddressPrefix", out string ip) && ip == addressPrefix))
|
||||
.Where(c => c.TryGetValue("Name", out string name) && name == Name || c.TryGetValue("InternalIPInterfaceAddressPrefix", out string ip) && ip == addressPrefix)
|
||||
.Select(c => c["Name"]);
|
||||
foreach (var name in names)
|
||||
{
|
||||
@@ -351,7 +351,7 @@ namespace linker.tun
|
||||
if (session == 0) return Helper.EmptyArray;
|
||||
for (; tokenSource.IsCancellationRequested == false;)
|
||||
{
|
||||
IntPtr packetPtr = WinTun.WintunReceivePacket(session, out uint size);
|
||||
nint packetPtr = WinTun.WintunReceivePacket(session, out uint size);
|
||||
length = (int)size;
|
||||
|
||||
if (packetPtr != 0)
|
||||
@@ -382,7 +382,7 @@ namespace linker.tun
|
||||
{
|
||||
if (session == 0 || tokenSource.IsCancellationRequested) return false;
|
||||
|
||||
IntPtr packetPtr = WinTun.WintunAllocateSendPacket(session, (uint)packet.Length);
|
||||
nint packetPtr = WinTun.WintunAllocateSendPacket(session, (uint)packet.Length);
|
||||
if (packetPtr != 0)
|
||||
{
|
||||
packet.Span.CopyTo(new Span<byte>((byte*)packetPtr, packet.Length));
|
@@ -1,7 +1,7 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace linker.tun
|
||||
namespace linker.tun.device
|
||||
{
|
||||
internal static class LinuxAPI
|
||||
{
|
||||
@@ -33,7 +33,7 @@ namespace linker.tun
|
||||
[DllImport("libc.so.6", EntryPoint = "read", SetLastError = true)]
|
||||
internal static extern int Read(int handle, byte[] data, int length);
|
||||
[DllImport("libc.so.6", EntryPoint = "read", SetLastError = true)]
|
||||
internal static extern int Read(int handle, IntPtr data, int length);
|
||||
internal static extern int Read(int handle, nint data, int length);
|
||||
|
||||
[DllImport("libc.so.6", EntryPoint = "write", SetLastError = true)]
|
||||
internal static extern int Write(int handle, byte[] data, int length);
|
||||
@@ -45,7 +45,7 @@ namespace linker.tun
|
||||
Array.Resize(ref ifreqFREG0, 16);
|
||||
byte[] ifreqFREG1 = { 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
byte[] ifreq = BytesPlusBytes(ifreqFREG0, ifreqFREG1);
|
||||
return LinuxAPI.Ioctl(device, request, ifreq);
|
||||
return Ioctl(device, request, ifreq);
|
||||
}
|
||||
internal static byte[] BytesPlusBytes(byte[] A, byte[] B)
|
||||
{
|
@@ -1,20 +1,20 @@
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace linker.tun
|
||||
namespace linker.tun.device
|
||||
{
|
||||
internal static class OsxAPI
|
||||
{
|
||||
|
||||
// 定义 macOS 的 ioctl 命令(来自 <net/if_utun.h>)
|
||||
private const uint UTUN_CONTROL = 0x80000000; // 'u' << 24
|
||||
private const uint UTUN_CTRL_SET_IFNAME = (UTUN_CONTROL | 1);
|
||||
private const uint UTUN_CTRL_SET_IFNAME = UTUN_CONTROL | 1;
|
||||
|
||||
// P/Invoke 声明
|
||||
[DllImport("libc", EntryPoint = "ioctl", SetLastError = true)]
|
||||
private static extern int Ioctl(SafeFileHandle fd, uint request, IntPtr arg);
|
||||
private static extern int Ioctl(SafeFileHandle fd, uint request, nint arg);
|
||||
|
||||
public static int Ioctl(SafeFileHandle fd, IntPtr arg)
|
||||
public static int Ioctl(SafeFileHandle fd, nint arg)
|
||||
{
|
||||
return Ioctl(fd, UTUN_CTRL_SET_IFNAME, arg);
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace linker.tun
|
||||
namespace linker.tun.device
|
||||
{
|
||||
internal static class WinTun
|
||||
{
|
||||
@@ -52,12 +52,12 @@ namespace linker.tun
|
||||
[DllImport("iphlpapi.dll", SetLastError = true)]
|
||||
internal static extern uint CreateIpForwardEntry2(ref MIB_IPFORWARD_ROW2 Row);
|
||||
[DllImport("kernel32.dll")]
|
||||
internal static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
|
||||
internal static extern uint WaitForSingleObject(nint hHandle, uint dwMilliseconds);
|
||||
[DllImport("kernel32.dll")]
|
||||
internal static extern bool SetEvent(IntPtr hEvent);
|
||||
internal static extern bool SetEvent(nint hEvent);
|
||||
|
||||
[DllImport("wintun.dll", SetLastError = true)]
|
||||
internal static extern IntPtr WintunCreateAdapter(
|
||||
internal static extern nint WintunCreateAdapter(
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
string name,
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
@@ -68,31 +68,31 @@ namespace linker.tun
|
||||
internal static extern uint WintunGetRunningDriverVersion();
|
||||
|
||||
[DllImport("wintun.dll", SetLastError = true)]
|
||||
internal static extern void WintunGetAdapterLUID(IntPtr adapter, out ulong luid);
|
||||
internal static extern void WintunGetAdapterLUID(nint adapter, out ulong luid);
|
||||
|
||||
[DllImport("wintun.dll", SetLastError = true)]
|
||||
internal static extern IntPtr WintunStartSession(IntPtr adapter, uint capacity);
|
||||
internal static extern nint WintunStartSession(nint adapter, uint capacity);
|
||||
|
||||
[DllImport("wintun.dll", SetLastError = true)]
|
||||
internal static extern IntPtr WintunGetReadWaitEvent(IntPtr session);
|
||||
internal static extern nint WintunGetReadWaitEvent(nint session);
|
||||
|
||||
[DllImport("wintun.dll", SetLastError = true)]
|
||||
internal static extern IntPtr WintunReceivePacket(IntPtr session, out uint packetSize);
|
||||
internal static extern nint WintunReceivePacket(nint session, out uint packetSize);
|
||||
|
||||
[DllImport("wintun.dll", SetLastError = true)]
|
||||
internal static extern void WintunSendPacket(IntPtr session, IntPtr packet);
|
||||
internal static extern void WintunSendPacket(nint session, nint packet);
|
||||
|
||||
[DllImport("wintun.dll", SetLastError = true)]
|
||||
internal static extern void WintunEndSession(IntPtr session);
|
||||
internal static extern void WintunEndSession(nint session);
|
||||
|
||||
[DllImport("wintun.dll", SetLastError = true)]
|
||||
internal static extern void WintunCloseAdapter(IntPtr adapter);
|
||||
internal static extern void WintunCloseAdapter(nint adapter);
|
||||
|
||||
[DllImport("wintun.dll", SetLastError = true)]
|
||||
internal static extern IntPtr WintunAllocateSendPacket(IntPtr session, uint packetSize);
|
||||
internal static extern nint WintunAllocateSendPacket(nint session, uint packetSize);
|
||||
|
||||
[DllImport("wintun.dll", SetLastError = true)]
|
||||
internal static extern IntPtr WintunOpenAdapter(
|
||||
internal static extern nint WintunOpenAdapter(
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
string name);
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace linker.tun
|
||||
internal static extern bool WintunDeleteDriver();
|
||||
|
||||
[DllImport("wintun.dll", SetLastError = true)]
|
||||
internal static extern void WintunReleaseReceivePacket(IntPtr session, IntPtr packet);
|
||||
internal static extern void WintunReleaseReceivePacket(nint session, nint packet);
|
||||
|
||||
[DllImport("wintun.dll", SetLastError = true)]
|
||||
internal static extern void WintunSetLogger(WINTUN_LOGGER_CALLBACK newLogger);
|
61
src/linker.tun/hook/ILinkerTunPacketHook.cs
Normal file
61
src/linker.tun/hook/ILinkerTunPacketHook.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
|
||||
using linker.tun.device;
|
||||
|
||||
namespace linker.tun.hook
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据包钩子
|
||||
/// </summary>
|
||||
public interface ILinkerTunPacketHook
|
||||
{
|
||||
public LinkerTunPacketHookLevel Level { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 从网卡读取到数据包后
|
||||
/// </summary>
|
||||
/// <param name="packet"></param>
|
||||
/// <returns></returns>
|
||||
public bool Read(ReadOnlyMemory<byte> packet);
|
||||
/// <summary>
|
||||
/// 写入网卡前
|
||||
/// </summary>
|
||||
/// <param name="srcId"></param>
|
||||
/// <param name="packet"></param>
|
||||
/// <returns></returns>
|
||||
public bool Write(string srcId, ReadOnlyMemory<byte> packet);
|
||||
}
|
||||
/// <summary>
|
||||
/// 回调处理级别
|
||||
/// </summary>
|
||||
public enum LinkerTunPacketHookLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// 最低的,也是最早执行的,不要用这个
|
||||
/// </summary>
|
||||
Lowest = int.MinValue,
|
||||
Low9 = -9,
|
||||
Low8 = -8,
|
||||
Low7 = -7,
|
||||
Low6 = -6,
|
||||
Low5 = -5,
|
||||
Low4 = -4,
|
||||
Low3 = -3,
|
||||
Low2 = -2,
|
||||
Low1 = -1,
|
||||
Normal = 0,
|
||||
High1 = 1,
|
||||
High2 = 2,
|
||||
High3 = 3,
|
||||
High4 = 4,
|
||||
High5 = 5,
|
||||
High6 = 6,
|
||||
High7 = 7,
|
||||
High8 = 8,
|
||||
High9 = 9,
|
||||
/// <summary>
|
||||
/// 最高的,也是最晚执行的,不要用这个
|
||||
/// </summary>
|
||||
Highest = int.MaxValue
|
||||
}
|
||||
|
||||
}
|
68
src/linker.tun/hook/LinkerTunPacketHookLanDstProxy.cs
Normal file
68
src/linker.tun/hook/LinkerTunPacketHookLanDstProxy.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using linker.libs;
|
||||
using linker.nat;
|
||||
using linker.tun.device;
|
||||
using System.Net;
|
||||
|
||||
namespace linker.tun.hook
|
||||
{
|
||||
internal sealed class LinkerTunPacketHookLanDstProxy : ILinkerTunPacketHook
|
||||
{
|
||||
public LinkerTunPacketHookLevel Level => LinkerTunPacketHookLevel.Highest;
|
||||
private LinkerDstProxy linkerDstNat = new LinkerDstProxy();
|
||||
|
||||
public bool Running => linkerDstNat.Running;
|
||||
|
||||
public LinkerTunPacketHookLanDstProxy()
|
||||
{
|
||||
}
|
||||
|
||||
public void Setup(IPAddress address, byte prefixLength, LinkerTunAppNatItemInfo[] items, ref string error)
|
||||
{
|
||||
if (OperatingSystem.IsWindows() == false) return;
|
||||
|
||||
if (address == null || address.Equals(IPAddress.Any) || prefixLength == 0)
|
||||
{
|
||||
error = "DProxy need CIDR,like 10.18.18.0/24";
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
IPAddress network = NetworkHelper.ToNetworkIP(address, NetworkHelper.ToPrefixValue(prefixLength));
|
||||
string result = CommandHelper.PowerShell($"Get-NetNat", [], out string e);
|
||||
if (string.IsNullOrWhiteSpace(result) == false && result.Contains($"{network}/{prefixLength}"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.Instance.Error(ex);
|
||||
}
|
||||
|
||||
Shutdown();
|
||||
linkerDstNat.Setup(address, items.Select(c => new ValueTuple<IPAddress, byte>(c.IP, c.PrefixLength)).ToArray(), ref error);
|
||||
}
|
||||
public void Shutdown()
|
||||
{
|
||||
try
|
||||
{
|
||||
linkerDstNat.Shutdown();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
public bool Read(ReadOnlyMemory<byte> packet)
|
||||
{
|
||||
linkerDstNat.Read(packet);
|
||||
return true;
|
||||
}
|
||||
public bool Write(string srcId, ReadOnlyMemory<byte> packet)
|
||||
{
|
||||
return linkerDstNat.Write(packet);
|
||||
}
|
||||
}
|
||||
}
|
29
src/linker.tun/hook/LinkerTunPacketHookLanMap.cs
Normal file
29
src/linker.tun/hook/LinkerTunPacketHookLanMap.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using linker.nat;
|
||||
using static linker.nat.LinkerDstMapping;
|
||||
|
||||
namespace linker.tun.hook
|
||||
{
|
||||
internal sealed class LinkerTunPacketHookLanMap : ILinkerTunPacketHook
|
||||
{
|
||||
public LinkerTunPacketHookLevel Level => LinkerTunPacketHookLevel.Lowest;
|
||||
|
||||
|
||||
private readonly LinkerDstMapping linkerDstMapping = new LinkerDstMapping();
|
||||
public void SetMap(DstMapInfo[] maps)
|
||||
{
|
||||
linkerDstMapping.SetDsts(maps);
|
||||
}
|
||||
|
||||
public bool Read(ReadOnlyMemory<byte> packet)
|
||||
{
|
||||
linkerDstMapping.ToFakeDst(packet);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Write(string srcId, ReadOnlyMemory<byte> packet)
|
||||
{
|
||||
linkerDstMapping.ToRealDst(packet);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,10 +1,11 @@
|
||||
using linker.libs;
|
||||
using linker.snat;
|
||||
using linker.nat;
|
||||
using linker.tun.device;
|
||||
using System.Net;
|
||||
|
||||
namespace linker.tun
|
||||
namespace linker.tun.hook
|
||||
{
|
||||
internal sealed class LanSnat : ILinkerTunPacketHook
|
||||
internal sealed class LinkerTunPacketHookLanSrcNat : ILinkerTunPacketHook
|
||||
{
|
||||
public LinkerTunPacketHookLevel Level => LinkerTunPacketHookLevel.Highest;
|
||||
public bool Running => linkerSrcNat.Running;
|
||||
@@ -12,7 +13,7 @@ namespace linker.tun
|
||||
private LinkerSrcNat linkerSrcNat = new LinkerSrcNat();
|
||||
|
||||
|
||||
public LanSnat()
|
||||
public LinkerTunPacketHookLanSrcNat()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -64,11 +65,11 @@ namespace linker.tun
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
public bool ReadAfter(ReadOnlyMemory<byte> packet)
|
||||
public bool Read(ReadOnlyMemory<byte> packet)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
public bool WriteBefore(string srcId, ReadOnlyMemory<byte> packet)
|
||||
public bool Write(string srcId, ReadOnlyMemory<byte> packet)
|
||||
{
|
||||
return linkerSrcNat.Running == false || linkerSrcNat.Inject(packet) == false;
|
||||
}
|
@@ -38,7 +38,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\linker.libs\linker.libs.csproj" />
|
||||
<ProjectReference Include="..\linker.snat\linker.snat.csproj" />
|
||||
<ProjectReference Include="..\linker.nat\linker.nat.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="w-100">
|
||||
<div>
|
||||
<span class="yellow">使用系统NAT或应用层SNAT</span>
|
||||
<span class="yellow">使用系统NAT或应用层DNAT</span>
|
||||
</div>
|
||||
<div class="wrap">
|
||||
<el-table stripe :data="state.lans" border size="small" width="100%" height="400px" @cell-dblclick="handleCellClick">
|
||||
|
@@ -18,7 +18,7 @@
|
||||
<strong class="yellow" :title="tuntap.list[item.MachineId].NatError">{{ tuntap.list[item.MachineId].IP }}</strong>
|
||||
</template>
|
||||
<template v-else-if="tuntap.list[item.MachineId].AppNat && tuntap.list[item.MachineId].running">
|
||||
<strong class="app-nat" :title="`虚拟网卡IP\r\n应用层SNAT\r\n如果无法使用点对网,请重启一次系统`">{{ tuntap.list[item.MachineId].IP }}</strong>
|
||||
<strong class="app-nat" :title="`虚拟网卡IP\r\n应用层DNAT`">{{ tuntap.list[item.MachineId].IP }}</strong>
|
||||
</template>
|
||||
<template v-else-if="tuntap.list[item.MachineId].running">
|
||||
<strong class="green gateway" :title="`虚拟网卡IP\r\n系统NAT`">{{ tuntap.list[item.MachineId].IP }}</strong>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
v1.9.1
|
||||
2025-09-20 01:21:24
|
||||
2025-09-21 18:35:14
|
||||
1. 一些累计更新
|
||||
2. 服务器转发多节点
|
||||
3. 虚拟网卡下伪造ACK为TCP-in-TCP隧道提速
|
||||
|
Reference in New Issue
Block a user