Index: Modules/_hashopenssl.c =================================================================== --- Modules/_hashopenssl.c (revision 82445) +++ Modules/_hashopenssl.c (working copy) @@ -16,6 +16,9 @@ #include "Python.h" #include "structmember.h" +/* Provide the user with a way of finding out why a name isn't available */ +static PyObject *_errors_dict; + #ifdef WITH_THREAD #include "pythread.h" #define ENTER_HASHLIB(obj) \ @@ -36,6 +39,8 @@ #endif /* EVP is the preferred interface to hashing in OpenSSL */ +#include "openssl/ssl.h" +#include "openssl/err.h" #include #define MUNCH_SIZE INT_MAX @@ -525,11 +530,65 @@ } /* used in the init function to setup a constructor */ -#define INIT_CONSTRUCTOR_CONSTANTS(NAME) do { \ +static void +hash_init_failure(PyObject *name) +{ + char buf[2048]; + char *errstr; + PyObject *error_str; + + errstr = ERR_error_string(ERR_peek_last_error(), NULL); + PyOS_snprintf(buf, sizeof(buf), "_hashopenssl.c:%d: %s", __LINE__, errstr); + ERR_clear_error(); + + error_str = PyString_FromString(buf); + if (!error_str) + return; + + PyDict_SetItem(_errors_dict, + name, + error_str); +} + +/* Remove a PyMethodDef from an arrary of them: */ +static int +delete_method_by_name(struct PyMethodDef *ml, const char *name) +{ + assert(ml); + assert(name); + + /* Find the def: */ + while (ml->ml_name && strcmp(ml->ml_name, name)) { + ml++; + } + + if (!ml->ml_name) { + /* Not found */ + return -1; + } + + /* Delete this PyMethodDef by overwriting with the next defs until you + reach the sentinel value: */ + *ml = ml[1]; + while (ml->ml_name) { + ml++; + *ml = ml[1]; + } + + /* Success: */ + return 0; +} + +#define INIT_CONSTRUCTOR_CONSTANTS(NAME) do { \ CONST_ ## NAME ## _name_obj = PyString_FromString(#NAME); \ if (EVP_get_digestbyname(#NAME)) { \ CONST_new_ ## NAME ## _ctx_p = &CONST_new_ ## NAME ## _ctx; \ - EVP_DigestInit(CONST_new_ ## NAME ## _ctx_p, EVP_get_digestbyname(#NAME)); \ + if (!EVP_DigestInit(CONST_new_ ## NAME ## _ctx_p, EVP_get_digestbyname(#NAME))) { \ + /* Failure: */ \ + hash_init_failure(CONST_ ## NAME ## _name_obj); \ + CONST_new_ ## NAME ## _ctx_p = NULL; \ + delete_method_by_name(EVP_functions, "openssl_" #NAME); \ + } \ } \ } while (0); @@ -565,13 +624,31 @@ { PyObject *m; + _errors_dict = PyDict_New(); + if (!_errors_dict) + return; + + SSL_load_error_strings(); + SSL_library_init(); OpenSSL_add_all_digests(); - /* TODO build EVP_functions openssl_* entries dynamically based - * on what hashes are supported rather than listing many - * but having some be unsupported. Only init appropriate - * constants. */ + /* + Figure out what hash functions are available, setting up the convenience + constructors, and removing those that aren't available from + the EVP_functions array. + Note: each method's generated PyCFunctionObject stores a copy of the + PyMethodDef*, so it's easiest to keep this as global data. + */ + INIT_CONSTRUCTOR_CONSTANTS(md5); + INIT_CONSTRUCTOR_CONSTANTS(sha1); +#ifdef _OPENSSL_SUPPORTS_SHA2 + INIT_CONSTRUCTOR_CONSTANTS(sha224); + INIT_CONSTRUCTOR_CONSTANTS(sha256); + INIT_CONSTRUCTOR_CONSTANTS(sha384); + INIT_CONSTRUCTOR_CONSTANTS(sha512); +#endif + Py_TYPE(&EVPtype) = &PyType_Type; if (PyType_Ready(&EVPtype) < 0) return; @@ -580,18 +657,11 @@ if (m == NULL) return; + PyModule_AddObject(m, "_errors", _errors_dict); + #if HASH_OBJ_CONSTRUCTOR Py_INCREF(&EVPtype); PyModule_AddObject(m, "HASH", (PyObject *)&EVPtype); #endif - /* these constants are used by the convenience constructors */ - INIT_CONSTRUCTOR_CONSTANTS(md5); - INIT_CONSTRUCTOR_CONSTANTS(sha1); -#ifdef _OPENSSL_SUPPORTS_SHA2 - INIT_CONSTRUCTOR_CONSTANTS(sha224); - INIT_CONSTRUCTOR_CONSTANTS(sha256); - INIT_CONSTRUCTOR_CONSTANTS(sha384); - INIT_CONSTRUCTOR_CONSTANTS(sha512); -#endif }