cpython: a79003f25a41 (original) (raw)
--- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -803,6 +803,29 @@ the specification of normal, OS-level so SSL sockets also have the following additional methods and attributes: +.. method:: SSLSocket.read(len=0, buffer=None) +
- Read up to len bytes of data from the SSL socket and return the result as
- a
bytes
instance. If buffer is specified, then read into the buffer - instead, and return the number of bytes read. +
+.. method:: SSLSocket.write(buf) +
- Write buf to the SSL socket and return the number of bytes written. The
- buf argument must be an object supporting the buffer interface. +
- The :meth:
~SSLSocket.read
and :meth:~SSLSocket.write
methods are the - low-level methods that read and write unencrypted, application-level data
- and and decrypt/encrypt it to encrypted, wire-level data. These methods
- require an active SSL connection, i.e. the handshake was completed and
- :meth:
SSLSocket.unwrap
was not called. + - Normally you should use the socket API methods like
- :meth:
~socket.socket.recv
and :meth:~socket.socket.send
instead of these - methods. +
.. method:: SSLSocket.do_handshake() Perform the SSL setup handshake. @@ -935,6 +958,11 @@ SSL sockets also have the following addi .. versionadded:: 3.5 +.. method:: SSLSocket.pending() +
.. attribute:: SSLSocket.context
The :class:SSLContext
object this SSL socket is tied to. If the SSL
@@ -944,6 +972,22 @@ SSL sockets also have the following addi
.. versionadded:: 3.2
+.. attribute:: SSLSocket.server_side
+
- A boolean which is
True
for server-side sockets andFalse
for - client-side sockets. +
- .. versionadded:: 3.5 +
+.. attribute:: SSLSocket.server_hostname +
- A
bytes
instance containing the'idna'
encoded version of the - hostname specified in the server_hostname argument in
- :meth:
SSLContext.wrap_socket
. If no server_hostname was specified, this - attribute will be
None
. + - .. versionadded:: 3.5 +
SSL Contexts
------------
@@ -1670,6 +1714,130 @@ thus several things you need to be aware
select.select([], [sock], [])
+Memory BIO Support
+------------------
+
+.. versionadded:: 3.5
+
+Ever since the SSL module was introduced in Python 2.6, the :class:SSLSocket
+class has provided two related but distinct areas of functionality:
+
+- SSL protocol handling
+- Network IO
+
+The network IO API is identical to that provided by :class:socket.socket
,
+from which :class:SSLSocket
also inherits. This allows an SSL socket to be
+used as a drop-in replacement for a regular socket, making it very easy to add
+SSL support to an existing application.
+
+Combining SSL protocol handling and network IO usually works well, but there
+are some cases where it doesn't. An example is async IO frameworks that want to
+use a different IO multiplexing model than the "select/poll on a file
+descriptor" (readiness based) model that is assumed by :class:socket.socket
+and by the internal OpenSSL socket IO routines. This is mostly relevant for
+platforms like Windows where this model is not efficient. For this purpose, a
+reduced scope variant of :class:SSLSocket
called :class:SSLObject
is
+provided.
+
+.. class:: SSLObject
+
- A reduced-scope variant of :class:
SSLSocket
representing an SSL protocol - instance that does not contain any network IO methods. +
+The following methods are available from :class:SSLSocket
:
+
+- :attr:~SSLSocket.context
+- :attr:~SSLSocket.server_side
+- :attr:~SSLSocket.server_hostname
+- :meth:~SSLSocket.read
+- :meth:~SSLSocket.write
+- :meth:~SSLSocket.getpeercert
+- :meth:~SSLSocket.selected_npn_protocol
+- :meth:~SSLSocket.cipher
+- :meth:~SSLSocket.compression
+- :meth:~SSLSocket.pending
+- :meth:~SSLSocket.do_handshake
+- :meth:~SSLSocket.unwrap
+- :meth:~SSLSocket.get_channel_binding
+
+An SSLObject communicates with the outside world using memory buffers. The
+class :class:MemoryBIO
provides a memory buffer that can be used for this
+purpose. It wraps an OpenSSL memory BIO (Basic IO) object:
+
+.. class:: MemoryBIO
+
+.. attribute:: MemoryBIO.pending +
+.. attribute:: MemoryBIO.eof +
+.. method:: MemoryBIO.read(n=-1) +
- Read up to n bytes from the memory buffer. If n is not specified or
- negative, all bytes are returned. +
+.. method:: MemoryBIO.write(buf) +
- Write the bytes from buf to the memory BIO. The buf argument must be an
- object supporting the buffer protocol. +
- The return value is the number of bytes written, which is always equal to
- the length of buf. +
+.. method:: MemoryBIO.write_eof() +
- Write an EOF marker to the memory BIO. After this method has been called, it
- is illegal to call :meth:
~MemoryBIO.write
. The attribute :attr:eof
will - become true after all data currently in the buffer has been read. +
+An :class:SSLObject
instance can be created using the
+:meth:~SSLContext.wrap_bio
method. This method will create the
+:class:SSLObject
instance and bind it to a pair of BIOs. The incoming BIO
+is used to pass data from Python to the SSL protocol instance, while the
+outgoing BIO is used to pass data the other way around.
+
+.. method:: SSLContext.wrap_bio(incoming, outgoing, server_side=False, [](#l1.161)
server_hostname=None)[](#l1.162)
- Create a new :class:
SSLObject
instance by wrapping the BIO objects - incoming and outgoing. The SSL routines will read input data from the
- incoming BIO and write data to the outgoing BIO. +
- The server_side and server_hostname parameters have the same meaning as
- in :meth:
SSLContext.wrap_socket
. +
+Some notes related to the use of :class:SSLObject
:
+
+- All IO on an :class:SSLObject
is non-blocking. This means that for example
- :meth:
~SSLSocket.read
will raise an :exc:SSLWantReadError
if it needs - more data than the incoming BIO has available.
+
+- There is no module-level
wrap_bio
call like there is for - :meth:
~SSLContext.wrap_socket
. An :class:SSLObject
is always created via - an :class:
SSLContext
. + +- There is no do_handshake_on_connect machinery. You must always manually - call :meth:
~SSLSocket.do_handshake
to start the handshake. + +- There is no handling of suppress_ragged_eofs. All end-of-file conditions - that are in violation of the protocol are reported via the :exc:
SSLEOFError
- exception.
+
+- The method :meth:
~SSLSocket.unwrap
call does not return anything, unlike - for an SSL socket where it returns the underlying socket. + +- The server_name_callback callback passed to
- :meth:
SSLContext.set_servername_callback
will get an :class:SSLObject
- instance instead of a :class:
SSLSocket
instance as its first parameter. + + .. _ssl-security: Security considerations
--- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -97,7 +97,7 @@ from enum import Enum as _Enum, IntEnum import _ssl # if we can't import it, let the error propagate from _ssl import OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_INFO, OPENSSL_VERSION -from _ssl import _SSLContext +from _ssl import _SSLContext, MemoryBIO from _ssl import ( SSLError, SSLZeroReturnError, SSLWantReadError, SSLWantWriteError, SSLSyscallError, SSLEOFError, @@ -352,6 +352,12 @@ class SSLContext(_SSLContext): server_hostname=server_hostname, _context=self)
- def wrap_bio(self, incoming, outgoing, server_side=False,
server_hostname=None):[](#l2.17)
sslobj = self._wrap_bio(incoming, outgoing, server_side=server_side,[](#l2.18)
server_hostname=server_hostname)[](#l2.19)
return SSLObject(sslobj)[](#l2.20)
+ def set_npn_protocols(self, npn_protocols): protos = bytearray() for protocol in npn_protocols: @@ -469,6 +475,129 @@ def _create_stdlib_context(protocol=PROT return context + +class SSLObject:
- """This class implements an interface on top of a low-level SSL object as
- implemented by OpenSSL. This object captures the state of an SSL connection
- but does not provide any network IO itself. IO needs to be performed
- through separate "BIO" objects which are OpenSSL's IO abstraction layer.
- This class does not have a public constructor. Instances are returned by
SSLContext.wrap_bio
. This class is typically used by framework authors- that want to implement asynchronous IO for SSL through memory buffers.
* Any form of network IO incluging methods such as ``recv`` and ``send``.[](#l2.42)
* The ``do_handshake_on_connect`` and ``suppress_ragged_eofs`` machinery.[](#l2.43)
- """
- def init(self, sslobj, owner=None):
self._sslobj = sslobj[](#l2.47)
# Note: _sslobj takes a weak reference to owner[](#l2.48)
self._sslobj.owner = owner or self[](#l2.49)
- @property
- def context(self):
"""The SSLContext that is currently in use."""[](#l2.53)
return self._sslobj.context[](#l2.54)
- @property
- def server_side(self):
"""Whether this is a server-side socket."""[](#l2.62)
return self._sslobj.server_side[](#l2.63)
- @property
- def server_hostname(self):
"""The currently set server hostname (for SNI), or ``None`` if no[](#l2.67)
server hostame is set."""[](#l2.68)
return self._sslobj.server_hostname[](#l2.69)
- def read(self, len=0, buffer=None):
"""Read up to 'len' bytes from the SSL object and return them.[](#l2.72)
If 'buffer' is provided, read into this buffer and return the number of[](#l2.74)
bytes read.[](#l2.75)
"""[](#l2.76)
if buffer is not None:[](#l2.77)
v = self._sslobj.read(len, buffer)[](#l2.78)
else:[](#l2.79)
v = self._sslobj.read(len or 1024)[](#l2.80)
return v[](#l2.81)
- def write(self, data):
"""Write 'data' to the SSL object and return the number of bytes[](#l2.84)
written.[](#l2.85)
The 'data' argument must support the buffer interface.[](#l2.87)
"""[](#l2.88)
return self._sslobj.write(data)[](#l2.89)
- def getpeercert(self, binary_form=False):
"""Returns a formatted version of the data in the certificate provided[](#l2.92)
by the other end of the SSL channel.[](#l2.93)
Return None if no certificate was provided, {} if a certificate was[](#l2.95)
provided, but not validated.[](#l2.96)
"""[](#l2.97)
return self._sslobj.peer_certificate(binary_form)[](#l2.98)
- def selected_npn_protocol(self):
"""Return the currently selected NPN protocol as a string, or ``None``[](#l2.101)
if a next protocol was not negotiated or if NPN is not supported by one[](#l2.102)
of the peers."""[](#l2.103)
if _ssl.HAS_NPN:[](#l2.104)
return self._sslobj.selected_npn_protocol()[](#l2.105)
- def cipher(self):
"""Return the currently selected cipher as a 3-tuple ``(name,[](#l2.108)
ssl_version, secret_bits)``."""[](#l2.109)
return self._sslobj.cipher()[](#l2.110)
- def compression(self):
"""Return the current compression algorithm in use, or ``None`` if[](#l2.113)
compression was not negotiated or not supported by one of the peers."""[](#l2.114)
return self._sslobj.compression()[](#l2.115)
- def pending(self):
"""Return the number of bytes that can be read immediately."""[](#l2.118)
return self._sslobj.pending()[](#l2.119)
- def do_handshake(self, block=False):
"""Start the SSL/TLS handshake."""[](#l2.122)
self._sslobj.do_handshake()[](#l2.123)
if self.context.check_hostname:[](#l2.124)
if not self.server_hostname:[](#l2.125)
raise ValueError("check_hostname needs server_hostname "[](#l2.126)
"argument")[](#l2.127)
match_hostname(self.getpeercert(), self.server_hostname)[](#l2.128)
- def unwrap(self):
"""Start the SSL shutdown handshake."""[](#l2.131)
return self._sslobj.shutdown()[](#l2.132)
- def get_channel_binding(self, cb_type="tls-unique"):
"""Get channel binding data for current connection. Raise ValueError[](#l2.135)
if the requested `cb_type` is not supported. Return bytes of the data[](#l2.136)
or None if the data is not available (e.g. before the handshake)."""[](#l2.137)
if cb_type not in CHANNEL_BINDING_TYPES:[](#l2.138)
raise ValueError("Unsupported channel binding type")[](#l2.139)
if cb_type != "tls-unique":[](#l2.140)
raise NotImplementedError([](#l2.141)
"{0} channel binding type not implemented"[](#l2.142)
.format(cb_type))[](#l2.143)
return self._sslobj.tls_unique_cb()[](#l2.144)
- def version(self):
"""Return a string identifying the protocol version used by the[](#l2.147)
current SSL channel. """[](#l2.148)
return self._sslobj.version()[](#l2.149)
+ + class SSLSocket(socket): """This class implements a subtype of socket.socket that wraps the underlying OS socket in an SSL context when necessary, and @@ -556,8 +685,9 @@ class SSLSocket(socket): if connected: # create the SSL object try:
self._sslobj = self._context._wrap_socket(self, server_side,[](#l2.159)
server_hostname)[](#l2.160)
sslobj = self._context._wrap_socket(self, server_side,[](#l2.161)
server_hostname)[](#l2.162)
self._sslobj = SSLObject(sslobj, owner=self)[](#l2.163) if do_handshake_on_connect:[](#l2.164) timeout = self.gettimeout()[](#l2.165) if timeout == 0.0:[](#l2.166)
@@ -602,11 +732,7 @@ class SSLSocket(socket): if not self._sslobj: raise ValueError("Read on closed or unwrapped SSL socket.") try:
if buffer is not None:[](#l2.171)
v = self._sslobj.read(len, buffer)[](#l2.172)
else:[](#l2.173)
v = self._sslobj.read(len or 1024)[](#l2.174)
return v[](#l2.175)
return self._sslobj.read(len, buffer)[](#l2.176) except SSLError as x:[](#l2.177) if x.args[0] == SSL_ERROR_EOF and self.suppress_ragged_eofs:[](#l2.178) if buffer is not None:[](#l2.179)
@@ -633,7 +759,7 @@ class SSLSocket(socket): self._checkClosed() self._check_connected()
return self._sslobj.peer_certificate(binary_form)[](#l2.184)
return self._sslobj.getpeercert(binary_form)[](#l2.185)
def selected_npn_protocol(self): self._checkClosed() @@ -773,7 +899,7 @@ class SSLSocket(socket): def unwrap(self): if self._sslobj:
s = self._sslobj.shutdown()[](#l2.193)
s = self._sslobj.unwrap()[](#l2.194) self._sslobj = None[](#l2.195) return s[](#l2.196) else:[](#l2.197)
@@ -794,12 +920,6 @@ class SSLSocket(socket): finally: self.settimeout(timeout)
if self.context.check_hostname:[](#l2.202)
if not self.server_hostname:[](#l2.203)
raise ValueError("check_hostname needs server_hostname "[](#l2.204)
"argument")[](#l2.205)
match_hostname(self.getpeercert(), self.server_hostname)[](#l2.206)
- def _real_connect(self, addr, connect_ex): if self.server_side: raise ValueError("can't connect in server-side mode") @@ -807,7 +927,8 @@ class SSLSocket(socket): # connected at the time of the call. We connect it, then wrap it. if self._connected: raise ValueError("attempt to connect already-connected SSLSocket!")
self._sslobj = self.context._wrap_socket(self, False, self.server_hostname)[](#l2.215)
sslobj = self.context._wrap_socket(self, False, self.server_hostname)[](#l2.216)
self._sslobj = SSLObject(sslobj, owner=self)[](#l2.217) try:[](#l2.218) if connect_ex:[](#l2.219) rc = socket.connect_ex(self, addr)[](#l2.220)
@@ -850,15 +971,9 @@ class SSLSocket(socket):
if the requested cb_type
is not supported. Return bytes of the data
or None if the data is not available (e.g. before the handshake).
"""
if cb_type not in CHANNEL_BINDING_TYPES:[](#l2.225)
raise ValueError("Unsupported channel binding type")[](#l2.226)
if cb_type != "tls-unique":[](#l2.227)
raise NotImplementedError([](#l2.228)
"{0} channel binding type not implemented"[](#l2.229)
.format(cb_type))[](#l2.230) if self._sslobj is None:[](#l2.231) return None[](#l2.232)
return self._sslobj.tls_unique_cb()[](#l2.233)
return self._sslobj.get_channel_binding(cb_type)[](#l2.234)
--- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -518,9 +518,14 @@ class BasicSocketTests(unittest.TestCase def test_unknown_channel_binding(self): # should raise ValueError for unknown type s = socket.socket(socket.AF_INET)
with ssl.wrap_socket(s) as ss:[](#l3.7)
s.bind(('127.0.0.1', 0))[](#l3.8)
s.listen()[](#l3.9)
c = socket.socket(socket.AF_INET)[](#l3.10)
c.connect(s.getsockname())[](#l3.11)
with ssl.wrap_socket(c, do_handshake_on_connect=False) as ss:[](#l3.12) with self.assertRaises(ValueError):[](#l3.13) ss.get_channel_binding("unknown-type")[](#l3.14)
s.close()[](#l3.15)
@unittest.skipUnless("tls-unique" in ssl.CHANNEL_BINDING_TYPES, "'tls-unique' channel binding not available") @@ -1247,6 +1252,69 @@ class SSLErrorTests(unittest.TestCase): self.assertEqual(cm.exception.errno, ssl.SSL_ERROR_WANT_READ) +class MemoryBIOTests(unittest.TestCase): +
- def test_read_write(self):
bio = ssl.MemoryBIO()[](#l3.26)
bio.write(b'foo')[](#l3.27)
self.assertEqual(bio.read(), b'foo')[](#l3.28)
self.assertEqual(bio.read(), b'')[](#l3.29)
bio.write(b'foo')[](#l3.30)
bio.write(b'bar')[](#l3.31)
self.assertEqual(bio.read(), b'foobar')[](#l3.32)
self.assertEqual(bio.read(), b'')[](#l3.33)
bio.write(b'baz')[](#l3.34)
self.assertEqual(bio.read(2), b'ba')[](#l3.35)
self.assertEqual(bio.read(1), b'z')[](#l3.36)
self.assertEqual(bio.read(1), b'')[](#l3.37)
- def test_eof(self):
bio = ssl.MemoryBIO()[](#l3.40)
self.assertFalse(bio.eof)[](#l3.41)
self.assertEqual(bio.read(), b'')[](#l3.42)
self.assertFalse(bio.eof)[](#l3.43)
bio.write(b'foo')[](#l3.44)
self.assertFalse(bio.eof)[](#l3.45)
bio.write_eof()[](#l3.46)
self.assertFalse(bio.eof)[](#l3.47)
self.assertEqual(bio.read(2), b'fo')[](#l3.48)
self.assertFalse(bio.eof)[](#l3.49)
self.assertEqual(bio.read(1), b'o')[](#l3.50)
self.assertTrue(bio.eof)[](#l3.51)
self.assertEqual(bio.read(), b'')[](#l3.52)
self.assertTrue(bio.eof)[](#l3.53)
- def test_pending(self):
bio = ssl.MemoryBIO()[](#l3.56)
self.assertEqual(bio.pending, 0)[](#l3.57)
bio.write(b'foo')[](#l3.58)
self.assertEqual(bio.pending, 3)[](#l3.59)
for i in range(3):[](#l3.60)
bio.read(1)[](#l3.61)
self.assertEqual(bio.pending, 3-i-1)[](#l3.62)
for i in range(3):[](#l3.63)
bio.write(b'x')[](#l3.64)
self.assertEqual(bio.pending, i+1)[](#l3.65)
bio.read()[](#l3.66)
self.assertEqual(bio.pending, 0)[](#l3.67)
- def test_buffer_types(self):
bio = ssl.MemoryBIO()[](#l3.70)
bio.write(b'foo')[](#l3.71)
self.assertEqual(bio.read(), b'foo')[](#l3.72)
bio.write(bytearray(b'bar'))[](#l3.73)
self.assertEqual(bio.read(), b'bar')[](#l3.74)
bio.write(memoryview(b'baz'))[](#l3.75)
self.assertEqual(bio.read(), b'baz')[](#l3.76)
- def test_error_types(self):
bio = ssl.MemoryBIO()[](#l3.79)
self.assertRaises(TypeError, bio.write, 'foo')[](#l3.80)
self.assertRaises(TypeError, bio.write, None)[](#l3.81)
self.assertRaises(TypeError, bio.write, True)[](#l3.82)
self.assertRaises(TypeError, bio.write, 1)[](#l3.83)
+ + class NetworkedTests(unittest.TestCase): def test_connect(self): @@ -1577,6 +1645,95 @@ class NetworkedTests(unittest.TestCase): self.assertIs(ss.context, ctx2) self.assertIs(ss._sslobj.context, ctx2) + +class NetworkedBIOTests(unittest.TestCase): +
- def ssl_io_loop(self, sock, incoming, outgoing, func, *args, **kwargs):
# A simple IO loop. Call func(*args) depending on the error we get[](#l3.97)
# (WANT_READ or WANT_WRITE) move data between the socket and the BIOs.[](#l3.98)
timeout = kwargs.get('timeout', 10)[](#l3.99)
count = 0[](#l3.100)
while True:[](#l3.101)
errno = None[](#l3.102)
count += 1[](#l3.103)
try:[](#l3.104)
ret = func(*args)[](#l3.105)
except ssl.SSLError as e:[](#l3.106)
# Note that we get a spurious -1/SSL_ERROR_SYSCALL for[](#l3.107)
# non-blocking IO. The SSL_shutdown manpage hints at this.[](#l3.108)
# It *should* be safe to just ignore SYS_ERROR_SYSCALL because[](#l3.109)
# with a Memory BIO there's no syscalls (for IO at least).[](#l3.110)
if e.errno not in (ssl.SSL_ERROR_WANT_READ,[](#l3.111)
ssl.SSL_ERROR_WANT_WRITE,[](#l3.112)
ssl.SSL_ERROR_SYSCALL):[](#l3.113)
raise[](#l3.114)
errno = e.errno[](#l3.115)
# Get any data from the outgoing BIO irrespective of any error, and[](#l3.116)
# send it to the socket.[](#l3.117)
buf = outgoing.read()[](#l3.118)
sock.sendall(buf)[](#l3.119)
# If there's no error, we're done. For WANT_READ, we need to get[](#l3.120)
# data from the socket and put it in the incoming BIO.[](#l3.121)
if errno is None:[](#l3.122)
break[](#l3.123)
elif errno == ssl.SSL_ERROR_WANT_READ:[](#l3.124)
buf = sock.recv(32768)[](#l3.125)
if buf:[](#l3.126)
incoming.write(buf)[](#l3.127)
else:[](#l3.128)
incoming.write_eof()[](#l3.129)
if support.verbose:[](#l3.130)
sys.stdout.write("Needed %d calls to complete %s().\n"[](#l3.131)
% (count, func.__name__))[](#l3.132)
return ret[](#l3.133)
- def test_handshake(self):
with support.transient_internet("svn.python.org"):[](#l3.136)
sock = socket.socket(socket.AF_INET)[](#l3.137)
sock.connect(("svn.python.org", 443))[](#l3.138)
incoming = ssl.MemoryBIO()[](#l3.139)
outgoing = ssl.MemoryBIO()[](#l3.140)
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)[](#l3.141)
ctx.verify_mode = ssl.CERT_REQUIRED[](#l3.142)
ctx.load_verify_locations(SVN_PYTHON_ORG_ROOT_CERT)[](#l3.143)
if ssl.HAS_SNI:[](#l3.144)
ctx.check_hostname = True[](#l3.145)
sslobj = ctx.wrap_bio(incoming, outgoing, False, 'svn.python.org')[](#l3.146)
else:[](#l3.147)
ctx.check_hostname = False[](#l3.148)
sslobj = ctx.wrap_bio(incoming, outgoing, False)[](#l3.149)
self.assertIs(sslobj._sslobj.owner, sslobj)[](#l3.150)
self.assertIsNone(sslobj.cipher())[](#l3.151)
self.assertRaises(ValueError, sslobj.getpeercert)[](#l3.152)
if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES:[](#l3.153)
self.assertIsNone(sslobj.get_channel_binding('tls-unique'))[](#l3.154)
self.ssl_io_loop(sock, incoming, outgoing, sslobj.do_handshake)[](#l3.155)
self.assertTrue(sslobj.cipher())[](#l3.156)
self.assertTrue(sslobj.getpeercert())[](#l3.157)
if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES:[](#l3.158)
self.assertTrue(sslobj.get_channel_binding('tls-unique'))[](#l3.159)
self.ssl_io_loop(sock, incoming, outgoing, sslobj.unwrap)[](#l3.160)
self.assertRaises(ssl.SSLError, sslobj.write, b'foo')[](#l3.161)
sock.close()[](#l3.162)
- def test_read_write_data(self):
with support.transient_internet("svn.python.org"):[](#l3.165)
sock = socket.socket(socket.AF_INET)[](#l3.166)
sock.connect(("svn.python.org", 443))[](#l3.167)
incoming = ssl.MemoryBIO()[](#l3.168)
outgoing = ssl.MemoryBIO()[](#l3.169)
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)[](#l3.170)
ctx.verify_mode = ssl.CERT_NONE[](#l3.171)
sslobj = ctx.wrap_bio(incoming, outgoing, False)[](#l3.172)
self.ssl_io_loop(sock, incoming, outgoing, sslobj.do_handshake)[](#l3.173)
req = b'GET / HTTP/1.0\r\n\r\n'[](#l3.174)
self.ssl_io_loop(sock, incoming, outgoing, sslobj.write, req)[](#l3.175)
buf = self.ssl_io_loop(sock, incoming, outgoing, sslobj.read, 1024)[](#l3.176)
self.assertEqual(buf[:5], b'HTTP/')[](#l3.177)
self.ssl_io_loop(sock, incoming, outgoing, sslobj.unwrap)[](#l3.178)
sock.close()[](#l3.179)
+ + try: import threading except ImportError: @@ -3061,10 +3218,11 @@ def test_main(verbose=False): if not os.path.exists(filename): raise support.TestFailed("Can't read certificate file %r" % filename)
if support.is_resource_enabled('network'): tests.append(NetworkedTests)
tests.append(NetworkedBIOTests)[](#l3.194)
if _have_threads: thread_info = support.threading_setup()
--- a/Misc/NEWS +++ b/Misc/NEWS @@ -166,6 +166,9 @@ Core and Builtins Library ------- +- Issue #21965: Add support for in-memory SSL to the ssl module. Patch
--- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -64,6 +64,7 @@ static PySocketModule_APIObject PySocket #include "openssl/ssl.h" #include "openssl/err.h" #include "openssl/rand.h" +#include "openssl/bio.h" /* SSL error object */ static PyObject *PySSLErrorObject; @@ -226,10 +227,19 @@ typedef struct { char shutdown_seen_zero; char handshake_done; enum py_ssl_server_or_client socket_type;
} PySSLSocket; +typedef struct {
+} PySSLMemoryBIO; + static PyTypeObject PySSLContext_Type; static PyTypeObject PySSLSocket_Type; +static PyTypeObject PySSLMemoryBIO_Type; static PyObject *PySSL_SSLwrite(PySSLSocket *self, PyObject *args); static PyObject *PySSL_SSLread(PySSLSocket *self, PyObject *args); @@ -240,6 +250,7 @@ static PyObject PySSL_cipher(PySSLSocke #define PySSLContext_Check(v) (Py_TYPE(v) == &PySSLContext_Type) #define PySSLSocket_Check(v) (Py_TYPE(v) == &PySSLSocket_Type) +#define PySSLMemoryBIO_Check(v) (Py_TYPE(v) == &PySSLMemoryBIO_Type) typedef enum { SOCKET_IS_NONBLOCKING, @@ -254,6 +265,9 @@ typedef enum { #define ERRSTR1(x,y,z) (x ":" y ": " z) #define ERRSTR(x) ERRSTR1("_ssl.c", Py_STRINGIFY(LINE), x) +/ Get the socket from a PySSLSocket, if it has one */ +#define GET_SOCKET(obj) ((obj)->Socket ? [](#l5.44)
- SSL errors. @@ -417,13 +431,12 @@ PySSL_SetError(PySSLSocket *obj, int ret case SSL_ERROR_SYSCALL: { if (e == 0) {
PySocketSockObject *s[](#l5.53)
= (PySocketSockObject *) PyWeakref_GetObject(obj->Socket);[](#l5.54)
PySocketSockObject *s = GET_SOCKET(obj);[](#l5.55) if (ret == 0 || (((PyObject *)s) == Py_None)) {[](#l5.56) p = PY_SSL_ERROR_EOF;[](#l5.57) type = PySSLEOFErrorObject;[](#l5.58) errstr = "EOF occurred in violation of protocol";[](#l5.59)
} else if (ret == -1) {[](#l5.60)
} else if (s && ret == -1) {[](#l5.61) /* underlying BIO reported an I/O error */[](#l5.62) Py_INCREF(s);[](#l5.63) ERR_clear_error();[](#l5.64)
@@ -477,10 +490,12 @@ static PyObject * static PySSLSocket * newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, enum py_ssl_server_or_client socket_type,
char *server_hostname)[](#l5.69)
char *server_hostname,[](#l5.70)
PySSLMemoryBIO *inbio, PySSLMemoryBIO *outbio)[](#l5.71)
{ PySSLSocket *self; SSL_CTX *ctx = sslctx->ctx;
- PyObject *hostname; long mode; self = PyObject_New(PySSLSocket, &PySSLSocket_Type); @@ -493,6 +508,18 @@ newPySSLSocket(PySSLContext *sslctx, PyS self->ctx = sslctx; self->shutdown_seen_zero = 0; self->handshake_done = 0;
- self->owner = NULL;
- if (server_hostname != NULL) {
hostname = PyUnicode_Decode(server_hostname, strlen(server_hostname),[](#l5.85)
"idna", "strict");[](#l5.86)
if (hostname == NULL) {[](#l5.87)
Py_DECREF(self);[](#l5.88)
return NULL;[](#l5.89)
}[](#l5.90)
self->server_hostname = hostname;[](#l5.91)
- } else
self->server_hostname = NULL;[](#l5.93)
+ Py_INCREF(sslctx); /* Make sure the SSL error state is initialized */ @@ -502,8 +529,17 @@ newPySSLSocket(PySSLContext *sslctx, PyS PySSL_BEGIN_ALLOW_THREADS self->ssl = SSL_new(ctx); PySSL_END_ALLOW_THREADS
- SSL_set_app_data(self->ssl,self);
- SSL_set_fd(self->ssl, Py_SAFE_DOWNCAST(sock->sock_fd, SOCKET_T, int));
- SSL_set_app_data(self->ssl, self);
- if (sock) {
SSL_set_fd(self->ssl, Py_SAFE_DOWNCAST(sock->sock_fd, SOCKET_T, int));[](#l5.106)
- } else {
/* BIOs are reference counted and SSL_set_bio borrows our reference.[](#l5.108)
* To prevent a double free in memory_bio_dealloc() we need to take an[](#l5.109)
* extra reference here. */[](#l5.110)
CRYPTO_add(&inbio->bio->references, 1, CRYPTO_LOCK_BIO);[](#l5.111)
CRYPTO_add(&outbio->bio->references, 1, CRYPTO_LOCK_BIO);[](#l5.112)
SSL_set_bio(self->ssl, inbio->bio, outbio->bio);[](#l5.113)
- } mode = SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER;
#ifdef SSL_MODE_AUTO_RETRY mode |= SSL_MODE_AUTO_RETRY; @@ -518,7 +554,7 @@ newPySSLSocket(PySSLContext sslctx, PyS / If the socket is in non-blocking mode or timeout mode, set the BIO * to non-blocking mode (blocking is the default) */
- if (sock && sock->sock_timeout >= 0.0) { BIO_set_nbio(SSL_get_rbio(self->ssl), 1); BIO_set_nbio(SSL_get_wbio(self->ssl), 1); } @@ -531,10 +567,13 @@ newPySSLSocket(PySSLContext *sslctx, PyS PySSL_END_ALLOW_THREADS self->socket_type = socket_type;
- self->Socket = PyWeakref_NewRef((PyObject *) sock, NULL);
- if (self->Socket == NULL) {
Py_DECREF(self);[](#l5.133)
return NULL;[](#l5.134)
- if (sock != NULL) {
self->Socket = PyWeakref_NewRef((PyObject *) sock, NULL);[](#l5.136)
if (self->Socket == NULL) {[](#l5.137)
Py_DECREF(self);[](#l5.138)
Py_XDECREF(self->server_hostname);[](#l5.139)
return NULL;[](#l5.140)
} return self; } @@ -546,20 +585,21 @@ static PyObject *PySSL_SSLdo_handshake(P int ret; int err; int sockstate, nonblocking;}[](#l5.141)
- if (((PyObject*)sock) == Py_None) {
_setSSLError("Underlying socket connection gone",[](#l5.153)
PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__);[](#l5.154)
return NULL;[](#l5.155)
- if (sock) {
if (((PyObject*)sock) == Py_None) {[](#l5.159)
_setSSLError("Underlying socket connection gone",[](#l5.160)
PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__);[](#l5.161)
return NULL;[](#l5.162)
}[](#l5.163)
Py_INCREF(sock);[](#l5.164)
/* just in case the blocking state of the socket has been changed */[](#l5.166)
nonblocking = (sock->sock_timeout >= 0.0);[](#l5.167)
BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking);[](#l5.168)
}BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking);[](#l5.169)
- /* just in case the blocking state of the socket has been changed */
- nonblocking = (sock->sock_timeout >= 0.0);
- BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking);
- BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking);
/* Actually negotiate SSL connection / / XXX If SSL_do_handshake() returns 0, it's also a failure. */ @@ -593,7 +633,7 @@ static PyObject *PySSL_SSLdo_handshake(P break; } } while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE);
@@ -608,7 +648,7 @@ static PyObject *PySSL_SSLdo_handshake(P return Py_None; error:
@@ -1483,6 +1523,54 @@ on the SSLContext to change the certific SSLSocket before the cryptographic exchange handshake messages\n"); +static PyObject * +PySSL_get_server_side(PySSLSocket *self, void *c) +{
+} + +PyDoc_STRVAR(PySSL_get_server_side_doc, +"Whether this is a server-side socket."); + +static PyObject * +PySSL_get_server_hostname(PySSLSocket *self, void *c) +{
- if (self->server_hostname == NULL)
Py_RETURN_NONE;[](#l5.215)
- Py_INCREF(self->server_hostname);
- return self->server_hostname;
+} + +PyDoc_STRVAR(PySSL_get_server_hostname_doc, +"The currently set server hostname (for SNI)."); + +static PyObject * +PySSL_get_owner(PySSLSocket *self, void *c) +{
+} + +static int +PySSL_set_owner(PySSLSocket *self, PyObject *value, void *c) +{
- Py_XDECREF(self->owner);
- self->owner = PyWeakref_NewRef(value, NULL);
- if (self->owner == NULL)
return -1;[](#l5.242)
- return 0;
+} + +PyDoc_STRVAR(PySSL_get_owner_doc, +"The Python-level owner of this object.[](#l5.247) +Passed as "self" in servername callback."); + static void PySSL_dealloc(PySSLSocket *self) { @@ -1492,6 +1580,8 @@ static void PySSL_dealloc(PySSLSocket *s SSL_free(self->ssl); Py_XDECREF(self->Socket); Py_XDECREF(self->ctx);
@@ -1508,10 +1598,10 @@ check_socket_and_wait_for_timeout(PySock int rc; /* Nothing to do unless we're in timeout mode (not non-blocking) */
- if ((s == NULL) || (s->sock_timeout == 0.0))
return SOCKET_IS_NONBLOCKING;[](#l5.268)
- else if (s->sock_timeout < 0.0) return SOCKET_IS_BLOCKING;
/* Guard against closed socket */ if (s->sock_fd < 0) @@ -1572,18 +1662,19 @@ static PyObject *PySSL_SSLwrite(PySSLSoc int sockstate; int err; int nonblocking;
- if (((PyObject*)sock) == Py_None) {
_setSSLError("Underlying socket connection gone",[](#l5.284)
PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__);[](#l5.285)
return NULL;[](#l5.286)
- if (sock != NULL) {
if (((PyObject*)sock) == Py_None) {[](#l5.290)
_setSSLError("Underlying socket connection gone",[](#l5.291)
PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__);[](#l5.292)
return NULL;[](#l5.293)
}[](#l5.294)
}Py_INCREF(sock);[](#l5.295)
if (!PyArg_ParseTuple(args, "y*:write", &buf)) {
Py_DECREF(sock);[](#l5.300)
} @@ -1593,10 +1684,12 @@ static PyObject *PySSL_SSLwrite(PySSLSoc goto error; }Py_XDECREF(sock);[](#l5.301) return NULL;[](#l5.302)
- /* just in case the blocking state of the socket has been changed */
- nonblocking = (sock->sock_timeout >= 0.0);
- BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking);
- BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking);
- if (sock != NULL) {
/* just in case the blocking state of the socket has been changed */[](#l5.314)
nonblocking = (sock->sock_timeout >= 0.0);[](#l5.315)
BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking);[](#l5.316)
BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking);[](#l5.317)
- }
sockstate = check_socket_and_wait_for_timeout(sock, 1); if (sockstate == SOCKET_HAS_TIMED_OUT) { @@ -1640,7 +1733,7 @@ static PyObject *PySSL_SSLwrite(PySSLSoc } } while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE);
@@ -1648,7 +1741,7 @@ static PyObject *PySSL_SSLwrite(PySSLSoc return PySSL_SetError(self, len, FILE, LINE); error:
@@ -1688,15 +1781,16 @@ static PyObject *PySSL_SSLread(PySSLSock int sockstate; int err; int nonblocking;
- if (((PyObject*)sock) == Py_None) {
_setSSLError("Underlying socket connection gone",[](#l5.348)
PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__);[](#l5.349)
return NULL;[](#l5.350)
- if (sock != NULL) {
if (((PyObject*)sock) == Py_None) {[](#l5.354)
_setSSLError("Underlying socket connection gone",[](#l5.355)
PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__);[](#l5.356)
return NULL;[](#l5.357)
}[](#l5.358)
}Py_INCREF(sock);[](#l5.359)
buf.obj = NULL; buf.buf = NULL; @@ -1722,10 +1816,12 @@ static PyObject *PySSL_SSLread(PySSLSock } }
- /* just in case the blocking state of the socket has been changed */
- nonblocking = (sock->sock_timeout >= 0.0);
- BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking);
- BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking);
- if (sock != NULL) {
/* just in case the blocking state of the socket has been changed */[](#l5.374)
nonblocking = (sock->sock_timeout >= 0.0);[](#l5.375)
BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking);[](#l5.376)
BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking);[](#l5.377)
- }
/* first check if there are bytes ready to be read */ PySSL_BEGIN_ALLOW_THREADS @@ -1781,7 +1877,7 @@ static PyObject *PySSL_SSLread(PySSLSock } done:
@@ -1792,7 +1888,7 @@ done: } error:
- Py_XDECREF(sock); if (!buf_passed) Py_XDECREF(dest); else @@ -1809,21 +1905,22 @@ static PyObject *PySSL_SSLshutdown(PySSL { int err, ssl_err, sockstate, nonblocking; int zeros = 0;
- /* Guard against closed socket */
- if ((((PyObject*)sock) == Py_None) || (sock->sock_fd < 0)) {
_setSSLError("Underlying socket connection gone",[](#l5.409)
PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__);[](#l5.410)
return NULL;[](#l5.411)
- if (sock != NULL) {
/* Guard against closed socket */[](#l5.415)
if ((((PyObject*)sock) == Py_None) || (sock->sock_fd < 0)) {[](#l5.416)
_setSSLError("Underlying socket connection gone",[](#l5.417)
PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__);[](#l5.418)
return NULL;[](#l5.419)
}[](#l5.420)
Py_INCREF(sock);[](#l5.421)
/* Just in case the blocking state of the socket has been changed */[](#l5.423)
nonblocking = (sock->sock_timeout >= 0.0);[](#l5.424)
BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking);[](#l5.425)
}BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking);[](#l5.426)
- /* Just in case the blocking state of the socket has been changed */
- nonblocking = (sock->sock_timeout >= 0.0);
- BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking);
- BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking);
while (1) { PySSL_BEGIN_ALLOW_THREADS @@ -1881,15 +1978,17 @@ static PyObject *PySSL_SSLshutdown(PySSL } if (err < 0) {
Py_DECREF(sock);[](#l5.441)
@@ -1937,6 +2036,12 @@ If the TLS handshake is not yet complete static PyGetSetDef ssl_getsetlist[] = { {"context", (getter) PySSL_get_context, (setter) PySSL_set_context, PySSL_set_context_doc},
- {"server_side", (getter) PySSL_get_server_side, NULL,
PySSL_get_server_side_doc},[](#l5.463)
- {"server_hostname", (getter) PySSL_get_server_hostname, NULL,
PySSL_get_server_hostname_doc},[](#l5.465)
- {"owner", (getter) PySSL_get_owner, (setter) PySSL_set_owner,
{NULL}, /* sentinel */ }; @@ -2825,14 +2930,49 @@ context_wrap_socket(PySSLContext *self,PySSL_get_owner_doc},[](#l5.467)
- res = (PyObject *) newPySSLSocket(self, sock, server_side, hostname,
if (hostname != NULL) PyMem_Free(hostname); return res; } static PyObject * +context_wrap_bio(PySSLContext *self, PyObject *args, PyObject *kwds) +{NULL, NULL);[](#l5.478)
- char *kwlist[] = {"incoming", "outgoing", "server_side",
"server_hostname", NULL};[](#l5.488)
- int server_side;
- char *hostname = NULL;
- PyObject *hostname_obj = Py_None, *res;
- PySSLMemoryBIO *incoming, *outgoing;
- /* server_hostname is either None (or absent), or to be encoded
using the idna encoding. */[](#l5.495)
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O!i|O:_wrap_bio", kwlist,
&PySSLMemoryBIO_Type, &incoming,[](#l5.497)
&PySSLMemoryBIO_Type, &outgoing,[](#l5.498)
&server_side, &hostname_obj))[](#l5.499)
return NULL;[](#l5.500)
- if (hostname_obj != Py_None) {
if (!PyArg_Parse(hostname_obj, "et", "idna", &hostname))[](#l5.503)
return NULL;[](#l5.504)
PyErr_SetString(PyExc_ValueError, "server_hostname is not supported "[](#l5.506)
"by your OpenSSL library");[](#l5.507)
return NULL;[](#l5.508)
- res = (PyObject *) newPySSLSocket(self, NULL, server_side, hostname,
incoming, outgoing);[](#l5.513)
+} + +static PyObject * session_stats(PySSLContext *self, PyObject *unused) { int r; @@ -2938,11 +3078,25 @@ static int ssl = SSL_get_app_data(s); assert(PySSLSocket_Check(ssl));
- /* The servername callback expects a argument that represents the current
* SSL connection and that has a .context attribute that can be changed to[](#l5.530)
* identify the requested hostname. Since the official API is the Python[](#l5.531)
* level API we want to pass the callback a Python level object rather than[](#l5.532)
* a _ssl.SSLSocket instance. If there's an "owner" (typically an[](#l5.533)
* SSLObject) that will be passed. Otherwise if there's a socket then that[](#l5.534)
* will be passed. If both do not exist only then the C-level object is[](#l5.535)
* passed. */[](#l5.536)
- if (ssl->owner)
ssl_socket = PyWeakref_GetObject(ssl->owner);[](#l5.538)
- else if (ssl->Socket)
ssl_socket = PyWeakref_GetObject(ssl->Socket);[](#l5.540)
- else
ssl_socket = (PyObject *) ssl;[](#l5.542)
if (servername == NULL) { result = PyObject_CallFunctionObjArgs(ssl_ctx->set_hostname, ssl_socket, @@ -3171,6 +3325,8 @@ static PyGetSetDef context_getsetlist[] static struct PyMethodDef context_methods[] = { {"_wrap_socket", (PyCFunction) context_wrap_socket, METH_VARARGS | METH_KEYWORDS, NULL},
- {"_wrap_bio", (PyCFunction) context_wrap_bio,
{"set_ciphers", (PyCFunction) set_ciphers, METH_VARARGS, NULL}, {"_set_npn_protocols", (PyCFunction) _set_npn_protocols, @@ -3240,6 +3396,225 @@ static PyTypeObject PySSLContext_Type = }; +/*METH_VARARGS | METH_KEYWORDS, NULL},[](#l5.557)
- */ + +static PyObject * +memory_bio_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{
- char *kwlist[] = {NULL};
- BIO *bio;
- PySSLMemoryBIO *self;
- bio = BIO_new(BIO_s_mem());
- if (bio == NULL) {
PyErr_SetString(PySSLErrorObject,[](#l5.581)
"failed to allocate BIO");[](#l5.582)
return NULL;[](#l5.583)
- }
- /* Since our BIO is non-blocking an empty read() does not indicate EOF,
* just that no data is currently available. The SSL routines should retry[](#l5.586)
* the read, which we can achieve by calling BIO_set_retry_read(). */[](#l5.587)
- BIO_set_retry_read(bio);
- BIO_set_mem_eof_return(bio, -1);
- assert(type != NULL && type->tp_alloc != NULL);
- self = (PySSLMemoryBIO *) type->tp_alloc(type, 0);
- if (self == NULL) {
BIO_free(bio);[](#l5.594)
return NULL;[](#l5.595)
- }
- self->bio = bio;
- self->eof_written = 0;
+} + +static void +memory_bio_dealloc(PySSLMemoryBIO *self) +{
+} + +static PyObject * +memory_bio_get_pending(PySSLMemoryBIO *self, void *c) +{
+} + +PyDoc_STRVAR(PySSL_memory_bio_pending_doc, +"The number of bytes pending in the memory BIO."); + +static PyObject * +memory_bio_get_eof(PySSLMemoryBIO *self, void *c) +{
+} + +PyDoc_STRVAR(PySSL_memory_bio_eof_doc, +"Whether the memory BIO is at EOF."); + +static PyObject * +memory_bio_read(PySSLMemoryBIO *self, PyObject *args) +{
- result = PyBytes_FromStringAndSize(NULL, len);
- if ((result == NULL) || (len == 0))
return result;[](#l5.644)
- nbytes = BIO_read(self->bio, PyBytes_AS_STRING(result), len);
- /* There should never be any short reads but check anyway. */
- if ((nbytes < len) && (_PyBytes_Resize(&result, len) < 0)) {
Py_DECREF(result);[](#l5.649)
return NULL;[](#l5.650)
- }
+} + +PyDoc_STRVAR(PySSL_memory_bio_read_doc, +"read([len]) -> bytes\n[](#l5.657) +\n[](#l5.658) +Read up to len bytes from the memory BIO.\n[](#l5.659) +\n[](#l5.660) +If len is not specified, read the entire buffer.\n[](#l5.661) +If the return value is an empty bytes instance, this means either\n[](#l5.662) +EOF or that no data is available. Use the "eof" property to\n[](#l5.663) +distinguish between the two."); + +static PyObject * +memory_bio_write(PySSLMemoryBIO *self, PyObject *args) +{
- if (buf.len > INT_MAX) {
PyErr_Format(PyExc_OverflowError,[](#l5.676)
"string longer than %d bytes", INT_MAX);[](#l5.677)
goto error;[](#l5.678)
- }
- if (self->eof_written) {
PyErr_SetString(PySSLErrorObject,[](#l5.682)
"cannot write() after write_eof()");[](#l5.683)
goto error;[](#l5.684)
- }
- nbytes = BIO_write(self->bio, buf.buf, buf.len);
- if (nbytes < 0) {
_setSSLError(NULL, 0, __FILE__, __LINE__);[](#l5.689)
goto error;[](#l5.690)
- }
+} + +PyDoc_STRVAR(PySSL_memory_bio_write_doc, +"write(b) -> len\n[](#l5.702) +\n[](#l5.703) +Writes the bytes b into the memory BIO. Returns the number\n[](#l5.704) +of bytes written."); + +static PyObject * +memory_bio_write_eof(PySSLMemoryBIO *self, PyObject *args) +{
- self->eof_written = 1;
- /* After an EOF is written, a zero return from read() should be a real EOF
* i.e. it should not be retried. Clear the SHOULD_RETRY flag. */[](#l5.712)
- BIO_clear_retry_flags(self->bio);
- BIO_set_mem_eof_return(self->bio, 0);
+} + +PyDoc_STRVAR(PySSL_memory_bio_write_eof_doc, +"write_eof()\n[](#l5.720) +\n[](#l5.721) +Write an EOF marker to the memory BIO.\n[](#l5.722) +When all data has been read, the "eof" property will be True."); + +static PyGetSetDef memory_bio_getsetlist[] = {
- {"pending", (getter) memory_bio_get_pending, NULL,
PySSL_memory_bio_pending_doc},[](#l5.727)
- {"eof", (getter) memory_bio_get_eof, NULL,
PySSL_memory_bio_eof_doc},[](#l5.729)
- {NULL}, /* sentinel */
+}; + +static struct PyMethodDef memory_bio_methods[] = {
- {"read", (PyCFunction) memory_bio_read,
METH_VARARGS, PySSL_memory_bio_read_doc},[](#l5.735)
- {"write", (PyCFunction) memory_bio_write,
METH_VARARGS, PySSL_memory_bio_write_doc},[](#l5.737)
- {"write_eof", (PyCFunction) memory_bio_write_eof,
METH_NOARGS, PySSL_memory_bio_write_eof_doc},[](#l5.739)
- {NULL, NULL} /* sentinel */
+}; + +static PyTypeObject PySSLMemoryBIO_Type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "_ssl.MemoryBIO", /tp_name/
- sizeof(PySSLMemoryBIO), /tp_basicsize/
- 0, /tp_itemsize/
- (destructor)memory_bio_dealloc, /tp_dealloc/
- 0, /tp_print/
- 0, /tp_getattr/
- 0, /tp_setattr/
- 0, /tp_reserved/
- 0, /tp_repr/
- 0, /tp_as_number/
- 0, /tp_as_sequence/
- 0, /tp_as_mapping/
- 0, /tp_hash/
- 0, /tp_call/
- 0, /tp_str/
- 0, /tp_getattro/
- 0, /tp_setattro/
- 0, /tp_as_buffer/
- Py_TPFLAGS_DEFAULT, /tp_flags/
- 0, /tp_doc/
- 0, /tp_traverse/
- 0, /tp_clear/
- 0, /tp_richcompare/
- 0, /tp_weaklistoffset/
- 0, /tp_iter/
- 0, /tp_iternext/
- memory_bio_methods, /tp_methods/
- 0, /tp_members/
- memory_bio_getsetlist, /tp_getset/
- 0, /tp_base/
- 0, /tp_dict/
- 0, /tp_descr_get/
- 0, /tp_descr_set/
- 0, /tp_dictoffset/
- 0, /tp_init/
- 0, /tp_alloc/
- memory_bio_new, /tp_new/
+}; + #ifdef HAVE_OPENSSL_RAND @@ -3927,6 +4302,8 @@ PyInit__ssl(void) return NULL; if (PyType_Ready(&PySSLSocket_Type) < 0) return NULL;
m = PyModule_Create(&_sslmodule); if (m == NULL) @@ -3990,6 +4367,9 @@ PyInit__ssl(void) if (PyDict_SetItemString(d, "_SSLSocket", (PyObject *)&PySSLSocket_Type) != 0) return NULL;