summaryrefslogtreecommitdiffstats
path: root/src/lib/dns/gen-rdatacode.py.in
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dns/gen-rdatacode.py.in')
-rw-r--r--src/lib/dns/gen-rdatacode.py.in391
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)