Skip to content

Instantly share code, notes, and snippets.

@andreif
Last active May 7, 2024 09:28
Show Gist options
  • Save andreif/6040183 to your computer and use it in GitHub Desktop.
Save andreif/6040183 to your computer and use it in GitHub Desktop.
# Original from http://code.activestate.com/recipes/491264/ (r4)
import socket
class DNSQuery:
def __init__(self, data):
self.data=data
self.dominio=''
tipo = (ord(data[2]) >> 3) & 15 # Opcode bits
if tipo == 0: # Standard query
ini=12
lon=ord(data[ini])
while lon != 0:
self.dominio+=data[ini+1:ini+lon+1]+'.'
ini+=lon+1
lon=ord(data[ini])
def respuesta(self, ip):
packet=''
if self.dominio:
packet+=self.data[:2] + "\x81\x80"
packet+=self.data[4:6] + self.data[4:6] + '\x00\x00\x00\x00' # Questions and Answers Counts
packet+=self.data[12:] # Original Domain Name Question
packet+='\xc0\x0c' # Pointer to domain name
packet+='\x00\x01\x00\x01\x00\x00\x00\x3c\x00\x04' # Response type, ttl and resource data length -> 4 bytes
packet+=str.join('',map(lambda x: chr(int(x)), ip.split('.'))) # 4bytes of IP
return packet
if __name__ == '__main__':
ip='192.168.1.1'
print 'pyminifakeDNS:: dom.query. 60 IN A %s' % ip
udps = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udps.bind(('',53))
try:
while 1:
data, addr = udps.recvfrom(1024)
p=DNSQuery(data)
udps.sendto(p.respuesta(ip), addr)
print 'Respuesta: %s -> %s' % (p.dominio, ip)
except KeyboardInterrupt:
print 'Finalizando'
udps.close()
# coding=utf-8
"""
$ python dns.py
$ dig @0.0.0.0 -p 5053 -t A ietf.org
"""
import socket
import datetime
TTL = 300
IP = '192.168.1.1'
RECORD_TYPES = {
'\x00\x01': 'A',
'\x00\x05': 'CNAME',
'\x00\x0f': 'MX',
'\x00\x02': 'NS',
'\x00\x10': 'TXT',
'\x00\x1c': 'AAAA',
'\x00\xff': 'ANY',
}
class Request(object):
def __init__(self, data):
self.data = data
def get_domain(self):
domain = ''
tipo = (ord(self.data[2]) >> 3) & 15 # Opcode bits
if tipo == 0: # Standard query
ini = 12
lon = ord(self.data[ini])
while lon != 0:
domain += self.data[ini + 1:ini + lon + 1] + '.'
ini += lon + 1
lon = ord(self.data[ini])
return domain
def get_raw_type(self):
return self.data[-4:-2]
def get_type(self):
return RECORD_TYPES.get(self.get_raw_type(), 'Unknown')
def response(self):
return Response(request=self)
def int_to_hex(value, zfill=None):
h = hex(value) # 300 -> '0x12c'
h = h[2:].zfill((zfill or 0) * 2) # '0x12c' -> '00012c' if zfill=3
return h.decode('hex')
def bin_to_hex(value):
# http://stackoverflow.com/questions/2072351/python-conversion-from-binary-string-to-hexadecimal/2072384#2072384
# '0000 0100 1000 1101' -> '\x04\x8d'
value = value.replace(' ', '')
h = '%0*X' % ((len(value) + 3) // 4, int(value, 2))
return h.decode('hex')
class Response(object):
def __init__(self, request):
self.request = request
def __str__(self):
return self.render_packet()
def _get_ip_bytes(self):
return str.join('', map(lambda x: chr(int(x)), IP.split('.')))
def _get_ttl_bytes(self):
return int_to_hex(TTL, zfill=4)
def render_packet(self):
packet = ''
if self.request.get_domain():
d = self.request.data
packet += d[:2] # Transaction ID
flags = ''
flags += '1' # 1=response, 0=query
flags += '0000' # opcode, 0=standard query, 1=inverse query, 2=server status request
flags += '1' # Authoritative Answer
flags += '0' # Trancated response
flags += '0' # Recursion Desired
flags += '0' # Recursion Available
flags += '000' # reserved, have to be 0
flags += '0000' # RCode, 0=no error
packet += bin_to_hex(flags)
packet += d[4:6] # Number of Questions
packet += d[4:6] # Number of Answer RRs
packet += '\x00\x00' # Number of Authority RRs
packet += '\x00\x00' # Number of Additional RRs
packet += d[12:] # Original Domain Name Question
packet += '\xc0\x0c' # NAME (domain)
packet += self.request.get_raw_type() # TYPE
packet += '\x00\x01' # CLASS (Internet)
packet += self._get_ttl_bytes() # TTL time to live
packet += int_to_hex(4, zfill=2) # RDLENGTH
packet += self._get_ip_bytes() # RDATA
return packet
if __name__ == '__main__':
print "Starting name server..."
udps = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udps.bind(('', 5053))
try:
while 1:
data, addr = udps.recvfrom(2048)
p = Request(data)
resp = str(p.response())
udps.sendto(resp, addr)
now = datetime.datetime.utcnow()
cols = [now.strftime('%Y-%m-%d %H:%M:%S.%f')] + list(addr) + [p.get_type(), p.get_domain()]
print ' '.join((unicode(x) for x in cols))
print data.encode('hex')
print resp.encode('hex')
except KeyboardInterrupt:
print "Done."
udps.close()
# Tutorial: http://mysshlog.co.uk/tutorials/Python-Simple-Dns-Server
# Source: http://mysshlog.co.uk/download/Dns_Server_Python.py
## {{{ http://code.activestate.com/recipes/491264/ (r4)
#Edited By MySSHLog.co.uk
import socket
import optparse
import time
class DNSQuery:
def __init__(self, data):
self.data=data
self.dominio=''
self.DnsType=''
HDNS=data[-4:-2].encode("hex")
if HDNS == "0001":
self.DnsType='A'
elif HDNS == "000f":
self.DnsType='MX'
elif HDNS == "0002":
self.DnsType='NS'
elif HDNS == "0010":
self.DnsType="TXT"
else:
self.DnsType="Unknown"
tipo = (ord(data[2]) >> 3) & 15 # Opcode bits
if tipo == 0: # Standard query
ini=12
lon=ord(data[ini])
while lon != 0:
self.dominio+=data[ini+1:ini+lon+1]+'.'
ini+=lon+1
lon=ord(data[ini])
def respuesta(self, ip):
packet=''
if self.dominio:
packet+=self.data[:2] + "\x81\x80"
packet+=self.data[4:6] + self.data[4:6] + '\x00\x00\x00\x00' # Questions and Answers Counts
packet+=self.data[12:] # Original Domain Name Question
packet+='\xc0\x0c' # Pointer to domain name
packet+='\x00\x01\x00\x01\x00\x00\x00\x3c\x00\x04' # Response type, ttl and resource data length -> 4 bytes
packet+=str.join('',map(lambda x: chr(int(x)), ip.split('.'))) # 4bytes of IP
return packet
if __name__ == '__main__':
parser = optparse.OptionParser()
parser.add_option("-f", "--filename", action="store", type="string",dest="SaveFile", help="input a filename to log output too")
(options, args) = parser.parse_args()
ip='192.168.1.1'
print 'pyminifakeDNS:: dom.query. 60 IN A %s' % ip
udps = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udps.bind(('',53))
try:
while 1:
data, addr = udps.recvfrom(1024)
p=DNSQuery(data)
udps.sendto(p.respuesta(ip), addr)
print 'Respuesta: %s -> %s -> %s -> %s' % (addr[0], p.DnsType, p.dominio, ip)
if options.SaveFile:
MyDate=time.strftime('%Y %m %d')
MyTime=time.strftime('%H:%M:%S')
logfile = open(options.SaveFile,"a")
logfile.write('%s,%s,%s,%s,%s,%s\n' % (MyDate,MyTime,addr[0], p.DnsType,p.dominio,ip))
logfile.close
except KeyboardInterrupt:
print 'Finalizando'
udps.close()
## end of http://code.activestate.com/recipes/491264/ }}}
@denvist
Copy link

denvist commented Jun 18, 2019

An error in "gistfile1.py" and "Simple DNS server.py":

Server log:

$ sudo python2 dns.py 
pyminifakeDNS:: dom.query. 60 IN A 192.168.1.1
Respuesta: 127.0.0.1 -> Unknown -> test.ru. -> 192.168.1.1

Client log:

$ dig test.ru @127.0.0.1
;; Warning: Message parser reports malformed message packet.

; <<>> DiG 9.10.6 <<>> test.ru @127.0.0.1 -p 5555
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57386
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: Message has 16 extra bytes at end

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;test.ru.			IN	A
......

And there is no dns response

@ingamedeo
Copy link

Retry with dig test.ru @127.0.0.1 +noedns

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment