代码改变世界

Linux上DNS解析总是选择resolv.conf中第二位的DNS服务器IP地址

2018-08-13 14:41  Loull  阅读(4198)  评论(0编辑  收藏  举报

问题现象:

在Linux机器上,用户自建了一台DNS服务器。然后改动/etc/resolv.conf将其服务器IP地址添加到第一项。将阿里云的内网DNS放到第二位,然而在测试过程中发现telnet,ping以及用户的应用都只会选用排在第二位的DNS服务器。在我们测试机上同样可以复现该问题,也就是配置完成后,无论是telnet还是ping都只会使用第二位的DNS服务器,而忽略了第一位的服务器。

 

问题初步排查:

  1. 尝试了dig和nslookup都没有问题,都会尝试第一位的DNS服务器。问题总是存在于telnet和ping,甚至客户的应用程序也有问题。
  2. 尝试安装和重启nscd也没有效果。
  3. 启用strace来跟踪telnet的行为,发现telnet的行为确实会打开resolv.conf,但是在进行socket操作的时候连接的是第二位DNS服务器:

 

重新排序后位置发生了变化,GLIBC库的函数将第二位的IP移动到了第一位

__libc_res_nsend:

if (__builtin_expect ((statp->options & RES_ROTATE) != 0, 0) &&
    (statp->options & RES_BLAST) == 0) {
    struct sockaddr_in6 *ina;
    unsigned int map;

    n = 0;
    while (n < MAXNS && EXT(statp).nsmap[n] == MAXNS)
        n++;
    if (n < MAXNS) {
        ina = EXT(statp).nsaddrs[n];
        map = EXT(statp).nsmap[n];
        for (;;) {
            ns = n + 1;
            while (ns < MAXNS
                   && EXT(statp).nsmap[ns] == MAXNS)
                ns++;
            if (ns == MAXNS)
                break;
            EXT(statp).nsaddrs[n] = EXT(statp).nsaddrs[ns]; -----------> 把第二个IP地址移动到第一个
            EXT(statp).nsmap[n] = EXT(statp).nsmap[ns];
            n = ns;
        }
        EXT(statp).nsaddrs[n] = ina;
        EXT(statp).nsmap[n] = map;

从整个代码逻辑中可以发现,如果配置rotate轮询选项,__libc_res_nsend会首先使用第二位的IP地址。这一点似乎不和常理。

搜索之后,我们发现该问题已经在RedHat官网上有了说明,但是没有有效的解决方案:

Why option rotate in resolv.conf picks up second nameserver as first every time?
https://access.redhat.com/solutions/1426263

针对该问题我们认为能够规避的方法有两个:

  1. 在resolv.conf去除rotate选项,但是会影响DNS客户端的行为,无法轮询。
  2. 把自建的服务配置在第一位和第二位,把公共服务器配置在第三位。缺点是一旦自建服务器出现问题,DNS需要花更多的时间移动到第三位的公共服务器。