mirror of
				https://github.com/python/cpython.git
				synced 2025-10-25 15:58:57 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			134 lines
		
	
	
	
		
			3.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			134 lines
		
	
	
	
		
			3.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
| #! /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()
 | 
