Viewing file: make_ssl_data.py (3.92 KB) -rwxr-xr-x Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
#! /usr/bin/env python3
""" This script should be called *manually* when we want to upgrade SSLError `library` and `reason` mnemonics to a more recent OpenSSL version.
It takes two arguments: - the path to the OpenSSL source tree (e.g. git checkout) - the path to the header file to be generated Modules/_ssl_data_{version}.h - error codes are version specific """
import argparse import datetime import operator import os import re import sys
parser = argparse.ArgumentParser( description="Generate ssl_data.h from OpenSSL sources" ) parser.add_argument("srcdir", help="OpenSSL source directory") parser.add_argument( "output", nargs="?", type=argparse.FileType("w"), default=sys.stdout )
def _file_search(fname, pat): with open(fname, encoding="utf-8") as f: for line in f: match = pat.search(line) if match is not None: yield match
def parse_err_h(args): """Parse err codes, e.g. ERR_LIB_X509: 11""" pat = re.compile(r"#\s*define\W+ERR_LIB_(\w+)\s+(\d+)") lib2errnum = {} for match in _file_search(args.err_h, pat): libname, num = match.groups() lib2errnum[libname] = int(num)
return lib2errnum
def parse_openssl_error_text(args): """Parse error reasons, X509_R_AKID_MISMATCH""" # ignore backslash line continuation for now pat = re.compile(r"^((\w+?)_R_(\w+)):(\d+):") for match in _file_search(args.errtxt, pat): reason, libname, errname, num = match.groups() if "_F_" in reason: # ignore function codes continue num = int(num) yield reason, libname, errname, num
def parse_extra_reasons(args): """Parse extra reasons from openssl.ec""" pat = re.compile(r"^R\s+((\w+)_R_(\w+))\s+(\d+)") for match in _file_search(args.errcodes, pat): reason, libname, errname, num = match.groups() num = int(num) yield reason, libname, errname, num
def gen_library_codes(args): """Generate table short libname to numeric code""" yield "static struct py_ssl_library_code library_codes[] = {" for libname in sorted(args.lib2errnum): yield f"#ifdef ERR_LIB_{libname}" yield f' {{"{libname}", ERR_LIB_{libname}}},' yield "#endif" yield " { NULL }" yield "};" yield ""
def gen_error_codes(args): """Generate error code table for error reasons""" yield "static struct py_ssl_error_code error_codes[] = {" for reason, libname, errname, num in args.reasons: yield f" #ifdef {reason}" yield f' {{"{errname}", ERR_LIB_{libname}, {reason}}},' yield " #else" yield f' {{"{errname}", {args.lib2errnum[libname]}, {num}}},' yield " #endif"
yield " { NULL }" yield "};" yield ""
def main(): args = parser.parse_args()
args.err_h = os.path.join(args.srcdir, "include", "openssl", "err.h") if not os.path.isfile(args.err_h): # Fall back to infile for OpenSSL 3.0.0 args.err_h += ".in" args.errcodes = os.path.join(args.srcdir, "crypto", "err", "openssl.ec") args.errtxt = os.path.join(args.srcdir, "crypto", "err", "openssl.txt")
if not os.path.isfile(args.errtxt): parser.error(f"File {args.errtxt} not found in srcdir\n.")
# {X509: 11, ...} args.lib2errnum = parse_err_h(args)
# [('X509_R_AKID_MISMATCH', 'X509', 'AKID_MISMATCH', 110), ...] reasons = [] reasons.extend(parse_openssl_error_text(args)) reasons.extend(parse_extra_reasons(args)) # sort by libname, numeric error code args.reasons = sorted(reasons, key=operator.itemgetter(0, 3))
lines = [ "/* File generated by Tools/ssl/make_ssl_data.py */" f"/* Generated on {datetime.datetime.utcnow().isoformat()} */" ] lines.extend(gen_library_codes(args)) lines.append("") lines.extend(gen_error_codes(args))
for line in lines: args.output.write(line + "\n")
if __name__ == "__main__": main()
|