解析主机名需要 5 秒

Tom*_*art 10 dns debian ipv6 bind nslookup

我有一个主bind9DNS 服务器和 2 个在 IPv4(Debian Jessie)上运行的从服务器,使用/etc/bind/named.conf

listen-on-v6 { none; };
Run Code Online (Sandbox Code Playgroud)

当我尝试从不同的服务器连接时,每个连接至少需要 5 秒(我使用Joseph 的时间信息进行调试):

$ curl -w "@curl-format.txt" -o /dev/null -s https://example.com
            time_namelookup:  5.512
               time_connect:  5.512
            time_appconnect:  5.529
           time_pretransfer:  5.529
              time_redirect:  0.000
         time_starttransfer:  5.531
                            ----------
                 time_total:  5.531
Run Code Online (Sandbox Code Playgroud)

根据curl,查找需要大部分时间,但是标准nslookup非常快:

$ time nslookup example.com > /dev/null 2>&1

real    0m0.018s
user    0m0.016s
sys     0m0.000s
Run Code Online (Sandbox Code Playgroud)

强制curl使用 IPv4 后,情况变得更好:

$ curl -4 -w "@curl-format.txt" -o /dev/null -s https://example.com

            time_namelookup:  0.004
               time_connect:  0.005
            time_appconnect:  0.020
           time_pretransfer:  0.020
              time_redirect:  0.000
         time_starttransfer:  0.022
                            ----------
                 time_total:  0.022
Run Code Online (Sandbox Code Playgroud)

我在主机上禁用了 IPv6:

echo 1 > /proc/sys/net/ipv6/conf/eth0/disable_ipv6
Run Code Online (Sandbox Code Playgroud)

虽然问题仍然存在。我试过运行strace看看超时的原因是什么:

