.. versionchanged:: 3.3
:exc:`SSLError` used to be a subtype of :exc:`socket.error`.
+.. exception:: SSLZeroReturnError
+
+ A subclass of :exc:`SSLError` raised when trying to read or write and
+ the SSL connection has been closed cleanly. Note that this doesn't
+ mean that the underlying transport (read TCP) has been closed.
+
+ .. versionadded:: 3.3
+
+.. exception:: SSLWantReadError
+
+ A subclass of :exc:`SSLError` raised by a :ref:`non-blocking SSL socket
+ <ssl-nonblocking>` when trying to read or write data, but more data needs
+ to be received on the underlying TCP transport before the request can be
+ fulfilled.
+
+ .. versionadded:: 3.3
+
+.. exception:: SSLWantWriteError
+
+ A subclass of :exc:`SSLError` raised by a :ref:`non-blocking SSL socket
+ <ssl-nonblocking>` when trying to read or write data, but more data needs
+ to be sent on the underlying TCP transport before the request can be
+ fulfilled.
+
+ .. versionadded:: 3.3
+
+.. exception:: SSLSyscallError
+
+ A subclass of :exc:`SSLError` raised when a system error was encountered
+ while trying to fulfill an operation on a SSL socket. Unfortunately,
+ there is no easy way to inspect the original errno number.
+
+ .. versionadded:: 3.3
+
+.. exception:: SSLEOFError
+
+ A subclass of :exc:`SSLError` raised when the SSL connection has been
+ terminated abrupted. Generally, you shouldn't try to reuse the underlying
+ transport when this error is encountered.
+
+ .. versionadded:: 3.3
+
.. exception:: CertificateError
Raised to signal an error with a certificate (such as mismatching
try:
s.do_handshake()
break
- except ssl.SSLError as err:
- if err.args[0] == ssl.SSL_ERROR_WANT_READ:
- select.select([s], [], [], 5.0)
- elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE:
- select.select([], [s], [], 5.0)
- else:
- raise
+ except ssl.SSLWantReadError:
+ select.select([s], [], [], 5.0)
+ except ssl.SSLWantWriteError:
+ select.select([], [s], [], 5.0)
# SSL established
self.assertTrue(s.getpeercert())
finally:
count += 1
s.do_handshake()
break
- except ssl.SSLError as err:
- if err.args[0] == ssl.SSL_ERROR_WANT_READ:
- select.select([s], [], [])
- elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE:
- select.select([], [s], [])
- else:
- raise
+ except ssl.SSLWantReadError:
+ select.select([s], [], [])
+ except ssl.SSLWantWriteError:
+ select.select([], [s], [])
s.close()
if support.verbose:
sys.stdout.write("\nNeeded %d calls to do_handshake() to establish session.\n" % count)
def _do_ssl_handshake(self):
try:
self.socket.do_handshake()
- except ssl.SSLError as err:
- if err.args[0] in (ssl.SSL_ERROR_WANT_READ,
- ssl.SSL_ERROR_WANT_WRITE):
- return
- elif err.args[0] == ssl.SSL_ERROR_EOF:
- return self.handle_close()
+ except (ssl.SSLWantReadError, ssl.SSLWantWriteError):
+ return
+ except ssl.SSLEOFError:
+ return self.handle_close()
+ except ssl.SSLError:
raise
except socket.error as err:
if err.args[0] == errno.ECONNABORTED:
/* SSL error object */
static PyObject *PySSLErrorObject;
+static PyObject *PySSLZeroReturnErrorObject;
+static PyObject *PySSLWantReadErrorObject;
+static PyObject *PySSLWantWriteErrorObject;
+static PyObject *PySSLSyscallErrorObject;
+static PyObject *PySSLEOFErrorObject;
#ifdef WITH_THREAD
PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
{
PyObject *v;
+ PyObject *type = PySSLErrorObject;
char buf[2048];
char *errstr;
int err;
switch (err) {
case SSL_ERROR_ZERO_RETURN:
- errstr = "TLS/SSL connection has been closed";
+ errstr = "TLS/SSL connection has been closed (EOF)";
+ type = PySSLZeroReturnErrorObject;
p = PY_SSL_ERROR_ZERO_RETURN;
break;
case SSL_ERROR_WANT_READ:
errstr = "The operation did not complete (read)";
+ type = PySSLWantReadErrorObject;
p = PY_SSL_ERROR_WANT_READ;
break;
case SSL_ERROR_WANT_WRITE:
p = PY_SSL_ERROR_WANT_WRITE;
+ type = PySSLWantWriteErrorObject;
errstr = "The operation did not complete (write)";
break;
case SSL_ERROR_WANT_X509_LOOKUP:
= (PySocketSockObject *) PyWeakref_GetObject(obj->Socket);
if (ret == 0 || (((PyObject *)s) == Py_None)) {
p = PY_SSL_ERROR_EOF;
+ type = PySSLEOFErrorObject;
errstr = "EOF occurred in violation of protocol";
} else if (ret == -1) {
/* underlying BIO reported an I/O error */
return v;
} else { /* possible? */
p = PY_SSL_ERROR_SYSCALL;
+ type = PySSLSyscallErrorObject;
errstr = "Some I/O error occurred";
}
} else {
ERR_clear_error();
v = Py_BuildValue("(is)", p, buf);
if (v != NULL) {
- PyErr_SetObject(PySSLErrorObject, v);
+ PyErr_SetObject(type, v);
Py_DECREF(v);
}
return NULL;
PyDoc_STRVAR(SSLError_doc,
"An error occurred in the SSL implementation.");
+PyDoc_STRVAR(SSLZeroReturnError_doc,
+"SSL/TLS session closed cleanly.");
+
+PyDoc_STRVAR(SSLWantReadError_doc,
+"Non-blocking SSL socket needs to read more data\n"
+"before the requested operation can be completed.");
+
+PyDoc_STRVAR(SSLWantWriteError_doc,
+"Non-blocking SSL socket needs to write more data\n"
+"before the requested operation can be completed.");
+
+PyDoc_STRVAR(SSLSyscallError_doc,
+"System error when attempting SSL operation.");
+
+PyDoc_STRVAR(SSLEOFError_doc,
+"SSL/TLS connection terminated abruptly.");
+
PyMODINIT_FUNC
PyInit__ssl(void)
NULL);
if (PySSLErrorObject == NULL)
return NULL;
- if (PyDict_SetItemString(d, "SSLError", PySSLErrorObject) != 0)
+ PySSLZeroReturnErrorObject = PyErr_NewExceptionWithDoc(
+ "ssl.SSLZeroReturnError", SSLZeroReturnError_doc,
+ PySSLErrorObject, NULL);
+ PySSLWantReadErrorObject = PyErr_NewExceptionWithDoc(
+ "ssl.SSLWantReadError", SSLWantReadError_doc,
+ PySSLErrorObject, NULL);
+ PySSLWantWriteErrorObject = PyErr_NewExceptionWithDoc(
+ "ssl.SSLWantWriteError", SSLWantWriteError_doc,
+ PySSLErrorObject, NULL);
+ PySSLSyscallErrorObject = PyErr_NewExceptionWithDoc(
+ "ssl.SSLSyscallError", SSLSyscallError_doc,
+ PySSLErrorObject, NULL);
+ PySSLEOFErrorObject = PyErr_NewExceptionWithDoc(
+ "ssl.SSLEOFError", SSLEOFError_doc,
+ PySSLErrorObject, NULL);
+ if (PySSLZeroReturnErrorObject == NULL
+ || PySSLWantReadErrorObject == NULL
+ || PySSLWantWriteErrorObject == NULL
+ || PySSLSyscallErrorObject == NULL
+ || PySSLEOFErrorObject == NULL)
+ return NULL;
+ if (PyDict_SetItemString(d, "SSLError", PySSLErrorObject) != 0
+ || PyDict_SetItemString(d, "SSLZeroReturnError", PySSLZeroReturnErrorObject) != 0
+ || PyDict_SetItemString(d, "SSLWantReadError", PySSLWantReadErrorObject) != 0
+ || PyDict_SetItemString(d, "SSLWantWriteError", PySSLWantWriteErrorObject) != 0
+ || PyDict_SetItemString(d, "SSLSyscallError", PySSLSyscallErrorObject) != 0
+ || PyDict_SetItemString(d, "SSLEOFError", PySSLEOFErrorObject) != 0)
return NULL;
if (PyDict_SetItemString(d, "_SSLContext",
(PyObject *)&PySSLContext_Type) != 0)