v2ray中DNS运作流程


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

以以下路由配置为例

{
    "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 的代理请求,的确会相对地慢点。

Reference

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


文章作者: SekiBetu
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 SekiBetu !
评论
  目录