VPN 改了我的 DNS 吗?对比连接前后
配置文件可能会主动改变查询去向。请固定一个变量,对比 VPN 连接前后的解析策略与实际返回结果。
你连上隧道、打开一个内部面板,某个名字就解析到一个从没见过的地址。或者反过来:浏览器还能用,而命令行工具突然什么都解析不出来。两种结果都来自同一个变化,人们通常用一句“VPN 用了自己的 DNS”来解释,但这句话既不足以定位问题,也不足以验证。那句话里藏着两个问题:这个配置文件应当接管哪些查询,以及你的机器到底有没有把它们发给分配到的解析器。把这两个问题分开,就是整套方法,因为混在一起测试,两个方向都会给出自信的错误答案。
为什么配置文件要提供解析器
在隧道里,远端后面的名字通常解析到只存在于该网络的地址。像 corp.internal 这样的私有区域没有公共委派,因此为它服务的解析器必须能从隧道内部访问。同一机制还服务第二个目的:当查询本来会经运营商发出时,远端的解析器会把查询留在加密路径内。所以 DNS 服务器地址是作为连接策略的一部分下发的,而不是你在机器上一次性选定的全局设置。
地址本身什么也说明不了。解析器只有在有路由可达时才能应答,而最常见的故障就是只有在隧道建立时才可达的解析器。隧道断开而策略仍在时,查询会发往没有路径的地址,症状是超时而不是明确的报错。这个区别在读下一份测试结果时很关键,因为超时会把人们引向根本无关的传输设置。
先比较两种设计,再比较返回结果
全隧道与分离 DNS 会带来相反的预期,两者都不是故障。全隧道下每个查询都可能转到运营商或企业解析器,因此连接后公共域名答得不一样是正常的。分离 DNS 下只有列表中的域走私有解析器,其余保持本地;Windows 上域名列表在 Name Resolution Policy Table 中,macOS 在 DNS 设置的匹配域中,systemd-resolved 主机则在每条链路的路由域中。
这才是首先要做的比较,因为它决定什么算缺陷。分离 DNS 下,从新解析器应答的公共域名值得追查,而正常应答的内部域名说明系统在正常工作。全隧道下,仍由运营商解析器应答的公共域名才是异常。微软关于 VPN 名称解析的文档描述了这条规则在 Windows 上的顺序,包括匹配的策略条目如何先于适配器自身的服务器被采用。
房间里还有第三个容易被忘掉的解析器。浏览器的安全 DNS 会自行解析,并且在隧道改动了操作系统策略之后仍可能继续应答,所以页面能打开只证明浏览器解析器能用,并不证明配置文件已生效。只看浏览器显示的结果,有一半时间在测错的层。
用单一变量记录基线
改隧道。同一步里不要连名字、工具或搜索后缀一起改。选一个你有权查询的名字,记下用了哪个应用,再写下返回的地址或确切的错误。Windows 上 Get-DnsClientServerAddress -AddressFamily IPv4 打印每个适配器的服务器,Get-DnsClientNrptPolicy 打印策略条目;使用 systemd-resolved 的 Linux 上 resolvectl status 显示每条链路的 DNS 服务器与路由域;macOS 上 scutil --dns 打印系统实际会遵循的解析器顺序。
然后通过客户端常规控件连上,重复完全相同的命令。保留第一次输出而不是凭记忆,因为对比才是重点,清缓存之后的第二次读数已经是另一种测量。如果基线是浏览器在前台时取的,而连接后的运行发生在终端里,你比较的是两个解析器,而不是同一个解析器的两种状态。
显式查询与应用查询是两种测试
用 nslookup name 10.20.0.1 或 dig @10.20.0.1 name 向指定服务器提问,只证明一件事:你点名的那台服务器在可达时会应答。它完全绕过选择逻辑,因此无法告诉你系统是否会为这个名字选中它。用它测分配到的解析器是否可达,不要用它测策略。
经本地解析器的查询才真正走到选择。resolvectl query name 走同一套路由域规则并报告是哪种协议应答,Windows 的 Resolve-DnsName 会经过策略表与客户端缓存,scutil --dns 则显示一次查询会遵循的顺序。两种都跑,并分列记录。显式查询成功而应用查询失败,是路由或选择的问题,不是解析器拒绝工作。
缓存挡在你和答案之间。操作系统、本地存根解析器、浏览器和负缓存都持有结果,所以不发出任何一个包,查询也可能成功。清任何东西之前先记下结果:ipconfig /flushdns、resolvectl flush-caches,macOS 上是 sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder。这一步先别做大规模网络重置,它会毁掉你还在收集的证据。
这个差别意味着什么
对比记录下来的两行,在动任何设置之前先给变化归类。
| Observation after connect | Most likely cause | Next step |
|---|---|---|
| Answer moves to a private address range | Expected under split DNS or a private zone | Confirm the suffix is in the effective policy, then test the service |
| Lookup times out while connected, works after disconnect | The assigned resolver has no route from this machine | Probe it with an explicit query, then re-read the policy |
| Internal name fails, public names work | Wrong namespace or a suffix missing from the policy | Query the fully qualified name and check the domain list per platform |
| Internal name returns a public address | Split DNS not applied; a public resolver answered | Check browser secure DNS first, then the per-link domain list |
有一行值得强调。名字能解析不等于服务能用,DNS 成功只说明找到了地址。它对转发、防火墙规则,以及账号是否有权使用该服务都一无所知,所以最后要用真实应用去测,而不是假设故障自己走了。
恢复基线并验证
用客户端自己的控件断开,而不是杀掉进程,然后重读同样三条命令。每条链路的服务器、策略条目和测试名的应答都应回到先前写下的基线。干净断开后仍残留策略条目是真实缺陷,值得记录;这也是手改自动生成的解析器文件属坏习惯的原因:文件会被重写而改动消失,或者改动存活下来并把一个已经没有路由的解析器钉住。
按顺序走一遍这份清单。先判断配置文件实现了哪种设计,再评判任何结果。两次运行都固定名字、工具与后缀。把显式查询与应用查询分开。读生效的策略,而不只是应答。写完对比之后再清缓存。用真实应用验证,然后重启一次,确认断开状态依旧干净。