Commit 67afeea6 authored by Jim O'Gorman (Kali Developer)'s avatar Jim O'Gorman (Kali Developer)
Browse files

Imported Upstream version 0.2.1

parent 1bd88ea9
Version 0.2.1
* Fixed a Python 2.6 compatibility issue. (thanks Mehran Goudarzi)
Version 0.2
Changes
* Added IPv6 support.
* Added AAAA, MX, CNAME, NS, SOA and NAPTR support.
* Added support for ANY queries (returns all known fake records).
* Changed file format to support more DNS record types.
* Added alternative DNS port support (contributed by fnv).
* Added alternative listening port support for the server (contributed by Mark Straver).
* Updated bundled dnslib library to the latest version - 0.8.2.
* Included IPy library for IPv6 support.
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
[*] Run in MiTM mode and inject fake DNS responses.
No preview for this file type
*.google.com=1.1.1.1
thesprawl.org=2.2.2.2
*.wordpress.*=3.3.3.3
[A] # Queries for IPv4 address records
*.thesprawl.org=192.0.2.1
[AAAA] # Queries for IPv6 address records
*.thesprawl.org=2001:db8::1
[MX] # Queries for mail server records
*.thesprawl.org=mail.fake.com
[NS] # Queries for mail server records
*.thesprawl.org=ns.fake.com
[CNAME] # Queries for alias records
*.thesprawl.org=www.fake.com
[TXT] # Queries for text records
*.thesprawl.org=fake message
[PTR]
*.2.0.192.in-addr.arpa=fake.com
[SOA]
*.thesprawl.org=ns.fake.com. hostmaster.fake.com. 1 10800 3600 604800 3600
[NAPTR]
*.thesprawl.org=100 10 U E2U+sip !^.*$!sip:customer-service@fake.com! .
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
......@@ -35,10 +35,10 @@ class Bimap(object):
raise
def __getitem__(self,k):
return self.lookup(k)
return self.lookup(k,k)
def __getattr__(self,k):
return self.lookup(k)
return self.lookup(k,k)
if __name__ == '__main__':
import doctest
......
......@@ -33,6 +33,12 @@ class Buffer(object):
self.data = data
self.offset = 0
def remaining(self):
"""
Return bytes remaining
"""
return len(self.data) - self.offset
def get(self,len):
"""
Gen len bytes at current offset (& increment offset)
......
# -*- coding: utf-8 -*-
import random,socket,struct
from bit import get_bits,set_bits
from bimap import Bimap
from buffer import Buffer
from label import DNSLabel,DNSLabelError,DNSBuffer
QTYPE = Bimap({1:'A', 2:'NS', 5:'CNAME', 6:'SOA', 12:'PTR', 15:'MX',
......@@ -13,12 +15,12 @@ QTYPE = Bimap({1:'A', 2:'NS', 5:'CNAME', 6:'SOA', 12:'PTR', 15:'MX',
48:'DNSKEY', 49:'DHCID', 50:'NSEC3', 51:'NSEC3PARAM',
55:'HIP', 99:'SPF', 249:'TKEY', 250:'TSIG', 251:'IXFR',
252:'AXFR', 255:'*', 32768:'TA', 32769:'DLV'})
CLASS = Bimap({ 1:'IN', 2:'CS', 3:'CH', 4:'Hesiod', 255:'*'})
CLASS = Bimap({ 1:'IN', 2:'CS', 3:'CH', 4:'Hesiod', 254:'None', 255:'*'})
QR = Bimap({ 0:'QUERY', 1:'RESPONSE' })
RCODE = Bimap({ 0:'None', 1:'Format Error', 2:'Server failure',
3:'Name Error', 4:'Not Implemented', 5:'Refused' })
OPCODE = Bimap({ 0:'QUERY', 1:'IQUERY', 2:'STATUS' })
3:'Name Error', 4:'Not Implemented', 5:'Refused', 6:'YXDOMAIN',
7:'YXRRSET', 8:'NXRRSET', 9:'NOTAUTH', 10:'NOTZONE'})
OPCODE = Bimap({ 0:'QUERY', 1:'IQUERY', 2:'STATUS', 5:'UPDATE' })
class DNSError(Exception):
pass
......@@ -38,9 +40,13 @@ class DNSRecord(object):
* DNSHeader
* DNSQuestion
* RR (resource records)
* RD (resource data - superclass for TXT,A,MX,CNAME,PRT,SOA)
* RD (resource data - superclass for TXT,A,AAAA,MX,CNAME,PRT,SOA,NAPTR)
* DNSLabel (envelope for a DNS label)
The library has (in theory) very rudimentary support for EDNS0 options
however this has not been tested due to a lack of data (anyone wanting
to improve support or provide test data please raise an issue)
Note: In version 0.3 the library was modified to use the DNSLabel class to
support arbirary DNS labels (as specified in RFC2181) - and specifically
to allow embedded '.'s. In most cases this is transparent (DNSLabel will
......@@ -115,14 +121,36 @@ class DNSRecord(object):
Changelog:
0.1 2010-09-19 Initial Release
0.2 2010-09-22 Minor fixes
0.3 2010-10-02 Add DNSLabel class to supportt arbitrary labels (embedded '.')
0.4 2011-10-31 Update DNS record types
* 0.1 2010-09-19 Initial Release
* 0.2 2010-09-22 Minor fixes
* 0.3 2010-10-02 Add DNSLabel class to support arbitrary labels (embedded '.')
* 0.4 2012-02-26 Merge with dbslib-circuits
* 0.5 2012-09-13 Add support for RFC2136 DDNS updates
Patch provided by Wesley Shields <wxs@FreeBSD.org> - thanks
* 0.6 2012-10-20 Basic AAAA support
* 0.7 2012-10-20 Add initial EDNS0 support (untested)
* 0.8 2012-11-04 Add support for NAPTR, Authority RR and additional RR
Patch provided by Stefan Andersson (https://bitbucket.org/norox) - thanks
* 0.8.1 2012-11-05 Added NAPTR test case and fixed logic error
Patch provided by Stefan Andersson (https://bitbucket.org/norox) - thanks
* 0.8.2 2012-11-11 Patch to fix IPv6 formatting
Patch provided by Torbjörn Lönnemark (https://bitbucket.org/tobbezz) - thanks
License:
* BSD
Author:
* Paul Chakravarti (paul.chakravarti@gmail.com)
Master Repository/Issues:
* https://bitbucket.org/paulc/dnslib
"""
version = "0.4.0"
version = "0.8.2"
@classmethod
def parse(cls,packet):
......@@ -133,19 +161,27 @@ class DNSRecord(object):
header = DNSHeader.parse(buffer)
questions = []
rr = []
ns = []
ar = []
for i in range(header.q):
questions.append(DNSQuestion.parse(buffer))
for i in range(header.a):
rr.append(RR.parse(buffer))
return cls(header,questions,rr)
for i in range(header.ns):
ns.append(RR.parse(buffer))
for i in range(header.ar):
ar.append(RR.parse(buffer))
return cls(header,questions,rr,ns=ns,ar=ar)
def __init__(self,header=None,questions=None,rr=None,q=None,a=None):
def __init__(self,header=None,questions=None,rr=None,q=None,a=None,ns=None,ar=None):
"""
Create DNSRecord
"""
self.header = header or DNSHeader()
self.questions = questions or []
self.rr = rr or []
self.ns = ns or []
self.ar = ar or []
# Shortcuts to add a single Question/Answer
if q:
self.questions.append(q)
......@@ -154,9 +190,11 @@ class DNSRecord(object):
self.set_header_qa()
def reply(self,data="",ra=1,aa=1):
answer = RDMAP.get(QTYPE[self.q.qtype],RD)(data)
return DNSRecord(DNSHeader(id=self.header.id,bitmap=self.header.bitmap,qr=1,ra=ra,aa=aa),
q=self.q,
a=RR(self.q.qname,self.q.qtype,rdata=RDMAP[QTYPE[self.q.qtype]](data)))
a=RR(self.q.qname,self.q.qtype,rdata=answer))
def add_question(self,q):
self.questions.append(q)
......@@ -166,9 +204,19 @@ class DNSRecord(object):
self.rr.append(rr)
self.set_header_qa()
def add_ns(self,ns):
self.ns.append(ns)
self.set_header_qa()
def add_ar(self,ar):
self.ar.append(ar)
self.set_header_qa()
def set_header_qa(self):
self.header.q = len(self.questions)
self.header.a = len(self.rr)
self.header.ns = len(self.ns)
self.header.ar = len(self.ar)
# Shortcut to get first question
def get_q(self):
......@@ -188,6 +236,10 @@ class DNSRecord(object):
q.pack(buffer)
for rr in self.rr:
rr.pack(buffer)
for ns in self.ns:
ns.pack(buffer)
for ar in self.ar:
ar.pack(buffer)
return buffer.data
def send(self,dest,port=53):
......@@ -201,6 +253,8 @@ class DNSRecord(object):
sections = [ str(self.header) ]
sections.extend([str(q) for q in self.questions])
sections.extend([str(rr) for rr in self.rr])
sections.extend([str(rr) for rr in self.ns])
sections.extend([str(rr) for rr in self.ar])
return "\n".join(sections)
class DNSHeader(object):
......@@ -304,14 +358,24 @@ class DNSHeader(object):
self.tc and 'TC',
self.rd and 'RD',
self.ra and 'RA' ]
if OPCODE[self.opcode] == 'UPDATE':
f1='zo'
f2='pr'
f3='up'
f4='ad'
else:
f1='q'
f2='a'
f3='ns'
f4='ar'
return "<DNS Header: id=0x%x type=%s opcode=%s flags=%s " \
"rcode=%s q=%d a=%d ns=%d ar=%d>" % (
"rcode=%s %s=%d %s=%d %s=%d %s=%d>" % (
self.id,
QR[self.qr],
OPCODE[self.opcode],
",".join(filter(None,f)),
RCODE[self.rcode],
self.q, self.a, self.ns, self.ar )
f1, self.q, f2, self.a, f3, self.ns, f4, self.ar )
class DNSQuestion(object):
......@@ -345,17 +409,31 @@ class DNSQuestion(object):
return "<DNS Question: %r qtype=%s qclass=%s>" % (
self.qname, QTYPE[self.qtype], CLASS[self.qclass])
class EDNSOption(object):
def __init__(self,code,data):
self.code = code
self.data = data
def __str__(self):
return "<EDNS Option: Code=%d Data=%s>" % (self.code,self.data)
class RR(object):
@classmethod
def parse(cls,buffer):
rname = buffer.decode_name()
rtype,rclass,ttl,rdlength = buffer.unpack("!HHIH")
type = QTYPE[rtype]
try:
rdata = RDMAP[type].parse(buffer,rdlength)
except KeyError:
rdata = RD.parse(buffer,rdlength)
if rtype == QTYPE.OPT:
options = []
option_buffer = Buffer(buffer.get(rdlength))
while option_buffer.remaining() > 4:
code,length = option_buffer.unpack("!HH")
data = option_buffer.get(length)
options.append(EDNSOption(code,data))
rdata = options
else:
rdata = RDMAP.get(QTYPE[rtype],RD).parse(buffer,rdlength)
return cls(rname,rtype,rclass,ttl,rdata)
def __init__(self,rname=[],rtype=1,rclass=1,ttl=0,rdata=None):
......@@ -388,8 +466,8 @@ class RR(object):
def __str__(self):
return "<DNS RR: %r rtype=%s rclass=%s ttl=%d rdata='%s'>" % (
self.rname, QTYPE.lookup(self.rtype,self.rtype),
CLASS[self.rclass], self.ttl, self.rdata)
self.rname, QTYPE[self.rtype], CLASS[self.rclass],
self.ttl, self.rdata)
class RD(object):
......@@ -437,6 +515,25 @@ class A(RD):
def pack(self,buffer):
buffer.pack("!BBBB",*map(int,self.data.split(".")))
class AAAA(RD):
"""
Basic support for AAAA record - assumes IPv6 address data is presented
as a simple tuple of 16 bytes
"""
@classmethod
def parse(cls,buffer,length):
data = buffer.unpack("!16B")
return cls(data)
def pack(self,buffer):
buffer.pack("!16B",*self.data)
def __str__(self):
hexes = map('{:02x}'.format, self.data)
return ':'.join([''.join(hexes[i:i+2]) for i in xrange(0, len(hexes), 2)])
class MX(RD):
@classmethod
......@@ -544,8 +641,46 @@ class SOA(RD):
def __str__(self):
return "%s:%s:%s" % (self.mname,self.rname,":".join(map(str,self.times)))
RDMAP = { 'CNAME':CNAME, 'A':A, 'TXT':TXT, 'MX':MX,
'PTR':PTR, 'SOA':SOA, 'NS':NS }
class NAPTR(RD):
def __init__(self,order,preference,flags,service,regexp,replacement=None):
self.order = order
self.preference = preference
self.flags = flags
self.service = service
self.regexp = regexp
self.replacement = replacement or DNSLabel([])
@classmethod
def parse(cls, buffer, length):
order, preference = buffer.unpack('!HH')
(length,) = buffer.unpack('!B')
flags = buffer.get(length)
(length,) = buffer.unpack('!B')
service = buffer.get(length)
(length,) = buffer.unpack('!B')
regexp = buffer.get(length)
replacement = buffer.decode_name()
return cls(order, preference, flags, service, regexp, replacement)
def pack(self, buffer):
buffer.pack('!HH', self.order, self.preference)
buffer.pack('!B', len(self.flags))
buffer.append(self.flags)
buffer.pack('!B', len(self.service))
buffer.append(self.service)
buffer.pack('!B', len(self.regexp))
buffer.append(self.regexp)
buffer.encode_name(self.replacement)
def __str__(self):
return '%d %d "%s" "%s" "%s" %s' %(
self.order,self.preference,self.flags,
self.service,self.regexp,self.replacement or '.'
)
RDMAP = { 'CNAME':CNAME, 'A':A, 'AAAA':AAAA, 'TXT':TXT, 'MX':MX,
'PTR':PTR, 'SOA':SOA, 'NS':NS, 'NAPTR': NAPTR}
def test_unpack(s):
"""
......@@ -617,6 +752,20 @@ def test_unpack(s):
<DNS Header: id=0x28fb type=RESPONSE opcode=QUERY flags=RD,RA rcode=None q=1 a=1 ns=0 ar=0>
<DNS Question: 'google.com' qtype=SOA qclass=IN>
<DNS RR: 'google.com' rtype=SOA rclass=IN ttl=5 rdata='ns1.google.com:dns-admin.google.com:2008110701:7200:1800:1209600:300'>
Standard query response NAPTR sip2sip.info
>>> unpack('740481800001000300000000077369703273697004696e666f0000230001c00c0023000100000c940027001e00640173075349502b44325500045f736970045f756470077369703273697004696e666f00c00c0023000100000c940027000a00640173075349502b44325400045f736970045f746370077369703273697004696e666f00c00c0023000100000c94002900140064017308534950532b44325400055f73697073045f746370077369703273697004696e666f00')
<DNS Header: id=0x7404 type=RESPONSE opcode=QUERY flags=RD,RA rcode=None q=1 a=3 ns=0 ar=0>
<DNS Question: 'sip2sip.info' qtype=NAPTR qclass=IN>
<DNS RR: 'sip2sip.info' rtype=NAPTR rclass=IN ttl=3220 rdata='30 100 "s" "SIP+D2U" "" _sip._udp.sip2sip.info'>
<DNS RR: 'sip2sip.info' rtype=NAPTR rclass=IN ttl=3220 rdata='10 100 "s" "SIP+D2T" "" _sip._tcp.sip2sip.info'>
<DNS RR: 'sip2sip.info' rtype=NAPTR rclass=IN ttl=3220 rdata='20 100 "s" "SIPS+D2T" "" _sips._tcp.sip2sip.info'>
Standard query response NAPTR 0.0.0.0.1.1.1.3.9.3.0.1.8.7.8.e164.org
>>> unpack('aef0818000010001000000000130013001300130013101310131013301390133013001310138013701380465313634036f72670000230001c00c002300010000a6a300320064000a0175074532552b53495022215e5c2b3f282e2a2924217369703a5c5c31406677642e70756c7665722e636f6d2100')
<DNS Header: id=0xaef0 type=RESPONSE opcode=QUERY flags=RD,RA rcode=None q=1 a=1 ns=0 ar=0>
<DNS Question: '0.0.0.0.1.1.1.3.9.3.0.1.8.7.8.e164.org' qtype=NAPTR qclass=IN>
<DNS RR: '0.0.0.0.1.1.1.3.9.3.0.1.8.7.8.e164.org' rtype=NAPTR rclass=IN ttl=42659 rdata='100 10 "u" "E2U+SIP" "!^\+?(.*)$!sip:\\\\1@fwd.pulver.com!" .'>
"""
pass
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment