漫談代理和 DNS 解析之二

在一個代理隨手可得的時代,開發者們究竟做了些什麼?

2017 年年初寫的漫谈 Shadowsocks 代理和 DNS 解析是迄今為止我博客閱讀量最高的一篇文章。

而兩年後的今天,各色各樣的付費服務商和跨平台客戶端像雨後春筍一樣,作為普通人只要跟隨商家教程,下載軟體,輸入信息,就能有接近置身牆外的體驗。在一個代理隨手可得的時代,開發者們究竟做了些什麼?


回到最根本的問題,我們希望訪問一台或一系列1服務器時,選擇適合的路由2方式。怎麼定義適合呢?對於大多數人,首要是能訪問所有網站。其次,對於既可以直接連接,又可以通過代理連接的網站,對於備份、視頻和下載為目的的請求,選擇數據成本低的方式;對於一般瀏覽和遊戲,選擇丟包低、時延低的方式。總之,為了優化訪問體驗,對發出的請求進行分流是一件十分必要的事情。

同時,由於本地 DNS 解析污染和基本 DNS 的 CDN 部署,我們不得不進行本地 DNS 解析和遠程 NDS 解析。

最初可以依據 IP 和域名進行分流的實現是 proxy auto-config (PAC),最初 PAC 是為瀏覽器設計的,但目前主流作業系統均有支持。PAC 實則是一個如 function FindProxyForURL(url, host) 的 JavaScript Function ,它的返回值是一個策略,可以為一個 HTTP Proxy 或 DIRECT3mono_pac 就是一個生成基於 IP 和域名分流的 PAC 的 Python 腳本。但 PAC 也並非一個完美的方式,首先,編輯分流策略需要懂得 JavaScript 語法,因此 PAC 還是常見分流方式的時候,網路上有很多生成 PAC 策略的開源項目。其次,有不少軟體不兼容 PAC,例如說自行實現了 HTTP 協議,或者是基於 TCP 實現的非 HTTP 協議,或者是 UDP 協議。這個問題常見於非瀏覽器程式,這類程式要麼只支持設置一個代理而不能實現分流,有的甚至連代理都不支持設置。

這個時候 shadowsocks-libev 正處於 2.5.1 版本,正式支持了 access control list (ACL) 功能,在 3.0.6 版本 ss-local 的內置分流功能在這個 commit 之後就如常工作了,這裡是一些使用例子。ACL 功能使得 shadowsocks-libev 能對接受的請求按照一定的規則進行直連或代理。內置分流是如何實現的呢?實際上,shadowsocks-libev 還內置了一個異步 DNS client,並允許用戶將系統的 SOCKS5 代理設置為 shadowsocks-libev。這樣,shadowsocks-libev 便幾乎全局接管了系統流量4,當 shadowsocks-libev 接收到一個域名時候,首先會對 ACL 列表中的條目進行正則匹配,如果沒有結果,則利用自帶的 DNS client 進行解析,得到 IP 之後再次匹配。就這樣,shadowsocks-libev 把本來在瀏覽器解析 PAC 的功能移動到 shadowsocks-libev 內部,解決了 PAC 分流在不同應用間行為不一致的問題。更重要的是,當域名解析為 IP,並且根據 ACL 規則觸發代理時,shadowsocks-libev 會將域名,而不是 IP 交給 ss-server,就這樣優雅地解決了 DNS 污染和 CDN 解析不是最優的問題。

分流代理在內建 DNS Client 這件事上,shadowsocks-libev 並不是唯一一個,V2Ray 和 Surge 還有 Clash5 均是內建了 DNS Client。特別是 Clash 支持部署在 Linux 系統,利用系統的 Netfilter 功能實現對 OUTPUT 鏈和 NAT 鏈上的 TCP 包進行代理,讓我們在 OpenWrt 路由器部署代理又多了一種新選擇。

在這件事上開發者費盡苦心,可是在異國他鄉這本應該不是一個煩惱。


  1. 一個域名的解析有多個記錄是很常見的情況。 

  2. 廣義上的路由,因為我們常說的 SOCKS 代理是基於 TCP / UDP 協議的,並沒有(也不能)改變 IP 包的路由。 

  3. Proxy Auto-Configuration (PAC) file - HTTP | MDN 

  4. 通常地,系統設置的 SOCKS / HTTP 代理對程式來說只是「一個建議」,應用程式可以選擇不遵循系統代理。 

  5. 截至目前為止,Clash 的內建 DNS 仍在開發中。