Surge - 转发,策略和规则

规则系统中有两个基本概念:策略和规则

Posted by maicoo on May 7, 2021

请求在处理完毕后将被转发。如果 Surge 的出站模式设置为直接连接,那么该请求将被直接发往目标服务器,如果出站模式设置为全局代理,那么将转发给代理服务器。

当出站模式设置被设置为规则判定时,将根据配置的规则决定转发策略。

策略

策略:描述了 Surge 进行转发的方式,有三种类别:

  • 内置策略:DIRECT、REJECT、REJECT-TINYGIF、REJECT-DROP
  • 代理策略:每个策略对应一个代理服务
  • 策略组:根据一定的规则从子策略中选择一个最终策略。

内置策略

内置策略由 Surge 提供,不随配置而变化:

  • DIRECT:将该请求直接发往目标服务器
  • REJECT:拒绝该请求,当连接类型为 HTTP 时,会返回一个错误页面。(该行为可被 show-error-page-for-reject 参数控制)
  • REJECT-TINYGIF:拒绝该请求,当连接类型为 HTTP 时,返回一个 1px 的 GIF 图片响应。若为其他类型连接则直接断开。该策略主要用于 Web 广告屏蔽。
  • REJECT-DROP:拒绝该请求,与 REJECT 不同的是,该策略将静默抛弃请求。因为部分程序有着十分暴力的重试逻辑,连接失败后会立刻进行重试,导致请求风暴。

由于操作系统对用户空间程序(user-space program)的 socket 并没有提供抛弃的操作,Surge 静默抛弃的实现方式是将该 socket 闲置一段时间后再关闭。

同时,如果发往某主机名的请求短时间内大量触发 REJECT/REJECT-TINYGIF 策略(当前版本的阈值为 30 秒内 10 次),为了避免产生大量资源浪费,Surge 将自动升级策略为 REJECT-DROP 策略。

代理策略

代理策略由用户自己定义,每个策略描述了一个代理服务,当使用该策略时即为通过该代理服务转发请求。

一个简单的代理策略定义行如下:

1
2
[Proxy]
ProxyA = http, 11.22.33.44, 8080, username=user, password=pass

目前大多数用户都是用机场订阅,这里也就放个内置策略自定义别名(栗子如下)或者自建玩家放置服务器节点。

1
2
3
4
5
6
7
8
9
10
11
[Proxy]
# 当请求为 HTTP 请求时返回一个极小的 GIF
REJECT-TINYGIF = reject-tinygif
# 阻止请求
REJECT = reject
# 直接连接
DIRECT = direct

[Rule]
IP-CIDR,0.0.0.0/32,REJECT,no-resolve
OR,((RULE-SET,LAN), (GEOIP,CN)),DIRECT

策略组

Surge 提供多种不同类型的策略组以满足各种场景的不同需求,在具体讲解各种策略组前,需要先了解连通性测试。

连通性测试

Surge 的多个功能会用到连通性测试,测试方式有 3 种

  1. ICMP Ping 测试:简单的 Ping 测试,用于反映当前物理网络状况。Mac 版本首页卡片和网络诊断中的路由延迟为该测试结果。

  2. DNS 查询测试:向所有 DNS 服务器并行查询 bing.com 域名的 A 记录,结果为收到响应的最短时间,用于反映当前物理网络状况,同时简单确认具有 Internet 访问。Mac 版本首页卡片和网络诊断中的 DNS 延迟,Mac 版本主菜单和 iOS 版本通知中心插件的连通性测试延迟为该测试结果。

  3. HTTP 测试:向目标 HTTP 服务器发出 HEAD 请求,计算收到响应头的时间,任意响应数据包均判定为有效。测试地址可自定义,建议选择在全球都有节点的 URL。Mac 版本首页卡片的 Internet 和代理延迟,策略组的判断基准,网络诊断的代理测试为该测试结果。

策略组使用方法 3 作为判断基准而非方法 1 是因为:

  • 代理服务器可能有中转,Ping 测试只能表示到达中转服务器的延迟。
  • 除了与代理服务器间的连通性,代理服务器的 DNS 和出口网络情况也应该进行考量。
  • 某些代理协议因设计欠考虑,会引入额外的延迟开销,如 SOCKS5,也应当被考量。

一个策略组可以包括多个子策略

子策略可以是一个代理策略,或者另一个策略组,或者是一个内置策略 (DIRECT 或 REJECT)。

有 5 种策略组类型: select, url-test, fallback, ssidload-balance

select 策略组

通过 UI 菜单选择一个策略,举个栗子:

1
2
3
4
5
6
7
[Proxy Group]
Proxy🪁 = select, 🇺🇸 America, 🇭🇰 Load balance
🇺🇸 America = url-test, policy-path=https://sub.store/download/STT, interval=600, tolerance=100, evaluate-before-use=true
🇭🇰 Load balance = load-balance, policy-path=https://sub.store/download/STT, interval=600, tolerance=100, persistent=true

[Rule]
FINAL,Proxy🪁

如上栗子表示这个配置有两条外部代理服务器链接,策略组 Proxy🪁可以在两者之间通过 UI 手动选择;

默认规则 FINAL 随策略组 Proxy🪁 的选择而被约束。

url-test 策略组

并发测试所有子策略,选择延迟最低的策略。有以下几个参数

  • url:用于测试的 URL(该参数新版 Surge 已无需在策略组中添加)。
  • timeout:可选, 秒 (默认值: 5s),测试的最长等待时间。如果某策略在该时间后依然没有完成,放弃该策略。
  • interval:可选, 秒 (默认值: 600s) 每次测试的间隔时间。指定在多长时间后,上次的测试结果将被抛弃。所有类 url-test 组的测试时机为:
  • 首次使用时进行测试。
  • 后续使用该策略组时,如果上次测试的时间间隔已大于 interval 设置时间,则再次触发测试。
  • 当目前选中策略产生不可恢复性错误时,直接触发测试。
  • 网络切换后,将清理之前的测试结果,当策略组被使用时触发首次测试。
  • tolerance:可选, 毫秒 (默认值: 100ms) 容忍度,如果某几个策略测试结果相差不大,那么会导致在这几个策略中频繁切换,如果策略的代理服务器的出口 IP 不同,可能会触发目标网站的风险控制。所以加入了容忍度设计,仅当新一次的测试结果中,最佳策略比原选中策略的延迟差大于容忍度时,才会切换至新的策略。
  • evaluate-before-use:默认情况下,在首次使用策略组时将直接使用子策略中的第一个策略,同时触发延迟测试。如果配置了 evaluate-before-use=true,那么首次使用时将等待测试完毕后选择最佳策略。
1
2
3
4
[Proxy Group]
🇺🇸 America = url-test, policy-path=https://sub.store/download/STT, interval=600, tolerance=100, evaluate-before-use=true

[Rule]

Surge 新版本 URL 测试使用全局测试,在设置中添加测试 URL 即可,策略组中无需单独加入。

fallback 策略组

与 url-test 组基本一致,区别是只关心子策略是否可用而不关心具体延迟,然后从可用策略中选择靠前的策略。可以通过调小 timeout 参数将缓慢线路也标记为不可用。该类型没有 tolerance 参数。

1
2
3
4
5
[Proxy Group]
# 调小 timeout 参数
🇨🇳 Fallback = fallback, policy-path=https://sub.store/download/STT, timeout=3, interval=600, evaluate-before-use=true
[Rule]

load-balance 策略组

负载均衡组,随机从子策略中选取一个策略使用。

使用 [General] url 参数时,按照 fallback 组的行为进行可用性检查,然后仅从可用的子策略中随机选取。

除 url、timeout、interval外,还有一个参数:

persistent:当 persistent=true 时,对于同一目标主机名,将尽量使用同一个策略。避免因出口 IP 不同而触发目标网站的风险控制。但当可用性改变时可能导致策略变化。

1
2
3
4
[Proxy Group]
# 配置 persistent=true 参数,避免 IP 跳动引发风险控制
🇸🇬 Singapore = load-balance, policy-path=https://sub.store/download/STT, interval=600, tolerance=100, persistent=true
[Rule]

ssid 策略组

虽然名字依然是 SSID 策略组,但是功能已经扩展,可根据当前网络的 SSID、BSSID、路由 IP 地址等因素选择子策略。iOS 版本还可以为数据网络指定策略。

1
2
3
4
5
6
[Proxy Group]
Adblock = ssid, default = REJECT, "SSID Here" = DIRECT

[Rule]
RULE-SET,https://www.example.com/reject.list,Adblock
FINAL,DIRECT

如上示例,规则中添加了一条被拒绝规则集,该规则集没有直接指定内置 REJECT 策略,而是添加了一个 Adblock 的 SSID 策略组,表示在默认情况下它是一个 REJECT 策略,但在 SSID 为 “SSID Here” 的网络下使用直连策略。

策略组其它参数(隐藏、禁用通知、过滤器)

1
2
3
4
5
[Proxy Group]
# 参数 hidden=true 隐藏该策略组,在 UI 界面将不被展示
# 参数 no-alert=true 禁用策略组变化通知
# 参数 policy-regex-filter=台湾 节点过滤器,表示这条订阅中筛选出台湾节点
🇨🇳 Fallback = fallback, policy-path=https://sub.store/download/STT, policy-regex-filter=台湾, timeout=3, interval=600, hidden=true, no-alert=true

规则

规则由四个部分组成:类型、条件、策略和参数。当条件满足时,该规则匹配,使用该规则指定的策略。

Surge 使用规则系统来对选择每个连接的出口策略。规则的匹配方式为自上而下,逐一测试。最末尾规则一定是一个 FINAL 规则,当所有规则都不匹配时使用。

域名规则

当连接的目标主机名符合时,匹配该规则。

  • DOMAIN:严格匹配某域名。
  • DOMAIN-SUFFIX:匹配某域名及其子域名,如 DOMAIN-SUFFIX,apple.com 可以匹配 apple.com 和 www.apple.com,但是不会匹配 anapple.com。
  • DOMAIN-KEYWORD:简单的字符串搜索,只要域名包含子串就会匹配。
  • DOMAIN-SET:专为大量域名集列表文件设计,支持上万条记录的快速查询。文件中每行为一个域名,如果某行以 . 开头则表示匹配所有子域名和该域名本身。可用于广告过滤。

IP 地址规则 IP-CIDR,IP-CIDR6,GEOIP 三种类型

当连接的目标主机的 IP 地址符合时,匹配该规则。

当目标主机名是一个域名或主机名时,IP 类型规则会触发本地 DNS 解析。根据解析得到的 IP 地址进行判断。当解析失败时: 如果最终的 FINAL 规则带有 dns-failed 标记,那么将直接匹配 FINAL 规则。如果 FINAL 规则不带有 dns-failed 标记,该请求将直接失败。

IP 类型规则有一个专有的参数 no-resolve,如果一个 IP 规则带有该参数,那么 1. 如果目标主机名是一个域名,那么将跳过该规则,不触发 DNS 解析。 2. 如果目标主机名是 IP 地址,按规则进行判断。 3. 如果目标主机名是一个域名,且先前出现的 IP 规则已经触发了 DNS 解析获得了 IP 地址,那么使用该 IP 地址进行判断。

由于 DNS 查询有时间开销,所以在配置规则时,最优的方式是尽量先不触发 DNS 解析,将所有会触发 DNS 解析的规则放在底部。这样应使用代理策略的请求就完全避免了在本地进行 DNS 解析。

但是也不用刻意的去完全避免解析,因为一旦决定使用 DIRECT 策略,那么最终还是要进行解析。Surge 有完备的 DNS 缓存系统,不必在意短时间内的重复解析。

