├── README.md └── SimpleDNSServer.py /README.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | ========================= 3 | 4 | SimpleDNSServer is a simple DNS server which redirects DNS query to hosting machine. It is an enhancement of [miniDNS](http://code.google.com/p/minidns/). 5 | 6 | Additionally, you can specify a hosts file, in which ip address will be chosen instead. 7 | 8 | Python 3.x is being supported on master branch. Please use tag py2.7 for python 2.x。 9 | 10 | -------------------------------------------------------------------------------- /SimpleDNSServer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import socket 5 | import _thread 6 | import re 7 | from datetime import datetime 8 | 9 | # DNSQuery class from http://code.activestate.com/recipes/491264-mini-fake-dns-server/ 10 | class DNSQuery: 11 | def __init__(self, data): 12 | self.data=data 13 | self.domain='' 14 | 15 | tipo = (data[2] >> 3) & 15 # Opcode bits 16 | if tipo == 0: # Standard query 17 | ini=12 18 | lon=data[ini] 19 | while lon != 0: 20 | self.domain+=data[ini+1:ini+lon+1].decode('ascii')+'.' 21 | ini+=lon+1 22 | lon=data[ini] 23 | 24 | def respuesta(self, ip): 25 | packet=b'' 26 | if self.domain: 27 | packet+=self.data[:2] + b'\x81\x80' 28 | packet+=self.data[4:6] + self.data[4:6] + b'\x00\x00\x00\x00' # Questions and Answers Counts 29 | packet+=self.data[12:] # Original Domain Name Question 30 | packet+=b'\xc0\x0c' # Pointer to domain name 31 | packet+=b'\x00\x01\x00\x01\x00\x00\x00\x3c\x00\x04' # Response type, ttl and resource data length -> 4 bytes 32 | packet+=str.join('',map(lambda x: chr(int(x)), ip.split('.'))).encode('latin') # 4bytes of IP 33 | return packet 34 | 35 | 36 | # get_ip_address by domain name 37 | def get_ip_address_by_domain(domain): 38 | ip_address = '127.0.0.1' 39 | domain = domain.rstrip('.') 40 | if domain in host_ip_map: 41 | ip_address = host_ip_map[domain] 42 | else: 43 | list = socket.getaddrinfo(domain, 80) 44 | if len(list) > 0: 45 | ip_address = list[0][4][0] 46 | 47 | return ip_address 48 | 49 | 50 | def usage(): 51 | print("") 52 | print("Usage:") 53 | print("") 54 | print("\t# SimpleDNSServer [hosts file]") 55 | print("") 56 | print("Description:") 57 | print("") 58 | print("\tSimpleDNSServer will redirect DNS query to local machine.") 59 | print("") 60 | print("\tYou can optionally specify a hosts file to the command line:\n") 61 | print("\t\t# SimpleDNSServer hosts\n") 62 | print("\tThe ip address will be chosen prior to system hosts setting and remote dns query from local machine. \n") 63 | print("\tIf SimpleDNSServer and the DNS setting machine are same, you should set an optional DNS server in the DNS setting to avoid DNS query failure caused by redirecting recursively.\n") 64 | print("") 65 | 66 | sys.exit(1) 67 | 68 | def query_and_send_back_ip(data, addr, reqtime): 69 | try: 70 | p=DNSQuery(data) 71 | print('%s Request domain: %s from %s' % (reqtime.strftime("%H:%M:%S.%f"), p.domain, addr[0])) 72 | ip = get_ip_address_by_domain(p.domain) 73 | udps.sendto(p.respuesta(ip), addr) 74 | dis = datetime.now() - reqtime 75 | 76 | print('%s Request from %s cost %s : %s -> %s' % (reqtime.strftime("%H:%M:%S.%f"), addr[0], dis.seconds + dis.microseconds/1000000, p.domain, get_ip_address_by_domain(p.domain))) 77 | except Exception as e: 78 | print('query for:%s error:%s' % (p.domain, e)) 79 | 80 | def get_host_ip_map(hostsfile): 81 | host_ip_map = {} 82 | try: 83 | f = open(hostsfile) 84 | for l in f.readlines(): 85 | if not l.startswith('#'): 86 | addrs = re.findall('[^\s]+', l) 87 | if len(addrs) > 1: 88 | for ad in addrs[1:]: 89 | host_ip_map[ad] = addrs[0] 90 | 91 | except: 92 | pass 93 | finally: 94 | if not f: 95 | f.close() 96 | 97 | return host_ip_map 98 | 99 | 100 | if __name__ == '__main__': 101 | hostsfile = None 102 | host_ip_map = {} 103 | 104 | if len(sys.argv) > 1: 105 | if len(sys.argv) > 2 or sys.argv[-1] == '-h' or sys.argv[-1] == '--help': 106 | usage() 107 | else: 108 | hostsfile = sys.argv[-1] 109 | host_ip_map = get_host_ip_map(hostsfile) 110 | 111 | try: 112 | udps = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 113 | udps.bind(('',53)) 114 | except Exception as e: 115 | print("Failed to create socket on UDP port 53:", e) 116 | sys.exit(1) 117 | 118 | print('SimpleDNSServer :: hosts file -> %s\n' % hostsfile) 119 | 120 | try: 121 | while 1: 122 | data, addr = udps.recvfrom(1024) 123 | _thread.start_new_thread(query_and_send_back_ip, (data, addr, datetime.now())) 124 | except KeyboardInterrupt: 125 | print('\n^C, Exit!') 126 | except Exception as e: 127 | print('\nError: %s' % e) 128 | finally: 129 | udps.close() 130 | 131 | --------------------------------------------------------------------------------