Releases · Fndroid/clash_for_windows_pkg · GitHub
https://github.com/Fndroid/clash-win-docs-new
对于不遵循系统代理的软件,TUN 模式可以接管其流量并交由 CFW 处理,在 Windows 中,TUN 模式性能比 TAP 模式好.
#Windows
启动 TUN 模式需要进行如下操作:
- 点击
General
中Service Mode
右边Manage
,在打开窗口中安装服务模式,安装完成应用会自动重启,Service Mode 右边地球图标变为绿色
即安装成功(无法安装参考:这里) - 点击
General
中TUN Mode
右边开关启动 TUN 模式
#macOS
启动 TUN 模式需要进行如下操作:
- 点击
General
中Service Mode
右边Manage
,在打开窗口中安装服务模式,安装完成应用会自动重启,Service Mode 右边地球图标变为绿色
即安装成功 - 点击
General
中TUN Mode
右边开关启动 TUN 模式
TIP
若要将此 Mac 设置为代理网关,打开 IP 转发即可:
sudo sysctl -w net.inet.ip.forwarding=1
这种做法将在机器下次重启后失效,如果想要永久保存,编辑文件/etc/sysctl.conf
,配置下面变量:
net.inet.ip.forwarding=1
net.inet6.ip6.forwarding=1
或者使用 LaunchDaemons 进行配置:
- 新建
network.forwarding.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>Network Forwarding</string>
<key>UserName</key>
<string>root</string>
<key>GroupName</key>
<string>wheel</string>
<key>ProgramArguments</key>
<array>
<string>/usr/sbin/sysctl</string>
<string>-w</string>
<string>net.inet.ip.forwarding=1</string>
<string>net.inet6.ip6.forwarding=1</string>
</array>
<key>KeepAlive</key>
<false/>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
- 将文件添加进
/Library/LaunchDaemons
sudo launchctl load /Library/LaunchDaemons/network.forwarding.plist
#Linux
启动 TUN 模式需要进行如下操作:
- 点击
General
中Service Mode
右边Manage
,在打开窗口中安装服务模式,安装完成应用会自动重启(某些系统需要手动重启 APP),Service Mode 右边地球图标变为绿色
即安装成功 - 点击
General
中TUN Mode
右边开关启动 TUN 模式
TIP
Service Mode 安装脚本使用 Kr328/clash-premium-installer
#配置文件参考
from
https://docs.cfw.lbyczf.com/contents/tun.html#windows
-------------------------------------------------------------------------
Clash的TUN 和 TAP 模式
非系统代理应用上网
浏览器之类的应用都是使用系统代理的,一些非系统代理应用,可以通过设置被 cfw 接管。
目前有两种模式,TAP 模式和 TUN 模式,更推荐使用TUN模式
(下面是 windows 系统方法,mac 参考 https://docs.cfw.lbyczf.com)。
TUN 模式
方法一
-
进入网站 https://www.wintun.net ,点击界面中 Download Wintun xxx 下载压缩包,根据系统版本(win10 64 位对应的是 amd64 目录下)将对应目录中 wintun.dll 复制至 clash 的 Home Directory 目录中(General 页面的 Home Directory 点击就可以打开)
-
点击 General 中 Service Mode 右边 Manage,在打开窗口中安装服务模式,安装完成应用会自动重启,Service Mode 右边地球图标变为绿色即安装成功
-
点击 settings——Profile Mixin——YAML,点击 edit,输入下面的内容,点击保存
mixin: |
- 返回 general 页面开启 Mixin 即可,之后点击 connections 会发现所有连接都是 TUN 模式
方法二
如果上述方法卡在第二步,点击安装以后一段时间都没有任何反应,可以采取如下方法
- 打开 home directory(General 页面的 Home Directory 点击就可以打开)
- 进入 service 文件夹
- 在资源管理器上方的路径处,默认应为 C:\Users\Administrator.config\clash\service,改成 cmd 并回车,即为在此处打开 cmd
- 打开以后,输入
service install clash-core-service.exe
等待安装完毕后,关闭窗口,重启计算机 - 点击 settings——Profile Mixin——YAML,点击 edit,输入下面的内容,点击保存
mixin: |
- 返回 general 页面开启 Mixin 即可,之后点击 connections 会发现所有连接都是 TUN 模式
TAP 模式
-
点击 General 页面中 TAP Device 选项的 Manage 按钮,在弹出对话框中选择 Install 将会安装 TAP 网卡,此网卡用于接管系统流量,安装完成可在系统网络连接中看到名为 cfw-tap 的网卡
-
启动 TAP 模式:点击 settings——Profile Mixin——YAML,点击 edit,输入下面的内容,点击保存
-
返回 general 页面开启 Mixin 即可。
mixin: |
---------------------------------------
关于 Clash 科学上网的最佳实践
节点一直都连得上,只是墙在 DNS 方面又加高了一些。
经过几天的搜寻资料,阅读文档,尝试各种组合,我可以负责任的说,在现在这个时间节点,没有人比我更懂软件里这些配置项。
DNS – 上 网的根基
目前所有公共 DNS 都污染了谷歌 / ChatGPT 这些域名。普通 DNS 请求走的是 UDP 明文连接,少部分情况走 TCP 但是也是明文,污染一部分来自于墙的抢答,墙现在是随机返回一个 IP,没什么规律可言,所以没有好的检测应对策略。另一部分来自于 DNS 本身的污染,污染源我就无从得知了。具体表现如下,从国内 223.5.5.5 查询 chat.openai.com,返回的结果是被污染过的:
dig chat.openai.com @223.5.5.5
;; ANSWER SECTION:
chat.openai.com. 1 IN A 128.242.245.93
;; Query time: 8 msec
;; SERVER: 223.5.5.5#53(223.5.5.5) (UDP)
但是从境外请求 223.5.5.5 就是正确的
dig chat.openai.com @223.5.5.5
;; ANSWER SECTION:
chat.openai.com. 146 IN CNAME chat.openai.com.cdn.cloudflare.net.
chat.openai.com.cdn.cloudflare.net. 146 IN A 104.18.37.228
chat.openai.com.cdn.cloudflare.net. 146 IN A 172.64.150.28
;; Query time: 28 msec
;; SERVER: 223.5.5.5#53(223.5.5.5) (UDP)
有人可能会觉得墙在抢答,DNS 本身还是干净的,那我们再来一组实验,从境内分别使用国内的 DoH 和国外的 DoH:
# 从境内请求境内的 DoH
dog --time chat.openai.com --https @https://dns.alidns.com/dns-query
A chat.openai.com. 1m45s 108.160.162.102
Ran in 118ms
# 从境内访问境外的 DoH
dog --time chat.openai.com --https @https://1.1.1.1/dns-query
CNAME chat.openai.com. 1m53s "chat.openai.com.cdn.cloudflare.net."
A chat.openai.com.cdn.cloudflare.net. 1m53s 172.64.150.28
A chat.openai.com.cdn.cloudflare.net. 1m53s 104.18.37.228
Ran in 1631ms
结果不言而喻了,国内的 DNS 环境,除了墙会抢答之外,DNS 本身也会污染一些结果。不过我相信阿里 DNS 解析大多数域名还是干净的,只是污染了某几个特殊照顾的域名。这点也是下面某些配置的前提。
关键词说明
为避免歧义,先对几个关键词作出说明
- 终端:指运行师夷长技工具的设备,例如电脑/手机等等
- 代理工具:即上面说的师夷长技工具,例如 Clash / Surge 等等
- 远端:指代理工具使用的节点
- 本地:泛指终端和代理工具
- 兜底策略:没有匹配任何规则的策略,例如 DIRECT / Proxy 等等
关于 tun 模式和 fake IP
在普通的模式下,即设置系统 socks5 / http 代理的情况下,当终端发起一个连接的时候是直接全部封装网络包发出去,发之前不做域名解析。在这种情况下,如果触发了代理工具的分流规则,自动就会走远端解析,反之如果没触发,走直连并且使用前面配置的 nameserver (223.5.5.5) 解析。
而在 tun 模式下,由于在网卡层接管流量,代理是透明的,终端则和正常请求一样,需要先拿到一个 IP 才可以发起连接,普遍来讲代理工具都会劫持所有 53 端口的请求,也就是说 IP 普遍来讲都是代理工具提供的,可以是真实解析再提供,也可以是提供一个假的 IP 骗终端先发起连接。
真实解析固然是好,但是速度从几十毫秒到几千毫秒不等,而且这个 IP 还不一定用得上,例如发起的连接触发了分流规则,走远端解析,或是 IP 被污染导致网站打开失败。这个时候就可以引入一个概念 fake IP。fake IP 这个概念出自 RFC3089,早在 2001 年就被提出来了,下文的讨论假设 fake IP 被设置为 28.0.0.1/8
网段.
在 tun 模式下,代理工具是可以劫持所有网络包的,假设用户请求 chat.openai.com,代理工具先返回一个假的 IP 比如 28.0.0.2
,同时记住这个映射关系:"chat.openai.com": "28.0.0.2"
,当终端对 28.0.0.2
发起连接的时候,代理工具就知道要访问的域名是 chat.openai.com
,就开始判断规则,看是否整个走远端解析,或是走直连并且使用 223.5.5.5 解析。在这里不由得佩服代理工具开发人员的智慧。
简单讲:
- 使用普通的系统代理可以让终端第一时间发起连接,进入分流阶段,缺点是部分应用不遵守系统代理,优点是除了前面一个缺点全都是优点
- tun 模式是为了让终端实现全局代理的能力,但是 tun 模式下,终端拿到 IP 之后才会发起连接
- fake IP 是为了让终端第一时间发起连接,进入分流阶段,后续和系统代理相同
用 fake IP 接管 DNS 会造成一些意想不到的问题,这里需要和普通系统代理做个取舍。
配置误区之 fallback dns
fallback dns 是 redir-host 时代的产物,是为了解决 DNS 污染问题。
redir-host 模式也是 tun 模式的一种(还有一种叫 fake IP),tun 模式工作在第三层网络层,拿不到请求的域名,只能拿到连接的 IP 地址。那他如何根据域名规则分流呢?答案是先拦截 53 端口的 DNS 请求,自己建立一个映射表,在终端发起请求的时候来匹配访问的域名,从而进行后续分流等操作。
但是如果多个网站部署在同一个 IP 下,或者 DNS 被污染到同一个 IP,redir-host 就不准了(除非加上嗅探功能)。配置 fallback dns 之后,如果获取到的 IP 被判定为被污染(需要回滚),就使用 fallback dns 解析出来的结果。为了确保拿到的结果完全正确,fallback dns 要设置为境外的加密 DNS 服务器,因为无加密或者境内的 DNS 都会被污染。
但正如前文论述,使用境外加密 DNS 毫无体验可言,而且这个本地解析出来的 IP,绝大多数场景下是用不到的,最终还是会走远端解析。终端只是需要一个 IP 建立连接,代理工具只是需要一个 IP 来维护映射表而已。费时费力 fallback 出来的一个真实 DNS 结果,最终还用不上。fallback DNS 的核心功能只是一定程度上解决 DNS 污染问题,降低了映射表出错的概率。
所以随着发展,redir-host 已经被官方扫进了垃圾堆,毕竟硬伤太多了。在继任者 fake IP 模式下,代理工具拦截 53 端口的查询,立刻返回一个假的 IP,让终端立刻发起连接。由于假的 IP 是自己生成的,可以确保每个域名 IP 都不同,彻底杜绝了上述映射表出错的可能性,并且分流结果可以拿去给远端解析。
所以,在弃用 redir-host 并且改用 fake IP 模式的情况下,fallback 极大概率是不需要配置的,把黑名单规则写全,全部走远端解析,剩下的没被污染的域名走国内 223.5.5.5 岂不是更快。
如果一定要用 fallback DNS 的话,确保填入境外的加密 DNS 即可,因为 fallback DNS 的最终目的是正确解析被污染的域名。不过这样体验极差,境外加密 DNS 至少需要 1.5 秒拿到结果。
Clash / Surge 配置注意事项
很多年前大家都熟练掌握分流配置,自动测速配置,规则集这些,就不再赘述,主要分享下有关 DNS 污染的应对策略。
协议选择
首先,原厂 shadowsocks 仍然是最稳的协议之一,v2ray 插件我测试下来测速会慢个 1 秒左右,原因未知,其他协议例如 vless vmess 也都不错,性能表现都大同小异,没有发现哪一种协议被墙盯上的
nameserver 配置
nameserver 使用普通的无加密 UDP 协议就好,例如 223.5.5.5 和 119.29.29.29,前提是大厂 DNS 只重点关照特定几个域名,以及墙只抢答 53 端口的这些域名。如果将来有一天大面积抢答国外域名,需要再想对策,例如使用境内 DoH。
为什么不建议折腾 DoH DoT 呢,用境内的 DoH 解析 chat.openai.com,该污染照样污染,第一章节已经论证过了。也别想着用境外的 DoH DoT,DoT 853 公网端口不定时抽风,稳定性很差。境外的 DoH 普遍查询一次都在 2 秒左右,毫无体验可言,第一章节也已经论证过了。
gfwlist 域名直接走远端解析就好,本地只解析直连的网站,而这些没命中规则的域名大概率是没被污染的,走 DoH 解析没有意义,除非你特别介意明文传输的 DNS 数据包。
fallback dns 配置
在上个章节已经论述过,fake IP 模式下不需要配置 fallback dns。我自己还遇到过一些麻烦,贴出来仅供参考:
- Clash Premium 内核下,如果规则里写了
IP-CIDR,1.1.1.1/32,Proxy,no-resolve
,终端进行dig xxx.com @1.1.1.1
的时候是真的会走代理 - Clash Meta 内核不支持这个特性,我没做太密集的测试,初步测试是不行
- 会有大量网站打不开,原因不明
- 即使我使用例如
https://1.1.1.1/dns-query
不需要解析 DoH 域名的,也会有网站打不开的情况,原因不明
况且 fallback 支持的能力太弱了,只支持 geosite 匹配和 geoip 匹配,很容易误伤,即要么很多漏网之鱼,要么一杆子全打死,导致体验极差。本来好好的交给远端解析就好,多此一举还惹一身麻烦,总之只要你的 gfwlist 规则够用,就不太可能需要 fallback DNS。
分流配置注意事项
分流配置方面,尽可能移除不带 no-resolve 的 IP 规则(例如谷歌云亚马逊云的 IP 段),TG 之类的 IP 规则要保留但是必须加上 no-resolve,这样可以避免多一次解析 DNS。举个例子:
- 假设现在有一个规则列表,里面有域名匹配,有 IP 匹配。当终端发起一个连接的时候,如果没有命中域名匹配,或者域名规则比较靠后,则终端上运行的代理工具会发起一次 DNS 请求,用来判断是否在 IP 匹配规则里,这里就会有潜在的 DNS 污染 /泄露 问题
- 带上 no-resolve 之后,则不会进行 DNS 解析,除非应用是对 IP 发起连接才会匹配到这条 IP 规则
其他配置
节点测速必须用完整的时间,不要使用 unified-delay,因为某些协议的握手时间很离谱。例如我的节点上同时有原厂 shadowsocks 和 v2ray 插件 websocket 混淆的 shadowsocks,v2ray 插件的完整时间比原厂慢 1 秒,应该就是握手速度太慢,但是如果开启 unified-delay,插件版反而快 100ms。
如果你的兜底策略是直连,那么前面只写需要 Proxy 的规则就好了,不命中就会直连,没必要增加配置的复杂度,反之亦然。
我个人更喜欢兜底策略用直连了,因为现在各 vps 的 IP 地址被黑产污染的很厉害,兜底配置走代理很容易触发风控,例如 Cloudflare 的人机验证,谷歌的人机验证。我个人的想法是,只要 gfwlist 域名匹配规则够详细,就很难遇到 DNS 污染问题,对于非 gfwlist 的域名,走直连也没什么不好,遇到哪个网站直连慢的自己添加一条代理规则就好。
tun 模式的缺陷
tun 模式有两个问题,第一个问题下一个章节有解决方案,第二个就要自己取舍了
- 配合去广告 DNS 使用的时候,由于终端拿到的假 IP 不为空,有些没节操的软件会引发雪崩,即不断循环发请求,导致设备发热以及额外的流量消耗
- ping 命令拿不到真实的延迟
DNS 进阶配置
部分用户是有去广告的需求的,会在规则列表引进来几万条拦截广告配置,但是终端毕竟处理能力有限,不如把这件事交给服务器来做,即搭建一个 AdGuard Home 服务。
众所周知中国境内想要搭建 53 端口的 DNS 服务器是违法的,不过搭建在非 53 端口目前还没人管。恰巧 Clash 这类软件支持自定义端口号的 DNS 服务器,自建的好处是重复请求的域名处理时间基本上是 0 毫秒,请求的速度只取决于网络延迟,比公共 DNS 还是要快很多。
由于中国很大,很多网站的 CDN 都细化到省份甚至城市,自建 DNS 的风险之一就是会把 CDN 解析到距离服务器最近的位置。通常来讲横跨半个中国网速也不会太慢,但是这样毕竟不环保,解决方案是打开 AdGuard Home 的 EDNS Client Subnet 功能,上游 DNS 服务器就会根据 ECS 的信息返回正确的 IP。
请注意并不是所有上游 DNS 服务器都支持 ECS 功能,测试方法如下
# 新加坡
dig +subnet=111.65.50.0/24 api.bilibili.com @223.5.5.5
;; ANSWER SECTION:
api.bilibili.com. 1 IN CNAME a.w.bilicdn1.com.
a.w.bilicdn1.com. 1 IN CNAME i.w.bilicdn1.com.
i.w.bilicdn1.com. 1 IN A 164.52.39.43
/* 164.52.39.43 归属地是新加坡 CDS Global Cloud CO., LTD */
# 广东深圳电信
dig +subnet=218.17.109.0/24 api.bilibili.com @223.5.5.5
;; ANSWER SECTION:
api.bilibili.com. 1 IN CNAME a.w.bilicdn1.com.
a.w.bilicdn1.com. 1 IN A 59.36.228.17
/* 59.36.228.17 归属地是佛山 中国电信 */
# 北京移动
dig +subnet=223.104.3.0/24 api.bilibili.com @223.5.5.5
;; ANSWER SECTION:
api.bilibili.com. 90 IN CNAME a.w.bilicdn1.com.
a.w.bilicdn1.com. 90 IN A 111.31.33.21
/* 111.31.33.21 归属地是天津 中国移动 */
根据我的测试,国内只有 223.5.5.5 和 119.29.29.29 支持 EDNS 解析,国外只有 8.8.8.8 和 223.5.5.5 支持 EDNS。这些公共 DNS 服务器对应的 IPv6 地址也支持 EDNS。
将上述支持 EDNS 的服务器配置到 AdGuard Home上游 DNS 列表,即可较为完美的实现去广告+CDN 加速。附加好处是自建服务器到终端这段路径的 DNS 响应不会被 GFW 抢答,因为现阶段 GFW 只抢答 53 端口。PS: 如果上游配置的是 53 端口无加密 DNS,自建服务器到上游仍然会被抢答。
另外需要注意,开启 EDNS 功能会在一定程度上影响 DNS 缓存,如果你只在服务器物理位置附近活动,建议不要开启此功能,如果你全国各地到处跑,开启 EDNS 会更好一些。
配置完之后即可实现去广告了,不过恭喜你喜提机身发热,请求日志雪崩的问题,日志列表可以看到大量涌入的类似于 dial DIRECT error: dial tcp4 0.0.0.0:443: connect: connection refused
的错误,原因是 fake IP 模式下广告软件收到一个假的但是有效的 IP,而自建 DNS 给代理工具返回的是一个空 IP(0.0.0.0)。有些没节操的软件这时候就会疯狂重试,点名批评一下微软和英伟达的日志上报代码。解决方案是搜集日志列表雪崩的域名,或者 AdguardHome 拦截排行榜前几名的域名,写成 Clash 规则并且 REJECT 即可,REJECT 仍然还有少部分会不断重试但是总体情况已经好很多了。
参考资料
---------------------------------------------------------------------------------
浅谈在代理环境中的 DNS 解析行为
虽然 Fake IP 这个概念早在 2001 年就被提出来了,但是到 Clash 提供 fake-ip 增强模式以后,依然有很多人对 Fake IP 这个概念以及其作用知之甚少。本文就简单谈谈在代理环境中,TCP 连接建立之前发生的事。由于移动设备操作系统中网络栈相对复杂,本文的例子也并不一定适用于移动端环境。文章中也许会存在很多错误,也希望各路大佬的勘误和斧正。
不使用代理
如果在不使用任何代理的情况下,打开一个没有命中 DNS 缓存的网站(比如 blog.skk.moe)的时候,浏览器和操作系统大概会执行这么一些操作: 浏览器自己都有 DNS 缓存机制,因此浏览器会先开始寻找自己的缓存,不过没有找到 blog.skk.moe 的解析结果 浏览器通过调用操作系统的 getaddrinfo 方法,向操作系统寻求解析结果 操作系统自己也有一层 DNS 缓存,但是现在操作系统从自己的缓存中依然找不到这一结果 在系统的网络设置之中有设置上游 DNS 地址,假设操作系统中设置的是 119.29.29.29,那么操作系统会向 119.29.29.29 发起解析请求(UDP 流量)拿到 blog.skk.moe 的 IP 当然如果 119.29.29.29 自己没有 blog.skk.moe 的解析结果会找它的上游去要。不过我们不关心这一点,反正最后 119.29.29.29 会把 blog.skk.moe 的解析结果返回给设备的操作系统 现在,浏览器已经可以开始向 blog.skk.moe 的 IP 发起 HTTPS 连接了.
以上是打开一个网页常见的 DNS 解析流程,对于其它非 HTTP 的 TCP 连接(比如 SMTP)也都差不多是这个流程——由于 TCP/IP 的协议特性,在应用发起 TCP 连接时,会先发出一个 DNS question(发一个 IP Packet),获取要连接的服务器的 IP 地址,然后直接向这个 IP 地址发起连接。
设置代理并使用直连
现在,我们在应用程序(比如我们的浏览器、或者其它应用)中设置了代理,但是这个代理不涉及到任何远端服务器(直连模式)。接下来以设置了 SOCKS5 代理的浏览器为例。 浏览器不再需要从自己的 DNS 缓存中寻找 blog.skk.moe,因为已经有了 SOCKS5 代理,浏览器可以直接将域名封装在 SOCKS5 流量之中发往代理客户端 代理客户端从 SOCKS5 流量中抽出 blog.skk.moe 这个域名并设法获得解析结果 代理客户端将你的 SOCKS5 流量还原成标准的 TCP 请求 代理客户端将这个 TCP 连接建立起来,在这个例子之中 TCP 连接承载的是 HTTPS 之前由于获取解析结果是浏览器在操作,而大部分浏览器都会选择调用系统的 getaddrinfo 方法,因此如果你想要在 DNS 上做一些黑魔法就只能在操作系统层面实现,比如在本机或者别处架设一个带黑魔法的 DNS 服务器,然后你系统中设置使用这个 DNS 服务器。现在 DNS 解析是由代理客户端执行,因此在代理客户端上就可以实现一些黑魔法。比如 Surge 自己实现了一个 DNS Server 可以并发向多个上游同时发起查询、比如 V2Ray 可以实现不同域名的查询分流,等等。当然代理客户端也可以使用操作系统的 getaddrinfo 方法。
设置代理并将流量转发到远端服务器
现在在上一步的基础之上,我们为代理服务器设置了一个远端服务器,这个代理会使用 某种协议 和远端服务器通信,并且这种协议和 SOCKS5 一样支持将域名封装在传输中。浏览器和代理客户端之间依然使用 SOCKS5 通信。 因为已经有了 SOCKS5 代理,浏览器可以直接将域名 blog.skk.moe 和整个请求封装在 SOCKS5 流量之中发往代理客户端 代理客户端从 SOCKS5 流量中抽出 blog.skk.moe 这个域名以及其它数据 代理客户端使用 某种协议 将浏览器发出的 SOCKS5 的流量重组并发给远端服务器 远端服务器使用相同的 某种协议 从流量中获得其中的域名 blog.skk.moe 远端服务器的代理服务端发起了一次 DNS 解析请求试图解析 blog.skk.moe。绝大部分情况下,代理的服务端都会直接使用操作系统的 getaddrinfo 方法、也就是由远端服务器的操作系统负责 DNS 这一次,不论是代理客户端还是你的浏览器都没有进行 DNS 解析,DNS 解析是在远端服务器上进行的。因为 某种协议 支持封装域名,然后这一次和 blog.skk.moe 连接的是远端服务器,考虑到针对 CDN 优化,DNS 解析自然需要在远端服务器上执行。 现在我已经介绍了通过代理直连和通过代理发送给远端服务器了。但是毫无疑问,我相信本文所有的读者自己使用的上网方式都不会是全面直连或者全面代理。这就是接下来要讲的:
设置代理并使用 IP 规则和域名规则进行分流
分流是一个麻烦事。一般情况下,你可能会需要使用域名进行分流(不论是白名单还是黑名单)。不过更多情况下你会使用到基于 IP 的规则来进行分流。 先来看第一个例子:使用域名规则进行分流。 浏览器将带有域名 blog.skk.moe 的 HTTPS 请求封装在 SOCKS5 流量之中发往代理客户端 代理客户端从 SOCKS5 流量中抽取出域名 blog.skk.moe 代理客户端开始将blog.skk.moe 和域名规则列表开始比较。这个列表可以是白名单或黑名单,域名可能也没有匹配上。反正最终比较得出的结果就是 blog.skk.moe 是否需要走代理。 如果不需要走代理,代理客户端剩下会做的事情和本文第二部分「设置代理并使用直连」就完全一样了;同理,需要走代理的话就需要进行本文第三部分的那个流程 使用域名规则分流很简单,除非 blog.skk.moe 最终是直连,否则代理客户端不需要进行 DNS 解析。 现在来看第二个例子:使用 IP 规则分流。 浏览器将带有域名 blog.skk.moe 的 HTTPS 请求封装在 SOCKS5 流量之中发往代理客户端 代理客户端从 SOCKS5 流量中抽取出域名 blog.skk.moe 代理客户端得到 blog.skk.moe 的解析结果 代理客户端开始将blog.skk.moe 的解析结果和 IP 规则列表开始比较。这个列表可以是 cnlist 或者 MaxMind IP 数据库。反正最终得出的结果就是 blog.skk.moe 解析结果的 IP 是否需要走代理。 如果不需要走代理,代理客户端剩下会做的事情和本文第二部分「设置代理并使用直连」就完全一样了;同理,需要走代理的话就需要进行本文第三部分的那个流程。 使用 IP 规则分流,前提首先你得有一个 IP 拿来比较。所以代理客户端必须先进行一次 DNS 解析。使用什么方法进行 DNS 解析并不重要,之前已经说过代理客户端甚至可以使用自己的黑魔法,而我们只需要关心最终代理客户端拿到了一个 IP 并且可以用于规则判定。 此时需要注意的是,虽然代理客户端获得了一个 IP,但是你只有在直连的时候,代理客户端可能(并且基本上都会)复用这个 IP;如果是将流量交给远程服务器,由于 某种协议 支持封装域名,因此远程服务器拿到的还是域名不是 IP、还需要进行一次解析。也就是说,远端服务器连接的 IP 与 代理客户端解析得到的 IP 毫无关系。
使用 redir / tun2socks 实现全局流量经过代理
在开始之前,我们先复习一下 TCP/IP 协议怎么说的——「在应用发起 TCP 连接时,会先发出一个 DNS question(发一个 IP Packet),获取要连接的服务器的 IP 地址,然后直接向这个 IP 地址发起连接」 全局流量代理可能会出现在路由器上或者 TUN/TAP 型的支持全局代理客户端上。用户不再主动为每个应用程序设置代理。此时应用程序是不会感知到代理客户端的存在,它们会正常的发起 TCP 连接,并且由于 TCP/IP 协议,在拿到 DNS 解析结果之前,连接是不能建立的。 浏览器自己都有 DNS 缓存机制,因此浏览器会先开始寻找自己的缓存,不过没有找到 blog.skk.moe 的解析结果 浏览器通过调用操作系统的 getaddrinfo 方法,向操作系统寻求解析结果 操作系统自己也有一层 DNS 缓存,但是现在操作系统从自己的缓存中依然找不到这一结果 在系统的网络设置之中有设置上游 DNS 地址。代理客户端可能会修改系统设置中的 DNS 到 127.0.0.1 或者别的 IP、也可能保留用户之前的设置,这无所谓,因为... 操作系统发出的 DNS 解析请求会经过代理客户端并最终被截获 代理客户端可以将这个解析请求原样发出去、或者用自己的黑魔法,总之代理客户端都会拿到一个解析结果 代理客户端将这个解析结果返回回去,操作系统拿到了这个解析结果并返回给浏览器 浏览器对这个解析结果的 IP 建立一个 TCP 连接并发送出去 这个 TCP 连接被代理客户端截获。由于之前代理客户端进行的 DNS 解析请求这一动作,代理客户端可以找到这个只包含目标 IP 的 TCP 连接原来的目标域名 如果是支持 redir 的代理客户端,那么代理客户端就会直接将域名和 TCP 连接中的其它数据封装成 某种协议 发给远端服务器;或者封装成 SOCKS5 后交给支持 SOCKS5 的代理客户端 如果代理客户端需要按照域名进行分流,一般会在第 6 步代理客户端解析出一个 IP 或者第 9 步代理客户端拿到域名以后。FancySS、KoolSS、SSTap 的流程大抵都是如此。 和应用程序直接将流量封装成 SOCKS5 大有不同,在类似于透明代理的环境下浏览器和其它应用程序是正常地发起 TCP 连接。因此除非得到一个 DNS 解析结果,否则 TCP 连接不会建立;代理客户端也会需要通过这个 DNS 查询动作,才能找到之后的 TCP 连接的域名。 你大概能够发现,浏览器、应用程序直接设置 SOCKS5 代理的话,可以不在代理客户端发起 DNS 解析请求就能将流量发送给远端服务器;而在透明代理模式下,不论是否需要 IP 规则分流都需要先进行一次 DNS 解析才能建立连接。 有没有办法能像直接设置 SOCKS5 代理一样省掉一次 DNS 解析呢?有,就是代理客户端自己不先执行查询动作,丢一个 Fake IP 回去让浏览器、应用程序立刻建立 TCP 连接:
在 redir / tun2socks 中使用 Fake IP
Fake IP 的定义出自 RFC3089:
https://tools.ietf.org/rfc/rfc3089
。这个 RFC 定义了一种新的将 TCP 连接封装成 SOCKS 协议的方法。 浏览器自己都有 DNS 缓存机制,因此浏览器会先开始寻找自己的缓存,不过并没有找到 blog.skk.moe 的解析结果 浏览器通过调用操作系统的 getaddrinfo 方法,向操作系统寻求解析结果 操作系统自己也有一层 DNS 缓存,但是现在操作系统从自己的缓存中依然找不到这一结果 在系统的网络设置之中设置了一个专门的上游 DNS 地址,可能是用户手动设置的也可能是代理客户端设置的。不论如何,这个设置最终会使操作系统向代理客户端发起 DNS 请求 操作系统发出的 DNS 解析请求会经过代理客户端并最终被截获 代理客户端从解析请求中获得域名,从 Fake IP 池中选取一个 IP 建立映射 代理客户端将这个 Fake IP 返回回去,操作系统拿到了这个 Fake IP 并返回给浏览器 浏览器对 Fake IP 建立一个 TCP 连接并发送出去 这个 TCP 连接被代理客户端截获。代理客户端抽取出 Fake IP 并反查出这个 TCP 连接中对应的域名 有了 TCP 连接和域名,代理客户端可以轻易地将其使用 SOSCKS5 或者 某种协议 进行封装 有了 Fake IP,代理客户端无需进行 DNS 解析。最后不论是浏览器、代理客户端还是远端服务器都不会去和 Fake IP 进行连接,因为在代理客户端这里就已经完成了截获、重新封装。 即使按照域名规则分流,代理客户端都没有进行 DNS 解析的需要。只有在遇到了按照 IP 进行分流的规则时,代理客户端才需要进行一次解析拿到一个 IP 用于判断。即便如此,这个 IP 只用于分流规则的匹配,不会被用于实际的连接。
FancySS 和 Surge / Clash 的区别
FancySS 是使用的 redir,Surge 的增强模式使用的是 Fake IP,Clash 的增强模式既有 redir-host 也有 Fake IP。首先把 FancySS 等路由器上常见的代理客户端和 Clash 的 redir-host 分为一类,Surge 的增强模式和 Clash 的 fake-ip 模式分为另一类。 路由器上常见的代理客户端一般内置了 dns2socks、dnscryp-proxy、PCap_DNSProxy 等等 DNS 方案、也支持按照一定的规则进行分流,但是都是用于答复应用程序的 DNS question 使其建立 TCP 连接的,除非直连,否则通过这些 DNS 方案拿到的解析结果的 IP 并不会被用上。 大部分路由器上的代理客户端,DNS 解析请求都是通过路由器本机发出(或转发到单一远端服务器进行解析),因此解析结果只能说「至少能用」(不一定是有 CDN 优化的,甚至有可能会有 DNS 污染),如果流量不经过代理客户端直接发往这些 IP 地址,一般也不会影响浏览器、应用程序的正常使用。因此路由器上的代理客户端可以实现通过 iptables 控制让某些端口、某些设备的流量不经过代理客户端。 而在 Fake IP 模式下,浏览器、应用程序都是对 Fake IP 发起连接,如果没有代理客户端对连接进行重新封转,那么这部分流量就不能被发往真实的目的 IP,因此所有流量都必须经过代理客户端,而根据端口、设备的分流就需要由代理客户端自己实现。
如果操作系统或者浏览器缓存了 DNS 解析结果
之前的透明代理的两个例子中,我们都假定浏览器和操作系统都没有缓存 DNS 解析结果。但是,如果操作系统或者应用程序缓存了 DNS 解析结果会发生什么? 如果是不使用 Fake IP 的 redir / tun2socks 情况下,由于操作系统、浏览器或者应用程序中的任何一个缓存了 DNS 解析结果,因此 TCP 连接可以直接根据缓存的解析结果的 IP 建立,代理客户端并没有预先收到对应的 DNS question。在这种情况下,代理客户端有可能直接将这个连接视为和 IP 连接而不是和域名连接,根据域名规则的分流可能就会因此失效,不过根据 IP 分流的规则没有失效。 如果为了避免域名分流规则失效,你可以设法阻止操作系统或者浏览器缓存 DNS 解析结果,这样每次建立 TCP 连接之前都会发送 DNS question 使代理客户端探测到域名。但是这意味着每次 TCP 连接建立都需要代理客户端进行一次 DNS 解析请求(当然代理客户端可以对 DNS 解析进行缓存避免出现延时激增)。 而对于 Fake IP 模式来说,由于代理客户端内存储有 Fake IP 和真实域名之间的映射表,因此即使操作系统或应用程序缓存了 Fake IP,在之后的 TCP 连接中,代理客户端收到流量后依然可以抽取出 Fake IP 反查出域名,因此不受 DNS 缓存的影响。 我在这里留几个问题给大家思考一下: 如果使用了 Fake IP,代理客户端不论域名是否真实存在都会返回一个 Fake IP 给浏览器,那么浏览器在试图访问一个不存在的域名时,错误信息应该是什么样的?会不会出现 DNS 解析失败的错误信息? 如果操作系统或者浏览器缓存了 Fake IP,但是代理客户端中 Fake IP 和域名的映射表丢失以后,会出现什么状况?可能会出现什么错误信息? 第二个问题很有趣。因为如果你找到了第二个问题的答案,你就会意识到 Clash 在 Fake IP 模式下偶发的无法上网的原因了。
参考资料
HTTP 代理原理及实现(一):
https://imququ.com/post/web-proxy.html
我的文章中举得都是 SOCKS5 的例子,如果想了解一下在 HTTP 代理中流量是如何被封装的,可以看看屈屈的这篇博客.
Surge 原理与实现:
https://medium.com/@Blankwonder/surge-%E5%8E%9F%E7%90%86%E4%B8%8E%E5%AE%9E%E7%8E%B0-8aa3304fb3bb
Surge 开发者写的 Surge 早期版本的工作原理,可以了解一下 Surge 是怎么处理各种协议的流量的.
漫谈各种黑科技式 DNS技术在代理环境中的应用:
Kitsunebi 开发者写的文章,详细地介绍了在不同的 V2Ray 配置下的 DNS 行为,同时还有对移动端网络栈的一些介绍.
FROM
https://blog.skk.moe/post/what-happend-to-dns-in-proxy/