Home | History | Annotate | Download | only in layers
      1 ## This file is part of Scapy
      2 ## See http://www.secdev.org/projects/scapy for more informations
      3 ## Copyright (C) Philippe Biondi <phil (at] secdev.org>
      4 ## This program is published under a GPLv2 license
      5 
      6 """
      7 DNS: Domain Name System.
      8 """
      9 
     10 from __future__ import absolute_import
     11 import socket,struct
     12 
     13 from scapy.config import conf
     14 from scapy.packet import *
     15 from scapy.fields import *
     16 from scapy.compat import *
     17 from scapy.ansmachine import *
     18 from scapy.sendrecv import sr1
     19 from scapy.layers.inet import IP, DestIPField, UDP, TCP
     20 from scapy.layers.inet6 import DestIP6Field
     21 from scapy.error import warning
     22 from functools import reduce
     23 import scapy.modules.six as six
     24 from scapy.modules.six.moves import range
     25 
     26 class 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 
     34 class 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 
     75 class 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 
     98 def 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 
    130 class 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 
    182 class 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 
    192 class 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 
    250 class 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 
    265 class 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
    320 dnstypes = {
    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 
    338 dnsqtypes = {251: "IXFR", 252: "AXFR", 253: "MAILB", 254: "MAILA", 255: "ALL"}
    339 dnsqtypes.update(dnstypes)
    340 dnsclasses =  {1: 'IN',  2: 'CS',  3: 'CH',  4: 'HS',  255: 'ANY'}
    341 
    342 
    343 class 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 
    354 class 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 
    363 class 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
    379 dnssecalgotypes = { 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
    388 dnssecdigesttypes = { 0:"Reserved", 1:"SHA-1", 2:"SHA-256", 3:"GOST R 34.11-94",  4:"SHA-384" }
    389 
    390 
    391 class 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 
    407 def 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 
    447 def 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 
    499 class 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 
    511 class _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 
    523 class 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 
    539 class 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 
    558 class 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 
    570 class 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 
    586 class 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)
    601 class 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
    609 class 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 
    627 class 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 
    643 class 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)
    656 tsig_algo_sizes = { "HMAC-MD5.SIG-ALG.REG.INT": 16,
    657                     "hmac-sha1": 20 }
    658 
    659 class 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 
    696 class 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 
    715 DNSRR_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 
    728 DNSSEC_CLASSES = tuple(six.itervalues(DNSRR_DISPATCHER))
    729 
    730 def isdnssecRR(obj):
    731     return isinstance(obj, DNSSEC_CLASSES)
    732 
    733 class 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 
    744 bind_layers(UDP, DNS, dport=5353)
    745 bind_layers(UDP, DNS, sport=5353)
    746 bind_layers(UDP, DNS, dport=53)
    747 bind_layers(UDP, DNS, sport=53)
    748 DestIPField.bind_addr(UDP, "224.0.0.251", dport=5353)
    749 DestIP6Field.bind_addr(UDP, "ff02::fb", dport=5353)
    750 bind_layers(TCP, DNS, dport=53)
    751 bind_layers(TCP, DNS, sport=53)
    752 
    753 
    754 @conf.commands.register
    755 def 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"
    757 dyndns_add(nameserver, name, rdata, type="A", ttl=10) -> result code (0=ok)
    758 
    759 example: dyndns_add("ns1.toto.com", "dyn.toto.com", "127.0.0.1")
    760 RFC2136
    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
    777 def dyndns_del(nameserver, name, type="ALL", ttl=10):
    778     """Send a DNS delete message to a nameserver for "name"
    779 dyndns_del(nameserver, name, type="ANY", ttl=10) -> result code (0=ok)
    780 
    781 example: dyndns_del("ns1.toto.com", "dyn.toto.com")
    782 RFC2136
    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 
    796 class 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