gen-wiredata.py.in 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. #!@PYTHON@
  2. # Copyright (C) 2010 Internet Systems Consortium.
  3. #
  4. # Permission to use, copy, modify, and distribute this software for any
  5. # purpose with or without fee is hereby granted, provided that the above
  6. # copyright notice and this permission notice appear in all copies.
  7. #
  8. # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
  9. # DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
  10. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
  11. # INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
  12. # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  13. # FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  14. # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  15. # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. import configparser, re, time, sys
  17. from datetime import datetime
  18. from optparse import OptionParser
  19. re_hex = re.compile(r'^0x[0-9a-fA-F]+')
  20. re_decimal = re.compile(r'^\d+$')
  21. re_string = re.compile(r"\'(.*)\'$")
  22. dnssec_timefmt = '%Y%m%d%H%M%S'
  23. dict_qr = { 'query' : 0, 'response' : 1 }
  24. dict_opcode = { 'query' : 0, 'iquery' : 1, 'status' : 2, 'notify' : 4,
  25. 'update' : 5 }
  26. rdict_opcode = dict([(dict_opcode[k], k.upper()) for k in dict_opcode.keys()])
  27. dict_rcode = { 'noerror' : 0, 'formerr' : 1, 'servfail' : 2, 'nxdomain' : 3,
  28. 'notimp' : 4, 'refused' : 5, 'yxdomain' : 6, 'yxrrset' : 7,
  29. 'nxrrset' : 8, 'notauth' : 9, 'notzone' : 10 }
  30. rdict_rcode = dict([(dict_rcode[k], k.upper()) for k in dict_rcode.keys()])
  31. dict_rrtype = { 'none' : 0, 'a' : 1, 'ns' : 2, 'md' : 3, 'mf' : 4, 'cname' : 5,
  32. 'soa' : 6, 'mb' : 7, 'mg' : 8, 'mr' : 9, 'null' : 10,
  33. 'wks' : 11, 'ptr' : 12, 'hinfo' : 13, 'minfo' : 14, 'mx' : 15,
  34. 'txt' : 16, 'rp' : 17, 'afsdb' : 18, 'x25' : 19, 'isdn' : 20,
  35. 'rt' : 21, 'nsap' : 22, 'nsap_tr' : 23, 'sig' : 24, 'key' : 25,
  36. 'px' : 26, 'gpos' : 27, 'aaaa' : 28, 'loc' : 29, 'nxt' : 30,
  37. 'srv' : 33, 'naptr' : 35, 'kx' : 36, 'cert' : 37, 'a6' : 38,
  38. 'dname' : 39, 'opt' : 41, 'apl' : 42, 'ds' : 43, 'sshfp' : 44,
  39. 'ipseckey' : 45, 'rrsig' : 46, 'nsec' : 47, 'dnskey' : 48,
  40. 'dhcid' : 49, 'nsec3' : 50, 'nsec3param' : 51, 'hip' : 55,
  41. 'spf' : 99, 'unspec' : 103, 'tkey' : 249, 'tsig' : 250,
  42. 'dlv' : 32769, 'ixfr' : 251, 'axfr' : 252, 'mailb' : 253,
  43. 'maila' : 254, 'any' : 255 }
  44. rdict_rrtype = dict([(dict_rrtype[k], k.upper()) for k in dict_rrtype.keys()])
  45. dict_rrclass = { 'in' : 1, 'ch' : 3, 'hs' : 4, 'any' : 255 }
  46. rdict_rrclass = dict([(dict_rrclass[k], k.upper()) for k in \
  47. dict_rrclass.keys()])
  48. dict_algorithm = { 'rsamd5' : 1, 'dh' : 2, 'dsa' : 3, 'ecc' : 4,
  49. 'rsasha1' : 5 }
  50. dict_nsec3_algorithm = { 'reserved' : 0, 'sha1' : 1 }
  51. rdict_algorithm = dict([(dict_algorithm[k], k.upper()) for k in \
  52. dict_algorithm.keys()])
  53. rdict_nsec3_algorithm = dict([(dict_nsec3_algorithm[k], k.upper()) for k in \
  54. dict_nsec3_algorithm.keys()])
  55. header_xtables = { 'qr' : dict_qr, 'opcode' : dict_opcode,
  56. 'rcode' : dict_rcode }
  57. question_xtables = { 'rrtype' : dict_rrtype, 'rrclass' : dict_rrclass }
  58. rrsig_xtables = { 'algorithm' : dict_algorithm }
  59. def parse_value(value, xtable = {}):
  60. if re.search(re_hex, value):
  61. return int(value, 16)
  62. if re.search(re_decimal, value):
  63. return int(value)
  64. m = re.match(re_string, value)
  65. if m:
  66. return m.group(1)
  67. lovalue = value.lower()
  68. if lovalue in xtable:
  69. return xtable[lovalue]
  70. return value
  71. def code_totext(code, dict):
  72. if code in dict.keys():
  73. return dict[code] + '(' + str(code) + ')'
  74. return str(code)
  75. def encode_name(name, absolute=True):
  76. # make sure the name is dot-terminated. duplicate dots will be ignored
  77. # below.
  78. name += '.'
  79. labels = name.split('.')
  80. wire = ''
  81. for l in labels:
  82. if len(l) > 4 and l[0:4] == 'ptr=':
  83. # special meta-syntax for compression pointer
  84. wire += '%04x' % (0xc000 | int(l[4:]))
  85. break
  86. if absolute or len(l) > 0:
  87. wire += '%02x' % len(l)
  88. wire += ''.join(['%02x' % ord(ch) for ch in l])
  89. if len(l) == 0:
  90. break
  91. return wire
  92. def encode_string(name, len=None):
  93. if type(name) is int and len is not None:
  94. return '%0.*x' % (len * 2, name)
  95. return ''.join(['%02x' % ord(ch) for ch in name])
  96. def count_namelabels(name):
  97. if name == '.': # special case
  98. return 0
  99. m = re.match('^(.*)\.$', name)
  100. if m:
  101. name = m.group(1)
  102. return len(name.split('.'))
  103. def get_config(config, section, configobj, xtables = {}):
  104. try:
  105. for field in config.options(section):
  106. value = config.get(section, field)
  107. if field in xtables.keys():
  108. xtable = xtables[field]
  109. else:
  110. xtable = {}
  111. configobj.__dict__[field] = parse_value(value, xtable)
  112. except configparser.NoSectionError:
  113. return False
  114. return True
  115. def print_header(f, input_file):
  116. f.write('''###
  117. ### This data file was auto-generated from ''' + input_file + '''
  118. ###
  119. ''')
  120. class Name:
  121. name = 'example.com'
  122. pointer = None # no compression by default
  123. def dump(self, f):
  124. name = self.name
  125. if self.pointer is not None:
  126. if len(name) > 0 and name[-1] != '.':
  127. name += '.'
  128. name += 'ptr=%d' % self.pointer
  129. name_wire = encode_name(name)
  130. f.write('\n# DNS Name: %s' % self.name)
  131. if self.pointer is not None:
  132. f.write(' + compression pointer: %d' % self.pointer)
  133. f.write('\n')
  134. f.write('%s' % name_wire)
  135. f.write('\n')
  136. class DNSHeader:
  137. id = 0x1035
  138. (qr, aa, tc, rd, ra, ad, cd) = 0, 0, 0, 0, 0, 0, 0
  139. mbz = 0
  140. rcode = 0 # noerror
  141. opcode = 0 # query
  142. (qdcount, ancount, nscount, arcount) = 1, 0, 0, 0
  143. def dump(self, f):
  144. f.write('\n# Header Section\n')
  145. f.write('# ID=' + str(self.id))
  146. f.write(' QR=' + ('Response' if self.qr else 'Query'))
  147. f.write(' Opcode=' + code_totext(self.opcode, rdict_opcode))
  148. f.write(' Rcode=' + code_totext(self.rcode, rdict_rcode))
  149. f.write('%s' % (' AA' if self.aa else ''))
  150. f.write('%s' % (' TC' if self.tc else ''))
  151. f.write('%s' % (' RD' if self.rd else ''))
  152. f.write('%s' % (' AD' if self.ad else ''))
  153. f.write('%s' % (' CD' if self.cd else ''))
  154. f.write('\n')
  155. f.write('%04x ' % self.id)
  156. flag_and_code = 0
  157. flag_and_code |= (self.qr << 15 | self.opcode << 14 | self.aa << 10 |
  158. self.tc << 9 | self.rd << 8 | self.ra << 7 |
  159. self.mbz << 6 | self.ad << 5 | self.cd << 4 |
  160. self.rcode)
  161. f.write('%04x\n' % flag_and_code)
  162. f.write('# QDCNT=%d, ANCNT=%d, NSCNT=%d, ARCNT=%d\n' %
  163. (self.qdcount, self.ancount, self.nscount, self.arcount))
  164. f.write('%04x %04x %04x %04x\n' % (self.qdcount, self.ancount,
  165. self.nscount, self.arcount))
  166. class DNSQuestion:
  167. name = 'example.com.'
  168. rrtype = parse_value('A', dict_rrtype)
  169. rrclass = parse_value('IN', dict_rrclass)
  170. def dump(self, f):
  171. f.write('\n# Question Section\n')
  172. f.write('# QNAME=%s QTYPE=%s QCLASS=%s\n' %
  173. (self.name,
  174. code_totext(self.rrtype, rdict_rrtype),
  175. code_totext(self.rrclass, rdict_rrclass)))
  176. f.write(encode_name(self.name))
  177. f.write(' %04x %04x\n' % (self.rrtype, self.rrclass))
  178. class EDNS:
  179. name = '.'
  180. udpsize = 4096
  181. extrcode = 0
  182. version = 0
  183. do = 0
  184. mbz = 0
  185. rdlen = 0
  186. def dump(self, f):
  187. f.write('\n# EDNS OPT RR\n')
  188. f.write('# NAME=%s TYPE=%s UDPSize=%d ExtRcode=%s Version=%s DO=%d\n' %
  189. (self.name, code_totext(dict_rrtype['opt'], rdict_rrtype),
  190. self.udpsize, self.extrcode, self.version,
  191. 1 if self.do else 0))
  192. code_vers = (self.extrcode << 8) | (self.version & 0x00ff)
  193. extflags = (self.do << 15) | (self.mbz & 0x8000)
  194. f.write('%s %04x %04x %04x %04x\n' %
  195. (encode_name(self.name), dict_rrtype['opt'], self.udpsize,
  196. code_vers, extflags))
  197. f.write('# RDLEN=%d\n' % self.rdlen)
  198. f.write('%04x\n' % self.rdlen)
  199. class SOA:
  200. # this currently doesn't support name compression within the RDATA.
  201. rdlen = -1 # auto-calculate
  202. mname = 'ns.example.com'
  203. rname = 'root.example.com'
  204. serial = 2010012601
  205. refresh = 3600
  206. retry = 300
  207. expire = 3600000
  208. minimum = 1200
  209. def dump(self, f):
  210. mname_wire = encode_name(self.mname)
  211. rname_wire = encode_name(self.rname)
  212. rdlen = self.rdlen
  213. if rdlen < 0:
  214. rdlen = int(20 + len(mname_wire) / 2 + len(str(rname_wire)) / 2)
  215. f.write('\n# SOA RDATA (RDLEN=%d)\n' % rdlen)
  216. f.write('%04x\n' % rdlen);
  217. f.write('# NNAME=%s RNAME=%s\n' % (self.mname, self.rname))
  218. f.write('%s %s\n' % (mname_wire, rname_wire))
  219. f.write('# SERIAL(%d) REFRESH(%d) RETRY(%d) EXPIRE(%d) MINIMUM(%d)\n' %
  220. (self.serial, self.refresh, self.retry, self.expire,
  221. self.minimum))
  222. f.write('%08x %08x %08x %08x %08x\n' % (self.serial, self.refresh,
  223. self.retry, self.expire,
  224. self.minimum))
  225. class TXT:
  226. rdlen = -1 # auto-calculate
  227. nstring = 1 # number of character-strings
  228. stringlen = -1 # default string length, auto-calculate
  229. string = 'Test String' # default string
  230. def dump(self, f):
  231. stringlen_list = []
  232. string_list = []
  233. wirestring_list = []
  234. for i in range(0, self.nstring):
  235. key_string = 'string' + str(i)
  236. if key_string in self.__dict__:
  237. string_list.append(self.__dict__[key_string])
  238. else:
  239. string_list.append(self.string)
  240. wirestring_list.append(encode_string(string_list[-1]))
  241. key_stringlen = 'stringlen' + str(i)
  242. if key_stringlen in self.__dict__:
  243. stringlen_list.append(self.__dict__[key_stringlen])
  244. else:
  245. stringlen_list.append(self.stringlen)
  246. if stringlen_list[-1] < 0:
  247. stringlen_list[-1] = int(len(wirestring_list[-1]) / 2)
  248. rdlen = self.rdlen
  249. if rdlen < 0:
  250. rdlen = int(len(''.join(wirestring_list)) / 2) + self.nstring
  251. f.write('\n# TXT RDATA (RDLEN=%d)\n' % rdlen)
  252. f.write('%04x\n' % rdlen);
  253. for i in range(0, self.nstring):
  254. f.write('# String Len=%d, String=\"%s\"\n' %
  255. (stringlen_list[i], string_list[i]))
  256. f.write('%02x%s%s\n' % (stringlen_list[i],
  257. ' ' if len(wirestring_list[i]) > 0 else '',
  258. wirestring_list[i]))
  259. class RP:
  260. '''Implements rendering RP RDATA in the wire format.
  261. Configurable parameters are as follows:
  262. - rdlen: 16-bit RDATA length. If omitted, the accurate value is auto
  263. calculated and used; if negative, the RDLEN field will be omitted from
  264. the output data.
  265. - mailbox: The mailbox field.
  266. - text: The text field.
  267. All of these parameters have the default values and can be omitted.
  268. '''
  269. rdlen = None # auto-calculate
  270. mailbox = 'root.example.com'
  271. text = 'rp-text.example.com'
  272. def dump(self, f):
  273. mailbox_wire = encode_name(self.mailbox)
  274. text_wire = encode_name(self.text)
  275. if self.rdlen is None:
  276. self.rdlen = (len(mailbox_wire) + len(text_wire)) / 2
  277. else:
  278. self.rdlen = int(self.rdlen)
  279. if self.rdlen >= 0:
  280. f.write('\n# RP RDATA (RDLEN=%d)\n' % self.rdlen)
  281. f.write('%04x\n' % self.rdlen)
  282. else:
  283. f.write('\n# RP RDATA (RDLEN omitted)\n')
  284. f.write('# MAILBOX=%s TEXT=%s\n' % (self.mailbox, self.text))
  285. f.write('%s %s\n' % (mailbox_wire, text_wire))
  286. class NSECBASE:
  287. '''Implements rendering NSEC/NSEC3 type bitmaps commonly used for
  288. these RRs. The NSEC and NSEC3 classes will be inherited from this
  289. class.'''
  290. nbitmap = 1 # number of bitmaps
  291. block = 0
  292. maplen = None # default bitmap length, auto-calculate
  293. bitmap = '040000000003' # an arbtrarily chosen bitmap sample
  294. def dump(self, f):
  295. # first, construct the bitmpa data
  296. block_list = []
  297. maplen_list = []
  298. bitmap_list = []
  299. for i in range(0, self.nbitmap):
  300. key_bitmap = 'bitmap' + str(i)
  301. if key_bitmap in self.__dict__:
  302. bitmap_list.append(self.__dict__[key_bitmap])
  303. else:
  304. bitmap_list.append(self.bitmap)
  305. key_maplen = 'maplen' + str(i)
  306. if key_maplen in self.__dict__:
  307. maplen_list.append(self.__dict__[key_maplen])
  308. else:
  309. maplen_list.append(self.maplen)
  310. if maplen_list[-1] is None: # calculate it if not specified
  311. maplen_list[-1] = int(len(bitmap_list[-1]) / 2)
  312. key_block = 'block' + str(i)
  313. if key_block in self.__dict__:
  314. block_list.append(self.__dict__[key_block])
  315. else:
  316. block_list.append(self.block)
  317. # dump RR-type specific part (NSEC or NSEC3)
  318. self.dump_fixedpart(f, 2 * self.nbitmap + \
  319. int(len(''.join(bitmap_list)) / 2))
  320. # dump the bitmap
  321. for i in range(0, self.nbitmap):
  322. f.write('# Bitmap: Block=%d, Length=%d\n' %
  323. (block_list[i], maplen_list[i]))
  324. f.write('%02x %02x %s\n' %
  325. (block_list[i], maplen_list[i], bitmap_list[i]))
  326. class NSEC(NSECBASE):
  327. rdlen = None # auto-calculate
  328. nextname = 'next.example.com'
  329. def dump_fixedpart(self, f, bitmap_totallen):
  330. name_wire = encode_name(self.nextname)
  331. if self.rdlen is None:
  332. # if rdlen needs to be calculated, it must be based on the bitmap
  333. # length, because the configured maplen can be fake.
  334. self.rdlen = int(len(name_wire) / 2) + bitmap_totallen
  335. f.write('\n# NSEC RDATA (RDLEN=%d)\n' % self.rdlen)
  336. f.write('%04x\n' % self.rdlen);
  337. f.write('# Next Name=%s (%d bytes)\n' % (self.nextname,
  338. int(len(name_wire) / 2)))
  339. f.write('%s\n' % name_wire)
  340. class NSEC3(NSECBASE):
  341. rdlen = None # auto-calculate
  342. hashalg = 1 # SHA-1
  343. optout = False # opt-out flag
  344. mbz = 0 # other flag fields (none defined yet)
  345. iterations = 1
  346. saltlen = 5
  347. salt = 's' * saltlen
  348. hashlen = 20
  349. hash = 'h' * hashlen
  350. def dump_fixedpart(self, f, bitmap_totallen):
  351. if self.rdlen is None:
  352. # if rdlen needs to be calculated, it must be based on the bitmap
  353. # length, because the configured maplen can be fake.
  354. self.rdlen = 4 + 1 + len(self.salt) + 1 + len(self.hash) \
  355. + bitmap_totallen
  356. f.write('\n# NSEC3 RDATA (RDLEN=%d)\n' % self.rdlen)
  357. f.write('%04x\n' % self.rdlen)
  358. optout_val = 1 if self.optout else 0
  359. f.write('# Hash Alg=%s, Opt-Out=%d, Other Flags=%0x, Iterations=%d\n' %
  360. (code_totext(self.hashalg, rdict_nsec3_algorithm),
  361. optout_val, self.mbz, self.iterations))
  362. f.write('%02x %02x %04x\n' %
  363. (self.hashalg, (self.mbz << 1) | optout_val, self.iterations))
  364. f.write("# Salt Len=%d, Salt='%s'\n" % (self.saltlen, self.salt))
  365. f.write('%02x%s%s\n' % (self.saltlen,
  366. ' ' if len(self.salt) > 0 else '',
  367. encode_string(self.salt)))
  368. f.write("# Hash Len=%d, Hash='%s'\n" % (self.hashlen, self.hash))
  369. f.write('%02x%s%s\n' % (self.hashlen,
  370. ' ' if len(self.hash) > 0 else '',
  371. encode_string(self.hash)))
  372. class RRSIG:
  373. rdlen = -1 # auto-calculate
  374. covered = 1 # A
  375. algorithm = 5 # RSA-SHA1
  376. labels = -1 # auto-calculate (#labels of signer)
  377. originalttl = 3600
  378. expiration = int(time.mktime(datetime.strptime('20100131120000',
  379. dnssec_timefmt).timetuple()))
  380. inception = int(time.mktime(datetime.strptime('20100101120000',
  381. dnssec_timefmt).timetuple()))
  382. tag = 0x1035
  383. signer = 'example.com'
  384. signature = 0x123456789abcdef123456789abcdef
  385. def dump(self, f):
  386. name_wire = encode_name(self.signer)
  387. sig_wire = '%x' % self.signature
  388. rdlen = self.rdlen
  389. if rdlen < 0:
  390. rdlen = int(18 + len(name_wire) / 2 + len(str(sig_wire)) / 2)
  391. labels = self.labels
  392. if labels < 0:
  393. labels = count_namelabels(self.signer)
  394. f.write('\n# RRSIG RDATA (RDLEN=%d)\n' % rdlen)
  395. f.write('%04x\n' % rdlen);
  396. f.write('# Covered=%s Algorithm=%s Labels=%d OrigTTL=%d\n' %
  397. (code_totext(self.covered, rdict_rrtype),
  398. code_totext(self.algorithm, rdict_algorithm), labels,
  399. self.originalttl))
  400. f.write('%04x %02x %02x %08x\n' % (self.covered, self.algorithm,
  401. labels, self.originalttl))
  402. f.write('# Expiration=%s, Inception=%s\n' %
  403. (str(self.expiration), str(self.inception)))
  404. f.write('%08x %08x\n' % (self.expiration, self.inception))
  405. f.write('# Tag=%d Signer=%s and Signature\n' % (self.tag, self.signer))
  406. f.write('%04x %s %s\n' % (self.tag, name_wire, sig_wire))
  407. class TSIG:
  408. rdlen = None # auto-calculate
  409. algorithm = 'hmac-sha256'
  410. time_signed = 1286978795 # arbitrarily chosen default
  411. fudge = 300
  412. mac_size = None # use a common value for the algorithm
  413. mac = None # use 'x' * mac_size
  414. original_id = 2845 # arbitrarily chosen default
  415. error = 0
  416. other_len = None # 6 if error is BADTIME; otherwise 0
  417. other_data = None # use time_signed + fudge + 1 for BADTIME
  418. dict_macsize = { 'hmac-md5' : 16, 'hmac-sha1' : 20, 'hmac-sha256' : 32 }
  419. def dump(self, f):
  420. if str(self.algorithm) == 'hmac-md5':
  421. name_wire = encode_name('hmac-md5.sig-alg.reg.int')
  422. else:
  423. name_wire = encode_name(self.algorithm)
  424. rdlen = self.rdlen
  425. mac_size = self.mac_size
  426. if mac_size is None:
  427. if self.algorithm in self.dict_macsize.keys():
  428. mac_size = self.dict_macsize[self.algorithm]
  429. else:
  430. raise RuntimeError('TSIG Mac Size cannot be determined')
  431. mac = encode_string('x' * mac_size) if self.mac is None else \
  432. encode_string(self.mac, mac_size)
  433. other_len = self.other_len
  434. if other_len is None:
  435. # 18 = BADTIME
  436. other_len = 6 if self.error == 18 else 0
  437. other_data = self.other_data
  438. if other_data is None:
  439. other_data = '%012x' % (self.time_signed + self.fudge + 1) \
  440. if self.error == 18 else ''
  441. else:
  442. other_data = encode_string(self.other_data, other_len)
  443. if rdlen is None:
  444. rdlen = int(len(name_wire) / 2 + 16 + len(mac) / 2 + \
  445. len(other_data) / 2)
  446. f.write('\n# TSIG RDATA (RDLEN=%d)\n' % rdlen)
  447. f.write('%04x\n' % rdlen);
  448. f.write('# Algorithm=%s Time-Signed=%d Fudge=%d\n' %
  449. (self.algorithm, self.time_signed, self.fudge))
  450. f.write('%s %012x %04x\n' % (name_wire, self.time_signed, self.fudge))
  451. f.write('# MAC Size=%d MAC=(see hex)\n' % mac_size)
  452. f.write('%04x%s\n' % (mac_size, ' ' + mac if len(mac) > 0 else ''))
  453. f.write('# Original-ID=%d Error=%d\n' % (self.original_id, self.error))
  454. f.write('%04x %04x\n' % (self.original_id, self.error))
  455. f.write('# Other-Len=%d Other-Data=(see hex)\n' % other_len)
  456. f.write('%04x%s\n' % (other_len,
  457. ' ' + other_data if len(other_data) > 0 else ''))
  458. def get_config_param(section):
  459. config_param = {'name' : (Name, {}),
  460. 'header' : (DNSHeader, header_xtables),
  461. 'question' : (DNSQuestion, question_xtables),
  462. 'edns' : (EDNS, {}), 'soa' : (SOA, {}), 'txt' : (TXT, {}),
  463. 'rp' : (RP, {}), 'rrsig' : (RRSIG, {}),
  464. 'nsec' : (NSEC, {}), 'nsec3' : (NSEC3, {}),
  465. 'tsig' : (TSIG, {}) }
  466. s = section
  467. m = re.match('^([^:]+)/\d+$', section)
  468. if m:
  469. s = m.group(1)
  470. return config_param[s]
  471. usage = '''usage: %prog [options] input_file'''
  472. if __name__ == "__main__":
  473. parser = OptionParser(usage=usage)
  474. parser.add_option('-o', '--output', action='store', dest='output',
  475. default=None, metavar='FILE',
  476. help='output file name [default: prefix of input_file]')
  477. (options, args) = parser.parse_args()
  478. if len(args) == 0:
  479. parser.error('input file is missing')
  480. configfile = args[0]
  481. outputfile = options.output
  482. if not outputfile:
  483. m = re.match('(.*)\.[^.]+$', configfile)
  484. if m:
  485. outputfile = m.group(1)
  486. else:
  487. raise ValueError('output file is not specified and input file is not in the form of "output_file.suffix"')
  488. config = configparser.SafeConfigParser()
  489. config.read(configfile)
  490. output = open(outputfile, 'w')
  491. print_header(output, configfile)
  492. # First try the 'custom' mode; if it fails assume the standard mode.
  493. try:
  494. sections = config.get('custom', 'sections').split(':')
  495. except configparser.NoSectionError:
  496. sections = ['header', 'question', 'edns']
  497. for s in sections:
  498. section_param = get_config_param(s)
  499. (obj, xtables) = (section_param[0](), section_param[1])
  500. if get_config(config, s, obj, xtables):
  501. obj.dump(output)
  502. output.close()