diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 5df61746515be4..2eefefc32eb0d7 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -1157,6 +1157,11 @@ SSL Sockets to create instances directly. This was never documented or officially supported. + .. versionchanged:: 3.10 + Python now uses ``SSL_read_ex`` and ``SSL_write_ex`` internally. The + functions support reading and writing of data larger than 2 GB. Writing + zero-length data no longer fails with a protocol violation error. + SSL sockets also have the following additional methods and attributes: .. method:: SSLSocket.read(len=1024, buffer=None) diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index c95b2a8d814f4c..2e126e6cf3d133 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -1130,6 +1130,17 @@ def test_connect_ex_error(self): ) self.assertIn(rc, errors) + def test_read_write_zero(self): + # empty reads and writes now work, bpo-42854, bpo-31711 + client_context, server_context, hostname = testing_context() + server = ThreadedEchoServer(context=server_context) + with server: + with client_context.wrap_socket(socket.socket(), + server_hostname=hostname) as s: + s.connect((HOST, server.port)) + self.assertEqual(s.recv(0), b"") + self.assertEqual(s.send(b""), 0) + class ContextTests(unittest.TestCase): diff --git a/Modules/_ssl.c b/Modules/_ssl.c index f67af82b76cc31..d142dac95f4cb7 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -2352,7 +2352,8 @@ static PyObject * _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b) /*[clinic end generated code: output=aa7a6be5527358d8 input=77262d994fe5100a]*/ { - int len; + size_t count = 0; + int retval; int sockstate; _PySSLError err; int nonblocking; @@ -2370,12 +2371,6 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b) Py_INCREF(sock); } - if (b->len > INT_MAX) { - PyErr_Format(PyExc_OverflowError, - "string longer than %d bytes", INT_MAX); - goto error; - } - if (sock != NULL) { /* just in case the blocking state of the socket has been changed */ nonblocking = (sock->sock_timeout >= 0); @@ -2405,8 +2400,8 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b) do { PySSL_BEGIN_ALLOW_THREADS - len = SSL_write(self->ssl, b->buf, (int)b->len); - err = _PySSL_errno(len <= 0, self->ssl, len); + retval = SSL_write_ex(self->ssl, b->buf, (size_t)b->len, &count); + err = _PySSL_errno(retval == 0, self->ssl, retval); PySSL_END_ALLOW_THREADS self->err = err; @@ -2439,11 +2434,11 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b) err.ssl == SSL_ERROR_WANT_WRITE); Py_XDECREF(sock); - if (len <= 0) - return PySSL_SetError(self, len, __FILE__, __LINE__); + if (retval == 0) + return PySSL_SetError(self, retval, __FILE__, __LINE__); if (PySSL_ChainExceptions(self) < 0) return NULL; - return PyLong_FromLong(len); + return PyLong_FromSize_t(count); error: Py_XDECREF(sock); PySSL_ChainExceptions(self); @@ -2493,7 +2488,8 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len, { PyObject *dest = NULL; char *mem; - int count; + size_t count = 0; + int retval; int sockstate; _PySSLError err; int nonblocking; @@ -2556,8 +2552,8 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len, do { PySSL_BEGIN_ALLOW_THREADS - count = SSL_read(self->ssl, mem, len); - err = _PySSL_errno(count <= 0, self->ssl, count); + retval = SSL_read_ex(self->ssl, mem, (size_t)len, &count); + err = _PySSL_errno(retval == 0, self->ssl, retval); PySSL_END_ALLOW_THREADS self->err = err; @@ -2590,8 +2586,8 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len, } while (err.ssl == SSL_ERROR_WANT_READ || err.ssl == SSL_ERROR_WANT_WRITE); - if (count <= 0) { - PySSL_SetError(self, count, __FILE__, __LINE__); + if (retval == 0) { + PySSL_SetError(self, retval, __FILE__, __LINE__); goto error; } if (self->exc_type != NULL) @@ -2604,7 +2600,7 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len, return dest; } else { - return PyLong_FromLong(count); + return PyLong_FromSize_t(count); } error: