• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#! /usr/bin/env python3
2
3"""
4This script should be called *manually* when we want to upgrade SSLError
5`library` and `reason` mnemnonics 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 os
15import re
16import sys
17import _ssl
18
19
20def parse_error_codes(h_file, prefix, libcode):
21    pat = re.compile(r"#define\W+(%s([\w]+))\W+(\d+)\b" % re.escape(prefix))
22    codes = []
23    with open(h_file, "r", encoding="latin1") as f:
24        for line in f:
25            match = pat.search(line)
26            if match:
27                code, name, num = match.groups()
28                num = int(num)
29                # e.g. ("SSL_R_BAD_DATA", ("ERR_LIB_SSL", "BAD_DATA", 390))
30                codes.append((code, (libcode, name, num)))
31    return codes
32
33if __name__ == "__main__":
34    openssl_inc = sys.argv[1]
35    outfile = sys.argv[2]
36    use_stdout = outfile == '-'
37    f = sys.stdout if use_stdout else open(outfile, "w")
38    error_libraries = {
39        # mnemonic -> (library code, error prefix, header file)
40        'PEM': ('ERR_LIB_PEM', 'PEM_R_', 'crypto/pem/pem.h'),
41        'SSL': ('ERR_LIB_SSL', 'SSL_R_', 'ssl/ssl.h'),
42        'X509': ('ERR_LIB_X509', 'X509_R_', 'crypto/x509/x509.h'),
43        }
44
45    # Read codes from libraries
46    new_codes = []
47    for libcode, prefix, h_file in sorted(error_libraries.values()):
48        new_codes += parse_error_codes(os.path.join(openssl_inc, h_file),
49                                       prefix, libcode)
50    new_code_nums = set((libcode, num)
51                        for (code, (libcode, name, num)) in new_codes)
52
53    # Merge with existing codes (in case some old codes disappeared).
54    codes = {}
55    for errname, (libnum, errnum) in _ssl.err_names_to_codes.items():
56        lib = error_libraries[_ssl.lib_codes_to_names[libnum]]
57        libcode = lib[0]              # e.g. ERR_LIB_PEM
58        errcode = lib[1] + errname    # e.g. SSL_R_BAD_SSL_SESSION_ID_LENGTH
59        # Only keep it if the numeric codes weren't reused
60        if (libcode, errnum) not in new_code_nums:
61            codes[errcode] = libcode, errname, errnum
62    codes.update(dict(new_codes))
63
64    def w(l):
65        f.write(l + "\n")
66    w("/* File generated by Tools/ssl/make_ssl_data.py */")
67    w("/* Generated on %s */" % datetime.datetime.now().isoformat())
68    w("")
69
70    w("static struct py_ssl_library_code library_codes[] = {")
71    for mnemo, (libcode, _, _) in sorted(error_libraries.items()):
72        w('    {"%s", %s},' % (mnemo, libcode))
73    w('    { NULL }')
74    w('};')
75    w("")
76
77    w("static struct py_ssl_error_code error_codes[] = {")
78    for errcode, (libcode, name, num) in sorted(codes.items()):
79        w('  #ifdef %s' % (errcode))
80        w('    {"%s", %s, %s},' % (name, libcode, errcode))
81        w('  #else')
82        w('    {"%s", %s, %d},' % (name, libcode, num))
83        w('  #endif')
84    w('    { NULL }')
85    w('};')
86    if not use_stdout:
87        f.close()
88