C/C++:gethostbyname 同主机同域名有时阻塞有时立刻错误返回
最近项目中遇到一个很奇特的问题:
在同一主机上,gethostbyname 调用,对同一域名进行查询出现两种情况:
1.阻塞一段时间,然后失败返回;
2.不阻塞,立刻失败返回。
虽然都是失败,但为什么有时候会阻塞,有时候立刻返回失败?
我们知道,gethostbyname 是可能阻塞向域名服务器发送 DNS QUERY 请求获取目的信息。
通常,gethostbyname 将按照 /etc/nsswitch.conf 定义的顺序依次查询。
man /etc/nsswitch.conf:
hosts Host names and numbers, used by gethostbyname(3) and similar functions.)
例如,我的 /etc/nsswitch.conf 定义的关于 hosts 查询顺序为:
[test1280@localhost ~]$ grep hosts /etc/nsswitch.conf | grep "^[^#]"
hosts: files dns
PS:
files = /etc/hosts/
dns = /etc/resolv.conf
/etc/hosts:
这个文件是个静态映射表,将主机名映射到IP地址。
[root@localhost ~]# cat /etc/hosts
127.0.0.1 localhost.localdomain localhost
::1 localhost6.localdomain6 localhost6
/etc/resolv.conf:
这个文件是 resolver 的配置文件。记录的是多个 DNS 域名服务器 IP 地址。
当我们需要进行 DNS 查询时,将依次向这个文件指向的 DNS 域名服务器发送 DNS QUERY 请求获取信息。
[root@localhost ~]# cat /etc/resolv.conf
# Generated by NetworkManager
domain localdomain
search localdomain
nameserver 192.168.40.2
而 /etc/nsswitch.conf 配置中 hosts 一项则定义,当我查询域名 “ABC” 时的查询顺序。
按照我上面的配置,先 files 后 dns,也就是说:
1.尝试从 /etc/hosts 中查询 ABC 对应的 IP 地址,如果有直接返回成功,否则2;
2.尝试从 /etc/resolv.conf 中指向的 DNS(从上到下依次尝试【nameserver】) 查询 ABC,如果找到直接返回成功,否则返回失败。
Demo:
main.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <time.h>
extern int h_errno;
int main(int argc, char **argv)
{
char *hostname = argc < 2 ? "test1280.jlnu.edu" : argv[1];
printf("gethostbyname start:%ld\n", time(NULL));
struct hostent *h = gethostbyname(hostname);
printf("gethostbyname end :%ld\n", time(NULL));
if (h == NULL)
{
printf("gethostbyname(%s) error(%s)\n", hostname, hstrerror(h_errno));
exit(2);
}
printf("gethostbyname(%s) OK: %p\n", hostname, h);
exit(0);
}
当我在 root 下执行:
[root@localhost ~]# ./main abc
gethostbyname start:1538821042
gethostbyname end :1538821052
gethostbyname(abc) error(Host name lookup failure)
失败,阻塞 10 秒返回。
当我在普通用户 test1280 下执行:
[test1280@localhost ~]$ ./main abc
gethostbyname start:1538821147
gethostbyname end :1538821147
gethostbyname(abc) error(Host name lookup failure)
失败,立刻返回。
到这一步其实已经可以有点联想啦!(虽然当时我并没有…)
我们尝试抓包看看!
abc 这个域名在 /etc/hosts 中没有映射规则,正常来说为获取这个域名信息,将会有 DNS 查询发生。
我的 /etc/resolv.conf 的 nameserver 仅一个:192.168.40.2
tcpdump -i eth0 host 192.168.40.2 -s 0 -w dns.cap
1)在 root 下抓包,在 root 下执行 main:
2)在 root 下抓包,在 test1280 下执行 main:啥也没抓到。
即,在 test1280 账户中执行 main 调用 gethostbyname,没有发生 DNS 查询。
因为,/etc/resolv.conf 对于普通用户 test1280 没有读权限!
[root@localhost ~]# ll /etc/resolv.conf
-rw-r-----. 1 root root 92 Oct 6 02:33 /etc/resolv.conf
对于超级用户 root,执行 gethostbyname 时,底层实现以 root 身份打开此配置文件获取 DNS IP 地址,然后发送 DNS QUERY,阻塞等待查询结果返回。
对于普通用户 test1280,执行 gethostbyname 时,底层实现无法以 test1280 身份打开此配置文件获取 DNS IP 地址,于是直接失败返回,没有执行 DNS 查询。
当我给 /etc/resolv.conf 添加任意用户读权限后,在 test1280 账户执行 main 程序将会阻塞一段时间失败返回,并可抓到 DNS QUERY 包。