| 
									
										
										
										
											2012-06-22 21:12:59 +02:00
										 |  |  | #! /usr/bin/env python3 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-22 22:07:01 +02:00
										 |  |  | """
 | 
					
						
							|  |  |  | This script should be called *manually* when we want to upgrade SSLError | 
					
						
							| 
									
										
										
										
											2019-07-31 08:16:13 +10:00
										 |  |  | `library` and `reason` mnemonics to a more recent OpenSSL version. | 
					
						
							| 
									
										
										
										
											2012-06-22 22:07:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | It takes two arguments: | 
					
						
							| 
									
										
										
										
											2015-01-18 17:39:32 +01:00
										 |  |  | - the path to the OpenSSL source tree (e.g. git checkout) | 
					
						
							| 
									
										
										
										
											2021-04-09 18:34:39 +02:00
										 |  |  | - the path to the header file to be generated Modules/_ssl_data_{version}.h | 
					
						
							|  |  |  | - error codes are version specific | 
					
						
							| 
									
										
										
										
											2012-06-22 22:07:01 +02:00
										 |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-09 18:34:39 +02:00
										 |  |  | import argparse | 
					
						
							| 
									
										
										
										
											2012-06-22 21:12:59 +02:00
										 |  |  | import datetime | 
					
						
							| 
									
										
										
										
											2021-04-09 18:34:39 +02:00
										 |  |  | import operator | 
					
						
							| 
									
										
										
										
											2012-06-22 21:12:59 +02:00
										 |  |  | import os | 
					
						
							|  |  |  | import re | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-09 18:34:39 +02:00
										 |  |  | 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: | 
					
						
							| 
									
										
										
										
											2012-06-22 21:12:59 +02:00
										 |  |  |         for line in f: | 
					
						
							|  |  |  |             match = pat.search(line) | 
					
						
							| 
									
										
										
										
											2021-04-09 18:34:39 +02:00
										 |  |  |             if match is not None: | 
					
						
							|  |  |  |                 yield match | 
					
						
							| 
									
										
										
										
											2012-06-22 21:12:59 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-09 18:34:39 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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 | 
					
						
							| 
									
										
										
										
											2020-04-11 15:36:12 -05:00
										 |  |  |             continue | 
					
						
							| 
									
										
										
										
											2021-04-09 18:34:39 +02:00
										 |  |  |         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() |