Signs of Triviality

Opinions, mostly my own, on the importance of being and other things.
[homepage] [index] [jschauma@netmeister.org] [@jschauma] [RSS]

DNS tcpdump by example

April 6th, 2018

Dick TraceyFor my class on System Administration, I often use a homework assignment requiring students to capture and analyze tcpdump(1) output to help them understand how the DNS works in detail. This is accompanying the lecture notes, in which we do go through the packets from the server to the roots etc., but after grading the assignment, I often provide additional and repeated explanations to help students understand what they should have been able to get out of the assignment. This post serves as a sample solution that I hope is useful to others in understanding a little bit about both the DNS and tcpdump(1).

In this example, I set up an AWS EC2 instance using the NetBSD AMI 'ami-569ed93c'. Configuring a DNS resolver is as trivial as:

  • adding 'named=YES' to /etc/rc.conf
  • replacing the 'nameserver' line in /etc/resolv.conf with nameserver 127.0.0.1
  • running '/etc/rc.d/named start'

Next, we begin capturing network traffic (excluding SSH traffic, which we are not interested in and which we'd otherwise see a fair bit of, since we are logged in via SSH), followed by a simple DNS lookup:

# tcpdump -w /tmp/dns.pcap -i xennet0 port not 22 &
# host www.yahoo.com
www.yahoo.com is an alias for atsv2-fp.wg1.b.yahoo.com.
atsv2-fp.wg1.b.yahoo.com has address 98.138.219.232
atsv2-fp.wg1.b.yahoo.com has address 72.30.35.9
atsv2-fp.wg1.b.yahoo.com has address 72.30.35.10
atsv2-fp.wg1.b.yahoo.com has address 98.138.219.231
atsv2-fp.wg1.b.yahoo.com has IPv6 address 2001:4998:44:41d::3
atsv2-fp.wg1.b.yahoo.com has IPv6 address 2001:4998:44:41d::4
atsv2-fp.wg1.b.yahoo.com has IPv6 address 2001:4998:58:1836::10
atsv2-fp.wg1.b.yahoo.com has IPv6 address 2001:4998:58:1836::11
# fg
tcpdump -w /tmp/dns.pcap -i xennet0 port not 22
^C118 packets captured
210 packets received by filter
0 packets dropped by kernel
# 

We now should have everything we need in our pcap file:

$ tcpdump -n -t -r /tmp/dns.pcap port 53
reading from file /tmp/dns.pcap, link-type EN10MB (Ethernet)
IP 10.239.226.139.49357 > 202.12.27.33.53: 64795% [1au] DNSKEY? dlv.isc.org. (52)
IP 10.239.226.139.64544 > 202.12.27.33.53: 27552% [1au] DNSKEY? . (40)
IP 10.239.226.139.59484 > 202.12.27.33.53: 60312 [1au] NS? . (40)
IP 10.239.226.139.63183 > 202.12.27.33.53: 31286 [1au] A? www.yahoo.com. (54)
IP 202.12.27.33.53 > 10.239.226.139.64544: 27552*-|$ 0/0/1 (28)
IP 202.12.27.33.53 > 10.239.226.139.59484: 60312*-| 0/0/1 (28)
IP 10.239.226.139.64249 > 202.12.27.33.53: Flags [S], seq 1711881477, win 32768, options [...], length 0
IP 10.239.226.139.64248 > 202.12.27.33.53: Flags [S], seq 1730617655, win 32768, options [...], length 0
IP 202.12.27.33.53 > 10.239.226.139.49357: 64795-| 0/0/1 (40)
IP 10.239.226.139.64247 > 202.12.27.33.53: Flags [S], seq 1753257479, win 32768, options [...], length 0
[ 540 more lines here ]
$ 

Ok, there's bunch of things going on here. There are queries beyond the one we had made (What's up with the dlv.isc.org query?), there's TCP involved (Wait, what? I thought DNS is UDP?), and it will take some practice to identify and trim out only the relevant packets. Let's take a closer look as we go through this. Let's play Dig +Trace-y:

By default, bind(8) on NetBSD is configured with DNSSEC validation enabled, as shown in /etc/named.conf:

options {
        directory "/etc/namedb";
        dnssec-enable yes;
        dnssec-validation auto;
        dnssec-lookaside auto;
        managed-keys-directory "keys";
        bindkeys-file "bind.keys";
        allow-recursion { localhost; localnets; };
};

