我有一个运行 Debian Wheezy 的虚拟机,即使解析器立即回复,某些主机名查找也需要几秒钟才能完成。奇怪的是,查找getaddrinfo()
会受到影响,但gethostbyname()
不会。
我已经切换到 Google 解析器以排除本地解析器损坏的可能性,所以我的/etc/resolv.conf
看起来像:
search my-domain.com
nameserver 8.8.4.4
nameserver 8.8.8.8
我的nsswitch.conf
有这样一行:
hosts: files dns
我的/etc/hosts
不包含任何异常的东西。
如果我尝试telnet webserver 80
,它会在获得名称解析之前挂起几秒钟。输出ltrace
[1] 显示挂起发生在getaddrinfo()
呼叫中:
getaddrinfo("ifconfig.me", "telnet", { AI_CANONNAME, 0, SOCK_STREAM, 0, 0, NULL, '\000', NULL }, 0x7fffb4ffc160) = 0 <5.020621>
然而,tcpdump
显示名称服务器立即回复,并且只有在第二次回复时才telnet
解除阻止。回复看起来完全相同:
05:52:58.609731 IP 192.168.1.75.43017 > 8.8.4.4.53: 54755+ A? ifconfig.me. (29)
05:52:58.609786 IP 192.168.1.75.43017 > 8.8.4.4.53: 26090+ AAAA? ifconfig.me. (29)
05:52:58.612188 IP 8.8.4.4.53 > 192.168.1.75.43017: 54755 4/0/0 A 219.94.235.40, A 133.242.129.236, A 49.212.149.105, A 49.212.202.172 (93)
[...five second pause...]
05:53:03.613811 IP 192.168.1.75.43017 > 8.8.4.4.53: 54755+ A? ifconfig.me. (29)
05:53:03.616424 IP 8.8.4.4.53 > 192.168.1.75.43017: 54755 4/0/0 A 219.94.235.40, A 133.242.129.236, A 49.212.149.105, A 49.212.202.172 (93)
05:53:03.616547 IP 192.168.1.75.43017 > 8.8.4.4.53: 26090+ AAAA? ifconfig.me. (29)
05:53:03.618907 IP 8.8.4.4.53 > 192.168.1.75.43017: 26090 0/1/0 (76)
我检查了主机防火墙日志,端口 53 上没有任何内容被阻止。
是什么导致第一个 DNS 回复被忽略?
[1] 我在我的文件中添加了几行,ltrace.conf
这样我就可以看到addrinfo
结构内部。
答案1
第一个 DNS 回复不会被忽略。getaddrinfo()
直到收到第一个 AAAA 查询(ID:26090)的响应后才返回。所以这里真正的问题是为什么你的机器没有立即收到 AAAA 查询的响应,而它已经收到了 A 查询的响应(ID:54755)。
getaddrinfo()
和的区别之一gethostbyname()
是前者同时支持 IPv4 和 IPv6,而后者仅支持 IPv4。因此,当您调用getaddrinfo()
设置ai_family
为 0 ( AF_UNSPEC
) 时,它不会返回,直到获得响应(或达到超时)为止两个都A 和 AAAA 查询提供的域名。gethostbyname()
只查询A记录。
很难远程确定可能导致问题的原因,尤其是在您删除了一些tcpdump
输出的情况下。有些东西可能会选择性地过滤/丢弃虚拟机和 Google 公共 DNS 解析器之间的 DNS 流量。我尝试使用 KVM Debian Wheezy VM 重现您的问题,但telnet ifconfig.me
几乎立即打印了该Trying <IP_address_here>...
行(这意味着它已经解析了该名称)。
答案2
这是由位于 VMware 基础设施前面的 Juniper 防火墙上过于严格的规则集引起的。
我构建了一个测试解析器,这样我就可以看到对话的双方,并且 Kempniu 在他出色的答案中识别出的丢失数据包确实在途中的某个地方被丢弃。正如该答案中所述,getaddrinfo()
如果没有指定地址族,将等待与以下内容相关的答案全部在返回之前支持家庭(或者,就我而言,超时)。
我负责运营该网络的同事指出
Juniper 防火墙上的默认行为是在收到与该会话匹配的 DNS 回复后立即关闭与 DNS 相关的会话。
因此,防火墙看到了 IPv4 响应,注意到它回答了虚拟机的查询,并关闭了该端口的入站路径。因此,随后的 IPv6 回复数据包被丢弃。我不知道为什么两个数据包都第二次通过,但在防火墙上禁用此功能解决了问题。
这是来自 Juniper 知识库的相关摘录:
以下是 DNS 回复数据包被丢弃的场景:
- 当第一个 DNS 查询数据包到达防火墙并且配置了允许策略时,就会创建 DNS 流量会话。默认超时为 60 秒。
- 在会话关闭之前,会传输一个新的 DNS 查询,并且由于它与现有会话匹配(因为源和目标端口/IP 对始终相同),因此它会由防火墙转发。请注意,会话超时不会根据任何新到达的数据包进行刷新。
- 当第一个 DNS 查询响应(回复)到达设备时,无论剩余超时时间是多少,创建的 DNS 会话都会超时。
- 当 DNS 回复通过防火墙时,会话就会过期。
- 由于不存在会话,所有后续 DNS 回复都会被防火墙丢弃。
如果您想投票赞成这个答案,请同时投票给 Kempniu 的答案。如果没有它,我仍然会四处寻找虚拟机上的一些配置问题。