Back to Dashboard

Homework 1 DNS Server

Author: Zhen TONG 120090694

Query

The DNS Header is the beginning of DNS messages (both queries and responses). It has:

bytes convert

The purpose of header_to_bytes() method is to convert the attributes of the class instance into a binary representation (a byte sequence)

  1. A tuple named field_tuple is created. This tuple contains values extracted from the instance's attributes, specifically query_id, flags, question_count, answer_count, authority_count, and additional_count. These attributes seem to represent fields in a DNS header.

  2. return struct.pack("!HHHHHH", *field_tuple): This line uses the struct module's pack function to convert the field_tuple into a binary representation. Here's what each part of the format string "!HHHHHH" means:

    • "!" specifies the network byte order (big-endian), ensuring that the data is correctly formatted for network transmission.

    • "H" indicates a 16-bit unsigned short integer (2 bytes). Since there are six "H" format specifiers, this means that the method is expected to pack six 16-bit values.

 

 

 

Question

A DNS question consists of the following components:

Name encoding

The name in question should follow the name encoding format, for example the encoding of "www.example.com" would be b"\x03www\x07example\x03com\x00". Each part of the domain name is with a byte called Length Prefix that represents the length of that part. "\x00" is the null terminator, indicating the end of the domain name.

 

Build Query

Combine the binary representations of the DNS header and DNS question into a single binary message, which is the complete DNS query message that can be used for DNS resolution.

Send Query with Socket

Sending a DNS query using a UDP socket to the Google Public DNS server (8.8.8.8) on port 53 (the standard DNS port), and then receiving the response. Here's a breakdown of the implementation:

  1. Import the socket module: This code relies on the Python socket module to create and manage network sockets.

  2. Create a UDP socket: The socket.socket() function is called to create a socket. It specifies the address family (socket.AF_INET for IPv4) and socket type (socket.SOCK_DGRAM for UDP, which is commonly used for DNS queries).

  3. Send a DNS query: The sock.sendto() method is used to send a DNS query (query) to the IP address "8.8.8.8" and port 53, which is the address for the Google Public DNS server.

  4. Receive the response: The sock.recvfrom() method is used to receive the DNS response, which is stored in the response variable. The maximum size of the response that can be received is limited to 1024 bytes. The _ variable is used to ignore the sender's address information since it's not needed in this code.

  5. Header: If we want to ask for a public server for an domain name, we need to set the flags to RECURSION_DESIRED = 1 << 8, if we want to ask for a root server, then set it to 0.

Response

Parse Header

parse_header(reader) is designed to parse a DNS header from a binary data stream using the struct module. The header is expected to be 12 bytes long, and each of the 6 fields in the header is assumed to be a 2-byte (16-bit) integer. The function reads the binary data from the reader object and returns a DNSHeader object.

DNS Record

The DNS Response includes 5 fields:

Here we mainly focus on 3 types of records parsing, NS (Name Server), A (Address), and CNAME (Canonical Name) records.

  1. A Record (TYPE_A):

    • The code checks if the DNS record type is TYPE_A=1(Address).

    • If it is, it reads the IP address data from the response based on the data length specified in the DNS record. This data represents an IPv4 address.

    • The code then converts the binary IP address data into a human-readable string format (e.g., "192.168.1.1") using the bytes_2_string function.

    • The IP address is stored in the ip variable.

  2. NS Record (TYPE_NS):

    • The code checks if the DNS record type is TYPE_NS=2(Name Server).

    • If it is, it decodes the domain name from the response and converts it to a UTF-8 encoded string. The resulting string represents the name server responsible for the domain.

  3. CNAME Record (TYPE_CNAME):

    • The code checks if the DNS record type is TYPE_CNAME=5(Canonical Name).

    • If it is, it decodes the canonical domain name from the response and converts it to a UTF-8 encoded string. The canonical name is an alias for the original domain.

Parse Domain Name

DNS compression is used to reduce the size of DNS response messages when multiple domain names share common parts. This compression technique is specified in the DNS protocol to improve efficiency.RFC 1035, section 4.1.4

In a DNS response message, when you encounter a length field (e.g., 192) that starts with the bits 11, (11000000 in binary), it signifies a compression pointer. These pointers are used to refer back to previously occurring domain name parts within the same DNS message. The actual domain name data is not repeated; instead, it's referenced by the pointer.

Here's a simplified example to illustrate this concept:

Suppose you have a DNS response message like this:

Now, imagine you have another domain name in the same DNS response that is the same as the first one, like this:

To avoid repeating the same domain name, DNS compression is used. In the second occurrence of the domain name, instead of repeating "03www06example03com00," a compression pointer is used:

 

Resolver

The code uses an iterative process to resolve a domain name to its IP address:

The resolver was initialized with a root name ROOT_SERVER = '198.41.0.4'. Then it send the query to the name server and waiting for the response. After that, the response is packed into a DNSPacket using a parsing rule mentioned previously.

Finally, we get a IP response for the domain.

Run

First, activate the server program with running the python3 code.

Then enter the instruction for query. YOU DON"T NEED TO TYPE dig IT IS GIVEN TO YOU!!!

Ask Public Server for www.example.com

Ask Root Server for www.example.com

Ask Public Server for www.baidu.com

Ask Root Server for www.baidu.com

Cache: If you query the domain you have been queried before.

Reset

exit