Since both DNSSEC validation and DNSSEC Lookaside Validation are enabled, bind(8) will use certain defaults for the initial trust anchors, which, in this version of bind(8), still includes the use of dlv.isc.org as the DLV zone. As a result, our very first query to one of the root servers is for the DNSKEY Resource Record for dlv.isc.org as well as for the root zone itself (.):

IP 10.239.226.139.49357 > 202.12.27.33.53: 64795% [1au] DNSKEY? dlv.isc.org. (52)
IP 10.239.226.139.64544 > 202.12.27.33.53: 27552% [1au] DNSKEY? . (40)
IP 10.239.226.139.59484 > 202.12.27.33.53: 60312 [1au] NS? . (40)

We also see bind(8) updating its cache of nameservers responsible for the root (NS? .), which it previously read from the root hints, which is how bind(8) figured out which server to talk to to begin with, since right now it has not yet cached any information about any other nameservers. In our example, bind(8) picked 202.12.27.33, or M.ROOT-SERVERS.NET:

$ grep -A3 "zone \"\." /etc/named.conf
zone "." {
        type hint;
        file "root.cache";
};
$ grep M.ROOT-SERVERS.NET. /etc/namedb/root.cache 
.                        3600000      NS   M.ROOT-SERVERS.NET.
M.ROOT-SERVERS.NET.      3600000      A    202.12.27.33
M.ROOT-SERVERS.NET.      3600000      AAAA 2001:DC3::35
$ 

So the queries made so far do not have anything (directly) to do with the lookup of our hostname www.yahoo.com. The first query relevant to this lookup (with the fields annotated as per the tcpdump(1) manual page) is:



proto src                  > dst:             id    op?  flags qtype qclass name           (len)
IP    10.239.226.139.63183 > 202.12.27.33.53: 31286      [1au] A?           www.yahoo.com. (54)

That is, our query goes to m.root-servers.net, id 31286, with one additional RR section in the query ([1au]), asking for an A record for "www.yahoo.com". Our query is 54 bytes long, which is a bit larger than the 13 bytes needed for "www.yahoo.com", however. If you pass -v -v to tcpdump, you can get a little bit more information and you may find that our DNS server also adds an additional OPT pseudo-DNS record type, used to support EDNS. In particular, bind(8) uses an EDNS option code 65001 to send and process so-called "Source Identity Tokens", or DNS Cookies, which explains the larger query size.

We then see the responses from m.root-servers.net to our server for the DNSKEY, NS, and A queries (IDs 64795 and 27552, 60312, and 31286 respectively):

IP 202.12.27.33.53 > 10.239.226.139.64544: 27552*-|$ 0/0/1 (28)
IP 202.12.27.33.53 > 10.239.226.139.59484: 60312*-| 0/0/1 (28)
IP 202.12.27.33.53 > 10.239.226.139.49357: 64795-| 0/0/1 (40)
IP 202.12.27.33.53 > 10.239.226.139.63183: 31286-| 0/0/1 (42)

Note that the results show zero answer records, zero name server records, and one additional record (0/0/1) being returned. The *-|$ flags indicate that the authoritative answer bit was set (*) for IDs 27552 and 60312 (which cover queries for the root), but not for 64795 (which was the query for dlv.isc.org), nor for 31286 (which was for www.yahoo.com), recursion available was not set (-), the authenticated data (AD) bit was set for ID 27552 ($), and, notably, the truncated message (|) was set.

