Introduction
In today’s connected world, DNS servers play a crucial role in translating human-friendly domain names into IP addresses that computers use to identify each other on the network. While many rely on public DNS services, setting up your own local DNS server can be beneficial for various reasons, including local development, internal network management, and educational purposes.
Development and Testing: Easily create and manage custom domains for testing web applications.
Educational Purposes: Learn how DNS works and experiment with DNS configurations.
Local Network Management: Manage internal domains within a private network.
Create HTML Files
Create two HTML files (a.html
and b.html
) in a directory named html-files
. These files will be served when you access a.com
and b.com
Respectively.
<!-- a.com -->
<!DOCTYPE html>
<html>
<head>
<title>A Page</title>
</head>
<body>
<h1>Welcome to A.com</h1>
</body>
</html>
<!-- b.com -->
<!DOCTYPE html>
<html>
<head>
<title>B Page</title>
</head>
<body>
<h1>Welcome to B.com</h1>
</body>
</html>
Create a Simple HTTP Server
Next, we’ll create a simple HTTP server using Python’s built-in. http.server
Module to serve the HTML files.
#http_server.py
import http.server
import socketserver
PORT = 8080
DIRECTORY = "html-files"
class MyHttpRequestHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
if self.path == '/a.com':
self.path = '/a.html'
elif self.path == '/b.com':
self.path = '/b.html'
return http.server.SimpleHTTPRequestHandler.do_GET(self)
def translate_path(self, path):
return f"./{DIRECTORY}/{path}"
handler_object = MyHttpRequestHandler
my_server = socketserver.TCPServer(("0.0.0.0", PORT), handler_object)
print(f"Serving HTTP on port {PORT}")
my_server.serve_forever()
Explanation
- Imports:
-
http.server
: Provides basic HTTP server functionality. -
socketserver
: A framework for network servers.
-
- Configuration:
-
PORT = 8080
: The server will listen on port 8080. -
DIRECTORY = "html-files"
: The directory where HTML files are stored.
-
- Request Handler:
-
MyHttpRequestHandler
: Custom request handler class that overridesdo_GET
to serve specific files fora.com
andb.com
.
-
- Request Handling:
-
do_GET(self)
: Checks the requested path and maps/a.com
to/a.html
and/b.com
to/b.html
. Thehttp.server.SimpleHTTPRequestHandler
Serves these files. -
translate_path(self, path)
: Translates the requested path to the correct file path within thehtml-files
directory.
-
- Server Setup:
socketserver.TCPServer(("0.0.0.0", PORT), handler_object)
: Binds the server to all available interfaces on port 8080.my_server.serve_forever()
: Starts the server to handle incoming requests indefinitely.
Create a Simple DNS Server
We’ll use the dnslib
Library to create a DNS server that resolves a.com
and b.com
to your local machine’s IP address.
from dnslib import DNSRecord, QTYPE, RR, A, DNSHeader
import socket
import socketserver
# Get the local IP address
hostname = socket.gethostname()
local_ip = socket.gethostbyname(hostname)
# DNS server configuration
DOMAIN_TO_IP = {
'a.com.': local_ip,
'b.com.': local_ip,
}
class DNSHandler(socketserver.BaseRequestHandler):
def handle(self):
data = self.request[0].strip()
socket = self.request[1]
try:
request = DNSRecord.parse(data)
print(f"Received request for: {str(request.q.qname)}")
# Create a DNS response with the same ID and the appropriate flags
reply = DNSRecord(DNSHeader(id=request.header.id, qr=1, aa=1, ra=1), q=request.q)
qname = str(request.q.qname)
qtype = QTYPE[request.q.qtype]
if qname in DOMAIN_TO_IP:
reply.add_answer(RR(qname, QTYPE.A, rdata=A(DOMAIN_TO_IP[qname])))
print(f"Resolved {qname} to {DOMAIN_TO_IP[qname]}")
else:
print(f"No record found for {qname}")
socket.sendto(reply.pack(), self.client_address)
except Exception as e:
print(f"Error handling request: {e}")
if __name__ == "__main__":
server = socketserver.UDPServer(("0.0.0.0", 53), DNSHandler)
print("DNS Server is running...")
server.serve_forever()
Explanation
- Imports:
-
dnslib
: Provides DNS record creation and parsing. -
socket
,socketserver
: Modules for network communication.
-
- Get Local IP Address:
-
hostname = socket.gethostname()
: Retrieves the hostname of the local machine. -
local_ip = socket.gethostbyname(hostname)
: Resolves the hostname to an IP address.
-
- DNS Configuration:
-
DOMAIN_TO_IP
: A dictionary mapping domain names to the local machine’s IP address.
-
- DNS Request Handler:
-
DNSHandler
: Custom request handler class for handling DNS requests.
-
- Request Handling:
-
handle(self)
: Processes incoming DNS requests. -
data = self.request[0].strip()
: Reads the incoming data. -
request = DNSRecord.parse(data)
: Parses the DNS request. -
print(f"Received request for: {str(request.q.qname)}")
: Logs the requested domain. -
DNSRecord(DNSHeader(id=request.header.id, qr=1, aa=1, ra=1), q=request.q)
: Creates a DNS response with the same ID and appropriate flags. -
if qname in DOMAIN_TO_IP
: Checks if the requested domain is in the configuration. -
reply.add_answer(RR(qname, QTYPE.A, rdata=A(DOMAIN_TO_IP[qname])))
: Adds an answer to the DNS response. -
socket.sendto(reply.pack(), self.client_address)
: Sends the DNS response back to the client.
-
- Server Setup:
socketserver.UDPServer(("0.0.0.0", 53), DNSHandler)
: Binds the server to all available interfaces on port 53.server.serve_forever()
: Starts the server to handle incoming requests indefinitely.
Configure Your Devices With New DNS Address(Mobile)
To access a.com
and b.com
from your mobile device, you need to configure your mobile device to use your local DNS server.
On Android:
- Open Wi-Fi Settings:
-
Go to
Settings
>Network & Internet
>Wi-Fi
. -
Tap on the Wi-Fi network you are connected to.
-
- Modify Network:
-
Tap on
Advanced
>IP settings
And change it fromDHCP
toStatic
.
-
- Enter DNS Server Information:
- IP address: Your device's IP address in the same subnet (e.g.,
192.168.1.*
). - Gateway: Your router's IP address (e.g.,
192.168.1.*
). - Network prefix length: Usually
24
. - DNS 1: Your local DNS server IP address (your PC’s IP address, e.g.,
192.168.1.*
). - DNS 2: Leave blank or set to
8.8.8.8
(optional).
- IP address: Your device's IP address in the same subnet (e.g.,
On iOS:
- Open Wi-Fi Settings:
-
Go to
Settings
>Wi-Fi
. -
Tap on the
i
Icon next to the connected network.
-
- Configure DNS:
- Tap on
Configure DNS
. - Select
Manual
. - Add your local DNS server IP address (your Mac’s IP address, e.g.,
192.168.1.*
)
- Tap on
Access the Sites
Open a web browser on your mobile device, and navigate to:
http://a.com:8080
http://b.com:8080
Troubleshooting
Common Issues and Solutions
- DNS Server Not Running:
-
Ensure you are running the DNS server with root privileges.
-
Check for errors in the console output.
-
- Firewall Settings:
-
Ensure your firewall allows incoming connections on ports 53 (DNS) and 8080 (HTTP).
-
- Network Configuration:
-
Verify that your local machine and mobile device are on the same network.
-
- DNS Resolution:
-
Use the dig command to test DNS resolution locally:
dig @127.0.0.1 b.com
-
Conclusion
Setting up a local DNS server with Python is a powerful way to manage custom domains for development and internal network management. By following this guide, you can create a fully functional local DNS server and serve custom HTML pages from local domains accessible from any device on your network.
I hope this article helps you understand the process and benefits of running your own DNS server.
Next, we’ll discuss security considerations when running your own DNS server.
Happy coding!