17db96d56Sopenharmony_ci#! /usr/bin/env python3
27db96d56Sopenharmony_ci
37db96d56Sopenharmony_ci"""
47db96d56Sopenharmony_ciThis script should be called *manually* when we want to upgrade SSLError
57db96d56Sopenharmony_ci`library` and `reason` mnemonics to a more recent OpenSSL version.
67db96d56Sopenharmony_ci
77db96d56Sopenharmony_ciIt takes two arguments:
87db96d56Sopenharmony_ci- the path to the OpenSSL source tree (e.g. git checkout)
97db96d56Sopenharmony_ci- the path to the header file to be generated Modules/_ssl_data_{version}.h
107db96d56Sopenharmony_ci- error codes are version specific
117db96d56Sopenharmony_ci"""
127db96d56Sopenharmony_ci
137db96d56Sopenharmony_ciimport argparse
147db96d56Sopenharmony_ciimport datetime
157db96d56Sopenharmony_ciimport operator
167db96d56Sopenharmony_ciimport os
177db96d56Sopenharmony_ciimport re
187db96d56Sopenharmony_ciimport sys
197db96d56Sopenharmony_ci
207db96d56Sopenharmony_ci
217db96d56Sopenharmony_ciparser = argparse.ArgumentParser(
227db96d56Sopenharmony_ci    description="Generate ssl_data.h from OpenSSL sources"
237db96d56Sopenharmony_ci)
247db96d56Sopenharmony_ciparser.add_argument("srcdir", help="OpenSSL source directory")
257db96d56Sopenharmony_ciparser.add_argument(
267db96d56Sopenharmony_ci    "output", nargs="?", type=argparse.FileType("w"), default=sys.stdout
277db96d56Sopenharmony_ci)
287db96d56Sopenharmony_ci
297db96d56Sopenharmony_ci
307db96d56Sopenharmony_cidef _file_search(fname, pat):
317db96d56Sopenharmony_ci    with open(fname, encoding="utf-8") as f:
327db96d56Sopenharmony_ci        for line in f:
337db96d56Sopenharmony_ci            match = pat.search(line)
347db96d56Sopenharmony_ci            if match is not None:
357db96d56Sopenharmony_ci                yield match
367db96d56Sopenharmony_ci
377db96d56Sopenharmony_ci
387db96d56Sopenharmony_cidef parse_err_h(args):
397db96d56Sopenharmony_ci    """Parse err codes, e.g. ERR_LIB_X509: 11"""
407db96d56Sopenharmony_ci    pat = re.compile(r"#\s*define\W+ERR_LIB_(\w+)\s+(\d+)")
417db96d56Sopenharmony_ci    lib2errnum = {}
427db96d56Sopenharmony_ci    for match in _file_search(args.err_h, pat):
437db96d56Sopenharmony_ci        libname, num = match.groups()
447db96d56Sopenharmony_ci        lib2errnum[libname] = int(num)
457db96d56Sopenharmony_ci
467db96d56Sopenharmony_ci    return lib2errnum
477db96d56Sopenharmony_ci
487db96d56Sopenharmony_ci
497db96d56Sopenharmony_cidef parse_openssl_error_text(args):
507db96d56Sopenharmony_ci    """Parse error reasons, X509_R_AKID_MISMATCH"""
517db96d56Sopenharmony_ci    # ignore backslash line continuation for now
527db96d56Sopenharmony_ci    pat = re.compile(r"^((\w+?)_R_(\w+)):(\d+):")
537db96d56Sopenharmony_ci    for match in _file_search(args.errtxt, pat):
547db96d56Sopenharmony_ci        reason, libname, errname, num = match.groups()
557db96d56Sopenharmony_ci        if "_F_" in reason:
567db96d56Sopenharmony_ci            # ignore function codes
577db96d56Sopenharmony_ci            continue
587db96d56Sopenharmony_ci        num = int(num)
597db96d56Sopenharmony_ci        yield reason, libname, errname, num
607db96d56Sopenharmony_ci
617db96d56Sopenharmony_ci
627db96d56Sopenharmony_cidef parse_extra_reasons(args):
637db96d56Sopenharmony_ci    """Parse extra reasons from openssl.ec"""
647db96d56Sopenharmony_ci    pat = re.compile(r"^R\s+((\w+)_R_(\w+))\s+(\d+)")
657db96d56Sopenharmony_ci    for match in _file_search(args.errcodes, pat):
667db96d56Sopenharmony_ci        reason, libname, errname, num = match.groups()
677db96d56Sopenharmony_ci        num = int(num)
687db96d56Sopenharmony_ci        yield reason, libname, errname, num
697db96d56Sopenharmony_ci
707db96d56Sopenharmony_ci
717db96d56Sopenharmony_cidef gen_library_codes(args):
727db96d56Sopenharmony_ci    """Generate table short libname to numeric code"""
737db96d56Sopenharmony_ci    yield "static struct py_ssl_library_code library_codes[] = {"
747db96d56Sopenharmony_ci    for libname in sorted(args.lib2errnum):
757db96d56Sopenharmony_ci        yield f"#ifdef ERR_LIB_{libname}"
767db96d56Sopenharmony_ci        yield f'    {{"{libname}", ERR_LIB_{libname}}},'
777db96d56Sopenharmony_ci        yield "#endif"
787db96d56Sopenharmony_ci    yield "    { NULL }"
797db96d56Sopenharmony_ci    yield "};"
807db96d56Sopenharmony_ci    yield ""
817db96d56Sopenharmony_ci
827db96d56Sopenharmony_ci
837db96d56Sopenharmony_cidef gen_error_codes(args):
847db96d56Sopenharmony_ci    """Generate error code table for error reasons"""
857db96d56Sopenharmony_ci    yield "static struct py_ssl_error_code error_codes[] = {"
867db96d56Sopenharmony_ci    for reason, libname, errname, num in args.reasons:
877db96d56Sopenharmony_ci        yield f"  #ifdef {reason}"
887db96d56Sopenharmony_ci        yield f'    {{"{errname}", ERR_LIB_{libname}, {reason}}},'
897db96d56Sopenharmony_ci        yield "  #else"
907db96d56Sopenharmony_ci        yield f'    {{"{errname}", {args.lib2errnum[libname]}, {num}}},'
917db96d56Sopenharmony_ci        yield "  #endif"
927db96d56Sopenharmony_ci
937db96d56Sopenharmony_ci    yield "    { NULL }"
947db96d56Sopenharmony_ci    yield "};"
957db96d56Sopenharmony_ci    yield ""
967db96d56Sopenharmony_ci
977db96d56Sopenharmony_ci
987db96d56Sopenharmony_cidef main():
997db96d56Sopenharmony_ci    args = parser.parse_args()
1007db96d56Sopenharmony_ci
1017db96d56Sopenharmony_ci    args.err_h = os.path.join(args.srcdir, "include", "openssl", "err.h")
1027db96d56Sopenharmony_ci    if not os.path.isfile(args.err_h):
1037db96d56Sopenharmony_ci        # Fall back to infile for OpenSSL 3.0.0
1047db96d56Sopenharmony_ci        args.err_h += ".in"
1057db96d56Sopenharmony_ci    args.errcodes = os.path.join(args.srcdir, "crypto", "err", "openssl.ec")
1067db96d56Sopenharmony_ci    args.errtxt = os.path.join(args.srcdir, "crypto", "err", "openssl.txt")
1077db96d56Sopenharmony_ci
1087db96d56Sopenharmony_ci    if not os.path.isfile(args.errtxt):
1097db96d56Sopenharmony_ci        parser.error(f"File {args.errtxt} not found in srcdir\n.")
1107db96d56Sopenharmony_ci
1117db96d56Sopenharmony_ci    # {X509: 11, ...}
1127db96d56Sopenharmony_ci    args.lib2errnum = parse_err_h(args)
1137db96d56Sopenharmony_ci
1147db96d56Sopenharmony_ci    # [('X509_R_AKID_MISMATCH', 'X509', 'AKID_MISMATCH', 110), ...]
1157db96d56Sopenharmony_ci    reasons = []
1167db96d56Sopenharmony_ci    reasons.extend(parse_openssl_error_text(args))
1177db96d56Sopenharmony_ci    reasons.extend(parse_extra_reasons(args))
1187db96d56Sopenharmony_ci    # sort by libname, numeric error code
1197db96d56Sopenharmony_ci    args.reasons = sorted(reasons, key=operator.itemgetter(0, 3))
1207db96d56Sopenharmony_ci
1217db96d56Sopenharmony_ci    lines = [
1227db96d56Sopenharmony_ci        "/* File generated by Tools/ssl/make_ssl_data.py */"
1237db96d56Sopenharmony_ci        f"/* Generated on {datetime.datetime.utcnow().isoformat()} */"
1247db96d56Sopenharmony_ci    ]
1257db96d56Sopenharmony_ci    lines.extend(gen_library_codes(args))
1267db96d56Sopenharmony_ci    lines.append("")
1277db96d56Sopenharmony_ci    lines.extend(gen_error_codes(args))
1287db96d56Sopenharmony_ci
1297db96d56Sopenharmony_ci    for line in lines:
1307db96d56Sopenharmony_ci        args.output.write(line + "\n")
1317db96d56Sopenharmony_ci
1327db96d56Sopenharmony_ci
1337db96d56Sopenharmony_ciif __name__ == "__main__":
1347db96d56Sopenharmony_ci    main()
135