EthicsComputerCourses
Join training +91 90532 10052

DNS

Computer Networks · Lesson 23 of 32 Advanced 7 min read Updated

By the end of this lesson you will be able to explain the DNS hierarchy, follow a lookup from your laptop to an authoritative server step by step, read the common record types, use nslookup and dig, and build a real DNS query message with Python's struct.

People remember names like irctc.co.in; computers need IP addresses. The Domain Name System (DNS, RFC 1034 and RFC 1035) is the internet's distributed phone book. Almost every connection you make starts with a DNS lookup, so when DNS fails, "the internet is down" for most people.

Names are a hierarchy

A domain name is read from right to left, from the most general part to the most specific:

www.results.example.ac.in.

PartNameWho runs it
. (the final dot, usually hidden)RootThe root server operators
inTop-level domain (TLD)India's country-code TLD registry
ac.inSecond-level domainUsed for academic institutions in India
example.ac.inThe organisation's domainThe college
results.example.ac.inSubdomainThe college's IT team
wwwA host nameThe college's IT team

No single computer holds every name. Each level only knows who is responsible for the level below it. Handing responsibility down like this is called delegation.

Think of finding a student in a big university. You ask the main office ("Which college?"), the college office ("Which department?"), and the department ("Which classroom?"). Each office only knows the next step.

How a lookup works, step by step

Two kinds of DNS servers take part:

  • A recursive resolver does the hard work for you. It is usually run by your ISP, or a public service, and your device's settings name it.
  • Authoritative servers hold the official answers for a zone (a part of the namespace).

When Ananya types www.example.ac.in:

  1. Her laptop checks its own cache. Nothing there, so it asks the recursive resolver. This is a recursive query: "give me the final answer".
  2. The resolver checks its cache. If empty, it asks a root server: "Where is www.example.ac.in?" The root replies: "I do not know, but here are the servers for .in." This is a referral.
  3. The resolver asks a .in server. It refers the resolver to the servers for ac.in, and so on down to example.ac.in.
  4. The example.ac.in authoritative server gives the final answer: the IP address.
  5. The resolver caches the answer and returns it to the laptop.

Steps 2 to 4 are iterative queries: each server answers with "ask them instead", and the resolver does the walking. Let us simulate it.

PythonRun it yourself
servers = {
    "root":            {"in.": "tld-in"},
    "tld-in":          {"ac.in.": "ac-in"},
    "ac-in":           {"example.ac.in.": "college-ns"},
    "college-ns":      {"www.example.ac.in.": "203.0.113.80"},
}

def resolve(name):
    server = "root"
    while True:
        zone = servers[server]
        if name in zone:
            print(f"  {server:<11} answers: {zone[name]}")
            return zone[name]
        match = [z for z in zone if name.endswith(z)]
        print(f"  {server:<11} refers to: {zone[match[0]]}")
        server = zone[match[0]]

print("Answer:", resolve("www.example.ac.in."))
Output
  root        refers to: tld-in
  tld-in      refers to: ac-in
  ac-in       refers to: college-ns
  college-ns  answers: 203.0.113.80
Answer: 203.0.113.80

There are 13 named root server identities (a.root-servers.net to m.root-servers.net), but each is served by many machines around the world, including several in India, using anycast: the same address announced from many places, so your query reaches a nearby copy.

Caching and TTL

If every lookup went to the root, the root servers would melt. Instead, every answer comes with a TTL (time to live), in seconds. Resolvers and devices keep the answer that long and reuse it.

PythonRun it yourself
cache = {}

def lookup(name, now):
    if name in cache and cache[name][1] > now:
        return f"t={now:>4}s {name}: {cache[name][0]} (cached, {cache[name][1] - now}s left)"
    ip, ttl = "203.0.113.80", 300            # pretend we asked the servers
    cache[name] = (ip, now + ttl)
    return f"t={now:>4}s {name}: {ip} (asked servers, TTL {ttl})"

for t in [0, 60, 299, 300, 450]:
    print(lookup("www.example.ac.in", t))
Output
t=   0s www.example.ac.in: 203.0.113.80 (asked servers, TTL 300)
t=  60s www.example.ac.in: 203.0.113.80 (cached, 240s left)
t= 299s www.example.ac.in: 203.0.113.80 (cached, 1s left)
t= 300s www.example.ac.in: 203.0.113.80 (asked servers, TTL 300)
t= 450s www.example.ac.in: 203.0.113.80 (cached, 150s left)

Caching is also why a website move takes a while to be seen everywhere: old answers live until their TTL runs out. Administrators lower the TTL a day before a planned move.

Record types

A DNS zone holds resource records. The common types:

