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:
-
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.
-
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 callbind
, you should be able to specifyINADDR6_ANY
as your desired IP address and your program will receive all IP packets destined for your VM on port 53. -
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