|
@@ -1,610 +0,0 @@
|
|
|
-#!@PYTHON@
|
|
|
-
|
|
|
-# Copyright (C) 2010 Internet Systems Consortium.
|
|
|
-#
|
|
|
-# Permission to use, copy, modify, and distribute this software for any
|
|
|
-# purpose with or without fee is hereby granted, provided that the above
|
|
|
-# copyright notice and this permission notice appear in all copies.
|
|
|
-#
|
|
|
-# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
|
|
|
-# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
|
|
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
|
|
|
-# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
|
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
|
|
|
-# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
|
|
-# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
|
|
|
-# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
-
|
|
|
-import configparser, re, time, socket, sys
|
|
|
-from datetime import datetime
|
|
|
-from optparse import OptionParser
|
|
|
-
|
|
|
-re_hex = re.compile(r'^0x[0-9a-fA-F]+')
|
|
|
-re_decimal = re.compile(r'^\d+$')
|
|
|
-re_string = re.compile(r"\'(.*)\'$")
|
|
|
-
|
|
|
-dnssec_timefmt = '%Y%m%d%H%M%S'
|
|
|
-
|
|
|
-dict_qr = { 'query' : 0, 'response' : 1 }
|
|
|
-dict_opcode = { 'query' : 0, 'iquery' : 1, 'status' : 2, 'notify' : 4,
|
|
|
- 'update' : 5 }
|
|
|
-rdict_opcode = dict([(dict_opcode[k], k.upper()) for k in dict_opcode.keys()])
|
|
|
-dict_rcode = { 'noerror' : 0, 'formerr' : 1, 'servfail' : 2, 'nxdomain' : 3,
|
|
|
- 'notimp' : 4, 'refused' : 5, 'yxdomain' : 6, 'yxrrset' : 7,
|
|
|
- 'nxrrset' : 8, 'notauth' : 9, 'notzone' : 10 }
|
|
|
-rdict_rcode = dict([(dict_rcode[k], k.upper()) for k in dict_rcode.keys()])
|
|
|
-dict_rrtype = { 'none' : 0, 'a' : 1, 'ns' : 2, 'md' : 3, 'mf' : 4, 'cname' : 5,
|
|
|
- 'soa' : 6, 'mb' : 7, 'mg' : 8, 'mr' : 9, 'null' : 10,
|
|
|
- 'wks' : 11, 'ptr' : 12, 'hinfo' : 13, 'minfo' : 14, 'mx' : 15,
|
|
|
- 'txt' : 16, 'rp' : 17, 'afsdb' : 18, 'x25' : 19, 'isdn' : 20,
|
|
|
- 'rt' : 21, 'nsap' : 22, 'nsap_tr' : 23, 'sig' : 24, 'key' : 25,
|
|
|
- 'px' : 26, 'gpos' : 27, 'aaaa' : 28, 'loc' : 29, 'nxt' : 30,
|
|
|
- 'srv' : 33, 'naptr' : 35, 'kx' : 36, 'cert' : 37, 'a6' : 38,
|
|
|
- 'dname' : 39, 'opt' : 41, 'apl' : 42, 'ds' : 43, 'sshfp' : 44,
|
|
|
- 'ipseckey' : 45, 'rrsig' : 46, 'nsec' : 47, 'dnskey' : 48,
|
|
|
- 'dhcid' : 49, 'nsec3' : 50, 'nsec3param' : 51, 'hip' : 55,
|
|
|
- 'spf' : 99, 'unspec' : 103, 'tkey' : 249, 'tsig' : 250,
|
|
|
- 'dlv' : 32769, 'ixfr' : 251, 'axfr' : 252, 'mailb' : 253,
|
|
|
- 'maila' : 254, 'any' : 255 }
|
|
|
-rdict_rrtype = dict([(dict_rrtype[k], k.upper()) for k in dict_rrtype.keys()])
|
|
|
-dict_rrclass = { 'in' : 1, 'ch' : 3, 'hs' : 4, 'any' : 255 }
|
|
|
-rdict_rrclass = dict([(dict_rrclass[k], k.upper()) for k in \
|
|
|
- dict_rrclass.keys()])
|
|
|
-dict_algorithm = { 'rsamd5' : 1, 'dh' : 2, 'dsa' : 3, 'ecc' : 4,
|
|
|
- 'rsasha1' : 5 }
|
|
|
-dict_nsec3_algorithm = { 'reserved' : 0, 'sha1' : 1 }
|
|
|
-rdict_algorithm = dict([(dict_algorithm[k], k.upper()) for k in \
|
|
|
- dict_algorithm.keys()])
|
|
|
-rdict_nsec3_algorithm = dict([(dict_nsec3_algorithm[k], k.upper()) for k in \
|
|
|
- dict_nsec3_algorithm.keys()])
|
|
|
-
|
|
|
-header_xtables = { 'qr' : dict_qr, 'opcode' : dict_opcode,
|
|
|
- 'rcode' : dict_rcode }
|
|
|
-question_xtables = { 'rrtype' : dict_rrtype, 'rrclass' : dict_rrclass }
|
|
|
-rrsig_xtables = { 'algorithm' : dict_algorithm }
|
|
|
-
|
|
|
-def parse_value(value, xtable = {}):
|
|
|
- if re.search(re_hex, value):
|
|
|
- return int(value, 16)
|
|
|
- if re.search(re_decimal, value):
|
|
|
- return int(value)
|
|
|
- m = re.match(re_string, value)
|
|
|
- if m:
|
|
|
- return m.group(1)
|
|
|
- lovalue = value.lower()
|
|
|
- if lovalue in xtable:
|
|
|
- return xtable[lovalue]
|
|
|
- return value
|
|
|
-
|
|
|
-def code_totext(code, dict):
|
|
|
- if code in dict.keys():
|
|
|
- return dict[code] + '(' + str(code) + ')'
|
|
|
- return str(code)
|
|
|
-
|
|
|
-def encode_name(name, absolute=True):
|
|
|
- # make sure the name is dot-terminated. duplicate dots will be ignored
|
|
|
- # below.
|
|
|
- name += '.'
|
|
|
- labels = name.split('.')
|
|
|
- wire = ''
|
|
|
- for l in labels:
|
|
|
- if len(l) > 4 and l[0:4] == 'ptr=':
|
|
|
- # special meta-syntax for compression pointer
|
|
|
- wire += '%04x' % (0xc000 | int(l[4:]))
|
|
|
- break
|
|
|
- if absolute or len(l) > 0:
|
|
|
- wire += '%02x' % len(l)
|
|
|
- wire += ''.join(['%02x' % ord(ch) for ch in l])
|
|
|
- if len(l) == 0:
|
|
|
- break
|
|
|
- return wire
|
|
|
-
|
|
|
-def encode_string(name, len=None):
|
|
|
- if type(name) is int and len is not None:
|
|
|
- return '%0.*x' % (len * 2, name)
|
|
|
- return ''.join(['%02x' % ord(ch) for ch in name])
|
|
|
-
|
|
|
-def count_namelabels(name):
|
|
|
- if name == '.': # special case
|
|
|
- return 0
|
|
|
- m = re.match('^(.*)\.$', name)
|
|
|
- if m:
|
|
|
- name = m.group(1)
|
|
|
- return len(name.split('.'))
|
|
|
-
|
|
|
-def get_config(config, section, configobj, xtables = {}):
|
|
|
- try:
|
|
|
- for field in config.options(section):
|
|
|
- value = config.get(section, field)
|
|
|
- if field in xtables.keys():
|
|
|
- xtable = xtables[field]
|
|
|
- else:
|
|
|
- xtable = {}
|
|
|
- configobj.__dict__[field] = parse_value(value, xtable)
|
|
|
- except configparser.NoSectionError:
|
|
|
- return False
|
|
|
- return True
|
|
|
-
|
|
|
-def print_header(f, input_file):
|
|
|
- f.write('''###
|
|
|
-### This data file was auto-generated from ''' + input_file + '''
|
|
|
-###
|
|
|
-''')
|
|
|
-
|
|
|
-class Name:
|
|
|
- name = 'example.com'
|
|
|
- pointer = None # no compression by default
|
|
|
- def dump(self, f):
|
|
|
- name = self.name
|
|
|
- if self.pointer is not None:
|
|
|
- if len(name) > 0 and name[-1] != '.':
|
|
|
- name += '.'
|
|
|
- name += 'ptr=%d' % self.pointer
|
|
|
- name_wire = encode_name(name)
|
|
|
- f.write('\n# DNS Name: %s' % self.name)
|
|
|
- if self.pointer is not None:
|
|
|
- f.write(' + compression pointer: %d' % self.pointer)
|
|
|
- f.write('\n')
|
|
|
- f.write('%s' % name_wire)
|
|
|
- f.write('\n')
|
|
|
-
|
|
|
-class DNSHeader:
|
|
|
- id = 0x1035
|
|
|
- (qr, aa, tc, rd, ra, ad, cd) = 0, 0, 0, 0, 0, 0, 0
|
|
|
- mbz = 0
|
|
|
- rcode = 0 # noerror
|
|
|
- opcode = 0 # query
|
|
|
- (qdcount, ancount, nscount, arcount) = 1, 0, 0, 0
|
|
|
- def dump(self, f):
|
|
|
- f.write('\n# Header Section\n')
|
|
|
- f.write('# ID=' + str(self.id))
|
|
|
- f.write(' QR=' + ('Response' if self.qr else 'Query'))
|
|
|
- f.write(' Opcode=' + code_totext(self.opcode, rdict_opcode))
|
|
|
- f.write(' Rcode=' + code_totext(self.rcode, rdict_rcode))
|
|
|
- f.write('%s' % (' AA' if self.aa else ''))
|
|
|
- f.write('%s' % (' TC' if self.tc else ''))
|
|
|
- f.write('%s' % (' RD' if self.rd else ''))
|
|
|
- f.write('%s' % (' AD' if self.ad else ''))
|
|
|
- f.write('%s' % (' CD' if self.cd else ''))
|
|
|
- f.write('\n')
|
|
|
- f.write('%04x ' % self.id)
|
|
|
- flag_and_code = 0
|
|
|
- flag_and_code |= (self.qr << 15 | self.opcode << 14 | self.aa << 10 |
|
|
|
- self.tc << 9 | self.rd << 8 | self.ra << 7 |
|
|
|
- self.mbz << 6 | self.ad << 5 | self.cd << 4 |
|
|
|
- self.rcode)
|
|
|
- f.write('%04x\n' % flag_and_code)
|
|
|
- f.write('# QDCNT=%d, ANCNT=%d, NSCNT=%d, ARCNT=%d\n' %
|
|
|
- (self.qdcount, self.ancount, self.nscount, self.arcount))
|
|
|
- f.write('%04x %04x %04x %04x\n' % (self.qdcount, self.ancount,
|
|
|
- self.nscount, self.arcount))
|
|
|
-
|
|
|
-class DNSQuestion:
|
|
|
- name = 'example.com.'
|
|
|
- rrtype = parse_value('A', dict_rrtype)
|
|
|
- rrclass = parse_value('IN', dict_rrclass)
|
|
|
- def dump(self, f):
|
|
|
- f.write('\n# Question Section\n')
|
|
|
- f.write('# QNAME=%s QTYPE=%s QCLASS=%s\n' %
|
|
|
- (self.name,
|
|
|
- code_totext(self.rrtype, rdict_rrtype),
|
|
|
- code_totext(self.rrclass, rdict_rrclass)))
|
|
|
- f.write(encode_name(self.name))
|
|
|
- f.write(' %04x %04x\n' % (self.rrtype, self.rrclass))
|
|
|
-
|
|
|
-class EDNS:
|
|
|
- name = '.'
|
|
|
- udpsize = 4096
|
|
|
- extrcode = 0
|
|
|
- version = 0
|
|
|
- do = 0
|
|
|
- mbz = 0
|
|
|
- rdlen = 0
|
|
|
- def dump(self, f):
|
|
|
- f.write('\n# EDNS OPT RR\n')
|
|
|
- f.write('# NAME=%s TYPE=%s UDPSize=%d ExtRcode=%s Version=%s DO=%d\n' %
|
|
|
- (self.name, code_totext(dict_rrtype['opt'], rdict_rrtype),
|
|
|
- self.udpsize, self.extrcode, self.version,
|
|
|
- 1 if self.do else 0))
|
|
|
-
|
|
|
- code_vers = (self.extrcode << 8) | (self.version & 0x00ff)
|
|
|
- extflags = (self.do << 15) | (self.mbz & 0x8000)
|
|
|
- f.write('%s %04x %04x %04x %04x\n' %
|
|
|
- (encode_name(self.name), dict_rrtype['opt'], self.udpsize,
|
|
|
- code_vers, extflags))
|
|
|
- f.write('# RDLEN=%d\n' % self.rdlen)
|
|
|
- f.write('%04x\n' % self.rdlen)
|
|
|
-
|
|
|
-class RR:
|
|
|
- '''This is a base class for various types of RR test data.
|
|
|
- For each RR type (A, AAAA, NS, etc), we define a derived class of RR
|
|
|
- to dump type specific RDATA parameters. This class defines parameters
|
|
|
- common to all types of RDATA, namely the owner name, RR class and TTL.
|
|
|
- The dump() method of derived classes are expected to call dump_header(),
|
|
|
- whose default implementation is provided in this class. This method
|
|
|
- decides whether to dump the test data as an RR (with name, type, class)
|
|
|
- or only as RDATA (with its length), and dumps the corresponding data
|
|
|
- via the specified file object.
|
|
|
-
|
|
|
- By convention we assume derived classes are named after the common
|
|
|
- standard mnemonic of the corresponding RR types. For example, the
|
|
|
- derived class for the RR type SOA should be named "SOA".
|
|
|
-
|
|
|
- Configurable parameters are as follows:
|
|
|
- - as_rr (bool): Whether or not the data is to be dumped as an RR. False
|
|
|
- by default.
|
|
|
- - rr_class (string): The RR class of the data. Only meaningful when the
|
|
|
- data is dumped as an RR. Default is 'IN'.
|
|
|
- - rr_ttl (integer): The TTL value of the RR. Only meaningful when the
|
|
|
- data is dumped as an RR. Default is 86400 (1 day).
|
|
|
- '''
|
|
|
-
|
|
|
- def __init__(self):
|
|
|
- self.as_rr = False
|
|
|
- # only when as_rr is True, same for class/TTL:
|
|
|
- self.rr_name = 'example.com'
|
|
|
- self.rr_class = 'IN'
|
|
|
- self.rr_ttl = 86400
|
|
|
- def dump_header(self, f, rdlen):
|
|
|
- type_txt = self.__class__.__name__
|
|
|
- type_code = parse_value(type_txt, dict_rrtype)
|
|
|
- if self.as_rr:
|
|
|
- rrclass = parse_value(self.rr_class, dict_rrclass)
|
|
|
- f.write('\n# %s RR (QNAME=%s Class=%s TTL=%d RDLEN=%d)\n' %
|
|
|
- (type_txt, self.rr_name,
|
|
|
- code_totext(rrclass, rdict_rrclass), self.rr_ttl, rdlen))
|
|
|
- f.write('%s %04x %04x %08x %04x\n' %
|
|
|
- (encode_name(self.rr_name), type_code, rrclass,
|
|
|
- self.rr_ttl, rdlen))
|
|
|
- else:
|
|
|
- f.write('\n# %s RDATA (RDLEN=%d)\n' % (type_txt, rdlen))
|
|
|
- f.write('%04x\n' % rdlen)
|
|
|
-
|
|
|
-class A(RR):
|
|
|
- rdlen = 4 # fixed by default
|
|
|
- address = '192.0.2.1'
|
|
|
-
|
|
|
- def dump(self, f):
|
|
|
- self.dump_header(f, self.rdlen)
|
|
|
- f.write('# Address=%s\n' % (self.address))
|
|
|
- bin_address = socket.inet_aton(self.address)
|
|
|
- f.write('%02x%02x%02x%02x\n' % (bin_address[0], bin_address[1],
|
|
|
- bin_address[2], bin_address[3]))
|
|
|
-
|
|
|
-class NS(RR):
|
|
|
- rdlen = None # auto calculate
|
|
|
- nsname = 'ns.example.com'
|
|
|
-
|
|
|
- def dump(self, f):
|
|
|
- nsname_wire = encode_name(self.nsname)
|
|
|
- if self.rdlen is None:
|
|
|
- self.rdlen = len(nsname_wire) / 2
|
|
|
- self.dump_header(f, self.rdlen)
|
|
|
- f.write('# NS name=%s\n' % (self.nsname))
|
|
|
- f.write('%s\n' % nsname_wire)
|
|
|
-
|
|
|
-class SOA(RR):
|
|
|
- rdlen = None # auto-calculate
|
|
|
- mname = 'ns.example.com'
|
|
|
- rname = 'root.example.com'
|
|
|
- serial = 2010012601
|
|
|
- refresh = 3600
|
|
|
- retry = 300
|
|
|
- expire = 3600000
|
|
|
- minimum = 1200
|
|
|
- def dump(self, f):
|
|
|
- mname_wire = encode_name(self.mname)
|
|
|
- rname_wire = encode_name(self.rname)
|
|
|
- if self.rdlen is None:
|
|
|
- self.rdlen = int(20 + len(mname_wire) / 2 + len(str(rname_wire)) / 2)
|
|
|
- self.dump_header(f, self.rdlen)
|
|
|
- f.write('# NNAME=%s RNAME=%s\n' % (self.mname, self.rname))
|
|
|
- f.write('%s %s\n' % (mname_wire, rname_wire))
|
|
|
- f.write('# SERIAL(%d) REFRESH(%d) RETRY(%d) EXPIRE(%d) MINIMUM(%d)\n' %
|
|
|
- (self.serial, self.refresh, self.retry, self.expire,
|
|
|
- self.minimum))
|
|
|
- f.write('%08x %08x %08x %08x %08x\n' % (self.serial, self.refresh,
|
|
|
- self.retry, self.expire,
|
|
|
- self.minimum))
|
|
|
-
|
|
|
-class TXT(RR):
|
|
|
- rdlen = None # auto-calculate
|
|
|
- nstring = 1 # number of character-strings
|
|
|
- stringlen = -1 # default string length, auto-calculate
|
|
|
- string = 'Test String' # default string
|
|
|
- def dump(self, f):
|
|
|
- stringlen_list = []
|
|
|
- string_list = []
|
|
|
- wirestring_list = []
|
|
|
- for i in range(0, self.nstring):
|
|
|
- key_string = 'string' + str(i)
|
|
|
- if key_string in self.__dict__:
|
|
|
- string_list.append(self.__dict__[key_string])
|
|
|
- else:
|
|
|
- string_list.append(self.string)
|
|
|
- wirestring_list.append(encode_string(string_list[-1]))
|
|
|
- key_stringlen = 'stringlen' + str(i)
|
|
|
- if key_stringlen in self.__dict__:
|
|
|
- stringlen_list.append(self.__dict__[key_stringlen])
|
|
|
- else:
|
|
|
- stringlen_list.append(self.stringlen)
|
|
|
- if stringlen_list[-1] < 0:
|
|
|
- stringlen_list[-1] = int(len(wirestring_list[-1]) / 2)
|
|
|
- if self.rdlen is None:
|
|
|
- self.rdlen = int(len(''.join(wirestring_list)) / 2) + self.nstring
|
|
|
- self.dump_header(f, self.rdlen)
|
|
|
- for i in range(0, self.nstring):
|
|
|
- f.write('# String Len=%d, String=\"%s\"\n' %
|
|
|
- (stringlen_list[i], string_list[i]))
|
|
|
- f.write('%02x%s%s\n' % (stringlen_list[i],
|
|
|
- ' ' if len(wirestring_list[i]) > 0 else '',
|
|
|
- wirestring_list[i]))
|
|
|
-
|
|
|
-class RP:
|
|
|
- '''Implements rendering RP RDATA in the wire format.
|
|
|
- Configurable parameters are as follows:
|
|
|
- - rdlen: 16-bit RDATA length. If omitted, the accurate value is auto
|
|
|
- calculated and used; if negative, the RDLEN field will be omitted from
|
|
|
- the output data.
|
|
|
- - mailbox: The mailbox field.
|
|
|
- - text: The text field.
|
|
|
- All of these parameters have the default values and can be omitted.
|
|
|
- '''
|
|
|
- rdlen = None # auto-calculate
|
|
|
- mailbox = 'root.example.com'
|
|
|
- text = 'rp-text.example.com'
|
|
|
- def dump(self, f):
|
|
|
- mailbox_wire = encode_name(self.mailbox)
|
|
|
- text_wire = encode_name(self.text)
|
|
|
- if self.rdlen is None:
|
|
|
- self.rdlen = (len(mailbox_wire) + len(text_wire)) / 2
|
|
|
- else:
|
|
|
- self.rdlen = int(self.rdlen)
|
|
|
- if self.rdlen >= 0:
|
|
|
- f.write('\n# RP RDATA (RDLEN=%d)\n' % self.rdlen)
|
|
|
- f.write('%04x\n' % self.rdlen)
|
|
|
- else:
|
|
|
- f.write('\n# RP RDATA (RDLEN omitted)\n')
|
|
|
- f.write('# MAILBOX=%s TEXT=%s\n' % (self.mailbox, self.text))
|
|
|
- f.write('%s %s\n' % (mailbox_wire, text_wire))
|
|
|
-
|
|
|
-class NSECBASE:
|
|
|
- '''Implements rendering NSEC/NSEC3 type bitmaps commonly used for
|
|
|
- these RRs. The NSEC and NSEC3 classes will be inherited from this
|
|
|
- class.'''
|
|
|
- nbitmap = 1 # number of bitmaps
|
|
|
- block = 0
|
|
|
- maplen = None # default bitmap length, auto-calculate
|
|
|
- bitmap = '040000000003' # an arbtrarily chosen bitmap sample
|
|
|
- def dump(self, f):
|
|
|
- # first, construct the bitmpa data
|
|
|
- block_list = []
|
|
|
- maplen_list = []
|
|
|
- bitmap_list = []
|
|
|
- for i in range(0, self.nbitmap):
|
|
|
- key_bitmap = 'bitmap' + str(i)
|
|
|
- if key_bitmap in self.__dict__:
|
|
|
- bitmap_list.append(self.__dict__[key_bitmap])
|
|
|
- else:
|
|
|
- bitmap_list.append(self.bitmap)
|
|
|
- key_maplen = 'maplen' + str(i)
|
|
|
- if key_maplen in self.__dict__:
|
|
|
- maplen_list.append(self.__dict__[key_maplen])
|
|
|
- else:
|
|
|
- maplen_list.append(self.maplen)
|
|
|
- if maplen_list[-1] is None: # calculate it if not specified
|
|
|
- maplen_list[-1] = int(len(bitmap_list[-1]) / 2)
|
|
|
- key_block = 'block' + str(i)
|
|
|
- if key_block in self.__dict__:
|
|
|
- block_list.append(self.__dict__[key_block])
|
|
|
- else:
|
|
|
- block_list.append(self.block)
|
|
|
-
|
|
|
- # dump RR-type specific part (NSEC or NSEC3)
|
|
|
- self.dump_fixedpart(f, 2 * self.nbitmap + \
|
|
|
- int(len(''.join(bitmap_list)) / 2))
|
|
|
-
|
|
|
- # dump the bitmap
|
|
|
- for i in range(0, self.nbitmap):
|
|
|
- f.write('# Bitmap: Block=%d, Length=%d\n' %
|
|
|
- (block_list[i], maplen_list[i]))
|
|
|
- f.write('%02x %02x %s\n' %
|
|
|
- (block_list[i], maplen_list[i], bitmap_list[i]))
|
|
|
-
|
|
|
-class NSEC(NSECBASE):
|
|
|
- rdlen = None # auto-calculate
|
|
|
- nextname = 'next.example.com'
|
|
|
- def dump_fixedpart(self, f, bitmap_totallen):
|
|
|
- name_wire = encode_name(self.nextname)
|
|
|
- if self.rdlen is None:
|
|
|
- # if rdlen needs to be calculated, it must be based on the bitmap
|
|
|
- # length, because the configured maplen can be fake.
|
|
|
- self.rdlen = int(len(name_wire) / 2) + bitmap_totallen
|
|
|
- f.write('\n# NSEC RDATA (RDLEN=%d)\n' % self.rdlen)
|
|
|
- f.write('%04x\n' % self.rdlen);
|
|
|
- f.write('# Next Name=%s (%d bytes)\n' % (self.nextname,
|
|
|
- int(len(name_wire) / 2)))
|
|
|
- f.write('%s\n' % name_wire)
|
|
|
-
|
|
|
-class NSEC3(NSECBASE):
|
|
|
- rdlen = None # auto-calculate
|
|
|
- hashalg = 1 # SHA-1
|
|
|
- optout = False # opt-out flag
|
|
|
- mbz = 0 # other flag fields (none defined yet)
|
|
|
- iterations = 1
|
|
|
- saltlen = 5
|
|
|
- salt = 's' * saltlen
|
|
|
- hashlen = 20
|
|
|
- hash = 'h' * hashlen
|
|
|
- def dump_fixedpart(self, f, bitmap_totallen):
|
|
|
- if self.rdlen is None:
|
|
|
- # if rdlen needs to be calculated, it must be based on the bitmap
|
|
|
- # length, because the configured maplen can be fake.
|
|
|
- self.rdlen = 4 + 1 + len(self.salt) + 1 + len(self.hash) \
|
|
|
- + bitmap_totallen
|
|
|
- f.write('\n# NSEC3 RDATA (RDLEN=%d)\n' % self.rdlen)
|
|
|
- f.write('%04x\n' % self.rdlen)
|
|
|
- optout_val = 1 if self.optout else 0
|
|
|
- f.write('# Hash Alg=%s, Opt-Out=%d, Other Flags=%0x, Iterations=%d\n' %
|
|
|
- (code_totext(self.hashalg, rdict_nsec3_algorithm),
|
|
|
- optout_val, self.mbz, self.iterations))
|
|
|
- f.write('%02x %02x %04x\n' %
|
|
|
- (self.hashalg, (self.mbz << 1) | optout_val, self.iterations))
|
|
|
- f.write("# Salt Len=%d, Salt='%s'\n" % (self.saltlen, self.salt))
|
|
|
- f.write('%02x%s%s\n' % (self.saltlen,
|
|
|
- ' ' if len(self.salt) > 0 else '',
|
|
|
- encode_string(self.salt)))
|
|
|
- f.write("# Hash Len=%d, Hash='%s'\n" % (self.hashlen, self.hash))
|
|
|
- f.write('%02x%s%s\n' % (self.hashlen,
|
|
|
- ' ' if len(self.hash) > 0 else '',
|
|
|
- encode_string(self.hash)))
|
|
|
-
|
|
|
-class RRSIG:
|
|
|
- rdlen = -1 # auto-calculate
|
|
|
- covered = 1 # A
|
|
|
- algorithm = 5 # RSA-SHA1
|
|
|
- labels = -1 # auto-calculate (#labels of signer)
|
|
|
- originalttl = 3600
|
|
|
- expiration = int(time.mktime(datetime.strptime('20100131120000',
|
|
|
- dnssec_timefmt).timetuple()))
|
|
|
- inception = int(time.mktime(datetime.strptime('20100101120000',
|
|
|
- dnssec_timefmt).timetuple()))
|
|
|
- tag = 0x1035
|
|
|
- signer = 'example.com'
|
|
|
- signature = 0x123456789abcdef123456789abcdef
|
|
|
- def dump(self, f):
|
|
|
- name_wire = encode_name(self.signer)
|
|
|
- sig_wire = '%x' % self.signature
|
|
|
- rdlen = self.rdlen
|
|
|
- if rdlen < 0:
|
|
|
- rdlen = int(18 + len(name_wire) / 2 + len(str(sig_wire)) / 2)
|
|
|
- labels = self.labels
|
|
|
- if labels < 0:
|
|
|
- labels = count_namelabels(self.signer)
|
|
|
- f.write('\n# RRSIG RDATA (RDLEN=%d)\n' % rdlen)
|
|
|
- f.write('%04x\n' % rdlen);
|
|
|
- f.write('# Covered=%s Algorithm=%s Labels=%d OrigTTL=%d\n' %
|
|
|
- (code_totext(self.covered, rdict_rrtype),
|
|
|
- code_totext(self.algorithm, rdict_algorithm), labels,
|
|
|
- self.originalttl))
|
|
|
- f.write('%04x %02x %02x %08x\n' % (self.covered, self.algorithm,
|
|
|
- labels, self.originalttl))
|
|
|
- f.write('# Expiration=%s, Inception=%s\n' %
|
|
|
- (str(self.expiration), str(self.inception)))
|
|
|
- f.write('%08x %08x\n' % (self.expiration, self.inception))
|
|
|
- f.write('# Tag=%d Signer=%s and Signature\n' % (self.tag, self.signer))
|
|
|
- f.write('%04x %s %s\n' % (self.tag, name_wire, sig_wire))
|
|
|
-
|
|
|
-class TSIG(RR):
|
|
|
- rdlen = None # auto-calculate
|
|
|
- algorithm = 'hmac-sha256'
|
|
|
- time_signed = 1286978795 # arbitrarily chosen default
|
|
|
- fudge = 300
|
|
|
- mac_size = None # use a common value for the algorithm
|
|
|
- mac = None # use 'x' * mac_size
|
|
|
- original_id = 2845 # arbitrarily chosen default
|
|
|
- error = 0
|
|
|
- other_len = None # 6 if error is BADTIME; otherwise 0
|
|
|
- other_data = None # use time_signed + fudge + 1 for BADTIME
|
|
|
- dict_macsize = { 'hmac-md5' : 16, 'hmac-sha1' : 20, 'hmac-sha256' : 32 }
|
|
|
-
|
|
|
- # TSIG has some special defaults
|
|
|
- def __init__(self):
|
|
|
- super().__init__()
|
|
|
- self.rr_class = 'ANY'
|
|
|
- self.rr_ttl = 0
|
|
|
-
|
|
|
- def dump(self, f):
|
|
|
- if str(self.algorithm) == 'hmac-md5':
|
|
|
- name_wire = encode_name('hmac-md5.sig-alg.reg.int')
|
|
|
- else:
|
|
|
- name_wire = encode_name(self.algorithm)
|
|
|
- mac_size = self.mac_size
|
|
|
- if mac_size is None:
|
|
|
- if self.algorithm in self.dict_macsize.keys():
|
|
|
- mac_size = self.dict_macsize[self.algorithm]
|
|
|
- else:
|
|
|
- raise RuntimeError('TSIG Mac Size cannot be determined')
|
|
|
- mac = encode_string('x' * mac_size) if self.mac is None else \
|
|
|
- encode_string(self.mac, mac_size)
|
|
|
- other_len = self.other_len
|
|
|
- if other_len is None:
|
|
|
- # 18 = BADTIME
|
|
|
- other_len = 6 if self.error == 18 else 0
|
|
|
- other_data = self.other_data
|
|
|
- if other_data is None:
|
|
|
- other_data = '%012x' % (self.time_signed + self.fudge + 1) \
|
|
|
- if self.error == 18 else ''
|
|
|
- else:
|
|
|
- other_data = encode_string(self.other_data, other_len)
|
|
|
- if self.rdlen is None:
|
|
|
- self.rdlen = int(len(name_wire) / 2 + 16 + len(mac) / 2 + \
|
|
|
- len(other_data) / 2)
|
|
|
- self.dump_header(f, self.rdlen)
|
|
|
- f.write('# Algorithm=%s Time-Signed=%d Fudge=%d\n' %
|
|
|
- (self.algorithm, self.time_signed, self.fudge))
|
|
|
- f.write('%s %012x %04x\n' % (name_wire, self.time_signed, self.fudge))
|
|
|
- f.write('# MAC Size=%d MAC=(see hex)\n' % mac_size)
|
|
|
- f.write('%04x%s\n' % (mac_size, ' ' + mac if len(mac) > 0 else ''))
|
|
|
- f.write('# Original-ID=%d Error=%d\n' % (self.original_id, self.error))
|
|
|
- f.write('%04x %04x\n' % (self.original_id, self.error))
|
|
|
- f.write('# Other-Len=%d Other-Data=(see hex)\n' % other_len)
|
|
|
- f.write('%04x%s\n' % (other_len,
|
|
|
- ' ' + other_data if len(other_data) > 0 else ''))
|
|
|
-
|
|
|
-def get_config_param(section):
|
|
|
- config_param = {'name' : (Name, {}),
|
|
|
- 'header' : (DNSHeader, header_xtables),
|
|
|
- 'question' : (DNSQuestion, question_xtables),
|
|
|
- 'edns' : (EDNS, {}), 'a' : (A, {}), 'ns' : (NS, {}),
|
|
|
- 'soa' : (SOA, {}), 'txt' : (TXT, {}),
|
|
|
- 'rp' : (RP, {}), 'rrsig' : (RRSIG, {}),
|
|
|
- 'nsec' : (NSEC, {}), 'nsec3' : (NSEC3, {}),
|
|
|
- 'tsig' : (TSIG, {}) }
|
|
|
- s = section
|
|
|
- m = re.match('^([^:]+)/\d+$', section)
|
|
|
- if m:
|
|
|
- s = m.group(1)
|
|
|
- return config_param[s]
|
|
|
-
|
|
|
-usage = '''usage: %prog [options] input_file'''
|
|
|
-
|
|
|
-if __name__ == "__main__":
|
|
|
- parser = OptionParser(usage=usage)
|
|
|
- parser.add_option('-o', '--output', action='store', dest='output',
|
|
|
- default=None, metavar='FILE',
|
|
|
- help='output file name [default: prefix of input_file]')
|
|
|
- (options, args) = parser.parse_args()
|
|
|
-
|
|
|
- if len(args) == 0:
|
|
|
- parser.error('input file is missing')
|
|
|
- configfile = args[0]
|
|
|
-
|
|
|
- outputfile = options.output
|
|
|
- if not outputfile:
|
|
|
- m = re.match('(.*)\.[^.]+$', configfile)
|
|
|
- if m:
|
|
|
- outputfile = m.group(1)
|
|
|
- else:
|
|
|
- raise ValueError('output file is not specified and input file is not in the form of "output_file.suffix"')
|
|
|
-
|
|
|
- config = configparser.SafeConfigParser()
|
|
|
- config.read(configfile)
|
|
|
-
|
|
|
- output = open(outputfile, 'w')
|
|
|
-
|
|
|
- print_header(output, configfile)
|
|
|
-
|
|
|
- # First try the 'custom' mode; if it fails assume the standard mode.
|
|
|
- try:
|
|
|
- sections = config.get('custom', 'sections').split(':')
|
|
|
- except configparser.NoSectionError:
|
|
|
- sections = ['header', 'question', 'edns']
|
|
|
-
|
|
|
- for s in sections:
|
|
|
- section_param = get_config_param(s)
|
|
|
- (obj, xtables) = (section_param[0](), section_param[1])
|
|
|
- if get_config(config, s, obj, xtables):
|
|
|
- obj.dump(output)
|
|
|
-
|
|
|
- output.close()
|