VEthernet 框架实现 tun2socks 的技术原理

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连接状态表(privateLinkTablepublicLinkTable)用于跟踪活动连接

应用层(Application Layer)

  • Socks5Ethernet继承TapTap2Socket,实现SOCKS5代理功能

1.2 数据流转路径

发起网络请求
路由规则
原始IP包
协议解析
TCP
UDP
ICMP
建立连接
端口映射
SOCKS5握手
SOCKS5 UDP
互联网
响应数据
解封装
UDP返回
构造TCP包
构造UDP包
注入数据包
返回数据
交付
应用程序
系统协议栈
TAP虚拟网卡
Tap.Input事件
TapTap2Socket.ProcessInput
ProcessTcpInput
ProcessUdpInput
ProcessIcmpInput
Connection
Port/Datagram
代理服务器
目标服务器
Tap.Output

二、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代理,实现了完整的握手流程:

握手流程包括:

  1. 协议协商:发送支持的认证方法(无认证或用户名/密码)
  2. 认证(可选):如果启用认证,发送用户名和密码
  3. 连接请求:发送目标地址和端口,建立代理连接

握手完成后,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的完整启动流程:

  1. 权限检查:必须以管理员权限运行
  2. TAP驱动部署:检查并安装TAP-Windows驱动
  3. 网关修复:尝试恢复系统默认网关
  4. 防火墙规则:添加程序到防火墙白名单
  5. 代理服务器解析:解析命令行参数,支持域名解析
  6. 创建Socks5Ethernet实例:初始化核心引擎
  7. 路由配置:修改路由表和DNS设置
  8. 启动监听:开始处理数据包

八、技术特点

8.1 优势

  1. 完全用户态实现:无需内核模块,便于调试和部署
  2. 协议完整性:实现完整的TCP/IP协议栈,支持分片重组
  3. 灵活的扩展性:面向对象设计,易于扩展新的代理协议
  4. 跨平台潜力:核心逻辑与平台无关,仅TAP层需要适配
  5. 高性能:异步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)

csharp 复制代码
    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)

csharp 复制代码
            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)

csharp 复制代码
        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)

csharp 复制代码
    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)

csharp 复制代码
        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)

csharp 复制代码
        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)

csharp 复制代码
        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)

csharp 复制代码
    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)

csharp 复制代码
        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)

csharp 复制代码
        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)

csharp 复制代码
        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)

csharp 复制代码
        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)

csharp 复制代码
                    if (this.Tap != null)
                    {
                        this.DeleteAllRouter();
                        this.SwitchNamespaceServers(this.RestoreDNSServerAddresses);
                    }

File: tun2socks/Socks5Ethernet.cs (L296-316)

csharp 复制代码
        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)

csharp 复制代码
        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)

csharp 复制代码
        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)

csharp 复制代码
        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)

csharp 复制代码
        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)

csharp 复制代码
    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)

csharp 复制代码
        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)

csharp 复制代码
        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)

csharp 复制代码
        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)

csharp 复制代码
        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;
        }
相关推荐
酣大智11 分钟前
参考模型--物理层
网络
人工智能AI技术1 小时前
【C#程序员入门AI】本地大模型落地:用Ollama+C#在本地运行Llama 3/Phi-3,无需云端
人工智能·c#
B2_Proxy1 小时前
IP 来源合规性,正在成为全球业务的隐性门槛
网络·爬虫·网络协议·安全
怣501 小时前
Windows 11 临时文件清理完全指南:释放宝贵磁盘空间
windows·清理c盘
MMME~2 小时前
Ansible Playbook高效自动化实战指南
网络·自动化·ansible
数据安全科普王2 小时前
从 HTTP/1.1 到 HTTP/3:协议演进如何改变 Web 性能?
网络·其他
舰长1152 小时前
linux 实现文件共享的实现方式比较
linux·服务器·网络
学***54233 小时前
如何轻松避免网络负载过大
开发语言·网络·php
热爱生活的五柒3 小时前
WebDAV如何使用?
windows
weixin_395448913 小时前
main.c_cursor_0129
前端·网络·算法