## This file is part of Scapy
## See http://www.secdev.org/projects/scapy for more informations
## Copyright (C) Philippe Biondi <phil@secdev.org>
## This program is published under a GPLv2 license

"""
DNS: Domain Name System.
"""

import socket,struct

from scapy.packet import *
from scapy.fields import *
from scapy.ansmachine import *
from scapy.layers.inet import IP, UDP
from scapy.utils import str2bytes

class DNSStrField(StrField):

    def h2i(self, pkt, x):
      if type(x) == str:
        x = x.encode('ascii')
      if x == b"":
        return b"."
      return x

    def i2m(self, pkt, x):
        if type(x) == str:
          x = x.encode('ascii')
        if x == b".":
          return b"\x00"

        x = [k[:63] for k in x.split(b".")] # Truncate chunks that cannot be encoded (more than 63 bytes..)
        x = map(lambda y: bytes([len(y)]) + y, x)
        x = b"".join(x)
        if x[-1] != 0:
            x += b"\x00"
        return x

    def getfield(self, pkt, s):
        n = b""

        #if ord(s[0]) == 0:
        if (s[0]) == 0:
          return s[1:], b"."

        while 1:
            #l = ord(s[0])
            l = (s[0])
            s = s[1:]
            if not l:
                break
            if l & 0xc0:
                raise Scapy_Exception("DNS message can't be compressed at this point!")
            else:
                n += s[:l]+b"."
                s = s[l:]
        return s, n


class DNSRRCountField(ShortField):
    holds_packets=1
    def __init__(self, name, default, rr):
        ShortField.__init__(self, name, default)
        self.rr = rr
    def _countRR(self, pkt):
        x = getattr(pkt,self.rr)
        i = 0
        while isinstance(x, DNSRR) or isinstance(x, DNSQR) or isdnssecRR(x):
            x = x.payload
            i += 1
        return i
        
    def i2m(self, pkt, x):
        if x is None:
            x = self._countRR(pkt)
        return x
    def i2h(self, pkt, x):
        if x is None:
            x = self._countRR(pkt)
        return x
    

def DNSgetstr(s,p):
    name = b""
    q = 0
    jpath = [p]
    while 1:
        if p >= len(s):
            warning("DNS RR prematured end (ofs=%i, len=%i)"%(p,len(s)))
            break
        #l = ord(s[p])
        l = s[p]
        p += 1
        if l & 0xc0:
            if not q:
                q = p+1
            if p >= len(s):
                warning("DNS incomplete jump token at (ofs=%i)" % p)
                break
            p = ((l & 0x3f) << 8) + s[p] - 12
            if p in jpath:
                warning("DNS decompression loop detected")
                break
            jpath.append(p)
            continue
        elif l > 0:
            name += s[p:p+l]+b"."
            p += l
            continue
        break
    if q:
        p = q
    return name,p
        

class DNSRRField(StrField):
    holds_packets=1
    def __init__(self, name, countfld, passon=1):
        StrField.__init__(self, name, None)
        self.countfld = countfld
        self.passon = passon
    def i2m(self, pkt, x):
        if x is None:
            return b""
        return bytes(x)
    def decodeRR(self, name, s, p):
        ret = s[p:p+10]
        type,cls,ttl,rdlen = struct.unpack("!HHIH", ret)
        p += 10
        rr = DNSRR(b"\x00"+ret+s[p:p+rdlen])
        if type in [2, 3, 4, 5]:
            rr.rdata = DNSgetstr(s,p)[0]
            del(rr.rdlen)
        elif type in dnsRRdispatcher.keys():
            rr = dnsRRdispatcher[type](b"\x00"+ret+s[p:p+rdlen])
        else:
          del(rr.rdlen)
        
        p += rdlen
        
        rr.rrname = name
        return rr,p
    def getfield(self, pkt, s):
        if type(s) is tuple :
            s,p = s
        else:
            p = 0
        ret = None
        c = getattr(pkt, self.countfld)
        if c > len(s):
            warning("wrong value: DNS.%s=%i" % (self.countfld,c))
            return s,b""
        while c:
            c -= 1
            name,p = DNSgetstr(s,p)
            rr,p = self.decodeRR(name, s, p)
            if ret is None:
                ret = rr
            else:
                ret.add_payload(rr)
        if self.passon:
            return (s,p),ret
        else:
            return s[p:],ret
            
            
