Surge 原理与实现
Surge 是基于 iOS 9 的新特性 Network Extension 开发的一款网络调试工具,工作原理是使用 Packet Tunnel Provider 给系统套上一个代理,Surge 有两个主要组件:Surge 代理服务器和 Surge TUN 接口。程序运行之后,Surge 会将自身设置为默认的 HTTP/HTTPS 代理服务器来处理所有的 HTTP/HTTPS 流量。针对一些不服从系统代理设置(如 Mail.app )的应用程序 ,将由 Surge 的 TUN 接口来进行处理。
Surge 会接管全局的(几乎)所有通信,所以所有网络方面的电量消耗都会被算在 Surge 头上,实际上 Surge 的运行功耗很少,使用中也不会感到 Surge 对电量有明显影响。
Surge Proxy Server
这是 Surge 最核心的部分,用于处理所有的 HTTP / HTTPS 请求。用 Proxy 去处理请求而不是用 TUN,有很多原因,主要是因为 TUN 是工作在 IP Layer 的:
Proxy 可以直接收到包含域名 / URL 的请求,从而使基于域名的规则过滤成为可能(甚至 URL,之后版本可能考虑加入支持)
Proxy 可以省略不必要的 DNS 请求,对首次访问的速度提升相当明显
Surge 的 Proxy 支持全局的 HTTP Connection Keep Alive 机制,极大的减少了 TCP 握手产生的开销(跨应用间也能共享一个 TCP Connection,但由于内存限制在 iOS 版本上超时时间很短,之后 Mac 版本上的效果会明显一些)
准确的记录下每个请求的 header 和 body(从 TCP 层面直接抓,可能会有误)
使得之后的高级功能(如修改 header,rewrite URL)成为可能
减少了不必要的 IP Packet 相关开销
这样 Surge iOS 版本与 Mac 版本可以共享核心代码
这是 Surge 中最复杂的组件,基本上等同于一个不带 cache 的 squid,其中的难点一方面是调度问题,另一方面是怎么样去判断一个 HTTP 请求是否完成(比如 chunked transfer encoding),本身 HTTP RFC 在这方面就有非常多的细节问题,再加上很多自制的 HTTP Server 不是特别的遵守规范,所以只能通过大量的时间去积累经验完成。
DNS Client
可以完全自定义 upstream DNS 服务器地址,无视系统设置
可以并发的向多个 DNS 服务器发出 question
可以自定义超时和重发的策略
getaddrinfo 等方法本身不支持 cache
为之后的 Local DNS Map 功能提供了基础
Surge TUN Interface
部分应用使用的是 HTTP/HTTPS 以外的 TCP 协议 (如 SPDY、IMAP),没有遵从代理设置,因此无法被 Surge 接管流量。为了解决这个问题,才加入了这个组件。
核心原理就是通过一个 Surge 内部的 TCP stack,将 IP Packet 中的 TCP 数据提取出来,再重新用 Proxy 进行请求(或者直接连接对应的服务器),再将返回的数据重新封装成 IP Packet。
正如前面提到的,用 TUN 处理请求会有一些问题,最大的问题是,由 TUN Interface 处理的流量,DOMAIN 相关的 Rule 会无效,除非使用了 force-remote-dns 选项。
这是由 TCP/IP 协议的特性所决定的,App 会先发出一个 DNS question,获取要连接的服务器的 IP 地址,然后直接向这个 IP 地址发起连接,所以有了第 4 个组件。
IP Layer DNS Forwarder
这个组件配合 TUN Interface 使用,会将收到 DNS 的 IP Packet,进行简单改动后直接转发给 upstream DNS。
但是在转发前,该组件会检查需要解析的域名,是否匹配上了带有 force-remote-dns 选项的规则,如果是,不进行转发,直接返回一个 240.1.x.x 的 fake IP。当 TUN Interface 收到一个发往 240.1.x.x 的包的时候,反向查出真正的域名是什么,然后直接以域名的形式转交给 Proxy,避免本地的 DNS 查询动作。
force-remote-dns 选项主要是为了解决有些域名在本地查询会有障碍的问题(如公司内网域名),然而因为返回了一个 fake IP,且 Surge 没有权限去强制清除系统或者应用的 DNS cache,所以在 Surge 关闭后可能导致一些问题,所以请谨慎地只给确实需要的域名开启这个选项。