• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1## This file is part of Scapy
2## See http://www.secdev.org/projects/scapy for more informations
3## Copyright (C) Philippe Biondi <phil@secdev.org>
4## This program is published under a GPLv2 license
5
6"""
7DNS: Domain Name System.
8"""
9
10from __future__ import absolute_import
11import socket,struct
12
13from scapy.config import conf
14from scapy.packet import *
15from scapy.fields import *
16from scapy.compat import *
17from scapy.ansmachine import *
18from scapy.sendrecv import sr1
19from scapy.layers.inet import IP, DestIPField, UDP, TCP
20from scapy.layers.inet6 import DestIP6Field
21from scapy.error import warning
22from functools import reduce
23import scapy.modules.six as six
24from scapy.modules.six.moves import range
25
26class InheritOriginDNSStrPacket(Packet):
27    __slots__ = Packet.__slots__ + ["_orig_s", "_orig_p"]
28
29    def __init__(self, _pkt=None, _orig_s=None, _orig_p=None, *args, **kwargs):
30        self._orig_s = _orig_s
31        self._orig_p = _orig_p
32        Packet.__init__(self, _pkt=_pkt, *args, **kwargs)
33
34class DNSStrField(StrField):
35    def h2i(self, pkt, x):
36        if not x:
37            return b"."
38        return x
39
40    def i2m(self, pkt, x):
41        if x == b".":
42          return b"\x00"
43
44        # Truncate chunks that cannot be encoded (more than 63 bytes..)
45        x = b"".join(chb(len(y)) + y for y in (k[:63] for k in x.split(b".")))
46        if orb(x[-1]) != 0:
47            x += b"\x00"
48        return x
49
50    def getfield(self, pkt, s):
51        n = b""
52        if orb(s[0]) == 0:
53            return s[1:], b"."
54        while True:
55            l = orb(s[0])
56            s = s[1:]
57            if not l:
58                break
59            if l & 0xc0:
60                p = ((l & ~0xc0) << 8) + orb(s[0]) - 12
61                if hasattr(pkt, "_orig_s") and pkt._orig_s:
62                    ns = DNSgetstr(pkt._orig_s, p)[0]
63                    n += ns
64                    s = s[1:]
65                    if not s:
66                        break
67                else:
68                    raise Scapy_Exception("DNS message can't be compressed at this point!")
69            else:
70                n += s[:l] + b"."
71                s = s[l:]
72        return s, n
73
74
75class DNSRRCountField(ShortField):
76    __slots__ = ["rr"]
77    def __init__(self, name, default, rr):
78        ShortField.__init__(self, name, default)
79        self.rr = rr
80    def _countRR(self, pkt):
81        x = getattr(pkt,self.rr)
82        i = 0
83        while isinstance(x, DNSRR) or isinstance(x, DNSQR) or isdnssecRR(x):
84            x = x.payload
85            i += 1
86        return i
87
88    def i2m(self, pkt, x):
89        if x is None:
90            x = self._countRR(pkt)
91        return x
92    def i2h(self, pkt, x):
93        if x is None:
94            x = self._countRR(pkt)
95        return x
96
97
98def DNSgetstr(s, p):
99    name = b""
100    q = 0
101    jpath = [p]
102    while True:
103        if p >= len(s):
104            warning("DNS RR prematured end (ofs=%i, len=%i)"%(p,len(s)))
105            break
106        l = orb(s[p]) # current value of the string at p
107        p += 1
108        if l & 0xc0: # Pointer label
109            if not q:
110                q = p+1
111            if p >= len(s):
112                warning("DNS incomplete jump token at (ofs=%i)" % p)
113                break
114            p = ((l & ~0xc0) << 8) + orb(s[p]) - 12
115            if p in jpath:
116                warning("DNS decompression loop detected")
117                break
118            jpath.append(p)
119            continue
120        elif l > 0: # Label
121            name += s[p:p+l] + b"."
122            p += l
123            continue
124        break
125    if q:
126        p = q
127    return name, p
128
129
130class DNSRRField(StrField):
131    __slots__ = ["countfld", "passon"]
132    holds_packets = 1
133    def __init__(self, name, countfld, passon=1):
134        StrField.__init__(self, name, None)
135        self.countfld = countfld
136        self.passon = passon
137    def i2m(self, pkt, x):
138        if x is None:
139            return b""
140        return raw(x)
141    def decodeRR(self, name, s, p):
142        ret = s[p:p+10]
143        type,cls,ttl,rdlen = struct.unpack("!HHIH", ret)
144        p += 10
145        rr = DNSRR(b"\x00"+ret+s[p:p+rdlen], _orig_s=s, _orig_p=p)
146        if type in [2, 3, 4, 5]:
147            rr.rdata = DNSgetstr(s,p)[0]
148            del(rr.rdlen)
149        elif type in DNSRR_DISPATCHER:
150            rr = DNSRR_DISPATCHER[type](b"\x00"+ret+s[p:p+rdlen], _orig_s=s, _orig_p=p)
151        else:
152          del(rr.rdlen)
153
154        p += rdlen
155
156        rr.rrname = name
157        return rr, p
158    def getfield(self, pkt, s):
159        if isinstance(s, tuple) :
160            s,p = s
161        else:
162            p = 0
163        ret = None
164        c = getattr(pkt, self.countfld)
165        if c > len(s):
166            warning("wrong value: DNS.%s=%i", self.countfld, c)
167            return s,b""
168        while c:
169            c -= 1
170            name,p = DNSgetstr(s,p)
171            rr,p = self.decodeRR(name, s, p)
172            if ret is None:
173                ret = rr
174            else:
175                ret.add_payload(rr)
176        if self.passon:
177            return (s,p),ret
178        else:
179            return s[p:],ret
180
181
182class DNSQRField(DNSRRField):
183    def decodeRR(self, name, s, p):
184        ret = s[p:p+4]
185        p += 4
186        rr = DNSQR(b"\x00"+ret, _orig_s=s, _orig_p=p)
187        rr.qname = name
188        return rr, p
189
190
191
192class RDataField(StrLenField):
193    def m2i(self, pkt, s):
194        family = None
195        if pkt.type == 1: # A
196            family = socket.AF_INET
197        elif pkt.type in [2, 5, 12]: # NS, CNAME, PTR
198            l = orb(s[0])
199            if l & 0xc0 and hasattr(pkt, "_orig_s") and pkt._orig_s: # Compression detected
200                p = ((l & ~0xc0) << 8) + orb(s[1]) - 12
201                s = DNSgetstr(pkt._orig_s, p)[0]
202            else: # No compression / Cannot decompress
203                if hasattr(pkt, "_orig_s") and pkt._orig_s:
204                    s = DNSgetstr(pkt._orig_s, pkt._orig_p)[0]
205                else:
206                    s = DNSgetstr(s, 0)[0]
207        elif pkt.type == 16: # TXT
208            ret_s = b""
209            tmp_s = s
210            # RDATA contains a list of strings, each are prepended with
211            # a byte containing the size of the following string.
212            while tmp_s:
213                tmp_len = orb(tmp_s[0]) + 1
214                if tmp_len > len(tmp_s):
215                  warning("DNS RR TXT prematured end of character-string (size=%i, remaining bytes=%i)" % (tmp_len, len(tmp_s)))
216                ret_s += tmp_s[1:tmp_len]
217                tmp_s = tmp_s[tmp_len:]
218            s = ret_s
219        elif pkt.type == 28: # AAAA
220            family = socket.AF_INET6
221        if family is not None:
222            s = inet_ntop(family, s)
223        return s
224    def i2m(self, pkt, s):
225        if pkt.type == 1: # A
226            if s:
227                s = inet_aton(s)
228        elif pkt.type in [2, 3, 4, 5, 12]: # NS, MD, MF, CNAME, PTR
229            s = b"".join(chb(len(x)) + x for x in s.split(b'.'))
230            if orb(s[-1]):
231                s += b"\x00"
232        elif pkt.type == 16: # TXT
233            if s:
234                s = raw(s)
235                ret_s = b""
236                # The initial string must be splitted into a list of strings
237                # prepended with theirs sizes.
238                while len(s) >= 255:
239                    ret_s += b"\xff" + s[:255]
240                    s = s[255:]
241                # The remaining string is less than 255 bytes long
242                if len(s):
243                    ret_s += struct.pack("!B", len(s)) + s
244                s = ret_s
245        elif pkt.type == 28: # AAAA
246            if s:
247                s = inet_pton(socket.AF_INET6, s)
248        return s
249
250class RDLenField(Field):
251    def __init__(self, name):
252        Field.__init__(self, name, None, "H")
253    def i2m(self, pkt, x):
254        if x is None:
255            rdataf = pkt.get_field("rdata")
256            x = len(rdataf.i2m(pkt, pkt.rdata))
257        return x
258    def i2h(self, pkt, x):
259        if x is None:
260            rdataf = pkt.get_field("rdata")
261            x = len(rdataf.i2m(pkt, pkt.rdata))
262        return x
263
264
265class DNS(Packet):
266    name = "DNS"
267    fields_desc = [
268        ConditionalField(ShortField("length", None),
269                         lambda p: isinstance(p.underlayer, TCP)),
270        ShortField("id", 0),
271        BitField("qr", 0, 1),
272        BitEnumField("opcode", 0, 4, {0: "QUERY", 1: "IQUERY", 2: "STATUS"}),
273        BitField("aa", 0, 1),
274        BitField("tc", 0, 1),
275        BitField("rd", 1, 1),
276        BitField("ra", 0, 1),
277        BitField("z", 0, 1),
278        # AD and CD bits are defined in RFC 2535
279        BitField("ad", 0, 1),  # Authentic Data
280        BitField("cd", 0, 1),  # Checking Disabled
281        BitEnumField("rcode", 0, 4, {0: "ok", 1: "format-error",
282                                     2: "server-failure", 3: "name-error",
283                                     4: "not-implemented", 5: "refused"}),
284        DNSRRCountField("qdcount", None, "qd"),
285        DNSRRCountField("ancount", None, "an"),
286        DNSRRCountField("nscount", None, "ns"),
287        DNSRRCountField("arcount", None, "ar"),
288        DNSQRField("qd", "qdcount"),
289        DNSRRField("an", "ancount"),
290        DNSRRField("ns", "nscount"),
291        DNSRRField("ar", "arcount", 0),
292    ]
293
294    def answers(self, other):
295        return (isinstance(other, DNS)
296                and self.id == other.id
297                and self.qr == 1
298                and other.qr == 0)
299
300    def mysummary(self):
301        type = ["Qry","Ans"][self.qr]
302        name = ""
303        if self.qr:
304            type = "Ans"
305            if self.ancount > 0 and isinstance(self.an, DNSRR):
306                name = ' "%s"' % self.an.rdata
307        else:
308            type = "Qry"
309            if self.qdcount > 0 and isinstance(self.qd, DNSQR):
310                name = ' "%s"' % self.qd.qname
311        return 'DNS %s%s ' % (type, name)
312
313    def post_build(self, pkt, pay):
314        if isinstance(self.underlayer, TCP) and self.length is None:
315            pkt = struct.pack("!H", len(pkt) - 2) + pkt[2:]
316        return pkt + pay
317
318
319# https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4
320dnstypes = {
321    0:"ANY",
322    1: "A", 2: "NS", 3: "MD", 4: "MF", 5: "CNAME", 6: "SOA", 7: "MB", 8: "MG",
323    9: "MR", 10: "NULL", 11: "WKS", 12: "PTR", 13: "HINFO", 14: "MINFO",
324    15: "MX", 16: "TXT", 17: "RP", 18: "AFSDB", 19: "X25", 20: "ISDN", 21: "RT",
325    22: "NSAP", 23: "NSAP-PTR", 24: "SIG", 25: "KEY", 26: "PX", 27: "GPOS",
326    28: "AAAA", 29: "LOC", 30: "NXT", 31: "EID", 32: "NIMLOC", 33: "SRV",
327    34: "ATMA", 35: "NAPTR", 36: "KX", 37: "CERT", 38: "A6", 39: "DNAME",
328    40: "SINK", 41: "OPT", 42: "APL", 43: "DS", 44: "SSHFP", 45: "IPSECKEY",
329    46: "RRSIG", 47: "NSEC", 48: "DNSKEY", 49: "DHCID", 50: "NSEC3",
330    51: "NSEC3PARAM", 52: "TLSA", 53: "SMIMEA", 55: "HIP", 56: "NINFO", 57: "RKEY",
331    58: "TALINK", 59: "CDS", 60: "CDNSKEY", 61: "OPENPGPKEY", 62: "CSYNC",
332    99: "SPF", 100: "UINFO", 101: "UID", 102: "GID", 103: "UNSPEC", 104: "NID",
333    105: "L32", 106: "L64", 107: "LP", 108: "EUI48", 109: "EUI64",
334    249: "TKEY", 250: "TSIG", 256: "URI", 257: "CAA", 258: "AVC",
335    32768: "TA", 32769: "DLV", 65535: "RESERVED"
336}
337
338dnsqtypes = {251: "IXFR", 252: "AXFR", 253: "MAILB", 254: "MAILA", 255: "ALL"}
339dnsqtypes.update(dnstypes)
340dnsclasses =  {1: 'IN',  2: 'CS',  3: 'CH',  4: 'HS',  255: 'ANY'}
341
342
343class DNSQR(InheritOriginDNSStrPacket):
344    name = "DNS Question Record"
345    show_indent=0
346    fields_desc = [DNSStrField("qname", "www.example.com"),
347                   ShortEnumField("qtype", 1, dnsqtypes),
348                   ShortEnumField("qclass", 1, dnsclasses)]
349
350
351
352# RFC 2671 - Extension Mechanisms for DNS (EDNS0)
353
354class EDNS0TLV(Packet):
355    name = "DNS EDNS0 TLV"
356    fields_desc = [ ShortEnumField("optcode", 0, { 0: "Reserved", 1: "LLQ", 2: "UL", 3: "NSID", 4: "Reserved", 5: "PING" }),
357                    FieldLenField("optlen", None, "optdata", fmt="H"),
358                    StrLenField("optdata", "", length_from=lambda pkt: pkt.optlen) ]
359
360    def extract_padding(self, p):
361        return "", p
362
363class DNSRROPT(InheritOriginDNSStrPacket):
364    name = "DNS OPT Resource Record"
365    fields_desc = [ DNSStrField("rrname",""),
366                    ShortEnumField("type", 41, dnstypes),
367                    ShortField("rclass", 4096),
368                    ByteField("extrcode", 0),
369                    ByteField("version", 0),
370                    # version 0 means EDNS0
371                    BitEnumField("z", 32768, 16, { 32768: "D0" }),
372                    # D0 means DNSSEC OK from RFC 3225
373                    FieldLenField("rdlen", None, length_of="rdata", fmt="H"),
374                    PacketListField("rdata", [], EDNS0TLV, length_from=lambda pkt: pkt.rdlen) ]
375
376# RFC 4034 - Resource Records for the DNS Security Extensions
377
378# 09/2013 from http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml
379dnssecalgotypes = { 0:"Reserved", 1:"RSA/MD5", 2:"Diffie-Hellman", 3:"DSA/SHA-1",
380                    4:"Reserved", 5:"RSA/SHA-1", 6:"DSA-NSEC3-SHA1",
381                    7:"RSASHA1-NSEC3-SHA1", 8:"RSA/SHA-256", 9:"Reserved",
382                   10:"RSA/SHA-512", 11:"Reserved", 12:"GOST R 34.10-2001",
383                   13:"ECDSA Curve P-256 with SHA-256", 14: "ECDSA Curve P-384 with SHA-384",
384                  252:"Reserved for Indirect Keys", 253:"Private algorithms - domain name",
385                  254:"Private algorithms - OID", 255:"Reserved" }
386
387# 09/2013 from http://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml
388dnssecdigesttypes = { 0:"Reserved", 1:"SHA-1", 2:"SHA-256", 3:"GOST R 34.11-94",  4:"SHA-384" }
389
390
391class TimeField(IntField):
392
393    def any2i(self, pkt, x):
394        if isinstance(x, str):
395            import time, calendar
396            t = time.strptime(x, "%Y%m%d%H%M%S")
397            return int(calendar.timegm(t))
398        return x
399
400    def i2repr(self, pkt, x):
401        import time
402        x = self.i2h(pkt, x)
403        t = time.strftime("%Y%m%d%H%M%S", time.gmtime(x))
404        return "%s (%d)" % (t ,x)
405
406
407def bitmap2RRlist(bitmap):
408    """
409    Decode the 'Type Bit Maps' field of the NSEC Resource Record into an
410    integer list.
411    """
412    # RFC 4034, 4.1.2. The Type Bit Maps Field
413
414    RRlist = []
415
416    while bitmap:
417
418        if len(bitmap) < 2:
419            warning("bitmap too short (%i)" % len(bitmap))
420            return
421
422        window_block = orb(bitmap[0]) # window number
423        offset = 256 * window_block # offset of the Resource Record
424        bitmap_len = orb(bitmap[1]) # length of the bitmap in bytes
425
426        if bitmap_len <= 0 or bitmap_len > 32:
427            warning("bitmap length is no valid (%i)" % bitmap_len)
428            return
429
430        tmp_bitmap = bitmap[2:2+bitmap_len]
431
432        # Let's compare each bit of tmp_bitmap and compute the real RR value
433        for b in range(len(tmp_bitmap)):
434            v = 128
435            for i in range(8):
436                if orb(tmp_bitmap[b]) & v:
437                    # each of the RR is encoded as a bit
438                    RRlist += [ offset + b*8 + i ]
439                v = v >> 1
440
441        # Next block if any
442        bitmap = bitmap[2+bitmap_len:]
443
444    return RRlist
445
446
447def RRlist2bitmap(lst):
448    """
449    Encode a list of integers representing Resource Records to a bitmap field
450    used in the NSEC Resource Record.
451    """
452    # RFC 4034, 4.1.2. The Type Bit Maps Field
453
454    import math
455
456    bitmap = b""
457    lst = [abs(x) for x in sorted(set(lst)) if x <= 65535]
458
459    # number of window blocks
460    max_window_blocks = int(math.ceil(lst[-1] / 256.))
461    min_window_blocks = int(math.floor(lst[0] / 256.))
462    if min_window_blocks == max_window_blocks:
463        max_window_blocks += 1
464
465    for wb in range(min_window_blocks, max_window_blocks+1):
466        # First, filter out RR not encoded in the current window block
467        # i.e. keep everything between 256*wb <= 256*(wb+1)
468        rrlist = sorted(x for x in lst if 256 * wb <= x < 256 * (wb + 1))
469        if not rrlist:
470            continue
471
472        # Compute the number of bytes used to store the bitmap
473        if rrlist[-1] == 0: # only one element in the list
474            bytes_count = 1
475        else:
476            max = rrlist[-1] - 256*wb
477            bytes_count = int(math.ceil(max // 8)) + 1  # use at least 1 byte
478        if bytes_count > 32: # Don't encode more than 256 bits / values
479            bytes_count = 32
480
481        bitmap += struct.pack("BB", wb, bytes_count)
482
483        # Generate the bitmap
484        # The idea is to remove out of range Resource Records with these steps
485        # 1. rescale to fit into 8 bits
486        # 2. x gives the bit position ; compute the corresponding value
487        # 3. sum everything
488        bitmap += b"".join(
489            struct.pack(
490                b"B",
491                sum(2 ** (7 - (x - 256 * wb) + (tmp * 8)) for x in rrlist
492                if 256 * wb + 8 * tmp <= x < 256 * wb + 8 * tmp + 8),
493            ) for tmp in range(bytes_count)
494        )
495
496    return bitmap
497
498
499class RRlistField(StrField):
500    def h2i(self, pkt, x):
501        if isinstance(x, list):
502            return RRlist2bitmap(x)
503        return x
504
505    def i2repr(self, pkt, x):
506        x = self.i2h(pkt, x)
507        rrlist = bitmap2RRlist(x)
508        return [ dnstypes.get(rr, rr) for rr in rrlist ] if rrlist else repr(x)
509
510
511class _DNSRRdummy(InheritOriginDNSStrPacket):
512    name = "Dummy class that implements post_build() for Resource Records"
513    def post_build(self, pkt, pay):
514        if not self.rdlen == None:
515            return pkt
516
517        lrrname = len(self.fields_desc[0].i2m("", self.getfieldval("rrname")))
518        l = len(pkt) - lrrname - 10
519        pkt = pkt[:lrrname+8] + struct.pack("!H", l) + pkt[lrrname+8+2:]
520
521        return pkt
522
523class DNSRRSOA(_DNSRRdummy):
524    name = "DNS SOA Resource Record"
525    fields_desc = [ DNSStrField("rrname",""),
526                    ShortEnumField("type", 6, dnstypes),
527                    ShortEnumField("rclass", 1, dnsclasses),
528                    IntField("ttl", 0),
529                    ShortField("rdlen", None),
530                    DNSStrField("mname", ""),
531                    DNSStrField("rname", ""),
532                    IntField("serial", 0),
533                    IntField("refresh", 0),
534                    IntField("retry", 0),
535                    IntField("expire", 0),
536                    IntField("minimum", 0)
537                  ]
538
539class DNSRRRSIG(_DNSRRdummy):
540    name = "DNS RRSIG Resource Record"
541    fields_desc = [ DNSStrField("rrname",""),
542                    ShortEnumField("type", 46, dnstypes),
543                    ShortEnumField("rclass", 1, dnsclasses),
544                    IntField("ttl", 0),
545                    ShortField("rdlen", None),
546                    ShortEnumField("typecovered", 1, dnstypes),
547                    ByteEnumField("algorithm", 5, dnssecalgotypes),
548                    ByteField("labels", 0),
549                    IntField("originalttl", 0),
550                    TimeField("expiration", 0),
551                    TimeField("inception", 0),
552                    ShortField("keytag", 0),
553                    DNSStrField("signersname", ""),
554                    StrField("signature", "")
555                  ]
556
557
558class DNSRRNSEC(_DNSRRdummy):
559    name = "DNS NSEC Resource Record"
560    fields_desc = [ DNSStrField("rrname",""),
561                    ShortEnumField("type", 47, dnstypes),
562                    ShortEnumField("rclass", 1, dnsclasses),
563                    IntField("ttl", 0),
564                    ShortField("rdlen", None),
565                    DNSStrField("nextname", ""),
566                    RRlistField("typebitmaps", "")
567                  ]
568
569
570class DNSRRDNSKEY(_DNSRRdummy):
571    name = "DNS DNSKEY Resource Record"
572    fields_desc = [ DNSStrField("rrname",""),
573                    ShortEnumField("type", 48, dnstypes),
574                    ShortEnumField("rclass", 1, dnsclasses),
575                    IntField("ttl", 0),
576                    ShortField("rdlen", None),
577                    FlagsField("flags", 256, 16, "S???????Z???????"),
578                    # S: Secure Entry Point
579                    # Z: Zone Key
580                    ByteField("protocol", 3),
581                    ByteEnumField("algorithm", 5, dnssecalgotypes),
582                    StrField("publickey", "")
583                  ]
584
585
586class DNSRRDS(_DNSRRdummy):
587    name = "DNS DS Resource Record"
588    fields_desc = [ DNSStrField("rrname",""),
589                    ShortEnumField("type", 43, dnstypes),
590                    ShortEnumField("rclass", 1, dnsclasses),
591                    IntField("ttl", 0),
592                    ShortField("rdlen", None),
593                    ShortField("keytag", 0),
594                    ByteEnumField("algorithm", 5, dnssecalgotypes),
595                    ByteEnumField("digesttype", 5, dnssecdigesttypes),
596                    StrField("digest", "")
597                  ]
598
599
600# RFC 5074 - DNSSEC Lookaside Validation (DLV)
601class DNSRRDLV(DNSRRDS):
602    name = "DNS DLV Resource Record"
603    def __init__(self, *args, **kargs):
604       DNSRRDS.__init__(self, *args, **kargs)
605       if not kargs.get('type', 0):
606           self.type = 32769
607
608# RFC 5155 - DNS Security (DNSSEC) Hashed Authenticated Denial of Existence
609class DNSRRNSEC3(_DNSRRdummy):
610    name = "DNS NSEC3 Resource Record"
611    fields_desc = [ DNSStrField("rrname",""),
612                    ShortEnumField("type", 50, dnstypes),
613                    ShortEnumField("rclass", 1, dnsclasses),
614                    IntField("ttl", 0),
615                    ShortField("rdlen", None),
616                    ByteField("hashalg", 0),
617                    BitEnumField("flags", 0, 8, {1:"Opt-Out"}),
618                    ShortField("iterations", 0),
619                    FieldLenField("saltlength", 0, fmt="!B", length_of="salt"),
620                    StrLenField("salt", "", length_from=lambda x: x.saltlength),
621                    FieldLenField("hashlength", 0, fmt="!B", length_of="nexthashedownername"),
622                    StrLenField("nexthashedownername", "", length_from=lambda x: x.hashlength),
623                    RRlistField("typebitmaps", "")
624                  ]
625
626
627class DNSRRNSEC3PARAM(_DNSRRdummy):
628    name = "DNS NSEC3PARAM Resource Record"
629    fields_desc = [ DNSStrField("rrname",""),
630                    ShortEnumField("type", 51, dnstypes),
631                    ShortEnumField("rclass", 1, dnsclasses),
632                    IntField("ttl", 0),
633                    ShortField("rdlen", None),
634                    ByteField("hashalg", 0),
635                    ByteField("flags", 0),
636                    ShortField("iterations", 0),
637                    FieldLenField("saltlength", 0, fmt="!B", length_of="salt"),
638                    StrLenField("salt", "", length_from=lambda pkt: pkt.saltlength)
639                  ]
640
641# RFC 2782 - A DNS RR for specifying the location of services (DNS SRV)
642
643class DNSRRSRV(InheritOriginDNSStrPacket):
644    name = "DNS SRV Resource Record"
645    fields_desc = [ DNSStrField("rrname",""),
646                    ShortEnumField("type", 51, dnstypes),
647                    ShortEnumField("rclass", 1, dnsclasses),
648                    IntField("ttl", 0),
649                    ShortField("rdlen", None),
650                    ShortField("priority", 0),
651                    ShortField("weight", 0),
652                    ShortField("port", 0),
653                    DNSStrField("target",""), ]
654
655# RFC 2845 - Secret Key Transaction Authentication for DNS (TSIG)
656tsig_algo_sizes = { "HMAC-MD5.SIG-ALG.REG.INT": 16,
657                    "hmac-sha1": 20 }
658
659class TimeSignedField(StrFixedLenField):
660    def __init__(self, name, default):
661        StrFixedLenField.__init__(self, name, default, 6)
662
663    def _convert_seconds(self, packed_seconds):
664        """Unpack the internal representation."""
665        seconds = struct.unpack("!H", packed_seconds[:2])[0]
666        seconds += struct.unpack("!I", packed_seconds[2:])[0]
667        return seconds
668
669    def h2i(self, pkt, seconds):
670        """Convert the number of seconds since 1-Jan-70 UTC to the packed
671           representation."""
672
673        if seconds is None:
674            seconds = 0
675
676        tmp_short = (seconds >> 32) & 0xFFFF
677        tmp_int = seconds & 0xFFFFFFFF
678
679        return struct.pack("!HI", tmp_short, tmp_int)
680
681    def i2h(self, pkt, packed_seconds):
682        """Convert the internal representation to the number of seconds
683           since 1-Jan-70 UTC."""
684
685        if packed_seconds is None:
686            return None
687
688        return self._convert_seconds(packed_seconds)
689
690    def i2repr(self, pkt, packed_seconds):
691        """Convert the internal representation to a nice one using the RFC
692           format."""
693        time_struct = time.gmtime(self._convert_seconds(packed_seconds))
694        return time.strftime("%a %b %d %H:%M:%S %Y", time_struct)
695
696class DNSRRTSIG(_DNSRRdummy):
697    name = "DNS TSIG Resource Record"
698    fields_desc = [ DNSStrField("rrname", ""),
699                    ShortEnumField("type", 250, dnstypes),
700                    ShortEnumField("rclass", 1, dnsclasses),
701                    IntField("ttl", 0),
702                    ShortField("rdlen", None),
703                    DNSStrField("algo_name", "hmac-sha1"),
704                    TimeSignedField("time_signed", 0),
705                    ShortField("fudge", 0),
706                    FieldLenField("mac_len", 20, fmt="!H", length_of="mac_data"),
707                    StrLenField("mac_data", "", length_from=lambda pkt: pkt.mac_len),
708                    ShortField("original_id", 0),
709                    ShortField("error", 0),
710                    FieldLenField("other_len", 0, fmt="!H", length_of="other_data"),
711                    StrLenField("other_data", "", length_from=lambda pkt: pkt.other_len)
712                  ]
713
714
715DNSRR_DISPATCHER = {
716    33: DNSRRSRV,        # RFC 2782
717    41: DNSRROPT,        # RFC 1671
718    43: DNSRRDS,         # RFC 4034
719    46: DNSRRRSIG,       # RFC 4034
720    47: DNSRRNSEC,       # RFC 4034
721    48: DNSRRDNSKEY,     # RFC 4034
722    50: DNSRRNSEC3,      # RFC 5155
723    51: DNSRRNSEC3PARAM, # RFC 5155
724    250: DNSRRTSIG,      # RFC 2845
725    32769: DNSRRDLV,     # RFC 4431
726}
727
728DNSSEC_CLASSES = tuple(six.itervalues(DNSRR_DISPATCHER))
729
730def isdnssecRR(obj):
731    return isinstance(obj, DNSSEC_CLASSES)
732
733class DNSRR(InheritOriginDNSStrPacket):
734    name = "DNS Resource Record"
735    show_indent=0
736    fields_desc = [ DNSStrField("rrname",""),
737                    ShortEnumField("type", 1, dnstypes),
738                    ShortEnumField("rclass", 1, dnsclasses),
739                    IntField("ttl", 0),
740                    RDLenField("rdlen"),
741                    RDataField("rdata", "", length_from=lambda pkt:pkt.rdlen) ]
742
743
744bind_layers(UDP, DNS, dport=5353)
745bind_layers(UDP, DNS, sport=5353)
746bind_layers(UDP, DNS, dport=53)
747bind_layers(UDP, DNS, sport=53)
748DestIPField.bind_addr(UDP, "224.0.0.251", dport=5353)
749DestIP6Field.bind_addr(UDP, "ff02::fb", dport=5353)
750bind_layers(TCP, DNS, dport=53)
751bind_layers(TCP, DNS, sport=53)
752
753
754@conf.commands.register
755def dyndns_add(nameserver, name, rdata, type="A", ttl=10):
756    """Send a DNS add message to a nameserver for "name" to have a new "rdata"
757dyndns_add(nameserver, name, rdata, type="A", ttl=10) -> result code (0=ok)
758
759example: dyndns_add("ns1.toto.com", "dyn.toto.com", "127.0.0.1")
760RFC2136
761"""
762    zone = name[name.find(".")+1:]
763    r=sr1(IP(dst=nameserver)/UDP()/DNS(opcode=5,
764                                       qd=[DNSQR(qname=zone, qtype="SOA")],
765                                       ns=[DNSRR(rrname=name, type="A",
766                                                 ttl=ttl, rdata=rdata)]),
767          verbose=0, timeout=5)
768    if r and r.haslayer(DNS):
769        return r.getlayer(DNS).rcode
770    else:
771        return -1
772
773
774
775
776@conf.commands.register
777def dyndns_del(nameserver, name, type="ALL", ttl=10):
778    """Send a DNS delete message to a nameserver for "name"
779dyndns_del(nameserver, name, type="ANY", ttl=10) -> result code (0=ok)
780
781example: dyndns_del("ns1.toto.com", "dyn.toto.com")
782RFC2136
783"""
784    zone = name[name.find(".")+1:]
785    r=sr1(IP(dst=nameserver)/UDP()/DNS(opcode=5,
786                                       qd=[DNSQR(qname=zone, qtype="SOA")],
787                                       ns=[DNSRR(rrname=name, type=type,
788                                                 rclass="ANY", ttl=0, rdata="")]),
789          verbose=0, timeout=5)
790    if r and r.haslayer(DNS):
791        return r.getlayer(DNS).rcode
792    else:
793        return -1
794
795
796class DNS_am(AnsweringMachine):
797    function_name="dns_spoof"
798    filter = "udp port 53"
799
800    def parse_options(self, joker="192.168.1.1", match=None):
801        if match is None:
802            self.match = {}
803        else:
804            self.match = match
805        self.joker=joker
806
807    def is_request(self, req):
808        return req.haslayer(DNS) and req.getlayer(DNS).qr == 0
809
810    def make_reply(self, req):
811        ip = req.getlayer(IP)
812        dns = req.getlayer(DNS)
813        resp = IP(dst=ip.src, src=ip.dst)/UDP(dport=ip.sport,sport=ip.dport)
814        rdata = self.match.get(dns.qd.qname, self.joker)
815        resp /= DNS(id=dns.id, qr=1, qd=dns.qd,
816                    an=DNSRR(rrname=dns.qd.qname, ttl=10, rdata=rdata))
817        return resp
818
819
820