部落格

兩份名單可以行完全相同,只是順序對調。

規則檔案不是集合,是一份有序名單。引擎對每一條連線只走一遍,先命中的一行給出動作,後面的行不再看。語法都寫對、順序寫反,結果可以完全相反。

何時才執行

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

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

可以寫得再完整的規則,只要策略停在 Proxy,出站仍一律進目前記錄;停在 Direct,則一律原路徑。檔案不會「自己開啟」。把策略改回 Config 之後,目前選中的那一份配置才開始逐條匹配。策略三項的含義見 隧道。因此「規則沒生效」有兩種完全不同的意思。一種是引擎根本沒被問到。另一種是引擎問到了,但先命中的那一行不是你以為的那一行。對照時先確認前者,再改後者。

配置檔案可以有多份。連線時只使用目前選中的一份。伺服器記錄是另一套物件:換高亮行不會換檔案,換檔案也不會換高亮行。一份配置裡寫了什麼名單,與列表裡此刻高亮哪一條出口,是兩件獨立的事。見 物件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 訪問同一服務,看起來會像規則「有時靈有時不靈」。見 DNS

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 看這一次位元組站在哪一側。首頁試紙只能輸入域名,模擬不了「只有地址、沒有名字」的請求,更模擬不了裝置上的地理庫版本。

行數多不等於例外被看見

在檔案末尾繼續追加域名行,擋不住已經寫在前面的寬條件。要讓某一主機走另一條路,必須把它寫到會截走它的那一行之前。順序錯了,再長的名單也只是一份被截短的名單。