Files
linker/linker/plugins/socks5/Socks5Proxy.cs
snltty 921c34af9e sync
2024-10-31 17:45:21 +08:00

278 lines
11 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.config;
using linker.tunnel;
using linker.tunnel.connection;
using linker.libs;
using linker.libs.socks5;
using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using linker.plugins.tunnel;
using linker.plugins.client;
using linker.plugins.socks5.config;
using System.Text;
using System.Collections.Generic;
using linker.plugins.relay.client;
namespace linker.plugins.socks5
{
public sealed partial class TunnelProxy : TunnelBase
{
private IPEndPoint proxyEP;
private uint[] maskValues = Array.Empty<uint>();
private readonly ConcurrentDictionary<uint, string> ip2MachineDic = new ConcurrentDictionary<uint, string>();
protected override string TransactionId => "socks5";
public TunnelProxy(FileConfig config, TunnelTransfer tunnelTransfer, RelayTransfer relayTransfer, ClientSignInTransfer clientSignInTransfer, ClientSignInState clientSignInState)
: base(config, tunnelTransfer, relayTransfer, clientSignInTransfer, clientSignInState)
{
TaskUdp();
}
/// <summary>
/// 设置IP等下有连接进来用IP匹配才能知道这个连接是要连谁
/// </summary>
/// <param name="ips"></param>
public void SetIPs(Socks5LanIPAddress[] ips)
{
var dic = ips.GroupBy(c => c.NetWork).ToDictionary(c => c.Key, d => d.Select(e => e.MachineId).ToList());
foreach (var item in dic.Where(c => c.Value.Count > 0))
{
string machineId = item.Value[0];
ip2MachineDic.AddOrUpdate(item.Key, machineId, (a, b) => machineId);
}
maskValues = ips.Select(c => c.MaskValue).Distinct().OrderBy(c => c).ToArray();
}
/// <summary>
/// 设置IP等下有连接进来用IP匹配才能知道这个连接是要连谁
/// </summary>
/// <param name="machineId"></param>
/// <param name="ip"></param>
public void SetIP(string machineId, uint ip)
{
ip2MachineDic.AddOrUpdate(ip, machineId, (a, b) => machineId);
}
public void Start(int port)
{
if (proxyEP != null)
{
Stop(proxyEP.Port);
}
Start(new IPEndPoint(IPAddress.Any, port), 3);
proxyEP = new IPEndPoint(IPAddress.Any, LocalEndpoint.Port);
}
protected override void Connected(ITunnelConnection connection)
{
BindConnectionReceive(connection);
}
/// <summary>
/// 接收隧道的数据
/// </summary>
/// <param name="connection"></param>
private void BindConnectionReceive(ITunnelConnection connection)
{
connection.BeginReceive(this, new AsyncUserTunnelToken
{
Connection = connection,
Proxy = new ProxyInfo { }
});
}
/// <summary>
/// tcp连接隧道
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
private async ValueTask<bool> ConnectTunnelConnection(AsyncUserToken token)
{
int length = await token.Socket.ReceiveAsync(token.Buffer.AsMemory(), SocketFlags.None);
if (length == 0)
{
return true;
}
token.Proxy.Data = token.Buffer.AsMemory(0, length);
token.Proxy.TargetEP = null;
//步骤request
token.Proxy.Rsv = (byte)Socks5EnumStep.Request;
if (await ReceiveCommandData(token) == false)
{
return true;
}
await token.Socket.SendAsync(new byte[] { 0x05, 0x00 }).ConfigureAwait(false);
//步骤command
token.Proxy.Data = Helper.EmptyArray;
token.Proxy.Rsv = (byte)Socks5EnumStep.Command;
if (await ReceiveCommandData(token).ConfigureAwait(false) == false)
{
return true;
}
Socks5EnumRequestCommand command = (Socks5EnumRequestCommand)token.Proxy.Data.Span[1];
//是UDP中继不做连接操作等UDP数据过去的时候再绑定
if (command == Socks5EnumRequestCommand.UdpAssociate)
{
await token.Socket.SendAsync(Socks5Parser.MakeConnectResponse(new IPEndPoint(IPAddress.Any, proxyEP.Port), (byte)Socks5EnumResponseCommand.ConnecSuccess).AsMemory()).ConfigureAwait(false);
return false;
}
//获取远端地址
uint targetIP;
ReadOnlyMemory<byte> ipArray = Socks5Parser.GetRemoteEndPoint(token.Proxy.Data, out Socks5EnumAddressType addressType, out ushort port, out int index);
token.Proxy.Data = token.Proxy.Data.Slice(index);
if (addressType == Socks5EnumAddressType.IPV6)
{
byte[] response1 = Socks5Parser.MakeConnectResponse(new IPEndPoint(IPAddress.Any, 0), (byte)Socks5EnumResponseCommand.AddressNotAllow);
await token.Socket.SendAsync(response1.AsMemory()).ConfigureAwait(false);
return true;
}
if (addressType == Socks5EnumAddressType.Domain)
{
if (IPAddress.TryParse(Encoding.UTF8.GetString(ipArray.Span), out IPAddress ip) == false)
{
byte[] response1 = Socks5Parser.MakeConnectResponse(new IPEndPoint(IPAddress.Any, 0), (byte)Socks5EnumResponseCommand.AddressNotAllow);
await token.Socket.SendAsync(response1.AsMemory()).ConfigureAwait(false);
return true;
}
token.Proxy.TargetEP = new IPEndPoint(ip, port);
targetIP = BinaryPrimitives.ReadUInt32BigEndian(ip.GetAddressBytes());
}
else
{
token.Proxy.TargetEP = new IPEndPoint(new IPAddress(ipArray.Span), port);
targetIP = BinaryPrimitives.ReadUInt32BigEndian(ipArray.Span);
}
token.Connection = await ConnectTunnel(targetIP).ConfigureAwait(false);
Socks5EnumResponseCommand resp = token.Connection != null && token.Connection.Connected ? Socks5EnumResponseCommand.ConnecSuccess : Socks5EnumResponseCommand.NetworkError;
byte[] response = Socks5Parser.MakeConnectResponse(new IPEndPoint(IPAddress.Any, 0), (byte)resp);
await token.Socket.SendAsync(response.AsMemory()).ConfigureAwait(false);
return true;
}
/// <summary>
/// udp连接隧道
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
private async ValueTask ConnectTunnelConnection(AsyncUserUdpToken token)
{
ReadOnlyMemory<byte> ipArray = Socks5Parser.GetRemoteEndPoint(token.Proxy.Data, out Socks5EnumAddressType addressType, out ushort port, out int index);
if (addressType == Socks5EnumAddressType.IPV6)
{
return;
}
uint targetIP = BinaryPrimitives.ReadUInt32BigEndian(ipArray.Span);
token.Proxy.TargetEP = new IPEndPoint(new IPAddress(ipArray.Span), port);
//解析出udp包的数据部分
token.Proxy.Data = Socks5Parser.GetUdpData(token.Proxy.Data);
token.Connection = await ConnectTunnel(targetIP).ConfigureAwait(false);
}
/// <summary>
/// udp回复消息自定义处理因为socks5要打包一些格式才能返回
/// </summary>
/// <param name="token"></param>
/// <param name="asyncUserUdpToken"></param>
/// <returns></returns>
private async ValueTask<bool> ConnectionReceiveUdp(AsyncUserTunnelToken token, AsyncUserUdpToken asyncUserUdpToken)
{
IPEndPoint target = token.Proxy.TargetEP;
byte[] data = Socks5Parser.MakeUdpResponse(target, token.Proxy.Data, out int length);
try
{
await asyncUserUdpToken.SourceSocket.SendToAsync(data.AsMemory(0, length), token.Proxy.SourceEP).ConfigureAwait(false);
}
catch (Exception ex)
{
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Error(ex);
}
}
finally
{
Socks5Parser.Return(data);
}
return true;
}
/// <summary>
/// 打洞或者中继
/// </summary>
/// <param name="ip"></param>
/// <returns></returns>
private async ValueTask<ITunnelConnection> ConnectTunnel(uint ip)
{
//匹配掩码查找
for (int i = 0; i < maskValues.Length; i++)
{
uint network = ip & maskValues[i];
if (ip2MachineDic.TryGetValue(network, out string machineId))
{
return await ConnectTunnel(machineId, TunnelProtocolType.Udp).ConfigureAwait(false);
}
}
return null;
}
/// <summary>
/// 接收socks5完整数据包
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
private async Task<bool> ReceiveCommandData(AsyncUserToken token)
{
int totalLength = token.Proxy.Data.Length;
EnumProxyValidateDataResult validate = ValidateData(token.Proxy);
//太短
while ((validate & EnumProxyValidateDataResult.TooShort) == EnumProxyValidateDataResult.TooShort)
{
totalLength += await token.Socket.ReceiveAsync(token.Buffer.AsMemory(totalLength), SocketFlags.None).ConfigureAwait(false);
token.Proxy.Data = token.Buffer.AsMemory(0, totalLength);
validate = ValidateData(token.Proxy);
}
//不短,又不相等,直接关闭连接
if ((validate & EnumProxyValidateDataResult.Equal) != EnumProxyValidateDataResult.Equal)
{
return false;
}
return true;
}
/// <summary>
/// 验证socks5数据包完整性
/// </summary>
/// <param name="info"></param>
/// <returns></returns>
private EnumProxyValidateDataResult ValidateData(ProxyInfo info)
{
return (Socks5EnumStep)info.Rsv switch
{
Socks5EnumStep.Request => Socks5Parser.ValidateRequestData(info.Data),
Socks5EnumStep.Command => Socks5Parser.ValidateCommandData(info.Data),
Socks5EnumStep.Auth => Socks5Parser.ValidateAuthData(info.Data, Socks5EnumAuthType.Password),
Socks5EnumStep.Forward => EnumProxyValidateDataResult.Equal,
Socks5EnumStep.ForwardUdp => EnumProxyValidateDataResult.Equal,
_ => EnumProxyValidateDataResult.Equal
};
}
}
}