Sukka's Blog

童话只美在真实却从不续写

简析代理客户端导致的远端服务器流媒体 DNS 解锁失效的可能原因

Sukka's Avatar 2020-01-26 技术向

  1. 1. 客户端在代理环境中的 DNS 解析行为
  2. 2. DNS 解锁 是什么
  3. 3. DNS 解锁 的局限性
  4. 4. 如何正确配置客户端与服务端

之前和几个商业性质的公共代理服务提供商交流,提到了即使在远端服务器上部署了 DNS 解锁,一些用户仍然无法观看 Netflix 等流媒体。写一篇文章记录一下简单分析的结果。

注意!这篇文章仅供参考,公共代理服务提供商(及其用户)可以参考本文对相关配置进行自查,但不可将此文作为搪塞流媒体解锁不稳定的借口!

客户端在代理环境中的 DNS 解析行为

在继续阅读之前,我强烈建议先阅读我之前的文章「浅谈在代理环境中的 DNS 解析行为」。如果你已经看过那篇文章,那么就跟着我的思路复习一下那篇文章中的重点:

  • 由于 TCP/IP 的协议特性,在应用发起 TCP 连接时,会先发出一个 DNS question(发一个 IP Packet),获取要连接的服务器的 IP 地址,然后直接向这个 IP 地址发起连接。
  • 由于 某种协议(某些协议)支持封装域名,因此远端服务器可以拿到域名而不是 IP,因此远端服务器上的代理服务端还需要进行一次解析。此时,远端服务器连接的 IP 与 本地解析得到的 IP 毫无关系
  • 代理客户端有以下几种方式反查 TCP 连接的域名:
    • 通过 HTTP / SOCKS 方式接入代理客户端的,可以直接获取封装在 HTTP / SOCKS 中的域名
    • 使用代理客户端提供的 redir 和 TUN 的,代理客户端可以通过截获 DNS question 反推 TCP 连接的目标域名
  • 而如果远端服务器拿到的直接是 IP 不是域名,那么远端服务器就会直接向这个 IP 发起连接。

DNS 解锁 是什么

虽然 DNS 解锁可谓是家喻户晓,但是鲜有文章详细进行过详细介绍。所以在分析 DNS 解锁为什么会失效之前,首先还是分析一下 DNS 解锁的原理。我们知道代理客户端会将目标域名或 IP 封装并通过隧道发送给代理服务端,服务端会与目标域名或 IP 进行连接,并将连接封装后通过隧道发送回代理客户端。我们还知道,由于流媒体服务(如 Netflix、HBO、Amazon Prime Video 等)必须使用特定的 IP 地址才能访问。

这些 IP 一般被称为「原生 IP」。我本人并不赞同这个「原生」说法,毕竟不存在「私生」的 IP

+----------+       +----------------+       +-----------+
| | | | | |
| Client +-------> Proxy Server +-------> Website |
| | | | | |
+----------+ +----------------+ +-----------+

但是如果你的远端服务器还不支持访问某些网站(如上文提到的流媒体)时,此时就需要在远端服务器上再加一个跳板:

+----------+       +----------------+       +------------------------+      +-----------+
| | | | | | | |
| Client +-------> Proxy Server +-------> Another Proxy Server +------> Website |
| | | | | | | |
+----------+ +----------------+ +------------------------+ +-----------+

一般的,这个跳板会使用 SNI 代理服务器。不知道什么是 SNI 的请参看 维基百科。SNI 代理可以视为一个与透明代理相似的反向代理工具,可以在 TCP 层直接将流量在不解包的情况下进行转发(因为工作在 TCP 层,所以不需要配置证书、没有 TLS 加密解密过程、也能反向代理 HTTPS 网站),可以实现对任意网站的代理。

互联网运营商(ISP)通过 DNS 将用户劫持到的缓存服务器就是 SNI 代理服务器。

在支持访问流媒体的服务器上安装 SNI 代理,将远端服务器的流量全部转发到这个 SNI 代理服务器上,远端服务器也就具备了访问受限制的流媒体的能力。但是在实际操作中,考虑到购买支持访问流媒体的服务器的成本,因此远端服务器一般只转发流媒体网站的流量到 SNI 代理服务器上:

                                            +-----------+
| |
+---> Website |
+----------+ +----------------+ | | |
| | | | | +-----------+
| Client +------> Proxy Server +----+
| | | | | +-------------+ +-----------------+
+----------+ +----------------+ | | | | |
+---> SNI Proxy +------> Netflix / HBO |
| | | |
+-------------+ +-----------------+

分流的方法类似于运营商的 DNS 劫持。在远端服务器上配置 DNS,将流媒体网站的劫持到支持解锁的 SNI 代理服务器上,其它网站不做劫持即可。通过这种方式甚至可以将不同的流媒体的 DNS 劫持到不同的 SNI 代理服务器上,实现对不流媒体服务的解锁。由于分流依赖 DNS,故名「DNS 解锁」。

DNS 解锁 的局限性

由于分流的实现依赖于 DNS,因此,当代理客户端与代理服务端之间通过 某种协议 建立隧道以后,必须传入域名而不是 IP,才能触发远端服务器的 DNS 解析行为、从而实现将流媒体部分请求分流给 SNI 代理服务器。反之,如果代理客户端传入的不是域名而是 IP,那么远端服务器则不需要进行 DNS 解析直接对该 IP 发起连接、无法实现分流。

根据这个思路,我们就可以找到 DNS 解锁失效的两种可能原因:一种是流媒体的客户端的实现方式。流媒体的客户端可能内置了 DNS 解析、并在 DNS 解析完成后直接向 IP 发起 TCP 连接,代理客户端就无从得知连接的原始域名,那么就会将 IP 而不是域名发送给位于远端服务器上的代理服务端;另一种是代理客户端的实现。Clash 和 Surge 等代理客户端是可以将域名发送给远端服务器的,但是有一些代理客户端,在完成了 DNS 解析以后,并不会反推 TCP 连接的域名、而是直接将 IP 发送给远端服务器。

如何正确配置客户端与服务端

对于远程服务器,应该配置 DNS 劫持(即将 53 端口所有流量转发给 DNS 解锁的 DNS 上),可以使用 iptables 和 DNAT 实现。

对于代理客户端,首先应该推荐使用 Clash 和 Surge,正如我前文所说,这两个代理客户端均支持反查 TCP 连接的域名、并将域名发送给代理服务端。如果当前没有在使用 Clash 或 Surge 的用户遇到无法通过 DNS 解锁访问流媒体服务的,应该尝试使用 Clash 和 Surge、排查问题是否出在代理客户端上。
对于 不确定是否支持反解、发送域名给代理服务端 的代理客户端,如路由器上的 KoolSS、FancySS 等,DNS 模式应该选择 dns2socksss-tunnel(如果是 Padavan,应勾选启用 dnsproxy),因为这些模式都会将对国外域名的 DNS 查询转发到远程服务器,而此时如果远端服务器上正确配置了 DNS 劫持,DNS 解锁的 DNS 也可以收到查询请求,最终也可以在远端服务器上实现流媒体分流。在这些客户端上不应该使用 chinadns 等直连进行 DNS 查询的模式(chinadns 不支持通过代理进行 DNS 查询,因此解析请求不会经过远端服务器)和 PCap_DNSProxy 等 DNS 加密模式(远端服务器无法识别加密后的 DNS 解析请求、无法转发给 DNS 解锁)。我和几个商业性质的公共代理服务提供商交流的结果是,Lean 的 luci-app-ssr-plus 几乎一定会导致 DNS 解锁失效。

本文最后更新于 天前,文中所描述的信息可能已发生改变