天天看點

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 包。

繼續閱讀