天天看点

C/C++:gethostbyname 同主机同域名有时阻塞有时立刻错误返回

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 查询顺序为:

[[email protected] ~]$ grep hosts /etc/nsswitch.conf | grep "^[^#]"
hosts:      files dns

PS:
files = /etc/hosts/
dns   = /etc/resolv.conf
           

/etc/hosts:

这个文件是个静态映射表,将主机名映射到IP地址。

[[email protected] ~]# cat /etc/hosts
127.0.0.1	localhost.localdomain	localhost
::1	localhost6.localdomain6	localhost6
           

/etc/resolv.conf:

这个文件是 resolver 的配置文件。记录的是多个 DNS 域名服务器 IP 地址。

当我们需要进行 DNS 查询时,将依次向这个文件指向的 DNS 域名服务器发送 DNS QUERY 请求获取信息。

[[email protected] ~]# 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 下执行:

[[email protected] ~]# ./main abc
gethostbyname start:1538821042
gethostbyname end  :1538821052
gethostbyname(abc) error(Host name lookup failure)
           

失败,阻塞 10 秒返回。

当我在普通用户 test1280 下执行:

[[email protected] ~]$ ./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:

C/C++:gethostbyname 同主机同域名有时阻塞有时立刻错误返回
C/C++:gethostbyname 同主机同域名有时阻塞有时立刻错误返回

2)在 root 下抓包,在 test1280 下执行 main:啥也没抓到。

即,在 test1280 账户中执行 main 调用 gethostbyname,没有发生 DNS 查询。

因为,/etc/resolv.conf 对于普通用户 test1280 没有读权限!

[[email protected] ~]# 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 包。

继续阅读