mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
168 lines
5.1 KiB
Python
Executable File
168 lines
5.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
"""
|
|
Generate Rust SSL error mapping code from OpenSSL sources.
|
|
|
|
This is based on CPython's Tools/ssl/make_ssl_data.py but generates
|
|
Rust code instead of C headers.
|
|
|
|
It takes two arguments:
|
|
- the path to the OpenSSL source tree (e.g. git checkout)
|
|
- the path to the Rust file to be generated (e.g. stdlib/src/ssl/ssl_data.rs)
|
|
- error codes are version specific
|
|
"""
|
|
|
|
import argparse
|
|
import datetime
|
|
import operator
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description="Generate ssl_data.rs from OpenSSL sources"
|
|
)
|
|
parser.add_argument("srcdir", help="OpenSSL source directory")
|
|
parser.add_argument("output", nargs="?", default=None)
|
|
|
|
|
|
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_rust(args):
|
|
"""Generate Rust phf map for library codes"""
|
|
yield "// Maps lib_code -> library name"
|
|
yield '// Example: 20 -> "SSL"'
|
|
yield "pub static LIBRARY_CODES: phf::Map<u32, &'static str> = phf_map! {"
|
|
|
|
# Deduplicate: keep the last one if there are duplicates
|
|
seen = {}
|
|
for libname in sorted(args.lib2errnum):
|
|
lib_num = args.lib2errnum[libname]
|
|
seen[lib_num] = libname
|
|
|
|
for lib_num in sorted(seen.keys()):
|
|
libname = seen[lib_num]
|
|
yield f' {lib_num}u32 => "{libname}",'
|
|
yield "};"
|
|
yield ""
|
|
|
|
|
|
def gen_error_codes_rust(args):
|
|
"""Generate Rust phf map for error codes"""
|
|
yield "// Maps encoded (lib, reason) -> error mnemonic"
|
|
yield '// Example: encode_error_key(20, 134) -> "CERTIFICATE_VERIFY_FAILED"'
|
|
yield "// Key encoding: (lib << 32) | reason"
|
|
yield "pub static ERROR_CODES: phf::Map<u64, &'static str> = phf_map! {"
|
|
for reason, libname, errname, num in args.reasons:
|
|
if libname not in args.lib2errnum:
|
|
continue
|
|
lib_num = args.lib2errnum[libname]
|
|
# Encode (lib, reason) as single u64
|
|
key = (lib_num << 32) | num
|
|
yield f' {key}u64 => "{errname}",'
|
|
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/make_ssl_data_rs.py",
|
|
f"// Generated on {datetime.datetime.now(datetime.timezone.utc).isoformat()}",
|
|
f"// Source: OpenSSL from {args.srcdir}",
|
|
"// spell-checker: disable",
|
|
"",
|
|
"use phf::phf_map;",
|
|
"",
|
|
]
|
|
lines.extend(gen_library_codes_rust(args))
|
|
lines.extend(gen_error_codes_rust(args))
|
|
|
|
# Add helper function
|
|
lines.extend(
|
|
[
|
|
"/// Helper function to create encoded key from (lib, reason) pair",
|
|
"#[inline]",
|
|
"pub fn encode_error_key(lib: i32, reason: i32) -> u64 {",
|
|
" ((lib as u64) << 32) | (reason as u64 & 0xFFFFFFFF)",
|
|
"}",
|
|
"",
|
|
]
|
|
)
|
|
|
|
if args.output is None:
|
|
for line in lines:
|
|
print(line)
|
|
else:
|
|
with open(args.output, "w") as output:
|
|
for line in lines:
|
|
print(line, file=output)
|
|
|
|
print(f"Generated {args.output}")
|
|
print(f"Found {len(args.lib2errnum)} library codes")
|
|
print(f"Found {len(args.reasons)} error codes")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|