Surge 中文白皮书 (草稿第三部分,转发,代理和规则系统)
--
请求在处理完毕后将被转发。如果 Surge 的出站模式设置为直接连接,那么该请求将被直接发往目标服务器,如果出站模式设置为全局代理,那么将转发给代理服务器。
当出站模式设置被设置为规则判定时,将根据配置的规则决定转发策略。
规则系统
规则系统中有两个基本概念:策略和规则
- 策略:描述了 Surge 进行转发的方式,有三种类别:
- 内置策略:DIRECT、REJECT、REEJCT-TINYGIF、REJECT-DROP
- 代理策略:每个策略对应一个代理服务
- 策略组:根据一定的规则从子策略中选择一个最终策略。
- select 策略组:通过 UI 菜单选择一个策略。
- url-test 策略组:选择延迟最低的策略。
- fallback 策略组:选择可用的策略中,最靠前的策略。
- ssid 策略组:根据当前的 Wi-Fi SSID 选择一个策略。
- load-balance 策略组:随机使用一个子策略,可选进行可用性检查。
- 规则:规则由四个部分组成:类型、条件、策略和参数。当条件满足时,该规则匹配,使用该规则指定的策略。
策略
下面具体说明各种策略的用途
内置策略
内置策略由 Surge 提供,不随配置而变化:
- DIRECT:将该请求直接发往目标服务器
- REJECT:拒绝该请求,当连接类型为 HTTP 时,会返回一个错误页面。(该行为可被 show-error-page-for-reject 参数控制)
- REEJCT-TINYGIF:拒绝该请求,当连接类型为 HTTP 时,返回一个 1px 的 GIF 图片响应。若为其他类型连接则直接断开。该策略主要用于 Web 广告屏蔽。
- REJECT-DROP:拒绝该请求,与 REJECT 不同的是,该策略将静默抛弃请求。因为部分程序有着十分暴力的重试逻辑,连接失败后会立刻进行重试,导致请求风暴。
由于操作系统对用户空间程序(user-space program)的 socket 并没有提供抛弃的操作,Surge 静默抛弃的实现方式是将该 socket 闲置一段时间后再关闭。
同时,如果发往某主机名的请求短时间内大量触发 REJECT/REEJCT-TINYGIF 策略(当前版本的阈值为 30 秒内 10 次),为了避免产生大量资源浪费,Surge 将自动升级策略为 REJECT-DROP 策略。
代理策略
代理策略由用户自己定义,每个策略描述了一个代理服务,当使用该策略时即为通过该代理服务转发请求。
一个简单的代理策略定义行如下:
ProxyA = http, 11.22.33.44, 8080, username=user, password=pass
其中,ProxyA 为策略名,供规则和策略组所使用。第一个参数为代理协议类型,目前 Surge 支持的代理协议类型有:http,https,socks5,socks5-tls,ss,snell,vmess,trojan,以及两个特殊类型 external 和 direct。 第二个参数为代理服务器主机名,第三个参数为代理服务器端口号,后续为 key=value 的参数表,根据协议类型不同需要提供不同的参数。
不同的代理协议类型有其特定的参数,有些参数是所有代理策略通用的,这里单独讲解几个参数,完整参数表请参见手册:
- tfo:开启 TCP Fast Open,TFO 可以使 TCP 握手时顺带传递第一个数据包,以此降低代理协议握手的时间开销。但是由于这是 2014 年才引入的新特性,如果和目标服务器之前的设备有任何一个不支持该特性,则会导致异常,目前观察到大多数 ISP 的网络都有异常产生的机率,所以除非是内网代理服务器,否则不推荐开启。
- underlying-proxy:通过另一个代理使用该代理,该功能在其他软件中可能被称为代理链(Proxy chain)。(目前仅在 iOS beta 版本可用)
- interface:强制指定该代理使用某一网卡进行连接
- allow-other-interface:当 interface 参数指定的网卡不存在时,是否允许使用默认网卡进行连接。如果为 false 且指定的网卡不存在,连接将直接断开。
- always-use-connect:http 类型代理专用参数,如上一章所描述,当使用 HTTP 代理转发 HTTP 请求时,是按照会话制进行转发的,如果 always-use-connect=true,将会把远端 HTTP 代理当做一个标准的 TCP 代理使用。
direct 类型 (仅限 Surge Mac)
这是一个特别的类型,严格来说并不是一个代理,用于强制使用某一个网卡进行请求。
PolicyName = direct, interface=en2, allow-other-interface=false
请注意,由于 Darwin 内核的限制,使用的网卡必须拥有连接的目标主机的路由表条目,否则无法使用该网卡。
举例说明,比如同时连接了有线网络和无线网路,通过 netstat -rn 命令我们可以看到两个网卡 en0 和 en1 都拥有 default 的全覆盖路由表条目,只是优先级不同。
Internet: Destination Gateway Flags Netif Expire default 192.168.20.1 UGSc en0 default 192.168.20.1 UGScI en1
这种情况下可以使用 direct 策略自由选择 en0 或者 en1 作为出口。
配合 VPN 使用
某些 VPN 连接后,会加入一个很小的内网路由表,由于覆盖小优先级高,可用于访问特定内网资源。在未开启增强模式时,如果出口策略是 DIRECT,Surge 也会按照这个行为进行本地转发。
但如果开启了增强模式,由于 Surge 的 VIF 已配置为默认路由,为了能将连接从本地物理网卡发出,出口连接将会强制绑定原先的默认网卡,无视路由表。这会导致 VPN 的内网路由表失效,该情况下可使用 direct 类型策略强制绑定 VPN 的 utun 设备解决。
[Proxy] CorpVPN = direct, interface=utun1, allow-other-interface=true
[Rule] DOMAIN-SUFFIX,internal.corp.com,CorpVPN
external 类型 (仅限 Surge Mac)
external 类型策略可以让 Surge 与其他代理客户端更方便的协同工作。
该功能目前只能通过直接编辑配置实现,策略定义行为: External = external, exec = “/usr/local/bin/local”, args = “-c”, args = “/usr/local/etc/config.json”, local-port = 1080, addresses = 11.22.33.44
其中 args 和 addresses 参数为选填,其他必填。args 和 addresses 字段可以反复使用进行追加。
当使用到该策略时 Surge 会进行以下工作:
- 使用 exec 和 args 参数启动该外部程序,之后向 SOCKS5 127.0.0.1:[local-port] 转发请求。
- 如果外部进程被终止,当再次使用该策略时会自动进行重启。
- Surge 会在启动增强模式时自动将 addresses 参数中的地址排除在 VIF 路由表外。(请在该字段填写使用的代理服务器 IP 地址)
- 当由 Surge 启动的外部进程的请求被 Surge VIF 处理时,永远使用 DIRECT 策略。(为了应对像 obfs-local 这样的插件请求问题,外部进程的子进程也会被同样处理) Surge 退出时会自动关闭所有外部进程,增强模式关闭时会自动清理加入的路由表。
上述 3 和 4 的功能是有重叠的,请尽量使用 addresses 声明使用到的地址以排除 TUN 处理,这样可以减少系统开销,4 的功能是一重额外保护。
策略组
Surge 提供多种不同类型的策略组以满足各种场景的不同需求,在具体讲解各种策略组前,需要先了解连通性测试。
连通性测试
Surge 的多个功能会用到连通性测试,测试方式有 3 种
- ICMP Ping 测试:简单的 Ping 测试,用于反映当前物理网络状况。
Mac 版本首页卡片和网络诊断中的路由延迟为该测试结果。
- DNS 查询测试:向所有 DNS 服务器并行查询 bing.com 域名的 A 记录,结果为收到响应的最短时间,用于反映当前物理网络状况,同时简单确认具有 Internet 访问。
Mac 版本首页卡片和网络诊断中的 DNS 延迟,Mac 版本主菜单和 iOS 版本通知中心插件的连通性测试延迟为该测试结果。
- HTTP 测试:向目标 HTTP 服务器发出 HEAD 请求,计算收到响应头的时间,任意响应数据包均判定为有效。测试地址可自定义,建议选择在全球都有节点的 URL。
Mac 版本首页卡片的 Internet 和代理延迟,策略组的判断基准,网络诊断的代理测试为该测试结果。
策略组使用方法 3 作为判断基准而非方法 1 是因为:
- 代理服务器可能有中转,Ping 测试只能表示到达中转服务器的延迟。
- 除了与代理服务器间的连通性,代理服务器的 DNS 和出口网络情况也应该进行考量。
- 某些代理协议因设计欠考虑,会引入额外的延迟开销,如 SOCKS5,也应当被考量。
url-test 策略组
并发测试所有子策略,选择延迟最低的策略。有以下几个参数
- url:用于测试的 URL。
- timeout:测试的最长等待时间,超过该时间的策略将标记为失败不再继续等待。
- interval:每次测试的间隔时间。所有类 url-test 组的测试时机为:
- 首次使用时进行测试。
- 后续使用该策略组时,如果上次测试的时间间隔已大于 interval 设置时间,则再次触发测试。
- 当目前选中策略产生不可恢复性错误时,直接触发测试。
- 网络切换后,将清理之前的测试结果,当策略组被使用时触发首次测试。
- tolerance:容忍度,如果某几个策略测试结果相差不大,那么会导致在这几个策略中频繁切换,如果策略的代理服务器的出口 IP 不同,可能会触发目标网站的风险控制。所以加入了容忍度设计,仅当新一次的测试结果中,最佳策略比原选中策略的延迟差大于容忍度时,才会切换至新的策略。
- evaluate-before-use:默认情况下,在首次使用策略组时将直接使用子策略中的第一个策略,同时触发延迟测试。如果配置了 evaluate-before-use=true,那么首次使用时将等待测试完毕后选择最佳策略。
fallback 策略组
与 url-test 组基本一致,区别是只关心子策略是否可用而不关心具体延迟,然后从可用策略中选择靠前的策略。可以通过调小 timeout 参数将缓慢线路也标记为不可用。该类型没有 tolerance 参数。
load-balance 策略组
负载均衡组,随机从子策略中选取一个策略使用。
当配置了 url 参数时,会按照 fallback 组的行为进行可用性检查,然后仅从可用的子策略中随机选取。
除 url、timeout、interval外,还有一个参数:
- persistent:当 persistent=true 时,对于同一目标主机名,将尽量使用同一个策略。避免因出口 IP 不同而触发目标网站的风险控制。但当可用性改变时可能导致策略变化。
ssid 策略组
虽然名字依然是 SSID 策略组,但是功能已经扩展,可根据当前网络的 SSID、BSSID、路由 IP 地址等因素选择子策略。iOS 版本还可以为数据网络指定策略。
规则
Surge 使用规则系统来对选择每个连接的出口策略。规则的匹配方式为自上而下,逐一测试。最末尾规则一定是一个 FINAL 规则,当所有规则都不匹配时使用。
域名规则
当连接的目标主机名符合时,匹配该规则。
- DOMAIN:严格匹配某域名。
- DOMAIN-SUFFIX:匹配某域名及其子域名,如 DOMAIN-SUFFIX,apple.com 可以匹配 apple.com 和 www.apple.com,但是不会匹配 anapple.com。
- DOMAIN-KEYWORD:简单的字符串搜索,只要域名包含子串就会匹配。
- DOMAIN-SET:专为大量域名集列表文件设计,支持上万条记录的快速查询。文件中每行为一个域名,如果某行以 . 开头则表示匹配所有子域名和该域名本身。可用于广告过滤。
域名和主机名
域名其实是主机名的一种形式,Surge 内部对域名和主机名并没有区分,所有文档所提到的域名和主机名所对应的处理逻辑都是一样的。
比如,DOMAIN,1.2.3.4 规则其实也可以用于匹配目标主机为 IP 地址 1.2.3.4 的连接。DOMAIN,MacBook.local 也可用于匹配 Bonjour 主机名。
IP 地址规则
当连接的目标主机的 IP 地址符合时,匹配该规则。包含 IP-CIDR,IP-CIDR6,GEOIP 三种类型。
当目标主机名是一个域名或主机名时,IP 类型规则会触发本地 DNS 解析。根据解析得到的 IP 地址进行判断。当解析失败是:
- 如果最终的 FINAL 规则带有 dns-failed 标记,那么将直接匹配 FINAL 规则。
- 如果 FINAL 规则不带有 dns-failed 标记,该请求将直接失败。
IP 类型规则有一个专有的参数 no-resolve,如果一个 IP 规则带有该参数,那么
- 如果目标主机名是一个域名,那么将跳过该规则,不触发 DNS 解析。
- 如果目标主机名是 IP 地址,按规则进行判断。
- 如果目标主机名是一个域名,且先前出现的 IP 规则已经触发了 DNS 解析获得了 IP 地址,那么使用该 IP 地址进行判断。
由于 DNS 查询有时间开销,所以在配置规则时,最优的方式是尽量先不触发 DNS 解析,将所有会触发 DNS 解析的规则放在底部。这样应使用代理策略的请求就完全避免了在本地进行 DNS 解析。
但是也不用刻意的去完全避免解析,因为一旦决定使用 DIRECT 策略,那么最终还是要进行解析。Surge 有完备的 DNS 缓存系统,不必在意短时间内的重复解析。
不过需要注意,如果某目标主机在本地 DNS 不可被解析,那么一定需要加入对应的规则,在触发 DNS 前就决定策略终止匹配。或者对 FINAL 规则加上 dns-failed 标记并使用代理策略。
HTTP 相关规则
仅对 HTTP 请求有效的规则,包含 URL-REGEX 和 USER-AGENT。
比较特殊的是,只有由于只有进行 MITM 解密后才可获取到 URL,所以 URL-REGEX 对未解密的 HTTPS 连接无效。但是 USER-AGENT 规则却对未解密的 HTTPS 也连接有效,因为程序在使用 HTTP 代理时,会在发送 CONNECT 请求时带上自己 User Agent 的明文。
其他规则
- PROCESS-NAME:仅对 Mac 版本有效,可以匹配程序名。
- SRC-IP:可匹配连接来源 IP 地址,接管其他设备连接时可使用。
- IN-PORT:Mac 版本支持多端口监听,可为不同监听端口配置特定的规则。
- DEST-PORT:可匹配目标主机的端口号。
- PROTOCOL:可根据连接的协议进行匹配,取值范围是 HTTP,HTTPS,TCP,UDP。(虽然逻辑关系上 HTTP 和 HTTPS 都是 TCP 的一种特殊形式,但是该规则将按照前一章的分类区别对待。)
规则集
RULE-SET 规则集可以将多个子规则放在一个单独的文件中,便于分享和复用。但是规则集中的规则不可以指定策略,整个规则集指向一个同一个策略。
另外 Surge 自带了 SYSTEM 和 LAN 两个规则集,规则集包含的具体子规则会随 Surge 更新而有所调整。注意 LAN 规则集会触发 DNS 解析。
RULE-SET 和 DOMAIN-SET 的不同
RULE-SET 可包含所有类型的子规则,执行效率和在主配置中的规则没有区别,而 DOMAIN-SET 仅可使用 DOMAIN 和 DOMAIN-SUFFIX 两种形式的内容,使用了特别的逻辑进行优化,在内容非常多时性能有极大的提升。(千条以上,否则两者没有太大的区别)
逻辑规则
可通过 AND,OR,NOT 运算对所有规则类型进行组合使用。如
AND,((PROCESS-NAME,Google Chrome),(PROTOCOL,UDP)),REJECT
可以拦截 Chrome 发出的 UDP 数据包。