123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- """Domain Name System."""
- from pypacker import pypacker, triggerlist
- TriggerList = triggerlist.TriggerList
- import struct
- import logging
- unpack = struct.unpack
- logger = logging.getLogger("pypacker")
- DNS_Q = 0
- DNS_R = 1
- # Opcodes
- DNS_QUERY = 0
- DNS_IQUERY = 1
- DNS_STATUS = 2
- DNS_NOTIFY = 4
- DNS_UPDATE = 5
- # Flags
- DNS_AN = 0x8000 # this is a response
- DNS_CD = 0x0010 # checking disabled
- DNS_AD = 0x0020 # authenticated data
- DNS_Z = 0x0040 # unused
- DNS_RA = 0x0080 # recursion available
- DNS_RD = 0x0100 # recursion desired
- DNS_TC = 0x0200 # truncated
- DNS_AA = 0x0400 # authoritative answer
- # Response codes
- DNS_RCODE_NOERR = 0
- DNS_RCODE_FORMERR = 1
- DNS_RCODE_SERVFAIL = 2
- DNS_RCODE_NXDOMAIN = 3
- DNS_RCODE_NOTIMP = 4
- DNS_RCODE_REFUSED = 5
- DNS_RCODE_YXDOMAIN = 6
- DNS_RCODE_YXRRSET = 7
- DNS_RCODE_NXRRSET = 8
- DNS_RCODE_NOTAUTH = 9
- DNS_RCODE_NOTZONE = 10
- # RR types
- DNS_A = 1
- DNS_NS = 2
- DNS_CNAME = 5
- DNS_SOA = 6
- DNS_WKS = 11
- DNS_PTR = 12
- DNS_HINFO = 13
- DNS_MINFO = 14
- DNS_MX = 15
- DNS_TXT = 16
- DNS_RP = 17
- DNS_SIG = 24
- DNS_GPOS = 27
- DNS_AAAA = 28
- DNS_LOC = 29
- DNS_SRV = 33
- DNS_NAPTR = 35
- DNS_KX = 36
- DNS_CERT = 37
- DNS_DNAME = 39
- DNS_DS = 43
- DNS_SSHFP = 44
- DNS_IPSECKEY = 45
- DNS_RRSIG = 46
- DNS_NSEC = 47
- DNS_DNSKEY = 48
- DNS_DHCID = 49
- DNS_NSEC3 = 50
- DNS_NSEC3PARAM = 51
- DNS_TLSA = 52
- DNS_SPF = 99
- DNS_TKEY = 249
- DNS_TSIG = 250
- DNS_IXFR = 251
- DNS_AXFR = 252
- DNS_CAA = 257
- DNS_TA = 32768
- DNS_DLV = 32769
- # RR classes
- DNS_IN = 1
- DNS_CHAOS = 3
- DNS_HESIOD = 4
- DNS_ANY = 255
- class DNS(pypacker.Packet):
- __hdr__ = (
- ("id", "H", 0x1234),
- ("flags", "H", DNS_AD | DNS_RD),
- ("questions_amount", "H", 0),
- ("answers_amount", "H", 0),
- ("authrr_amount", "H", 0),
- ("addrr_amount", "H", 0),
- ("queries", None, TriggerList),
- ("answers", None, TriggerList),
- ("auths", None, TriggerList),
- ("addrecords", None, TriggerList)
- )
- class Query(pypacker.Packet):
- """DNS question."""
- __hdr__ = (
- ("name", None, b"\x03www\x04test\x03com\x00"),
- ("type", "H", DNS_A),
- ("cls", "H", DNS_IN)
- )
- name_s = pypacker.get_property_dnsname("name")
- def _dissect(self, buf):
- idx = buf.find(b"\x00")
- #logger.debug("name in Query: %s" % buf[:idx+1])
- self.name = buf[:idx + 1]
- #logger.debug("val / format: %s %s" % (self._name, self._name_format))
- return len(buf) # name (including 0) + type + cls
- class Answer(pypacker.Packet):
- """DNS resource record."""
- __hdr__ = (
- ("name", "H", 0xc00c),
- ("type", "H", DNS_A),
- ("cls", "H", DNS_IN),
- ("ttl", "I", 180),
- ("dlen", "H", 4), # length of the next field
- ("address", None, b"1234") # eg IPv4
- )
- def _dissect(self, buf):
- # needed set format
- addr_len = unpack(">H", buf[10:12])[0]
- self.address = buf[12:12 + addr_len]
- # logger.debug("address: %s" % self.address)
- return 12 + addr_len
- class Auth(pypacker.Packet):
- """Auth, generic type."""
- __hdr__ = (
- ("name", "H", 0),
- ("type", "H", 0),
- ("cls", "H", 0),
- ("ttl", "I", 0),
- ("dlen", "H", 0), # length of the rest of header: server + x, x becmoes body content
- ("server", None, b"\x03www\x04test\x03com\x00")
- # TODO: add fields for mailbox, serial, refresh etc.
- )
- server_s = pypacker.get_property_dnsname("server")
- def _dissect(self, buf):
- # needed set format
- # find server name by 0-termination
- idx = buf.find(b"\x00", 12)
- if idx == -1:
- idx = len(buf)
- self.server = buf[12: idx + 1]
- # logger.debug("server: %s" % self.server)
- return idx + 1
- class AuthSOA(pypacker.Packet):
- """
- Auth type SOA.
- Not used atm
- """
- __hdr__ = (
- ("name", "H", 0),
- ("type", "H", 0),
- ("cls", "H", 0),
- ("ttl", "I", 0),
- ("dlen", "H", 0),
- ("name2", None, b"\x03www\x04test\x03com\x00"),
- ("mailbox", None, b"\x03www\x04test\x03com\x00"),
- ("pserver", "H", 0),
- ("mbox", "H", 0),
- ("serial", "H", 0),
- ("refresh", "H", 0),
- ("retry", "H", 0),
- ("expire", "H", 0),
- ("minttl", "H", 0)
- )
- name_s = pypacker.get_property_dnsname("name")
- mailbox_s = pypacker.get_property_dnsname("mailbox")
- def _dissect(self, buf):
- # set format
- # find server name by 0-termination
- idx = buf.find(b"\x00", 12)
- # logger.debug(buf[12: idx+1])
- # don't add trailing \0
- self.name = buf[12: idx + 1]
- # logger.debug("name: %s" % buf[idx + 1: -14])
- self.mailbox = buf[idx + 1: -14]
- return len(buf)
- class AddRecord(pypacker.Packet):
- """DNS additional records."""
- __hdr__ = (
- ("name", "H", 0),
- ("type", "H", 0x0001),
- ("clz", "H", 0x0001),
- ("ts", "I", 0),
- ("dlen", "H", 0),
- ("addr", None, b"\x01\x02\x03\x04")
- )
- def _dissect(self, buf):
- # logger.debug(buf[0: idx+1])
- self.addr = buf[12:]
- # logger.debug("addr: %s" % self.addr)
- return len(buf)
- class AddRecordRoot(pypacker.Packet):
- """DNS additional records."""
- __hdr__ = (
- ("name", "B", 0),
- ("type", "H", 0x0001),
- ("udpsize", "H", 0x0001),
- ("rcode", "B", 0),
- ("v", "B", 0),
- ("z", "H", 0),
- ("dlen", "H", 0)
- )
- def _dissect(self, buf):
- # unpack basic data to get things done
- quests_amount, ans_amount, authserver_amount, addreq_amount = unpack(">HHHH", buf[4:12])
- off = 12
- # TODO: use lazy dissect, dns seems to be too shitty for this
- #
- # parse queries
- #
- #logger.debug(">>> parsing questions: %d" % quests_amount)
- while quests_amount > 0:
- # find name by 0-termination
- idx = buf.find(b"\x00", off)
- q_end = idx + 5
- #logger.debug("name is: %s" % buf[off: q_end-4])
- #logger.debug("Query is: %s" % buf[off: q_end])
- #logger.debug(len(buf[off: q_end]))
- q = DNS.Query(buf[off: q_end])
- # logger.debug("query is following..")
- #logger.debug("Query: %s" % q)
- # logger.debug("query name format: %s" % q._name_format)
- self.queries.append(q)
- off = q_end
- quests_amount -= 1
- #
- # parse answers
- #
- #logger.debug(">>> parsing answers: %d" % ans_amount)
- while ans_amount > 0:
- # find name by label/0-termination
- # TODO: handle non-label names
- dlen = unpack(">H", buf[off + 10: off + 12])[0]
- index_end = 12 + dlen
- # logger.debug("Answer is: %r" % buf[off: off + index_end])
- a = DNS.Answer(buf[off: off + index_end])
- # logger.debug("Answer: %s" % a)
- self.answers.append(a)
- off += index_end
- ans_amount -= 1
- #
- # parse authorative servers
- #
- #logger.debug(">>> parsing authorative servers: %d" % authserver_amount)
- while authserver_amount > 0:
- dlen = unpack(">H", buf[off + 10: off + 12])[0]
- authlen = 12 + dlen
- # logger.debug("Auth: %r" % buf[off: off + authlen])
- a = DNS.Auth(buf[off: off + authlen])
- # logger.debug("Auth server: %s" % a)
- self.auths.append(a)
- off += authlen
- authserver_amount -= 1
- #
- # parse additional requests
- #
- #logger.debug(">>> parsing additional records: %d" % addreq_amount)
- while addreq_amount > 0:
- if buf[off: off + 3] == b"\x00\x00\x29":
- a = DNS.AddRecordRoot(buf[off: off + 11])
- #logger.debug(a)
- #logger.debug(a.bin())
- #logger.debug(len(a.bin()))
- off += 11
- else:
- # logger.debug(buf[idx:])
- # logger.debug(buf[off:])
- # logger.debug("data length via: %r" % buf[idx + 9: idx + 11])
- dlen = unpack(">H", buf[off + 10: off + 10 + 2])[0]
- # logger.debug("AddRecord: %s" % buf[off: off + 12 + dlen])
- a = DNS.AddRecord(buf[off: off + 12 + dlen])
- # logger.debug("Additional Record: %s" % a)
- off += 12 + dlen
- self.addrecords.append(a)
- addreq_amount -= 1
- # logger.debug("dns: %s" % self)
- return off
- def bin(self, update_auto_fields=True):
- if update_auto_fields and self._header_changed:
- # logger.debug("updating lenghts")
- # avoid lazy dissect by checking for [b"bytes", dissect_callback]
- # first assigning to length will trigger _unpack(...)
- if self._queries.__class__ is not list:
- self.questions_amount = len(self.queries)
- if self._answers.__class__ is not list:
- self.answers_amount = len(self.answers)
- if self._auths.__class__ is not list:
- self.authrr_amount = len(self.auths)
- if self._addrecords.__class__ is not list:
- self.addrr_amount = len(self.addrecords)
- # logger.debug("finished updating lengths")
- return pypacker.Packet.bin(self, update_auto_fields=update_auto_fields)
|