tun2proxy 代码库 DNS 解析说明

tun2proxy通过在src/args.rs文件中定义的ArgDns 枚举来提供三种不同的DNS处理策略:Virtual, OverTcp, 和 Direct。核心的处理逻辑位于 src/lib.rs` 文件的主循环中,它会根据用户选择的策略来处理发往53端口(DNS端口)的UDP数据包。

以下是三种策略的详细代码逻辑分析:

1. Direct DNS (--dns direct) - 直接转发

这是最简单直接的策略,也是默认策略。

  • 工作原理
    tun2proxy 捕获到一个目标为DNS服务器(端口53)的UDP数据包时,它不会解析或修改DNS查询内容本身。它的核心操作是:如果数据包的目标IP是一个私有或保留地址,它会将其目标IP地址修改为您在参数中指定的公共DNS服务器地址(--dns-addr,默认为 8.8.8.8)。然后,这个数据包会像其他普通数据包一样,被路由到TUN虚拟网卡,并通过代理服务器转发出去。

  • 代码逻辑
    src/lib.rs 的主循环中,如果DNS策略是 Direct,代码会执行 info.dst.set_ip(dns_addr); 这行指令,将数据包的目标地址设置为配置的DNS服务器地址,然后就进入标准的UDP代理处理流程。

  • 适用场景

    • 当你信任你的代理服务器能够正确、高效地处理UDP数据包时。
    • 当你的系统或应用需要直接与特定的DNS服务器通信时。
    • 这是最接近“透明代理”的模式,对DNS查询的干预最少。

2. DNS over TCP (--dns over-tcp) - TCP封装

此策略旨在解决代理服务器对UDP支持不佳或网络环境对UDP不友好的问题。

  • 工作原理
    tun2proxy 捕获到一个UDP DNS查询时,它并不会直接转发这个UDP包。相反,它会:

    1. 将这个会话的协议标记从UDP更改为TCP (info.protocol = IpProtocol::Tcp)。
    2. 与代理服务器建立一个TCP连接。
    3. 在原始的DNS查询数据包前,加上一个2字节的长度字段(符合RFC 7766规范),然后通过这个TCP连接发送给代理。
    4. 代理服务器将通过TCP将查询转发给DNS服务器,收到TCP响应后,再原路返回给 tun2proxy
    5. tun2proxy 接收到TCP形式的DNS响应,去掉长度字段,将其还原为原始的UDP数据包格式,再发送给发起请求的应用程序。
  • 代码逻辑
    相关逻辑主要在 src/lib.rshandle_dns_over_tcp_session 函数中实现。它会建立一个TCP流,并在发送和接收数据时处理2字节的长度前缀,确保DNS报文的正确封装和解封装。

  • 适用场景

    • 代理服务器不支持UDP转发(UDP Associate)。
    • 网络环境(如移动网络)对UDP不友好,丢包严重,但TCP连接稳定。
    • 需要更可靠的DNS查询传输。

3. Virtual DNS (--dns virtual) - 虚拟IP映射

这是功能最强大也是最复杂的策略,通常被称为 "Fake IP" 模式,能有效防止DNS泄露。

  • 工作原理

    1. 拦截查询tun2proxy 捕获到DNS查询后,并不向任何外部DNS服务器转发。
    2. 分配虚拟IP:它在内部维护一个IP池(默认为 198.18.0.0/15)。src/virtual_dns.rs 中的 VirtualDns 模块会为查询的域名(例如 www.google.com)从这个池中分配一个虚拟IP(例如 198.18.0.10),并记录下 www.google.com -> 198.18.0.10 这个映射关系。
    3. 伪造响应tun2proxy 会伪造一个DNS响应包,发回给应用程序,告诉它 www.google.com 的IP地址是 198.18.0.10
    4. IP连接捕获:应用程序收到响应后,会尝试与 198.18.0.10 这个虚拟IP建立TCP或UDP连接。
    5. 域名反向解析与代理tun2proxy 捕获到这个发往虚拟IP的连接请求。它会查询内部的映射表,将目标IP 198.18.0.10 反向解析回原始域名 www.google.com。然后,它向SOCKS5/SOCKS4a/HTTP代理服务器发起请求,要求连接到域名 www.google.com,而不是那个虚拟IP。
    6. 远程解析:最终的DNS解析由远端的代理服务器完成。
  • 代码逻辑

    • src/virtual_dns.rs: 实现了 VirtualDns 结构体,包括LRU缓存来管理IP和域名的映射,以及分配和回收虚拟IP的逻辑。
    • src/lib.rs: handle_virtual_dns_session 函数调用 VirtualDns 来生成伪造的DNS响应。在处理TCP和UDP会话时,会检查目标IP是否在虚拟池中,如果是,则解析出域名,并将域名传递给代理处理器(例如 SocksProxyImpl)。
  • 优势与适用场景

    • 防DNS泄露:真正的DNS解析发生在代理服务器端,本地不会有任何真实的DNS流量产生。
    • 强制远程解析:完美支持SOCKS5h、SOCKS4a这类需要代理服务器进行域名解析的协议。
    • 兼容性强:即使代理不支持UDP,也能通过此方式处理需要域名解析的UDP应用,因为解析过程被 tun2proxy 内部接管了。
    • 推荐默认选择:在绝大多数需要强隐私保护和高度兼容性的场景下,这是最佳选择。

总结

策略工作原理优点缺点
Direct直接将DNS UDP包转发给指定DNS服务器。简单高效,对DNS干预最少。依赖代理的UDP转发能力,可能存在DNS泄露风险。
Over-TCP将DNS UDP查询封装成TCP流发送。可靠性高,适用于UDP不稳定的环境或代理。性能开销略高,增加了握手延迟。
Virtual内部维护虚拟IP池,将域名映射到虚拟IP,由代理服务器执行最终解析。彻底防止DNS泄露,兼容性极强,支持远程域名解析。实现最复杂,可能会与某些依赖特定IP行为的软件有冲突。

透明代理说明

在网络代理的领域中,“透明”这个词指的是代理服务器对用户和应用程序的“隐形”程度。一个完全“透明的代理”意味着,无论是用户还是正在运行的应用程序,都不知道自己正在通过一个代理服务器来访问网络。

具体来说,"透明"体现在以下几个方面:

  1. 无需配置

    • 非透明代理:你需要在操作系统或特定软件(如浏览器)的设置中,手动输入代理服务器的IP地址和端口号。应用程序知道它必须先把网络请求发送给代理服务器。
    • 透明代理:用户和应用程序完全不需要进行任何代理设置。网络流量被网络设备(如路由器或像 tun2proxy 这样的软件)在底层自动拦截,并强制重定向到代理服务器。
  2. 应用程序无感知

    • 应用程序像平常一样发起网络连接,它认为自己是直接连接到目标服务器(例如 www.google.com)。
    • 它不知道中间有一个“人”在拦截、检查和转发它的所有数据。所有这一切都在它不知情的情况下发生。
  3. 流量的自动重定向

    • 透明代理的核心技术在于流量的重定向。在 tun2proxy 的场景中,它通过创建一个虚拟网络接口(TUN设备)并修改系统的路由表来实现这一点。
    • 当你的电脑发送任何网络数据包时,操作系统根据新的路由规则,不会把它们发送到默认的物理网关,而是发送到这个TUN虚拟接口。
    • tun2proxy 程序就在这个接口的另一端监听,捕获所有流经的数据包,然后再将这些数据包通过你配置的SOCKS5或HTTP代理发送出去。

tun2proxy 如何实现“透明代理”?

tun2proxy 正是一个典型的透明代理工具。当你使用 --setup 参数时,它会自动完成以下操作,让代理过程对你完全“透明”:

  • 创建虚拟网卡:创建一个 tun 设备,这是一个软件层面的虚拟网卡。
  • 修改路由:修改你电脑的路由表,将大部分网络流量都指向这个虚拟网卡,而不是你的物理路由器。
  • 修改DNS:它可能会修改你系统的DNS设置(例如,指向它自己的虚拟DNS服务器 198.18.0.1),以确保DNS查询也被它捕获,从而防止DNS泄露。

当你运行一个程序(比如浏览器)时,浏览器向 www.example.com 发出请求。这个请求的数据包被系统路由到tun设备,tun2proxy接收到后,再通过远端的代理服务器去访问 www.example.com。整个过程中,浏览器本身并不知道这一切的发生,它就像直接连接到了互联网一样。

所以,简单来说,“透明代理”的“透明”指的是代理过程的“隐形性”和“无感知性”。它让网络流量在用户和应用程序毫不知情的情况下,被自动地、强制地通过代理服务器进行转发。