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 overrides do_GET to serve specific files for a.com and b.com. Request Handling: do_GET(self): Checks the requested path and maps /a.com to /a.html and /b.com to /b.html. The http.server.SimpleHTTPRequestHandler Serves these files. translate_path(self, path): Translates the requested path to the correct file path within the html-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 from DHCP to Static. 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). 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.*) 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! 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. Development and Testing Educational Purposes : Learn how DNS works and experiment with DNS configurations. Educational Purposes Local Network Management : Manage internal domains within a private network. Local Network Management 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.html b.html html-files a.com b.com <!-- a.com --> <!DOCTYPE html> <html> <head> <title>A Page</title> </head> <body> <h1>Welcome to A.com</h1> </body> </html> <!-- 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> <!-- 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 #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() #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 overrides do_GET to serve specific files for a.com and b.com. Request Handling: do_GET(self): Checks the requested path and maps /a.com to /a.html and /b.com to /b.html. The http.server.SimpleHTTPRequestHandler Serves these files. translate_path(self, path): Translates the requested path to the correct file path within the html-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. Imports : http.server: Provides basic HTTP server functionality. socketserver: A framework for network servers. Imports http.server: Provides basic HTTP server functionality. socketserver: A framework for network servers. http.server: Provides basic HTTP server functionality. http.server : Provides basic HTTP server functionality. http.server socketserver: A framework for network servers. socketserver : A framework for network servers. socketserver Configuration : PORT = 8080: The server will listen on port 8080. DIRECTORY = "html-files": The directory where HTML files are stored. Configuration PORT = 8080: The server will listen on port 8080. DIRECTORY = "html-files": The directory where HTML files are stored. PORT = 8080: The server will listen on port 8080. PORT = 8080 : The server will listen on port 8080. PORT = 8080 DIRECTORY = "html-files": The directory where HTML files are stored. DIRECTORY = "html-files" : The directory where HTML files are stored. DIRECTORY = "html-files" Request Handler : MyHttpRequestHandler: Custom request handler class that overrides do_GET to serve specific files for a.com and b.com. Request Handler MyHttpRequestHandler: Custom request handler class that overrides do_GET to serve specific files for a.com and b.com. MyHttpRequestHandler: Custom request handler class that overrides do_GET to serve specific files for a.com and b.com. MyHttpRequestHandler : Custom request handler class that overrides do_GET to serve specific files for a.com and b.com . MyHttpRequestHandler do_GET a.com b.com Request Handling : do_GET(self): Checks the requested path and maps /a.com to /a.html and /b.com to /b.html. The http.server.SimpleHTTPRequestHandler Serves these files. translate_path(self, path): Translates the requested path to the correct file path within the html-files directory. Request Handling do_GET(self): Checks the requested path and maps /a.com to /a.html and /b.com to /b.html. The http.server.SimpleHTTPRequestHandler Serves these files. translate_path(self, path): Translates the requested path to the correct file path within the html-files directory. do_GET(self): Checks the requested path and maps /a.com to /a.html and /b.com to /b.html. The http.server.SimpleHTTPRequestHandler Serves these files. do_GET(self) : Checks the requested path and maps /a.com to /a.html and /b.com to /b.html . The http.server.SimpleHTTPRequestHandler Serves these files. do_GET(self) /a.com /a.html /b.com /b.html http.server.SimpleHTTPRequestHandler translate_path(self, path): Translates the requested path to the correct file path within the html-files directory. translate_path(self, path) : Translates the requested path to the correct file path within the html-files directory. translate_path(self, path) html-files 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. 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. socketserver.TCPServer(("0.0.0.0", PORT), handler_object) : Binds the server to all available interfaces on port 8080. socketserver.TCPServer(("0.0.0.0", PORT), handler_object) my_server.serve_forever() : Starts the server to handle incoming requests indefinitely. my_server.serve_forever() 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. dnslib a.com b.com 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() 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. Imports : dnslib: Provides DNS record creation and parsing. socket, socketserver: Modules for network communication. Imports dnslib: Provides DNS record creation and parsing. socket, socketserver: Modules for network communication. dnslib: Provides DNS record creation and parsing. dnslib : Provides DNS record creation and parsing. dnslib socket, socketserver: Modules for network communication. socket , socketserver : Modules for network communication. socket socketserver 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. 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. hostname = socket.gethostname(): Retrieves the hostname of the local machine. hostname = socket.gethostname() : Retrieves the hostname of the local machine. hostname = socket.gethostname() local_ip = socket.gethostbyname(hostname): Resolves the hostname to an IP address. local_ip = socket.gethostbyname(hostname) : Resolves the hostname to an IP address. local_ip = socket.gethostbyname(hostname) DNS Configuration : DOMAIN_TO_IP: A dictionary mapping domain names to the local machine’s IP address. DNS Configuration DOMAIN_TO_IP: A dictionary mapping domain names to the local machine’s IP address. DOMAIN_TO_IP: A dictionary mapping domain names to the local machine’s IP address. DOMAIN_TO_IP : A dictionary mapping domain names to the local machine’s IP address. DOMAIN_TO_IP DNS Request Handler : DNSHandler: Custom request handler class for handling DNS requests. DNS Request Handler DNSHandler: Custom request handler class for handling DNS requests. DNSHandler: Custom request handler class for handling DNS requests. DNSHandler : Custom request handler class for handling DNS requests. DNSHandler 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. 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. handle(self): Processes incoming DNS requests. handle(self) : Processes incoming DNS requests. handle(self) data = self.request[0].strip(): Reads the incoming data. data = self.request[0].strip() : Reads the incoming data. data = self.request[0].strip() request = DNSRecord.parse(data): Parses the DNS request. request = DNSRecord.parse(data) : Parses the DNS request. request = DNSRecord.parse(data) print(f"Received request for: {str(request.q.qname)}"): Logs the requested domain. print(f"Received request for: {str(request.q.qname)}") : Logs the requested domain. print(f"Received request for: {str(request.q.qname)}") 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. 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. DNSRecord(DNSHeader(id=request.header.id, qr=1, aa=1, ra=1), q=request.q) if qname in DOMAIN_TO_IP: Checks if the requested domain is in the configuration. if qname in DOMAIN_TO_IP : Checks if the requested domain is in the configuration. if qname in DOMAIN_TO_IP reply.add_answer(RR(qname, QTYPE.A, rdata=A(DOMAIN_TO_IP[qname]))): Adds an answer to the DNS response. reply.add_answer(RR(qname, QTYPE.A, rdata=A(DOMAIN_TO_IP[qname]))) : Adds an answer to the DNS response. reply.add_answer(RR(qname, QTYPE.A, rdata=A(DOMAIN_TO_IP[qname]))) socket.sendto(reply.pack(), self.client_address): Sends the DNS response back to the client. socket.sendto(reply.pack(), self.client_address) : Sends the DNS response back to the client. socket.sendto(reply.pack(), self.client_address) 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. 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. socketserver.UDPServer(("0.0.0.0", 53), DNSHandler) : Binds the server to all available interfaces on port 53. socketserver.UDPServer(("0.0.0.0", 53), DNSHandler) server.serve_forever() : Starts the server to handle incoming requests indefinitely. server.serve_forever() 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. a.com b.com 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 from DHCP to Static. 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). Open Wi-Fi Settings : Go to Settings > Network & Internet > Wi-Fi. Tap on the Wi-Fi network you are connected to. Open Wi-Fi Settings Go to Settings > Network & Internet > Wi-Fi. Tap on the Wi-Fi network you are connected to. Go to Settings > Network & Internet > Wi-Fi. Go to Settings > Network & Internet > Wi-Fi . Settings Network & Internet Wi-Fi Tap on the Wi-Fi network you are connected to. Tap on the Wi-Fi network you are connected to. Modify Network : Tap on Advanced > IP settings And change it from DHCP to Static. Modify Network Tap on Advanced > IP settings And change it from DHCP to Static. Tap on Advanced > IP settings And change it from DHCP to Static. Tap on Advanced > IP settings And change it from DHCP to Static . Advanced IP settings DHCP Static 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). 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., 192.168.1.* ). IP address 192.168.1.* Gateway : Your router's IP address (e.g., 192.168.1.* ). Gateway 192.168.1.* Network prefix length : Usually 24 . Network prefix length 24 DNS 1 : Your local DNS server IP address (your PC’s IP address, e.g., 192.168.1.* ). DNS 1 192.168.1.* DNS 2 : Leave blank or set to 8.8.8.8 (optional). DNS 2 8.8.8.8 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.*) Open Wi-Fi Settings : Go to Settings > Wi-Fi. Tap on the i Icon next to the connected network. Open Wi-Fi Settings Go to Settings > Wi-Fi. Tap on the i Icon next to the connected network. Go to Settings > Wi-Fi. Go to Settings > Wi-Fi . Settings Wi-Fi Tap on the i Icon next to the connected network. Tap on the i Icon next to the connected network. i 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.*) 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 Configure DNS . Configure DNS Select Manual . Manual Add your local DNS server IP address (your Mac’s IP address, e.g., 192.168.1.* ) 192.168.1.* Access the Sites Open a web browser on your mobile device, and navigate to: http://a.com:8080 http://b.com:8080 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 DNS Server Not Running : Ensure you are running the DNS server with root privileges. Check for errors in the console output. DNS Server Not Running Ensure you are running the DNS server with root privileges. Check for errors in the console output. Ensure you are running the DNS server with root privileges. Ensure you are running the DNS server with root privileges. Check for errors in the console output. Check for errors in the console output. Firewall Settings : Ensure your firewall allows incoming connections on ports 53 (DNS) and 8080 (HTTP). Firewall Settings Ensure your firewall allows incoming connections on ports 53 (DNS) and 8080 (HTTP). Ensure your firewall allows incoming connections on ports 53 (DNS) and 8080 (HTTP). 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. Network Configuration Verify that your local machine and mobile device are on the same network. Verify that your local machine and mobile device are on the same network. 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 DNS Resolution Use the dig command to test DNS resolution locally: dig @127.0.0.1 b.com Use the dig command to test DNS resolution locally: dig @127.0.0.1 b.com Use the dig command to test DNS resolution locally: dig @127.0.0.1 b.com 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!