博客

两份名单可以行完全相同,只是顺序对调。

规则文件不是集合,是一份有序名单。引擎对每一条连接只走一遍,先命中的一行给出动作,后面的行不再看。语法都写对、顺序写反,结果可以完全相反。

何时才运行

引擎的开关在策略上,不在文件里

Shadowrocket 配置文件 default.conf:Rule 289 条
配置文件里的 Rule。规则只在策略为 Config 时被问到。截自 App Store,以你设备上的版本为准。

可以写得再完整的规则,只要策略停在 Proxy,出站仍一律进当前记录;停在 Direct,则一律原路径。文件不会「自己打开」。把策略改回 Config 之后,当前选中的那一份配置才开始逐条匹配。策略三项的含义见 隧道。因此「规则没生效」有两种完全不同的意思。一种是引擎根本没被问到。另一种是引擎问到了,但先命中的那一行不是你以为的那一行。对照时先确认前者,再改后者。

配置文件可以有多份。连接时只使用当前选中的一份。服务器记录是另一套对象:换高亮行不会换文件,换文件也不会换高亮行。一份配置里写了什么名单,与列表里此刻高亮哪一条出口,是两件独立的事。见 对象配置。产品页上的域名试纸用一份写死的示意名单,只演示「自上而下」。GEOIP 在网页上模拟不了。把试纸的结果当成自己手机上的分流,会得到错误结论。

顺序

更窄的条件写在更宽的前面

常见写法是:条件类型,参数,动作。动作为 DIRECT、PROXY、REJECT。匹配自上而下。某一行命中之后,后面的行不再看。所以例外必须写在通例之前。把 FINAL 放在文件中间,等于下面所有行作废——它们还在文件里,但永远不会被问到。行格式与条件类型的细则在 规则 章,下面只用一份对调说明顺序本身。

# A:先点名主机,再写后缀
DOMAIN,api.example.com,PROXY
DOMAIN-SUFFIX,example.com,DIRECT
FINAL,PROXY

# B:先写后缀
DOMAIN-SUFFIX,example.com,DIRECT
DOMAIN,api.example.com,PROXY
FINAL,PROXY

api.example.com:名单 A 命中第一行,走记录;名单 B 命中后缀,走直连。对 www.example.com:两份都会在后缀处走直连。对一个谁都没点名的主机:两份都落到 FINAL,走记录。差异只发生在「既被具体行覆盖、又被宽行覆盖」的那些名字上。KEYWORD 比 SUFFIX 更宽,GEOIP 比大多数域名行更宽,FINAL 最宽。一个稳妥的顺序是:具体主机 → 后缀 / 关键字 → 网段 → GEOIP → FINAL。这不是语法要求,是为了让例外有机会被看见。

DOMAIN 要求主机名完全相等,DOMAIN-SUFFIX 要求等于该后缀或以「.后缀」结尾,DOMAIN-KEYWORD 只要求包含该字符串。后缀行不会自动覆盖另一个注册域:写了 google.com 并不等于管到了 gstatic.com。IP-CIDR 只管 IPv4,IPv6 目的地址看不见这一行,请求继续往下掉,直到 GEOIP 或 FINAL。只补了 IPv4 的私网段、设备却用 IPv6 访问同一服务,看起来会像规则「有时灵有时不灵」。见 解析

GEOIP 按目的 IP 在地理库中的归类,不是按域名注册地,也不是按网站语言。CDN 把同一主机名指到不同国家的地址时,GEOIP 的结果会跟着变。规则看到的名字,也不一定是地址栏里那串:应用如果直接连接 IP、或不带 SNI,域名行全部落空,最后打在地址行或 FINAL 上。这就是「同一网站,有时走代理有时走直连」的一种来源——两次请求带给引擎的信息不一样。

外来文件

先读 FINAL,再决定要不要用

外来列表的作者按他自己的出口在写。FINAL 写成 PROXY 还是 DIRECT,决定了所有未被点名流量的命运,比再加一百条域名行更关键。若你的记录本身在国内、只想让少数主机走记录,FINAL 写成 PROXY 会把大量无关请求送进节点。若记录在境外、希望未点名的都走记录,FINAL 写成 DIRECT 则多数网站根本不会进节点。

改了一行从未被走到的规则,Data 不会变,网页也不会变。要用固定的一条记录、只切换策略做对照:Proxy 下该主机已经能打开,再回到 Config 看计数站在哪一侧。同时换记录、换文件、改 FINAL,每一次变化都可以用另外两个变量解释。订阅型规则的刷新与服务器容器的刷新是不同对象上的同一类动作:失败时保留上一次成功的文件,不会为了失败把规则抹空。

动作也不是「连上」的不同程度。DIRECT 从原路径离开,PROXY 发给当前高亮的记录,REJECT 不发出。把本该直连的系统服务写成 PROXY,常见后果是推送或商店更新变慢;把本该进记录的主机写成 DIRECT,则 Data 只见 Direct 增加;写成 REJECT,应用会报网络错误,容易被误判成节点坏了。改动作之前先确认这一行真的会被命中。

同一条连接带给引擎的信息也可能不完整。浏览器通常既有主机名又有解析出的地址,域名行和地址行都有机会命中,仍然是先中的那一行。某个客户端只连 IP、不带 SNI,域名行全部落空,只能打在 IP-CIDR、GEOIP 或 FINAL 上。于是「Safari 走代理、某应用走直连」:两个程序送给引擎的东西不一样,不是引擎随机。对照时不要只改域名行,也要用 Data 看这一次字节站在哪一侧。首页试纸只能输入域名,模拟不了「只有地址、没有名字」的请求,更模拟不了设备上的地理库版本。

行数多不等于例外被看见

在文件末尾继续追加域名行,挡不住已经写在前面的宽条件。要让某一主机走另一条路,必须把它写到会截走它的那一行之前。顺序错了,再长的名单也只是一份被截短的名单。