不过需要注意,如果某目标主机在本地 DNS 不可被解析,那么一定需要加入对应的规则,在触发 DNS 前就决定策略终止匹配。或者对 FINAL 规则加上 dns-failed 标记并使用代理策略。

HTTP 规则 URL-REGEX 和 USER-AGENT

比较特殊的是,只有由于只有进行 MITM 解密后才可获取到 URL,所以 URL-REGEX 对未解密的 HTTPS 连接无效。但是 USER-AGENT 规则却对未解密的 HTTPS 也连接有效,因为程序在使用 HTTP 代理时,会在发送 CONNECT 请求时带上自己 User Agent 的明文。

1
2
3
4
[Rule]
# 微信的 USER-AGENT
USER-AGENT,MicroMessenger Client,DIRECT
USER-AGENT,WeChat*,DIRECT

其他规则

  • PROCESS-NAME仅对 Mac 版本有效,可以匹配程序名。
  • SRC-IP仅对 Mac 版本有效,可匹配连接来源 IP 地址,接管其他设备连接时可使用。
  • IN-PORT仅对 Mac 版本有效,Mac 版本支持多端口监听,可为不同监听端口配置特定的规则。
  • DEST-PORT:可匹配目标主机的端口号,当目标主机端口号一致时匹配。
  • PROTOCOL:可根据连接的协议进行匹配,取值范围是 HTTP,HTTPS,TCP,UDP。(虽然逻辑关系上 HTTP 和 HTTPS 都是 TCP 的一种特殊形式,但是该规则将按照前一章的分类区别对待。)
  • SCRIPT:可以使用 JavaScript 根据各种参数完全自由的选择策略。
  • SUBNET:当 Wi-Fi SSID/BSSID、路由器 IP 地址匹配时匹配。

蜂窝网络类规则 CELLULAR-CARRIER 和 CELLULAR-RADIO

  • CELLULAR-CARRIER:当数据网络的 MCC-MNC 编码一致时匹配,编码必须以 xxx-xx 的格式输入,当使用 Wi-Fi 网络时该规则无效。
  • CELLULAR-RADIO:当数据网络的无线电接入符合时匹配,可选参数 GPRS, Edge, WCDMA, HSUPA, CDMA1x, CDMAEVDORev0, CDMAEVDORevA, CDMAEVDORevB, eHRPD, HRPD, LTE, NRNSA, NR, 当使用 Wi-Fi 网络时该规则无效。

Surge 查看当前蜂窝网络 CELLULAR-CARRIER 和 CELLULAR-RADIO 方法:在脚本编辑器输入如下字符,然后点击执行,即可看到日志记录。

1
2
console.log($network)
$done()

规则集

RULE-SET 规则集可以将多个子规则放在一个单独的文件中,便于分享和复用。但是规则集中的规则不可以指定策略,整个规则集指向一个同一个策略。

另外 Surge 自带了 SYSTEM 和 LAN 两个规则集,规则集包含的具体子规则会随 Surge 更新而有所调整。注意 LAN 规则集会触发 DNS 解析。

1
2
3
[Rule]
RULE-SET,SYSTEM,DIRECT
RULE-SET,LAN,DIRECT

RULE-SET 和 DOMAIN-SET 的不同

RULE-SET 可包含所有类型的子规则,执行效率和在主配置中的规则没有区别,而 DOMAIN-SET 仅可使用 DOMAIN 和 DOMAIN-SUFFIX 两种形式的内容,使用了特别的逻辑进行优化,在内容非常多时性能有极大的提升。(千条以上,否则两者没有太大的区别)

逻辑规则

可通过 AND,OR,NOT 运算对所有规则类型进行组合使用。如

1
2
3
[Rule]
# 可以拦截 Chrome 发出的 UDP 数据包
AND,((PROCESS-NAME,Google Chrome),(PROTOCOL,UDP)),REJECT

详见