The goal of this assignment is to build a UDP client program that performs queries against a DNS server. You will use a number of addressing functions. You will work with a complex protocol. You already have a skeletal UDP client from the previous assignment to build on.
In all problems you should make an effort to handle errors in a reasonable way, they will be tested with invalid inputs or usage. Give the user a meaningful message if they misuse the program or an error occurs.
The sample input/output shown in the problem statements are not an exhaustive list of tests, you should invent and try others.
Your code should have some comments. They should indicate who wrote the program, what the purpose of the program is, how to run the program and explain what any important or potentially confusing code is doing.
Assignment Due: Wed 13 Oct
To submit: submit cs491n dns <list of files>
The <list of files> should include all your source files (.c and .h) as well as the Makefile you used to build the program (if you used one). If you didn't use a Makefile, include comments or a README indicating how you built it.
Write a DNS Client program in C using UDP sockets. It should allow the user to request DNS resolution of one or more hostnames to IP addresses.
Your program should take one or more arguments, a list of hostnames that the user wants to have resolved to a hostname/IP pair. It should also take some optional switches as shown below.
The -n switch will tell the client which nameserver to consult for answers when doing DNS queries. By default, the program should speak to the campus nameserver, umbc3.umbc.edu/22.214.171.124, for any UDP DNS requests. The user may specify an alternate nameserver by IP or hostname with this switch.
The -v switch should turn on "verbose" mode in the client. Normally the user only sees their requested output and maybe some error messages. If -v is activated, they should receive some additional debugging messages about what is going on during the program's run. The messages you present are at your discretion.
sample runs $ dnscli www.gl.umbc.edu using nameserver umbc3.umbc.edu (126.96.36.199) # simplest operation Question was: www.gl.umbc.edu Answer: www.gl.umbc.edu Addr: 188.8.131.52 $ dnscli -n umbc5.umbc.edu www.gl.umbc.edu # -n by hostname using nameserver umbc5.umbc.edu (184.108.40.206) Question was: www.gl.umbc.edu Answer: www.gl.umbc.edu Addr: 220.127.116.11 $ dnscli -n 18.104.22.168 www.gl.umbc.edu # -n by IP using nameserver umbc5.umbc.edu (22.214.171.124) Question was: www.gl.umbc.edu Answer: www.gl.umbc.edu Addr: 126.96.36.199 $ dnscli -v www.fark.com # -v, with what I chose to output verbose mode on using nameserver umbc3.umbc.edu (188.8.131.52) === starting query for www.fark.com sent 30 bytes recv'd 82 bytes 12 02 80 80 00 01 00 01 00 02 00 00 03 77 77 77 04 66 61 72 6b 03 63 6f 6d 00 00 01 00 01 c0 0c 00 01 00 01 00 00 6c 56 00 04 d1 32 ec 64 c0 10 00 02 00 01 00 00 a8 ad 00 06 03 6e 73 31 c0 10 c0 10 00 02 00 01 00 00 a8 ad 00 06 03 6e 73 32 c0 10 recv'd 1 questions and 1 answers Question was: www.fark.com RR type 1 =========== Answer: www.fark.com Addr: 184.108.40.206 $ dnscli www.gl.umbc.edu www.somethingawful.com # multiple queries iterated using nameserver umbc3.umbc.edu (220.127.116.11) Question was: www.gl.umbc.edu Answer: www.gl.umbc.edu Addr: 18.104.22.168 Question was: www.somethingawful.com Answer: www.somethingawful.com Addr: 22.214.171.124 $ dnscli www.google.com # more than one answer using nameserver umbc3.umbc.edu (126.96.36.199) Question was: www.google.com Answer: www.google.akadns.net Addr: 188.8.131.52 Answer: www.google.akadns.net Addr: 184.108.40.206
Your output should be similar to that shown above, but you can take liberties with the format of the information. Just make sure the name/ip pair for the nameserver queried is obvious, as is the pairs returned from the query.
Intially, your program needs to decide which nameserver to consult. By default, you talk to the campus server mentioned above. If overriden by -n, you talk to another. You should tell the user which server you are consulting in any case, by naming the hostname and IP of the server in the output. You may use gethostbyname() and gethostbyaddr() and other DNS-aware functions in this part of the program. When resolving the name/ip of the nameserver, just one answer is required in cases where the nameserver might have a lot of aliases or other data in the struct hostent returned, just take the first ones you find.
When you go to look up the requested hosts, you will do so by constructing one or more UDP DNS requests to the server and sending them. The received responses should be parsed for the answers. The user should see neatly-readable output telling them the hostname/IP pairs for all the requested hosts it was able to resolve, and messages about those that could not be found.
In DNS terminology, your program will build Question(s) to put inside a DNS query message, and receive back some number of Resource Records in another message. We are interested in parsing the Answers RR's for our information, for this assignment we won't bother with the Authority or Additional RR's (unless you are curious). We want to find any Answer RR's containing A Records with the IPs of the hostnames we queried for.
You should parse the entire Answers section of the response for all the different IPs that are given for each response. You may make all your queries in the same message, or perform multiple queries, one for each host, your choice. If you send different messages for each, consider using the Identification field of the DNS message to help you sort out the responses.
If you query a for a "big" site like www.yahoo.com or www.google.com, you will get a big response, with many records in the return. Queries for a smaller site like www.gl.umbc.edu will yield a simpler response to parse. Also, you won't always get the same response from the same query each time.
You can assume the server will not send back a horribly-malformed response. But some responses are complex, with many RR's. What kind of response do you receive when you query for a host that doesn't exist? The user should be told you couldn't find that one.
You will need to be able to encode hostnames in the funny format DNS specifies for your query. The harder part is parsing the response's coding of hostnames. The compression scheme they use involves pointers to other parts of the response to avoid sending duplicate information.
It is useful to have a packet sniffer running when you send a query to see if it is (1) going out at all, and (2) is formatted properly. If you don't have this, then try printing a hex/ascii dump of the data you sent/received to study.
You should consider using getopt() to parse the commandline switches.
Some other, NOT required things you might want to try: