Files
linker/linker.tunnel/proxy/TunnelProxyUdp.cs
snltty aa65b16892 sync
2024-07-10 19:03:21 +08:00

466 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using linker.tunnel.connection;
using linker.libs;
using linker.libs.extends;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
namespace linker.tunnel.proxy
{
public partial class TunnelProxy
{
private ConcurrentDictionary<int, AsyncUserUdpToken> udpListens = new ConcurrentDictionary<int, AsyncUserUdpToken>();
private ConcurrentDictionary<ConnectIdUdp, AsyncUserUdpTokenTarget> udpConnections = new(new ConnectIdUdpComparer());
/// <summary>
/// 监听一个端口
/// </summary>
/// <param name="port"></param>
private void StartUdp(IPEndPoint ep, byte buffersize)
{
try
{
Socket socketUdp = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socketUdp.EnableBroadcast = true;
socketUdp.WindowsUdpBug();
socketUdp.Bind(ep);
AsyncUserUdpToken asyncUserUdpToken = new AsyncUserUdpToken
{
ListenPort = ep.Port,
SourceSocket = socketUdp,
Proxy = new ProxyInfo { Port = (ushort)ep.Port, Step = ProxyStep.Forward, ConnectId = 0, Protocol = ProxyProtocol.Udp, Direction = ProxyDirection.Forward }
};
udpListens.AddOrUpdate(ep.Port, asyncUserUdpToken, (a, b) => asyncUserUdpToken);
_ = ReceiveUdp(asyncUserUdpToken, buffersize);
}
catch (Exception ex)
{
LoggerHelper.Instance.Error(ex);
}
}
private async Task ReceiveUdp(AsyncUserUdpToken token, byte buffersize)
{
byte[] bytes = new byte[(1 << buffersize) * 1024];
IPEndPoint TempRemoteEP = new IPEndPoint(IPAddress.Any, IPEndPoint.MinPort);
while (true)
{
try
{
SocketReceiveFromResult result = await token.SourceSocket.ReceiveFromAsync(bytes, TempRemoteEP).ConfigureAwait(false);
token.Proxy.SourceEP = result.RemoteEndPoint as IPEndPoint;
token.Proxy.Data = bytes.AsMemory(0, result.ReceivedBytes);
await ConnectTunnelConnection(token).ConfigureAwait(false);
if (token.Proxy.TargetEP != null)
{
if (token.Connection != null)
{
await SendToConnection(token).ConfigureAwait(false);
}
else if (token.Connections != null)
{
await SendToConnections(token).ConfigureAwait(false);
}
}
}
catch (Exception ex)
{
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Error(ex);
}
break;
}
}
CloseClientSocket(token);
}
/// <summary>
/// 发送给对方,通过隧道
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
private async Task SendToConnection(AsyncUserUdpToken token)
{
if (token.Connection == null) return;
SemaphoreSlim semaphoreSlim = token.Proxy.Direction == ProxyDirection.Forward ? semaphoreSlimForward : semaphoreSlimReverse;
await semaphoreSlim.WaitAsync().ConfigureAwait(false);
byte[] connectData = token.Proxy.ToBytes(out int length);
try
{
bool res = await token.Connection.SendAsync(connectData.AsMemory(0, length)).ConfigureAwait(false);
if (res == false)
{
CloseClientSocket(token);
}
}
catch (Exception)
{
CloseClientSocket(token);
}
finally
{
token.Proxy.Return(connectData);
semaphoreSlim.Release();
}
}
/// <summary>
/// 发送给很多对方,通过隧道
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
private async Task SendToConnections(AsyncUserUdpToken token)
{
SemaphoreSlim semaphoreSlim = token.Proxy.Direction == ProxyDirection.Forward ? semaphoreSlimForward : semaphoreSlimReverse;
await semaphoreSlim.WaitAsync().ConfigureAwait(false);
byte[] connectData = token.Proxy.ToBytes(out int length);
try
{
await Task.WhenAll(token.Connections.Select(c => c.SendAsync(connectData.AsMemory(0, length)))).ConfigureAwait(false);
}
catch (Exception)
{
CloseClientSocket(token);
}
finally
{
token.Proxy.Return(connectData);
semaphoreSlim.Release();
}
}
/// <summary>
/// 连接UDP隧道
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
protected virtual async ValueTask ConnectTunnelConnection(AsyncUserUdpToken token)
{
await ValueTask.CompletedTask;
}
/// <summary>
/// 收到隧道数据确定是udp该连接连接该发送发送
/// </summary>
/// <param name="tunnelToken"></param>
/// <returns></returns>
private async Task SendToSocketUdp(AsyncUserTunnelToken tunnelToken)
{
if (tunnelToken.Proxy.Direction == ProxyDirection.Forward)
{
ConnectIdUdp connectId = tunnelToken.GetUdpConnectId();
try
{
IPEndPoint target = new IPEndPoint(tunnelToken.Proxy.TargetEP.Address, tunnelToken.Proxy.TargetEP.Port);
if (udpConnections.TryGetValue(connectId, out AsyncUserUdpTokenTarget token))
{
token.Connection = tunnelToken.Connection;
await token.TargetSocket.SendToAsync(tunnelToken.Proxy.Data, target).ConfigureAwait(false);
token.Update();
return;
}
_ = ConnectUdp(tunnelToken, target);
}
catch (Exception ex)
{
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Error(ex);
}
if (udpConnections.TryRemove(connectId, out AsyncUserUdpTokenTarget token))
{
CloseClientSocket(token);
}
}
}
else
{
if (udpListens.TryGetValue(tunnelToken.Proxy.Port, out AsyncUserUdpToken asyncUserUdpToken))
{
try
{
asyncUserUdpToken.Connection = tunnelToken.Connection;
if (await ConnectionReceiveUdp(tunnelToken, asyncUserUdpToken).ConfigureAwait(false) == false)
{
await asyncUserUdpToken.SourceSocket.SendToAsync(tunnelToken.Proxy.Data, tunnelToken.Proxy.SourceEP).ConfigureAwait(false);
}
}
catch (Exception ex)
{
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Error(ex);
}
}
}
}
}
private async Task ConnectUdp(AsyncUserTunnelToken tunnelToken, IPEndPoint target)
{
Socket socket = new Socket(target.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
socket.WindowsUdpBug();
await socket.SendToAsync(tunnelToken.Proxy.Data, target).ConfigureAwait(false);
ConnectIdUdp connectId = tunnelToken.GetUdpConnectId();
AsyncUserUdpTokenTarget udpToken = new AsyncUserUdpTokenTarget
{
Proxy = new ProxyInfo
{
ConnectId = tunnelToken.Proxy.ConnectId,
Direction = ProxyDirection.Reverse,
Protocol = tunnelToken.Proxy.Protocol,
SourceEP = tunnelToken.Proxy.SourceEP,
TargetEP = tunnelToken.Proxy.TargetEP,
Step = tunnelToken.Proxy.Step,
Port = tunnelToken.Proxy.Port,
BufferSize = tunnelToken.Proxy.BufferSize,
},
TargetSocket = socket,
ConnectId = connectId,
Connection = tunnelToken.Connection,
Buffer = new byte[(1 << tunnelToken.Proxy.BufferSize) * 1024]
};
udpToken.Proxy.Direction = ProxyDirection.Reverse;
udpConnections.AddOrUpdate(connectId, udpToken, (a, b) => udpToken);
try
{
while (true)
{
SocketReceiveFromResult result = await socket.ReceiveFromAsync(udpToken.Buffer, SocketFlags.None, target).ConfigureAwait(false);
udpToken.Proxy.Data = udpToken.Buffer.AsMemory(0, result.ReceivedBytes);
udpToken.Update();
await SendToConnection(udpToken).ConfigureAwait(false);
}
}
catch (Exception)
{
}
finally
{
if (udpConnections.TryRemove(connectId, out AsyncUserUdpTokenTarget token))
{
CloseClientSocket(token);
}
}
}
/// <summary>
/// 连接对方返回UDP是否要自己处理
/// </summary>
/// <param name="token"></param>
/// <param name="asyncUserUdpToken"></param>
/// <returns>true表示自己已经处理过了不需要再处理了</returns>
protected virtual async ValueTask<bool> ConnectionReceiveUdp(AsyncUserTunnelToken token, AsyncUserUdpToken asyncUserUdpToken)
{
return await ValueTask.FromResult(false);
}
/// <summary>
/// b端接收到服务的数据通过隧道发送给a
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
private async Task SendToConnection(AsyncUserUdpTokenTarget token)
{
// SemaphoreSlim semaphoreSlim = token.Proxy.Direction == ProxyDirection.Forward ? semaphoreSlimForward : semaphoreSlimReverse;
// await semaphoreSlim.WaitAsync();
byte[] connectData = token.Proxy.ToBytes(out int length);
try
{
bool res = await token.Connection.SendAsync(connectData.AsMemory(0, length)).ConfigureAwait(false);
if (res == false)
{
CloseClientSocket(token);
}
}
catch (Exception)
{
CloseClientSocket(token);
}
finally
{
token.Proxy.Return(connectData);
// semaphoreSlim.Release();
}
}
/// <summary>
/// 检查udp过期
/// </summary>
private void TaskUdp()
{
Task.Run(async () =>
{
while (true)
{
var connections = udpConnections.Where(c => c.Value.Timeout).Select(c => c.Key).ToList();
foreach (var item in connections)
{
if (udpConnections.TryRemove(item, out AsyncUserUdpTokenTarget token))
{
try
{
token.Clear();
}
catch (Exception)
{
}
}
}
await Task.Delay(5000).ConfigureAwait(false);
}
});
}
private void CloseClientSocketUdp(ITunnelConnection connection)
{
int hashcode1 = connection.RemoteMachineId.GetHashCode();
int hashcode2 = connection.TransactionId.GetHashCode();
var tokens = udpConnections.Where(c => c.Key.hashcode1 == hashcode1 && c.Key.hashcode2 == hashcode2).ToList();
foreach (var item in tokens)
{
try
{
if (udpConnections.TryRemove(item.Key, out AsyncUserUdpTokenTarget token))
{
token.Clear();
}
}
catch (Exception)
{
}
}
}
private void CloseClientSocket(AsyncUserUdpToken token)
{
if (token == null) return;
token.Clear();
}
private void CloseClientSocket(AsyncUserUdpTokenTarget token)
{
if (token == null) return;
if (udpConnections.TryRemove(token.ConnectId, out _))
{
token.Clear();
}
token.Clear();
}
public void StopUdp()
{
foreach (var item in udpListens)
{
item.Value.Clear();
}
udpListens.Clear();
foreach (var item in udpConnections)
{
item.Value.Clear();
}
udpConnections.Clear();
}
public virtual void StopUdp(int port)
{
if (udpListens.TryRemove(port, out AsyncUserUdpToken udpClient))
{
udpClient.Clear();
}
if (udpListens.Count == 0)
{
foreach (var item in udpConnections)
{
item.Value.Clear();
}
udpConnections.Clear();
}
}
}
public sealed class AsyncUserUdpToken
{
public int ListenPort { get; set; }
public Socket SourceSocket { get; set; }
public ITunnelConnection Connection { get; set; }
public List<ITunnelConnection> Connections { get; set; }
public ProxyInfo Proxy { get; set; }
public uint TargetIP { get; set; }
public void Clear()
{
SourceSocket?.SafeClose();
SourceSocket = null;
GC.Collect();
GC.SuppressFinalize(this);
}
}
public sealed class AsyncUserUdpTokenTarget
{
public Socket TargetSocket { get; set; }
public ITunnelConnection Connection { get; set; }
public ProxyInfo Proxy { get; set; }
public ConnectIdUdp ConnectId { get; set; }
public byte[] Buffer { get; set; }
public long LastTime { get; set; } = Environment.TickCount64;
public bool Timeout => Environment.TickCount64 - LastTime > 15000;
public void Clear()
{
TargetSocket?.SafeClose();
TargetSocket = null;
GC.Collect();
GC.SuppressFinalize(this);
}
public void Update()
{
LastTime = Environment.TickCount64;
}
}
public sealed class ConnectIdUdpComparer : IEqualityComparer<ConnectIdUdp>
{
public bool Equals(ConnectIdUdp x, ConnectIdUdp y)
{
return x.source != null && x.source.Equals(y.source) && x.connectId == y.connectId && x.hashcode1 == y.hashcode1 && x.hashcode2 == y.hashcode2;
}
public int GetHashCode(ConnectIdUdp obj)
{
if (obj.source == null) return 0;
return obj.source.GetHashCode() ^ obj.connectId.GetHashCode() ^ obj.hashcode1 ^ obj.hashcode2;
}
}
public readonly struct ConnectIdUdp
{
public readonly IPEndPoint source { get; }
public readonly ulong connectId { get; }
public int hashcode1 { get; }
public int hashcode2 { get; }
public ConnectIdUdp(ulong connectId, IPEndPoint source, int hashcode1, int hashcode2)
{
this.connectId = connectId;
this.source = source;
this.hashcode1 = hashcode1;
this.hashcode2 = hashcode2;
}
}
}