VEthernet 框架实现 tun2socks 的技术原理
概述
VEthernet是一个基于Windows TAP虚拟网卡的网络协议栈框架,它通过在用户态实现完整的TCP/IP协议处理,将虚拟网卡流量转发到SOCKS5代理服务器。本文将分析VEthernet框架的技术架构以及tun2socks的实现原理。
一、整体设计
1.1 核心组件层次
VEthernet采用分层架构设计,主要包含以下核心组件:
底层设备层(TAP Driver Layer)
Layer3Netif类负责TAP虚拟网卡的管理,包括设备发现、驱动安装和底层设备操作
协议处理层(Protocol Stack Layer)
TapTap2Socket作为核心基类,实现IP/TCP/UDP/ICMP协议的解析和处理- 维护TCP连接状态表(
privateLinkTable和publicLinkTable)用于跟踪活动连接
应用层(Application Layer)
Socks5Ethernet继承TapTap2Socket,实现SOCKS5代理功能
1.2 数据流转路径
二、TAP虚拟网卡管理
2.1 驱动部署与设备发现
tun2socks启动时首先检查并安装TAP-Windows驱动。DeploymentTapWindows方法负责驱动的自动部署:
设备发现通过扫描Windows注册表实现,查找所有已安装的TAP设备组件ID:
2.2 设备初始化与配置
TAP设备打开后,需要通过DeviceIoControl进行配置,包括:
- 设置媒体状态(
TAP_WIN_IOCTL_SET_MEDIA_STATUS) - 配置TUN模式(
TAP_WIN_IOCTL_CONFIG_TUN) - 设置IP地址和网关
三、协议栈实现原理
3.1 IP层处理
数据包首先经过IP层解析,处理IP分片重组:
分片包会被缓存在subpackageTable中,等待所有分片到齐后重组成完整的IP包。
3.2 TCP连接状态机
VEthernet实现了完整的TCP状态机,通过TapTcpLink类维护连接状态:
TCP连接生命周期管理包括:
- 连接建立:捕获SYN包,创建到代理服务器的真实连接
- 数据传输:双向转发应用层数据
- 连接关闭:处理FIN/RST包,维护优雅关闭
- 超时管理:定期检查非活跃连接并清理
3.3 协议分发机制
数据包根据协议类型分发到不同的处理器:
四、SOCKS5代理实现
4.1 TCP连接代理
Connection类负责TCP连接的SOCKS5代理,实现了完整的握手流程:
握手流程包括:
- 协议协商:发送支持的认证方法(无认证或用户名/密码)
- 认证(可选):如果启用认证,发送用户名和密码
- 连接请求:发送目标地址和端口,建立代理连接
握手完成后,Connection进入数据转发模式:
- 从虚拟网卡接收的数据通过
OnMessage方法发送到代理服务器 - 从代理服务器接收的数据通过
OnTunnelInput方法发送回虚拟网卡
4.2 UDP数据报代理
UDP代理比TCP更复杂,因为需要维护端口映射关系。Datagram类管理所有UDP端口:
每个UDP源端口对应一个Port实例,负责:
- 与SOCKS5服务器建立TCP控制连接
- 执行SOCKS5 UDP ASSOCIATE握手
- 创建UDP套接字用于数据传输
- 封装/解封装SOCKS5 UDP协议头
UDP数据包的SOCKS5封装和解封装:
4.3 ICMP处理
ICMP Echo Request(ping)直接在本地响应,无需代理:
五、路由与DNS管理
5.1 路由表操作
tun2socks通过修改系统路由表将所有流量导向TAP虚拟网卡:
核心路由策略:
- 添加0.0.0.0/1和128.0.0.0/1两条路由,覆盖所有地址
- 为代理服务器地址添加直连路由,避免死循环
- 支持bypass列表,允许特定IP直连
清理路由时恢复原有路由表:
5.2 DNS切换
为避免DNS泄漏,tun2socks会切换系统DNS服务器:
程序退出时恢复原DNS配置:
5.3 网关保护机制
为防止恶意软件修改默认网关,提供网关保护功能:
六、性能优化技术
6.1 连接复用与池化
使用并发字典(ConcurrentDictionary)管理连接表,支持高并发访问。
6.2 异步I/O模型
所有网络操作均采用异步模式,避免线程阻塞:
- TCP使用
BeginReceive/EndReceive异步接收 - UDP使用
AsyncSocket封装异步操作
6.3 协程支持
使用YieldContext实现协程式的异步编程,简化SOCKS5握手流程:
6.4 内存管理
- 连接超时自动清理,防止内存泄漏
- DNS端口快速老化(3秒),优化短连接场景
七、流程分析
tun2socks的完整启动流程:
- 权限检查:必须以管理员权限运行
- TAP驱动部署:检查并安装TAP-Windows驱动
- 网关修复:尝试恢复系统默认网关
- 防火墙规则:添加程序到防火墙白名单
- 代理服务器解析:解析命令行参数,支持域名解析
- 创建Socks5Ethernet实例:初始化核心引擎
- 路由配置:修改路由表和DNS设置
- 启动监听:开始处理数据包
八、技术特点
8.1 优势
- 完全用户态实现:无需内核模块,便于调试和部署
- 协议完整性:实现完整的TCP/IP协议栈,支持分片重组
- 灵活的扩展性:面向对象设计,易于扩展新的代理协议
- 跨平台潜力:核心逻辑与平台无关,仅TAP层需要适配
- 高性能:异步I/O和连接池化设计
8.2 适用场景
- VPN客户端实现
- 透明代理工具
- 网络流量分析
- 协议转换网关
- 测试和开发环境
九、技术要点
9.1 虚拟网卡与真实网络的桥接
VEthernet的核心在于将虚拟网卡捕获的原始IP包转换为Socket连接,实现了Layer 3到Layer 4的映射。这种设计使得上层应用程序无需感知代理的存在。
9.2 双向NAT机制
- 出站方向:虚拟网卡的源地址/端口映射到代理服务器连接
- 入站方向:代理服务器的响应重新封装成IP包注入虚拟网卡
9.3 协议状态同步
维护两组状态机:
- 虚拟侧状态(
VirtualState):虚拟网卡看到的连接状态 - 本地侧状态(
LocalState):真实Socket的连接状态
两组状态需要同步,确保连接生命周期的一致性。
后续
VEthernet框架展示了如何在用户态实现一个完整的网络协议栈,并将其与SOCKS5代理协议结合。通过虚拟网卡、协议解析、状态管理和路由控制的有机结合,实现了透明的全局代理功能。这种架构不仅适用于tun2socks,还可以扩展到其他网络应用场景,为网络编程提供了极具参考价值的实现范例。
备注
本文基于VEthernet框架的实际代码分析,重点阐述了tun2socks的实现原理。VEthernet采用C#开发,使用P/Invoke调用Windows API进行底层设备操作。框架核心在于TapTap2Socket基类,它提供了协议栈的通用实现,而Socks5Ethernet等派生类则实现特定的代理协议。
技术点包括:
- TAP虚拟网卡的驱动管理和设备操作
- IP/TCP/UDP/ICMP协议的完整解析
- TCP连接状态机的实现
- SOCKS5协议的握手和数据转发
- 系统路由表和DNS的动态修改
- 异步I/O和协程支持
这套框架的设计思想对于理解VPN、代理工具的底层实现具有重要参考价值。
Citations
File: VEthernet/Net/Tun/Layer3Netif.cs (L21-240)
public unsafe sealed class Layer3Netif
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private static readonly Stopwatch _getAllNetworkInterfacesConcurrent = new Stopwatch();
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private static NetworkInterface[] _cachedAllNetworkInterfaces = null;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private static string _preferredNetworkInterfaceId;
public const int MTU = SocketExtension.MTU;
public class AdapterInterface
{
public string Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Mask { get; set; }
public string GatewayServer { get; set; }
public string DhcpServer { get; set; }
public string PrimaryWinsServer { get; set; }
public string SecondaryWinsServer { get; set; }
public string MacAddress { get; set; }
public int IfIndex { get; set; }
public int IfType { get; set; } // MIB_IF_TYPE
public OperationalStatus Status { get; set; }
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
private struct IP_ADAPTER_INFO
{
public IntPtr Next;
public int ComboIndex;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_ADAPTER_NAME_LENGTH + 4)]
public string AdapterName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_ADAPTER_DESCRIPTION_LENGTH + 4)]
public string AdapterDescription;
public uint AddressLength;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_ADAPTER_ADDRESS_LENGTH)]
public byte[] Address;
public int Index;
public int Type;
public uint DhcpEnabled;
public IntPtr CurrentIpAddress;
public IP_ADDR_STRING IpAddressList;
public IP_ADDR_STRING GatewayList;
public IP_ADDR_STRING DhcpServer;
public bool HaveWins;
public IP_ADDR_STRING PrimaryWinsServer;
public IP_ADDR_STRING SecondaryWinsServer;
public int LeaseObtained;
public int LeaseExpires;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
private struct IP_ADDR_STRING
{
public IntPtr Next;
public IP_ADDRESS_STRING IpAddress;
public IP_ADDRESS_STRING IpMask;
public int Context;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
private struct IP_ADDRESS_STRING
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string Address;
}
[DllImport("Iphlpapi.dll", SetLastError = false, CharSet = CharSet.Ansi)]
private static extern int GetAdaptersInfo(IntPtr pAdapterInfo, ref long pBufOutLen);
[DllImport("Iphlpapi.dll", SetLastError = false, CharSet = CharSet.Unicode)]
private static extern int GetIfEntry(ref MIB_IFROW pIfRow);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct MIB_IFROW
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_INTERFACE_NAME_LEN)]
public string wszName;
public uint dwIndex; // index of the interface
public uint dwType; // type of interface
public uint dwMtu; // max transmission unit
public uint dwSpeed; // speed of the interface
public uint dwPhysAddrLen; // length of physical address
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAXLEN_PHYSADDR)]
public byte[] bPhysAddr; // physical address of adapter
public uint dwAdminStatus; // administrative status
public uint dwOperStatus; // operational status
public uint dwLastChange; // last time operational status changed
public uint dwInOctets; // octets received
public uint dwInUcastPkts; // unicast packets received
public uint dwInNUcastPkts; // non-unicast packets received
public uint dwInDiscards; // received packets discarded
public uint dwInErrors; // erroneous packets received
public uint dwInUnknownProtos; // unknown protocol packets received
public uint dwOutOctets; // octets sent
public uint dwOutUcastPkts; // unicast packets sent
public uint dwOutNUcastPkts; // non-unicast packets sent
public uint dwOutDiscards; // outgoing packets discarded
public uint dwOutErrors; // erroneous packets sent
public uint dwOutQLen; // output queue length
public uint dwDescrLen; // length of bDescr member
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAXLEN_IFDESCR)]
public byte[] bDescr; // interface description
}
private const int MAX_INTERFACE_NAME_LEN = 256;
private const int MAXLEN_PHYSADDR = 8;
private const int MAXLEN_IFDESCR = 256;
private const int MAX_ADAPTER_DESCRIPTION_LENGTH = 128;
private const int ERROR_BUFFER_OVERFLOW = 111;
private const int MAX_ADAPTER_NAME_LENGTH = 256;
private const int MAX_ADAPTER_ADDRESS_LENGTH = 8;
private const int MIB_IF_TYPE_OTHER = 1;
private const int MIB_IF_TYPE_ETHERNET = 6;
private const int MIB_IF_TYPE_TOKENRING = 9;
private const int MIB_IF_TYPE_FDDI = 15;
private const int MIB_IF_TYPE_PPP = 23;
private const int MIB_IF_TYPE_LOOPBACK = 24;
private const int MIB_IF_TYPE_SLIP = 28;
private const int IF_OPER_STATUS_OPERATIONAL = 5;
public static OperationalStatus GetOperationalStatus(int ifIndex)
{
MIB_IFROW m = new MIB_IFROW()
{
dwIndex = (uint)ifIndex,
};
int err = GetIfEntry(ref m);
if (err == 0)
{
if (m.dwOperStatus == IF_OPER_STATUS_OPERATIONAL)
{
return OperationalStatus.Up;
}
return OperationalStatus.Down;
}
return OperationalStatus.Unknown;
}
public static AdapterInterface[] GetAllAdapterInterfaces()
{
long structSize = Marshal.SizeOf(typeof(IP_ADAPTER_INFO));
IntPtr pArray = Marshal.AllocHGlobal(new IntPtr(structSize));
int ret = GetAdaptersInfo(pArray, ref structSize);
if (ret == ERROR_BUFFER_OVERFLOW) // ERROR_BUFFER_OVERFLOW == 111
{
// Buffer was too small, reallocate the correct size for the buffer.
pArray = Marshal.ReAllocHGlobal(pArray, new IntPtr(structSize));
ret = GetAdaptersInfo(pArray, ref structSize);
} // if
string any = IPAddress.Any.ToString();
List<AdapterInterface> interfaces = new List<AdapterInterface>();
if (ret == 0)
{
// Call Succeeded
IntPtr pEntry = pArray;
do
{
// Retrieve the adapter info from the memory address
IP_ADAPTER_INFO entry = (IP_ADAPTER_INFO)Marshal.PtrToStructure(pEntry, typeof(IP_ADAPTER_INFO));
AdapterInterface interfacex = new AdapterInterface()
{
Id = entry.AdapterName,
IfIndex = entry.Index,
Name = entry.AdapterDescription,
Address = entry.IpAddressList.IpAddress.Address,
Mask = entry.IpAddressList.IpMask.Address,
GatewayServer = entry.GatewayList.IpAddress.Address,
IfType = entry.Type,
Status = GetOperationalStatus(entry.Index),
};
interfaces.Add(interfacex);
if (entry.DhcpEnabled != 0)
{
interfacex.DhcpServer = entry.DhcpServer.IpAddress.Address;
}
if (entry.HaveWins)
{
interfacex.PrimaryWinsServer = entry.PrimaryWinsServer.IpAddress.Address;
interfacex.SecondaryWinsServer = entry.SecondaryWinsServer.IpAddress.Address;
}
if (string.IsNullOrEmpty(interfacex.Address)) interfacex.Address = any;
if (string.IsNullOrEmpty(interfacex.Mask)) interfacex.Mask = any;
if (string.IsNullOrEmpty(interfacex.GatewayServer)) interfacex.GatewayServer = any;
if (string.IsNullOrEmpty(interfacex.DhcpServer)) interfacex.DhcpServer = any;
if (string.IsNullOrEmpty(interfacex.PrimaryWinsServer)) interfacex.PrimaryWinsServer = any;
if (string.IsNullOrEmpty(interfacex.SecondaryWinsServer)) interfacex.SecondaryWinsServer = any;
interfacex.MacAddress = BitConverter.ToString(entry.Address, 0, (int)entry.AddressLength);
if (string.IsNullOrEmpty(interfacex.MacAddress))
{
interfacex.MacAddress = "00-00-00-00-00-00";
}
// Get next adapter (if any)
pEntry = entry.Next;
}
while (pEntry != IntPtr.Zero);
Marshal.FreeHGlobal(pArray);
}
else
{
Marshal.FreeHGlobal(pArray);
}
return interfaces.ToArray();
}
File: VEthernet/Net/Tun/Layer3Netif.cs (L749-794)
public static ICollection<string> FindAllComponentId()
{
string szOwnerKeyPath = "SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e972-e325-11ce-bfc1-08002be10318}";
ICollection<string> oDevComponentSet = new HashSet<string>();
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szOwnerKeyPath, 0, KEY_ALL_ACCESS, out UIntPtr hOwnerKey) == ERROR_SUCCESS)
{
byte[] szClassName = new byte[MAX_PATH];
uint dwIndex = 0;
byte[] data = new byte[MAX_PATH];
while (RegEnumKey(hOwnerKey, dwIndex++, szClassName, MAX_PATH) == ERROR_SUCCESS)
{
uint dwRegType = REG_NONE;
int dwSize = data.Length;
UIntPtr hSubKey = NULL;
string szSubKeyPath = szOwnerKeyPath + "\\" + Encoding.Default.GetString(szClassName);
if (RegOpenKey(HKEY_LOCAL_MACHINE, szSubKeyPath, out hSubKey) != ERROR_SUCCESS)
{
continue;
}
if (RegQueryValueEx(hSubKey, "ComponentId", 0, out dwRegType, data, ref dwSize) == ERROR_SUCCESS && dwRegType == REG_SZ)
{
if (dwSize < 3)
{
continue;
}
string szData = Encoding.Default.GetString(data, 0, 3).TrimEnd();
if ("tap" == szData)
{
dwSize = data.Length;
dwRegType = 0;
if (RegQueryValueEx(hSubKey, "NetCfgInstanceId", 0, out dwRegType, data, ref dwSize) == ERROR_SUCCESS && dwRegType == REG_SZ)
{
string szDevComponentId = dwSize < 1 ? string.Empty : Encoding.Default.GetString(data, 0, dwSize - 1).TrimEnd();
if (!string.IsNullOrEmpty(szDevComponentId))
{
oDevComponentSet.Add(szDevComponentId);
}
}
}
}
RegCloseKey(hSubKey);
}
RegCloseKey(hOwnerKey);
}
return oDevComponentSet;
}
File: VEthernet/Net/Tun/Layer3Netif.cs (L869-898)
public static bool DeviceIoControl(IntPtr tap, uint commands, byte[] contents)
{
IntPtr hEvent = NativeNetif.CreateEvent(IntPtr.Zero, false, false, null);
try
{
NativeNetif.OVERLAPPED overlapped = new NativeNetif.OVERLAPPED();
overlapped.hEvent = hEvent;
uint dw = 0;
uint content_size = 0;
if (contents == null)
{
return NativeNetif.DeviceIoControl(tap, commands,
contents, 0, contents, 0, ref dw, ref overlapped);
}
else
{
content_size = (uint)contents.Length;
return NativeNetif.DeviceIoControl(tap, commands,
contents, content_size, contents, content_size, ref dw, ref overlapped);
}
}
finally
{
if (hEvent != IntPtr.Zero)
{
NativeNetif.CloseHandle(hEvent);
}
}
}
File: VEthernet/Net/TapTap2Socket.cs (L28-141)
public unsafe class TapTap2Socket : IDisposable, IEnumerable<IConnection>
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly IDictionary<string, TapTcpLink> privateLinkTable = new ConcurrentDictionary<string, TapTcpLink>();
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly IDictionary<int, TapTcpLink> publicLinkTable = new ConcurrentDictionary<int, TapTcpLink>();
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly IDictionary<string, Subpackage> subpackageTable = new ConcurrentDictionary<string, Subpackage>();
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private volatile int aport = new global::VEthernet.Utilits.Random(Environment.TickCount).Next(1000, ushort.MaxValue);
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly Timer ticktmr = new Timer();
private readonly object syncobj = new object();
private bool checksum = false;
private SOCKET loopback = null;
private readonly NetworkStatistics networkStatistics = null;
public const int MaxFinalTime = 5 * 1000;
public const int MaxSynalTime = 20 * 1000;
private const int MaxAllocPortCount = IPEndPoint.MaxPort;
[TypeLibType(TypeLibTypeFlags.FHidden | TypeLibTypeFlags.FRestricted)]
internal sealed class TapTcpLink : IDisposable, IConnection
{
public IPEndPoint Destination = null;
public IPEndPoint Source = null;
public IPEndPoint VirtualAddress = null;
public IPFrame Connecting = null;
public TapTcpClient Socket = null;
public uint LocalSequenceNo = 0;
public uint VirtualSequenceNo = 0;
public Stopwatch ElapsedTime = new Stopwatch();
public bool Syn { get; set; } = false; // SYN
public bool Fin { get; set; } = false; // RST/FIN
public TcpState LocalState { get; set; } = TcpState.CLOSED;
public TcpState VirtualState { get; set; } = TcpState.CLOSED;
public ConnectionState State
{
get
{
bool lFin = unchecked(this.LocalState == TcpState.CLOSED || this.LocalState >= TcpState.LAST_ACK);
bool vFin = unchecked(this.VirtualState == TcpState.CLOSED || this.VirtualState >= TcpState.LAST_ACK);
bool rFin = unchecked(this.Fin || lFin || vFin);
if (this.Syn && !rFin)
{
return ConnectionState.Connection;
}
else if (rFin)
{
if (vFin && lFin)
{
return ConnectionState.Closed;
}
return ConnectionState.Disconnecting;
}
return ConnectionState.Connected;
}
}
EndPoint IConnection.Source => this.Source;
EndPoint IConnection.Destination => this.Destination;
public TapTcpLink() => this.RestartWatch();
public TapTcpLink RestartWatch()
{
Stopwatch stopwatch = this.ElapsedTime;
if (stopwatch != null)
{
stopwatch.Restart();
}
return this;
}
~TapTcpLink() => this.Dispose();
public TapTcpLink Activity()
{
if (!this.Fin && !this.Syn)
{
this.RestartWatch();
}
return this;
}
public void Dispose()
{
using (TapTcpClient socket = Interlocked.Exchange(ref this.Socket, null))
{
socket?.Abort();
}
this.Destination = null;
this.Source = null;
this.VirtualAddress = null;
this.Syn = false;
this.Fin = false;
this.Connecting = null;
Interlocked.Exchange(ref this.ElapsedTime, null);
GC.SuppressFinalize(this);
}
public override string ToString()
{
return $"{this.Source} -> {this.Destination}";
}
}
File: VEthernet/Net/TapTap2Socket.cs (L653-762)
protected virtual bool ProcessTickAlways()
{
IEnumerable<KeyValuePair<int, TapTcpLink>> shadowsLinkTable = null;
lock (this.syncobj)
{
shadowsLinkTable = this.publicLinkTable.ToList();
}
ResetTcpStatistics(this.networkStatistics.Tcp);
if (shadowsLinkTable == null)
{
return false;
}
int maxInactivityTime = this.MaxInactivityTime;
foreach (var kv in shadowsLinkTable)
{
TapTcpLink link = kv.Value;
if (link == null)
{
lock (this.syncobj)
{
this.publicLinkTable.Remove(kv.Key, out link);
}
if (link == null)
{
continue;
}
}
Stopwatch elapsedWatchTime = link.ElapsedTime;
ConnectionState connectionState = link.State;
switch (connectionState)
{
case ConnectionState.Connected:
if (elapsedWatchTime == null ||
elapsedWatchTime.ElapsedMilliseconds >= maxInactivityTime)
{
connectionState = ConnectionState.Disconnecting;
goto case ConnectionState.Disconnecting;
}
else
{
TapTcpClient socket = link.Socket;
if (socket != null && socket.Connected)
{
this.networkStatistics.Tcp.ActiveConnections++;
}
else
{
this.networkStatistics.Tcp.ConnectConnections++;
}
}
break;
case ConnectionState.Connection:
this.networkStatistics.Tcp.ConnectConnections++;
break;
case ConnectionState.Disconnecting:
this.networkStatistics.Tcp.DisconnectingConnections++;
if (this.Subnetstack)
{
connectionState = ConnectionState.Closed;
goto case ConnectionState.Closed;
}
break;
case ConnectionState.Closed:
this.networkStatistics.Tcp.ClosingConnections++;
break;
};
if (connectionState == ConnectionState.Connection)
{
if (elapsedWatchTime != null)
{
long synTime = elapsedWatchTime.ElapsedMilliseconds;
if (synTime < MaxSynalTime)
{
continue;
}
}
}
else if (connectionState == ConnectionState.Closed || connectionState == ConnectionState.Disconnecting)
{
link.Connecting = null;
using (TapTcpClient socket = link.Socket)
{
if (socket != null)
{
link.Socket = null;
socket.CloseOrAbort();
}
}
if (connectionState == ConnectionState.Closed)
{
goto closeTcpLink;
}
if (elapsedWatchTime != null)
{
long finalTime = elapsedWatchTime.ElapsedMilliseconds;
if (finalTime < MaxFinalTime)
{
continue;
}
}
}
else
{
continue;
}
closeTcpLink:
this.CloseTcpLink(link.Source, link.Destination);
}
return true;
}
File: VEthernet/Net/TapTap2Socket.cs (L774-878)
protected virtual bool ProcessSubpackage(IPFrame packet)
{
if ((packet.Flags & IPFlags.IP_MF) != 0 ||
((packet.Flags & IPFlags.IP_OFFMASK) != 0 && packet.FragmentOffset > 0))
{
if (packet.Payload.Length < 1)
{
return false;
}
packet = new IPFrame(packet.ProtocolType,
packet.Source,
packet.Destination,
packet.Payload.Depth())
{
FragmentOffset = packet.FragmentOffset,
Flags = packet.Flags,
Id = packet.Id,
Options = packet.Options,
Tos = packet.Tos,
Ttl = packet.Ttl,
};
Subpackage subpackage;
string key = $"{packet}/{packet.Id}";
lock (this.syncobj)
{
this.subpackageTable.TryGetValue(key, out subpackage);
if (subpackage == null)
{
subpackage = new Subpackage();
this.subpackageTable[key] = subpackage;
}
}
IList<IPFrame> frames = subpackage.Frames;
int index = frames.Count;
{
while (index > 0)
{
IPFrame left = frames[index - 1];
if (packet.FragmentOffset >= left.FragmentOffset)
{
break;
}
else
{
index--;
}
}
frames.Insert(index, packet);
}
int nextFragementOffset = 0;
bool fullFragementOffset = true;
{
int count = frames.Count;
for (index = 0; index < count; index++)
{
IPFrame left = frames[index];
if (left.FragmentOffset != nextFragementOffset)
{
fullFragementOffset = false;
break;
}
nextFragementOffset = left.FragmentOffset + left.Payload.Length;
}
if (fullFragementOffset)
{
IPFrame left = frames[frames.Count - 1];
if ((packet.Flags & IPFlags.IP_MF) == 0 &&
(packet.Flags & IPFlags.IP_OFFMASK) != 0 && left.FragmentOffset > 0)
{
left = unchecked(frames[0]);
lock (this.syncobj)
{
this.subpackageTable.Remove(key);
}
byte[] buffer = new byte[nextFragementOffset];
using (MemoryStream ms = new MemoryStream(buffer, true))
{
for (index = 0, count = frames.Count; index < count; index++)
{
var payload = frames[index].Payload;
ms.Write(payload.Buffer, payload.Offset, payload.Length);
}
}
IPFrame originNew = new IPFrame(left.ProtocolType,
left.Source,
left.Destination,
new BufferSegment(buffer))
{
Id = left.Id,
Options = left.Options,
Tos = left.Tos,
Ttl = left.Ttl,
Flags = IPFlags.IP_DF,
FragmentOffset = 0,
};
this.ProcessInput(packet: originNew);
}
}
}
return true;
}
else
{
return false;
}
File: VEthernet/Net/TapTap2Socket.cs (L881-921)
protected virtual void ProcessInput(IPFrame packet)
{
if (packet.ProtocolType == ProtocolType.Tcp)
{
if (this.Subnetstack)
{
this.SunetstackInput(packet);
return;
}
TcpFrame frame = TcpLayer.ParseFrame(packet, this.ValidateChecksum);
if (frame != null)
{
if (!this.ProcessTcpInput(packet, frame))
{
this.RST(frame);
}
}
}
else if (packet.ProtocolType == ProtocolType.Udp)
{
if (!this.ProcessSubpackage(packet))
{
UdpFrame frame = UdpLayer.ParseFrame(packet, this.ValidateChecksum);
if (frame != null)
{
this.ProcessUdpInput(packet, frame);
}
}
}
else if (packet.ProtocolType == ProtocolType.Icmp)
{
if (!this.ProcessSubpackage(packet))
{
IcmpFrame frame = IcmpLayer.ParseFrame(packet, this.ValidateChecksum);
if (frame != null)
{
this.ProcessIcmpInput(packet, frame);
}
}
}
}
File: tun2socks/Socks5Ethernet.cs (L21-63)
public class Socks5Ethernet : TapTap2Socket
{
private readonly IDictionary<IPAddress, RouteInformation> _cachesRoutes = new Dictionary<IPAddress, RouteInformation>();
private readonly object _syncobj = new object();
private readonly Stopwatch _sw = new Stopwatch();
private int _disposed = 0;
private IEnumerable<IPAddressRange> _bypassIplistRoutes = null;
[SecurityCritical]
[SecuritySafeCritical]
public Socks5Ethernet(IPEndPoint proxyServer, IPAddress[] dnsAddresses, bool productMode, bool protectDefaultGateway, string user, string password, string bypassIplist, NetworkStatistics networkStatistics) : base(subnetstack: true, 0, networkStatistics)
{
this.Server = proxyServer ?? throw new ArgumentNullException(nameof(proxyServer));
NetworkInterface exitNetworkInterface = Layer3Netif.GetPreferredNetworkInterfaceAddress(true, out IPAddress exitGatewayAddress);
if (exitNetworkInterface == null)
{
throw new InvalidOperationException("The preferred outbound ethernet device interface could not be found");
}
if (dnsAddresses != null && dnsAddresses.Length > 0)
{
this.ApplyDNSServerAddresses = dnsAddresses;
}
// Users in mainland China suggest setting this parameter is True, because there may be many malicious software or automation tools that modify Default Gatway on your computer.
this.ProtectDefaultGateway = protectDefaultGateway;
this.ProductMode = productMode;
this.ValidateChecksum = false;
if (!string.IsNullOrEmpty(user) && !string.IsNullOrEmpty(password))
{
this.User = user;
this.Password = password;
}
this.Datagram = this.CreateDatagram();
this.ExitNetworkInterface = exitNetworkInterface;
this.ExitGatewayAddress = exitGatewayAddress;
this.ExitInterfaceAddress = Layer3Netif.GetNetworkInterfaceAddress(exitNetworkInterface, out IPAddress exitInterfaceMask);
this.ExitInterfaceMask = exitInterfaceMask;
if (IPFrame.Equals(IPAddress.Any, this.ExitInterfaceAddress) || IPFrame.Equals(IPAddress.Any, this.ExitInterfaceMask))
{
throw new InvalidOperationException("The IPv4 address of the preferred outbound ethernet device interface could not be found");
}
this._bypassIplistRoutes = this.BypassIplistFromFileName(bypassIplist);
this.ExitInterfaceIfIndex = Layer3Netif.GetAdapterIndex(exitNetworkInterface);
}
File: tun2socks/Socks5Ethernet.cs (L163-179)
protected override void OnTick(EventArgs e)
{
// Prevent external IP routing table modify default gw, Cause ip traffic to go not tun2socks outbound.
Stopwatch sw = this._sw;
if (sw.IsRunning && this.ProtectDefaultGateway)
{
lock (this._syncobj)
{
if (sw.ElapsedMilliseconds >= this.AutomaticGatewayTimeout)
{
sw.Restart();
this.DeleteAllAnyAddress();
}
}
}
base.OnTick(e);
}
File: tun2socks/Socks5Ethernet.cs (L181-194)
protected virtual void AddAllRouter()
{
lock (this._syncobj)
{
this.DeleteAllAnyAddress();
IPAddress cirdMiddleMask = IPAddress.Parse("128.0.0.0"); // 0.0.0.0/1
Router.Create(IPAddress.Any, IPAddress.Any, this.Tap.GatewayAddress, this.Tap.Index, 1);
Router.Create(IPAddress.Any, cirdMiddleMask, this.Tap.GatewayAddress, this.Tap.Index, 1);
Router.Create(cirdMiddleMask, cirdMiddleMask, this.Tap.GatewayAddress, this.Tap.Index, 1);
Router.Create(this.Server.Address, this.ExitGatewayAddress, 1);
Router.AddFast(this._bypassIplistRoutes, this.ExitGatewayAddress);
}
}
File: tun2socks/Socks5Ethernet.cs (L196-209)
protected virtual void DeleteAllRouter()
{
lock (this._syncobj)
{
IPAddress cirdMiddleMask = IPAddress.Parse("128.0.0.0"); // 0.0.0.0/1
Router.Delete(IPAddress.Any, this.Tap.GatewayAddress);
Router.Delete(IPAddress.Any, this.Tap.GatewayAddress);
Router.Delete(cirdMiddleMask, this.Tap.GatewayAddress);
Router.Delete(this.Server.Address, this.ExitGatewayAddress);
Router.DeleteFast(Interlocked.Exchange(ref this._bypassIplistRoutes, null), this.ExitGatewayAddress);
this.AddAllAnyAddress();
}
}
File: tun2socks/Socks5Ethernet.cs (L211-219)
protected virtual void SwitchNamespaceServers(IPAddress[] addresses)
{
lock (this._syncobj)
{
Dnss.SetAddresses(this.Tap.Index, addresses);
Dnss.SetAddresses(this.ExitInterfaceIfIndex, addresses);
Dnss.Flush();
}
}
File: tun2socks/Socks5Ethernet.cs (L270-274)
if (this.Tap != null)
{
this.DeleteAllRouter();
this.SwitchNamespaceServers(this.RestoreDNSServerAddresses);
}
File: tun2socks/Socks5Ethernet.cs (L296-316)
protected override bool ProcessIcmpInput(IPFrame packet, IcmpFrame frame)
{
if (!base.ProcessIcmpInput(packet, frame))
{
return false;
}
if (frame.Type != IcmpType.ICMP_ECHO)
{
return false;
}
IcmpFrame e = new IcmpFrame(frame.Destination, frame.Source, frame.Payload)
{
Type = IcmpType.ICMP_ER,
Code = frame.Code,
Ttl = IPFrame.DefaultTtl,
Sequence = frame.Sequence,
Identification = frame.Identification,
};
this.Output(IcmpLayer.ToIPFrame(e));
return true;
}
File: tun2socks/Program.cs (L34-55)
private static bool DeploymentTapWindows()
{
string componentId = Layer3Netif.FindAllComponentId().FirstOrDefault();
if (!string.IsNullOrEmpty(componentId))
{
return true;
}
else
{
Console.WriteLine("Installing the VEthernet Windows Virtual Network Card TAP device driver.");
}
if (Layer3Netif.InstallTapWindows(AppDomain.CurrentDomain.BaseDirectory + "\\Driver", "VEthernet"))
{
Console.WriteLine("Installed the VEthernet Virtual Network Card TAP device driver to computer.");
return true;
}
else
{
Console.WriteLine("Unable to install Virtual Network Card TAP device driver to computer.");
return false;
}
}
File: tun2socks/Program.cs (L98-188)
private static void Main(string[] args)
{
Console.Title = string.Format(Program.ApplicationName, string.Empty);
Console.TreatControlCAsInput = true;
if (args.Length <= 0)
{
Console.WriteLine($"usage: {Process.GetCurrentProcess().MainModule.FileName} --dns-addresses=8.8.8.8;8.8.4.4 --product-mode=[yes|no] --protect-default-gateway=[yes|no] --proxyserver=192.168.0.21 --proxyport=1080 --proxyuser=[sa] --proxypassword=[admin] --bypass-iplist=./ip.txt");
Console.ReadKey(false);
return;
}
if (!Fw.IsAdministrator())
{
Console.WriteLine("Please run this program under administrator's privilege.");
Console.ReadKey(false);
return;
}
else if (!DeploymentTapWindows())
{
Console.ReadKey(false);
return;
}
else
{
// If the system physical hosting Ethernet card is not correctly configured with a default gateway address (0.0.0.0),
// tun2socks attempts to calculate the general default gateway address and configure it to the system to
RepiarDefaultGateway(); // restore the connection to the Internet network.
WebProxy.Direct(); // Disable system proxy settings.
// Add or modify tun2socks private/public network firewall rules.
Fw.NetFirewallAddAllApplication(Program.ApplicationName, Process.GetCurrentProcess().MainModule.FileName);
}
// Obtain the valid proxyserver address from the command line interface parameter.
string proxyserver = Environments.GetCommandArgumentString(args, "--proxyserver") ?? string.Empty;
if (!IPAddress.TryParse(proxyserver, out IPAddress proxyserverAddress) ||
proxyserver == null ||
proxyserverAddress.AddressFamily != AddressFamily.InterNetwork ||
IPFrame.Equals(IPAddress.Any, proxyserverAddress) ||
IPFrame.Equals(IPAddress.Broadcast, proxyserverAddress) ||
IPFrame.Equals(IPAddress.None, proxyserverAddress))
{
// Query the dns-server to obtain the IPv4 address of the proxy server.
try
{
proxyserverAddress = Dns.GetHostAddresses(proxyserver).FirstOrDefault(p =>
p.AddressFamily == AddressFamily.InterNetwork &&
!IPFrame.Equals(IPAddress.Any, p) &&
!IPFrame.Equals(IPAddress.Broadcast, p) &&
!IPFrame.Equals(IPAddress.None, p));
}
catch (Exception)
{
proxyserverAddress = null;
}
if (proxyserverAddress == null)
{
Console.WriteLine("Please use a valid socks5 agent server \"IPv4 or domain\" address.");
Console.ReadKey(false);
return;
}
}
// Gets the current cli-cmd configuration interface specified for the DNS servers list of the tun2socks vNetwork.
// Ipep::ToAddresses support the following character delimiters. The default is '; '
// Symbols: ',', '|', ';', '-', ':', '*'
IPAddress[] dnsAddresses = Ipep.ToAddresses(Environments.GetCommandArgumentString(args, "--dns-addresses"));
// Create an Ipep address for the socks5 server.
IPEndPoint serverEP = new IPEndPoint(proxyserverAddress, (int)Environments.GetCommandArgumentInt64(args, "--proxyport").GetValueOrDefault());
using (Socks5Ethernet ethernet = new Socks5Ethernet(serverEP, dnsAddresses,
productMode: ToBoolean(Environments.GetCommandArgumentString(args, "--product-mode"), true),
protectDefaultGateway: ToBoolean(Environments.GetCommandArgumentString(args, "--protect-default-gateway"), false),
user: Environments.GetCommandArgumentString(args, "--proxyuser"),
password: Environments.GetCommandArgumentString(args, "--proxypassword"),
bypassIplist: Environments.GetCommandArgumentString(args, "--bypass-iplist"), null))
{
Console.Title = string.Format(Program.ApplicationName, $"@{serverEP}");
Console.WriteLine("Application started. Press Ctrl+C to shut down.");
ethernet.Listen();
while (!ethernet.IsDisposed)
{
ConsoleKeyInfo cki = Console.ReadKey(false);
if (cki.Key == ConsoleKey.C && cki.Modifiers == ConsoleModifiers.Control
|| cki.Key == ConsoleKey.Escape)
{
break;
}
}
Console.WriteLine("Application is shutting down...");
}
File: tun2socks/Connection.cs (L68-99)
protected override void OnMessage(BufferSegment e)
{
if (!SocketExtension.BeginSend(this._server, e.Buffer, e.Offset, e.Length, (ar) =>
{
bool ok = SocketExtension.EndSend(this._server, ar);
if (ok)
{
ok = this.PullListener();
}
if (!ok)
{
this.Dispose();
}
}))
{
this.Dispose();
}
}
protected virtual bool OnTunnelInput(byte[] buffer, int offset, int length)
{
BufferSegment messages = new BufferSegment(buffer, offset, length);
return this.Send(messages, (ok) =>
{
if (!ok)
{
this.Dispose();
return;
}
this.PullTunnelReceive(null);
});
}
File: tun2socks/Connection.cs (L173-270)
private IEnumerable HandshakeTunnelAsync(YieldContext y)
{
Socket socket = this._server;
bool success = false;
do
{
Socks5Ethernet ethernet = (Socks5Ethernet)this.Tap;
bool authentication = !string.IsNullOrEmpty(ethernet.User) && !string.IsNullOrEmpty(ethernet.Password);
if (this.IsAbort)
{
break;
}
YieldContext.Integer outlen = new YieldContext.Integer();
byte[] messages = this._buffer;
messages[0] = 0x05; // VER
messages[1] = 0x01; // NMETHODS
messages[2] = (byte)(authentication ? 0x02 : 0x00); // METHODS
yield return y.Send(socket, messages, 0, 3, outlen);
if (outlen < 0)
{
break;
}
yield return y.Receive(socket, messages, 0, 2, outlen);
if (outlen < 1 || authentication && messages[1] != 0x02 || !authentication && messages[1] != 0x00)
{
break;
}
if (authentication)
{
using (MemoryStream ms = new MemoryStream(messages))
{
using (BinaryWriter bw = new BinaryWriter(ms))
{
byte[] bytes = Encoding.UTF8.GetBytes(ethernet.User);
bw.Write((byte)0x01);
bw.Write((byte)bytes.Length);
bw.Write(bytes);
bytes = Encoding.UTF8.GetBytes(ethernet.Password);
bw.Write((byte)bytes.Length);
bw.Write(bytes);
yield return y.Send(socket, messages, 0, (int)ms.Position, outlen);
if (outlen < 0)
{
break;
}
}
}
yield return y.Receive(socket, messages, 0, 2, outlen);
if (outlen <= 0 || messages[1] != 0x00)
{
break;
}
}
IPEndPoint destinationEP = this.RemoteEndPoint;
using (MemoryStream ms = new MemoryStream(messages))
{
using (BinaryWriter bw = new BinaryWriter(ms))
{
bw.Write((byte)0x05); // VAR
bw.Write((byte)0x01); // CMD
bw.Write((byte)0x00); // RSV
bw.Write((byte)0x01); // ATYPE(IPv4)
bw.Write(destinationEP.Address.GetAddressBytes());
bw.Write(CheckSum.htons((ushort)destinationEP.Port));
yield return y.Send(socket, messages, 0, (int)ms.Position, outlen);
if (outlen < 0)
{
break;
}
}
}
yield return y.Receive(socket, messages, 0, 10, outlen);
if (outlen <= 0 || messages[1] != 0x00)
{
break;
}
success = true;
} while (false);
if (!success)
{
this.Dispose();
}
else
{
this.OnOpen(EventArgs.Empty);
}
}
File: tun2socks/Datagram.cs (L11-105)
public class Datagram : IDisposable
{
private IDictionary<IPEndPoint, Port> _portTable = new ConcurrentDictionary<IPEndPoint, Port>();
private Timer _tickAlwaysTimer = null;
public Datagram(Socks5Ethernet ethernet)
{
this.Ethernet = ethernet ?? throw new ArgumentNullException(nameof(ethernet));
this._tickAlwaysTimer = new Timer();
this._tickAlwaysTimer.Interval = 1000;
this._tickAlwaysTimer.Tick += (sender, e) => this.ProcessTickAlways();
this._tickAlwaysTimer.Start();
}
public Socks5Ethernet Ethernet { get; }
~Datagram() => this.Dispose();
public virtual void Dispose()
{
using (var t = Interlocked.Exchange(ref this._tickAlwaysTimer, null))
{
t?.Stop();
}
IDictionary<IPEndPoint, Port> pairs = Interlocked.Exchange(ref this._portTable, null);
if (pairs != null)
{
foreach (var p in pairs.Values)
{
p?.Dispose();
}
}
GC.SuppressFinalize(this);
}
protected virtual bool ProcessTickAlways()
{
IDictionary<IPEndPoint, Port> pairs = this._portTable;
if (pairs == null)
{
return false;
}
foreach (var kv in pairs)
{
Port port = kv.Value;
if (port == null || port.IsPortAging)
{
if (port != null)
{
port.Dispose();
}
pairs.Remove(kv.Key);
}
}
return true;
}
protected internal virtual bool Input(UdpFrame packet)
{
if (packet == null)
{
return false;
}
IDictionary<IPEndPoint, Port> pairs = this._portTable;
if (pairs == null)
{
return false;
}
Port localPort = null;
lock (pairs)
{
if (!pairs.TryGetValue(packet.Source, out localPort) || localPort == null)
{
localPort = this.CreatePort(packet.Source);
if (localPort == null)
{
return false;
}
else if (!localPort.Listen())
{
localPort.Dispose();
return false;
}
pairs[packet.Source] = localPort;
}
}
if (localPort == null)
{
return false;
}
return localPort.Input(packet);
}
protected virtual Port CreatePort(IPEndPoint localEP) => new Port(this, localEP);
}
File: tun2socks/Port.cs (L38-70)
public const int MaxAgingTime = 150000;
public const int DnsAgingTime = 3000;
[SecurityCritical]
[SecuritySafeCritical]
public Port(Datagram datagram, IPEndPoint localEP)
{
this._datagram = datagram ?? throw new ArgumentNullException(nameof(datagram));
this._localEP = localEP ?? throw new ArgumentNullException(nameof(localEP));
this._agingsw.Start();
}
~Port() => this.Dispose();
public virtual bool IsDisposed => Interlocked.CompareExchange(ref this._disposed, 0, 0) != 0;
public virtual bool IsPortAging
{
get
{
if (this.IsDisposed)
{
return true;
}
long ticks = this._agingsw.ElapsedMilliseconds;
long maxAgingTime = MaxAgingTime;
if (Interlocked.CompareExchange(ref this._onlydnsport, 0, 0) == 1)
{
maxAgingTime = DnsAgingTime;
}
return ticks >= maxAgingTime;
}
}
File: tun2socks/Port.cs (L185-304)
private IEnumerable HandshakeTunnelAsync(YieldContext y)
{
Socket socket = this._server;
bool success = false;
do
{
Socks5Ethernet ethernet = this._datagram?.Ethernet;
if (ethernet == null)
{
break;
}
bool authentication = !string.IsNullOrEmpty(ethernet.User) && !string.IsNullOrEmpty(ethernet.Password);
if (this.IsDisposed)
{
break;
}
YieldContext.Integer outlen = new YieldContext.Integer();
byte[] messages = new byte[256]; // Allocation Granularity
messages[0] = 0x05; // VER
messages[1] = 0x01; // NMETHODS
messages[2] = (byte)(authentication ? 0x02 : 0x00); // METHODS
yield return y.Send(socket, messages, 0, 3, outlen);
if (outlen < 0)
{
break;
}
yield return y.Receive(socket, messages, 0, 2, outlen);
if (outlen < 1 || authentication && messages[1] != 0x02 || !authentication && messages[1] != 0x00)
{
break;
}
if (authentication)
{
using (MemoryStream ms = new MemoryStream(messages))
{
using (BinaryWriter bw = new BinaryWriter(ms))
{
byte[] bytes = Encoding.UTF8.GetBytes(ethernet.User);
bw.Write((byte)0x01);
bw.Write((byte)bytes.Length);
bw.Write(bytes);
bytes = Encoding.UTF8.GetBytes(ethernet.Password);
bw.Write((byte)bytes.Length);
bw.Write(bytes);
yield return y.Send(socket, messages, 0, (int)ms.Position, outlen);
if (outlen < 0)
{
break;
}
}
}
yield return y.Receive(socket, messages, 0, 2, outlen);
if (outlen <= 0 || messages[1] != 0x00)
{
break;
}
}
IPEndPoint bindEP = default(IPEndPoint);
try
{
IPEndPoint interfaceEP = (IPEndPoint)socket.LocalEndPoint;
this._socket = new NetworkSocket(interfaceEP.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
this._socket.Bind(new IPEndPoint(interfaceEP.Address, 0));
bindEP = (IPEndPoint)this._socket.LocalEndPoint;
this._asyncsocket = AsyncContext.GetContext().CreateSocket(this._socket);
}
catch (Exception)
{
break;
}
using (MemoryStream ms = new MemoryStream(messages))
{
using (BinaryWriter bw = new BinaryWriter(ms))
{
bw.Write((byte)0x05); // VAR
bw.Write((byte)0x03); // CMD
bw.Write((byte)0x00); // RSV
bw.Write((byte)0x01); // ATYPE(IPv4)
bw.Write(0);
bw.Write(CheckSum.htons((ushort)bindEP.Port));
yield return y.Send(socket, messages, 0, (int)ms.Position, outlen);
if (outlen < 0)
{
break;
}
}
}
yield return y.Receive(socket, messages, 0, 10, outlen);
if (outlen <= 0 || messages[1] != 0x00)
{
break;
}
IPAddress proxyAddress = this._datagram?.Ethernet?.Server?.Address;
if (proxyAddress != null)
{
success = true;
this._sendtoEP = new IPEndPoint(proxyAddress, (messages[8] << 8) | (messages[9] & 0xff));
}
} while (false);
if (!success)
{
this.Dispose();
}
else
{
this.OnOpen(EventArgs.Empty);
}
}
File: tun2socks/Port.cs (L336-375)
private void ProcessReceiveFrom()
{
if (this.IsDisposed)
{
return;
}
AsyncSocket socket = this._asyncsocket;
if (socket == null)
{
return;
}
byte[] buffer = socket.Context.Buffer;
if (buffer == null)
{
return;
}
bool success = socket.ReceiveFrom(buffer, 0, buffer.Length, (count, ep) =>
{
if (count < 1)
{
this.Dispose();
return;
}
if (count > 0)
{
if (ep is IPEndPoint remoteEP)
{
if (this.OnWanInput(buffer, count, remoteEP))
{
this._agingsw.Restart();
}
}
}
this.ProcessReceiveFrom();
});
if (!success)
{
this.Dispose();
}
}
File: tun2socks/Port.cs (L377-447)
protected unsafe virtual bool OnWanInput(byte[] buffer, int length, IPEndPoint remoteEP)
{
if (IPFrame.Equals(remoteEP, this._sendtoEP)) // 从服务器上收到报文
{
int offset;
NetworkAddress address = Socks5Extension.ResolveEP(buffer, &offset, length);
if (address == null || offset < 0 || offset >= length)
{
return false;
}
IPEndPoint sourceEP = address.EndPoint;
if (sourceEP == null)
{
return false;
}
return this.SendToLocal(new BufferSegment(buffer, offset, length), sourceEP);
}
else // 收到被穿透报文不知道是什么数据
{
return this.SendToLocal(new BufferSegment(buffer, 0, length), remoteEP);
}
}
protected virtual bool SendToLocal(BufferSegment messages, IPEndPoint sourceEP)
{
Datagram datagram = this._datagram;
if (datagram == null)
{
return false;
}
IPEndPoint localEP = this._localEP;
if (localEP == null)
{
return false;
}
IPFrame packet = UdpLayer.ToIPFrame(new UdpFrame(sourceEP, localEP, messages));
return datagram.Ethernet.Tap.Output(IPv4Layer.ToArray(packet));
}
protected virtual bool SendToServer(BufferSegment messages, IPEndPoint destinationEP)
{
Datagram datagram = this._datagram;
if (datagram == null)
{
return false;
}
Socket socket = this._socket;
if (socket == null)
{
return false;
}
IPEndPoint sendtoEP = this._sendtoEP;
if (sendtoEP == null)
{
return false;
}
IPEndPoint localEP = this._localEP;
if (localEP == null)
{
return false;
}
if (!Socks5Extension.SendTo(socket, messages.Buffer, messages.Offset, messages.Length, sendtoEP, destinationEP))
{
return false;
}
if (!datagram.Ethernet.ProductMode)
{
Console.WriteLine($"[{DateTime.Now}][UDP]{localEP.ToString().PadRight(16)} sendto {destinationEP}");
}
return true;
}


2万+

被折叠的 条评论
为什么被折叠?



