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:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2Lcd3Zq1EeRNzYsJ1MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2LcRHelR3LcJzLctmch1mclRXY39zMxQDOzcjMwEjNwATM4EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
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 包。