------------------[ Readings ]------------------ We looked through the skeleton of a UDP server http://www.cs.dartmouth.edu/~sergey/cs60/udp-echo/udpserver.c in http://www.cs.dartmouth.edu/~sergey/cs60/udp-echo/ . Read my comments on the system calls used (my comments usually start with NOTE: or Note:, except at the top.) We worked through the traffic that a caching, recursive DNS server sends to resolve a client's query. The summary of the traffic with the server's cache empty is in http://www.cs.dartmouth.edu/~sergey/cs60/dns/recursive-dns-lookup-noDNSSEC-annotated.txt and http://www.cs.dartmouth.edu/~sergey/cs60/dns/recursive-dns-lookup-noDNSSEC-another.txt. These files, less my comments, were translated from packet captures (named http://www.cs.dartmouth.edu/~sergey/cs60/dns/recursive-dns-lookup-noDNSSEC.pcap and http://www.cs.dartmouth.edu/~sergey/cs60/dns/recursive-dns-lookup-noDNSSEC-another.pcap) to the representation that the "dig" tool uses for the packets it sends and receives. You can also look at how tcpdump represents these packets, with "tcpdump -r <file> -n" (add -n to not perform extra name queries; don't add n to see how the IP addresses in the capture are resolved. Without -n, tcpdump will also resolve known port names, so "cs60.cs.dartmouth.edu.domain" and 129.170.213.53.53 stand for the same thing. Note that tcpdump adds the port 53 to the address over a dot; it's just a formatting idiosyncrasy). My script for doing the translation is "dns-pcap-to-dig.py". We made some captures of my DNS server running at 129.170.213.53 in class. You will find these in http://www.cs.dartmouth.edu/~sergey/cs60/dns/pcaps-04-18-17/ as dns-req-fresh.{pcap,txt} and dns-req-precached.{pcap.txt}. Note that the fresh requests were recorded after I flushed the cache of the DNS server; this caused it to walk the entire path from the root zone down. The "precached" versions were recorded with the cache primed by previous requests; thus, seeing the name for dartmouth.edu, the server would go straight to the nameserver it was previously supplied as authoritative for dartmouth.edu, skipping the root servers and the EDU zone servers. ------------------[ Reverse DNS resolution ]------------------ Reverse DNS resolution is mapping IP address to an FQDN name---if any. Many sites do not actually configure reverse resolution: you may be able to resolve a name to an address, but not the other way around. Configuring reverse name resolution typically requires a separate file ("reverse zone file"), and many domain administrators do not bother to set it up. Reverse resolution also uses names (recall that RR records must start with a name; the first byte we expect is the length of the first element of the name; and if it's 00 then we consider the name to be ".", the root zone itself). For reverse resolution of IPs to FQDNs, these names are contrived: they end in the imaginary top-level domain arpa, have a 2nd-level sub-domain in-addr.arpa, and then use the bytes of the IP address as if these were next-level subdomains, in reverse order. So, for the in-class example of 129.170.17.4, the name in the query is 4.17.170.129.in-addr.arpa. The type of this query is special, PTR (or type 12), to distinguish it from requests for addresses (type A, 1) or nameservers authoritative for a domain (type NS, 2). In the captures dns-reverse.{pcap,txt} and dns-reverse-afresh.{pcap,txt} (also in http://www.cs.dartmouth.edu/~sergey/cs60/dns/pcaps-04-18-17/), you can see the sequence of requests and delegations ("I don't know, but ask those servers"). The chain of PTR requests, intermingled with looking up authoritative nameservers' addresses, looks roughly like this: [dns-reverse.txt or pcap]: Root server "E" (192.203.230.10) is asked, who has 4.17.170.129.in-addr.arpa.? Root server "E" respond: ask these servers for "in-addr.arpa.", they might know. My server asks f.in-addr-servers.arpa. aka 193.0.9.1 next. That server responds: ask "129.in-addr.arpa." servers, here's a list. Most of the servers on that list are in "arin.net", one in "ripe.net" (look up what ARIN and RIPE mean). My server now has to resolve these names before it can proceed. It now asks the root servers (specifically, "J" 192.58.128.30), for the IPs of "u.arin.net", "z.arin.net", "r.arin.net", "z.arin.net", "y.arin.net", and "arin.authdns.ripe.net". The root server J declines to answer directly, but responds back with authoritative servers for ".net", like "a.gtld-servers.net". Go ask them. Our server asks "f.gtld-servers.net", 192.35.51.30. That server delegates to the bunch of servers for "arin.net". So our server's next requests are to "ns1.arin.net", 199.212.0.108. Remember, we are still looking for some servers that could help us resolve things in the "129.in-addr.arpa." zone. It is taking its time, finding out all of these names! "ripe.net" throws us a curve ball: some of the servers we get delegated to are in "verisigndns.com". So now we go back to root servers ("D" this time, 199.7.91.13) to ask about ".com" and then, to whichever delegate we get, about "verisigndns.com". While all of this discovery of nameservers is going on, our original objective of the PTR query for "4.17.170.129.in-addr.arpa." seems forgotten. But it was not forgotten: it times out on our server! So even though we continue receiving responses for the various nameserver names, our server sends back a SERVFAIL to the requester: -------------------[ 129.170.213.53 -> 129.170.212.20 ]------------------- ;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 63134 ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1 ;; QUESTION SECTION: ;4.17.170.129.in-addr.arpa. IN PTR ;; ADDITIONAL SECTION: ;OPT PSEUDOSECTION ;EDNS: version: 0, flags: ; udp: 4096 And that's too bad! We didn't even get to whoever was the authority for "170.129.in-addr.arpa.", let alone further! Although we did get to "129.in-addr.arpa.", and that is progress :) The request succeeds after a few more tries, when the server is lucky to pick the authorities that reply before the timeout. See dns-reverse-afresh.txt and dns-reverse-afresh.pcap for the log of another failed (timed-out) attempt at PTR (search for "PTR" in the txt file), then two more failures---because the SERVFAIL result has been cached by my server (this feature was added in 2014 to curb too many requests, see https://serverfault.com/questions/426807/how-long-does-negative-dns-caching-typically-last). Then, finally, it succeeds! First, the server for "170.129.in-addr.arpa." is discovered. It's ns1.augsburg.edu, along with a couple of Dartmouth servers: -------------------[ 204.61.216.50 -> 129.170.213.53 ]------------------- ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29128 ;; flags: qr; QUERY: 1, ANSWER: 0, AUTHORITY: 3, ADDITIONAL: 0 ;; QUESTION SECTION: ;4.17.170.129.in-addr.arpa. IN PTR ;; AUTHORITY SECTION: 170.129.in-addr.arpa. 86400 IN NS ns1.augsburg.edu. 170.129.in-addr.arpa. 86400 IN NS dns-ext2.dartmouth.edu. 170.129.in-addr.arpa. 86400 IN NS dns-ext1.dartmouth.edu. Then, finally, ns1.augsburg.edu. (141.224.68.1) is nice enough to respond on time! -------------------[ 141.224.68.1 -> 129.170.213.53 ]------------------- ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10153 ;; flags: qr aa; QUERY: 1, ANSWER: 1, AUTHORITY: 3, ADDITIONAL: 3 ;; QUESTION SECTION: ;4.17.170.129.in-addr.arpa. IN PTR ;; ANSWER SECTION: 4.17.170.129.in-addr.arpa. 259200 IN PTR ns1.dartmouth.edu. ;; AUTHORITY SECTION: 170.129.in-addr.arpa. 28800 IN NS dns-ext2.dartmouth.edu. 170.129.in-addr.arpa. 28800 IN NS dns-ext1.dartmouth.edu. 170.129.in-addr.arpa. 28800 IN NS dns-ext3.dartmouth.edu. ;; ADDITIONAL SECTION: dns-ext1.dartmouth.edu. 28800 IN A 129.170.170.2 dns-ext2.dartmouth.edu. 28800 IN A 129.170.170.10 dns-ext3.dartmouth.edu. 28800 IN A 132.177.102.11 And that is the time (4th in this capture, 5th overall) that our request for reverse lookup of 129.170.17.4 succeeds: -------------------[ 129.170.213.53 -> 129.170.212.20 ]------------------- ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 13088 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 3, ADDITIONAL: 4 ;; QUESTION SECTION: ;4.17.170.129.in-addr.arpa. IN PTR ;; ANSWER SECTION: 4.17.170.129.in-addr.arpa. 259200 IN PTR ns1.dartmouth.edu. ;; AUTHORITY SECTION: 170.129.in-addr.arpa. 28800 IN NS dns-ext1.dartmouth.edu. 170.129.in-addr.arpa. 28800 IN NS dns-ext3.dartmouth.edu. 170.129.in-addr.arpa. 28800 IN NS dns-ext2.dartmouth.edu. ;; ADDITIONAL SECTION: dns-ext1.dartmouth.edu. 28800 IN A 129.170.170.2 dns-ext2.dartmouth.edu. 28800 IN A 129.170.170.10 dns-ext3.dartmouth.edu. 172800 IN A 132.177.102.11 So, reverse DNS is harder that forward DNS! This happens for a reason: whenever we are supplied any authoritative nameserver names, we must forward-resolve them before we can proceed. If we start down the wrong path with the slower nameservers involved, our original query times out. --------------------[ Making DNS requests from C ]-------------------- The two principal functions for this are gethostbyname() for forward resolution, and gethostbyaddr() for reverse resolution. Calling these from your code will send DNS requests as above to the DNS server configured for your system (look in /etc/resolv.conf). Read the manual pages for these functions. See udpserver.c lookup_and_log_peer() function for an example, with comments. Read up on "struct sockaddr" and "struct sockaddr_in" in my notes http://www.cs.dartmouth.edu/~sergey/cs60/on-sockaddr-structs.txt