1f7d24e3fSHanoh Haim## This file is part of Scapy
2f7d24e3fSHanoh Haim## See http://www.secdev.org/projects/scapy for more informations
3f7d24e3fSHanoh Haim## Copyright (C) Philippe Biondi <phil@secdev.org>
4f7d24e3fSHanoh Haim## This program is published under a GPLv2 license
5f7d24e3fSHanoh Haim
6f7d24e3fSHanoh Haim"""
7f7d24e3fSHanoh HaimDNS: Domain Name System.
8f7d24e3fSHanoh Haim"""
9f7d24e3fSHanoh Haim
10f7d24e3fSHanoh Haimimport socket,struct
11f7d24e3fSHanoh Haim
12f7d24e3fSHanoh Haimfrom scapy.packet import *
13f7d24e3fSHanoh Haimfrom scapy.fields import *
14f7d24e3fSHanoh Haimfrom scapy.ansmachine import *
15f7d24e3fSHanoh Haimfrom scapy.layers.inet import IP, UDP
16f7d24e3fSHanoh Haim
17f7d24e3fSHanoh Haimclass DNSStrField(StrField):
18f7d24e3fSHanoh Haim
19f7d24e3fSHanoh Haim    def h2i(self, pkt, x):
20f7d24e3fSHanoh Haim      if x == "":
21f7d24e3fSHanoh Haim        return "."
22f7d24e3fSHanoh Haim      return x
23f7d24e3fSHanoh Haim
24f7d24e3fSHanoh Haim    def i2m(self, pkt, x):
25f7d24e3fSHanoh Haim        if x == ".":
26f7d24e3fSHanoh Haim          return "\x00"
27f7d24e3fSHanoh Haim
28f7d24e3fSHanoh Haim        x = [k[:63] for k in x.split(".")] # Truncate chunks that cannot be encoded (more than 63 bytes..)
29f7d24e3fSHanoh Haim        x = map(lambda y: chr(len(y))+y, x)
30f7d24e3fSHanoh Haim        x = "".join(x)
31f7d24e3fSHanoh Haim        if x[-1] != "\x00":
32f7d24e3fSHanoh Haim            x += "\x00"
33f7d24e3fSHanoh Haim        return x
34f7d24e3fSHanoh Haim
35f7d24e3fSHanoh Haim    def getfield(self, pkt, s):
36f7d24e3fSHanoh Haim        n = ""
37f7d24e3fSHanoh Haim
38f7d24e3fSHanoh Haim        if ord(s[0]) == 0:
39f7d24e3fSHanoh Haim          return s[1:], "."
40f7d24e3fSHanoh Haim
41f7d24e3fSHanoh Haim        while 1:
42f7d24e3fSHanoh Haim            l = ord(s[0])
43f7d24e3fSHanoh Haim            s = s[1:]
44f7d24e3fSHanoh Haim            if not l:
45f7d24e3fSHanoh Haim                break
46f7d24e3fSHanoh Haim            if l & 0xc0:
47f7d24e3fSHanoh Haim                raise Scapy_Exception("DNS message can't be compressed at this point!")
48f7d24e3fSHanoh Haim            else:
49f7d24e3fSHanoh Haim                n += s[:l]+"."
50f7d24e3fSHanoh Haim                s = s[l:]
51f7d24e3fSHanoh Haim        return s, n
52f7d24e3fSHanoh Haim
53f7d24e3fSHanoh Haim
54f7d24e3fSHanoh Haimclass DNSRRCountField(ShortField):
55f7d24e3fSHanoh Haim    holds_packets=1
56f7d24e3fSHanoh Haim    def __init__(self, name, default, rr):
57f7d24e3fSHanoh Haim        ShortField.__init__(self, name, default)
58f7d24e3fSHanoh Haim        self.rr = rr
59f7d24e3fSHanoh Haim    def _countRR(self, pkt):
60f7d24e3fSHanoh Haim        x = getattr(pkt,self.rr)
61f7d24e3fSHanoh Haim        i = 0
62f7d24e3fSHanoh Haim        while isinstance(x, DNSRR) or isinstance(x, DNSQR) or isdnssecRR(x):
63f7d24e3fSHanoh Haim            x = x.payload
64f7d24e3fSHanoh Haim            i += 1
65f7d24e3fSHanoh Haim        return i
66f7d24e3fSHanoh Haim
67f7d24e3fSHanoh Haim    def i2m(self, pkt, x):
68f7d24e3fSHanoh Haim        if x is None:
69f7d24e3fSHanoh Haim            x = self._countRR(pkt)
70f7d24e3fSHanoh Haim        return x
71f7d24e3fSHanoh Haim    def i2h(self, pkt, x):
72f7d24e3fSHanoh Haim        if x is None:
73f7d24e3fSHanoh Haim            x = self._countRR(pkt)
74f7d24e3fSHanoh Haim        return x
75f7d24e3fSHanoh Haim
76f7d24e3fSHanoh Haim
77f7d24e3fSHanoh Haimdef DNSgetstr(s,p):
78f7d24e3fSHanoh Haim    name = ""
79f7d24e3fSHanoh Haim    q = 0
80f7d24e3fSHanoh Haim    jpath = [p]
81f7d24e3fSHanoh Haim    while 1:
82f7d24e3fSHanoh Haim        if p >= len(s):
83f7d24e3fSHanoh Haim            warning("DNS RR prematured end (ofs=%i, len=%i)"%(p,len(s)))
84f7d24e3fSHanoh Haim            break
85f7d24e3fSHanoh Haim        l = ord(s[p])
86f7d24e3fSHanoh Haim        p += 1
87f7d24e3fSHanoh Haim        if l & 0xc0:
88f7d24e3fSHanoh Haim            if not q:
89f7d24e3fSHanoh Haim                q = p+1
90f7d24e3fSHanoh Haim            if p >= len(s):
91f7d24e3fSHanoh Haim                warning("DNS incomplete jump token at (ofs=%i)" % p)
92f7d24e3fSHanoh Haim                break
93f7d24e3fSHanoh Haim            p = ((l & 0x3f) << 8) + ord(s[p]) - 12
94f7d24e3fSHanoh Haim            if p in jpath:
95f7d24e3fSHanoh Haim                warning("DNS decompression loop detected")
96f7d24e3fSHanoh Haim                break
97f7d24e3fSHanoh Haim            jpath.append(p)
98f7d24e3fSHanoh Haim            continue
99f7d24e3fSHanoh Haim        elif l > 0:
100f7d24e3fSHanoh Haim            name += s[p:p+l]+"."
101f7d24e3fSHanoh Haim            p += l
102f7d24e3fSHanoh Haim            continue
103f7d24e3fSHanoh Haim        break
104f7d24e3fSHanoh Haim    if q:
105f7d24e3fSHanoh Haim        p = q
106f7d24e3fSHanoh Haim    return name,p
107f7d24e3fSHanoh Haim
108f7d24e3fSHanoh Haim
109f7d24e3fSHanoh Haimclass DNSRRField(StrField):
110f7d24e3fSHanoh Haim    holds_packets=1
111f7d24e3fSHanoh Haim    def __init__(self, name, countfld, passon=1):
112f7d24e3fSHanoh Haim        StrField.__init__(self, name, None)
113f7d24e3fSHanoh Haim        self.countfld = countfld
114f7d24e3fSHanoh Haim        self.passon = passon
115f7d24e3fSHanoh Haim    def i2m(self, pkt, x):
116f7d24e3fSHanoh Haim        if x is None:
117f7d24e3fSHanoh Haim            return ""
118f7d24e3fSHanoh Haim        return str(x)
119f7d24e3fSHanoh Haim    def decodeRR(self, name, s, p):
120f7d24e3fSHanoh Haim        ret = s[p:p+10]
121f7d24e3fSHanoh Haim        type,cls,ttl,rdlen = struct.unpack("!HHIH", ret)
122f7d24e3fSHanoh Haim        p += 10
123f7d24e3fSHanoh Haim        rr = DNSRR("\x00"+ret+s[p:p+rdlen])
124f7d24e3fSHanoh Haim        if type in [2, 3, 4, 5]:
125f7d24e3fSHanoh Haim            rr.rdata = DNSgetstr(s,p)[0]
126f7d24e3fSHanoh Haim            del(rr.rdlen)
127f7d24e3fSHanoh Haim        elif type in dnsRRdispatcher.keys():
128f7d24e3fSHanoh Haim            rr = dnsRRdispatcher[type]("\x00"+ret+s[p:p+rdlen])
129f7d24e3fSHanoh Haim	else:
130f7d24e3fSHanoh Haim          del(rr.rdlen)
131f7d24e3fSHanoh Haim
132f7d24e3fSHanoh Haim        p += rdlen
133f7d24e3fSHanoh Haim
134f7d24e3fSHanoh Haim        rr.rrname = name
135f7d24e3fSHanoh Haim        return rr,p
136f7d24e3fSHanoh Haim    def getfield(self, pkt, s):
137f7d24e3fSHanoh Haim        if type(s) is tuple :
138f7d24e3fSHanoh Haim            s,p = s
139f7d24e3fSHanoh Haim        else:
140f7d24e3fSHanoh Haim            p = 0
141f7d24e3fSHanoh Haim        ret = None
142f7d24e3fSHanoh Haim        c = getattr(pkt, self.countfld)
143f7d24e3fSHanoh Haim        if c > len(s):
144f7d24e3fSHanoh Haim            warning("wrong value: DNS.%s=%i" % (self.countfld,c))
145f7d24e3fSHanoh Haim            return s,""
146f7d24e3fSHanoh Haim        while c:
147f7d24e3fSHanoh Haim            c -= 1
148f7d24e3fSHanoh Haim            name,p = DNSgetstr(s,p)
149f7d24e3fSHanoh Haim            rr,p = self.decodeRR(name, s, p)
150f7d24e3fSHanoh Haim            if ret is None:
151f7d24e3fSHanoh Haim                ret = rr
152f7d24e3fSHanoh Haim            else:
153f7d24e3fSHanoh Haim                ret.add_payload(rr)
154f7d24e3fSHanoh Haim        if self.passon:
155f7d24e3fSHanoh Haim            return (s,p),ret
156f7d24e3fSHanoh Haim        else:
157f7d24e3fSHanoh Haim            return s[p:],ret
158f7d24e3fSHanoh Haim
159f7d24e3fSHanoh Haim
160f7d24e3fSHanoh Haimclass DNSQRField(DNSRRField):
161f7d24e3fSHanoh Haim    holds_packets=1
162f7d24e3fSHanoh Haim    def decodeRR(self, name, s, p):
163f7d24e3fSHanoh Haim        ret = s[p:p+4]
164f7d24e3fSHanoh Haim        p += 4
165f7d24e3fSHanoh Haim        rr = DNSQR("\x00"+ret)
166f7d24e3fSHanoh Haim        rr.qname = name
167f7d24e3fSHanoh Haim        return rr,p
168f7d24e3fSHanoh Haim
169f7d24e3fSHanoh Haim
170f7d24e3fSHanoh Haim
171f7d24e3fSHanoh Haimclass RDataField(StrLenField):
172f7d24e3fSHanoh Haim    def m2i(self, pkt, s):
173f7d24e3fSHanoh Haim        family = None
174f7d24e3fSHanoh Haim        if pkt.type == 1: # A
175f7d24e3fSHanoh Haim            family = socket.AF_INET
176f7d24e3fSHanoh Haim        elif pkt.type == 12: # PTR
177f7d24e3fSHanoh Haim            s = DNSgetstr(s, 0)[0]
178f7d24e3fSHanoh Haim        elif pkt.type == 16: # TXT
179f7d24e3fSHanoh Haim            ret_s = ""
180f7d24e3fSHanoh Haim            tmp_s = s
181f7d24e3fSHanoh Haim            # RDATA contains a list of strings, each are prepended with
182f7d24e3fSHanoh Haim            # a byte containing the size of the following string.
183f7d24e3fSHanoh Haim            while tmp_s:
184f7d24e3fSHanoh Haim                tmp_len = struct.unpack("!B", tmp_s[0])[0] + 1
185f7d24e3fSHanoh Haim                if tmp_len > len(tmp_s):
186f7d24e3fSHanoh Haim                  warning("DNS RR TXT prematured end of character-string (size=%i, remaining bytes=%i)" % (tmp_len, len(tmp_s)))
187f7d24e3fSHanoh Haim                ret_s += tmp_s[1:tmp_len]
188f7d24e3fSHanoh Haim                tmp_s = tmp_s[tmp_len:]
189f7d24e3fSHanoh Haim            s = ret_s
190f7d24e3fSHanoh Haim        elif pkt.type == 28: # AAAA
191f7d24e3fSHanoh Haim            family = socket.AF_INET6
192f7d24e3fSHanoh Haim        if family is not None:
193f7d24e3fSHanoh Haim            s = inet_ntop(family, s)
194f7d24e3fSHanoh Haim        return s
195f7d24e3fSHanoh Haim    def i2m(self, pkt, s):
196f7d24e3fSHanoh Haim        if pkt.type == 1: # A
197f7d24e3fSHanoh Haim            if s:
198f7d24e3fSHanoh Haim                s = inet_aton(s)
199f7d24e3fSHanoh Haim        elif pkt.type in [2,3,4,5]: # NS, MD, MF, CNAME
200f7d24e3fSHanoh Haim            s = "".join(map(lambda x: chr(len(x))+x, s.split(".")))
201f7d24e3fSHanoh Haim            if ord(s[-1]):
202f7d24e3fSHanoh Haim                s += "\x00"
203f7d24e3fSHanoh Haim        elif pkt.type == 16: # TXT
204f7d24e3fSHanoh Haim            if s:
205f7d24e3fSHanoh Haim                ret_s = ""
206f7d24e3fSHanoh Haim                # The initial string must be splitted into a list of strings
207f7d24e3fSHanoh Haim                # prepended with theirs sizes.
208f7d24e3fSHanoh Haim                while len(s) >= 255:
209f7d24e3fSHanoh Haim                    ret_s += "\xff" + s[:255]
210f7d24e3fSHanoh Haim                    s = s[255:]
211f7d24e3fSHanoh Haim                # The remaining string is less than 255 bytes long
212f7d24e3fSHanoh Haim                if len(s):
213f7d24e3fSHanoh Haim                    ret_s += struct.pack("!B", len(s)) + s
214f7d24e3fSHanoh Haim                s = ret_s
215f7d24e3fSHanoh Haim        elif pkt.type == 28: # AAAA
216f7d24e3fSHanoh Haim            if s:
217f7d24e3fSHanoh Haim                s = inet_pton(socket.AF_INET6, s)
218f7d24e3fSHanoh Haim        return s
219f7d24e3fSHanoh Haim
220f7d24e3fSHanoh Haimclass RDLenField(Field):
221f7d24e3fSHanoh Haim    def __init__(self, name):
222f7d24e3fSHanoh Haim        Field.__init__(self, name, None, "H")
223f7d24e3fSHanoh Haim    def i2m(self, pkt, x):
224f7d24e3fSHanoh Haim        if x is None:
225f7d24e3fSHanoh Haim            rdataf = pkt.get_field("rdata")
226f7d24e3fSHanoh Haim            x = len(rdataf.i2m(pkt, pkt.rdata))
227f7d24e3fSHanoh Haim        return x
228f7d24e3fSHanoh Haim    def i2h(self, pkt, x):
229f7d24e3fSHanoh Haim        if x is None:
230f7d24e3fSHanoh Haim            rdataf = pkt.get_field("rdata")
231f7d24e3fSHanoh Haim            x = len(rdataf.i2m(pkt, pkt.rdata))
232f7d24e3fSHanoh Haim        return x
233f7d24e3fSHanoh Haim
234f7d24e3fSHanoh Haim
235f7d24e3fSHanoh Haimclass DNS(Packet):
236f7d24e3fSHanoh Haim    name = "DNS"
237f7d24e3fSHanoh Haim    fields_desc = [ ShortField("id", 0),
238f7d24e3fSHanoh Haim                    BitField("qr", 0, 1),
239f7d24e3fSHanoh Haim                    BitEnumField("opcode", 0, 4, {0:"QUERY",1:"IQUERY",2:"STATUS"}),
240f7d24e3fSHanoh Haim                    BitField("aa", 0, 1),
241f7d24e3fSHanoh Haim                    BitField("tc", 0, 1),
242f7d24e3fSHanoh Haim                    BitField("rd", 0, 1),
243f7d24e3fSHanoh Haim                    BitField("ra", 0, 1),
244f7d24e3fSHanoh Haim                    BitField("z", 0, 1),
245f7d24e3fSHanoh Haim                    # AD and CD bits are defined in RFC 2535
246f7d24e3fSHanoh Haim                    BitField("ad", 0, 1), # Authentic Data
247f7d24e3fSHanoh Haim                    BitField("cd", 0, 1), # Checking Disabled
248f7d24e3fSHanoh Haim                    BitEnumField("rcode", 0, 4, {0:"ok", 1:"format-error", 2:"server-failure", 3:"name-error", 4:"not-implemented", 5:"refused"}),
249f7d24e3fSHanoh Haim                    DNSRRCountField("qdcount", None, "qd"),
250f7d24e3fSHanoh Haim                    DNSRRCountField("ancount", None, "an"),
251f7d24e3fSHanoh Haim                    DNSRRCountField("nscount", None, "ns"),
252f7d24e3fSHanoh Haim                    DNSRRCountField("arcount", None, "ar"),
253f7d24e3fSHanoh Haim                    DNSQRField("qd", "qdcount"),
254f7d24e3fSHanoh Haim                    DNSRRField("an", "ancount"),
255f7d24e3fSHanoh Haim                    DNSRRField("ns", "nscount"),
256f7d24e3fSHanoh Haim                    DNSRRField("ar", "arcount",0) ]
257f7d24e3fSHanoh Haim    def answers(self, other):
258f7d24e3fSHanoh Haim        return (isinstance(other, DNS)
259f7d24e3fSHanoh Haim                and self.id == other.id
260f7d24e3fSHanoh Haim                and self.qr == 1
261f7d24e3fSHanoh Haim                and other.qr == 0)
262f7d24e3fSHanoh Haim
263f7d24e3fSHanoh Haim    def mysummary(self):
264f7d24e3fSHanoh Haim        type = ["Qry","Ans"][self.qr]
265f7d24e3fSHanoh Haim        name = ""
266f7d24e3fSHanoh Haim        if self.qr:
267f7d24e3fSHanoh Haim            type = "Ans"
268f7d24e3fSHanoh Haim            if self.ancount > 0 and isinstance(self.an, DNSRR):
269f7d24e3fSHanoh Haim                name = ' "%s"' % self.an.rdata
270f7d24e3fSHanoh Haim        else:
271f7d24e3fSHanoh Haim            type = "Qry"
272f7d24e3fSHanoh Haim            if self.qdcount > 0 and isinstance(self.qd, DNSQR):
273f7d24e3fSHanoh Haim                name = ' "%s"' % self.qd.qname
274f7d24e3fSHanoh Haim        return 'DNS %s%s ' % (type, name)
275f7d24e3fSHanoh Haim
276f7d24e3fSHanoh Haimdnstypes = { 0:"ANY", 255:"ALL",
277f7d24e3fSHanoh Haim             1:"A", 2:"NS", 3:"MD", 4:"MF", 5:"CNAME", 6:"SOA", 7: "MB", 8:"MG",
278f7d24e3fSHanoh Haim             9:"MR",10:"NULL",11:"WKS",12:"PTR",13:"HINFO",14:"MINFO",15:"MX",16:"TXT",
279f7d24e3fSHanoh Haim             17:"RP",18:"AFSDB",28:"AAAA", 33:"SRV",38:"A6",39:"DNAME",
280f7d24e3fSHanoh Haim             41:"OPT", 43:"DS", 46:"RRSIG", 47:"NSEC", 48:"DNSKEY",
281f7d24e3fSHanoh Haim	     50: "NSEC3", 51: "NSEC3PARAM", 32769:"DLV" }
282f7d24e3fSHanoh Haim
283f7d24e3fSHanoh Haimdnsqtypes = {251:"IXFR",252:"AXFR",253:"MAILB",254:"MAILA",255:"ALL"}
284f7d24e3fSHanoh Haimdnsqtypes.update(dnstypes)
285f7d24e3fSHanoh Haimdnsclasses =  {1: 'IN',  2: 'CS',  3: 'CH',  4: 'HS',  255: 'ANY'}
286f7d24e3fSHanoh Haim
287f7d24e3fSHanoh Haim
288f7d24e3fSHanoh Haimclass DNSQR(Packet):
289f7d24e3fSHanoh Haim    name = "DNS Question Record"
290f7d24e3fSHanoh Haim    show_indent=0
291f7d24e3fSHanoh Haim    fields_desc = [ DNSStrField("qname",""),
292f7d24e3fSHanoh Haim                    ShortEnumField("qtype", 1, dnsqtypes),
293f7d24e3fSHanoh Haim                    ShortEnumField("qclass", 1, dnsclasses) ]
294f7d24e3fSHanoh Haim
295f7d24e3fSHanoh Haim
296f7d24e3fSHanoh Haim
297f7d24e3fSHanoh Haim# RFC 2671 - Extension Mechanisms for DNS (EDNS0)
298f7d24e3fSHanoh Haim
299f7d24e3fSHanoh Haimclass EDNS0TLV(Packet):
300f7d24e3fSHanoh Haim    name = "DNS EDNS0 TLV"
301f7d24e3fSHanoh Haim    fields_desc = [ ShortEnumField("optcode", 0, { 0: "Reserved", 1: "LLQ", 2: "UL", 3: "NSID", 4: "Reserved", 5: "PING" }),
302f7d24e3fSHanoh Haim                    FieldLenField("optlen", None, "optdata", fmt="H"),
303f7d24e3fSHanoh Haim                    StrLenField("optdata", "", length_from=lambda pkt: pkt.optlen) ]
304f7d24e3fSHanoh Haim
305f7d24e3fSHanoh Haim    def extract_padding(self, p):
306f7d24e3fSHanoh Haim	return "", p
307f7d24e3fSHanoh Haim
308f7d24e3fSHanoh Haimclass DNSRROPT(Packet):
309f7d24e3fSHanoh Haim    name = "DNS OPT Resource Record"
310f7d24e3fSHanoh Haim    fields_desc = [ DNSStrField("rrname",""),
311f7d24e3fSHanoh Haim                    ShortEnumField("type", 41, dnstypes),
312f7d24e3fSHanoh Haim                    ShortField("rclass", 4096),
313f7d24e3fSHanoh Haim                    ByteField("extrcode", 0),
314f7d24e3fSHanoh Haim                    ByteField("version", 0),
315f7d24e3fSHanoh Haim                    # version 0 means EDNS0
316f7d24e3fSHanoh Haim                    BitEnumField("z", 32768, 16, { 32768: "D0" }),
317f7d24e3fSHanoh Haim                    # D0 means DNSSEC OK from RFC 3225
318f7d24e3fSHanoh Haim                    FieldLenField("rdlen", None, length_of="rdata", fmt="H"),
319f7d24e3fSHanoh Haim                    PacketListField("rdata", [], EDNS0TLV, length_from=lambda pkt: pkt.rdlen) ]
320f7d24e3fSHanoh Haim
321f7d24e3fSHanoh Haim# RFC 4034 - Resource Records for the DNS Security Extensions
322f7d24e3fSHanoh Haim
323f7d24e3fSHanoh Haim# 09/2013 from http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml
324f7d24e3fSHanoh Haimdnssecalgotypes = { 0:"Reserved", 1:"RSA/MD5", 2:"Diffie-Hellman", 3:"DSA/SHA-1",
325f7d24e3fSHanoh Haim                    4:"Reserved", 5:"RSA/SHA-1", 6:"DSA-NSEC3-SHA1",
326f7d24e3fSHanoh Haim                    7:"RSASHA1-NSEC3-SHA1", 8:"RSA/SHA-256", 9:"Reserved",
327f7d24e3fSHanoh Haim                   10:"RSA/SHA-512", 11:"Reserved", 12:"GOST R 34.10-2001",
328f7d24e3fSHanoh Haim                   13:"ECDSA Curve P-256 with SHA-256", 14: "ECDSA Curve P-384 with SHA-384",
329f7d24e3fSHanoh Haim                  252:"Reserved for Indirect Keys", 253:"Private algorithms - domain name",
330f7d24e3fSHanoh Haim                  254:"Private algorithms - OID", 255:"Reserved" }
331f7d24e3fSHanoh Haim
332f7d24e3fSHanoh Haim# 09/2013 from http://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml
333f7d24e3fSHanoh Haimdnssecdigesttypes = { 0:"Reserved", 1:"SHA-1", 2:"SHA-256", 3:"GOST R 34.11-94",  4:"SHA-384" }
334f7d24e3fSHanoh Haim
335f7d24e3fSHanoh Haim
336f7d24e3fSHanoh Haimclass TimeField(IntField):
337f7d24e3fSHanoh Haim
338f7d24e3fSHanoh Haim    def any2i(self, pkt, x):
339f7d24e3fSHanoh Haim        if type(x) == str:
340f7d24e3fSHanoh Haim	    import time, calendar
341f7d24e3fSHanoh Haim	    t = time.strptime(x, "%Y%m%d%H%M%S")
342f7d24e3fSHanoh Haim            return int(calendar.timegm(t))
343f7d24e3fSHanoh Haim        return x
344f7d24e3fSHanoh Haim
345f7d24e3fSHanoh Haim    def i2repr(self, pkt, x):
346f7d24e3fSHanoh Haim	import time
347f7d24e3fSHanoh Haim	x = self.i2h(pkt, x)
348f7d24e3fSHanoh Haim	t = time.strftime("%Y%m%d%H%M%S", time.gmtime(x))
349f7d24e3fSHanoh Haim	return "%s (%d)" % (t ,x)
350f7d24e3fSHanoh Haim
351f7d24e3fSHanoh Haim
352f7d24e3fSHanoh Haimdef bitmap2RRlist(bitmap):
353f7d24e3fSHanoh Haim    """
354f7d24e3fSHanoh Haim    Decode the 'Type Bit Maps' field of the NSEC Resource Record into an
355f7d24e3fSHanoh Haim    integer list.
356f7d24e3fSHanoh Haim    """
357f7d24e3fSHanoh Haim    # RFC 4034, 4.1.2. The Type Bit Maps Field
358f7d24e3fSHanoh Haim
359f7d24e3fSHanoh Haim    RRlist = []
360f7d24e3fSHanoh Haim
361f7d24e3fSHanoh Haim    while bitmap:
362f7d24e3fSHanoh Haim
363f7d24e3fSHanoh Haim	if len(bitmap) < 2:
364f7d24e3fSHanoh Haim	    warning("bitmap too short (%i)" % len(bitmap))
365f7d24e3fSHanoh Haim	    return
366f7d24e3fSHanoh Haim
367f7d24e3fSHanoh Haim	window_block = ord(bitmap[0]) # window number
368f7d24e3fSHanoh Haim	offset = 256*window_block # offset of the Ressource Record
369f7d24e3fSHanoh Haim	bitmap_len = ord(bitmap[1]) # length of the bitmap in bytes
370f7d24e3fSHanoh Haim
371f7d24e3fSHanoh Haim	if bitmap_len <= 0 or bitmap_len > 32:
372f7d24e3fSHanoh Haim	    warning("bitmap length is no valid (%i)" % bitmap_len)
373f7d24e3fSHanoh Haim	    return
374f7d24e3fSHanoh Haim
375f7d24e3fSHanoh Haim	tmp_bitmap = bitmap[2:2+bitmap_len]
376f7d24e3fSHanoh Haim
377f7d24e3fSHanoh Haim	# Let's compare each bit of tmp_bitmap and compute the real RR value
378f7d24e3fSHanoh Haim	for b in xrange(len(tmp_bitmap)):
379f7d24e3fSHanoh Haim	    v = 128
380f7d24e3fSHanoh Haim	    for i in xrange(8):
381f7d24e3fSHanoh Haim		if ord(tmp_bitmap[b]) & v:
382f7d24e3fSHanoh Haim		    # each of the RR is encoded as a bit
383f7d24e3fSHanoh Haim		    RRlist += [ offset + b*8 + i ]
384f7d24e3fSHanoh Haim		v = v >> 1
385f7d24e3fSHanoh Haim
386f7d24e3fSHanoh Haim	# Next block if any
387f7d24e3fSHanoh Haim	bitmap = bitmap[2+bitmap_len:]
388f7d24e3fSHanoh Haim
389f7d24e3fSHanoh Haim    return RRlist
390f7d24e3fSHanoh Haim
391f7d24e3fSHanoh Haim
392f7d24e3fSHanoh Haimdef RRlist2bitmap(lst):
393f7d24e3fSHanoh Haim    """
394f7d24e3fSHanoh Haim    Encode a list of integers representing Resource Records to a bitmap field
395f7d24e3fSHanoh Haim    used in the NSEC Resource Record.
396f7d24e3fSHanoh Haim    """
397f7d24e3fSHanoh Haim    # RFC 4034, 4.1.2. The Type Bit Maps Field
398f7d24e3fSHanoh Haim
399f7d24e3fSHanoh Haim    import math
400f7d24e3fSHanoh Haim
401f7d24e3fSHanoh Haim    bitmap = ""
402f7d24e3fSHanoh Haim    lst = list(set(lst))
403f7d24e3fSHanoh Haim    lst.sort()
404f7d24e3fSHanoh Haim
405f7d24e3fSHanoh Haim    lst = filter(lambda x: x <= 65535, lst)
406f7d24e3fSHanoh Haim    lst = map(lambda x: abs(x), lst)
407f7d24e3fSHanoh Haim
408f7d24e3fSHanoh Haim    # number of window blocks
409f7d24e3fSHanoh Haim    max_window_blocks = int(math.ceil(lst[-1] / 256.))
410f7d24e3fSHanoh Haim    min_window_blocks = int(math.floor(lst[0] / 256.))
411f7d24e3fSHanoh Haim    if min_window_blocks == max_window_blocks:
412f7d24e3fSHanoh Haim	max_window_blocks += 1
413f7d24e3fSHanoh Haim
414f7d24e3fSHanoh Haim    for wb in xrange(min_window_blocks, max_window_blocks+1):
415f7d24e3fSHanoh Haim        # First, filter out RR not encoded in the current window block
416f7d24e3fSHanoh Haim        # i.e. keep everything between 256*wb <= 256*(wb+1)
417f7d24e3fSHanoh Haim        rrlist = filter(lambda x: 256*wb <= x and x < 256*(wb+1), lst)
418f7d24e3fSHanoh Haim        rrlist.sort()
419f7d24e3fSHanoh Haim        if rrlist == []:
420f7d24e3fSHanoh Haim            continue
421f7d24e3fSHanoh Haim
422f7d24e3fSHanoh Haim        # Compute the number of bytes used to store the bitmap
423f7d24e3fSHanoh Haim        if rrlist[-1] == 0: # only one element in the list
424f7d24e3fSHanoh Haim	    bytes = 1
425f7d24e3fSHanoh Haim        else:
426f7d24e3fSHanoh Haim	    max = rrlist[-1] - 256*wb
427f7d24e3fSHanoh Haim	    bytes = int(math.ceil(max / 8)) + 1  # use at least 1 byte
428f7d24e3fSHanoh Haim        if bytes > 32: # Don't encode more than 256 bits / values
429f7d24e3fSHanoh Haim	    bytes = 32
430f7d24e3fSHanoh Haim
431f7d24e3fSHanoh Haim        bitmap += struct.pack("B", wb)
432f7d24e3fSHanoh Haim        bitmap += struct.pack("B", bytes)
433f7d24e3fSHanoh Haim
434f7d24e3fSHanoh Haim        # Generate the bitmap
435f7d24e3fSHanoh Haim        for tmp in xrange(bytes):
436f7d24e3fSHanoh Haim            v = 0
437f7d24e3fSHanoh Haim            # Remove out of range Ressource Records
438f7d24e3fSHanoh Haim            tmp_rrlist = filter(lambda x: 256*wb+8*tmp <= x and x < 256*wb+8*tmp+8, rrlist)
439f7d24e3fSHanoh Haim            if not tmp_rrlist == []:
440f7d24e3fSHanoh Haim                # 1. rescale to fit into 8 bits
441f7d24e3fSHanoh Haim                tmp_rrlist = map(lambda x: (x-256*wb)-(tmp*8), tmp_rrlist)
442f7d24e3fSHanoh Haim                # 2. x gives the bit position ; compute the corresponding value
443f7d24e3fSHanoh Haim                tmp_rrlist = map(lambda x: 2**(7-x) , tmp_rrlist)
444f7d24e3fSHanoh Haim                # 3. sum everything
445f7d24e3fSHanoh Haim                v = reduce(lambda x,y: x+y, tmp_rrlist)
446f7d24e3fSHanoh Haim            bitmap += struct.pack("B", v)
447f7d24e3fSHanoh Haim
448f7d24e3fSHanoh Haim    return bitmap
449f7d24e3fSHanoh Haim
450f7d24e3fSHanoh Haim
451f7d24e3fSHanoh Haimclass RRlistField(StrField):
452f7d24e3fSHanoh Haim    def h2i(self, pkt, x):
453f7d24e3fSHanoh Haim	if type(x) == list:
454f7d24e3fSHanoh Haim	    return RRlist2bitmap(x)
455f7d24e3fSHanoh Haim	return x
456f7d24e3fSHanoh Haim
457f7d24e3fSHanoh Haim    def i2repr(self, pkt, x):
458f7d24e3fSHanoh Haim	x = self.i2h(pkt, x)
459f7d24e3fSHanoh Haim	rrlist = bitmap2RRlist(x)
460f7d24e3fSHanoh Haim	return [ dnstypes.get(rr, rr) for rr in rrlist ]
461f7d24e3fSHanoh Haim
462f7d24e3fSHanoh Haim
463f7d24e3fSHanoh Haimclass _DNSRRdummy(Packet):
464f7d24e3fSHanoh Haim    name = "Dummy class that implements post_build() for Ressource Records"
465f7d24e3fSHanoh Haim    def post_build(self, pkt, pay):
466f7d24e3fSHanoh Haim        if not self.rdlen == None:
467f7d24e3fSHanoh Haim            return pkt
468f7d24e3fSHanoh Haim
469f7d24e3fSHanoh Haim        lrrname = len(self.fields_desc[0].i2m("", self.getfieldval("rrname")))
470f7d24e3fSHanoh Haim        l = len(pkt) - lrrname - 10
471f7d24e3fSHanoh Haim        pkt = pkt[:lrrname+8] + struct.pack("!H", l) + pkt[lrrname+8+2:]
472f7d24e3fSHanoh Haim
473f7d24e3fSHanoh Haim        return pkt
474f7d24e3fSHanoh Haim
475f7d24e3fSHanoh Haimclass DNSRRSOA(_DNSRRdummy):
476f7d24e3fSHanoh Haim    name = "DNS SOA Resource Record"
477f7d24e3fSHanoh Haim    fields_desc = [ DNSStrField("rrname",""),
478f7d24e3fSHanoh Haim                    ShortEnumField("type", 6, dnstypes),
479f7d24e3fSHanoh Haim                    ShortEnumField("rclass", 1, dnsclasses),
480f7d24e3fSHanoh Haim                    IntField("ttl", 0),
481f7d24e3fSHanoh Haim                    ShortField("rdlen", None),
482f7d24e3fSHanoh Haim                    DNSStrField("mname", ""),
483f7d24e3fSHanoh Haim                    DNSStrField("rname", ""),
484f7d24e3fSHanoh Haim                    IntField("serial", 0),
485f7d24e3fSHanoh Haim                    IntField("refresh", 0),
486f7d24e3fSHanoh Haim                    IntField("retry", 0),
487f7d24e3fSHanoh Haim                    IntField("expire", 0),
488f7d24e3fSHanoh Haim                    IntField("minimum", 0)
489f7d24e3fSHanoh Haim                  ]
490f7d24e3fSHanoh Haim
491f7d24e3fSHanoh Haimclass DNSRRRSIG(_DNSRRdummy):
492f7d24e3fSHanoh Haim    name = "DNS RRSIG Resource Record"
493f7d24e3fSHanoh Haim    fields_desc = [ DNSStrField("rrname",""),
494f7d24e3fSHanoh Haim                    ShortEnumField("type", 46, dnstypes),
495f7d24e3fSHanoh Haim                    ShortEnumField("rclass", 1, dnsclasses),
496f7d24e3fSHanoh Haim                    IntField("ttl", 0),
497f7d24e3fSHanoh Haim                    ShortField("rdlen", None),
498f7d24e3fSHanoh Haim                    ShortEnumField("typecovered", 1, dnstypes),
499f7d24e3fSHanoh Haim                    ByteEnumField("algorithm", 5, dnssecalgotypes),
500f7d24e3fSHanoh Haim                    ByteField("labels", 0),
501f7d24e3fSHanoh Haim                    IntField("originalttl", 0),
502f7d24e3fSHanoh Haim                    TimeField("expiration", 0),
503f7d24e3fSHanoh Haim                    TimeField("inception", 0),
504f7d24e3fSHanoh Haim                    ShortField("keytag", 0),
505f7d24e3fSHanoh Haim                    DNSStrField("signersname", ""),
506f7d24e3fSHanoh Haim                    StrField("signature", "")
507f7d24e3fSHanoh Haim                  ]
508f7d24e3fSHanoh Haim
509f7d24e3fSHanoh Haim
510f7d24e3fSHanoh Haimclass DNSRRNSEC(_DNSRRdummy):
511f7d24e3fSHanoh Haim    name = "DNS NSEC Resource Record"
512f7d24e3fSHanoh Haim    fields_desc = [ DNSStrField("rrname",""),
513f7d24e3fSHanoh Haim                    ShortEnumField("type", 47, dnstypes),
514f7d24e3fSHanoh Haim                    ShortEnumField("rclass", 1, dnsclasses),
515f7d24e3fSHanoh Haim                    IntField("ttl", 0),
516f7d24e3fSHanoh Haim                    ShortField("rdlen", None),
517f7d24e3fSHanoh Haim                    DNSStrField("nextname", ""),
518f7d24e3fSHanoh Haim                    RRlistField("typebitmaps", "")
519f7d24e3fSHanoh Haim                  ]
520f7d24e3fSHanoh Haim
521f7d24e3fSHanoh Haim
522f7d24e3fSHanoh Haimclass DNSRRDNSKEY(_DNSRRdummy):
523f7d24e3fSHanoh Haim    name = "DNS DNSKEY Resource Record"
524f7d24e3fSHanoh Haim    fields_desc = [ DNSStrField("rrname",""),
525f7d24e3fSHanoh Haim                    ShortEnumField("type", 48, dnstypes),
526f7d24e3fSHanoh Haim                    ShortEnumField("rclass", 1, dnsclasses),
527f7d24e3fSHanoh Haim                    IntField("ttl", 0),
528f7d24e3fSHanoh Haim                    ShortField("rdlen", None),
529f7d24e3fSHanoh Haim                    FlagsField("flags", 256, 16, "S???????Z???????"),
530f7d24e3fSHanoh Haim                    # S: Secure Entry Point
531f7d24e3fSHanoh Haim                    # Z: Zone Key
532f7d24e3fSHanoh Haim                    ByteField("protocol", 3),
533f7d24e3fSHanoh Haim                    ByteEnumField("algorithm", 5, dnssecalgotypes),
534f7d24e3fSHanoh Haim                    StrField("publickey", "")
535f7d24e3fSHanoh Haim                  ]
536f7d24e3fSHanoh Haim
537f7d24e3fSHanoh Haim
538f7d24e3fSHanoh Haimclass DNSRRDS(_DNSRRdummy):
539f7d24e3fSHanoh Haim    name = "DNS DS Resource Record"
540f7d24e3fSHanoh Haim    fields_desc = [ DNSStrField("rrname",""),
541f7d24e3fSHanoh Haim                    ShortEnumField("type", 43, dnstypes),
542f7d24e3fSHanoh Haim                    ShortEnumField("rclass", 1, dnsclasses),
543f7d24e3fSHanoh Haim                    IntField("ttl", 0),
544f7d24e3fSHanoh Haim                    ShortField("rdlen", None),
545f7d24e3fSHanoh Haim                    ShortField("keytag", 0),
546f7d24e3fSHanoh Haim                    ByteEnumField("algorithm", 5, dnssecalgotypes),
547f7d24e3fSHanoh Haim                    ByteEnumField("digesttype", 5, dnssecdigesttypes),
548f7d24e3fSHanoh Haim                    StrField("digest", "")
549f7d24e3fSHanoh Haim                  ]
550f7d24e3fSHanoh Haim
551f7d24e3fSHanoh Haim
552f7d24e3fSHanoh Haim# RFC 5074 - DNSSEC Lookaside Validation (DLV)
553f7d24e3fSHanoh Haimclass DNSRRDLV(DNSRRDS):
554f7d24e3fSHanoh Haim    name = "DNS DLV Resource Record"
555f7d24e3fSHanoh Haim    def __init__(self, *args, **kargs):
556f7d24e3fSHanoh Haim       DNSRRDS.__init__(self, *args, **kargs)
557f7d24e3fSHanoh Haim       if not kargs.get('type', 0):
558f7d24e3fSHanoh Haim           self.type = 32769
559f7d24e3fSHanoh Haim
560f7d24e3fSHanoh Haim# RFC 5155 - DNS Security (DNSSEC) Hashed Authenticated Denial of Existence
561f7d24e3fSHanoh Haimclass DNSRRNSEC3(_DNSRRdummy):
562f7d24e3fSHanoh Haim    name = "DNS NSEC3 Resource Record"
563f7d24e3fSHanoh Haim    fields_desc = [ DNSStrField("rrname",""),
564f7d24e3fSHanoh Haim                    ShortEnumField("type", 50, dnstypes),
565f7d24e3fSHanoh Haim                    ShortEnumField("rclass", 1, dnsclasses),
566f7d24e3fSHanoh Haim                    IntField("ttl", 0),
567f7d24e3fSHanoh Haim                    ShortField("rdlen", None),
568f7d24e3fSHanoh Haim		    ByteField("hashalg", 0),
569f7d24e3fSHanoh Haim                    BitEnumField("flags", 0, 8, {1:"Opt-Out"}),
570f7d24e3fSHanoh Haim		    ShortField("iterations", 0),
571f7d24e3fSHanoh Haim		    FieldLenField("saltlength", 0, fmt="!B", length_of="salt"),
572f7d24e3fSHanoh Haim		    StrLenField("salt", "", length_from=lambda x: x.saltlength),
573f7d24e3fSHanoh Haim		    FieldLenField("hashlength", 0, fmt="!B", length_of="nexthashedownername"),
574f7d24e3fSHanoh Haim		    StrLenField("nexthashedownername", "", length_from=lambda x: x.hashlength),
575f7d24e3fSHanoh Haim                    RRlistField("typebitmaps", "")
576f7d24e3fSHanoh Haim		  ]
577f7d24e3fSHanoh Haim
578f7d24e3fSHanoh Haim
579f7d24e3fSHanoh Haimclass DNSRRNSEC3PARAM(_DNSRRdummy):
580f7d24e3fSHanoh Haim    name = "DNS NSEC3PARAM Resource Record"
581f7d24e3fSHanoh Haim    fields_desc = [ DNSStrField("rrname",""),
582f7d24e3fSHanoh Haim                    ShortEnumField("type", 51, dnstypes),
583f7d24e3fSHanoh Haim                    ShortEnumField("rclass", 1, dnsclasses),
584f7d24e3fSHanoh Haim                    IntField("ttl", 0),
585f7d24e3fSHanoh Haim                    ShortField("rdlen", None),
586f7d24e3fSHanoh Haim		    ByteField("hashalg", 0),
587f7d24e3fSHanoh Haim		    ByteField("flags", 0),
588f7d24e3fSHanoh Haim		    ShortField("iterations", 0),
589f7d24e3fSHanoh Haim		    FieldLenField("saltlength", 0, fmt="!B", length_of="salt"),
590f7d24e3fSHanoh Haim		    StrLenField("salt", "", length_from=lambda pkt: pkt.saltlength)
591f7d24e3fSHanoh Haim		  ]
592f7d24e3fSHanoh Haim
593f7d24e3fSHanoh Haim
595f7d24e3fSHanoh Haim
596f7d24e3fSHanoh Haimdef isdnssecRR(obj):
597f7d24e3fSHanoh Haim    list = [ isinstance (obj, cls) for cls in dnssecclasses ]
598f7d24e3fSHanoh Haim    return reduce(lambda x,y: x or y, list)
599f7d24e3fSHanoh Haim
600f7d24e3fSHanoh HaimdnsRRdispatcher = {     #6: DNSRRSOA,
601f7d24e3fSHanoh Haim                       41: DNSRROPT,        # RFC 1671
602f7d24e3fSHanoh Haim                       43: DNSRRDS,         # RFC 4034
603f7d24e3fSHanoh Haim                       46: DNSRRRSIG,       # RFC 4034
604f7d24e3fSHanoh Haim                       47: DNSRRNSEC,       # RFC 4034
605f7d24e3fSHanoh Haim                       48: DNSRRDNSKEY,     # RFC 4034
606f7d24e3fSHanoh Haim                       50: DNSRRNSEC3,      # RFC 5155
607f7d24e3fSHanoh Haim                       51: DNSRRNSEC3PARAM, # RFC 5155
608f7d24e3fSHanoh Haim                    32769: DNSRRDLV         # RFC 4431
609f7d24e3fSHanoh Haim                   }
610f7d24e3fSHanoh Haim
611f7d24e3fSHanoh Haimclass DNSRR(Packet):
612f7d24e3fSHanoh Haim    name = "DNS Resource Record"
613f7d24e3fSHanoh Haim    show_indent=0
614f7d24e3fSHanoh Haim    fields_desc = [ DNSStrField("rrname",""),
615f7d24e3fSHanoh Haim                    ShortEnumField("type", 1, dnstypes),
616f7d24e3fSHanoh Haim                    ShortEnumField("rclass", 1, dnsclasses),
617f7d24e3fSHanoh Haim                    IntField("ttl", 0),
618f7d24e3fSHanoh Haim                    RDLenField("rdlen"),
619f7d24e3fSHanoh Haim                    RDataField("rdata", "", length_from=lambda pkt:pkt.rdlen) ]
620f7d24e3fSHanoh Haim
621f7d24e3fSHanoh Haimbind_layers( UDP,           DNS,           dport=53)
622f7d24e3fSHanoh Haimbind_layers( UDP,           DNS,           sport=53)
623f7d24e3fSHanoh Haim
624f7d24e3fSHanoh Haim
625f7d24e3fSHanoh Haim@conf.commands.register
626f7d24e3fSHanoh Haimdef dyndns_add(nameserver, name, rdata, type="A", ttl=10):
627f7d24e3fSHanoh Haim    """Send a DNS add message to a nameserver for "name" to have a new "rdata"
628f7d24e3fSHanoh Haimdyndns_add(nameserver, name, rdata, type="A", ttl=10) -> result code (0=ok)
629f7d24e3fSHanoh Haim
630f7d24e3fSHanoh Haimexample: dyndns_add("ns1.toto.com", "dyn.toto.com", "")
631f7d24e3fSHanoh HaimRFC2136
632f7d24e3fSHanoh Haim"""
633f7d24e3fSHanoh Haim    zone = name[name.find(".")+1:]
634f7d24e3fSHanoh Haim    r=sr1(IP(dst=nameserver)/UDP()/DNS(opcode=5,
635f7d24e3fSHanoh Haim                                       qd=[DNSQR(qname=zone, qtype="SOA")],
636f7d24e3fSHanoh Haim                                       ns=[DNSRR(rrname=name, type="A",
637f7d24e3fSHanoh Haim                                                 ttl=ttl, rdata=rdata)]),
638f7d24e3fSHanoh Haim          verbose=0, timeout=5)
639f7d24e3fSHanoh Haim    if r and r.haslayer(DNS):
640f7d24e3fSHanoh Haim        return r.getlayer(DNS).rcode
641f7d24e3fSHanoh Haim    else:
642f7d24e3fSHanoh Haim        return -1
643f7d24e3fSHanoh Haim
644f7d24e3fSHanoh Haim
645f7d24e3fSHanoh Haim
646f7d24e3fSHanoh Haim
647f7d24e3fSHanoh Haim@conf.commands.register
648f7d24e3fSHanoh Haimdef dyndns_del(nameserver, name, type="ALL", ttl=10):
649f7d24e3fSHanoh Haim    """Send a DNS delete message to a nameserver for "name"
650f7d24e3fSHanoh Haimdyndns_del(nameserver, name, type="ANY", ttl=10) -> result code (0=ok)
651f7d24e3fSHanoh Haim
652f7d24e3fSHanoh Haimexample: dyndns_del("ns1.toto.com", "dyn.toto.com")
653f7d24e3fSHanoh HaimRFC2136
654f7d24e3fSHanoh Haim"""
655f7d24e3fSHanoh Haim    zone = name[name.find(".")+1:]
656f7d24e3fSHanoh Haim    r=sr1(IP(dst=nameserver)/UDP()/DNS(opcode=5,
657f7d24e3fSHanoh Haim                                       qd=[DNSQR(qname=zone, qtype="SOA")],
658f7d24e3fSHanoh Haim                                       ns=[DNSRR(rrname=name, type=type,
659f7d24e3fSHanoh Haim                                                 rclass="ANY", ttl=0, rdata="")]),
660f7d24e3fSHanoh Haim          verbose=0, timeout=5)
661f7d24e3fSHanoh Haim    if r and r.haslayer(DNS):
662f7d24e3fSHanoh Haim        return r.getlayer(DNS).rcode
663f7d24e3fSHanoh Haim    else:
664f7d24e3fSHanoh Haim        return -1
665f7d24e3fSHanoh Haim
666f7d24e3fSHanoh Haim
667f7d24e3fSHanoh Haimclass DNS_am(AnsweringMachine):
668f7d24e3fSHanoh Haim    function_name="dns_spoof"
669f7d24e3fSHanoh Haim    filter = "udp port 53"
670f7d24e3fSHanoh Haim
671f7d24e3fSHanoh Haim    def parse_options(self, joker="", match=None):
672f7d24e3fSHanoh Haim        if match is None:
673f7d24e3fSHanoh Haim            self.match = {}
674f7d24e3fSHanoh Haim        else:
675f7d24e3fSHanoh Haim            self.match = match
676f7d24e3fSHanoh Haim        self.joker=joker
677f7d24e3fSHanoh Haim
678f7d24e3fSHanoh Haim    def is_request(self, req):
679f7d24e3fSHanoh Haim        return req.haslayer(DNS) and req.getlayer(DNS).qr == 0
680f7d24e3fSHanoh Haim
681f7d24e3fSHanoh Haim    def make_reply(self, req):
682f7d24e3fSHanoh Haim        ip = req.getlayer(IP)
683f7d24e3fSHanoh Haim        dns = req.getlayer(DNS)
684f7d24e3fSHanoh Haim        resp = IP(dst=ip.src, src=ip.dst)/UDP(dport=ip.sport,sport=ip.dport)
685f7d24e3fSHanoh Haim        rdata = self.match.get(dns.qd.qname, self.joker)
686f7d24e3fSHanoh Haim        resp /= DNS(id=dns.id, qr=1, qd=dns.qd,
687f7d24e3fSHanoh Haim                    an=DNSRR(rrname=dns.qd.qname, ttl=10, rdata=rdata))
688f7d24e3fSHanoh Haim        return resp
689f7d24e3fSHanoh Haim
690f7d24e3fSHanoh Haim