Skip to content

Commit 89d1550

Browse files
authored
bpo-42854: Use SSL_read/write_ex() (GH-25468)
The ssl module 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. Signed-off-by: Christian Heimes <[email protected]>
1 parent 49fdf11 commit 89d1550

File tree

4 files changed

+34
-18
lines changed

4 files changed

+34
-18
lines changed

Doc/library/ssl.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,11 @@ SSL Sockets
11221122
to create instances directly. This was never documented or officially
11231123
supported.
11241124

1125+
.. versionchanged:: 3.10
1126+
Python now uses ``SSL_read_ex`` and ``SSL_write_ex`` internally. The
1127+
functions support reading and writing of data larger than 2 GB. Writing
1128+
zero-length data no longer fails with a protocol violation error.
1129+
11251130
SSL sockets also have the following additional methods and attributes:
11261131

11271132
.. method:: SSLSocket.read(len=1024, buffer=None)

Lib/test/test_ssl.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,17 @@ def test_connect_ex_error(self):
11101110
)
11111111
self.assertIn(rc, errors)
11121112

1113+
def test_read_write_zero(self):
1114+
# empty reads and writes now work, bpo-42854, bpo-31711
1115+
client_context, server_context, hostname = testing_context()
1116+
server = ThreadedEchoServer(context=server_context)
1117+
with server:
1118+
with client_context.wrap_socket(socket.socket(),
1119+
server_hostname=hostname) as s:
1120+
s.connect((HOST, server.port))
1121+
self.assertEqual(s.recv(0), b"")
1122+
self.assertEqual(s.send(b""), 0)
1123+
11131124

11141125
class ContextTests(unittest.TestCase):
11151126

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
The :mod:`ssl` module now uses ``SSL_read_ex`` and ``SSL_write_ex``
2+
internally. The functions support reading and writing of data larger
3+
than 2 GB. Writing zero-length data no longer fails with a protocol
4+
violation error.

Modules/_ssl.c

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2186,7 +2186,8 @@ static PyObject *
21862186
_ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)
21872187
/*[clinic end generated code: output=aa7a6be5527358d8 input=77262d994fe5100a]*/
21882188
{
2189-
int len;
2189+
size_t count = 0;
2190+
int retval;
21902191
int sockstate;
21912192
_PySSLError err;
21922193
int nonblocking;
@@ -2204,12 +2205,6 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)
22042205
Py_INCREF(sock);
22052206
}
22062207

2207-
if (b->len > INT_MAX) {
2208-
PyErr_Format(PyExc_OverflowError,
2209-
"string longer than %d bytes", INT_MAX);
2210-
goto error;
2211-
}
2212-
22132208
if (sock != NULL) {
22142209
/* just in case the blocking state of the socket has been changed */
22152210
nonblocking = (sock->sock_timeout >= 0);
@@ -2239,8 +2234,8 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)
22392234

22402235
do {
22412236
PySSL_BEGIN_ALLOW_THREADS
2242-
len = SSL_write(self->ssl, b->buf, (int)b->len);
2243-
err = _PySSL_errno(len <= 0, self->ssl, len);
2237+
retval = SSL_write_ex(self->ssl, b->buf, (int)b->len, &count);
2238+
err = _PySSL_errno(retval == 0, self->ssl, retval);
22442239
PySSL_END_ALLOW_THREADS
22452240
self->err = err;
22462241

@@ -2273,11 +2268,11 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)
22732268
err.ssl == SSL_ERROR_WANT_WRITE);
22742269

22752270
Py_XDECREF(sock);
2276-
if (len <= 0)
2277-
return PySSL_SetError(self, len, __FILE__, __LINE__);
2271+
if (retval == 0)
2272+
return PySSL_SetError(self, retval, __FILE__, __LINE__);
22782273
if (PySSL_ChainExceptions(self) < 0)
22792274
return NULL;
2280-
return PyLong_FromLong(len);
2275+
return PyLong_FromSize_t(count);
22812276
error:
22822277
Py_XDECREF(sock);
22832278
PySSL_ChainExceptions(self);
@@ -2327,7 +2322,8 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, int len, int group_right_1,
23272322
{
23282323
PyObject *dest = NULL;
23292324
char *mem;
2330-
int count;
2325+
size_t count = 0;
2326+
int retval;
23312327
int sockstate;
23322328
_PySSLError err;
23332329
int nonblocking;
@@ -2390,8 +2386,8 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, int len, int group_right_1,
23902386

23912387
do {
23922388
PySSL_BEGIN_ALLOW_THREADS
2393-
count = SSL_read(self->ssl, mem, len);
2394-
err = _PySSL_errno(count <= 0, self->ssl, count);
2389+
retval = SSL_read_ex(self->ssl, mem, len, &count);
2390+
err = _PySSL_errno(retval == 0, self->ssl, retval);
23952391
PySSL_END_ALLOW_THREADS
23962392
self->err = err;
23972393

@@ -2424,8 +2420,8 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, int len, int group_right_1,
24242420
} while (err.ssl == SSL_ERROR_WANT_READ ||
24252421
err.ssl == SSL_ERROR_WANT_WRITE);
24262422

2427-
if (count <= 0) {
2428-
PySSL_SetError(self, count, __FILE__, __LINE__);
2423+
if (retval == 0) {
2424+
PySSL_SetError(self, retval, __FILE__, __LINE__);
24292425
goto error;
24302426
}
24312427
if (self->exc_type != NULL)
@@ -2438,7 +2434,7 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, int len, int group_right_1,
24382434
return dest;
24392435
}
24402436
else {
2441-
return PyLong_FromLong(count);
2437+
return PyLong_FromSize_t(count);
24422438
}
24432439

24442440
error:

0 commit comments

Comments
 (0)