本文为摘选,仅作为备忘录使用

以以下路由配置为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
{
"dns": {
"servers": ["8.8.8.8", "localhost"]
},
"outbounds": [
{
"protocol": "vmess",
"settings": {
"vnext": [
{
"users": [
{
"id": "xxx-x-x-x-xx-x-x-x-x"
}
],
"address": "1.2.3.4",
"port": 10086
}
]
},
"streamSettings": {
"network": "tcp"
},
"tag": "proxy"
},
{
"tag": "direct",
"protocol": "freedom",
"settings": {}
}
],
"inbounds": [
{
"domainOverride": ["http", "tls"],
"port": 1086,
"listen": "127.0.0.1",
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": true,
"ip": "127.0.0.1"
}
}
],
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"ip": ["8.8.8.8"],
"outboundTag": "proxy"
},
{
"type": "field",
"domain": ["geosite:cn"],
"outboundTag": "direct"
}
]
}
}

内置 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

  1. 假设浏览器请求 https://www.bilibili.com
  2. 浏览器发 SOCKS 请求到 V2Ray
  3. 请求来到 V2Ray 的 inbound,再到路由过程
  4. 很明显 www.bilibili.com 这个域名包括在 geosite:cn 中,走 direct
  5. Freedom outbound (direct) 对 www.bilibili.com 发起 TCP 连接
  6. Freedom outbound 解析域名,因为这次没有用 UseIP,用的是系统 DNS
  7. 直接发 DNS 请求到 114.114.114.114
  8. 得到结果后可以跟 Bilibili 服务器建立连接,准备代理浏览器发过来的 HTTPS 流量

S5

  1. 再假设浏览器请求 https://www.google.com
  2. 浏览器发 SOCKS 请求到 V2Ray
  3. 请求来到 V2Ray 的 inbound,再到路由过程
  4. www.google.com 不在 gesoite:cn,也没匹配任何规则,本来应该直接走主 outbound: proxy,但因为我们用了 IPIfNonMatch 策略,V2Ray 会去尝试使用内置的 DNS 把 www.google.com 的 IP 解析出来
  5. V2Ray 使用内置 DNS 向 8.8.8.8 发起针对 www.google.com 的 DNS 请求,这个请求的流量将会是 UDP 流量
  6. 内置 DNS 发出的 DNS 请求会按路由规则走,因为 8.8.8.8 匹配了路由中的第一条规则,这个 DNS 请求的流量会走 proxy
  7. proxy 向远端代理服务器发起 TCP 代理连接(因为 “network”: “tcp”)
  8. 建立起 TCP 连接后,proxy 向远端代理服务器发出 udp:8.8.8.8:53 这样的代理请求
  9. 远端服务器表示接受这个代理请求后,proxy 用建立好的 TCP 连接向远端服务器发送承载了 DNS 请求的 UDP 流量(所以 V2Ray/VMess 目前是 UDP over TCP)
  10. 远端代理服务器接收到这些承载 DNS 请求的 UDP 流量后,发送给最终目标 udp:8.8.8.8:53
  11. 8.8.8.8 返回给远端代理服务器 DNS 结果后,远端代理服务器原路返回至本地 V2Ray 的内置 DNS,至此,从步骤 5 ~ 11,整个 DNS 解析过程完成。
  12. 接上面步骤 4,V2Ray 得到 www.google.com 的 IP,再进行一次规则匹配,很明显路由规则中没有相关的 IP 规则,所以还是没匹配到任何规则,最终还是走了主 outbound: proxy
  13. proxy 向远端代理服务器发起 TCP 代理连接(因为 “network”: “tcp”)
  14. 连接建立后,因为 proxy 中所用的 VMess 协议可以像 SOCKS 那样把域名交给代理服务器处理,所以本地的 V2Ray 不需要自己解析 www.google.com,把域名放进 VMess 协议的参数中一并交给代理服务器来处理
  15. 远端的 V2Ray 代理服务器收到这个代理请求后,它可能自己做域名解析,也可能继续交给下一级代理处理,只要后续代理都支持类 SOCKS 的域名处理方式,这个 DNS 请求就可以一推再推,推给最后一个代理服务器来处理,这个超出本文范围不作讨论,反正这个域名不需要我们本地去解析
  16. 远端代理服务器最后会发出针对 www.google.com 的 DNS 请求(至于究竟是如何发,发到哪个 DNS 服务器,我们不一定能知道,也不关心这个)
  17. 远端代理服务器得到 DNS 结果后,可以真正地向 Google 的服务器建立 TCP 连接 18. 远端的 V2Ray 做好准备后告诉本地 V2Ray 连接建立好了,可以传数据了
  18. 本地 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)查询」。
  • 既没有命中 hosts,又没有命中 DNS 服务器中的 domains 域名列表,则:
    • 默认使用「skipFallback 为默认值 false 的 DNS 服务器」依次查询。若第一个被选中的 DNS 服务器查询失败,或 expectIPs 不匹配,则使用下一个被选中的 DNS 服务器进行查询;否则返回解析得到的 IP。若所有被选中的 DNS 服务器均查询失败,返回空解析。
    • 若「skipFallback 为默认值 false 的 DNS 服务器」数量为 0 或 disableFallback 设置为 true,则使用 DNS 配置中的第一个 DNS 服务器进行查询。查询失败或不匹配 expectIPs 列表,返回空解析;否则返回解析得到的 IP。

Reference

漫谈各种黑科技式 DNS 技术在代理环境中的应用(作者@TachyonDevel)

DNS 域名解析 | V2Fly.org