TypeHoldsExample value
AAn IPv4 address203.0.113.80
AAAAAn IPv6 address2001:db8::80
CNAMEAn alias pointing to another namewww → example.ac.in
MXThe mail server for the domain, with a priority10 mail.example.ac.in
NSThe authoritative name servers of the zonens1.example.ac.in
TXTFree text, used for email security (SPF, DMARC) and domain verification"v=spf1 ..."
PTRReverse lookup: IP address to name80.113.0.203.in-addr.arpa → www.example.ac.in
SOAStart of authority: zone serial number and timersone per zone

Looking up names yourself

On Windows (and also Linux and macOS), use nslookup. Your addresses will differ; these are documentation addresses.

PowerShell
nslookup www.example.ac.in
Output
Server:  resolver.example.net
Address:  192.0.2.53

Non-authoritative answer:
Name:    www.example.ac.in
Addresses:  2001:db8::80
          203.0.113.80

"Non-authoritative" means the answer came from the resolver's cache, not directly from the zone's own server. On Linux and macOS, dig gives more detail, including the TTL:

Terminal
dig www.example.ac.in A +noall +answer
Output
www.example.ac.in.	300	IN	A	203.0.113.80

Inside a DNS query message

DNS messages normally travel over UDP port 53. Large answers and zone transfers between servers use TCP port 53. A query has a 12-byte header and a question. Names are encoded as labels: each part prefixed by its length, ending with a zero byte.

PythonRun it yourself
import struct

def build_query(name, qtype=1, query_id=0x2b1a):
    header = struct.pack("!HHHHHH", query_id, 0x0100, 1, 0, 0, 0)   # RD flag, 1 question
    qname = b"".join(bytes([len(p)]) + p.encode() for p in name.split(".")) + b"\x00"
    return header + qname + struct.pack("!HH", qtype, 1)            # type A, class IN

q = build_query("www.example.ac.in")
print(len(q), "bytes")
print(q.hex(" "))
Output
35 bytes
2b 1a 01 00 00 01 00 00 00 00 00 00 03 77 77 77 07 65 78 61 6d 70 6c 65 02 61 63 02 69 6e 00 00 01 00 01

Find the labels: 03 77 77 77 is length 3 then "www"; 07 then "example"; 02 "ac"; 02 "in"; 00 ends the name. Wireshark shows exactly these bytes when you capture a real lookup.

Common mistakes

  • Thinking one server knows all names. DNS is spread across millions of servers through delegation.
  • Confusing recursive and iterative. Your device asks the resolver recursively; the resolver asks the hierarchy iteratively.
  • Pointing a CNAME at an IP address. A CNAME must point to another name. Use an A or AAAA record for addresses.
  • Expecting changes to appear instantly. Cached answers last until their TTL expires.

Key points

  • DNS maps names to addresses using a hierarchy: root, TLD, second-level domain and below.
  • A recursive resolver walks the hierarchy with iterative queries and returns the final answer.
  • Answers are cached for their TTL, which keeps DNS fast and the root lightly loaded.
  • Know A, AAAA, CNAME, MX, NS, TXT, PTR and SOA records.
  • DNS uses UDP port 53 for normal queries and TCP port 53 for large responses and zone transfers.
  • DoH and DoT encrypt queries; DNSSEC signs answers.

Exercise

Add a CNAME step to the resolver simulation: make college-ns answer www.example.ac.in. with "CNAME portal.example.ac.in.", and add portal.example.ac.in. with the address 203.0.113.90. Change resolve so that when the answer starts with CNAME, it starts again from the root with the new name.

Show solution
Python
servers = {
    "root": {"in.": "tld-in"},
    "tld-in": {"ac.in.": "ac-in"},
    "ac-in": {"example.ac.in.": "college-ns"},
    "college-ns": {"www.example.ac.in.": "CNAME portal.example.ac.in.",
                   "portal.example.ac.in.": "203.0.113.90"},
}

def resolve(name):
    server = "root"
    while True:
        zone = servers[server]
        if name in zone:
            answer = zone[name]
            print(f"  {server} answers {name}: {answer}")
            if answer.startswith("CNAME "):
                name, server = answer.split()[1], "root"
                continue
            return answer
        server = zone[[z for z in zone if name.endswith(z)][0]]

print("Final:", resolve("www.example.ac.in."))
Output
  college-ns answers www.example.ac.in.: CNAME portal.example.ac.in.
  college-ns answers portal.example.ac.in.: 203.0.113.90
Final: 203.0.113.90

A CNAME says "this name is really that name", so the resolver restarts with the new name. Real resolvers often get both records in one reply when they are in the same zone.

Quick Quiz

1. Which record type holds an IPv6 address?

2. What does the root server return for a query about www.example.ac.in?

3. Which transport and port does a normal DNS query use?

4. Which record tells other servers where to deliver email for a domain?

Learn Computer Networks with a mentor

Free lessons get you started. Our Computer Science Fundamentals training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call