Information wants to be free...

Python DNS Proxy

Here is a DNS proxy server script written in Python. The purpose is to forward DNS requests to another (real) DNS server, and then send the replies back to the client. No mangling of the UDP datagrams are performed, the packets are simply passing through.

I have only tried to run the script on Linux, and the script will also attempt to find the script host's own DNS server in /etc/resolv.conf to use as the remote DNS server. And since it will attempt to bind on port 53, you will probably have to run the script as root.

Enjoy:

#!/usr/bin/python

from threading import Thread
import socket
import Queue
import time

class Forward(Thread):
    def __init__(self, packet, client_address, dns_ip, reply_queue):
        Thread.__init__(self)
        self.packet = packet
        self.client_address = client_address
        self.dns_ip = dns_ip
        self.dns_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.reply_queue = reply_queue

    def run(self):
        self.dns_sock.sendto(self.packet, (self.dns_ip, 53))
        while True:
            new_packet, address = self.dns_sock.recvfrom(512)
            if address[0] == self.dns_ip:
                self.reply_queue.put((new_packet, self.client_address))
                break

class DNSProxy(object):
    def __init__(self, listen_port, dns_ip):
        self.dns_ip = dns_ip
        self.reply_queue = Queue.Queue()
        self.server_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.server_sock.bind(("", listen_port))
        self.server_sock.setblocking(0)
        print "Using DNS server IP: %s" % (dns_ip)

    def check(self):
        # Check for incoming requests and start threads on them.
        try:
            packet, address = self.server_sock.recvfrom(512)
            if len(packet) > 0:
                print "Recevied request from:", address
                t = Forward(packet, address, self.dns_ip, self.reply_queue)
                t.start()
        except socket.error, e:
            if e[0] != 11 and e[0] != 10035:
                raise

        # Check if any threads has put a reply on the queue to send.
        try:
            packet, address = self.reply_queue.get_nowait()
            self.server_sock.sendto(packet, address)
            print "Sent reply to:", address
        except Queue.Empty:
            pass

def get_dns_server():
    fh = open("/etc/resolv.conf", "r")
    for line in fh:
        if len(line) > 11:
            if line[:11] == "nameserver ":
                return line[11:].strip()
    fh.close()

if __name__ == "__main__":
    proxy = DNSProxy(53, get_dns_server())
    while True:
        proxy.check()
        time.sleep(0.01)
          


Topic: Scripts and Code, by Kjetil @ 25/12-2011, Article Link