Lecture Overview
The Internet has two different ways to identify websites. Humans refer to
websites using human-readable names such as google.com and www.usna.edu, while computers refer to websites using IP addresses such
as 172.217.4.174 and 136.160.88.139. The Domain
Name System (DNS) is the protocol
that translates between the two.
In this lecture, we overview how the DNS protocol works.
Domain Hierarchy and Name Servers
Since the Internet has a huge number of hosts, the domain namespace is
structured by a tree-like hierarchy to efficiently handle the name resolution
process. See the picture below (from wiki).
DNS uses a collection of many name servers, which are servers dedicated to
replying to DNS requests. Each name server is responsible for a specific zone
of domains, so that no single server needs to store every domain on the
Internet.
Note:
The domain name and
zone are slightly different. The part of the
namespace
controlled by a name server is called a zone. It is often the
case that a domain name and the zone are the same, but it is not uncommon
that a single zone has multiple subdomains.
If you look at the configuration file of a DNS server, it is essentially about
the zone that it takes care of.
For example, a name server responsible for the .com zone only needs
to answer queries for domains that end in .com. This name server doesn’t need
to store any DNS information related to wikipedia.org. Likewise, a name server
responsible for the usna.edu zone doesn’t need to store any DNS information
related to usma.edu.
DNS Query Process
DNS queries work as follows:
- Your host will make a query to its local DNS server, which is specified by
the network setting of your host.
- Your local DNS server will always start with make a query to a DNS server
for the root zone.
|
[Local DNS Server] IP address of en.wikipedia.org? ---> [root server]
<----- Ask .org server.
[Local DNS Server] IP address of en.wikipedia.org? ---> [.org server]
<----- Ask wikipedia.org server.
[Local DNS Server] IP address of en.wikipedia.org? ---> [wikipedia.org server]
<----- It is 208.80.154.224.
|
- The root will direct the query to one of its child name servers. Then
the local nameserver makes a query to the child name server, and that name
server redirects you to one of its children.
To redirect you to a child name server, the parent name server must provide the
child’s zone (in a form human-readable domain name), and IP address, etc. so
that you can contact that child name server for more information.
-
The process is repeated until you make a query to a name server at the bottom
of the tree, which will return the IP address corresponding to your domain.
- Finally, the Local DNS Server will give the final answer to you.
DNS Message Format
DNS is designed to be lightweight and fast. It uses UDP and has a fairly simple
message format.
You can find more detail in
the RFC document.
[2 bytes] : [2 bytes]
+-------------------+----------------------+
| Identification | Flags |
+-------------------+----------------------+
| # Questions | # Answer RRs |
+-------------------+----------------------+
| # Authority RRs | # Additional RRs |
+------------------------------------------+
| Questions (variable # of RRs) |
+------------------------------------------+
| Answers (variable # of RRs) |
+------------------------------------------+
| Authority (variable # of RRs) |
+------------------------------------------+
| Additional info (variable # of RRs) |
+------------------------------------------+
|
-
Identification (2 bytes): It is randomly selected per query and used to match
requests to responses. When a DNS query is sent, the ID field is filled with
random bits. Since UDP is stateless, the DNS response must send back the same
bits in the ID field so that the original query sender knows which DNS query
the response corresponds to.
Suppose that an adversary somehow knows this ID field. What can he do?
-
Flags (2 bytes): They specify whether the message is a query or a response, as
well as whether the query was successful (e.g. the NOERROR flag is set in the
reply if the query succeeded, the NXDOMAIN flag is set in the reply if the
query asked about a non-existent name).
|
Structure of resource records
Each RR is a key-value pair with an associated type.
- A a DNS record key is a 3-tuple (Name, Class, Type):
- Name is the actual key data
- Class is always IN, which stands for "Internet".
- Type specifies the record type.
There are two main types of records in DNS.
- A type: A stands for "address". A type records map domains to IP
addresses.
- NS type: NS stands for "name server". NS type records provide the
authoritative name servers responsible for the queried name (i.e., zone).
- A DNS record value contains (TTL, Value):
- TTL is the time-to-live (how long, in seconds, the record can be cached)
- Value is the actual value data.
Side notes: more details about DNS flags
In this lecture, we will see the 2 byte field of flags in more detail.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QR| Opcode |AA|TC|RD|RA|Z |AD|CD| RCODE |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
- QR: A one bit field that specifies whether this message is a query (0), or
a response (1). Obviously, you should use 0 for your requests, and expect to
see a 1 in the response you receive.
- OPCODE: A four bit field that specifies kind of query in this message. You
should use 0, representing a standard query.
- AA (Authoritative Answer): this bit is only meaningful in responses, and
specifies that the responding name server is an authority for the domain name
in question section. You should use this bit to report whether or not the
response you receive is authoritative.
- TC (TrunCation): specifies that this message was truncated. For this project, you must exit and return
an error if you receive a response that is truncated.
- RD (Recursion Desired): this bit directs the name server to pursue the
query recursively. You should use 1, representing that you desire recursion.
- RA (Recursion Available): this bit is set or cleared in a response, and it
denotes whether recursive query support is available in the name server.
Recursive query support is optional.
- Z: Reserved for future use. You must set this field to 0.
- AD (Authentic Data): indicates the resolver believes the responses to be
authentic - that is, validated by DNSSEC which is a secure DNS protocol we will cover
later.
- CD (Checking Disabled): indicates a security-aware resolver should disable
signature validation (that is, not check DNSSEC records).
- RCODE (Response code): this 4 bit field is set as part of responses. The
values have the following interpretation:
- 0 No error condition
- 1 Format error - The name server was unable to interpret the query.
- 2 Server failure - The name server was unable to process this query due to a problem with the name server.
- 3 Name Error - Meaningful only for responses from an authoritative
name server, this code signifies that the domain name referenced in the query
does not exist.
- 4 Not Implemented - The name server does not support the requested kind of query.
- 5 Refused - The name server refuses to perform the specified operation for policy reasons.
Checking Actual Messages
We will use dig command to see the messages. The command has an
option (using the @ symbol) to specify what DNS server to send a query to.
The USNA firewall blocks DNS messages, so the following commands will not work
out. To test the commands, we recommend the
Google clould shell.
Step 1: Asking a root name server
For a root name server, we use a.root-servers.net (as you can
guess, b.root-servers.net is also a root name server).
First check out the flags.
- "qr": This flag is set (to be 1), and it means it's an answer (not a query).
- "rd": This means recursion desired.
Recall that the answer has four sections: Question, Answer, Authority,
Additional.
|
$ dig @a.root-servers.net en.wikipedia.org
; <<>> DiG 9.11.3-1ubuntu1.13-Ubuntu <<>> @a.root-servers.net en.wikipedia.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 39058
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 6, ADDITIONAL: 13
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;en.wikipedia.org. IN A
;; AUTHORITY SECTION:
org. 172800 IN NS d0.org.afilias-nst.org.
org. 172800 IN NS a0.org.afilias-nst.info.
...(omitted)...
org. 172800 IN NS b2.org.afilias-nst.org.
;; ADDITIONAL SECTION:
d0.org.afilias-nst.org. 172800 IN A 199.19.57.1
d0.org.afilias-nst.org. 172800 IN AAAA 2001:500:f::1
...(omitted)...
b2.org.afilias-nst.org. 172800 IN A 199.249.120.1
b2.org.afilias-nst.org. 172800 IN AAAA 2001:500:48::1
;; Query time: 10 msec
;; SERVER: 198.41.0.4#53(198.41.0.4)
;; WHEN: Wed Jul 27 15:43:31 EDT 2022
;; MSG SIZE rcvd: 447
|
Step 2: Asking .org name server
$ dig @d0.org.afilias-nst.org en.wikipedia.org
; <<>> DiG 9.11.3-1ubuntu1.13-Ubuntu <<>> @d0.org.afilias-nst.org en.wikipedia.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2344
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 3, ADDITIONAL: 4
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;en.wikipedia.org. IN A
;; AUTHORITY SECTION:
wikipedia.org. 3600 IN NS ns1.wikimedia.org.
wikipedia.org. 3600 IN NS ns2.wikimedia.org.
wikipedia.org. 3600 IN NS ns0.wikimedia.org.
;; ADDITIONAL SECTION:
ns0.wikimedia.org. 3600 IN A 208.80.154.238
ns1.wikimedia.org. 3600 IN A 208.80.153.231
ns2.wikimedia.org. 3600 IN A 91.198.174.239
;; Query time: 82 msec
;; SERVER: 199.19.57.1#53(199.19.57.1)
;; WHEN: Wed Jul 27 16:01:11 EDT 2022
;; MSG SIZE rcvd: 157
As before, this is a redirection answer. We will need to go to
ns1.wikimedia.org and ask once more.
Step 3: Asking wikimedia.org name server
$ dig @ns1.wikimedia.org en.wikipedia.org
; <<>> DiG 9.11.3-1ubuntu1.13-Ubuntu <<>> @ns1.wikimedia.org en.wikipedia.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 27943
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1024
; COOKIE: 2db0c1bf9d498a555cbc370a3e8ec13b (good)
;; QUESTION SECTION:
;en.wikipedia.org. IN A
;; ANSWER SECTION:
en.wikipedia.org. 86400 IN CNAME dyna.wikimedia.org.
;; Query time: 43 msec
;; SERVER: 208.80.153.231#53(208.80.153.231)
;; WHEN: Wed Jul 27 16:03:26 EDT 2022
;; MSG SIZE rcvd: 94
- Note that there is nothing in the authority section, but it has a single
entry in the answer section.
- Interestingly, the answer doesn't give an actual IP address. Rather, it
gives the CNAME (canonical name) of it. This means that
en.wikipedia.org is an alias or nickname. Its actual, canonical
name is dyna.wikimedia.org.
- It also uses a DNS Cookie, which is a lightweight mechanism that provide
limited protection to DNS servers and clients against a variety of
denial-of-service amplification or forgery. However, this optional mechanism
has not been adopted by many DNS systems, and it's known to be marginally
effective.
Final step : Asking wikimedia.org name server once again
Below, we see the actual IP address.
$ dig @ns1.wikimedia.org dyna.wikimedia.org
; <<>> DiG 9.11.3-1ubuntu1.13-Ubuntu <<>> @ns1.wikimedia.org dyna.wikimedia.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19683
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1024
; COOKIE: 6376c9df1b66e06c5d4695d508eddb99 (good)
;; QUESTION SECTION:
;dyna.wikimedia.org. IN A
;; ANSWER SECTION:
dyna.wikimedia.org. 600 IN A 208.80.154.224
;; Query time: 43 msec
;; SERVER: 208.80.153.231#53(208.80.153.231)
;; WHEN: Wed Jul 27 16:05:56 EDT 2022
;; MSG SIZE rcvd: 83
If we don't use @ option, the Local DNS Server does all these steps by itself.
$ dig en.wikipedia.org
; <<>> DiG 9.11.3-1ubuntu1.13-Ubuntu <<>> en.wikipedia.org
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32263
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;en.wikipedia.org. IN A
;; ANSWER SECTION:
en.wikipedia.org. 85815 IN CNAME dyna.wikimedia.org.
dyna.wikimedia.org. 417 IN A 208.80.154.224
;; Query time: 17 msec
;; SERVER: 192.168.1.1#53(192.168.1.1)
;; WHEN: Wed Jul 27 16:12:17 EDT 2022
;; MSG SIZE rcvd: 90
DNS and scapy
Let's write a simple DNS packet sniffer using scapy. The documentation is found in
here.
#!/usr/bin/python3
# sniff_dns.py
from scapy.all import *
def show_pkt(pkt):
if DNS in pkt:
pkt.show2()
sniff(filter="udp", prn=show_pkt, count=2)
|
To avoid additional DNS packets, we directly use the IP address for the @address field.
dig @208.80.153.231 en.wikipedia.org
Note the above corresponds to Step 3.
|
The following is what the sniffer shows:
First packet
...(omitted)...
###[ DNS ]###
id = 50528
qr = 0
opcode = QUERY
aa = 0
tc = 0
rd = 1
ra = 0
z = 0
ad = 1
cd = 0
rcode = ok
qdcount = 1
ancount = 0
nscount = 0
arcount = 1
\qd \
|###[ DNS Question Record ]###
| qname = 'en.wikipedia.org.'
| qtype = A
| qclass = IN
an = None
ns = None
\ar \
|###[ DNS OPT Resource Record ]###
| rrname = '.'
| type = OPT
| rclass = 4096
| extrcode = 0
| version = 0
| z = 0
| rdlen = None
| \rdata \
| |###[ DNS EDNS0 TLV ]###
| | optcode = 10
| | optlen = 8
| | optdata = '\xa38\xfe\xc4\x7fo\x80='
The packets consists of the following:
- id, flags, four counts
- four sections:
qd (question section), an (answer section), ns (authority section), ar (additional section, optional pseudosection).
In particular, optcode 10 in the ar record is about using a DNS Cookie.
|
Second packet
...(omitted)...
###[ DNS ]###
id = 50528
qr = 1
opcode = QUERY
aa = 1
tc = 0
rd = 1
ra = 0
z = 0
ad = 0
cd = 0
rcode = ok
qdcount = 1
ancount = 1
nscount = 0
arcount = 1
\qd \
|###[ DNS Question Record ]###
| qname = 'en.wikipedia.org.'
| qtype = A
| qclass = IN
\an \
|###[ DNS Resource Record ]###
| rrname = 'en.wikipedia.org.'
| type = CNAME
| rclass = IN
| ttl = 86400
| rdlen = None
| rdata = 'dyna.wikimedia.org.'
ns = None
\ar \
|###[ DNS OPT Resource Record ]###
| rrname = '.'
| type = OPT
| rclass = 1024
| extrcode = 0
| version = 0
| z = 0
| rdlen = None
| \rdata \
| |###[ DNS EDNS0 TLV ]###
| | optcode = 10
| | optlen = 16
| | optdata = '\xa38\xfe\xc4\x7fo
\x80=k\x13\xa9.\xe6\xa3\xb2@'
|