From a5d2b4cb180ec87d006d63f838860fba785bcad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lemburg?= Date: Sat, 16 Feb 2002 18:23:30 +0000 Subject: Break SSL support out of _socket module and place it into a new helper module _ssl. The support for the RAND_* APIs in _ssl is now only enabled for OpenSSL 0.9.5 and up since they were added in that release. Note that socketmodule.* should really be renamed to _socket.* -- unfortunately, this seems to lose the CVS history of the file. Please review and test... I was only able to test the header file chaos in socketmodule.c/h on Linux. The test run through fine and compiles don't give errors or warnings. WARNING: This patch does *not* include changes to the various non-Unix build process files. --- Lib/socket.py | 6 +- Misc/NEWS | 4 + Modules/Setup.dist | 6 +- Modules/_ssl.c | 461 +++++++++++++++++++++++++++++++++++ Modules/socketmodule.c | 647 +++++++------------------------------------------ Modules/socketmodule.h | 159 ++++++++++++ setup.py | 45 ++-- 7 files changed, 749 insertions(+), 579 deletions(-) create mode 100644 Modules/_ssl.c create mode 100644 Modules/socketmodule.h diff --git a/Lib/socket.py b/Lib/socket.py index d2ac593..c460c5f 100644 --- a/Lib/socket.py +++ b/Lib/socket.py @@ -39,6 +39,10 @@ the setsockopt() and getsockopt() methods. """ from _socket import * +try: + from _ssl import * +except ImportError: + pass import os, sys @@ -56,7 +60,7 @@ if (sys.platform.lower().startswith("win") return _socketobject(_realsocketcall(family, type, proto)) try: - _realsslcall = _socket.ssl + _realsslcall = _ssl.ssl except AttributeError: pass # No ssl else: diff --git a/Misc/NEWS b/Misc/NEWS index 72dd792..f6e6b77 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -21,6 +21,10 @@ Extension modules Library +- socket module: the SSL support was broken out of the main + _socket module C helper and placed into a new _ssl helper + which now gets imported by socket.py if available and working. + - encodings package: added aliases for all supported IANA character sets diff --git a/Modules/Setup.dist b/Modules/Setup.dist index f54b13b..df8176b 100644 --- a/Modules/Setup.dist +++ b/Modules/Setup.dist @@ -175,13 +175,13 @@ GLHACK=-Dclear=__GLclear # Dynamic readlines #xreadlines xreadlinesmodule.c -# for socket(2), without SSL support. +# Socket module helper for socket(2) #_socket socketmodule.c -# Socket module compiled with SSL support; you must comment out the other +# Socket module helper for SSL support; you must comment out the other # socket line above, and possibly edit the SSL variable: #SSL=/usr/local/ssl -#_socket socketmodule.c \ +#_ssl _ssl.c \ # -DUSE_SSL -I$(SSL)/include -I$(SSL)/include/openssl \ # -L$(SSL)/lib -lssl -lcrypto diff --git a/Modules/_ssl.c b/Modules/_ssl.c new file mode 100644 index 0000000..17ca3b0 --- /dev/null +++ b/Modules/_ssl.c @@ -0,0 +1,461 @@ +/* SSL socket module + + SSL support based on patches by Brian E Gallew and Laszlo Kovacs. + + This module is imported by socket.py. It should *not* be used + directly. + +*/ + +#include "Python.h" + +/* Include symbols from _socket module */ +#include "socketmodule.h" + +/* Include OpenSSL header files */ +#include "openssl/rsa.h" +#include "openssl/crypto.h" +#include "openssl/x509.h" +#include "openssl/pem.h" +#include "openssl/ssl.h" +#include "openssl/err.h" +#include "openssl/rand.h" + +/* SSL error object */ +static PyObject *PySSLErrorObject; + +/* SSL socket object */ + +#define X509_NAME_MAXLEN 256 + +/* RAND_* APIs got added to OpenSSL in 0.9.5 */ +#if OPENSSL_VERSION_NUMBER >= 0x0090500fL +# define HAVE_OPENSSL_RAND 1 +#else +# undef HAVE_OPENSSL_RAND +#endif + +typedef struct { + PyObject_HEAD + PySocketSockObject *Socket; /* Socket on which we're layered */ + SSL_CTX* ctx; + SSL* ssl; + X509* server_cert; + BIO* sbio; + char server[X509_NAME_MAXLEN]; + char issuer[X509_NAME_MAXLEN]; + +} PySSLObject; + +staticforward PyTypeObject PySSL_Type; +staticforward PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args); +staticforward PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args); + +#define PySSLObject_Check(v) ((v)->ob_type == &PySSL_Type) + +/* XXX It might be helpful to augment the error message generated + below with the name of the SSL function that generated the error. + I expect it's obvious most of the time. +*/ + +static PyObject * +PySSL_SetError(PySSLObject *obj, int ret) +{ + PyObject *v, *n, *s; + char *errstr; + int err; + + assert(ret <= 0); + + err = SSL_get_error(obj->ssl, ret); + n = PyInt_FromLong(err); + if (n == NULL) + return NULL; + v = PyTuple_New(2); + if (v == NULL) { + Py_DECREF(n); + return NULL; + } + + switch (SSL_get_error(obj->ssl, ret)) { + case SSL_ERROR_ZERO_RETURN: + errstr = "TLS/SSL connection has been closed"; + break; + case SSL_ERROR_WANT_READ: + errstr = "The operation did not complete (read)"; + break; + case SSL_ERROR_WANT_WRITE: + errstr = "The operation did not complete (write)"; + break; + case SSL_ERROR_WANT_X509_LOOKUP: + errstr = "The operation did not complete (X509 lookup)"; + break; + case SSL_ERROR_SYSCALL: + case SSL_ERROR_SSL: + { + unsigned long e = ERR_get_error(); + if (e == 0) { + /* an EOF was observed that violates the protocol */ + errstr = "EOF occurred in violation of protocol"; + } else if (e == -1) { + /* the underlying BIO reported an I/O error */ + Py_DECREF(v); + Py_DECREF(n); + return obj->Socket->errorhandler(); + } else { + /* XXX Protected by global interpreter lock */ + errstr = ERR_error_string(e, NULL); + } + break; + } + default: + errstr = "Invalid error code"; + } + s = PyString_FromString(errstr); + if (s == NULL) { + Py_DECREF(v); + Py_DECREF(n); + } + PyTuple_SET_ITEM(v, 0, n); + PyTuple_SET_ITEM(v, 1, s); + PyErr_SetObject(PySSLErrorObject, v); + return NULL; +} + +/* This is a C function to be called for new object initialization */ +static PySSLObject * +newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file) +{ + PySSLObject *self; + char *errstr = NULL; + int ret; + + self = PyObject_New(PySSLObject, &PySSL_Type); /* Create new object */ + if (self == NULL){ + errstr = "newPySSLObject error"; + goto fail; + } + memset(self->server, '\0', sizeof(char) * X509_NAME_MAXLEN); + memset(self->issuer, '\0', sizeof(char) * X509_NAME_MAXLEN); + self->server_cert = NULL; + self->ssl = NULL; + self->ctx = NULL; + self->Socket = NULL; + + if ((key_file && !cert_file) || (!key_file && cert_file)) { + errstr = "Both the key & certificate files must be specified"; + goto fail; + } + + self->ctx = SSL_CTX_new(SSLv23_method()); /* Set up context */ + if (self->ctx == NULL) { + errstr = "SSL_CTX_new error"; + goto fail; + } + + if (key_file) { + if (SSL_CTX_use_PrivateKey_file(self->ctx, key_file, + SSL_FILETYPE_PEM) < 1) { + errstr = "SSL_CTX_use_PrivateKey_file error"; + goto fail; + } + + if (SSL_CTX_use_certificate_chain_file(self->ctx, + cert_file) < 1) { + errstr = "SSL_CTX_use_certificate_chain_file error"; + goto fail; + } + } + + SSL_CTX_set_verify(self->ctx, + SSL_VERIFY_NONE, NULL); /* set verify lvl */ + self->ssl = SSL_new(self->ctx); /* New ssl struct */ + SSL_set_fd(self->ssl, Sock->sock_fd); /* Set the socket for SSL */ + SSL_set_connect_state(self->ssl); + + /* Actually negotiate SSL connection */ + /* XXX If SSL_connect() returns 0, it's also a failure. */ + ret = SSL_connect(self->ssl); + if (ret <= 0) { + PySSL_SetError(self, ret); + goto fail; + } + self->ssl->debug = 1; + + if ((self->server_cert = SSL_get_peer_certificate(self->ssl))) { + X509_NAME_oneline(X509_get_subject_name(self->server_cert), + self->server, X509_NAME_MAXLEN); + X509_NAME_oneline(X509_get_issuer_name(self->server_cert), + self->issuer, X509_NAME_MAXLEN); + } + self->Socket = Sock; + Py_INCREF(self->Socket); + return self; + fail: + if (errstr) + PyErr_SetString(PySSLErrorObject, errstr); + Py_DECREF(self); + return NULL; +} + +/* This is the Python function called for new object initialization */ +static PyObject * +PySocket_ssl(PyObject *self, PyObject *args) +{ + PySSLObject *rv; + PySocketSockObject *Sock; + char *key_file = NULL; + char *cert_file = NULL; + + if (!PyArg_ParseTuple(args, "O!|zz:ssl", + PySocketModule.Sock_Type, + (PyObject*)&Sock, + &key_file, &cert_file)) + return NULL; + + rv = newPySSLObject(Sock, key_file, cert_file); + if (rv == NULL) + return NULL; + return (PyObject *)rv; +} + +static char ssl_doc[] = +"ssl(socket, [keyfile, certfile]) -> sslobject"; + +/* SSL object methods */ + +static PyObject * +PySSL_server(PySSLObject *self) +{ + return PyString_FromString(self->server); +} + +static PyObject * +PySSL_issuer(PySSLObject *self) +{ + return PyString_FromString(self->issuer); +} + + +static void PySSL_dealloc(PySSLObject *self) +{ + if (self->server_cert) /* Possible not to have one? */ + X509_free (self->server_cert); + if (self->ssl) + SSL_free(self->ssl); + if (self->ctx) + SSL_CTX_free(self->ctx); + Py_XDECREF(self->Socket); + PyObject_Del(self); +} + +static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args) +{ + char *data; + int len; + + if (!PyArg_ParseTuple(args, "s#:write", &data, &len)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + len = SSL_write(self->ssl, data, len); + Py_END_ALLOW_THREADS + if (len > 0) + return PyInt_FromLong(len); + else + return PySSL_SetError(self, len); +} + +static char PySSL_SSLwrite_doc[] = +"write(s) -> len\n\ +\n\ +Writes the string s into the SSL object. Returns the number\n\ +of bytes written."; + +static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args) +{ + PyObject *buf; + int count = 0; + int len = 1024; + + if (!PyArg_ParseTuple(args, "|i:read", &len)) + return NULL; + + if (!(buf = PyString_FromStringAndSize((char *) 0, len))) + return NULL; + + Py_BEGIN_ALLOW_THREADS + count = SSL_read(self->ssl, PyString_AsString(buf), len); + Py_END_ALLOW_THREADS + if (count <= 0) { + Py_DECREF(buf); + return PySSL_SetError(self, count); + } + if (count != len && _PyString_Resize(&buf, count) < 0) + return NULL; + return buf; +} + +static char PySSL_SSLread_doc[] = +"read([len]) -> string\n\ +\n\ +Read up to len bytes from the SSL socket."; + +static PyMethodDef PySSLMethods[] = { + {"write", (PyCFunction)PySSL_SSLwrite, METH_VARARGS, + PySSL_SSLwrite_doc}, + {"read", (PyCFunction)PySSL_SSLread, METH_VARARGS, + PySSL_SSLread_doc}, + {"server", (PyCFunction)PySSL_server, METH_NOARGS}, + {"issuer", (PyCFunction)PySSL_issuer, METH_NOARGS}, + {NULL, NULL} +}; + +static PyObject *PySSL_getattr(PySSLObject *self, char *name) +{ + return Py_FindMethod(PySSLMethods, (PyObject *)self, name); +} + +staticforward PyTypeObject PySSL_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "socket.SSL", /*tp_name*/ + sizeof(PySSLObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)PySSL_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)PySSL_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ +}; + +#ifdef HAVE_OPENSSL_RAND + +/* helper routines for seeding the SSL PRNG */ +static PyObject * +PySSL_RAND_add(PyObject *self, PyObject *args) +{ + char *buf; + int len; + double entropy; + + if (!PyArg_ParseTuple(args, "s#d:RAND_add", &buf, &len, &entropy)) + return NULL; + RAND_add(buf, len, entropy); + Py_INCREF(Py_None); + return Py_None; +} + +static char PySSL_RAND_add_doc[] = +"RAND_add(string, entropy)\n\ +\n\ +Mix string into the OpenSSL PRNG state. entropy (a float) is a lower\n\ +bound on the entropy contained in string."; + +static PyObject * +PySSL_RAND_status(PyObject *self) +{ + return PyInt_FromLong(RAND_status()); +} + +static char PySSL_RAND_status_doc[] = +"RAND_status() -> 0 or 1\n\ +\n\ +Returns 1 if the OpenSSL PRNG has been seeded with enough data and 0 if not.\n\ +It is necessary to seed the PRNG with RAND_add() on some platforms before\n\ +using the ssl() function."; + +static PyObject * +PySSL_RAND_egd(PyObject *self, PyObject *arg) +{ + int bytes; + + if (!PyString_Check(arg)) + return PyErr_Format(PyExc_TypeError, + "RAND_egd() expected string, found %s", + arg->ob_type->tp_name); + bytes = RAND_egd(PyString_AS_STRING(arg)); + if (bytes == -1) { + PyErr_SetString(PySSLErrorObject, + "EGD connection failed or EGD did not return " + "enough data to seed the PRNG"); + return NULL; + } + return PyInt_FromLong(bytes); +} + +static char PySSL_RAND_egd_doc[] = +"RAND_egd(path) -> bytes\n\ +\n\ +Queries the entropy gather daemon (EGD) on socket path. Returns number\n\ +of bytes read. Raises socket.sslerror if connection to EGD fails or\n\ +if it does provide enough data to seed PRNG."; + +#endif + +/* List of functions exported by this module. */ + +static PyMethodDef PySSL_methods[] = { + {"ssl", PySocket_ssl, + METH_VARARGS, ssl_doc}, +#ifdef HAVE_OPENSSL_RAND + {"RAND_add", PySSL_RAND_add, METH_VARARGS, + PySSL_RAND_add_doc}, + {"RAND_egd", PySSL_RAND_egd, METH_O, + PySSL_RAND_egd_doc}, + {"RAND_status", (PyCFunction)PySSL_RAND_status, METH_NOARGS, + PySSL_RAND_status_doc}, +#endif + {NULL, NULL} /* Sentinel */ +}; + + +static char module_doc[] = +"Implementation module for SSL socket operations. See the socket module\n\ +for documentation."; + +DL_EXPORT(void) +init_ssl(void) +{ + PyObject *m, *d; + + PySSL_Type.ob_type = &PyType_Type; + + m = Py_InitModule3("_ssl", PySSL_methods, module_doc); + d = PyModule_GetDict(m); + + /* Load _socket module and its C API */ + if (PySocketModule_ImportModuleAndAPI()) + return; + + /* Init OpenSSL */ + SSL_load_error_strings(); + SSLeay_add_ssl_algorithms(); + + /* Add symbols to module dict */ + PySSLErrorObject = PyErr_NewException("socket.sslerror", NULL, NULL); + if (PySSLErrorObject == NULL) + return; + PyDict_SetItemString(d, "sslerror", PySSLErrorObject); + if (PyDict_SetItemString(d, "SSLType", + (PyObject *)&PySSL_Type) != 0) + return; + PyModule_AddIntConstant(m, "SSL_ERROR_ZERO_RETURN", + SSL_ERROR_ZERO_RETURN); + PyModule_AddIntConstant(m, "SSL_ERROR_WANT_READ", + SSL_ERROR_WANT_READ); + PyModule_AddIntConstant(m, "SSL_ERROR_WANT_WRITE", + SSL_ERROR_WANT_WRITE); + PyModule_AddIntConstant(m, "SSL_ERROR_WANT_X509_LOOKUP", + SSL_ERROR_WANT_X509_LOOKUP); + PyModule_AddIntConstant(m, "SSL_ERROR_SYSCALL", + SSL_ERROR_SYSCALL); + PyModule_AddIntConstant(m, "SSL_ERROR_SSL", + SSL_ERROR_SSL); +} diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 031d60f..fb7b046 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -1,7 +1,5 @@ /* Socket module */ -/* SSL support based on patches by Brian E Gallew and Laszlo Kovacs */ - /* This module provides an interface to Berkeley socket IPC. @@ -35,7 +33,6 @@ Module interface: - socket.AF_INET, socket.SOCK_STREAM, etc.: constants from - socket.inet_aton(IP address) -> 32-bit packed IP representation - socket.inet_ntoa(packed IP) -> IP address string -- socket.ssl(socket, keyfile, certfile) -> new ssl object - an Internet socket address is a pair (hostname, port) where hostname can be anything recognized by gethostbyname() (including the dd.dd.dd.dd notation) and port is in host byte order @@ -86,128 +83,111 @@ Socket methods: computed by the configure script are needed! */ #ifndef linux -#undef HAVE_GETHOSTBYNAME_R_3_ARG -#undef HAVE_GETHOSTBYNAME_R_5_ARG -#undef HAVE_GETHOSTBYNAME_R_6_ARG +# undef HAVE_GETHOSTBYNAME_R_3_ARG +# undef HAVE_GETHOSTBYNAME_R_5_ARG +# undef HAVE_GETHOSTBYNAME_R_6_ARG #endif #ifndef WITH_THREAD -#undef HAVE_GETHOSTBYNAME_R +# undef HAVE_GETHOSTBYNAME_R #endif #ifdef HAVE_GETHOSTBYNAME_R -#if defined(_AIX) || defined(__osf__) -#define HAVE_GETHOSTBYNAME_R_3_ARG -#elif defined(__sun) || defined(__sgi) -#define HAVE_GETHOSTBYNAME_R_5_ARG -#elif defined(linux) +# if defined(_AIX) || defined(__osf__) +# define HAVE_GETHOSTBYNAME_R_3_ARG +# elif defined(__sun) || defined(__sgi) +# define HAVE_GETHOSTBYNAME_R_5_ARG +# elif defined(linux) /* Rely on the configure script */ -#else -#undef HAVE_GETHOSTBYNAME_R -#endif +# else +# undef HAVE_GETHOSTBYNAME_R +# endif #endif #if !defined(HAVE_GETHOSTBYNAME_R) && defined(WITH_THREAD) && !defined(MS_WINDOWS) -#define USE_GETHOSTBYNAME_LOCK +# define USE_GETHOSTBYNAME_LOCK #endif #ifdef USE_GETHOSTBYNAME_LOCK -#include "pythread.h" +# include "pythread.h" #endif #if defined(PYCC_VACPP) -#include -#include -#include -#include -#include +# include +# include +# include +# include +# include #endif #if defined(PYOS_OS2) -#define INCL_DOS -#define INCL_DOSERRORS -#define INCL_NOPMAPI -#include +# define INCL_DOS +# define INCL_DOSERRORS +# define INCL_NOPMAPI +# include #endif - +/* Generic includes */ #include - #include + +/* Generic _socket.h definitions and includes */ +#define PySocket_BUILDING_SOCKET +#include "_socket.h" + +/* Addressing includes */ + #ifndef MS_WINDOWS -#include -#include -#include -#if !(defined(__BEOS__) || defined(__CYGWIN__) || (defined(PYOS_OS2) && defined(PYCC_VACPP))) -#include -#endif + +/* Non-MS WINDOWS includes */ +# include /* Headers needed for inet_ntoa() and inet_addr() */ -#ifdef __BEOS__ -#include -#elif defined(PYOS_OS2) && defined(PYCC_VACPP) -#include +# ifdef __BEOS__ +# include +# elif defined(PYOS_OS2) && defined(PYCC_VACPP) +# include typedef size_t socklen_t; -#else -#ifndef USE_GUSI1 -#include -#endif -#endif - -#ifndef RISCOS -#include -#else -#include -#define NO_DUP +# else +# ifndef USE_GUSI1 +# include +# endif +# endif + +# ifndef RISCOS +# include +# else +# include +# define NO_DUP int h_errno; /* not used */ -#endif -#else -#include -#include -#endif +# endif - -#ifdef HAVE_SYS_UN_H -#include #else -#undef AF_UNIX -#endif -#ifdef HAVE_NETPACKET_PACKET_H -#include -#include -#include +/* MS_WINDOWS includes */ +# include + #endif #ifdef HAVE_STDDEF_H -#include +# include #endif #ifndef offsetof -#define offsetof(type, member) ((size_t)(&((type *)0)->member)) +# define offsetof(type, member) ((size_t)(&((type *)0)->member)) #endif #ifndef O_NDELAY -#define O_NDELAY O_NONBLOCK /* For QNX only? */ +# define O_NDELAY O_NONBLOCK /* For QNX only? */ #endif #ifdef USE_GUSI1 /* fdopen() isn't declared in stdio.h (sigh) */ -#include +# include #endif #include "addrinfo.h" -#ifdef USE_SSL -#include "openssl/rsa.h" -#include "openssl/crypto.h" -#include "openssl/x509.h" -#include "openssl/pem.h" -#include "openssl/ssl.h" -#include "openssl/err.h" -#include "openssl/rand.h" -#endif /* USE_SSL */ - #ifndef HAVE_INET_PTON int inet_pton (int af, const char *src, void *dst); const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); @@ -242,19 +222,6 @@ const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); #define NO_DUP /* Actually it exists on NT 3.5, but what the heck... */ #endif -/* abstract the socket file descriptor type */ -#ifdef MS_WINDOWS -typedef SOCKET SOCKET_T; -# ifdef MS_WIN64 -# define SIZEOF_SOCKET_T 8 -# else -# define SIZEOF_SOCKET_T 4 -# endif -#else -typedef int SOCKET_T; -# define SIZEOF_SOCKET_T SIZEOF_INT -#endif - #ifdef MS_WIN32 # define EAFNOSUPPORT WSAEAFNOSUPPORT # define snprintf _snprintf @@ -269,7 +236,6 @@ typedef int SOCKET_T; #define SOCKETCLOSE close #endif - /* XXX There's a problem here: *static* functions are not supposed to have a Py prefix (or use CapitalizedWords). Later... */ @@ -280,11 +246,6 @@ static PyObject *PySocket_Error; static PyObject *PyH_Error; static PyObject *PyGAI_Error; -#ifdef USE_SSL -static PyObject *PySSLErrorObject; -#endif /* USE_SSL */ - - #ifdef RISCOS /* Global variable which is !=0 if Python is running in a RISC OS taskwindow */ static int taskwindow; @@ -459,64 +420,6 @@ PyGAI_Err(int error) return NULL; } - -/* The object holding a socket. It holds some extra information, - like the address family, which is used to decode socket address - arguments properly. */ - -typedef struct { - PyObject_HEAD - SOCKET_T sock_fd; /* Socket file descriptor */ - int sock_family; /* Address family, e.g., AF_INET */ - int sock_type; /* Socket type, e.g., SOCK_STREAM */ - int sock_proto; /* Protocol type, usually 0 */ - union sock_addr { - struct sockaddr_in in; -#ifdef AF_UNIX - struct sockaddr_un un; -#endif -#ifdef ENABLE_IPV6 - struct sockaddr_in6 in6; - struct sockaddr_storage storage; -#endif -#ifdef HAVE_NETPACKET_PACKET_H - struct sockaddr_ll ll; -#endif - } sock_addr; -} PySocketSockObject; - -#ifdef USE_SSL - -#define X509_NAME_MAXLEN 256 - -typedef struct { - PyObject_HEAD - PySocketSockObject *Socket; /* Socket on which we're layered */ - SSL_CTX* ctx; - SSL* ssl; - X509* server_cert; - BIO* sbio; - char server[X509_NAME_MAXLEN]; - char issuer[X509_NAME_MAXLEN]; - -} PySSLObject; - -staticforward PyTypeObject PySSL_Type; -staticforward PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args); -staticforward PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args); - -#define PySSLObject_Check(v) ((v)->ob_type == &PySSL_Type) - -#endif /* USE_SSL */ - -/* A forward reference to the Socktype type object. - The Socktype variable contains pointers to various functions, - some of which call newsockobject(), which uses Socktype, so - there has to be a circular reference. */ - -staticforward PyTypeObject PySocketSock_Type; - - /* Initialize a new socket object. */ static void @@ -530,6 +433,7 @@ init_sockobject(PySocketSockObject *s, s->sock_family = family; s->sock_type = type; s->sock_proto = proto; + s->errorhandler = &PySocket_Err; #ifdef RISCOS if(taskwindow) { socketioctl(s->sock_fd, 0x80046679, (u_long*)&block); @@ -867,7 +771,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, strncpy(ifr.ifr_name, interfaceName, sizeof(ifr.ifr_name)); ifr.ifr_name[(sizeof(ifr.ifr_name))-1] = '\0'; if (ioctl(s->sock_fd, SIOCGIFINDEX, &ifr) < 0) { - PySocket_Err(); + s->errorhandler(); return 0; } addr = &(s->sock_addr.ll); @@ -964,7 +868,7 @@ PySocketSock_accept(PySocketSockObject *s) #else if (newfd < 0) #endif - return PySocket_Err(); + return s->errorhandler(); /* Create the new object with unspecified family, to avoid calls to bind() etc. on it. */ @@ -1101,7 +1005,7 @@ PySocketSock_setsockopt(PySocketSockObject *s, PyObject *args) } res = setsockopt(s->sock_fd, level, optname, (void *)buf, buflen); if (res < 0) - return PySocket_Err(); + return s->errorhandler(); Py_INCREF(Py_None); return Py_None; } @@ -1143,7 +1047,7 @@ PySocketSock_getsockopt(PySocketSockObject *s, PyObject *args) res = getsockopt(s->sock_fd, level, optname, (void *)&flag, &flagsize); if (res < 0) - return PySocket_Err(); + return s->errorhandler(); return PyInt_FromLong(flag); } if (buflen <= 0 || buflen > 1024) { @@ -1158,7 +1062,7 @@ PySocketSock_getsockopt(PySocketSockObject *s, PyObject *args) (void *)PyString_AS_STRING(buf), &buflen); if (res < 0) { Py_DECREF(buf); - return PySocket_Err(); + return s->errorhandler(); } _PyString_Resize(&buf, buflen); return buf; @@ -1188,7 +1092,7 @@ PySocketSock_bind(PySocketSockObject *s, PyObject *addro) res = bind(s->sock_fd, addr, addrlen); Py_END_ALLOW_THREADS if (res < 0) - return PySocket_Err(); + return s->errorhandler(); Py_INCREF(Py_None); return Py_None; } @@ -1241,7 +1145,7 @@ PySocketSock_connect(PySocketSockObject *s, PyObject *addro) res = connect(s->sock_fd, addr, addrlen); Py_END_ALLOW_THREADS if (res < 0) - return PySocket_Err(); + return s->errorhandler(); Py_INCREF(Py_None); return Py_None; } @@ -1313,7 +1217,7 @@ PySocketSock_dup(PySocketSockObject *s) newfd = dup(s->sock_fd); if (newfd < 0) - return PySocket_Err(); + return s->errorhandler(); sock = (PyObject *) PySocketSock_New(newfd, s->sock_family, s->sock_type, @@ -1347,7 +1251,7 @@ PySocketSock_getsockname(PySocketSockObject *s) res = getsockname(s->sock_fd, (struct sockaddr *) addrbuf, &addrlen); Py_END_ALLOW_THREADS if (res < 0) - return PySocket_Err(); + return s->errorhandler(); return makesockaddr(s->sock_fd, (struct sockaddr *) addrbuf, addrlen); } @@ -1375,7 +1279,7 @@ PySocketSock_getpeername(PySocketSockObject *s) res = getpeername(s->sock_fd, (struct sockaddr *) addrbuf, &addrlen); Py_END_ALLOW_THREADS if (res < 0) - return PySocket_Err(); + return s->errorhandler(); return makesockaddr(s->sock_fd, (struct sockaddr *) addrbuf, addrlen); } @@ -1405,7 +1309,7 @@ PySocketSock_listen(PySocketSockObject *s, PyObject *arg) res = listen(s->sock_fd, backlog); Py_END_ALLOW_THREADS if (res < 0) - return PySocket_Err(); + return s->errorhandler(); Py_INCREF(Py_None); return Py_None; } @@ -1451,7 +1355,7 @@ PySocketSock_makefile(PySocketSockObject *s, PyObject *args) { if (fd >= 0) SOCKETCLOSE(fd); - return PySocket_Err(); + return s->errorhandler(); } f = PyFile_FromFile(fp, "", mode, fclose); if (f != NULL) @@ -1490,7 +1394,7 @@ PySocketSock_recv(PySocketSockObject *s, PyObject *args) Py_END_ALLOW_THREADS if (n < 0) { Py_DECREF(buf); - return PySocket_Err(); + return s->errorhandler(); } if (n != len && _PyString_Resize(&buf, n) < 0) return NULL; @@ -1541,7 +1445,7 @@ PySocketSock_recvfrom(PySocketSockObject *s, PyObject *args) Py_END_ALLOW_THREADS if (n < 0) { Py_DECREF(buf); - return PySocket_Err(); + return s->errorhandler(); } if (n != len && _PyString_Resize(&buf, n) < 0) return NULL; @@ -1575,7 +1479,7 @@ PySocketSock_send(PySocketSockObject *s, PyObject *args) n = send(s->sock_fd, buf, len, flags); Py_END_ALLOW_THREADS if (n < 0) - return PySocket_Err(); + return s->errorhandler(); return PyInt_FromLong((long)n); } @@ -1607,7 +1511,7 @@ PySocketSock_sendall(PySocketSockObject *s, PyObject *args) } while (len > 0); Py_END_ALLOW_THREADS if (n < 0) - return PySocket_Err(); + return s->errorhandler(); Py_INCREF(Py_None); return Py_None; } @@ -1643,7 +1547,7 @@ PySocketSock_sendto(PySocketSockObject *s, PyObject *args) n = sendto(s->sock_fd, buf, len, flags, addr, addrlen); Py_END_ALLOW_THREADS if (n < 0) - return PySocket_Err(); + return s->errorhandler(); return PyInt_FromLong((long)n); } @@ -1669,7 +1573,7 @@ PySocketSock_shutdown(PySocketSockObject *s, PyObject *arg) res = shutdown(s->sock_fd, how); Py_END_ALLOW_THREADS if (res < 0) - return PySocket_Err(); + return s->errorhandler(); Py_INCREF(Py_None); return Py_None; } @@ -2614,351 +2518,6 @@ static char getnameinfo_doc[] = \n\ Get host and port for a sockaddr."; -/* XXX It might be helpful to augment the error message generated - below with the name of the SSL function that generated the error. - I expect it's obvious most of the time. -*/ - -#ifdef USE_SSL -static PyObject * -PySSL_SetError(SSL *ssl, int ret) -{ - PyObject *v, *n, *s; - char *errstr; - int err; - - assert(ret <= 0); - - err = SSL_get_error(ssl, ret); - n = PyInt_FromLong(err); - if (n == NULL) - return NULL; - v = PyTuple_New(2); - if (v == NULL) { - Py_DECREF(n); - return NULL; - } - - switch (SSL_get_error(ssl, ret)) { - case SSL_ERROR_ZERO_RETURN: - errstr = "TLS/SSL connection has been closed"; - break; - case SSL_ERROR_WANT_READ: - errstr = "The operation did not complete (read)"; - break; - case SSL_ERROR_WANT_WRITE: - errstr = "The operation did not complete (write)"; - break; - case SSL_ERROR_WANT_X509_LOOKUP: - errstr = "The operation did not complete (X509 lookup)"; - break; - case SSL_ERROR_SYSCALL: - case SSL_ERROR_SSL: - { - unsigned long e = ERR_get_error(); - if (e == 0) { - /* an EOF was observed that violates the protocol */ - errstr = "EOF occurred in violation of protocol"; - } else if (e == -1) { - /* the underlying BIO reported an I/O error */ - Py_DECREF(v); - Py_DECREF(n); - return PySocket_Err(); - } else { - /* XXX Protected by global interpreter lock */ - errstr = ERR_error_string(e, NULL); - } - break; - } - default: - errstr = "Invalid error code"; - } - s = PyString_FromString(errstr); - if (s == NULL) { - Py_DECREF(v); - Py_DECREF(n); - } - PyTuple_SET_ITEM(v, 0, n); - PyTuple_SET_ITEM(v, 1, s); - PyErr_SetObject(PySSLErrorObject, v); - return NULL; -} - -/* This is a C function to be called for new object initialization */ -static PySSLObject * -newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file) -{ - PySSLObject *self; - char *errstr = NULL; - int ret; - - self = PyObject_New(PySSLObject, &PySSL_Type); /* Create new object */ - if (self == NULL){ - errstr = "newPySSLObject error"; - goto fail; - } - memset(self->server, '\0', sizeof(char) * X509_NAME_MAXLEN); - memset(self->issuer, '\0', sizeof(char) * X509_NAME_MAXLEN); - self->server_cert = NULL; - self->ssl = NULL; - self->ctx = NULL; - self->Socket = NULL; - - if ((key_file && !cert_file) || (!key_file && cert_file)) { - errstr = "Both the key & certificate files must be specified"; - goto fail; - } - - self->ctx = SSL_CTX_new(SSLv23_method()); /* Set up context */ - if (self->ctx == NULL) { - errstr = "SSL_CTX_new error"; - goto fail; - } - - if (key_file) { - if (SSL_CTX_use_PrivateKey_file(self->ctx, key_file, - SSL_FILETYPE_PEM) < 1) { - errstr = "SSL_CTX_use_PrivateKey_file error"; - goto fail; - } - - if (SSL_CTX_use_certificate_chain_file(self->ctx, - cert_file) < 1) { - errstr = "SSL_CTX_use_certificate_chain_file error"; - goto fail; - } - } - - SSL_CTX_set_verify(self->ctx, - SSL_VERIFY_NONE, NULL); /* set verify lvl */ - self->ssl = SSL_new(self->ctx); /* New ssl struct */ - SSL_set_fd(self->ssl, Sock->sock_fd); /* Set the socket for SSL */ - SSL_set_connect_state(self->ssl); - - /* Actually negotiate SSL connection */ - /* XXX If SSL_connect() returns 0, it's also a failure. */ - ret = SSL_connect(self->ssl); - if (ret <= 0) { - PySSL_SetError(self->ssl, ret); - goto fail; - } - self->ssl->debug = 1; - - if ((self->server_cert = SSL_get_peer_certificate(self->ssl))) { - X509_NAME_oneline(X509_get_subject_name(self->server_cert), - self->server, X509_NAME_MAXLEN); - X509_NAME_oneline(X509_get_issuer_name(self->server_cert), - self->issuer, X509_NAME_MAXLEN); - } - self->Socket = Sock; - Py_INCREF(self->Socket); - return self; - fail: - if (errstr) - PyErr_SetString(PySSLErrorObject, errstr); - Py_DECREF(self); - return NULL; -} - -/* This is the Python function called for new object initialization */ -static PyObject * -PySocket_ssl(PyObject *self, PyObject *args) -{ - PySSLObject *rv; - PySocketSockObject *Sock; - char *key_file = NULL; - char *cert_file = NULL; - - if (!PyArg_ParseTuple(args, "O!|zz:ssl", - &PySocketSock_Type, (PyObject*)&Sock, - &key_file, &cert_file)) - return NULL; - - rv = newPySSLObject(Sock, key_file, cert_file); - if (rv == NULL) - return NULL; - return (PyObject *)rv; -} - -static char ssl_doc[] = -"ssl(socket, [keyfile, certfile]) -> sslobject"; - -/* SSL object methods */ - -static PyObject * -PySSL_server(PySSLObject *self) -{ - return PyString_FromString(self->server); -} - -static PyObject * -PySSL_issuer(PySSLObject *self) -{ - return PyString_FromString(self->issuer); -} - - -static void PySSL_dealloc(PySSLObject *self) -{ - if (self->server_cert) /* Possible not to have one? */ - X509_free (self->server_cert); - if (self->ssl) - SSL_free(self->ssl); - if (self->ctx) - SSL_CTX_free(self->ctx); - Py_XDECREF(self->Socket); - PyObject_Del(self); -} - -static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args) -{ - char *data; - int len; - - if (!PyArg_ParseTuple(args, "s#:write", &data, &len)) - return NULL; - - Py_BEGIN_ALLOW_THREADS - len = SSL_write(self->ssl, data, len); - Py_END_ALLOW_THREADS - if (len > 0) - return PyInt_FromLong(len); - else - return PySSL_SetError(self->ssl, len); -} - -static char PySSL_SSLwrite_doc[] = -"write(s) -> len\n\ -\n\ -Writes the string s into the SSL object. Returns the number\n\ -of bytes written."; - -static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args) -{ - PyObject *buf; - int count = 0; - int len = 1024; - - if (!PyArg_ParseTuple(args, "|i:read", &len)) - return NULL; - - if (!(buf = PyString_FromStringAndSize((char *) 0, len))) - return NULL; - - Py_BEGIN_ALLOW_THREADS - count = SSL_read(self->ssl, PyString_AsString(buf), len); - Py_END_ALLOW_THREADS - if (count <= 0) { - Py_DECREF(buf); - return PySSL_SetError(self->ssl, count); - } - if (count != len && _PyString_Resize(&buf, count) < 0) - return NULL; - return buf; -} - -static char PySSL_SSLread_doc[] = -"read([len]) -> string\n\ -\n\ -Read up to len bytes from the SSL socket."; - -static PyMethodDef PySSLMethods[] = { - {"write", (PyCFunction)PySSL_SSLwrite, METH_VARARGS, - PySSL_SSLwrite_doc}, - {"read", (PyCFunction)PySSL_SSLread, METH_VARARGS, - PySSL_SSLread_doc}, - {"server", (PyCFunction)PySSL_server, METH_NOARGS}, - {"issuer", (PyCFunction)PySSL_issuer, METH_NOARGS}, - {NULL, NULL} -}; - -static PyObject *PySSL_getattr(PySSLObject *self, char *name) -{ - return Py_FindMethod(PySSLMethods, (PyObject *)self, name); -} - -staticforward PyTypeObject PySSL_Type = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "_socket.SSL", /*tp_name*/ - sizeof(PySSLObject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - /* methods */ - (destructor)PySSL_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - (getattrfunc)PySSL_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ -}; - -/* helper routines for seeding the SSL PRNG */ -static PyObject * -PySSL_RAND_add(PyObject *self, PyObject *args) -{ - char *buf; - int len; - double entropy; - - if (!PyArg_ParseTuple(args, "s#d:RAND_add", &buf, &len, &entropy)) - return NULL; - RAND_add(buf, len, entropy); - Py_INCREF(Py_None); - return Py_None; -} - -static char PySSL_RAND_add_doc[] = -"RAND_add(string, entropy)\n\ -\n\ -Mix string into the OpenSSL PRNG state. entropy (a float) is a lower\n\ -bound on the entropy contained in string."; - -static PyObject * -PySSL_RAND_status(PyObject *self) -{ - return PyInt_FromLong(RAND_status()); -} - -static char PySSL_RAND_status_doc[] = -"RAND_status() -> 0 or 1\n\ -\n\ -Returns 1 if the OpenSSL PRNG has been seeded with enough data and 0 if not.\n\ -It is necessary to seed the PRNG with RAND_add() on some platforms before\n\ -using the ssl() function."; - -static PyObject * -PySSL_RAND_egd(PyObject *self, PyObject *arg) -{ - int bytes; - - if (!PyString_Check(arg)) - return PyErr_Format(PyExc_TypeError, - "RAND_egd() expected string, found %s", - arg->ob_type->tp_name); - bytes = RAND_egd(PyString_AS_STRING(arg)); - if (bytes == -1) { - PyErr_SetString(PySSLErrorObject, - "EGD connection failed or EGD did not return " - "enough data to seed the PRNG"); - return NULL; - } - return PyInt_FromLong(bytes); -} - -static char PySSL_RAND_egd_doc[] = -"RAND_egd(path) -> bytes\n\ -\n\ -Queries the entropy gather daemon (EGD) on socket path. Returns number\n\ -of bytes read. Raises socket.sslerror if connection to EGD fails or\n\ -if it does provide enough data to seed PRNG."; - -#endif /* USE_SSL */ - - /* List of functions exported by this module. */ static PyMethodDef PySocket_methods[] = { @@ -2994,16 +2553,6 @@ static PyMethodDef PySocket_methods[] = { METH_VARARGS, getaddrinfo_doc}, {"getnameinfo", PySocket_getnameinfo, METH_VARARGS, getnameinfo_doc}, -#ifdef USE_SSL - {"ssl", PySocket_ssl, - METH_VARARGS, ssl_doc}, - {"RAND_add", PySSL_RAND_add, METH_VARARGS, - PySSL_RAND_add_doc}, - {"RAND_egd", PySSL_RAND_egd, METH_O, - PySSL_RAND_egd_doc}, - {"RAND_status", (PyCFunction)PySSL_RAND_status, METH_NOARGS, - PySSL_RAND_status_doc}, -#endif /* USE_SSL */ {NULL, NULL} /* Sentinel */ }; @@ -3094,6 +2643,14 @@ OS2init(void) #endif /* PYOS_OS2 */ +/* C API table - always add new things to the end for binary + compatibility. */ +static +PySocketModule_APIObject PySocketModuleAPI = +{ + &PySocketSock_Type, +}; + /* Initialize this module. * This is called when the first 'import socket' is done, * via a table in config.c, if config.c is compiled with USE_SOCKET @@ -3141,10 +2698,9 @@ init_socket(void) PySocketSock_Type.tp_getattro = PyObject_GenericGetAttr; PySocketSock_Type.tp_alloc = PyType_GenericAlloc; PySocketSock_Type.tp_free = _PyObject_Del; -#ifdef USE_SSL - PySSL_Type.ob_type = &PyType_Type; -#endif - m = Py_InitModule3("_socket", PySocket_methods, module_doc); + m = Py_InitModule3(PySocket_MODULE_NAME, + PySocket_methods, + module_doc); d = PyModule_GetDict(m); PySocket_Error = PyErr_NewException("socket.error", NULL, NULL); if (PySocket_Error == NULL) @@ -3159,29 +2715,6 @@ init_socket(void) if (PyGAI_Error == NULL) return; PyDict_SetItemString(d, "gaierror", PyGAI_Error); -#ifdef USE_SSL - SSL_load_error_strings(); - SSLeay_add_ssl_algorithms(); - PySSLErrorObject = PyErr_NewException("socket.sslerror", NULL, NULL); - if (PySSLErrorObject == NULL) - return; - PyDict_SetItemString(d, "sslerror", PySSLErrorObject); - if (PyDict_SetItemString(d, "SSLType", - (PyObject *)&PySSL_Type) != 0) - return; - PyModule_AddIntConstant(m, "SSL_ERROR_ZERO_RETURN", - SSL_ERROR_ZERO_RETURN); - PyModule_AddIntConstant(m, "SSL_ERROR_WANT_READ", - SSL_ERROR_WANT_READ); - PyModule_AddIntConstant(m, "SSL_ERROR_WANT_WRITE", - SSL_ERROR_WANT_WRITE); - PyModule_AddIntConstant(m, "SSL_ERROR_WANT_X509_LOOKUP", - SSL_ERROR_WANT_X509_LOOKUP); - PyModule_AddIntConstant(m, "SSL_ERROR_SYSCALL", - SSL_ERROR_SYSCALL); - PyModule_AddIntConstant(m, "SSL_ERROR_SSL", - SSL_ERROR_SSL); -#endif /* USE_SSL */ if (PyDict_SetItemString(d, "SocketType", (PyObject *)&PySocketSock_Type) != 0) return; @@ -3189,6 +2722,12 @@ init_socket(void) (PyObject *)&PySocketSock_Type) != 0) return; + /* Export C API */ + if (PyDict_SetItemString(d, PySocket_CAPI_NAME, + PyCObject_FromVoidPtr((void *)&PySocketModuleAPI, NULL) + ) != 0) + return; + /* Address families (we only support AF_INET and AF_UNIX) */ #ifdef AF_UNSPEC insint(d, "AF_UNSPEC", AF_UNSPEC); diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h new file mode 100644 index 0000000..606db3d --- /dev/null +++ b/Modules/socketmodule.h @@ -0,0 +1,159 @@ +/* Socket module header file */ + +/* Includes needed for the sockaddr_* symbols below */ +#ifndef MS_WINDOWS +# include +# include +# if !(defined(__BEOS__) || defined(__CYGWIN__) || (defined(PYOS_OS2) && defined(PYCC_VACPP))) +# include +# endif + +#else /* MS_WINDOWS */ +# include +#endif + +#ifdef HAVE_SYS_UN_H +# include +#else +# undef AF_UNIX +#endif + +#ifdef HAVE_NETPACKET_PACKET_H +# include +# include +# include +#endif + +#ifndef Py__SOCKET_H +#define Py__SOCKET_H +#ifdef __cplusplus +extern "C" { +#endif + +/* Python module and C API name */ +#define PySocket_MODULE_NAME "_socket" +#define PySocket_CAPI_NAME "CAPI" + +/* Abstract the socket file descriptor type */ +#ifdef MS_WINDOWS +typedef SOCKET SOCKET_T; +# ifdef MS_WIN64 +# define SIZEOF_SOCKET_T 8 +# else +# define SIZEOF_SOCKET_T 4 +# endif +#else +typedef int SOCKET_T; +# define SIZEOF_SOCKET_T SIZEOF_INT +#endif + +/* The object holding a socket. It holds some extra information, + like the address family, which is used to decode socket address + arguments properly. */ + +typedef struct { + PyObject_HEAD + SOCKET_T sock_fd; /* Socket file descriptor */ + int sock_family; /* Address family, e.g., AF_INET */ + int sock_type; /* Socket type, e.g., SOCK_STREAM */ + int sock_proto; /* Protocol type, usually 0 */ + union sock_addr { + struct sockaddr_in in; +#ifdef AF_UNIX + struct sockaddr_un un; +#endif +#ifdef ENABLE_IPV6 + struct sockaddr_in6 in6; + struct sockaddr_storage storage; +#endif +#ifdef HAVE_NETPACKET_PACKET_H + struct sockaddr_ll ll; +#endif + } sock_addr; + PyObject *(*errorhandler)(void); /* Error handler; checks + errno, returns NULL and + sets a Python exception */ +} PySocketSockObject; + +/* A forward reference to the Socktype type object. + The Socktype variable contains pointers to various functions, + some of which call newsockobject(), which uses Socktype, so + there has to be a circular reference. */ + +extern DL_IMPORT(PyTypeObject) PySocketSock_Type; + +/* --- C API ----------------------------------------------------*/ + +/* C API for usage by other Python modules */ +typedef struct { + + PyTypeObject *Sock_Type; + +} PySocketModule_APIObject; + +#ifndef PySocket_BUILDING_SOCKET + +/* --- C API ----------------------------------------------------*/ + +/* Interfacestructure to C API for other modules. + Call PySocket_ImportModuleAPI() to initialize this + structure. After that usage is simple: + + if (!PyArg_ParseTuple(args, "O!|zz:ssl", + &PySocketModule.Sock_Type, (PyObject*)&Sock, + &key_file, &cert_file)) + return NULL; + ... + +*/ + +static +PySocketModule_APIObject PySocketModule; + +/* You *must* call this before using any of the functions in + PySocketModule and check its outcome; otherwise all accesses will + result in a segfault. Returns 0 on success. */ + +#ifndef DPRINTF +# define DPRINTF if (0) printf +#endif + +static +int PySocketModule_ImportModuleAndAPI(void) +{ + PyObject *mod = 0, *v = 0; + char *apimodule = PySocket_MODULE_NAME; + char *apiname = PySocket_CAPI_NAME; + void *api; + + DPRINTF("Importing the %s C API...\n",apimodule); + mod = PyImport_ImportModule(apimodule); + if (mod == NULL) + goto onError; + DPRINTF(" %s package found\n",apimodule); + v = PyObject_GetAttrString(mod,apiname); + if (v == NULL) + goto onError; + Py_DECREF(mod); + DPRINTF(" API object %s found\n",apiname); + api = PyCObject_AsVoidPtr(v); + if (api == NULL) + goto onError; + Py_DECREF(v); + memcpy(&PySocketModule, api, sizeof(PySocketModule)); + DPRINTF(" API object loaded and initialized.\n"); + return 0; + + onError: + DPRINTF(" not found.\n"); + Py_XDECREF(mod); + Py_XDECREF(v); + return -1; +} + +#endif + +#ifdef __cplusplus +} +#endif +#endif /* !Py__SOCKET_H */ diff --git a/setup.py b/setup.py index e9758e3..c467ec7 100644 --- a/setup.py +++ b/setup.py @@ -173,21 +173,26 @@ class PyBuildExt(build_ext): self.get_ext_filename(self.get_ext_fullname(ext.name))) try: imp.load_dynamic(ext.name, ext_filename) - except ImportError: - self.announce('WARNING: removing "%s" since importing it failed' % - ext.name) - assert not self.inplace - fullname = self.get_ext_fullname(ext.name) - ext_filename = os.path.join(self.build_lib, - self.get_ext_filename(fullname)) - os.remove(ext_filename) - - # XXX -- This relies on a Vile HACK in - # distutils.command.build_ext.build_extension(). The - # _built_objects attribute is stored there strictly for - # use here. - for filename in self._built_objects: - os.remove(filename) + except ImportError, why: + + if 1: + self.announce('*** WARNING: removing "%s" since importing it' + ' failed: %s' % (ext.name, why)) + assert not self.inplace + fullname = self.get_ext_fullname(ext.name) + ext_filename = os.path.join(self.build_lib, + self.get_ext_filename(fullname)) + os.remove(ext_filename) + + # XXX -- This relies on a Vile HACK in + # distutils.command.build_ext.build_extension(). The + # _built_objects attribute is stored there strictly for + # use here. + for filename in self._built_objects: + os.remove(filename) + else: + self.announce('*** WARNING: importing extension "%s" ' + 'failed: %s' % (ext.name, why)) def get_platform (self): # Get value of sys.platform @@ -359,7 +364,8 @@ class PyBuildExt(build_ext): exts.append( Extension('crypt', ['cryptmodule.c'], libraries=libs) ) # socket(2) - # Detect SSL support for the socket module + exts.append( Extension('_socket', ['socketmodule.c']) ) + # Detect SSL support for the socket module (via _ssl) ssl_incs = find_file('openssl/ssl.h', inc_dirs, ['/usr/local/ssl/include', '/usr/contrib/ssl/include/' @@ -372,13 +378,10 @@ class PyBuildExt(build_ext): if (ssl_incs is not None and ssl_libs is not None): - exts.append( Extension('_socket', ['socketmodule.c'], + exts.append( Extension('_ssl', ['_ssl.c'], include_dirs = ssl_incs, library_dirs = ssl_libs, - libraries = ['ssl', 'crypto'], - define_macros = [('USE_SSL',1)] ) ) - else: - exts.append( Extension('_socket', ['socketmodule.c']) ) + libraries = ['ssl', 'crypto']) ) # Modules that provide persistent dictionary-like semantics. You will # probably want to arrange for at least one of them to be available on -- cgit v0.12