DNS Client

tags: C++ Computer Network Socket programming

About

The Domain Name System is a hierarchical and distributed naming system for computers, services, and other resources in the Internet or other Internet Protocol networks. It associates various information with domain names assigned to each of the associated entities.

Paul Mockapetris, a UCI alumnus, is the inventor of the Domain Name System (DNS) and his creation of DNS is one of the reasons that we have been able to scale the Internet to as we know it today!

Aim

Computer Networks is not really one of my greatest strengths, and what could be better (and cooler) than getting my hands dirty and implementing my own DNS server!

This project is a basic implementation of a DNS server with the goal of understanding how DNS works, what do the DNS question and response packets look like, and playing around with the dig and nc commands.

Implementation

- Understanding the DNS protocol

  1. Understanding the DNS packets

    To achieve this, I went through the DNS RFC (Pg 25) to understand the message sections and their respective formats

  2. Inspecting DNS packets

    To understand the DNS packets in practice, I used the nc command to listen to a port, and the dig command with the +noedns flag to create a query packet

# this records the query packet in query.txt
nc -u -l 1053 > query.txt
dig -p 1053 @127.0.0.1 +noedns google.com  # lookup google.com's IP

Then, I used this query packet to record a response packet by sending the same query to Google’s DNS

# this records the response packet in response.txt
nc -u 8.8.8.8 53 < query.txt > response.txt  # 53 is the DNS port

To inspect the query and response packets, I used the following commands:

hexdump -C query.txt
hexdump -C response.txt
  1. Parsing the response packet

    Once I had inspected the DNS packets, the next step was parsing the DNS response packet. DNS RFC (Pg 26-29) mention the Header, Question and Record formats. With the help of this specification, I was able to parse the response packets.
Parsing DNS response packet
Parsed DNS Response Packet


- Building a DNS stub resolver

A DNS stub resolver serves as an intermediary between the application requiring DNS resolution, and a recursive DNS resolver.

  1. Forming Query and Response packets

    As I already knew the format of the different sections, I now added writing functionality to support formation of query and response packets in addition to simply reading them.

2.Creating a UDP client

So far, I was able to parse DNS response packets off of the disk. But to build a stub resolver, I implemented a UDP client to send query packets to and receive response packets from a recursive DNS resolver.

DNS Stub resolver resolving a query of record type A
DNS Stub Resolver

In the image “1” represents the record type A.

- Adding Support for other Record Types

Up to this point, the DNS stub resolver only supported records of type A. In this step, I added support for other major record types such as CNAME, NS, MX and AAAA

DNS Stub resolver resolving a query of record type AAAA
DNS Stub Resolver (Type: AAAA)

- Implementing a DNS Proxy Server

To get one step closer to implementing a recursive DNS resolver, I first implemented a simple DNS proxy server. This server acts as a middleman and simply forwards the query to an actual DNS resolver and returns the response back to the client.

# Send a lookup query to the DNS Proxy Server listening on port 1053
dig -p 1053 @127.0.0.1 +noedns google.com  # lookup google.com's IP
DNS Proxy server 1
Proxy server receiving query from client
DNS Proxy server 2
Proxy server forwarding query to DNS resolver
DNS Stub resolver resolving a query of record type AAAA
Proxy server receiving response from DNS resolver and forwarding it to client
DNS Client
DNS client receives response from Proxy server

With this, my DNS proxy server is up and running and can respond to queries with different record types! 🥳 🎉

- Implementing the DNS recursive resolver

This is the final step of this project where I go about implementing a recursive resolver. So far my server was simply a proxy server, dependent on another server to actually perform the lookup. In this step, I recursively resolve a domain starting right from the root servers, resolving the top-level domains first, i.e. when resolving www.google.com, the root servers give me the information about com and tell me where to look next. I perform this lookup recursively till I end up with the entire domain name that the query was searching for.

# Send a lookup query to the DNS Server listening on port 3053
dig +noedns +norecurse @127.0.0.1 -p 3053 www.google.com  # lookup www.google.com's IP
DNS Query
Query for www.google.com received from the client
DNS Lookup 1
Performing the first lookup
DNS Response 1
Response received from the NS: 198.41.0.4
DNS Response 2
A random IP of an A type record is selected to perform the next lookup
DNS Lookup 2
Performing the second lookup
DNS Response 3
Response of the second lookup
DNS Lookup 3
Performing the final lookup
DNS Response 4
IP of www.google.com found and returned to the client!
DNS Client Resp
Client receives the response of the final lookup

What I learned

I gained a lot of knowledge w.r.t. Computer Networks and DNS while working on this project; parsing the DNS packets helped me refresh my bit manipulation skills and I worked with sockets once again while implementing the UDP client.

As I worked on this project independently, I navigated through all the challenges on my own and that helped me boost my confidence in my skills.

Home  |  /projects/ Github Linkedin Email Resume Website Hugo theme by Yukuro