規則

一份有序的名單。先命中的一行說了算。

規則檔案只在全域策略為 Config 時被詢問。Proxy 與 Direct 會跳過整份檔案。首頁的試紙用一份示意名單匹配域名,和裝置上正在用的檔案不是同一份。

  1. 01何時執行
  2. 02一行三個部分
  3. 03匹配順序
  4. 04域名條件
  5. 05地址與地理庫
  6. 06DIRECT · PROXY · REJECT
  7. 07主機名從哪來
  8. 08三條請求的走讀
  9. 09多份配置與外來列表

01

引擎的開關在策略上,不在檔案裡

可以寫得再完整的規則,只要策略停在 Proxy,出站仍一律進目前記錄;停在 Direct,則一律原路徑。檔案不會「自己開啟」。把策略改回 Config 之後,目前選中的那一份配置才開始逐條匹配。策略三項的含義見 隧道

配置檔案可以有多份。連線時只使用目前選中的一份。伺服器記錄是另一套物件:換高亮行不會換檔案,換檔案也不會換高亮行。一份配置裡寫了什麼名單,與列表裡此刻高亮哪一條出口,是兩件獨立的事。見 物件Config

因此「規則沒生效」有兩種完全不同的意思。一種是引擎根本沒被問到(策略不是 Config,或選中的不是你以為的那份檔案)。另一種是引擎問到了,但先命中的那一行不是你以為的那一行。對照時先確認前者,再改後者。

首頁試紙不是裝置上的檔案

產品頁上的域名試紙用一份寫死的示意名單。它只演示「自上而下、先命中的一行生效」。GEOIP 在網頁上模擬不了。把試紙的結果當成自己手機上的分流,會得到錯誤結論。

02

一行三個部分

常見寫法是:條件類型,參數,動作。用逗號分隔。類型與參數決定「這行管誰」,動作決定「管到了之後怎麼辦」。行首以 # 開頭的是註解,引擎跳過,不會參與匹配。

# 註解不會被匹配
DOMAIN,api.example.com,PROXY
DOMAIN-SUFFIX,example.com,DIRECT
IP-CIDR,10.0.0.0/8,DIRECT
GEOIP,CN,DIRECT
FINAL,PROXY

動作只有三種會真正改變出站:DIRECT(原路徑)、PROXY(目前高亮的記錄)、REJECT(攔截,不發出)。有的檔案會寫成策略組名,那是把動作指向另一套選擇邏輯;本頁只討論這三種直接動作。類型選錯、逗號寫少、把 URI 整段貼進規則行,這一行通常不會按你期望工作,請求會繼續往下掉。

同一檔案裡可以混用域名條件與地址條件。引擎對每一條連線只走一遍名單,不會「域名命中一次、IP 再命中一次然後取並集」。先命中的那一行已經給出動作,後面的行不再看。

03

更窄的條件寫在更寬的前面

Shadowrocket 設定檔 default.conf:Rule 289 條
設定檔裡的 Rule。規則只在策略為 Config 時被問到。截自 App Store,以你裝置上的版本為準。

匹配自上而下。某一行命中之後,後面的行不再看。所以例外必須寫在通例之前。把 FINAL 放在檔案中間,等於下面所有行作廢——它們還在檔案裡,但永遠不會被問到。

下面兩份名單的行完全相同,只是順序對調,結果相反。

# A:先點名主機,再寫後綴
DOMAIN,api.example.com,PROXY
DOMAIN-SUFFIX,example.com,DIRECT
FINAL,PROXY

# B:先寫後綴,api 那一行永遠走不到
DOMAIN-SUFFIX,example.com,DIRECT
DOMAIN,api.example.com,PROXY
FINAL,PROXY

api.example.com:名單 A 命中第一行,走記錄;名單 B 命中後綴,走直連。對 www.example.com:兩份都會在後綴處走直連。對 unrelated.org:兩份都落到 FINAL,走記錄。

KEYWORD 比 SUFFIX 更寬,GEOIP 比大多數域名行更寬,FINAL 最寬。一個穩妥的順序是:具體主機 → 後綴 / 關鍵字 → 網段 → GEOIP → FINAL。這不是語法要求,是為了讓例外有機會被看見。

04

域名條件:誰命中誰

DOMAIN
主機名必須完全相等。DOMAIN,example.com,PROXY 匹配 example.com,不匹配 www.example.com,也不匹配 api.example.com。需要管一個精確名字時用它,不要用它冒充後綴。
DOMAIN-SUFFIX
主機名等於該後綴,或以「.後綴」結尾。apple.com 匹配 apple.com 與 www.apple.com、icloud.com 不在其內。不匹配 notapple.com:後者只是以 apple.com 這串字元結尾,中間沒有那一個點。
DOMAIN-KEYWORD
主機名包含該字串即命中。KEYWORD,google 會打到 google.com,也會打到 googlevideo.com、以及任何名字裡夾了 google 的主機。範圍最大,也最容易誤傷。能改成後綴時不要用它。

後綴行不會自動覆蓋子域的子域以外的名字。寫了 DOMAIN-SUFFIX,google.com,PROXY 並不等於管到了 gstatic.com、youtube.com、googleapis.com。那些是不同的註冊域,要各自寫,或接受它們落到更後面的 GEOIP / FINAL。

大小寫按域名慣例視為不敏感。國際域名若以 Unicode 出現在規則裡、實際請求卻是 punycode(xn--),兩邊對不上,這一行不會命中。對照時以規則檔案裡實際寫下的位元組為準。

05

地址、埠、地理庫

IP-CIDR
按目的 IPv4 網段。例如 10.0.0.0/8172.16.0.0/12192.168.0.0/16 常被寫成 DIRECT,避免區域網進出隧道。只寫這一條時,IPv6 目的地址不會命中,請求繼續往下掉。
IP-CIDR6
IPv6 網段。需要同時處理兩種地址族時,兩條都要寫,或在設定裡關掉不使用的那一種。只補了 IPv4 的私網段、裝置卻用 IPv6 訪問同一服務,看起來會像「規則有時靈有時不靈」。
GEOIP
按目的 IP 在地理庫中的歸類,不是按域名註冊地,也不是按網站語言。GEOIP,CN,DIRECT 的意思是:這個目的地址被庫標成 CN 時走直連。庫版本在裝置上,網頁試紙模擬不了。CDN 把同一主機名指到不同國家的地址時,GEOIP 的結果會跟著變。
DEST-PORT
按目的埠。用來把某一類出口(例如只想讓 443 進記錄)從其他端口裡分開。它看不到主機名;和域名行寫在一起時,仍然是誰先命中誰說了算。
USER-AGENT
按用戶端宣告的 UA 字串。只有請求裡帶得出這項時才有意義。許多應用不走 HTTP 頭,或把 UA 留空,這一行對他們等於不存在。
FINAL
應放在最後。前面誰都沒命中時用這裡的動作。不要省略,也不要依賴「不寫就預設 PROXY」這種猜測。FINAL 寫成 PROXY 還是 DIRECT,決定了所有未被點名流量的命運,比再加一百條域名行更關鍵。

部分 IP 行帶 no-resolve 一類標註,表示不要為了匹配這一行去把域名解析成地址。解析策略還會和 DNS 頁裡的 DNS 設定互相影響:規則若只能看見主機名、看不見地址,地址行全部落空;只能看見地址、看不見主機名,則域名行全部落空。

06

三種動作不是「連上」的不同程度

把本該直連的系統服務寫成 PROXY,常見後果是推送、照片同步、商店更新變慢或失敗,因為它們被送進了一條並不為它們準備的出口。把本該進記錄的主機寫成 DIRECT,則 Data 只見 Direct 增加,網頁表現得像「沒開代理」。把主機寫成 REJECT,應用會報網路錯誤,容易被誤判成節點壞了。

改動作之前先確認這一行真的會被命中。改了一行從未被走到的規則,螢幕上不會有任何變化。用 Data 看計數站在哪一側,比反覆開關更有效。

07

規則看到的名字,不一定是位址列裡那串

域名條件依據解析結果、HTTP 的 Host、或 TLS 的 Server Name Indication。應用如果直接連線 IP、或不帶 SNI,規則可能只能看到地址,於是域名行全部落空,最後打在 IP-CIDR、GEOIP 或 FINAL 上。

這就是「同一網站,有時走代理有時走直連」的一種來源:有的請求帶主機名,有的只剩 IP。瀏覽器位址列顯示 www.example.com,底層卻可能先連 CDN 的地址;GEOIP 看到的是那臺 CDN 的所在庫,不是你對這個名字的印象。

系統 DNS 與應用內 DNS 各設一套且互相矛盾時,規則看到的主機名和真實目的地址會對不上。解析沒有異常時,不必同時改兩處。詳見 DNS

改寫 發生在請求已經被看見之後,不能代替規則決定走哪條路。先分流,再改寫。HTTPS 內容級的改寫還要額外的解密授權,見 系統

08

用同一份名單走讀三條請求

假設目前策略為 Config,檔案如下,目前高亮一條可用的記錄:

DOMAIN-SUFFIX,apple.com,DIRECT
DOMAIN-SUFFIX,icloud.com,DIRECT
DOMAIN-SUFFIX,github.com,PROXY
IP-CIDR,10.0.0.0/8,DIRECT
GEOIP,CN,DIRECT
FINAL,PROXY

www.apple.com

主機名以 .apple.com 結尾,命中第一行,DIRECT。不會問 github,也不會問 GEOIP。Data 應見 Direct 增加。這與 Apple 的伺服器在哪裡無關,只與這一行寫成了什麼有關。

github.com

前兩行不匹配,第三行命中,PROXY。GEOIP 寫在後面,即使這個地址被庫標成某個國家,也不會再被問到。Data 應見 Proxy 增加。若這裡 Proxy 不動、網頁失敗,先查記錄而不是再加一條 github 規則。

只知道目的地址 10.1.2.3,沒有主機名

所有域名行落空,命中 IP-CIDR,DIRECT。若同一臺服務有時用名字、有時用地址,你會看到兩條完全不同的動作。這不是引擎隨機,是兩次請求帶給引擎的資訊不一樣。

一個未被點名、目的 IP 被庫標成 CN 的主機

域名行都不中,網段不中,GEOIP,CN 命中,DIRECT。若庫把這個地址標成別的國家,則落到 FINAL,PROXY。換一次解析、換一個 CDN 節點,同一名字可以在這兩行之間跳動。

09

多份配置,外來列表

應用允許儲存多份配置,連線時只跑目前選中的一份。從一份切到另一份,等於整份名單替換,不是「兩條 FINAL 疊在一起」。匯入別人的列表之前,先找到它的 FINAL 和 GEOIP:這兩行決定了大多數未點名流量的命運。

若節點就在你現在這張網裡、只想讓少數主機走節點,FINAL 寫成 PROXY 會把大量無關請求送進節點。若節點在別的網路裡、希望未點名的都走節點,FINAL 寫成 DIRECT 則多數網站根本不會進節點。外來列表的作者按他自己的出口在寫,不是按你的出口在寫。

列表會過期。訂閱型規則的重新整理與伺服器容器的重新整理是不同物件上的同一類動作:儲存的是 URL,拉取成功才替換本地檔案,失敗時保留上一次成功的檔案,不會為了失敗把規則抹空。重新整理走當時的網路路徑,開關正連著一條已失效的記錄時,規則訂閱本身也可能重新整理失敗。見 物件Config

一次只改一個條件。改完用固定的一條記錄、只切換策略做對照,見 Data。同時換記錄、換檔案、改 FINAL,螢幕上的變化無法歸因。