class DNSQRField(DNSRRField):
    holds_packets=1
    def decodeRR(self, name, s, p):
        ret = s[p:p+4]
        p += 4
        rr = DNSQR(b"\x00"+ret)
        rr.qname = name
        return rr,p
        
        

class RDataField(StrLenField):
    def m2i(self, pkt, s):
        family = None
        if pkt.type == 1: # A
            family = socket.AF_INET
        elif pkt.type == 12: # PTR
            s = DNSgetstr(s, 0)[0]
        elif pkt.type == 16: # TXT
            ret_s = b""
            tmp_s = s
            # RDATA contains a list of strings, each are prepended with
            # a byte containing the size of the following string.
            while tmp_s:
                tmp_len = struct.unpack("!B", bytes([tmp_s[0]]))[0] + 1
                if tmp_len > len(tmp_s):
                  warning("DNS RR TXT prematured end of character-string (size=%i, remaining bytes=%i)" % (tmp_len, len(tmp_s)))
                ret_s += tmp_s[1:tmp_len]
                tmp_s = tmp_s[tmp_len:]
            s = ret_s
        elif pkt.type == 28: # AAAA
            family = socket.AF_INET6
        if family is not None:    
            s = inet_ntop(family, s)
        return s
    def i2m(self, pkt, s):
        if pkt.type == 1: # A
            if s:
                if type(s) is bytes:
                  s = s.decode('ascii')
                s = inet_aton(s)
        elif pkt.type in [2,3,4,5]: # NS, MD, MF, CNAME
            s = b"".join(map(lambda x: bytes([len(x)]) + x, s.split(b".")))
            #if ord(s[-1]):
            if s[-1]:
                s += b"\x00"
        elif pkt.type == 16: # TXT
            if s:
                ret_s = b""
                # The initial string must be splitted into a list of strings
                # prepended with theirs sizes.
                while len(s) >= 255:
                    ret_s += b"\xff" + s[:255]
                    s = s[255:]
                # The remaining string is less than 255 bytes long    
                if len(s):
                    ret_s += struct.pack("!B", len(s)) + s
                s = ret_s
        elif pkt.type == 28: # AAAA
            if s:
                s = inet_pton(socket.AF_INET6, s)
        return s

class RDLenField(Field):
    def __init__(self, name):
        Field.__init__(self, name, None, "H")
    def i2m(self, pkt, x):
        if x is None:
            rdataf = pkt.get_field("rdata")
            x = len(rdataf.i2m(pkt, pkt.rdata))
        return x
    def i2h(self, pkt, x):
        if x is None:
            rdataf = pkt.get_field("rdata")
            x = len(rdataf.i2m(pkt, pkt.rdata))
        return x
    

class DNS(Packet):
    name = "DNS"
    fields_desc = [ ShortField("id", 0),
                    BitField("qr", 0, 1),
                    BitEnumField("opcode", 0, 4, {0:"QUERY",1:"IQUERY",2:"STATUS"}),
                    BitField("aa", 0, 1),
                    BitField("tc", 0, 1),
                    BitField("rd", 0, 1),
                    BitField("ra", 0, 1),
                    BitField("z", 0, 1),
                    # AD and CD bits are defined in RFC 2535
                    BitField("ad", 0, 1), # Authentic Data
                    BitField("cd", 0, 1), # Checking Disabled
                    BitEnumField("rcode", 0, 4, {0:"ok", 1:"format-error", 2:"server-failure", 3:"name-error", 4:"not-implemented", 5:"refused"}),
                    DNSRRCountField("qdcount", None, "qd"),
                    DNSRRCountField("ancount", None, "an"),
                    DNSRRCountField("nscount", None, "ns"),
                    DNSRRCountField("arcount", None, "ar"),
                    DNSQRField("qd", "qdcount"),
                    DNSRRField("an", "ancount"),
                    DNSRRField("ns", "nscount"),
                    DNSRRField("ar", "arcount",0) ]
    def answers(self, other):
        return (isinstance(other, DNS)
                and self.id == other.id
                and self.qr == 1
                and other.qr == 0)
        
    def mysummary(self):
        type = ["Qry","Ans"][self.qr]
        name = ""
        if self.qr:
            type = "Ans"
            if self.ancount > 0 and isinstance(self.an, DNSRR):
                name = ' "%s"' % self.an.getstrval("rdata")
        else:
            type = "Qry"
            if self.qdcount > 0 and isinstance(self.qd, DNSQR):
                name = ' "%s"' % self.qd.getstrval("qname")
        return 'DNS %s%s ' % (type, name)

