Homework 4: Implementing a Recursive DNS resolver

In this homework, you’ll be implementing a recursive dns resolver that can service requests from non-recursive dns clients.

To get started, sync up your checkout of the public repository. The hw4 skeleton code can perform individual DNS queries, but has no logic to conduct a recursive query.

Your responsibility is to modify hw4 to work as follows:

  1. The file root-servers.txt contains a newline-delimited list of DNS root servers. Your server program must use this file to find a working root server to use. You can assume it will always be called root-servers.txt, and exist in the current working directory. If a server does not repond, your code must move on to the next server.

  2. When run as ./hw4 PORTNUM, your code should listen for DNS requests on PORTNUM, perform a recursive resolution to deterine the answer to the client’s request, and respond to the original query with a well formed response. When you call bind, you should be able to specify INADDR6_ANY as your desired IP address and your program will receive all IP packets destined for your VM on port 53.

  3. If your program is working correctly, you should be able to query it with the dig program. Querying for the same domain name to your server or to a different local resolver should return the same results in most cases.

You are obviously not allowed to use gethostbyname or getaddrinfo for this homework, nor are you allowed to execute an external program like host or nslookup or dig from your code.

Hints

To time out on a recv() call (in case you get no answer), use setsockopt() with the SO_RECVTIMEO option. More details about setsockopt() can be found here

Alternatively, you can use alarm(), and set up a handler for SIGALRM. man alarm and man signal will help you set that up. Or try sigitimer().

If you’re using these timer strategies, look for errno==EINTR (for alarm) or errno=EAGAIN (for timeout) when recv() returns -1 to see if you actually timed out. If you want to improve the performance of your code and get a good head start on the next assignment, you can use a select() call (which takes a timeout parameter) instead of the blocking recv().

You can try out the template version like this:

./hw4 -d -n nameserver -i domain

Given the IP address of a recursive nameserver, this program will query that nameserver for the domain in question and print the result.

To find a DNS server to use as nameserver, check your network settings, or run “cat /etc/resolv.conf” on a non-windows machine.

A good first step would be to modify the skeleton code so that it can look up domains without using an external recursive nameserver, e.g. running ./hw4 -i domain outputs the resolved IP address(es). Then it’s a short hop to receiving queries, recursively finding the answer, and then returning the response to the querier.

If you want to use your resolver operationally from another computer, it needs to run on port 53. You’ll need to run as root to bind to port 53 (or any other port < 1024).

To test your program, use command dig @ip:portnum domain to check whether your server resolves domain correctly.

Massive hint: recursion is your friend.

Turn-in instructions

The same rules apply for your submission as for previous homeworks, with one big exception: you are now expected to commit at least 4 revisions, each with significant edits and intelligible commit comments, at least 10 minutes apart, reflecting your progress toward the final turned-in result.

For example, you may commit the template version, a version that is able to read ‘root-servers.txt’, a broken version that doesn’t work at all, a version that can do the v4 but not v6 lookups, a version that handles all but the difficult ds.v6ns.test-ipv6.com example, and the final version. Remember to write a comment that indicates the current status of your project. You are of course welcome to make many more commits if you please.

To see how many commits you have, and to read the messages, use git log.

To see the difference between two revisions, use git diff.

Example use sessions

Program usage and example runs are given below. Implementation of command line option handling is given in the templete.

The server program is running with

sudo ./hw4 53

or

sudo ./hw4 -d 53

Your program should bind to all interfaces (INADDR6_ANY as explained on piazza) and the port number provided as the argument. When you bind in this way, you can use the special hostname localhost, the special addresses 127.0.0.1 and ::1 to query your code from the same machine, as well as your EC2 instance’s external IP to query your code from anywhere else on the internet (e.g. your laptop). For example, the server program is run on your VM, then when you run dig also from your VM, the results should be identical to the responses you get from other recursive nameservers:

edmond$ dig @localhost www.cs.uic.edu

;      DiG 9.8.5-P1      @localhost www.cs.uic.edu
; (1 server found)
;; global options: +cmd
;; Got answer:
;; -  HEADER  - opcode: QUERY, status: NOERROR, id: 31780
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;www.cs.uic.edu.			IN	A

;; ANSWER SECTION:
www.cs.uic.edu.		5	IN	A	131.193.32.29
www.cs.uic.edu.		5	IN	A	131.193.32.29

;; Query time: 21 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Mon Sep 23 13:42:01 CDT 2013
;; MSG SIZE  rcvd: 64

edmond$ dig @127.0.0.1 nonexistent.kaytwo.org

;      DiG 9.8.5-P1      @127.0.0.1 nonexistent.kaytwo.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; -  HEADER  - opcode: QUERY, status: NXDOMAIN, id: 58608
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;nonexistent.kaytwo.org.		IN	A

;; Query time: 45 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Mon Sep 23 13:50:56 CDT 2013
;; MSG SIZE  rcvd: 72

edmond$ dig @127.0.0.1 ds.v6ns.test-ipv6.com

;      DiG 9.8.5-P1      @127.0.0.1 ds.v6ns.test-ipv6.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; -  HEADER  - opcode: QUERY, status: NOERROR, id: 4446
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;ds.v6ns.test-ipv6.com.		IN	A

;; ANSWER SECTION:
ds.v6ns.test-ipv6.com.	5	IN	A	216.218.228.119
ds.v6ns.test-ipv6.com.	5	IN	A	216.218.228.119

;; Query time: 150 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Mon Sep 23 13:52:54 CDT 2013
;; MSG SIZE  rcvd: 71

You can also incorporate the code you write for this assignment into your everyday internet use - if you run your resolver on port 53 and then change the resolver IP in your operating system (see this page for details on how to change it) to your VM’s IP, you can see just how well your code stands up to real world use :).

Grading

These are good domains to try, which you definitely will be tested on. Additional domains may be added if we find something even more interesting than these.

www.domain.invalid
nonexistent.kaytwo.org
www.thelongestdomainnameintheworldandthensomeandthensomemoreandmore.com
ds.v6ns.test-ipv6.com
ipv6.google.com
www.cs.uic.edu
www.yahoo.com.tw
nibbles.cs.uic.edu
www.uic.edu

Grading will be automated. Make sure you receive the correct result while running each example above. Note that there is a parameter -i implemented in the template which you can use to pass a single query to the program. One option is to first implement the resolver as a command line program and then edit it to accept queries from the network rather than the command line. Functioning as a command line program will not be tested during grading though - to receive points for this assignment, your program must respond to dns requests when run as a server.

The following tests will be done manually and points will be deducted if your program can’t handle the corresponding issues.

-0.5: At least four descriptive commits are made -20: Hard codes a recursive nameserver to resolve the name