write(2, "*", 1*)                        = 1
write(2, " ", 1 )                        = 1
write(2, "Hostname was NOT found in DNS ca"..., 36Hostname was NOT found in DNS cache
) = 36
socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP) = 4
close(4)                                = 0
mmap(NULL, 8392704, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f220bcf8000
mprotect(0x7f220bcf8000, 4096, PROT_NONE) = 0
clone(child_stack=0x7f220c4f7fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f220c4f89d0, tls=0x7f220c4f8700, child_tidptr=0x7f220c4f89d0) = 2004
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, NULL, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
poll(0, 0, 4)                           = 0 (Timeout)
rt_sigaction(SIGPIPE, NULL, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
poll(0, 0, 8)                           = 0 (Timeout)
rt_sigaction(SIGPIPE, NULL, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
poll(0, 0, 16)                          = 0 (Timeout)
rt_sigaction(SIGPIPE, NULL, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
poll(0, 0, 32)                          = 0 (Timeout)
rt_sigaction(SIGPIPE, NULL, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
poll(0, 0, 64)                          = 0 (Timeout)
Run Code Online (Sandbox Code Playgroud)

这似乎不是防火墙问题,因为nslookup(或curl -4)使用相同的 DNS 服务器。知道有什么问题吗?

这是tcpdump主持人的来信tcpdump -vvv -s 0 -l -n port 53

tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
20:14:52.542526 IP (tos 0x0, ttl 64, id 35839, offset 0, flags [DF], proto UDP (17), length 63)
    192.168.1.1.59163 > 192.168.1.2.53: [bad udp cksum 0xf9f3 -> 0x96c7!] 39535+ A? example.com. (35)
20:14:52.542540 IP (tos 0x0, ttl 64, id 35840, offset 0, flags [DF], proto UDP (17), length 63)
    192.168.1.1.59163 > 192.168.1.2.53: [bad udp cksum 0xf9f3 -> 0x6289!] 45997+ AAAA? example.com. (35)
20:14:52.543281 IP (tos 0x0, ttl 61, id 63674, offset 0, flags [none], proto UDP (17), length 158)
    192.168.1.2.53 > 192.168.1.1.59163: [udp sum ok] 45997* q: AAAA? example.com. 1/1/0 example.com. [1h] CNAME s01.example.com. ns: example.com. [10m] SOA ns01.example.com. ns51.domaincontrol.com. 2016062008 28800 7200 1209600 600 (130)
20:14:57.547439 IP (tos 0x0, ttl 64, id 36868, offset 0, flags [DF], proto UDP (17), length 63)
    192.168.1.1.59163 > 192.168.1.2.53: [bad udp cksum 0xf9f3 -> 0x96c7!] 39535+ A? example.com. (35)
20:14:57.548188 IP (tos 0x0, ttl 61, id 64567, offset 0, flags [none], proto UDP (17), length 184)
    192.168.1.2.53 > 192.168.1.1.59163: [udp sum ok] 39535* q: A? example.com. 2/2/2 example.com. [1h] CNAME s01.example.com., s01.example.com. [1h] A 136.243.154.168 ns: example.com. [30m] NS ns01.example.com., example.com. [30m] NS ns02.example.com. ar: ns01.example.com. [1h] A 136.243.154.168, ns02.example.com. [1h] A 192.168.1.2 (156)
20:14:57.548250 IP (tos 0x0, ttl 64, id 36869, offset 0, flags [DF], proto UDP (17), length 63)
    192.168.1.1.59163 > 192.168.1.2.53: [bad udp cksum 0xf9f3 -> 0x6289!] 45997+ AAAA? example.com. (35)
20:14:57.548934 IP (tos 0x0, ttl 61, id 64568, offset 0, flags [none], proto UDP (17), length 158)
    192.168.1.2.53 > 192.168.1.1.59163: [udp sum ok] 45997* q: AAAA? example.com. 1/1/0 example.com. [1h] CNAME s01.example.com. ns: example.com. [10m] SOA ns01.example.com. ns51.domaincontrol.com. 2016062008 28800 7200 1209600 600 (130)
Run Code Online (Sandbox Code Playgroud)

编辑: 在绑定日志中经常出现此消息:

error sending response: host unreachable
Run Code Online (Sandbox Code Playgroud)

尽管如此,每个查询最终都会得到回答(只需要 5 秒)。所有机器都是物理服务器(这不是 NAT 的错),数据包更有可能被路由器阻止。这是很可能相关的问题:DNS 查找有时需要 5 秒

Tom*_*art 11

简短的回答:

一种解决方法是强制glibc重用套接字来查找AAAAA记录,方法是向 中添加一行/etc/resolv.conf

options single-request-reopen
Run Code Online (Sandbox Code Playgroud)

这个问题的真正原因可能是:

长答案:

程序喜欢curlwget使用 glibc 的函数getaddrinfo(),它试图通过并行查找两个 DNS 记录来与 IPv4 和 IPv6 兼容。在收到两条记录之前它不会返回结果(有几个与此类行为相关的问题) - 这解释了strace上述内容。当强制使用 IPv4 时,例如在curl -4内部gethostbyname()仅查询A记录。

tcpdump我们可以看出:

  • -> A? 开始时发送两个请求
  • -> AAAA? (请求 IPv6 地址)
  • <- AAAA 回复
  • -> A? 再次请求 IPv4 地址
  • <- A 收到回复
  • -> AAAA? 再次请求 IPv6
  • <- AAAA 回复

A由于某种原因,一个回复被丢弃,这是以下错误消息:

error sending response: host unreachable
Run Code Online (Sandbox Code Playgroud)

然而,我不清楚为什么需要第二次AAAA查询。

要验证您是否遇到了相同的问题,您可以在/etc/resolv.conf以下位置更新超时:

options timeout:3
Run Code Online (Sandbox Code Playgroud)

如此处所述

$ curl -w "@curl-format.txt" -o /dev/null -s https://example.com

            time_namelookup:  3.511
               time_connect:  3.511
            time_appconnect:  3.528
           time_pretransfer:  3.528
              time_redirect:  0.000
         time_starttransfer:  3.531
                            ----------
                 time_total:  3.531
Run Code Online (Sandbox Code Playgroud)

中还有另外两个相关选项man resolv.conf

单一请求(因为glibc的2.10)RES_SNGLKUP_res.options。默认情况下,glibc 从 2.9 版开始并行执行 IPv4 和 IPv6 查找。某些设备 DNS 服务器无法正确处理这些查询并使请求超时。此选项禁用该行为并使 glibc 依次执行 IPv6 和 IPv4 请求(以降低解析过程的速度为代价)。

single-request-reopen (自 glibc 2.9) 解析器对 A 和 AAAA 请求使用相同的套接字。一些硬件错误地只返回一个回复。当这种情况发生时,客户端系统将坐等第二个回复。打开此选项会更改此行为,以便如果未正确处理来自同一端口的两个请求,它将关闭套接字并在发送第二个请求之前打开一个新的套接字。

相关问题:


Rui*_*iro 5

正如@Tombart 所说,延迟是由于等待 IPv6 解析超时造成的。

另一种可能的做法是在 /etc/gai.conf 中优先考虑 IPv4

来自 /etc/gai.conf 中的注释

#   For sites which prefer IPv4 connections change the last line to
#
precedence ::ffff:0:0/96  100
Run Code Online (Sandbox Code Playgroud)

更改后gai.conf,您需要重新启动任何使用 DNS 解析器库的应用程序才能使更改生效。

请注意,如果您使用没有 IPv6 连接的 BIND 服务器,我建议禁用 IPv6named并从根提示中获取 IPv6 地址。显然它仍然会尝试解析 AAAA 地址。

所以对于 BIND 配置,

在 /etc/default/bind9 中,为 IPv4 地址添加 -4:

OPTIONS="-4 -u bind"
Run Code Online (Sandbox Code Playgroud)

并在 中/etc/bind/db.root删除所有具有 AAAA DNS 根的行。