dnstypes = { 0:"ANY", 255:"ALL",
             1:"A", 2:"NS", 3:"MD", 4:"MF", 5:"CNAME", 6:"SOA", 7: "MB", 8:"MG",
             9:"MR",10:"NULL",11:"WKS",12:"PTR",13:"HINFO",14:"MINFO",15:"MX",16:"TXT",
             17:"RP",18:"AFSDB",28:"AAAA", 33:"SRV",38:"A6",39:"DNAME",
             41:"OPT", 43:"DS", 46:"RRSIG", 47:"NSEC", 48:"DNSKEY",
             50: "NSEC3", 51: "NSEC3PARAM", 32769:"DLV" }

dnsqtypes = {251:"IXFR",252:"AXFR",253:"MAILB",254:"MAILA",255:"ALL"}
dnsqtypes.update(dnstypes)
dnsclasses =  {1: 'IN',  2: 'CS',  3: 'CH',  4: 'HS',  255: 'ANY'}


class DNSQR(Packet):
    name = "DNS Question Record"
    show_indent=0
    fields_desc = [ DNSStrField("qname",b""),
                    ShortEnumField("qtype", 1, dnsqtypes),
                    ShortEnumField("qclass", 1, dnsclasses) ]
                    
                    

# RFC 2671 - Extension Mechanisms for DNS (EDNS0)

class EDNS0TLV(Packet):
    name = "DNS EDNS0 TLV"
    fields_desc = [ ShortEnumField("optcode", 0, { 0: "Reserved", 1: "LLQ", 2: "UL", 3: "NSID", 4: "Reserved", 5: "PING" }),
                    FieldLenField("optlen", None, "optdata", fmt="H"),
                    StrLenField("optdata", b"", length_from=lambda pkt: pkt.optlen) ]

    def extract_padding(self, p):
        return b"", p

class DNSRROPT(Packet):
    name = "DNS OPT Resource Record"
    fields_desc = [ DNSStrField("rrname",b""),
                    ShortEnumField("type", 41, dnstypes),
                    ShortField("rclass", 4096),
                    ByteField("extrcode", 0),
                    ByteField("version", 0),
                    # version 0 means EDNS0
                    BitEnumField("z", 32768, 16, { 32768: "D0" }),
                    # D0 means DNSSEC OK from RFC 3225
                    FieldLenField("rdlen", None, length_of="rdata", fmt="H"),
                    PacketListField("rdata", [], EDNS0TLV, length_from=lambda pkt: pkt.rdlen) ]

# RFC 4034 - Resource Records for the DNS Security Extensions

# 09/2013 from http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml
dnssecalgotypes = { 0:"Reserved", 1:"RSA/MD5", 2:"Diffie-Hellman", 3:"DSA/SHA-1", 
                    4:"Reserved", 5:"RSA/SHA-1", 6:"DSA-NSEC3-SHA1",
                    7:"RSASHA1-NSEC3-SHA1", 8:"RSA/SHA-256", 9:"Reserved",
                   10:"RSA/SHA-512", 11:"Reserved", 12:"GOST R 34.10-2001",
                   13:"ECDSA Curve P-256 with SHA-256", 14: "ECDSA Curve P-384 with SHA-384",
                  252:"Reserved for Indirect Keys", 253:"Private algorithms - domain name",
                  254:"Private algorithms - OID", 255:"Reserved" }

# 09/2013 from http://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml
dnssecdigesttypes = { 0:"Reserved", 1:"SHA-1", 2:"SHA-256", 3:"GOST R 34.11-94",  4:"SHA-384" }


class TimeField(IntField):

    def any2i(self, pkt, x):
        if type(x) == str:
            import time, calendar
            t = time.strptime(x, "%Y%m%d%H%M%S")
            return int(calendar.timegm(t))
        return x

    def i2repr(self, pkt, x):
        import time
        x = self.i2h(pkt, x)
        t = time.strftime("%Y%m%d%H%M%S", time.gmtime(x))
        return "%s (%d)" % (t ,x) 


def bitmap2RRlist(bitmap):
    """
    Decode the 'Type Bit Maps' field of the NSEC Resource Record into an 
    integer list.
    """
    # RFC 4034, 4.1.2. The Type Bit Maps Field

    RRlist = []

    while bitmap:

        if len(bitmap) < 2:
            warning("bitmap too short (%i)" % len(bitmap))
            return
   
        #window_block = ord(bitmap[0]) # window number
        window_block = (bitmap[0]) # window number
        offset = 256*window_block # offset of the Ressource Record
        #bitmap_len = ord(bitmap[0]) # length of the bitmap in bytes
        bitmap_len = (bitmap[1]) # length of the bitmap in bytes
        
        if bitmap_len <= 0 or bitmap_len > 32:
            warning("bitmap length is no valid (%i)" % bitmap_len)
            return
        
        tmp_bitmap = bitmap[2:2+bitmap_len]
        
        # Let's compare each bit of tmp_bitmap and compute the real RR value
        for b in range(len(tmp_bitmap)):
            v = 128
            for i in range(8):
                #if ord(tmp_bitmap[b]) & v:
                if (tmp_bitmap[b]) & v:
                    # each of the RR is encoded as a bit
                    RRlist += [ offset + b*8 + i ] 
                v = v >> 1

# Next block if any
        bitmap = bitmap[2+bitmap_len:]

    return RRlist


