智能代理自动分流实现方案

通常,要实现代理的自动分流,那么就需要有一个判断规则,根据这个规则的结果,来决定流量的走向,是走代理还是直连。常用的判断规则就两种:

  • 域名
  • IP 针对流量的走向不同,又分两种方式:
  • 黑名单: 符合规则走代理,不符合的规则走直连;
  • 白名单: 符合规则走直连,不符合的规则走代理。

关于选择可以参考:

VPS 的连接速度很快,那么可以选择白名单。如果 VPS 连接速度很慢,可以选择黑名单。

1. 规则列表

1.1. 域名列表

常用的域名列表有:

其中,gfwList 是一个被 GFW 屏蔽的域名列表,这些域名都需要通过代理才能访问。白名单中的域名可以直接访问,其余的都会走代理。

gfwlist.txt 文件被 base64 编码,解码后是 ABP 格式: https://adblockplus.org/filter-cheatsheet

1.1.1. ABP 语法

“!” 为行注释符
注释行以该符号起始作为一行注释语义,用于规则描述。

“|” 为管线符号
来表示地址的最前端或最末端 比如 “|http://“ 或 |http://www.abc.com/a.js| 用于精确控制匹配的开始或结束。e.g:|http://www.abc.com 等于 |http://www.abc.com* , 可以匹配以 http://www.abc.com 开头的网址。

“||” 为子域通配符
方便匹配主域名下的所有子域。比如 “||www.baidu.com" 就可以不要前面的协议”http://“。e.g: ||www.abc.com 等于 www.abc.com , 只要网址中包含 www.abc.com 就可以被匹配。

“~” 为排除标识符
通配符能过滤大多数广告,但同时存在误杀, 可以通过排除标识符修正误杀链接。

“@@” 网址白名单
例如不拦截此条地址 @@|http://www.baidu.com/js/u.js或者 @@||www.baidu.com/js/u.js

“*” 为字符通配符
能够匹配0长度或任意长度的字符串。

“^” 为分隔符
可以匹配任何单个字符。

1.2. IP 列表

基本都是基于 MaxMind,IPIP.NET 等第三方的数据来生成的数据,都需要定时更新。

2. 实现方式

通常不会直接使用 gfwlist.txt 文件,会通过外部工具将 gfwlist.txt 和 user-url.txt(用户自定义配置满足 ABP 语法规则的文件) 转换成 PAC 或者 ACL 格式的文件后再使用。

2.1. PAC

PAC(Proxy auto-config),用于定义浏览器该如何自动选择适当的代理服务器来访问一个网址。本质是一个 js 文件,包含了 FindProxyForURL(url, host) 函数,该返回会返回使用直连还是某个代理。

Mac 上的 ShadowsocksX-NG,如果启用了 Proxy Auto Config Model, 会根据 gfwlist.txt 文件生成 gfwlist.js 文件存放在 ~/.ShadowsocksX-NG 目录。然后启动一个本地的 Web server,然后在系统的 Network 代理中配置 : http://127.0.0.1:30001/proxy.pac , 映射到生成的 gfwlist.js。
shadowsock-ng-pac-file

SwitchOmega 插件也是通过 gfwlist.txt 转 PAC 来实现。
switch-omega-pac

由于 PAC 是通过 js 来实现的,所以只支持浏览器的代理,代理 HTTP/HTTPS 请求。

2.2. ACL

由于 PAC 的缺点,只能运用到浏览器,所以,诞生了 ACL(Access Control Lists)。其中 shadowsocks-libev 是原生支持 ACL 的。

shadowsocks-android(默认使用了 shadowsocks-libev 编译后的库) 就将 gfwlist.txt 文件转换为 ACL 文件后来匹配规则。
shadowsocks-android-acl-file

语法规则可以参考: https://github.com/shadowsocks/shadowsocks-libev/tree/master/acl

常用的 ACL 配置:
https://github.com/ACL4SSR/ACL4SSR

2.3. Surge/Shadowrocket/Quantumult Conf

最早的时候,IOS 上的 Surge 采用的格式,后面 IOS 中的 APP 都采用这种格式。

IOS 常用的 APP 的分流配置文件格式:
https://github.com/lhie1/Rules
https://github.com/h2y/Shadowrocket-ADBlock-Rules

2.4. V2ray

