DEV Community

Naman Raj
Naman Raj

Posted on

16 1

DNS Explained: From Basics to Building My Own DNS Server

Hey everyone! πŸ‘‹ I'm Naman, a full-stack developer passionate about networking and system design. Recently, I built my own DNS server using Go, and in this blog, I'll walk you through the fundamentals of DNS and how did i create my own DNS server from scratch. Let's dive in!

🌍 What is DNS and Why is it Important?

DNS (Domain Name System) is like the phonebook of the internet. It translates human-friendly domain names (like google.com) into machine-readable IP addresses (like 142.250.182.206).

Imagine if every time you wanted to visit a website, you had to type in a long, numeric IP address. DNS eliminates that hassle by handling the lookup for you!

πŸ’‘ Example:
When you type namanraj.tech in your browser:

  • Your computer asks a DNS server: "What is the IP address of namanraj.tech?"
  • The DNS server replies with the correct IP (e.g., 76.76.21.93).
  • Your browser connects to that IP and loads the website.

πŸ”„ How DNS Works (Step-by-Step Resolution Process)

1. Stub Resolver Initiates the Query

  • The Stub Resolver is a small built-in DNS client in your operating system (Windows, Linux, macOS).

  • Its job is to check if the domain’s IP is already cached in your local system.

  • If the IP is not found, the stub resolver forwards the request to a Recursive DNS Resolver for further lookup

2. Recursive Resolver Handles the Query

  • The Recursive Resolver is a DNS server that helps find the IP address on behalf of your device.
  • Common recursive resolvers include:
    • Google Public DNS (8.8.8.8)
    • Cloudflare DNS (1.1.1.1)
    • Your ISP’s DNS resolver

3. Query to the Root DNS Server

  • The recursive resolver first asks a Root Name Server for guidance.
  • What is a Root Server?
    • Root servers are the highest level in the DNS hierarchy, acting as the entry point for all DNS lookups.
    • There are 13 root servers worldwide, and they know where to find the TLD (Top-Level Domain) servers for different domains.
    • Since the root server does not store individual website IPs, it replies:
    • "I don’t have the IP for namanraj.tech, but ask a .tech TLD server"

Here is a dig command that queries the root DNS server (a.root-servers.net) for the nameservers (NS records) responsible for the com top-level domain (TLD). It essentially asks the root server which DNS servers handle requests for domain names ending in .com.

Image description

It returns the list of nameservers that are authoritative for the com TLD. These are the servers that handle DNS queries for domains ending in .com.

4. Query to the TLD Name Server

  • The recursive resolver now asks the TLD Name Server for .tech
  • What is a TLD Server?
    • A Top-Level Domain (TLD) Name Server manages all domains with a specific extension, like:
    • .com domains β†’ Managed by .com TLD servers
    • .org domains β†’ Managed by .org TLD servers
    • .tech domains β†’ Managed by .tech TLD servers
    • Since the TLD server still does not store the IP of namanraj.tech, it responds:
      • "I don’t have the IP, but ask the Authoritative Name Server for namanraj.tech"

This dig command queries the DNS server at a.gtld-servers.net for the nameservers (NS records) responsible for the domain namanraj.tech. It essentially asks the a.gtld-servers.net server which DNS servers handle requests for namanraj.tech.

Image description

It will list the nameservers that are authoritative for the namanraj.tech domain. These are the servers that handle DNS queries for namanraj.tech

AUTHORITY SECTION: If the DNS server does not have the direct answer, it may provide a list of nameservers that are closer to the authoritative source

5. Query to the Authoritative Name Server

  • The recursive resolver now queries the Authoritative Name Server.
  • What is an Authoritative Name Server?
    • It is the final authority for a domain.
    • It stores and manages DNS records (A, CNAME, MX, etc.) for a domain (we will come to this later).
  • The authoritative name server for namanraj.tech (e.g., ns1.vercel-dns.com) replies:
    • "The IP address for namanraj.tech is 76.76.21.93."

This dig command queries the DNS system to find the nameservers responsible for the domain namanraj.tech. It asks which DNS servers handle requests for this domain.

Image description

It will give the list of nameservers that are associated with namanraj.tech domain that stores and manages DNS records. Here, they are:

  • ns2.vercel-dns.com
  • ns1.vercel-dns.com

6. Recursive Resolver Returns the IP

  • The recursive resolver caches the response for future use.
  • It sends the final IP address (76.76.21.93) back to your device.
  • Now that your device knows the correct IP, it sends an HTTP request to 76.76.21.93.
  • The web server at that IP responds, and namanraj.tech loads in your browser!

πŸ› οΈ Visualizing DNS: The Complete Resolution Flow (Step-by-Step)

Let's say you visit namanraj.tech , here's what happens :

Image description

πŸ—‚οΈ Types of DNS Records

DNS records define how a domain functions by mapping domain names to IP addresses, handling emails, and managing domain settings. Here are the most important ones:

1. A Record (Address Record) 🏠

  • Maps a domain to an IPv4 address, allowing browsers to locate websites.
  • Example: namanraj.tech β†’ 192.0.2.1
  • Use Case: When a user types namanraj.tech, the A record helps find the web server’s IP.

This dig command dig namanraj.tech A queries the DNS system to find the A (Address) record for the domain namanraj.tech. It asks for the IP address associated with this domain.

Image description

2. AAAA Record (IPv6 Address Record) 🌍

  • Similar to an A record but stores an IPv6 address instead of IPv4.
  • An AAAA (Quad-A) record is similar to an A record but maps a domain to an IPv6 address. IPv6 uses a 128-bit address format, supporting trillions of IP addresses solving the issue of IPv4 exhaustion.
  • Example: google.com β†’ 2607:f8b0:4005:80b::200e
  • Use Case: Websites that support IPv6 use AAAA records for better future-proofing.

Image description

3. CNAME (Canonical Name Record) πŸ”—

  • Creates an alias by pointing one domain to another domain instead of an IP.
  • Example: www.namanraj.tech β†’ namanraj.tech
  • Use Case: Ensures www.namanraj.tech always resolves to the same IP as namanraj.tech without maintaining separate A records.

4. MX Record (Mail Exchange Record) πŸ“§

  • Defines the mail server responsible for handling email delivery for a domain.
  • Example: namanraj.tech β†’ mail.google.com (Priority: 10)
  • Use Case: When someone emails you@namanraj.tech, the MX record ensures it reaches the correct mail server.

Image description

The answer section lists one MX record for google.com:

  • Priority: 10
  • Mail server: smtp.google.com

In MX records, the priority value determines the order of mail servers to be used. Lower values have higher priority. If the primary server is unavailable, the next server with a higher priority value is used.

5. NS Record (Name Server Record) πŸ“‘

  • Specifies the authoritative name servers that manage a domain’s DNS settings.
  • Example: namanraj.tech β†’ ns1.vercel-dns.com, ns2.vercel-dns.com
  • Use Case: These servers handle all DNS queries for namanraj.tech.

Image description

6. TXT Record (Text Record) πŸ”’

A TXT (Text) Record is a DNS record that allows a domain owner to store text-based information in the DNS. Unlike A or MX records, which are used for website and email routing, TXT records do not affect how a domain functions directly. Instead, they are mostly used for verification, security, and authentication purposes

  • Stores arbitrary text, often used for security & authentication (SPF, DKIM, verification).
  • Example: "v=spf1 include:_spf.google.com ~all" (SPF record for email validation)
  • Use Case: Prevents email spoofing and domain ownership verification.

Image description

  • The answer section lists multiple TXT records for google.com, including:
    • google-site-verification values
    • docusign values
    • onetrust-domain-verification value
    • globalsign-smime-dv value
    • v=spf1 include:_spf.google.com ~all (SPF record for email validation)
    • facebook-domain-verification value
    • MS (Microsoft) verification value
    • apple-domain-verification value
    • cisco-ci-domain-verification value

7. SOA Record (Start of Authority) πŸ“œ

The SOA (Start of Authority) record is a special DNS record that provides important administrative details about a domain’s DNS settings. It acts as the "boss" of the DNS zone and ensures everything is properly managed.

  • Contains administrative information about a domain, including the primary DNS server and refresh intervals
  • Example: Primary NS: ns1.vercel-dns.com, Admin: hostmaster.nsone.net
  • Use Case: Ensures DNS records are properly synchronized across name servers.

Image description

⚑ Building My Own DNS Server in Go: A Journey Through Networking Fundamentals πŸš€

To truly understand how DNS works, I decided to build my own custom DNS server in Goβ€”and in this Part, I’ll walk you through how I did it, the challenges I faced, and what I learned along the way.

GitHub Repo (Feel free to star ⭐ or contribute!)

Why Build a DNS Server?

  • How DNS queries/responses work (UDP vs. TCP)
  • Authoritative vs. Recursive DNS (mine is authoritative)
  • Real-world use cases (local development, ad-blocking, privacy)

This project was less about replacing Cloudflare and more about learning by doing.

Setting Up the Project

  • Installing Go & Fiber - Click Here
  • Dependencies Used (github.com/miekg/dns for DNS handling, Fiber for API).
  • In-memory storage (Simple map[string]string for records)
  • Project Structure Overview :
/my-dns-server
│── main.go        # Entry point of the DNS server
│── go.mod         # Dependency management
│── go.sum         # Dependency checksum file
Enter fullscreen mode Exit fullscreen mode

Building the DNS Server: Core Implementation

1. Initializing the DNS Server (startDNSServer)

Before handling requests, we need to launch our DNS server. Here's the foundation:

func startDNSServer() {
    // Set up DNS server
    server := &dns.Server{
        Addr: "0.0.0.0:9090", // Bind to all interfaces
        Net:  "udp",
    }

    // Handle DNS requests
    dns.HandleFunc(".", handleDNSRequest)

    // Start server
    log.Printf("Starting DNS server on port 9090...")
    err := server.ListenAndServe()
    if err != nil {
        log.Fatalf("Failed to start DNS server: %v", err)
    } else {
        log.Printf("DNS server is running on port 9090")
    }
}
Enter fullscreen mode Exit fullscreen mode

Key Decisions:

  • UDP Protocol: Chosen for performance (DNS mostly uses UDP for quick queries)
  • Port 9090: Avoids needing root privileges (vs standard port 53)
  • UDPSize: Supports large DNS responses (like DNSSEC)

Image description

2. Concurrent Operation with Fiber

Since DNS runs continuously, we launch it in a goroutine alongside our Fiber API:

func main() {
    go startDNSServer() // Run DNS in background
    startWebServer()    // Start Fiber API
}
Enter fullscreen mode Exit fullscreen mode

Why This Matters:

  • The DNS server keeps running while:
    • The Fiber API manages records
    • You query DNS from multiple clients simultaneously

3. DNS Server (UDP Port 9090)

The core DNS functionality listens for UDP requests and responds with stored records.

func handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
    m := new(dns.Msg)
    m.SetReply(r)
    m.Authoritative = true // Mark this server as authoritative
    found := false

    for _, q := range r.Question {
        log.Printf("Query: %s (%s)", q.Name, dns.TypeToString[q.Qtype])
        if q.Qtype == dns.TypeA {
            if ip, exists := dnsRecords[q.Name]; exists {
                rr, _ := dns.NewRR(fmt.Sprintf("%s A %s", q.Name, ip))
                m.Answer = append(m.Answer, rr)
                found = true
            }
        }
    }

    if !found {
        m.Rcode = dns.RcodeNameError // NXDOMAIN
    }

    w.WriteMsg(m)
}
Enter fullscreen mode Exit fullscreen mode

Image description

Why Using UDP and not TCP?

DNS primarily uses UDP (User Datagram Protocol) instead of TCP for 3 key reasons:

  • UDP is connectionless (no handshake like TCP).
  • A DNS query + response fits in 1 UDP packet (usually < 512 bytes).
  • UDP’s lightweight nature lets them serve more queries with fewer resources.

4. Web Management API (Fiber, Port 3000)

A simple REST API to add DNS records dynamically:

    // Web interface to manage DNS records
    app.Get("/records", func(c *fiber.Ctx) error {
        return c.JSON(dnsRecords)
    })

    app.Post("/records", func(c *fiber.Ctx) error {
        type Record struct {
            Domain string `json:"domain"`
            IP     string `json:"ip"`
        }

        var record Record
        if err := c.BodyParser(&record); err != nil {
            return c.Status(400).SendString("Bad request")
        }

        // Ensure domain ends with dot (DNS standard)
        if record.Domain[len(record.Domain)-1] != '.' {
            record.Domain += "."
        }

        dnsRecords[record.Domain] = record.IP
        return c.SendString("Record added successfully")
    })
Enter fullscreen mode Exit fullscreen mode

Image description

Challenges & Lessons Learned

1. DNS Queries Are UDP-Based (Mostly)

  • By default, DNS uses UDP (faster for small requests).

2. The Importance of the Trailing Dot

  • "example.com" β‰  "example.com." (fully qualified domain name).

3. Port Binding Permissions

  • Ports below 1024 require admin rights (sudo on Linux).
  • Used port 9090 for testing to avoid permission issues.

4. No Recursion (Yet!)

  • My server is authoritative-only (only answers for records it knows).
  • A recursive resolver would query other DNS servers if it doesn’t have an answer.

Practical Uses for a Custom DNS Server

  • Local Development
  • Ad-Blocking
  • Network Privacy

Try It Yourself!

1. Clone the repo:

git clone https://github.com/Denyme24/my-dns-server.git
cd my-dns-server
go run main.go
Enter fullscreen mode Exit fullscreen mode

2. Start querying!

Final Thoughts

Building a DNS server was a fantastic learning experienceβ€”I now appreciate how much work goes into something we use daily without thinking.

What would you add to this project? Let me know in the comments! πŸš€

(P.S. If you found this useful, consider starring the repo! ⭐)

Heroku

Deploy with ease. Manage efficiently. Scale faster.

Leave the infrastructure headaches to us, while you focus on pushing boundaries, realizing your vision, and making a lasting impression on your users.

Get Started

Top comments (12)

Collapse
 
adityaaa073 profile image
Aditya β€’

it was an interesting read!

Collapse
 
denyme24 profile image
Naman Raj β€’

thanks!!

Collapse
 
siliziummm profile image
silizium β€’

This was very informative. Thank you Naman.

Collapse
 
denyme24 profile image
Naman Raj β€’

glad, you find it informative. will keep writing such blogs.

Collapse
 
gad31 profile image
Gad β€’

Hey, great write up man. you say 'feel free to contribute' on your github. will you consider PRs ?

Collapse
 
denyme24 profile image
Naman Raj β€’

Yeah, the contributions are open for everyone, so go ahead.

Collapse
 
areebahmeddd profile image
Areeb β€’

Really well explained with the snippets! Easy to understand

Collapse
 
denyme24 profile image
Naman Raj β€’

so glad that you actually read the whole blog.

Collapse
 
irina_ecommerce profile image
Irina β€œe-commerce” Iva β€’

Hey, nice!
Do you accept any code improvements if I try?

Collapse
 
denyme24 profile image
Naman Raj β€’

yeah, sure!!

Collapse
 
priyanshukumarsinha profile image
Priyanshu Kumar Sinha β€’

The flow is absolutely well structured. Nice Read.

Collapse
 
denyme24 profile image
Naman Raj β€’

Thanks for the feedback.

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More

πŸ‘‹ Kindness is contagious

Please leave a ❀️ or a friendly comment on this post if you found it helpful!

Okay