Building Your Own DNS Server: A Step-by-Step Guide
Every time you type a website address into your browser, you interact with the Domain Name System (DNS). This system is crucial because it maps human-readable domain names to the IP addresses that computers use to identify each other on the network. In this article, we will delve into what a DNS server is, how it works, and how you can build your own DNS server using Node.js
What is a DNS Server?
A DNS server is essentially a computer server that maintains a database of public IP addresses and their associated hostnames. The primary function of DNS servers is to translate domain names, which are easy for people to remember, into IP addresses, which computers use to establish connections. For example, when you enter www.example.com
into your browser, a DNS server translates that into an IP address like 192.0.2.1
.
How DNS Works
The process begins when you type a domain name into a browser. The browser sends a request to a DNS server to translate the domain into an IP address. Once the IP address is received, the browser can initiate a connection to the server hosting the website content. This entire process is known as a DNS lookup.
Understanding DNS Resolution
DNS Hierarchy and Root Servers
The DNS system is structured hierarchically. At the top level are the root servers; there are 13 sets of these worldwide. These root servers are pivotal as they manage the database for top-level domains (TLDs) like .com
, .net
, .org
, and newer ones like .app
and .dev
.
Why are there only 13 DNS root server addresses?
A common misconception is that there are only 13 root servers in the world. In reality there are many more, but still only 13 IP addresses used to query the different root server networks. Limitations in the original architecture of DNS require there to be a maximum of 13 server addresses in the root zone. In the early days of the Internet, there was only one server for each of the 13 IP addresses, most of which were located in the United States.
Today each of the 13 IP addresses has several servers, which use Anycast routing to distribute requests based on load and proximity. Right now there are over 600 different DNS root servers distributed across every populated continent on earth.
Example of DNS Resolution
Let’s follow a DNS query step-by-step:
- Query Initiation: A user enters
example.com
into their browser. - Root Server Query: The query first reaches a DNS root server. The root server doesn’t know the exact IP address but knows where to direct the query for
.com
domains. - TLD Server Query: The root server directs the query to a TLD server for
.com
. The TLD server finds the nameserver handling the specific domainexample.com
. - Domain Nameserver Query: The query is then sent to the domain’s nameserver, which could be managed by a service like Google DNS or Cloudflare.
- IP Resolution: The nameserver returns the IP address for
example.com
, which is then used by the browser to connect to the web server and load the website.
Key Players in DNS
Major DNS Providers
Services like Google DNS (8.8.8.8) and Cloudflare (1.1.1.1) are major DNS providers. They offer fast DNS resolution services that are often more reliable and quicker than those provided by local ISPs. These providers also enhance privacy and security features for DNS queries.
DNS Record Types
Common DNS Records
- A Record: Directs a domain to a specific IPv4 address.
- CNAME Record: Points a domain or subdomain to another domain name, allowing for easier management of multiple domain names.
- NS Record: Delegates a domain or subdomain to use particular nameservers, essentially directing where the DNS queries should be sent.
Let’s imagine the DNS infrastructure as a multi-layered directory that guides you to the right location, much like a map or a set of signposts. Here’s a detailed visual explanation for each type of record:
1. A Record (Address Record)
Visual Metaphor: Think of an A record as a home address plaque on a house.
- Purpose: Maps a domain name directly to the numerical IP address of a server where the website is hosted. This is like having a house address that tells people exactly where you live.
Example:
- Domain:
example.com
- A Record: Points to
192.0.2.1
- Illustration: A signpost labeled
example.com
with an arrow pointing straight to a house marked192.0.2.1
.
2. NS Record (Name Server Record)
Visual Metaphor: Consider the NS record as a reference guide available at an information desk.
- Purpose: Delegates a domain or subdomain to a specific DNS server (nameserver). This is akin to asking a help desk which department (or server) can provide information about a particular area.
Example:
- Domain:
subdomain.example.com
- NS Record: Points to
ns1.exampledns.com
- Illustration: An information desk labeled
subdomain.example.com
where the guide hands out a card directing to another deskns1.exampledns.com
, which knows all the details.
3. CNAME Record (Canonical Name Record)
Visual Metaphor: Think of a CNAME record as a nickname or alias used instead of the real name.
- Purpose: Links a domain to another domain, which is resolved to its IP address. It’s like referring to someone by a nickname; when you ask for “Bob,” everyone knows you mean “Robert.”
Example:
- Primary Domain:
realname.com
→ IP:203.0.113.5
- Alias Domain:
nickname.com
- CNAME Record:
nickname.com
points torealname.com
- Illustration: A signpost for
nickname.com
with an arrow pointing to another signpost forrealname.com
, which then points directly to a house with the address203.0.113.5
.
Practical Examples
Using saquib.dev
:
A record
forsaquib.dev
could point to76.76.1.2
.CNAME record
forapp.spotify.com
might point tosaquib.dev
, indicating thatapp.spotify.com
is an alias forsaquib.dev
.NS record
forapp.saquib.dev
could delegate DNS responsibilities tons.myserver.com
, indicating that this server now has authority overapp.saquib.dev
.
Building a DNS Server in Node.js
Setting Up a Basic Server
Here’s a simple script to create a UDP server that listens on port 53, which is the standard port for DNS services:
const dgram = require("node:dgram");
const server = dgram.createSocket("udp4");
server.on("message", (msg, rinfo) => {
console.log({ msg: msg.toString(), rinfo });
});
server.bind(53, () => console.log("DNS Server is running on port 53"));
Handling DNS Queries with dns-packet
To decode DNS queries and send responses, we use the dns-packet
library:
const dnsPacket = require("dns-packet");
const server = dgram.createSocket("udp4");
const db = {
"ksaquib.surge.sh": {
type: 'A',
data: '1.2.3.4'
},
"blog.ksaquib.dev": {
type: 'CNAME',
data: 'saquib.com'
},
};
server.on("message", (msg, rinfo) => {
const incomingReq = dnsPacket.decode(msg);
const ipFromDb = db[incomingReq.questions[0].name];
const ans = dnsPacket.encode({
type: "response",
id: incomingReq.id,
flags: dnsPacket.AUTHORITATIVE_ANSWER,
questions: incomingReq.questions,
answers: [
{
type: ipFromDb.type,
class: "IN",
name: incomingReq.questions[0].name,
data: ipFromDb.data,
},
],
});
server.send(ans, rinfo.port, rinfo.address);
});
server.bind(53, () => console.log("DNS Server is running on port 53"));
Testing Your DNS Server
Using dig
Command
You can test your DNS server with the dig
command as follows:
dig @localhost ksaquib.surge.sh
This command should return the IP address you defined in your database, verifying that your server can handle DNS queries properly.