gen-rdatacode.py.in 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. #!@PYTHON@
  2. # Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
  3. #
  4. # Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
  9. # REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  10. # AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  11. # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  12. # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  13. # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  14. # PERFORMANCE OF THIS SOFTWARE.
  15. """\
  16. This is a supplemental script to (half) auto-generate DNS Rdata related
  17. classes and constants.
  18. """
  19. import os
  20. from os.path import getmtime
  21. import re
  22. import sys
  23. # new_rdata_factory_users[] is a list of tuples of the form (rrtype,
  24. # rrclass). Items in the list use the (new) RdataFactory class, and
  25. # items which are not in the list use OldRdataFactory class.
  26. # Note: rrtype and rrclass must be specified in lowercase in
  27. # new_rdata_factory_users.
  28. #
  29. # Example:
  30. # new_rdata_factory_users = [('a', 'in'), ('a', 'ch'), ('soa', 'generic')]
  31. new_rdata_factory_users = [('a', 'in'), ('aaaa', 'in'),
  32. ('afsdb', 'generic'),
  33. ('cname', 'generic'),
  34. ('dlv', 'generic'),
  35. ('dname', 'generic'),
  36. ('ds', 'generic'),
  37. ('hinfo', 'generic'),
  38. ('naptr', 'generic'),
  39. ('mx', 'generic'),
  40. ('ns', 'generic'),
  41. ('nsec', 'generic'),
  42. ('ptr', 'generic'),
  43. ('soa', 'generic'),
  44. ('spf', 'generic'),
  45. ('srv', 'in'),
  46. ('txt', 'generic')
  47. ]
  48. re_typecode = re.compile('([\da-z\-]+)_(\d+)')
  49. classcode2txt = {}
  50. typecode2txt = {}
  51. # For meta types and types well-known but not implemented. This is a dict from
  52. # type code values (as string) to textual mnemonic.
  53. meta_types = {
  54. # Real meta types. We won't have Rdata implement for them, but we need
  55. # RRType constants.
  56. '251': 'ixfr', '252': 'axfr', '255': 'any',
  57. # Obsolete types. We probalby won't implement Rdata for them, but it's
  58. # better to have RRType constants.
  59. '3': 'md', '4': 'mf', '7': 'mb', '8': 'mg', '9': 'mr', '30': 'nxt',
  60. '38': 'a6', '254': 'maila',
  61. # Types officially assigned but not yet supported in our implementation.
  62. '10': 'null', '11': 'wks', '19': 'x25', '21': 'rt', '22': 'nsap',
  63. '23': 'nsap-ptr', '24': 'sig', '20': 'isdn', '25': 'key', '26': 'px',
  64. '27': 'gpos', '29': 'loc', '36': 'kx', '37': 'cert', '42': 'apl',
  65. '45': 'ipseckey', '52': 'tlsa', '55': 'hip', '103': 'unspec',
  66. '104': 'nid', '105': 'l32', '106': 'l64', '107': 'lp', '249': 'tkey',
  67. '253': 'mailb', '256': 'uri', '257': 'caa'
  68. }
  69. # Classes that don't have any known types. This is a dict from type code
  70. # values (as string) to textual mnemonic.
  71. meta_classes = {'254': 'none'}
  72. typeandclass = []
  73. generic_code = 65536 # something larger than any code value
  74. rdata_declarations = ''
  75. class_definitions = ''
  76. classdir_mtime = 0
  77. rdatadef_mtime = 0
  78. rdatahdr_mtime = 0
  79. heading_txt = '''///////////////
  80. ///////////////
  81. /////////////// THIS FILE IS AUTOMATICALLY GENERATED BY gen-rdatacode.py.
  82. /////////////// DO NOT EDIT!
  83. ///////////////
  84. ///////////////
  85. '''
  86. def import_classdef(class_txt, file):
  87. content = ''
  88. rdata_source = open(file, 'r')
  89. for line in rdata_source.readlines():
  90. if re.match('// BEGIN_ISC_NAMESPACE', line):
  91. content += 'namespace isc {\n'
  92. content += 'namespace dns {\n'
  93. continue
  94. if re.match('// BEGIN_RDATA_NAMESPACE', line):
  95. content += 'namespace rdata {\n'
  96. content += 'namespace ' + class_txt + ' {\n'
  97. continue
  98. if re.match('// END_ISC_NAMESPACE', line):
  99. content += '} // end of namespace "dns"\n'
  100. content += '} // end of namespace "isc"\n'
  101. continue
  102. if re.match('// END_RDATA_NAMESPACE', line):
  103. content += '} // end of namespace "' + class_txt +'"\n'
  104. content += '} // end of namespace "rdata"\n'
  105. continue
  106. content += line
  107. rdata_source.close()
  108. return content
  109. def import_classheader(class_txt, type_txt, type_code, file):
  110. type_utxt = type_txt.upper()
  111. class_utxt = class_txt.upper()
  112. # for each CLASS_n/TYPE_m.h
  113. rdata_header = open(file, 'r')
  114. content = ''
  115. guard_macro = class_txt.upper() + '_' + type_txt.upper()
  116. guard_macro += '_' + type_code + '_H'
  117. for line in rdata_header.readlines():
  118. if re.match('// BEGIN_HEADER_GUARD', line):
  119. content += '#ifndef ' + guard_macro + '\n'
  120. content += '#define ' + guard_macro + ' 1\n'
  121. continue
  122. if re.match('// END_HEADER_GUARD', line):
  123. content += '#endif // ' + guard_macro + '\n'
  124. continue
  125. if re.match('// BEGIN_ISC_NAMESPACE', line):
  126. content += 'namespace isc {\n'
  127. content += 'namespace util {\n'
  128. content += '''
  129. class InputBuffer;
  130. class OutputBuffer;\n'''
  131. content += '}\n\n'
  132. content += 'namespace dns {\n'
  133. continue
  134. if re.match('// BEGIN_RDATA_NAMESPACE', line):
  135. content += 'namespace rdata {\n'
  136. content += 'namespace ' + class_txt + ' {\n'
  137. continue
  138. if re.match('// END_ISC_NAMESPACE', line):
  139. content += '} // end of namespace "dns"\n'
  140. content += '} // end of namespace "isc"\n'
  141. continue
  142. if re.match('// END_RDATA_NAMESPACE', line):
  143. content += '} // end of namespace "' + class_txt +'"\n'
  144. content += '} // end of namespace "rdata"\n'
  145. continue
  146. if re.match('// Local Variables:', line):
  147. break
  148. content += line
  149. if re.match('// BEGIN_COMMON_DECLARATIONS', line):
  150. content += '''
  151. class AbstractMessageRenderer;\n\n'''
  152. if re.match('\s+// BEGIN_COMMON_MEMBERS$', line):
  153. content += '''
  154. explicit ''' + type_utxt + '''(const std::string& type_str);
  155. ''' + type_utxt + '''(isc::util::InputBuffer& buffer, size_t rdata_len);
  156. ''' + type_utxt + '''(const ''' + type_utxt + '''& other);
  157. ''' + type_utxt + '''(
  158. MasterLexer& lexer, const Name* name,
  159. MasterLoader::Options options, MasterLoaderCallbacks& callbacks);
  160. virtual std::string toText() const;
  161. virtual void toWire(isc::util::OutputBuffer& buffer) const;
  162. virtual void toWire(AbstractMessageRenderer& renderer) const;
  163. virtual int compare(const Rdata& other) const;\n\n'''
  164. rdata_header.close()
  165. return content
  166. def import_definitions(classcode2txt, typecode2txt, typeandclass):
  167. global rdata_declarations
  168. global class_definitions
  169. global classdir_mtime
  170. global rdatadef_mtime
  171. global rdatahdr_mtime
  172. if classdir_mtime < getmtime('@srcdir@/rdata'):
  173. classdir_mtime = getmtime('@srcdir@/rdata')
  174. # Sort directories before iterating through them so that the directory
  175. # list is processed in the same order on all systems. The resulting
  176. # files should compile regardless of the order in which the components
  177. # are included but... Having a fixed order for the directories should
  178. # eliminate system-dependent problems. (Note that the directory names
  179. # in BIND 10 are ASCII, so the order should be locale-independent.)
  180. dirlist = os.listdir('@srcdir@/rdata')
  181. dirlist.sort()
  182. for dir in dirlist:
  183. classdir = '@srcdir@/rdata' + os.sep + dir
  184. m = re_typecode.match(dir)
  185. if os.path.isdir(classdir) and (m != None or dir == 'generic'):
  186. if dir == 'generic':
  187. class_txt = 'generic'
  188. class_code = generic_code
  189. else:
  190. class_txt = m.group(1)
  191. class_code = m.group(2)
  192. if not class_code in classcode2txt:
  193. classcode2txt[class_code] = class_txt
  194. # Same considerations as directories regarding sorted order
  195. # also apply to files.
  196. filelist = os.listdir(classdir)
  197. filelist.sort()
  198. for file in filelist:
  199. file = classdir + os.sep + file
  200. m = re_typecode.match(os.path.split(file)[1])
  201. if m != None:
  202. type_txt = m.group(1)
  203. type_code = m.group(2)
  204. if not type_code in typecode2txt:
  205. typecode2txt[type_code] = type_txt
  206. if re.search('\.cc$', file):
  207. if rdatadef_mtime < getmtime(file):
  208. rdatadef_mtime = getmtime(file)
  209. class_definitions += import_classdef(class_txt, file)
  210. elif re.search('\.h$', file):
  211. if rdatahdr_mtime < getmtime(file):
  212. rdatahdr_mtime = getmtime(file)
  213. rdata_declarations += import_classheader(class_txt,
  214. type_txt,
  215. type_code,
  216. file)
  217. typeandclass.append((type_txt, int(type_code),
  218. (class_txt, class_txt),
  219. int(class_code)))
  220. if class_txt == 'generic':
  221. typeandclass.append((type_txt, int(type_code),
  222. (class_txt, 'in'), 1))
  223. def need_generate(file, mtime):
  224. '''Check if we need to generate the specified file.
  225. To avoid unnecessary compilation, we skip (re)generating the file when
  226. the file already exists and newer than the base file.
  227. '''
  228. if os.path.exists(file) and getmtime(file) > mtime:
  229. return False
  230. return True
  231. def generate_rdatadef(file, basemtime):
  232. if not need_generate(file, basemtime):
  233. print('skip generating ' + file);
  234. return
  235. rdata_deffile = open(file, 'w')
  236. rdata_deffile.write(heading_txt)
  237. rdata_deffile.write(class_definitions)
  238. rdata_deffile.close()
  239. def generate_rdatahdr(file, heading, declarations, basemtime):
  240. if not need_generate(file, basemtime):
  241. print('skip generating ' + file);
  242. return
  243. heading += '''
  244. #ifndef DNS_RDATACLASS_H
  245. #define DNS_RDATACLASS_H 1
  246. #include <dns/master_loader.h>
  247. namespace isc {
  248. namespace dns {
  249. class Name;
  250. class MasterLexer;
  251. class MasterLoaderCallbacks;
  252. }
  253. }
  254. '''
  255. declarations += '''
  256. #endif // DNS_RDATACLASS_H
  257. // Local Variables:
  258. // mode: c++
  259. // End:
  260. '''
  261. rdata_header = open(file, 'w')
  262. rdata_header.write(heading)
  263. rdata_header.write(declarations)
  264. rdata_header.close()
  265. def generate_typeclasscode(fileprefix, basemtime, code2txt, type_or_class):
  266. placeholder = '@srcdir@/' + fileprefix + '-placeholder.h'
  267. outputfile = '@builddir@/' + fileprefix + '.h'
  268. py_outputfile = '@builddir@/python/' + fileprefix + '_constants_inc.cc'
  269. upper_key = type_or_class.upper() # TYPE or CLASS
  270. lower_key = 'rr' + type_or_class.lower() # rrtype or rrclass
  271. cap_key = type_or_class # Type or Class
  272. # We only decide whether to generate files for libdns++ files; Python
  273. # files are generated if and only if libdns++ files are generated.
  274. # In practice it should be sufficient.
  275. if (not need_generate(outputfile, basemtime) and
  276. getmtime(outputfile) > getmtime(placeholder)):
  277. print('skip generating ' + outputfile)
  278. return
  279. # Create a list of (code, code-text) pairs, where code-text is generally
  280. # upper-cased, with applying speicial filters when necessary.
  281. def convert(code_txt):
  282. # Workaround by heuristics: there's a "NULL" RR type, but it would
  283. # cause conflict with the C/C++ macro. We use Null as a special case.
  284. if code_txt == 'null':
  285. return 'Null'
  286. # Likewise, convert "nsap-ptr" to "NSAP_PTR" as a dash cannot be part
  287. # of a C/C++ variable.
  288. if code_txt == 'nsap-ptr':
  289. return 'NSAP_PTR'
  290. return code_txt.upper()
  291. codes = [ (code, convert(txt)) for code, txt in code2txt.items() ]
  292. # Dump source code for libdns++
  293. with open(placeholder, 'r') as header_temp:
  294. with open(outputfile, 'w') as header_out:
  295. header_out.write(heading_txt)
  296. for line in header_temp:
  297. header_out.write(line)
  298. if re.match('\s+// BEGIN_WELL_KNOWN_' + upper_key +
  299. '_DECLARATIONS$', line):
  300. for code in codes:
  301. header_out.write(' ' * 4 + 'static const RR' +
  302. cap_key + '& ' + code[1] + '();\n')
  303. if re.match('// BEGIN_WELL_KNOWN_' + upper_key +
  304. '_DEFINITIONS$', line):
  305. for code in codes:
  306. header_out.write('''inline const RR''' + cap_key +
  307. '''&
  308. RR''' + cap_key + '''::''' + code[1] + '''() {
  309. static RR''' + cap_key + ''' ''' + lower_key + '''(''' + code[0] + ''');
  310. return (''' + lower_key + ''');
  311. }\n
  312. ''')
  313. # Dump source code snippet for isc.dns Python module
  314. with open(py_outputfile, 'w') as py_out:
  315. py_out.write(" // auto-generated by ../gen-rdatacode.py."
  316. " Don't edit this file.\n")
  317. py_out.write("\n")
  318. for code in codes:
  319. py_out.write('''\
  320. installClassVariable(''' + lower_key + '''_type, "''' + code[1] + '''",
  321. createRR''' + cap_key + '''Object(RR''' + \
  322. cap_key + '''::''' + code[1] + '''()));
  323. ''')
  324. def generate_rrparam(fileprefix, basemtime):
  325. placeholder = '@srcdir@/' + fileprefix + '-placeholder.cc'
  326. outputfile = '@builddir@/' + fileprefix + '.cc'
  327. if not need_generate(outputfile, basemtime) and getmtime(outputfile) > getmtime(placeholder):
  328. print('skip generating ' + outputfile)
  329. return
  330. # sort by class, then by type
  331. typeandclassparams = ''
  332. typeandclass.sort(key = lambda x: (x[3], x[1]))
  333. for param in typeandclass:
  334. # for rrparamregistry.cc
  335. # each param is a tuple of (type_txt, type_code, class_tuple,
  336. # class_code)
  337. (type_txt, type_code, class_tuple, class_code) = param
  338. type_utxt = type_txt.upper()
  339. class_txt = class_tuple[0]
  340. class_utxt = class_tuple[1].upper()
  341. indent = ' ' * 8
  342. typeandclassparams += indent
  343. # By default, we use OldRdataFactory (see bug #2497). If you
  344. # want to pick RdataFactory for a particular type, add it to
  345. # new_rdata_factory_users. Note that we explicitly generate (for
  346. # optimization) class-independent ("generic") factories for class IN
  347. # for optimization.
  348. if (((type_txt.lower(), class_txt.lower()) in
  349. new_rdata_factory_users) or
  350. ((class_txt.lower() == 'in') and
  351. ((type_txt.lower(), 'generic') in new_rdata_factory_users))):
  352. rdf_class = 'RdataFactory'
  353. else:
  354. rdf_class = 'OldRdataFactory'
  355. if class_tuple[1] != 'generic':
  356. typeandclassparams += 'add("' + type_utxt + '", '
  357. typeandclassparams += str(type_code) + ', "' + class_utxt
  358. typeandclassparams += '", ' + str(class_code)
  359. typeandclassparams += ', RdataFactoryPtr(new ' + rdf_class + '<'
  360. typeandclassparams += class_txt + '::' + type_utxt + '>()));\n'
  361. else:
  362. typeandclassparams += 'add("' + type_utxt + '", ' + str(type_code)
  363. typeandclassparams += ', RdataFactoryPtr(new ' + rdf_class + '<'
  364. typeandclassparams += class_txt + '::' + type_utxt + '>()));\n'
  365. typeandclassparams += indent + '// Meta and non-implemented RR types\n'
  366. for type_code, type_txt in meta_types.items():
  367. typeandclassparams += indent + \
  368. 'addType("' + type_txt.upper() + '", ' + type_code + ');\n'
  369. typeandclassparams += indent + '// Meta classes\n'
  370. for cls_code, cls_txt in meta_classes.items():
  371. typeandclassparams += indent + \
  372. 'addClass("' + cls_txt.upper() + '", ' + cls_code + ');\n'
  373. rrparam_temp = open(placeholder, 'r')
  374. rrparam_out = open(outputfile, 'w')
  375. rrparam_out.write(heading_txt)
  376. for line in rrparam_temp.readlines():
  377. rrparam_out.write(line)
  378. if re.match('\s+// BEGIN_WELL_KNOWN_PARAMS', line):
  379. rrparam_out.write(typeandclassparams)
  380. rrparam_temp.close()
  381. rrparam_out.close()
  382. if __name__ == "__main__":
  383. try:
  384. import_definitions(classcode2txt, typecode2txt, typeandclass)
  385. generate_rdatadef('@builddir@/rdataclass.cc', rdatadef_mtime)
  386. generate_rdatahdr('@builddir@/rdataclass.h', heading_txt,
  387. rdata_declarations, rdatahdr_mtime)
  388. # merge auto-generated types/classes with meta maps and generate the
  389. # corresponding code.
  390. generate_typeclasscode('rrtype', rdatahdr_mtime,
  391. dict(typecode2txt, **meta_types), 'Type')
  392. generate_typeclasscode('rrclass', classdir_mtime,
  393. dict(classcode2txt, **meta_classes), 'Class')
  394. generate_rrparam('rrparamregistry', rdatahdr_mtime)
  395. except:
  396. sys.stderr.write('Code generation failed due to exception: %s\n' %
  397. sys.exc_info()[1])
  398. exit(1)