(I've taken the liberty of re-ordering the tcpdump output a little bit here; a few of the connections are made near simultaneously, so it helps to group what belongs together for the purposes of explaining the traffic. If you want to play along, the full pcap file is available here.)

The fact that the messages are truncated explains what happens next:

IP 10.239.226.139.64249 > 202.12.27.33.53: Flags [S], seq 1711881477, win 32768, options [...], length 0
IP 10.239.226.139.64248 > 202.12.27.33.53: Flags [S], seq 1730617655, win 32768, options [...], length 0
IP 10.239.226.139.64247 > 202.12.27.33.53: Flags [S], seq 1753257479, win 32768, options [...], length 0
IP 10.239.226.139.64246 > 202.12.27.33.53: Flags [S], seq 1762061450, win 32768, options [...], length 0

Because the response from m.root-servers.net was truncated -- it would not have fit into a single 512 byte UDP packet -- our server initiates a TCP connection to m.root-servers.net (bind(8) appears to somewhat inefficiently open a new connection for each of the queries). We'll only pick out the traffic relevant to our lookup of www.yahoo.com:

IP 10.239.226.139.64246 > 202.12.27.33.53: Flags [S], seq 1762061450, win 32768, options [...], length 0
IP 202.12.27.33.53 > 10.239.226.139.64246: Flags [S.], seq 1294098020, ack 1762061451, win 32768, options [...], length 0
IP 10.239.226.139.64246 > 202.12.27.33.53: Flags [.], ack 1, win 4197, options [...], length 0
IP 10.239.226.139.64246 > 202.12.27.33.53: Flags [P.], seq 1:57, ack 1, win 4197, options [...], length 5642475 [1au] A? www.yahoo.com. (54)
IP 202.12.27.33.53 > 10.239.226.139.64246: Flags [P.], seq 1:1176, ack 57, win 4197, options [...], length 117542475- 0/15/27 (1173)
IP 10.239.226.139.64246 > 202.12.27.33.53: Flags [F.], seq 57, ack 1176, win 4197, options [...], length 0
IP 202.12.27.33.53 > 10.239.226.139.64246: Flags [.], ack 58, win 4197, options [...], length 0
IP 202.12.27.33.53 > 10.239.226.139.64246: Flags [F.], seq 1176, ack 58, win 4197, options [...], length 0
IP 10.239.226.139.64246 > 202.12.27.33.53: Flags [.], ack 1177, win 4197, options [...], length 0

Here, we see the 3-way handshake, the query and response (in bold), and finally the connection closing. The query is the same as above; the response now contains zero answer records (because the root does not have any A records for www.yahoo.com), 15 name server records, and 27 additional records. To see the actual results, you can run tcpdump with the -v -v flags, and you'd yield:

202.12.27.33.53 > 10.239.226.139.64246: Flags [P.],
        cksum 0x51f2 (correct), seq 1:1176, ack 57, win 4197, options [nop,nop,TS val 1 ecr 1],
        length 117542475- q: A? www.yahoo.com.
        0/15/27 ns: com. NS b.gtld-servers.net., com. NS g.gtld-servers.net., com.
           NS i.gtld-servers.net., com. NS h.gtld-servers.net.,
           com. NS e.gtld-servers.net., com. NS
           d.gtld-servers.net., com. NS j.gtld-servers.net., com.
           NS m.gtld-servers.net., com. NS a.gtld-servers.net.,
           com. NS f.gtld-servers.net., com. NS
           k.gtld-servers.net., com. NS c.gtld-servers.net., com.
           NS l.gtld-servers.net., com. DS, com. RRSIG ar:
           a.gtld-servers.net. A 192.5.6.30, b.gtld-servers.net.
           A 192.33.14.30, c.gtld-servers.net. A 192.26.92.30,
           d.gtld-servers.net. A 192.31.80.30,
           e.gtld-servers.net. A 192.12.94.30,
           f.gtld-servers.net. A 192.35.51.30,
           g.gtld-servers.net. A 192.42.93.30,
           h.gtld-servers.net. A 192.54.112.30,
           i.gtld-servers.net. A 192.43.172.30,
           j.gtld-servers.net. A 192.48.79.30,
           k.gtld-servers.net. A 192.52.178.30,
           l.gtld-servers.net. A 192.41.162.30,
           m.gtld-servers.net. A 192.55.83.30,
           a.gtld-servers.net. AAAA 2001:503:a83e::2:30,
           b.gtld-servers.net. AAAA 2001:503:231d::2:30,
           c.gtld-servers.net. AAAA 2001:503:83eb::30,
           d.gtld-servers.net. AAAA 2001:500:856e::30,
           e.gtld-servers.net. AAAA 2001:502:1ca1::30,
           f.gtld-servers.net. AAAA 2001:503:d414::30,
           g.gtld-servers.net. AAAA 2001:503:eea3::30,
           h.gtld-servers.net. AAAA 2001:502:8cc::30,
           i.gtld-servers.net. AAAA 2001:503:39c1::30,
           j.gtld-servers.net. AAAA 2001:502:7094::30,
           k.gtld-servers.net. AAAA 2001:503:d2d::30,
           l.gtld-servers.net. AAAA 2001:500:d937::30,
           m.gtld-servers.net. AAAA 2001:501:b1f9::30, . OPT
           UDPsize=4096 DO (1173)

You can simulate our query by directly asking m.root-servers.net ourselves, for example via dig(1):

$ dig @m.root-servers.net -t A www.yahoo.com
; <<>> DiG 9.10.2-P4 <<>> @m.root-servers.net -t A www.yahoo.com
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2688
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 13, ADDITIONAL: 27
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.yahoo.com.			IN	A

;; AUTHORITY SECTION:
com.			172800	IN	NS	d.gtld-servers.net.
com.			172800	IN	NS	a.gtld-servers.net.
com.			172800	IN	NS	i.gtld-servers.net.
com.			172800	IN	NS	b.gtld-servers.net.
com.			172800	IN	NS	k.gtld-servers.net.
com.			172800	IN	NS	j.gtld-servers.net.
com.			172800	IN	NS	m.gtld-servers.net.
com.			172800	IN	NS	f.gtld-servers.net.
com.			172800	IN	NS	h.gtld-servers.net.
com.			172800	IN	NS	l.gtld-servers.net.
com.			172800	IN	NS	g.gtld-servers.net.
com.			172800	IN	NS	c.gtld-servers.net.
com.			172800	IN	NS	e.gtld-servers.net.

;; ADDITIONAL SECTION:
a.gtld-servers.net.	172800	IN	A	192.5.6.30
b.gtld-servers.net.	172800	IN	A	192.33.14.30
c.gtld-servers.net.	172800	IN	A	192.26.92.30
d.gtld-servers.net.	172800	IN	A	192.31.80.30
e.gtld-servers.net.	172800	IN	A	192.12.94.30
f.gtld-servers.net.	172800	IN	A	192.35.51.30
g.gtld-servers.net.	172800	IN	A	192.42.93.30
h.gtld-servers.net.	172800	IN	A	192.54.112.30
i.gtld-servers.net.	172800	IN	A	192.43.172.30
j.gtld-servers.net.	172800	IN	A	192.48.79.30
k.gtld-servers.net.	172800	IN	A	192.52.178.30
l.gtld-servers.net.	172800	IN	A	192.41.162.30
m.gtld-servers.net.	172800	IN	A	192.55.83.30
a.gtld-servers.net.	172800	IN	AAAA	2001:503:a83e::2:30
b.gtld-servers.net.	172800	IN	AAAA	2001:503:231d::2:30
c.gtld-servers.net.	172800	IN	AAAA	2001:503:83eb::30
d.gtld-servers.net.	172800	IN	AAAA	2001:500:856e::30
e.gtld-servers.net.	172800	IN	AAAA	2001:502:1ca1::30
f.gtld-servers.net.	172800	IN	AAAA	2001:503:d414::30
g.gtld-servers.net.	172800	IN	AAAA	2001:503:eea3::30
h.gtld-servers.net.	172800	IN	AAAA	2001:502:8cc::30
i.gtld-servers.net.	172800	IN	AAAA	2001:503:39c1::30
j.gtld-servers.net.	172800	IN	AAAA	2001:502:7094::30
k.gtld-servers.net.	172800	IN	AAAA	2001:503:d2d::30
l.gtld-servers.net.	172800	IN	AAAA	2001:500:d937::30
m.gtld-servers.net.	172800	IN	AAAA	2001:501:b1f9::30

;; Query time: 77 msec
;; SERVER: 2001:dc3::35#53(2001:dc3::35)
;; WHEN: Fri Apr 06 15:39:59 EDT 2018
;; MSG SIZE  rcvd: 838
$ 

In other words: we asked the root server "Do you know what the IP address is for www.yahoo.com?", and it replied "Nope, sorry, but seeing how you're looking for www.yahoo.com, here are the name servers responsible for .com. Oh, and since I'm guessing you will want to ask them next, why don't I go ahead and give you their IP addresses as well - here you go."

The dig(1) output only shows 13 name server results (versus the 15 noted in the tcpdump output) because the tool does not show the DS and RRSIG records being returned when our server, which supports DNSSEC, queries it. To confirm, run tcpdump(1) while running dig(1); you should notice that the query does not have DO bit set in the OPT section, indicating no support for DNSSEC security RRs, and a result indicating 0/13/27.

Ok, so far, so good. Now we know where to go next, so without further ado, our DNS server moves on to query one of the gTLD name servers responsible for .com:

IP 10.239.226.139.61919 > 192.55.83.30.53: 3207 [1au] A? www.yahoo.com. (54)
IP 192.55.83.30.53 > 10.239.226.139.61919: 3207-| 0/8/3 (498)
IP 10.239.226.139.64241 > 192.55.83.30.53: Flags [S], seq 1863732189, win 32768, options [...], length 0
IP 192.55.83.30.53 > 10.239.226.139.64241: Flags [S.], seq 24427409, ack 1863732190, win 1460, options [...], length 0
IP 10.239.226.139.64241 > 192.55.83.30.53: Flags [.], ack 1, win 33580, length 0
IP 10.239.226.139.64241 > 192.55.83.30.53: Flags [P.], seq 1:57, ack 1, win 33580, length 5661575 [1au] A? www.yahoo.com. (54)
IP 192.55.83.30.53 > 10.239.226.139.64241: Flags [P.], seq 1:784, ack 57, win 65535, length 78361575- 0/9/9 (781)
IP 10.239.226.139.64241 > 192.55.83.30.53: Flags [F.], seq 57, ack 784, win 33580, length 0
IP 192.55.83.30.53 > 10.239.226.139.64241: Flags [.], ack 58, win 65535, length 0
IP 192.55.83.30.53 > 10.239.226.139.64241: Flags [F.], seq 784, ack 58, win 65535, length 0
IP 10.239.226.139.64241 > 192.55.83.30.53: Flags [.], ack 785, win 33580, length 0

Here, we are hitting m.gtld-servers.net, getting again back a truncated response (|), switch over to TCP, and get our result with 0 answer records (m.gtld-servers.net is not authoritative for yahoo.com), 9 NS records, and 9 additional records (the IP addresses of the name servers returned). To simulate once more (again skipping the DNSSEC-related RRs):

$ dig @m.gtld-servers.net -t A www.yahoo.com
; <<>> DiG 9.10.6 <<>> @m.gtld-servers.net -t A www.yahoo.com
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 63633
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 5, ADDITIONAL: 9
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.yahoo.com.			IN	A

;; AUTHORITY SECTION:
yahoo.com.		172800	IN	NS ns1.yahoo.com.
yahoo.com.		172800	IN	NS ns5.yahoo.com.
yahoo.com.		172800	IN	NS ns2.yahoo.com.
yahoo.com.		172800	IN	NS ns3.yahoo.com.
yahoo.com.		172800	IN	NS ns4.yahoo.com.

;; ADDITIONAL SECTION:
ns1.yahoo.com.		172800	IN	AAAA 2001:4998:130::1001
ns1.yahoo.com.		172800	IN	A    68.180.131.16
ns5.yahoo.com.		172800	IN	A    119.160.253.83
ns2.yahoo.com.		172800	IN	AAAA 2001:4998:140::1002
ns2.yahoo.com.		172800	IN	A    68.142.255.16
ns3.yahoo.com.		172800	IN	A    203.84.221.53
ns3.yahoo.com.		172800	IN	AAAA 2406:8600:b8:fe03::1003
ns4.yahoo.com.		172800	IN	A    98.138.11.157

;; Query time: 35 msec
;; SERVER: 192.55.83.30#53(192.55.83.30)
;; WHEN: Fri Apr 06 16:17:32 EDT 2018
;; MSG SIZE  rcvd: 296

$ 

Now that we know whom to ask for yahoo.com, we should hopefully get to an actual answer to our query. Let's see:


IP 10.239.226.139.55510 > 119.160.253.83.53: 57391 [1au] A? www.yahoo.com. (54)
IP 119.160.253.83.53 > 10.239.226.139.55510: 57391*- 1/4/3 CNAME atsv2-fp.wg1.b.yahoo.com. (189)

Well, that was easy! At least we didn't have to fall back to TCP. But we still don't get back and IP address! Instead, it turns out that www.yahoo.com has a CNAME record, pointing to the canonical name atsv2-fp.wg1.b.yahoo.com, with additional nameservers provided to us by ns5-new.yahoo.com:

$ dig @119.160.253.83 -t A www.yahoo.com

 EDNS: version: 0, flags:; udp: 1272
;; QUESTION SECTION:
;www.yahoo.com.			IN	A

;; ANSWER SECTION:
www.yahoo.com.		1800	IN	CNAME atsv2-fp.wg1.b.yahoo.com.

;; AUTHORITY SECTION:
wg1.b.yahoo.com.	172800	IN	NS yf1.yahoo.com.
wg1.b.yahoo.com.	172800	IN	NS yf3.a1.b.yahoo.net.
wg1.b.yahoo.com.	172800	IN	NS yf4.a1.b.yahoo.net.
wg1.b.yahoo.com.	172800	IN	NS yf2.yahoo.com.

;; ADDITIONAL SECTION:
yf1.yahoo.com.		86400	IN	A 68.142.254.15
yf2.yahoo.com.		86400	IN	A 68.180.130.15

At this point, we would be done if we had merely asked for ANY record for www.yahoo.com. And if your instructor didn't make you sift through your tcpdump(1) output, you could also have observed these queries using the command 'dig +trace', as shown below by example of ash.cs.stevens-tech.edu:

$ dig +trace ash.cs.stevens-tech.edu
; <<>> DiG 9.10.2-P4 <<>> +trace ash.cs.stevens-tech.edu
;; global options: +cmd
.      503084  IN  NS  j.root-servers.net.
.      503084  IN  NS  e.root-servers.net.
.      503084  IN  NS  a.root-servers.net.
.      503084  IN  NS  g.root-servers.net.
.      503084  IN  NS  h.root-servers.net.
.      503084  IN  NS  c.root-servers.net.
.      503084  IN  NS  i.root-servers.net.
.      503084  IN  NS  f.root-servers.net.
.      503084  IN  NS  b.root-servers.net.
.      503084  IN  NS  m.root-servers.net.
.      503084  IN  NS  l.root-servers.net.
.      503084  IN  NS  k.root-servers.net.
.      503084  IN  NS  d.root-servers.net.
;; Received 1097 bytes from 127.0.0.1#53(127.0.0.1) in 0 ms

edu.      172800  IN  NS  a.edu-servers.net.
edu.      172800  IN  NS  c.edu-servers.net.
edu.      172800  IN  NS  d.edu-servers.net.
edu.      172800  IN  NS  f.edu-servers.net.
edu.      172800  IN  NS  g.edu-servers.net.
edu.      172800  IN  NS  l.edu-servers.net.
;; Received 622 bytes from 198.97.190.53#53(h.root-servers.net) in 3 ms

stevens-tech.edu.  172800  IN  NS  nrac.stevens-tech.edu.
stevens-tech.edu.  172800  IN  NS  sitult.stevens-tech.edu.
stevens-tech.edu.  172800  IN  NS  drdns2.stevens.edu.
;; Received 654 bytes from 192.31.80.30#53(d.edu-servers.net) in 1 ms

ash.cs.stevens-tech.edu. 604800  IN  A  155.246.89.159
cs.stevens-tech.edu.  3600  IN  NS  sitult.stevens-tech.edu.
cs.stevens-tech.edu.  3600  IN  NS  nrac.stevens-tech.edu.
cs.stevens-tech.edu.  3600  IN  NS  drdns2.stevens.edu.
;; Received 185 bytes from 155.246.248.20#53(drdns2.stevens.edu) in 8 ms
A graphic
illustrating the DNS queries.

But we used the host(1) command to perform the lookup, which will, by default, not just look for A records, but also AAAA and MX records. So we move on with our query by asking one of Yahoo's nameservers (ns4.yahoo.com) for the IP address of atsv2-fp.wg1.b.yahoo.com:

IP 10.239.226.139.57729 > 98.138.11.157.53: 50901 [1au] A? atsv2-fp.wg1.b.yahoo.com. (65)
IP 98.138.11.157.53 > 10.239.226.139.57729: 50901- 0/4/3 (171)

The dig(1) equivalent, once more:

$ dig @98.138.11.157 -t A atsv2-fp.wg1.b.yahoo.com.
;; AUTHORITY SECTION:
wg1.b.yahoo.com.	172800	IN	NS yf4.a1.b.yahoo.net.
wg1.b.yahoo.com.	172800	IN	NS yf3.a1.b.yahoo.net.
wg1.b.yahoo.com.	172800	IN	NS yf2.yahoo.com.
wg1.b.yahoo.com.	172800	IN	NS yf1.yahoo.com.

;; ADDITIONAL SECTION:
yf1.yahoo.com.		86400	IN	A 68.142.254.15
yf2.yahoo.com.		86400	IN	A 68.180.130.15
$ 

That is, ns4.yahoo.com tells us which nameservers are responsible for wg1.b.yahoo.com., and... hey, wait a second! We already knew this information! That's exactly the information supplied by ns5-new.yahoo.com in the AUTHORITY section. Why didn't bind(8) use that information right away?

Well, it turns out that CNAME records are special. The resolution algorithm defined in e.g. RFC1034 effectively says "if you encounter a CNAME that you didn't ask for, replace the original query with that result and restart", which appears to short circuit the processing of the AUTHORITY section, leading us to make one additional query to one of the authoritative name servers we had previously cached for yahoo.com.

Now that we have received this result from ns4.yahoo.com, we then proceed to try one of those name servers:

IP 10.239.226.139.59022 > 68.142.254.15.53: 12110 [1au] A? atsv2-fp.wg1.b.yahoo.com. (65)
IP 68.142.254.15.53 > 10.239.226.139.59022: 12110*- 4/0/1 A 72.30.35.9, A 98.138.219.231, A 72.30.35.10, A 98.138.219.232 (117)
IP 10.239.226.139.58946 > 68.180.130.15.53: 48507 [1au] AAAA? atsv2-fp.wg1.b.yahoo.com. (65)
IP 68.180.130.15.53 > 10.239.226.139.58946: 48507*- 4/0/1 AAAA 2001:4998:44:41d::4, AAAA
2001:4998:44:41d::3, AAAA 2001:4998:58:1836::11, AAAA 2001:4998:58:1836::10 (165)
IP 10.239.226.139.64418 > 68.180.130.15.53: 61781 [1au] MX? atsv2-fp.wg1.b.yahoo.com. (65)
IP 68.180.130.15.53 > 10.239.226.139.64418: 61781*- 0/1/1 (114)

Note: here we clearly see the query for not just the A record, but also the AAAA and MX records (although no MX records were found in the results):

$ dig @68.142.254.15 -t any atsv2-fp.wg1.b.yahoo.com
;; QUESTION SECTION:
;atsv2-fp.wg1.b.yahoo.com.	IN	ANY

;; ANSWER SECTION:
atsv2-fp.wg1.b.yahoo.com. 60	IN	A    72.30.35.9
atsv2-fp.wg1.b.yahoo.com. 60	IN	A    72.30.35.10
atsv2-fp.wg1.b.yahoo.com. 60	IN	AAAA 2001:4998:58:1836::11
atsv2-fp.wg1.b.yahoo.com. 60	IN	AAAA 2001:4998:58:1836::10

Finally, note that atsv2-fp.wg1.b.yahoo.com has two NS records for which no IP addresses were supplied in the "additional" section of the response: yf4.a1.b.yahoo.net and yf3.a1.b.yahoo.net. For these, our nameserver now dutifully will go and find IP addresses to cache, and for that, it needs to go back to the gTLD servers, since they are in the .net domain:

IP 10.239.226.139.51122 > 192.35.51.30.53: 46082% [1au] A? yf4.a1.b.yahoo.net. (59)
IP 10.239.226.139.63675 > 192.35.51.30.53: 320% [1au] AAAA? yf4.a1.b.yahoo.net. (59)
IP 192.35.51.30.53 > 10.239.226.139.63675: 320-| 0/8/3 (512)
IP 192.35.51.30.53 > 10.239.226.139.51122: 46082-| 0/8/3 (512)

The responses from f.gtld-servers.net are again truncated, and we expect our DNS server to now switch to TCP to complete this query, but at this point we have interrupted the tcpdump process, as our initial command and query was successfully completed.

And there you have it: a full trace of a DNS query made by a resolver for a hostname it has not yet cached any information for. It's what goes on behind this trivial example:

$ host www.yahoo.com
www.yahoo.com is an alias for atsv2-fp.wg1.b.yahoo.com.
atsv2-fp.wg1.b.yahoo.com has address 72.30.35.9
atsv2-fp.wg1.b.yahoo.com has address 72.30.35.10
atsv2-fp.wg1.b.yahoo.com has IPv6 address 2001:4998:58:1836::10
atsv2-fp.wg1.b.yahoo.com has IPv6 address 2001:4998:58:1836::11
$ 

Wireshark screenshotNote: you will see a lot of additional packets in your pcap file, including all the traffic related to DNSSEC, which we elided here. That includes queries for DS, RRSIG, DNSKEY records as well as lookups against the DLV. Going through those in detail shall be an exercise for another day, however...

Using e.g. Wireshark to analyze the packets can be more efficient and convenient, but I do maintain that any SysAdmin worth their salt ought to be able to go through a pcap file just using tcpdump(1), and doing so is a useful exercise in tracing packets.

April 6th, 2018


[Paranoid Principles] [Index]