YOKOHEI.COM

YOKOHEI.COM

›DNS Basics

DNS Basics

  • DNS 基礎
  • DNS と OS
  • TCP フォールバック
  • resolv.conf

DNS Misc

  • ゾーン転送
  • クラスレス逆引き
  • trace option
  • Ranking data (RFC 2181)

DNS と OS

DNS クエリが生成されるまで

curl や ping, dig などは、ホスト名に対して実行できる。
nsswitch.conf を見て hosts を見て resolve.conf を見て〜という流れ以上の粒度で、実際のところ何がどういう順序で実行され、 DNS クエリとして飛んでいくのかの挙動を追う。
(これを書いているときにめちゃめちゃいい外部ブログを見つけのでかなり参考にした)

strace からの調査

▼ ping コマンド実行時の strace

# strace -e trace=open -f ping -c 1 yokohei.com
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libcap.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libidn.so.11", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libattr.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
open("/etc/host.conf", O_RDONLY|O_CLOEXEC) = 4
open("/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 4
open("/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 4
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 4
open("/lib64/libnss_files.so.2", O_RDONLY|O_CLOEXEC) = 4
open("/etc/hosts", O_RDONLY|O_CLOEXEC)  = 4
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 4
open("/lib64/libnss_dns.so.2", O_RDONLY|O_CLOEXEC) = 4
open("/lib64/libresolv.so.2", O_RDONLY|O_CLOEXEC) = 4
PING yokohei.com (54.192.29.155) 56(84) bytes of data.
open("/etc/hosts", O_RDONLY|O_CLOEXEC)  = 4
64 bytes from 54.192.29.155: icmp_seq=1 ttl=242 time=1.02 ms

--- yokohei.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.025/1.025/1.025/0.000 ms
+++ exited with 0 +++

▼ dig コマンド実行時の strace

# strace -e trace=open -f dig yokohei.com +short
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib64/liblwres.so.80", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib64/libdns.so.81", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib64/libbind9.so.80", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib64/libisccfg.so.82", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib64/libgssapi_krb5.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib64/libkrb5.so.3", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib64/libk5crypto.so.3", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib64/libcom_err.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libcrypto.so.10", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib64/libisccc.so.80", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib64/libisc.so.83", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libcap.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libidn.so.11", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib64/libkrb5support.so.0", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libkeyutils.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libresolv.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libz.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libattr.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib64/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/proc/filesystems", O_RDONLY)     = 3
...
open("/usr/share/locale/en/libdns.cat", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en/LC_MESSAGES/libdns.cat", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/root/.digrc", O_RDONLY)          = -1 ENOENT (No such file or directory)
open("/etc/resolv.conf", O_RDONLY)      = 6
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 6
Process 3070 attached
Process 3069 attached
Process 3068 attached
54.192.29.138
54.192.29.155
54.192.29.6
54.192.29.63
[pid  3067] --- SIGTERM {si_signo=SIGTERM, si_code=SI_TKILL, si_pid=3067, si_uid=0} ---
[pid  3068] +++ exited with 0 +++
[pid  3070] +++ exited with 0 +++
[pid  3069] +++ exited with 0 +++
open("/proc/sys/vm/overcommit_memory", O_RDONLY|O_CLOEXEC) = 3
+++ exited with 0 +++

ping の時は nsswitch.conf が呼ばれているが、 dig のときは呼ばれない。
dig は DNS クエリを生成するためのツールなので、 nsswitch.conf を見る必要はない、ということかな。
なお、どちらも resolv.conf は呼んでいる。

nsswitch.conf

いろいろなカテゴリーの「名前」サービスの情報を、どの情報源からどういう順序で取得するかを決めるためのもの。
DNS に関する「名前」は hosts のデータベースとして扱われる。

/etc/nsswitch.conf
----
...
hosts: files dns
...
----

今回の例では、 ping の場合はこれが呼ぶようにマッピングされてた。
なので、 /etc/nsswitch.conf を書き換えて、 hosts: files だけにすると、 DNS 名前解決が使えなくなる。

# ping yokohei.com
ping: unknown host yokohei.com

# ping -c1 localhost
PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=255 time=0.018 ms

--- localhost ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.018/0.018/0.018/0.000 ms

逆に hosts: dns にすると hosts にかかれている localhost が引けなくなる。
以下のようなイメージ図。

ping.c

iputils
http://www.skbuff.net/iputils/

iputils/ping.c より一部抜粋

hp = gethostbyname(idn);
if (!hp) {
    fprintf(stderr, "ping: unknown host %s\n", target);
    exit(2);
}

iputils/ping6.c より一部抜粋

gai = getaddrinfo(target, NULL, &hints, &ai);
if (gai) {
    fprintf(stderr, "unknown host\n");
    exit(2);
}

ping6 では IPv6 に対応するため getaddrinfo が利用されている。

gethostbyname()

man gethostbyname より抜粋

The domain name queries carried out by gethostbyname() and gethostbyaddr() rely on the Name Service Switch (nsswitch.conf(5)) configured sources or a local name server (named(8)). The default action is to query the Name Service Switch (nsswitch.conf(5)) configured sources, failing that, a local name server (named(8)).

gethostbyname() 自体が、 nsswitch.conf によって設定を決められる。
ソースコードもちゃんと見たい。

getaddrinfo()

gethostbyname はもう古い、いまは getaddrinfo を見よ、とのこと。(by man page)

int getaddrinfo(const char *node, const char *service,
                const struct addrinfo *hints,
                struct addrinfo **res);
...
struct addrinfo {
    int              ai_flags;
    int              ai_family;
    int              ai_socktype;
    int              ai_protocol;
    socklen_t        ai_addrlen;
    struct sockaddr *ai_addr;
    char            *ai_canonname;
    struct addrinfo *ai_next;
};

外部資料

  • getaddrinfo - ライブラリコールの説明
    https://kazmax.zpp.jp/cmd/g/getaddrinfo.3.html
  • 6.4 Protocol-Independent Nodename and Service Name Translation - RFC 2553
    https://tools.ietf.org/html/rfc2553#section-6.4

参考

  • Anatomy of a Linux DNS Lookup
    https://zwischenzugs.com/2018/06/08/anatomy-of-a-linux-dns-lookup-part-i/

  • Domain name resolution
    https://wiki.archlinux.org/index.php/Domain_name_resolution

  • GETADDRINFO
    https://linuxjm.osdn.jp/html/LDP_man-pages/man3/getaddrinfo.3.html

  • NSSWITCH.CONF
    https://linuxjm.osdn.jp/html/LDP_man-pages/man5/nsswitch.conf.5.html

  • Tracing Linux Hostname Resolution
    https://www.kickflop.net/blog/2011/01/02/tracing-linux-hostname-resolution/

  • Linux Programmer's Manual RESOLV.CONF(5)
    http://man7.org/linux/man-pages/man5/resolv.conf.5.html

← DNS 基礎TCP フォールバック →
▼ Codes ▼
LeetCodeGitHub
▼ Profile ▼
LinkedInFlickr
▼ Logo made with DesignEvo ▼
DesignEvo
Copyright © 2020 Kohei Yoshida