v2ray中DNS运作流程
本文为摘选,仅作为备忘录使用
以以下路由配置为例
1 | { |
内置 DNS 用 8.8.8.8 做首选服务器,localhost 作备用,路由中首先来一条规则让 8.8.8.8 的流量一定走 proxy,匹配了 geosite:cn 中的域名的请求走 direct,如果没匹配任何规则,则走主 outbound,也即 outbounds 中的第一个,也即 proxy。
同样设置系统代理至 V2Ray 的 SOCKS inbound 127.0.0.1:1086,再来考虑浏览器做请求的过程,经过上面几个例子,可以看到很多步骤其实是一样的,所以后续例子中会简化一些:
S4
- 假设浏览器请求 https://www.bilibili.com
- 浏览器发 SOCKS 请求到 V2Ray
- 请求来到 V2Ray 的 inbound,再到路由过程
- 很明显 www.bilibili.com 这个域名包括在 geosite:cn 中,走 direct
- Freedom outbound (direct) 对 www.bilibili.com 发起 TCP 连接
- Freedom outbound 解析域名,因为这次没有用 UseIP,用的是系统 DNS
- 直接发 DNS 请求到 114.114.114.114
- 得到结果后可以跟 Bilibili 服务器建立连接,准备代理浏览器发过来的 HTTPS 流量
S5
- 再假设浏览器请求 https://www.google.com
- 浏览器发 SOCKS 请求到 V2Ray
- 请求来到 V2Ray 的 inbound,再到路由过程
- www.google.com 不在 gesoite:cn,也没匹配任何规则,本来应该直接走主 outbound: proxy,但因为我们用了 IPIfNonMatch 策略,V2Ray 会去尝试使用内置的 DNS 把 www.google.com 的 IP 解析出来
- V2Ray 使用内置 DNS 向 8.8.8.8 发起针对 www.google.com 的 DNS 请求,这个请求的流量将会是 UDP 流量
- 内置 DNS 发出的 DNS 请求会按路由规则走,因为 8.8.8.8 匹配了路由中的第一条规则,这个 DNS 请求的流量会走 proxy
- proxy 向远端代理服务器发起 TCP 代理连接(因为 “network”: “tcp”)
- 建立起 TCP 连接后,proxy 向远端代理服务器发出 udp:8.8.8.8:53 这样的代理请求
- 远端服务器表示接受这个代理请求后,proxy 用建立好的 TCP 连接向远端服务器发送承载了 DNS 请求的 UDP 流量(所以 V2Ray/VMess 目前是 UDP over TCP)
- 远端代理服务器接收到这些承载 DNS 请求的 UDP 流量后,发送给最终目标 udp:8.8.8.8:53
- 8.8.8.8 返回给远端代理服务器 DNS 结果后,远端代理服务器原路返回至本地 V2Ray 的内置 DNS,至此,从步骤 5 ~ 11,整个 DNS 解析过程完成。
- 接上面步骤 4,V2Ray 得到 www.google.com 的 IP,再进行一次规则匹配,很明显路由规则中没有相关的 IP 规则,所以还是没匹配到任何规则,最终还是走了主 outbound: proxy
- proxy 向远端代理服务器发起 TCP 代理连接(因为 “network”: “tcp”)
- 连接建立后,因为 proxy 中所用的 VMess 协议可以像 SOCKS 那样把域名交给代理服务器处理,所以本地的 V2Ray 不需要自己解析 www.google.com,把域名放进 VMess 协议的参数中一并交给代理服务器来处理
- 远端的 V2Ray 代理服务器收到这个代理请求后,它可能自己做域名解析,也可能继续交给下一级代理处理,只要后续代理都支持类 SOCKS 的域名处理方式,这个 DNS 请求就可以一推再推,推给最后一个代理服务器来处理,这个超出本文范围不作讨论,反正这个域名不需要我们本地去解析
- 远端代理服务器最后会发出针对 www.google.com 的 DNS 请求(至于究竟是如何发,发到哪个 DNS 服务器,我们不一定能知道,也不关心这个)
- 远端代理服务器得到 DNS 结果后,可以真正地向 Google 的服务器建立 TCP 连接 18. 远端的 V2Ray 做好准备后告诉本地 V2Ray 连接建立好了,可以传数据了
- 本地 V2Ray 就告诉浏览器连接好了,可以传数据了,浏览器就可以把 HTTPS 流量顺着这个代理链发送至 Google 的服务器
在上面 S5 中,步骤 5 ~ 11 做了次 DNS 请求,采用代理转发 DNS 请求流量的方式,而在步骤 14 中,又说可以不解析域名,交给远端服务器来解析,这两者其实并不冲突。一般来说,前者的处理方式就是实实在在的 UDP 流量代理而已,后者一般叫作远程 DNS 解析。用了 IPIfNonMatch
,对域名做一次 DNS 解析要经过 5 ~ 11 这么多步骤,看起来效率很慢,但如果代理服务器不慢的话,这个过程一般是很快的,最重要的是 V2Ray 内置 DNS 对 DNS 结果有一个缓存,所以并不需要每次都去做 DNS 请求。不管怎么说,毕竟还是做了额外的事情,而且有可能涉及到一个 UDP over TCP 的代理请求,的确会相对地慢点。
DNS 处理流程
若当前要查询的域名:
- 命中了
hosts
中的「域名 - IP」、「域名 - IP 数组」映射,则将该 IP 或 IP 数组作为 DNS 解析结果返回。 - 命中了
hosts
中的「域名 - 域名」映射,则该映射的值(另一个域名)将作为当前要查询的新域名,进入 DNS 处理流程,直到解析出 IP 后返回,或返回空解析。 - 没有命中
hosts
,但命中了某(几)个 DNS 服务器中的domains
域名列表,则按照命中的规则的优先级,依次使用该规则对应的 DNS 服务器进行查询。若命中的 DNS 服务器查询失败,或expectIPs
不匹配,则使用下一个命中的 DNS 服务器进行查询;否则返回解析得到的 IP。若所有命中的 DNS 服务器均查询失败,此时 DNS 组件:- 默认会进行 「DNS 回退(fallback)查询」:使用「上一轮失败查询中未被使用的、且
skipFallback
为默认值false
的 DNS 服务器」依次查询。若查询失败,或expectIPs
不匹配,返回空解析;否则返回解析得到的 IP。 - 若
disableFallback
设置为true
,则不会进行「DNS 回退(fallback)查询」。
- 默认会进行 「DNS 回退(fallback)查询」:使用「上一轮失败查询中未被使用的、且
- 既没有命中
hosts
,又没有命中 DNS 服务器中的domains
域名列表,则:- 默认使用「
skipFallback
为默认值false
的 DNS 服务器」依次查询。若第一个被选中的 DNS 服务器查询失败,或expectIPs
不匹配,则使用下一个被选中的 DNS 服务器进行查询;否则返回解析得到的 IP。若所有被选中的 DNS 服务器均查询失败,返回空解析。 - 若「
skipFallback
为默认值false
的 DNS 服务器」数量为 0 或disableFallback
设置为true
,则使用 DNS 配置中的第一个 DNS 服务器进行查询。查询失败或不匹配expectIPs
列表,返回空解析;否则返回解析得到的 IP。
- 默认使用「
Reference
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 惜别的秘密基地!
评论
UtterancesGiscus