--- _ssl.c.CURRENT_PYTHON_CVS 2003-01-28 18:11:49.000000000 -0500 +++ _ssl.c 2003-01-28 18:55:11.000000000 -0500 @@ -141,120 +141,146 @@ default: p = PY_SSL_ERROR_INVALID_ERROR_CODE; errstr = "Invalid error code"; } n = PyInt_FromLong((long) p); if (n == NULL) return NULL; v = PyTuple_New(2); if (v == NULL) { Py_DECREF(n); return NULL; } 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; } static PySSLObject * newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file) { PySSLObject *self; char *errstr = NULL; int ret; + int err; + int timedout; 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; } Py_BEGIN_ALLOW_THREADS self->ctx = SSL_CTX_new(SSLv23_method()); /* Set up context */ Py_END_ALLOW_THREADS if (self->ctx == NULL) { errstr = "SSL_CTX_new error"; goto fail; } if (key_file) { Py_BEGIN_ALLOW_THREADS ret = SSL_CTX_use_PrivateKey_file(self->ctx, key_file, SSL_FILETYPE_PEM); Py_END_ALLOW_THREADS if (ret < 1) { errstr = "SSL_CTX_use_PrivateKey_file error"; goto fail; } Py_BEGIN_ALLOW_THREADS ret = SSL_CTX_use_certificate_chain_file(self->ctx, cert_file); Py_END_ALLOW_THREADS if (ret < 1) { errstr = "SSL_CTX_use_certificate_chain_file error"; goto fail; } } Py_BEGIN_ALLOW_THREADS SSL_CTX_set_verify(self->ctx, SSL_VERIFY_NONE, NULL); /* set verify lvl */ self->ssl = SSL_new(self->ctx); /* New ssl struct */ Py_END_ALLOW_THREADS SSL_set_fd(self->ssl, Sock->sock_fd); /* Set the socket for SSL */ + + /* If the socket is is non-blocking mode or timeout mode, set the BIO + * to non-blocking mode (blocking is the default) + */ + if (Sock->sock_timeout >= 0.0) { + /* Set both the read and write BIO's to non-blocking mode */ + BIO_set_nbio(SSL_get_rbio(self->ssl), 1); + BIO_set_nbio(SSL_get_wbio(self->ssl), 1); + } + Py_BEGIN_ALLOW_THREADS SSL_set_connect_state(self->ssl); - + Py_END_ALLOW_THREADS /* Actually negotiate SSL connection */ /* XXX If SSL_connect() returns 0, it's also a failure. */ - ret = SSL_connect(self->ssl); - Py_END_ALLOW_THREADS + timedout = 0; + do { + Py_BEGIN_ALLOW_THREADS + ret = SSL_connect(self->ssl); + err = SSL_get_error(self->ssl, ret); + Py_END_ALLOW_THREADS + if (err == SSL_ERROR_WANT_READ) { + timedout = wait_for_timeout(Sock, 0); + } else if (err == SSL_ERROR_WANT_WRITE) { + timedout = wait_for_timeout(Sock, 1); + } + if (timedout) { + PyErr_SetString(PySSLErrorObject, "The connect operation timed out"); + return NULL; + } + } while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE); if (ret <= 0) { PySSL_SetError(self, ret); goto fail; } self->ssl->debug = 1; Py_BEGIN_ALLOW_THREADS 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); } Py_END_ALLOW_THREADS self->Socket = Sock; Py_INCREF(self->Socket); return self; fail: if (errstr) PyErr_SetString(PySSLErrorObject, errstr); Py_DECREF(self); return NULL; } static PyObject * PySocket_ssl(PyObject *self, PyObject *args) { PySSLObject *rv; PySocketSockObject *Sock; char *key_file = NULL; @@ -301,123 +327,149 @@ Py_XDECREF(self->Socket); PyObject_Del(self); } /* If the socket has a timeout, do a select() on the socket. The argument writing indicates the direction. Return non-zero if the socket timed out, zero otherwise. */ static int wait_for_timeout(PySocketSockObject *s, int writing) { fd_set fds; struct timeval tv; int rc; /* Nothing to do unless we're in timeout mode (not non-blocking) */ if (s->sock_timeout <= 0.0) return 0; /* Guard against closed socket */ if (s->sock_fd < 0) return 0; /* Construct the arguments to select */ tv.tv_sec = (int)s->sock_timeout; tv.tv_usec = (int)((s->sock_timeout - tv.tv_sec) * 1e6); FD_ZERO(&fds); FD_SET(s->sock_fd, &fds); /* See if the socket is ready */ + Py_BEGIN_ALLOW_THREADS if (writing) rc = select(s->sock_fd+1, NULL, &fds, NULL, &tv); else rc = select(s->sock_fd+1, &fds, NULL, NULL, &tv); + Py_END_ALLOW_THREADS /* Return 1 on timeout, 0 otherwise */ return rc == 0; } static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args) { char *data; int len; int timedout; + int err; if (!PyArg_ParseTuple(args, "s#:write", &data, &len)) return NULL; - Py_BEGIN_ALLOW_THREADS timedout = wait_for_timeout(self->Socket, 1); - Py_END_ALLOW_THREADS if (timedout) { PyErr_SetString(PySSLErrorObject, "The write operation timed out"); return NULL; } - Py_BEGIN_ALLOW_THREADS - len = SSL_write(self->ssl, data, len); - Py_END_ALLOW_THREADS + do { + err = 0; + Py_BEGIN_ALLOW_THREADS + len = SSL_write(self->ssl, data, len); + err = SSL_get_error(self->ssl, len); + Py_END_ALLOW_THREADS + if (err == SSL_ERROR_WANT_READ) { + timedout = wait_for_timeout(self->Socket, 0); + } else if (err == SSL_ERROR_WANT_WRITE) { + timedout = wait_for_timeout(self->Socket, 1); + } + if (timedout) { + PyErr_SetString(PySSLErrorObject, "The write operation timed out"); + return NULL; + } + } while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE); if (len > 0) return PyInt_FromLong(len); else return PySSL_SetError(self, len); } PyDoc_STRVAR(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; int timedout; + int err; if (!PyArg_ParseTuple(args, "|i:read", &len)) return NULL; if (!(buf = PyString_FromStringAndSize((char *) 0, len))) return NULL; - Py_BEGIN_ALLOW_THREADS timedout = wait_for_timeout(self->Socket, 0); - Py_END_ALLOW_THREADS if (timedout) { PyErr_SetString(PySSLErrorObject, "The read operation timed out"); return NULL; } - Py_BEGIN_ALLOW_THREADS - count = SSL_read(self->ssl, PyString_AsString(buf), len); - Py_END_ALLOW_THREADS + do { + err = 0; + Py_BEGIN_ALLOW_THREADS + count = SSL_read(self->ssl, PyString_AsString(buf), len); + err = SSL_get_error(self->ssl, count); + Py_END_ALLOW_THREADS + if (err == SSL_ERROR_WANT_READ) { + timedout = wait_for_timeout(self->Socket, 0); + } else if (err == SSL_ERROR_WANT_WRITE) { + timedout = wait_for_timeout(self->Socket, 1); + } + if (timedout) { + PyErr_SetString(PySSLErrorObject, "The read operation timed out"); + return NULL; + } + } while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE); if (count <= 0) { Py_DECREF(buf); return PySSL_SetError(self, count); } if (count != len) _PyString_Resize(&buf, count); return buf; } PyDoc_STRVAR(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); } static PyTypeObject PySSL_Type = {