def RRlist2bitmap(lst):
    """
    Encode a list of integers representing Resource Records to a bitmap field 
    used in the NSEC Resource Record.
    """
    # RFC 4034, 4.1.2. The Type Bit Maps Field

    import math

    bitmap = b""
    lst = list(set(lst))
    lst.sort()
    
    #lst = filter(lambda x: x <= 65535, lst)
    #lst = map(lambda x: abs(x), lst)
    lst = [ abs(x) for x in lst if x<= 65535 ]

    # number of window blocks
    max_window_blocks = int(math.ceil(lst[-1] / 256.))
    min_window_blocks = int(math.floor(lst[0] / 256.))
    if min_window_blocks == max_window_blocks:
        max_window_blocks += 1

    for wb in range(min_window_blocks, max_window_blocks+1):
        # First, filter out RR not encoded in the current window block
        # i.e. keep everything between 256*wb <= 256*(wb+1)
        #rrlist = filter(lambda x: 256*wb <= x and x < 256*(wb+1), lst)
        rrlist = [ x for x in lst if 256*wb <= x and x < 256*(wb+1) ]
        rrlist.sort()
        if rrlist == []:
            continue
     
        # Compute the number of bytes used to store the bitmap
        if rrlist[-1] == 0: # only one element in the list
            bs = 1
        else:
            max = rrlist[-1] - 256*wb
            #bs = int(math.ceil(max / 8)) + 1  # use at least 1 byte
            bs = int(max // 8) + 1  # use at least 1 byte
        if bs > 32: # Don't encode more than 256 bits / values
            bs = 32

        bitmap += struct.pack("B", wb)
        bitmap += struct.pack("B", bs)

        # Generate the bitmap
        for tmp in range(bs):
            v = 0
            # Remove out of range Ressource Records
            #tmp_rrlist = filter(lambda x: 256*wb+8*tmp <= x and x < 256*wb+8*tmp+8, rrlist)
            tmp_rrlist = [ x for x in rrlist if 256*wb+8*tmp <= x and x < 256*wb+8*tmp+8 ]
            if not tmp_rrlist == []:
                # 1. rescale to fit into 8 bits
                tmp_rrlist = map(lambda x: (x-256*wb)-(tmp*8), tmp_rrlist)
                # 2. x gives the bit position ; compute the corresponding value
                tmp_rrlist = map(lambda x: 2**(7-x) , tmp_rrlist)
                # 3. sum everything
                #v = reduce(lambda x,y: x+y, tmp_rrlist)
                v = sum(tmp_rrlist)
            bitmap += struct.pack("B", v)
   
    return bitmap


class RRlistField(StrField):
    def h2i(self, pkt, x):
        if type(x) == list:
            return RRlist2bitmap(x)
        return x

    def i2repr(self, pkt, x):
        x = self.i2h(pkt, x)
        rrlist = bitmap2RRlist(x)
        return [ dnstypes.get(rr, rr) for rr in rrlist ]


class _DNSRRdummy(Packet):
    name = "Dummy class that implements post_build() for Ressource Records"
    def post_build(self, pkt, pay):
        if not self.rdlen == None:
            return pkt

        lrrname = len(self.fields_desc[0].i2m(b"", self.getfieldval("rrname")))
        l = len(pkt) - lrrname - 10
        pkt = pkt[:lrrname+8] + struct.pack("!H", l) + pkt[lrrname+8+2:]
        
        return pkt

class DNSRRSOA(_DNSRRdummy):
    name = "DNS SOA Resource Record"
    fields_desc = [ DNSStrField("rrname",b""),
                    ShortEnumField("type", 6, dnstypes),
                    ShortEnumField("rclass", 1, dnsclasses),
                    IntField("ttl", 0),
                    ShortField("rdlen", None),
                    DNSStrField("mname", b""),
                    DNSStrField("rname", b""),
                    IntField("serial", 0),
                    IntField("refresh", 0),
                    IntField("retry", 0),
                    IntField("expire", 0),
                    IntField("minimum", 0)
                  ]

class DNSRRRSIG(_DNSRRdummy):
    name = "DNS RRSIG Resource Record"
    fields_desc = [ DNSStrField("rrname",b""),
                    ShortEnumField("type", 46, dnstypes),
                    ShortEnumField("rclass", 1, dnsclasses),
                    IntField("ttl", 0),
                    ShortField("rdlen", None),
                    ShortEnumField("typecovered", 1, dnstypes),
                    ByteEnumField("algorithm", 5, dnssecalgotypes),
                    ByteField("labels", 0),
                    IntField("originalttl", 0),
                    TimeField("expiration", 0),
                    TimeField("inception", 0),
                    ShortField("keytag", 0),
                    DNSStrField("signersname", b""),
                    StrField("signature", b"")
                  ]


class DNSRRNSEC(_DNSRRdummy):
    name = "DNS NSEC Resource Record"
    fields_desc = [ DNSStrField("rrname",b""),
                    ShortEnumField("type", 47, dnstypes),
                    ShortEnumField("rclass", 1, dnsclasses),
                    IntField("ttl", 0),
                    ShortField("rdlen", None),
                    DNSStrField("nextname", b""),
                    RRlistField("typebitmaps", b"")
                  ]


class DNSRRDNSKEY(_DNSRRdummy):
    name = "DNS DNSKEY Resource Record"
    fields_desc = [ DNSStrField("rrname",b""),
                    ShortEnumField("type", 48, dnstypes),
                    ShortEnumField("rclass", 1, dnsclasses),
                    IntField("ttl", 0),
                    ShortField("rdlen", None),
                    FlagsField("flags", 256, 16, "S???????Z???????"),
                    # S: Secure Entry Point
                    # Z: Zone Key
                    ByteField("protocol", 3),
                    ByteEnumField("algorithm", 5, dnssecalgotypes),
                    StrField("publickey", b"")
                  ]


class DNSRRDS(_DNSRRdummy):
    name = "DNS DS Resource Record"
    fields_desc = [ DNSStrField("rrname",b""),
                    ShortEnumField("type", 43, dnstypes),
                    ShortEnumField("rclass", 1, dnsclasses),
                    IntField("ttl", 0),
                    ShortField("rdlen", None),
                    ShortField("keytag", 0),
                    ByteEnumField("algorithm", 5, dnssecalgotypes),
                    ByteEnumField("digesttype", 5, dnssecdigesttypes),
                    StrField("digest", b"")
                  ]


# RFC 5074 - DNSSEC Lookaside Validation (DLV)
class DNSRRDLV(DNSRRDS):
    name = "DNS DLV Resource Record"
    def __init__(self, *args, **kargs):
       DNSRRDS.__init__(self, *args, **kargs)
       if not kargs.get('type', 0):
           self.type = 32769

# RFC 5155 - DNS Security (DNSSEC) Hashed Authenticated Denial of Existence
class DNSRRNSEC3(_DNSRRdummy):
    name = "DNS NSEC3 Resource Record"
    fields_desc = [ DNSStrField("rrname",b""),
                    ShortEnumField("type", 50, dnstypes),
                    ShortEnumField("rclass", 1, dnsclasses),
                    IntField("ttl", 0),
                    ShortField("rdlen", None),
                    ByteField("hashalg", 0), 
                    BitEnumField("flags", 0, 8, {1:"Opt-Out"}),
                    ShortField("iterations", 0), 
                    FieldLenField("saltlength", 0, fmt="!B", length_of="salt"),
                    StrLenField("salt", b"", length_from=lambda x: x.saltlength),
                    FieldLenField("hashlength", 0, fmt="!B", length_of="nexthashedownername"),
                    StrLenField("nexthashedownername", b"", length_from=lambda x: x.hashlength),
                    RRlistField("typebitmaps", b"")
                  ]


class DNSRRNSEC3PARAM(_DNSRRdummy):
    name = "DNS NSEC3PARAM Resource Record"
    fields_desc = [ DNSStrField("rrname",b""),
                    ShortEnumField("type", 51, dnstypes),
                    ShortEnumField("rclass", 1, dnsclasses),
                    IntField("ttl", 0),
                    ShortField("rdlen", None),
                    ByteField("hashalg", 0), 
                    ByteField("flags", 0), 
                    ShortField("iterations", 0), 
                    FieldLenField("saltlength", 0, fmt="!B", length_of="salt"),
                    StrLenField("salt", b"", length_from=lambda pkt: pkt.saltlength)
                  ]


dnssecclasses = [ DNSRROPT, DNSRRRSIG, DNSRRDLV, DNSRRDNSKEY, DNSRRNSEC, DNSRRDS, DNSRRNSEC3, DNSRRNSEC3PARAM ]

def isdnssecRR(obj):
    list = [ isinstance (obj, cls) for cls in dnssecclasses ]
    ret = False
    for i in list:
      ret = ret or i
    return ret

dnsRRdispatcher = {     #6: DNSRRSOA,
                       41: DNSRROPT,        # RFC 1671
                       43: DNSRRDS,         # RFC 4034
                       46: DNSRRRSIG,       # RFC 4034
                       47: DNSRRNSEC,       # RFC 4034
                       48: DNSRRDNSKEY,     # RFC 4034
                       50: DNSRRNSEC3,      # RFC 5155
                       51: DNSRRNSEC3PARAM, # RFC 5155
                    32769: DNSRRDLV         # RFC 4431
                   }

class DNSRR(Packet):
    name = "DNS Resource Record"
    show_indent=0
    fields_desc = [ DNSStrField("rrname",""),
                    ShortEnumField("type", 1, dnstypes),
                    ShortEnumField("rclass", 1, dnsclasses),
                    IntField("ttl", 0),
                    RDLenField("rdlen"),
                    RDataField("rdata", "", length_from=lambda pkt:pkt.rdlen) ]

bind_layers( UDP,           DNS,           dport=53)
bind_layers( UDP,           DNS,           sport=53)


@conf.commands.register
def dyndns_add(nameserver, name, rdata, type="A", ttl=10):
    """Send a DNS add message to a nameserver for "name" to have a new "rdata"
dyndns_add(nameserver, name, rdata, type="A", ttl=10) -> result code (0=ok)

example: dyndns_add("ns1.toto.com", "dyn.toto.com", "127.0.0.1")
RFC2136
"""
    zone = name[name.find(".")+1:]
    r=sr1(IP(dst=nameserver)/UDP()/DNS(opcode=5,
                                       qd=[DNSQR(qname=zone, qtype="SOA")],
                                       ns=[DNSRR(rrname=name, type="A",
                                                 ttl=ttl, rdata=rdata)]),
          verbose=0, timeout=5)
    if r and r.haslayer(DNS):
        return r.getlayer(DNS).rcode
    else:
        return -1
    
    
    

@conf.commands.register
def dyndns_del(nameserver, name, type="ALL", ttl=10):
    """Send a DNS delete message to a nameserver for "name"
dyndns_del(nameserver, name, type="ANY", ttl=10) -> result code (0=ok)

example: dyndns_del("ns1.toto.com", "dyn.toto.com")
RFC2136
"""
    zone = name[name.find(".")+1:]
    r=sr1(IP(dst=nameserver)/UDP()/DNS(opcode=5,
                                       qd=[DNSQR(qname=zone, qtype="SOA")],
                                       ns=[DNSRR(rrname=name, type=type,
                                                 rclass="ANY", ttl=0, rdata=b"")]),
          verbose=0, timeout=5)
    if r and r.haslayer(DNS):
        return r.getlayer(DNS).rcode
    else:
        return -1
    

class DNS_am(AnsweringMachine):
    function_name="dns_spoof"
    filter = "udp port 53"

    def parse_options(self, joker="192.168.1.1", match=None):
        if match is None:
            self.match = {}
        else:
            self.match = match
        self.joker=joker

    def is_request(self, req):
        return req.haslayer(DNS) and req.getlayer(DNS).qr == 0
    
    def make_reply(self, req):
        ip = req.getlayer(IP)
        dns = req.getlayer(DNS)
        resp = IP(dst=ip.src, src=ip.dst)/UDP(dport=ip.sport,sport=ip.dport)
        rdata = self.match.get(dns.qd.qname, self.joker)
        resp /= DNS(id=dns.id, qr=1, qd=dns.qd,
                    an=DNSRR(rrname=dns.qd.qname, ttl=10, rdata=rdata))
        return resp


