1#! /usr/bin/env python3 2 3""" 4This script should be called *manually* when we want to upgrade SSLError 5`library` and `reason` mnemonics to a more recent OpenSSL version. 6 7It takes two arguments: 8- the path to the OpenSSL source tree (e.g. git checkout) 9- the path to the C file to be generated 10 (probably Modules/_ssl_data.h) 11""" 12 13import datetime 14import glob 15import os 16import re 17import sys 18import _ssl 19 20 21def parse_error_codes(h_file, prefix, libcode): 22 pat = re.compile(r"#\s*define\W+(%s([\w]+))\W+(\d+)\b" % re.escape(prefix)) 23 codes = [] 24 with open(h_file, "r", encoding="latin1") as f: 25 for line in f: 26 match = pat.search(line) 27 if match: 28 code, name, num = match.groups() 29 num = int(num) 30 # e.g. ("SSL_R_BAD_DATA", ("ERR_LIB_SSL", "BAD_DATA", 390)) 31 codes.append((code, (libcode, name, num))) 32 assert codes, f"no codes found in {h_file}" 33 return codes 34 35if __name__ == "__main__": 36 openssl_inc = sys.argv[1] 37 outfile = sys.argv[2] 38 use_stdout = outfile == '-' 39 f = sys.stdout if use_stdout else open(outfile, "w") 40 # mnemonic -> (library code, error prefix, header file) 41 error_libraries = {} 42 for error_header in glob.glob(os.path.join(glob.escape(openssl_inc), 'include/openssl/*err.h')): 43 base = os.path.basename(error_header) 44 if base in ('buffererr.h', 'objectserr.h', 'storeerr.h'): 45 # Deprecated in 3.0. 46 continue 47 mnemonic = base[:-5].upper() 48 if mnemonic == "": 49 # err.h 50 lib_codes = { 51 code: num 52 for (code, (_, _, num)) in parse_error_codes(error_header, 'ERR_LIB_', None) 53 } 54 else: 55 error_libraries[mnemonic] = (f'ERR_LIB_{mnemonic}', f'{mnemonic}_R_', error_header) 56 57 # Read codes from libraries 58 new_codes = [] 59 for libcode, prefix, h_file in sorted(error_libraries.values()): 60 new_codes += parse_error_codes(h_file, prefix, libcode) 61 new_code_nums = set((libcode, num) 62 for (code, (libcode, name, num)) in new_codes) 63 64 # Merge with existing codes (in case some old codes disappeared). 65 codes = {} 66 for errname, (libnum, errnum) in _ssl.err_names_to_codes.items(): 67 lib = error_libraries[_ssl.lib_codes_to_names[libnum]] 68 libcode = lib[0] # e.g. ERR_LIB_PEM 69 errcode = lib[1] + errname # e.g. SSL_R_BAD_SSL_SESSION_ID_LENGTH 70 # Only keep it if the numeric codes weren't reused 71 if (libcode, errnum) not in new_code_nums: 72 codes[errcode] = libcode, errname, errnum 73 codes.update(dict(new_codes)) 74 75 def w(l): 76 f.write(l + "\n") 77 w("/* File generated by Tools/ssl/make_ssl_data.py */") 78 w("/* Generated on %s */" % datetime.datetime.now().isoformat()) 79 w("") 80 81 w("static struct py_ssl_library_code library_codes[] = {") 82 for mnemo, (libcode, _, _) in sorted(error_libraries.items()): 83 w(f'#ifdef {libcode}') 84 w(' {"%s", %s},' % (mnemo, libcode)) 85 w('#endif') 86 w(' { NULL }') 87 w('};') 88 w("") 89 90 w("static struct py_ssl_error_code error_codes[] = {") 91 for errcode, (libcode, name, num) in sorted(codes.items()): 92 w(' #ifdef %s' % (errcode)) 93 w(' {"%s", %s, %s},' % (name, libcode, errcode)) 94 w(' #else') 95 w(' {"%s", %s, %d},' % (name, lib_codes[libcode], num)) 96 w(' #endif') 97 w(' { NULL }') 98 w('};') 99 if not use_stdout: 100 f.close() 101