gethostbyname与DNS

一句话总结

域名查询(Domain Name Query)在Linux上的执行主要由glibc库函数gethostbynamegethostbyaddr来完成,通过strace跟踪可知gethostbyname的执行流程如下:

  1. 连接socket文件/var/run/nscd/socket,通过nscd进程缓存的内容来获取。nscd会实时监测/etc/hosts/etc/resolv.conf文件内容的变化并缓存,具体请参考/etc/nscd.conf中的说明。

  2. 如果没有nscd服务,则通过/etc/nsswitch.conf中的hosts配置项来决定域名查询获取顺序。通常该配置项为"hosts: files dns",则表示先读/etc/hosts,否则就读/etc/resolv.confDNS服务器发出域名解析请求。

注:关于DNS与dig命令的介绍请阅读阮一峰老师的文章DNS原理入门

strace跟踪gethostbyname

先介绍下我的系统环境:

# cat /etc/centos-release 
CentOS release 6.6 (Final)

# uname -a
Linux vm-10-11-174-154 2.6.32-926.504.30.3.letv.el6.x86_64 #1 SMP Mon Aug 3 16:29:31 CST 2015 x86_64 x86_64 x86_64 GNU/Linux

# ll /lib64/libc.so.6
lrwxrwxrwx 1 root root 12 3月  25 2016 /lib64/libc.so.6 -> libc-2.12.so*

# /lib64/libc.so.6 
GNU C Library stable release version 2.12, by Roland McGrath et al.
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 4.4.7 20120313 (Red Hat 4.4.7-16).
Compiled on a Linux 2.6.32 system on 2016-02-16.
Available extensions:
	The C stubs add-on version 2.1.2.
	crypt add-on version 2.1 by Michael Glad and others
	GNU Libidn by Simon Josefsson
	Native POSIX Threads Library by Ulrich Drepper et al
	BIND-8.2.3-T5B
	RT using linux kernel aio
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<http://www.gnu.org/software/libc/bugs.html>.

没有配置nscd的服务器上,用strace追踪gethostbyname,其大致流程摘录如下(省略的部分用......表示)。库函数gethostbyname的使用可参考该页面

# strace ./gethostbyname "lecloud.com"
execve("./gethostbyname", ["./gethostbyname", "lecloud.com"], [/* 32 vars */]) = 0
brk(0)                                  = 0x741000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa832c1e000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=61583, ...}) = 0
mmap(NULL, 61583, PROT_READ, MAP_PRIVATE, 4, 0) = 0x7fa832c0e000
close(4)                                = 0
open("/lib64/libc.so.6", O_RDONLY)      = 4
......
open("/etc/resolv.conf", O_RDONLY)      = 4
......
socket(PF_FILE, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 4
connect(4, {sa_family=AF_FILE, path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
close(4)                                = 0
socket(PF_FILE, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 4
connect(4, {sa_family=AF_FILE, path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
close(4)                                = 0
open("/etc/nsswitch.conf", O_RDONLY)    = 4
......
open("/etc/host.conf", O_RDONLY)        = 4
......
open("/etc/hosts", O_RDONLY|O_CLOEXEC)  = 4
......
socket(PF_INET, SOCK_DGRAM|SOCK_NONBLOCK, IPPROTO_IP) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
gettimeofday({1481080561, 158097}, NULL) = 0
poll([{fd=4, events=POLLOUT}], 1, 0)    = 1 ([{fd=4, revents=POLLOUT}])
sendto(4, "Zq\1\0\0\1\0\0\0\0\0\0\7lecloud\3com\0\0\1\0\1", 29, MSG_NOSIGNAL, NULL, 0) = 29
poll([{fd=4, events=POLLIN}], 1, 5000)  = 1 ([{fd=4, revents=POLLIN}])
ioctl(4, FIONREAD, [77])                = 0
recvfrom(4, "Zq\201\200\0\1\0\3\0\0\0\0\7lecloud\3com\0\0\1\0\1\300\f\0"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("127.0.0.1")}, [16]) = 77
close(4)                                = 0
exit_group(0)                           = ?

要想查看某个程序是否调用gethostbyname,只需用ltrace命令跟踪一下查看其库函数调用即可:

# ltrace ping -c 1 baidu.com 2>&1 | grep gethostbyname
gethostbyname("baidu.com")                       = 0x7f21dfaf16e0

注:在man gethostbyname中有一句:

The gethostbyname() and gethostbyaddr() functions are obsolete. Applications should use getaddrinfo(3) and getnameinfo(3) instead.

其它

nscd

笔者所在公司的服务器上并没有配置nscd,但笔者的阿里云个人服务器上却默认配置了nscd。从man nscd摘录如下:

/usr/sbin/nscd - Name Service Cache Daemon

Nscd is a daemon that provides a cache for the most common name service requests. The default configuration file, /etc/nscd.conf, determines the behavior of the cache daemon.

The daemon will try to watch for changes in configuration files appropriate for each database (e.g. /etc/passwd for the passwd database or /etc/hosts and /etc/resolv.conf for the hosts database), and flush the cache when these are changed.

dnsmasq

Linux服务器上一般都会配置dnsmasq服务,用于缓存DNS请求结果,节省应用程序的域名解析时间。笔者的笔记本Ubuntu 16.04 LTS也默认配置了dnsmasq,同样笔者的macOS Sierra上也默认有一个叫mDNSResponder的服务。dnsmasq简介如下:

dnsmasq - A lightweight DHCP and caching DNS server.

It is intended to provide coupled DNS and DHCP service to a LAN. Dnsmasq accepts DNS queries and either answers them from a small, local, cache or forwards them to a real, recursive, DNS server.

dnsmasq通常会绑定本地127.0.0.1:53,假设配置的DNS服务器是Google Public DNS,则dnsmasq的配置/etc/dnsmasq.conf一般如下:

listen-address=127.0.0.1
bind-interfaces
no-hosts
no-resolv
#all-servers
cache-size=10000
no-negcache
max-cache-ttl=600
filter-aaaa
server=8.8.8.8
server=8.8.4.4

这样,/etc/resolv.conf的配置如下。注意第一项nameserver是本地IP127.0.0.1,也就利用上了dnsmasq的DNS缓存功能。

nameserver 127.0.0.1
nameserver 8.8.8.8
nameserver 8.8.4.4

网络上广泛使用的DNS服务器通常是bind,简介如下:

BIND (Berkeley Internet Name Domain) is an implementation of the DNS (Domain Name System) protocols. BIND includes a DNS server (named), which resolves host names to IP addresses; a resolver library (routines for applications to use when interfacing with DNS); and tools for verifying that the DNS server is operating properly.

named - /usr/sbin/named

dig

strace追踪可知,dig命令是通过读配置文件/etc/resolv.conf,然后向其中列出的DNS服务器发出DNS请求。

# strace dig baidu.com 2>&1 | grep "/etc/resolv.conf"
open("/etc/resolv.conf", O_RDONLY)      = 10

程序跟踪与网络抓包

在日常开发和学习中,遇到问题或对某个东西感到疑惑的时候,对程序进行调用跟踪和对网络进行抓包,是非常有效的分析方式。

strace来跟踪系统函数调用,细节请参考man strace

ltrace来跟踪库函数调用,细节参考man ltrace

wireshark(GUI)、tsharktcpdump来进行网络抓包,细节参考各自的man说明页。

更加强大和复杂的动态追踪技术,请参考SystemTap和DTrace(DTrace for Linux 2016, wikipedia),我还没尝试过。还可以阅读大神章亦春(春哥)写的文章动态追踪技术漫谈

Written on July 24, 2016