diff options
Diffstat (limited to 'src/lib/dns/gen-rdatacode.py.in')
-rw-r--r-- | src/lib/dns/gen-rdatacode.py.in | 391 |
1 files changed, 391 insertions, 0 deletions
diff --git a/src/lib/dns/gen-rdatacode.py.in b/src/lib/dns/gen-rdatacode.py.in new file mode 100644 index 0000000..f39fc09 --- /dev/null +++ b/src/lib/dns/gen-rdatacode.py.in @@ -0,0 +1,391 @@ +#!@PYTHON@ + +# Copyright (C) 2010-2021 Internet Systems Consortium, Inc. ("ISC") +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +"""\ +This is a supplemental script to (half) auto-generate DNS Rdata related +classes and constants. +""" + +# This script should be used every time existing RR data changes or when new +# RR types are added or existing are removed. Since DHCP uses only four +# (A,AAAA,PTR,DHCID) RR types, its usage is expected to be an uncommon event. +# The only envisaged use case is if/when we decide to trim down libdns++. + +import os +from os.path import getmtime +import re +import sys + +re_typecode = re.compile('([\da-z\-]+)_(\d+)') +classcode2txt = {} +typecode2txt = {} +# For meta types and types well-known but not implemented. This is a dict from +# type code values (as string) to textual mnemonic. +meta_types = { + # Real meta types. We won't have Rdata implement for them, but we need + # RRType constants. + '251': 'ixfr', '252': 'axfr', '255': 'any', + # Obsolete types. We probably won't implement Rdata for them, but it's + # better to have RRType constants. + '3': 'md', '4': 'mf', '7': 'mb', '8': 'mg', '9': 'mr', '30': 'nxt', + '38': 'a6', '254': 'maila', + # Types officially assigned but not yet supported in our implementation. + '10': 'null', '11': 'wks', '19': 'x25', '21': 'rt', '22': 'nsap', + '23': 'nsap-ptr', '24': 'sig', '20': 'isdn', '25': 'key', '26': 'px', + '27': 'gpos', '29': 'loc', '36': 'kx', '37': 'cert', '42': 'apl', + '45': 'ipseckey', '55': 'hip', '103': 'unspec', + '104': 'nid', '105': 'l32', '106': 'l64', '107': 'lp', + '253': 'mailb', '256': 'uri' + } +# Classes that don't have any known types. This is a dict from type code +# values (as string) to textual mnemonic. +meta_classes = {'254': 'none'} +typeandclass = [] +generic_code = 65536 # something larger than any code value +rdata_declarations = '' +class_definitions = '' +classdir_mtime = 0 +rdatadef_mtime = 0 +rdatahdr_mtime = 0 +heading_txt = '''/////////////// +/////////////// +/////////////// THIS FILE IS AUTOMATICALLY GENERATED BY gen-rdatacode.py. +/////////////// DO NOT EDIT! +/////////////// +/////////////// + +''' + +def import_classdef(class_txt, file): + content = '' + rdata_source = open(file, 'r') + for line in rdata_source.readlines(): + if re.match('// BEGIN_ISC_NAMESPACE', line): + content += 'namespace isc {\n' + content += 'namespace dns {\n' + continue + if re.match('// BEGIN_RDATA_NAMESPACE', line): + content += 'namespace rdata {\n' + content += 'namespace ' + class_txt + ' {\n' + continue + if re.match('// END_ISC_NAMESPACE', line): + content += '} // end of namespace "dns"\n' + content += '} // end of namespace "isc"\n' + continue + if re.match('// END_RDATA_NAMESPACE', line): + content += '} // end of namespace "' + class_txt +'"\n' + content += '} // end of namespace "rdata"\n' + continue + content += line + rdata_source.close() + return content + +def import_classheader(class_txt, type_txt, type_code, file): + type_utxt = type_txt.upper() + class_utxt = class_txt.upper() + + # for each CLASS_n/TYPE_m.h + rdata_header = open(file, 'r') + content = '' + guard_macro = class_txt.upper() + '_' + type_txt.upper() + guard_macro += '_' + type_code + '_H' + for line in rdata_header.readlines(): + if re.match('// BEGIN_HEADER_GUARD', line): + content += '#ifndef ' + guard_macro + '\n' + content += '#define ' + guard_macro + ' 1\n' + continue + if re.match('// END_HEADER_GUARD', line): + content += '#endif // ' + guard_macro + '\n' + continue + if re.match('// BEGIN_ISC_NAMESPACE', line): + content += 'namespace isc {\n' + content += 'namespace util {\n' + content += ''' +class InputBuffer; +class OutputBuffer;\n''' + content += '}\n\n' + content += 'namespace dns {\n' + continue + if re.match('// BEGIN_RDATA_NAMESPACE', line): + content += 'namespace rdata {\n' + content += 'namespace ' + class_txt + ' {\n' + continue + if re.match('// END_ISC_NAMESPACE', line): + content += '} // end of namespace "dns"\n' + content += '} // end of namespace "isc"\n' + continue + if re.match('// END_RDATA_NAMESPACE', line): + content += '} // end of namespace "' + class_txt +'"\n' + content += '} // end of namespace "rdata"\n' + continue + if re.match('// Local Variables:', line): + break + content += line + if re.match('// BEGIN_COMMON_DECLARATIONS', line): + content += ''' +class AbstractMessageRenderer;\n\n''' + if re.match('\s+// BEGIN_COMMON_MEMBERS$', line): + content += ''' + explicit ''' + type_utxt + '''(const std::string& type_str); + ''' + type_utxt + '''(isc::util::InputBuffer& buffer, size_t rdata_len); + ''' + type_utxt + '''(const ''' + type_utxt + '''& other); + ''' + type_utxt + '''( + MasterLexer& lexer, const Name* name, + MasterLoader::Options options, MasterLoaderCallbacks& callbacks); + virtual std::string toText() const; + virtual void toWire(isc::util::OutputBuffer& buffer) const; + virtual void toWire(AbstractMessageRenderer& renderer) const; + virtual int compare(const Rdata& other) const;\n\n''' + rdata_header.close() + return content + +def import_definitions(classcode2txt, typecode2txt, typeandclass): + global rdata_declarations + global class_definitions + global classdir_mtime + global rdatadef_mtime + global rdatahdr_mtime + + if classdir_mtime < getmtime('@srcdir@/rdata'): + classdir_mtime = getmtime('@srcdir@/rdata') + + # Sort directories before iterating through them so that the directory + # list is processed in the same order on all systems. The resulting + # files should compile regardless of the order in which the components + # are included but... Having a fixed order for the directories should + # eliminate system-dependent problems. (Note that the directory names + # in BIND 10 are ASCII, so the order should be locale-independent.) + dirlist = os.listdir('@srcdir@/rdata') + dirlist.sort() + for dir in dirlist: + classdir = '@srcdir@/rdata' + os.sep + dir + m = re_typecode.match(dir) + if os.path.isdir(classdir) and (m != None or dir == 'generic'): + if dir == 'generic': + class_txt = 'generic' + class_code = generic_code + else: + class_txt = m.group(1) + class_code = m.group(2) + if not class_code in classcode2txt: + classcode2txt[class_code] = class_txt + + # Same considerations as directories regarding sorted order + # also apply to files. + filelist = os.listdir(classdir) + filelist.sort() + for file in filelist: + file = classdir + os.sep + file + m = re_typecode.match(os.path.split(file)[1]) + if m != None: + type_txt = m.group(1) + type_code = m.group(2) + if not type_code in typecode2txt: + typecode2txt[type_code] = type_txt + if re.search('\.cc$', file): + if rdatadef_mtime < getmtime(file): + rdatadef_mtime = getmtime(file) + class_definitions += import_classdef(class_txt, file) + elif re.search('\.h$', file): + if rdatahdr_mtime < getmtime(file): + rdatahdr_mtime = getmtime(file) + rdata_declarations += import_classheader(class_txt, + type_txt, + type_code, + file) + typeandclass.append((type_txt, int(type_code), + (class_txt, class_txt), + int(class_code))) + if class_txt == 'generic': + typeandclass.append((type_txt, int(type_code), + (class_txt, 'in'), 1)) + +def need_generate(file, mtime): + '''Check if we need to generate the specified file. + + To avoid unnecessary compilation, we skip (re)generating the file when + the file already exists and newer than the base file. + ''' + if os.path.exists(file) and getmtime(file) > mtime: + return False + return True + +def generate_rdatadef(file, basemtime): + if not need_generate(file, basemtime): + print('skip generating ' + file); + return + rdata_deffile = open(file, 'w') + rdata_deffile.write(heading_txt) + rdata_deffile.write(class_definitions) + rdata_deffile.close() + +def generate_rdatahdr(file, heading, declarations, basemtime): + if not need_generate(file, basemtime): + print('skip generating ' + file); + return + heading += ''' +#ifndef DNS_RDATACLASS_H +#define DNS_RDATACLASS_H 1 + +#include <dns/master_loader.h> + +namespace isc { +namespace dns { +class Name; +class MasterLexer; +class MasterLoaderCallbacks; +} +} +''' + declarations += ''' +#endif // DNS_RDATACLASS_H + +// Local Variables: +// mode: c++ +// End: +''' + rdata_header = open(file, 'w') + rdata_header.write(heading) + rdata_header.write(declarations) + rdata_header.close() + +def generate_typeclasscode(fileprefix, basemtime, code2txt, type_or_class): + placeholder = '@srcdir@/' + fileprefix + '-placeholder.h' + outputfile = '@builddir@/' + fileprefix + '.h' + py_outputfile = '@builddir@/python/' + fileprefix + '_constants_inc.cc' + upper_key = type_or_class.upper() # TYPE or CLASS + lower_key = 'rr' + type_or_class.lower() # rrtype or rrclass + cap_key = type_or_class # Type or Class + + # We only decide whether to generate files for libdns++ files; Python + # files are generated if and only if libdns++ files are generated. + # In practice it should be sufficient. + if (not need_generate(outputfile, basemtime) and + getmtime(outputfile) > getmtime(placeholder)): + print('skip generating ' + outputfile) + return + + # Create a list of (code, code-text) pairs, where code-text is generally + # upper-cased, with applying special filters when necessary. + def convert(code_txt): + # Workaround by heuristics: there's a "NULL" RR type, but it would + # cause conflict with the C/C++ macro. We use Null as a special case. + if code_txt == 'null': + return 'Null' + # Likewise, convert "nsap-ptr" to "NSAP_PTR" as a dash cannot be part + # of a C/C++ variable. + if code_txt == 'nsap-ptr': + return 'NSAP_PTR' + return code_txt.upper() + codes = [ (code, convert(txt)) for code, txt in code2txt.items() ] + + # Dump source code for libdns++ + with open(placeholder, 'r') as header_temp: + with open(outputfile, 'w') as header_out: + header_out.write(heading_txt) + for line in header_temp: + header_out.write(line) + if re.match('\s+// BEGIN_WELL_KNOWN_' + upper_key + + '_DECLARATIONS$', line): + for code in codes: + header_out.write(' ' * 4 + 'static const RR' + + cap_key + '& ' + code[1] + '();\n') + if re.match('// BEGIN_WELL_KNOWN_' + upper_key + + '_DEFINITIONS$', line): + for code in codes: + header_out.write('''inline const RR''' + cap_key + + '''& +RR''' + cap_key + '''::''' + code[1] + '''() { + static RR''' + cap_key + ''' ''' + lower_key + '''(''' + code[0] + '''); + return (''' + lower_key + '''); +}\n +''') + + # Dump source code snippet for isc.dns Python module + with open(py_outputfile, 'w') as py_out: + py_out.write(" // auto-generated by ../gen-rdatacode.py." + " Don't edit this file.\n") + py_out.write("\n") + for code in codes: + py_out.write('''\ + installClassVariable(''' + lower_key + '''_type, "''' + code[1] + '''", + createRR''' + cap_key + '''Object(RR''' + \ + cap_key + '''::''' + code[1] + '''())); +''') + +def generate_rrparam(fileprefix, basemtime): + placeholder = '@srcdir@/' + fileprefix + '-placeholder.cc' + outputfile = '@builddir@/' + fileprefix + '.cc' + if not need_generate(outputfile, basemtime) and getmtime(outputfile) > getmtime(placeholder): + print('skip generating ' + outputfile) + return + + # sort by class, then by type + typeandclassparams = '' + typeandclass.sort(key = lambda x: (x[3], x[1])) + for param in typeandclass: + # for rrparamregistry.cc + # each param is a tuple of (type_txt, type_code, class_tuple, + # class_code) + (type_txt, type_code, class_tuple, class_code) = param + type_utxt = type_txt.upper() + class_txt = class_tuple[0] + class_utxt = class_tuple[1].upper() + indent = ' ' * 8 + typeandclassparams += indent + + if class_tuple[1] != 'generic': + typeandclassparams += 'add("' + type_utxt + '", ' + typeandclassparams += str(type_code) + ', "' + class_utxt + typeandclassparams += '", ' + str(class_code) + typeandclassparams += ', RdataFactoryPtr(new ' + 'RdataFactory' + '<' + typeandclassparams += class_txt + '::' + type_utxt + '>()));\n' + else: + typeandclassparams += 'add("' + type_utxt + '", ' + str(type_code) + typeandclassparams += ', RdataFactoryPtr(new ' + 'RdataFactory' + '<' + typeandclassparams += class_txt + '::' + type_utxt + '>()));\n' + + typeandclassparams += indent + '// Meta and non-implemented RR types\n' + for type_code, type_txt in meta_types.items(): + typeandclassparams += indent + \ + 'addType("' + type_txt.upper() + '", ' + type_code + ');\n' + + typeandclassparams += indent + '// Meta classes\n' + for cls_code, cls_txt in meta_classes.items(): + typeandclassparams += indent + \ + 'addClass("' + cls_txt.upper() + '", ' + cls_code + ');\n' + + rrparam_temp = open(placeholder, 'r') + rrparam_out = open(outputfile, 'w') + rrparam_out.write(heading_txt) + for line in rrparam_temp.readlines(): + rrparam_out.write(line) + if re.match('\s+// BEGIN_WELL_KNOWN_PARAMS', line): + rrparam_out.write(typeandclassparams) + rrparam_temp.close() + rrparam_out.close() + +if __name__ == "__main__": + try: + import_definitions(classcode2txt, typecode2txt, typeandclass) + generate_rdatadef('@builddir@/rdataclass.cc', rdatadef_mtime) + generate_rdatahdr('@builddir@/rdataclass.h', heading_txt, + rdata_declarations, rdatahdr_mtime) + + # merge auto-generated types/classes with meta maps and generate the + # corresponding code. + generate_typeclasscode('rrtype', rdatahdr_mtime, + dict(typecode2txt, **meta_types), 'Type') + generate_typeclasscode('rrclass', classdir_mtime, + dict(classcode2txt, **meta_classes), 'Class') + + generate_rrparam('rrparamregistry', rdatahdr_mtime) + except: + sys.stderr.write('Code generation failed due to exception: %s\n' % + sys.exc_info()[1]) + exit(1) |