gethostbyname与DNS

作者: lfckop | 来源:发表于2017-12-02 16:41 被阅读208次

    一句话总结

    域名查询(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),我还没尝试过。还可以阅读大神章亦春(春哥)写的文章动态追踪技术漫谈

    相关文章

      网友评论

        本文标题:gethostbyname与DNS

        本文链接:https://www.haomeiwen.com/subject/rfwnbxtx.html