V2ray 使用 geoip.dat(根据 MaxMind IP库生成),geosite.data(自己维护的 domain 列表)两个文件来控制。使用方式:

1
2
3
4
5
"geosite:category-ads": "127.0.0.1"
"geosite:cn"
"geoip:cn",
"geoip:private"
"geosite:category-ads-all"

3. DNS 在透明代理中的使用

先拜读两篇大佬的文章:
漫谈各种黑科技式 DNS 技术在代理环境中的应用

浅谈在代理环境中的 DNS 解析行为

Surge 原理与实现

3.1. 本地解析 DNS

如果要对一个域名发起 TCP 连接请求,比如 HTTP 访问一个地址 www.baidu.com 首先需要把这个域名解析成一个 IP 地址,默认如果没有配置代理的时候,都是客户端调用系统的 getaddrinfo 去解析,获得 IP 地址后,然后向这个 IP 地址发起连接。

getaddrinfo:

1
2
3
1. 查看有没有 HOST
2. 查看有没有本地的 DNS 缓存
3. 使用 DNS Server 解析

3.2. 代理解析 DNS

如果配置了 SOCKS5 本地代理,往往是通过本地代理去做 DNS 解析,或者是本地代理将域名封装到请求中,发送到远程代理服务器,由远程代理服务器去解析。本地代理如果有自定义的 DNS 解析模块就会调用自己的的 DNS 解析器,如果没有,还是同样会调用系统的 getaddrinfo。

3.2.1. 域名分流

在本地代理做 DNS 解析的时候,这个时候,可以根据前面的域名规则来判断是否需要走代理,如果需要走代理,那么就可以用远程代理去解析,如果是直接,那么直接调用本地的解析,然后直接连接。

3.2.2. IP 分流

本地代理解析域名获取到 IP 后,根据 IP 规则来判断是否需要走代理,如果不需要走代理,直接发送数据到目标 IP,如果需要走代理,那么会把数据封装,发送到远程代理。

3.3. PC 端全局 DNS 代理

在 PC 端代理 DNS 解析,只需要本地启动一个 DNS 服务,然后把系统 DNS 改为 127.0.0.1 即可。这样,浏览器,系统中的应用,terminal 中的 curl, ping ,nslookup 命令都会调用本地的 自定义 DNS 解析器。

3.4. 移动端全局 DNS 代理

在移动端(包括路由器),如果要用到全局代理,那么就需要使用到 TUN/TAP 设备。

在移动端系统方面,iOS 既可以像桌面系统用 tun2socks 那样把所有 TCP/UDP 流量引导到 V2Ray,也可以设置一个 HTTP 代理把 HTTP 请求代理到 V2Ray 的 HTTP inbound 里;而 Android,只能以 tun2socks 那样的方式把 TCP/UDP 流量引导进去。

移动端需要使用 tun2socks 将 IP 数据报转成上层的 TCP 包。

3.4.1. 远程解析

本地代理在接收到 DNS 解析请求的时候,不管是调用系统的 getaddrinfo 还是自己的 DNS 解析模块,都会获取一个 IP 地址,然后返回给客户端,客户端再和这个 IP 地址建立连接。

但是,SOCKS 代理是会把域名发送给本地代理,本地代理可以通过自己定义的协议,将这个域名再次转发到远程的代理服务器,远程代理服务器获取到本地代理转发的请求后,是能获取到域名的,也就是远程代理服务器会再做一次 DNS 解析。这样,本地代理执行的 DNS 解析就无用了。

所以,不论是代理客户端还是你的浏览器都没有进行 DNS 解析,DNS 解析是在远端服务器上进行的。因为本地代理转发到远程代理时是可以带上域名的,并且,考虑到针对 CDN 优化,DNS 解析自然需要在远程服务器上执行更好。

3.4.2. Fake DNS

为了节省本地代理的 DNS 解析操作,出现了 Fake DNS, 原理就是本地代理在解析 DNS 时,直接返回一个 240.1.x.x fake IP,客户端收到这个 IP 地址后,向这个 240.1.x.x 发起连接。当收到这个 IP 的 TCP 数据包后,会反响查询这个 240.1.x.x 对应的域名地址,然后本地代理将域名和数据包的信息发送给远程代理,远程代理进行远程 DNS 解析。

Just for my love !!