blob: e71875ba9f0093fa4f29672f93ff304bd63362d5 [file] [log] [blame]
Victor Stinner231b4042015-01-14 00:19:09 +01001import collections
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +03002import enum
Victor Stinner978a9af2015-01-29 17:50:58 +01003import warnings
Victor Stinner231b4042015-01-14 00:19:09 +01004try:
5 import ssl
6except ImportError: # pragma: no cover
7 ssl = None
8
Neil Aspinallf7686c12017-12-19 19:45:42 +00009from . import constants
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +030010from . import exceptions
Victor Stinner231b4042015-01-14 00:19:09 +010011from . import protocols
12from . import transports
13from .log import logger
14
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +030015SSLAgainErrors = (ssl.SSLWantReadError, ssl.SSLSyscallError)
16
17
18class SSLProtocolState(enum.Enum):
19 UNWRAPPED = "UNWRAPPED"
20 DO_HANDSHAKE = "DO_HANDSHAKE"
21 WRAPPED = "WRAPPED"
22 FLUSHING = "FLUSHING"
23 SHUTDOWN = "SHUTDOWN"
24
25
26class AppProtocolState(enum.Enum):
27 # This tracks the state of app protocol (https://git.io/fj59P):
28 #
29 # INIT -cm-> CON_MADE [-dr*->] [-er-> EOF?] -cl-> CON_LOST
30 #
31 # * cm: connection_made()
32 # * dr: data_received()
33 # * er: eof_received()
34 # * cl: connection_lost()
35
36 STATE_INIT = "STATE_INIT"
37 STATE_CON_MADE = "STATE_CON_MADE"
38 STATE_EOF = "STATE_EOF"
39 STATE_CON_LOST = "STATE_CON_LOST"
40
Victor Stinner231b4042015-01-14 00:19:09 +010041
42def _create_transport_context(server_side, server_hostname):
43 if server_side:
44 raise ValueError('Server side SSL needs a valid SSLContext')
45
46 # Client side may pass ssl=True to use a default
47 # context; in that case the sslcontext passed is None.
48 # The default is secure for client connections.
Andrew Svetlov51d546a2017-11-18 18:54:05 +020049 # Python 3.4+: use up-to-date strong settings.
50 sslcontext = ssl.create_default_context()
51 if not server_hostname:
52 sslcontext.check_hostname = False
Victor Stinner231b4042015-01-14 00:19:09 +010053 return sslcontext
54
55
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +030056def add_flowcontrol_defaults(high, low, kb):
57 if high is None:
58 if low is None:
59 hi = kb * 1024
60 else:
61 lo = low
62 hi = 4 * lo
63 else:
64 hi = high
65 if low is None:
66 lo = hi // 4
67 else:
68 lo = low
Victor Stinner231b4042015-01-14 00:19:09 +010069
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +030070 if not hi >= lo >= 0:
71 raise ValueError('high (%r) must be >= low (%r) must be >= 0' %
72 (hi, lo))
Victor Stinner231b4042015-01-14 00:19:09 +010073
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +030074 return hi, lo
Victor Stinner231b4042015-01-14 00:19:09 +010075
76
77class _SSLProtocolTransport(transports._FlowControlMixin,
78 transports.Transport):
79
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +030080 _start_tls_compatible = True
Andrew Svetlov7c684072018-01-27 21:22:47 +020081 _sendfile_compatible = constants._SendfileMode.FALLBACK
82
jlacolineea2ef5d2017-10-19 19:49:57 +020083 def __init__(self, loop, ssl_protocol):
Victor Stinner231b4042015-01-14 00:19:09 +010084 self._loop = loop
85 self._ssl_protocol = ssl_protocol
Victor Stinner978a9af2015-01-29 17:50:58 +010086 self._closed = False
Victor Stinner231b4042015-01-14 00:19:09 +010087
88 def get_extra_info(self, name, default=None):
89 """Get optional transport information."""
90 return self._ssl_protocol._get_extra_info(name, default)
91
Yury Selivanova05a6ef2016-09-11 21:11:02 -040092 def set_protocol(self, protocol):
Yury Selivanov21790222018-05-29 05:02:40 -040093 self._ssl_protocol._set_app_protocol(protocol)
Yury Selivanova05a6ef2016-09-11 21:11:02 -040094
95 def get_protocol(self):
jlacolineea2ef5d2017-10-19 19:49:57 +020096 return self._ssl_protocol._app_protocol
Yury Selivanova05a6ef2016-09-11 21:11:02 -040097
Yury Selivanov5bb1afb2015-11-16 12:43:21 -050098 def is_closing(self):
99 return self._closed
100
Victor Stinner231b4042015-01-14 00:19:09 +0100101 def close(self):
102 """Close the transport.
103
104 Buffered data will be flushed asynchronously. No more data
105 will be received. After all buffered data is flushed, the
106 protocol's connection_lost() method will (eventually) called
107 with None as its argument.
108 """
Victor Stinner978a9af2015-01-29 17:50:58 +0100109 self._closed = True
Victor Stinner231b4042015-01-14 00:19:09 +0100110 self._ssl_protocol._start_shutdown()
111
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300112 def __del__(self, _warnings=warnings):
INADA Naoki3e2ad8e2017-04-25 10:57:18 +0900113 if not self._closed:
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300114 self._closed = True
115 _warnings.warn(
116 "unclosed transport <asyncio._SSLProtocolTransport "
117 "object>", ResourceWarning)
Victor Stinner978a9af2015-01-29 17:50:58 +0100118
Yury Selivanovd757aaf2017-12-18 17:03:23 -0500119 def is_reading(self):
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300120 return not self._ssl_protocol._app_reading_paused
Yury Selivanovd757aaf2017-12-18 17:03:23 -0500121
Victor Stinner231b4042015-01-14 00:19:09 +0100122 def pause_reading(self):
123 """Pause the receiving end.
124
125 No data will be passed to the protocol's data_received()
126 method until resume_reading() is called.
127 """
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300128 self._ssl_protocol._pause_reading()
Victor Stinner231b4042015-01-14 00:19:09 +0100129
130 def resume_reading(self):
131 """Resume the receiving end.
132
133 Data received will once again be passed to the protocol's
134 data_received() method.
135 """
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300136 self._ssl_protocol._resume_reading()
Victor Stinner231b4042015-01-14 00:19:09 +0100137
138 def set_write_buffer_limits(self, high=None, low=None):
139 """Set the high- and low-water limits for write flow control.
140
141 These two values control when to call the protocol's
142 pause_writing() and resume_writing() methods. If specified,
143 the low-water limit must be less than or equal to the
144 high-water limit. Neither value can be negative.
145
146 The defaults are implementation-specific. If only the
Serhiy Storchakad65c9492015-11-02 14:10:23 +0200147 high-water limit is given, the low-water limit defaults to an
Victor Stinner231b4042015-01-14 00:19:09 +0100148 implementation-specific value less than or equal to the
149 high-water limit. Setting high to zero forces low to zero as
150 well, and causes pause_writing() to be called whenever the
151 buffer becomes non-empty. Setting low to zero causes
152 resume_writing() to be called only once the buffer is empty.
153 Use of zero for either limit is generally sub-optimal as it
154 reduces opportunities for doing I/O and computation
155 concurrently.
156 """
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300157 self._ssl_protocol._set_write_buffer_limits(high, low)
158 self._ssl_protocol._control_app_writing()
159
160 def get_write_buffer_limits(self):
161 return (self._ssl_protocol._outgoing_low_water,
162 self._ssl_protocol._outgoing_high_water)
Victor Stinner231b4042015-01-14 00:19:09 +0100163
164 def get_write_buffer_size(self):
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300165 """Return the current size of the write buffers."""
166 return self._ssl_protocol._get_write_buffer_size()
167
168 def set_read_buffer_limits(self, high=None, low=None):
169 """Set the high- and low-water limits for read flow control.
170
171 These two values control when to call the upstream transport's
172 pause_reading() and resume_reading() methods. If specified,
173 the low-water limit must be less than or equal to the
174 high-water limit. Neither value can be negative.
175
176 The defaults are implementation-specific. If only the
177 high-water limit is given, the low-water limit defaults to an
178 implementation-specific value less than or equal to the
179 high-water limit. Setting high to zero forces low to zero as
180 well, and causes pause_reading() to be called whenever the
181 buffer becomes non-empty. Setting low to zero causes
182 resume_reading() to be called only once the buffer is empty.
183 Use of zero for either limit is generally sub-optimal as it
184 reduces opportunities for doing I/O and computation
185 concurrently.
186 """
187 self._ssl_protocol._set_read_buffer_limits(high, low)
188 self._ssl_protocol._control_ssl_reading()
189
190 def get_read_buffer_limits(self):
191 return (self._ssl_protocol._incoming_low_water,
192 self._ssl_protocol._incoming_high_water)
193
194 def get_read_buffer_size(self):
195 """Return the current size of the read buffer."""
196 return self._ssl_protocol._get_read_buffer_size()
Victor Stinner231b4042015-01-14 00:19:09 +0100197
Andrew Svetlov7c684072018-01-27 21:22:47 +0200198 @property
199 def _protocol_paused(self):
200 # Required for sendfile fallback pause_writing/resume_writing logic
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300201 return self._ssl_protocol._app_writing_paused
Andrew Svetlov7c684072018-01-27 21:22:47 +0200202
Victor Stinner231b4042015-01-14 00:19:09 +0100203 def write(self, data):
204 """Write some data bytes to the transport.
205
206 This does not block; it buffers the data and arranges for it
207 to be sent out asynchronously.
208 """
209 if not isinstance(data, (bytes, bytearray, memoryview)):
Yury Selivanov6370f342017-12-10 18:36:12 -0500210 raise TypeError(f"data: expecting a bytes-like instance, "
211 f"got {type(data).__name__}")
Victor Stinner231b4042015-01-14 00:19:09 +0100212 if not data:
213 return
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300214 self._ssl_protocol._write_appdata((data,))
215
216 def writelines(self, list_of_data):
217 """Write a list (or any iterable) of data bytes to the transport.
218
219 The default implementation concatenates the arguments and
220 calls write() on the result.
221 """
222 self._ssl_protocol._write_appdata(list_of_data)
223
224 def write_eof(self):
225 """Close the write end after flushing buffered data.
226
227 This raises :exc:`NotImplementedError` right now.
228 """
229 raise NotImplementedError
Victor Stinner231b4042015-01-14 00:19:09 +0100230
231 def can_write_eof(self):
232 """Return True if this transport supports write_eof(), False if not."""
233 return False
234
235 def abort(self):
236 """Close the transport immediately.
237
238 Buffered data will be lost. No more data will be received.
239 The protocol's connection_lost() method will (eventually) be
240 called with None as its argument.
241 """
Yury Selivanov415bc462018-06-05 08:59:58 -0400242 self._closed = True
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300243 self._ssl_protocol._abort()
244
245 def _force_close(self, exc):
246 self._closed = True
247 self._ssl_protocol._abort(exc)
248
249 def _test__append_write_backlog(self, data):
250 # for test only
251 self._ssl_protocol._write_backlog.append(data)
252 self._ssl_protocol._write_buffer_size += len(data)
Victor Stinner231b4042015-01-14 00:19:09 +0100253
254
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300255class SSLProtocol(protocols.BufferedProtocol):
256 max_size = 256 * 1024 # Buffer size passed to read()
Victor Stinner231b4042015-01-14 00:19:09 +0100257
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300258 _handshake_start_time = None
259 _handshake_timeout_handle = None
260 _shutdown_timeout_handle = None
Victor Stinner231b4042015-01-14 00:19:09 +0100261
262 def __init__(self, loop, app_protocol, sslcontext, waiter,
Yury Selivanov92e7c7f2016-10-05 19:39:54 -0400263 server_side=False, server_hostname=None,
Neil Aspinallf7686c12017-12-19 19:45:42 +0000264 call_connection_made=True,
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300265 ssl_handshake_timeout=None,
266 ssl_shutdown_timeout=None):
Victor Stinner231b4042015-01-14 00:19:09 +0100267 if ssl is None:
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300268 raise RuntimeError("stdlib ssl module not available")
269
270 self._ssl_buffer = bytearray(self.max_size)
271 self._ssl_buffer_view = memoryview(self._ssl_buffer)
Victor Stinner231b4042015-01-14 00:19:09 +0100272
Andrew Svetlov51eb1c62017-12-20 20:24:43 +0200273 if ssl_handshake_timeout is None:
274 ssl_handshake_timeout = constants.SSL_HANDSHAKE_TIMEOUT
275 elif ssl_handshake_timeout <= 0:
276 raise ValueError(
277 f"ssl_handshake_timeout should be a positive number, "
278 f"got {ssl_handshake_timeout}")
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300279 if ssl_shutdown_timeout is None:
280 ssl_shutdown_timeout = constants.SSL_SHUTDOWN_TIMEOUT
281 elif ssl_shutdown_timeout <= 0:
282 raise ValueError(
283 f"ssl_shutdown_timeout should be a positive number, "
284 f"got {ssl_shutdown_timeout}")
Andrew Svetlov51eb1c62017-12-20 20:24:43 +0200285
Victor Stinner231b4042015-01-14 00:19:09 +0100286 if not sslcontext:
Yury Selivanov6370f342017-12-10 18:36:12 -0500287 sslcontext = _create_transport_context(
288 server_side, server_hostname)
Victor Stinner231b4042015-01-14 00:19:09 +0100289
290 self._server_side = server_side
291 if server_hostname and not server_side:
292 self._server_hostname = server_hostname
293 else:
294 self._server_hostname = None
295 self._sslcontext = sslcontext
296 # SSL-specific extra info. More info are set when the handshake
297 # completes.
298 self._extra = dict(sslcontext=sslcontext)
299
300 # App data write buffering
301 self._write_backlog = collections.deque()
302 self._write_buffer_size = 0
303
304 self._waiter = waiter
Victor Stinner231b4042015-01-14 00:19:09 +0100305 self._loop = loop
Yury Selivanov21790222018-05-29 05:02:40 -0400306 self._set_app_protocol(app_protocol)
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300307 self._app_transport = None
308 self._app_transport_created = False
Victor Stinnerf7dc7fb2015-09-21 18:06:17 +0200309 # transport, ex: SelectorSocketTransport
Victor Stinner7e222f42015-01-15 13:16:27 +0100310 self._transport = None
Neil Aspinallf7686c12017-12-19 19:45:42 +0000311 self._ssl_handshake_timeout = ssl_handshake_timeout
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300312 self._ssl_shutdown_timeout = ssl_shutdown_timeout
313 # SSL and state machine
314 self._incoming = ssl.MemoryBIO()
315 self._outgoing = ssl.MemoryBIO()
316 self._state = SSLProtocolState.UNWRAPPED
317 self._conn_lost = 0 # Set when connection_lost called
318 if call_connection_made:
319 self._app_state = AppProtocolState.STATE_INIT
320 else:
321 self._app_state = AppProtocolState.STATE_CON_MADE
322 self._sslobj = self._sslcontext.wrap_bio(
323 self._incoming, self._outgoing,
324 server_side=self._server_side,
325 server_hostname=self._server_hostname)
326
327 # Flow Control
328
329 self._ssl_writing_paused = False
330
331 self._app_reading_paused = False
332
333 self._ssl_reading_paused = False
334 self._incoming_high_water = 0
335 self._incoming_low_water = 0
336 self._set_read_buffer_limits()
337 self._eof_received = False
338
339 self._app_writing_paused = False
340 self._outgoing_high_water = 0
341 self._outgoing_low_water = 0
342 self._set_write_buffer_limits()
343 self._get_app_transport()
Victor Stinner231b4042015-01-14 00:19:09 +0100344
Yury Selivanov21790222018-05-29 05:02:40 -0400345 def _set_app_protocol(self, app_protocol):
346 self._app_protocol = app_protocol
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300347 # Make fast hasattr check first
348 if (hasattr(app_protocol, 'get_buffer') and
349 isinstance(app_protocol, protocols.BufferedProtocol)):
350 self._app_protocol_get_buffer = app_protocol.get_buffer
351 self._app_protocol_buffer_updated = app_protocol.buffer_updated
352 self._app_protocol_is_buffer = True
353 else:
354 self._app_protocol_is_buffer = False
Yury Selivanov21790222018-05-29 05:02:40 -0400355
Victor Stinnerf07801b2015-01-29 00:36:35 +0100356 def _wakeup_waiter(self, exc=None):
357 if self._waiter is None:
358 return
359 if not self._waiter.cancelled():
360 if exc is not None:
361 self._waiter.set_exception(exc)
362 else:
363 self._waiter.set_result(None)
364 self._waiter = None
365
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300366 def _get_app_transport(self):
367 if self._app_transport is None:
368 if self._app_transport_created:
369 raise RuntimeError('Creating _SSLProtocolTransport twice')
370 self._app_transport = _SSLProtocolTransport(self._loop, self)
371 self._app_transport_created = True
372 return self._app_transport
373
Victor Stinner231b4042015-01-14 00:19:09 +0100374 def connection_made(self, transport):
375 """Called when the low-level connection is made.
376
377 Start the SSL handshake.
378 """
379 self._transport = transport
Victor Stinner231b4042015-01-14 00:19:09 +0100380 self._start_handshake()
381
382 def connection_lost(self, exc):
383 """Called when the low-level connection is lost or closed.
384
385 The argument is an exception object or None (the latter
386 meaning a regular EOF is received or the connection was
387 aborted or closed).
388 """
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300389 self._write_backlog.clear()
390 self._outgoing.read()
391 self._conn_lost += 1
392
393 # Just mark the app transport as closed so that its __dealloc__
394 # doesn't complain.
395 if self._app_transport is not None:
396 self._app_transport._closed = True
397
398 if self._state != SSLProtocolState.DO_HANDSHAKE:
399 if (
400 self._app_state == AppProtocolState.STATE_CON_MADE or
401 self._app_state == AppProtocolState.STATE_EOF
402 ):
403 self._app_state = AppProtocolState.STATE_CON_LOST
404 self._loop.call_soon(self._app_protocol.connection_lost, exc)
405 self._set_state(SSLProtocolState.UNWRAPPED)
Victor Stinner231b4042015-01-14 00:19:09 +0100406 self._transport = None
407 self._app_transport = None
Fantix Kingf683f462019-03-17 17:51:10 -0500408 self._app_protocol = None
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300409 self._wakeup_waiter(exc)
Victor Stinner231b4042015-01-14 00:19:09 +0100410
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300411 if self._shutdown_timeout_handle:
412 self._shutdown_timeout_handle.cancel()
413 self._shutdown_timeout_handle = None
414 if self._handshake_timeout_handle:
415 self._handshake_timeout_handle.cancel()
416 self._handshake_timeout_handle = None
Victor Stinner231b4042015-01-14 00:19:09 +0100417
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300418 def get_buffer(self, n):
419 want = n
420 if want <= 0 or want > self.max_size:
421 want = self.max_size
422 if len(self._ssl_buffer) < want:
423 self._ssl_buffer = bytearray(want)
424 self._ssl_buffer_view = memoryview(self._ssl_buffer)
425 return self._ssl_buffer_view
Victor Stinner231b4042015-01-14 00:19:09 +0100426
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300427 def buffer_updated(self, nbytes):
428 self._incoming.write(self._ssl_buffer_view[:nbytes])
Victor Stinner231b4042015-01-14 00:19:09 +0100429
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300430 if self._state == SSLProtocolState.DO_HANDSHAKE:
431 self._do_handshake()
Andrew Svetlov5e80a712018-03-10 17:48:35 +0200432
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300433 elif self._state == SSLProtocolState.WRAPPED:
434 self._do_read()
Victor Stinner231b4042015-01-14 00:19:09 +0100435
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300436 elif self._state == SSLProtocolState.FLUSHING:
437 self._do_flush()
Victor Stinner231b4042015-01-14 00:19:09 +0100438
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300439 elif self._state == SSLProtocolState.SHUTDOWN:
440 self._do_shutdown()
Victor Stinner231b4042015-01-14 00:19:09 +0100441
442 def eof_received(self):
443 """Called when the other end of the low-level stream
444 is half-closed.
445
446 If this returns a false value (including None), the transport
447 will close itself. If it returns a true value, closing the
448 transport is up to the protocol.
449 """
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300450 self._eof_received = True
Victor Stinner231b4042015-01-14 00:19:09 +0100451 try:
452 if self._loop.get_debug():
453 logger.debug("%r received EOF", self)
Victor Stinnerb507cba2015-01-29 00:35:56 +0100454
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300455 if self._state == SSLProtocolState.DO_HANDSHAKE:
456 self._on_handshake_complete(ConnectionResetError)
Victor Stinnerb507cba2015-01-29 00:35:56 +0100457
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300458 elif self._state == SSLProtocolState.WRAPPED:
459 self._set_state(SSLProtocolState.FLUSHING)
460 if self._app_reading_paused:
461 return True
462 else:
463 self._do_flush()
464
465 elif self._state == SSLProtocolState.FLUSHING:
466 self._do_write()
467 self._set_state(SSLProtocolState.SHUTDOWN)
468 self._do_shutdown()
469
470 elif self._state == SSLProtocolState.SHUTDOWN:
471 self._do_shutdown()
472
473 except Exception:
Victor Stinner231b4042015-01-14 00:19:09 +0100474 self._transport.close()
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300475 raise
Victor Stinner231b4042015-01-14 00:19:09 +0100476
477 def _get_extra_info(self, name, default=None):
478 if name in self._extra:
479 return self._extra[name]
Nikolay Kim2b27e2e2017-03-12 12:23:30 -0700480 elif self._transport is not None:
Victor Stinner231b4042015-01-14 00:19:09 +0100481 return self._transport.get_extra_info(name, default)
Nikolay Kim2b27e2e2017-03-12 12:23:30 -0700482 else:
483 return default
Victor Stinner231b4042015-01-14 00:19:09 +0100484
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300485 def _set_state(self, new_state):
486 allowed = False
Victor Stinner231b4042015-01-14 00:19:09 +0100487
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300488 if new_state == SSLProtocolState.UNWRAPPED:
489 allowed = True
490
491 elif (
492 self._state == SSLProtocolState.UNWRAPPED and
493 new_state == SSLProtocolState.DO_HANDSHAKE
494 ):
495 allowed = True
496
497 elif (
498 self._state == SSLProtocolState.DO_HANDSHAKE and
499 new_state == SSLProtocolState.WRAPPED
500 ):
501 allowed = True
502
503 elif (
504 self._state == SSLProtocolState.WRAPPED and
505 new_state == SSLProtocolState.FLUSHING
506 ):
507 allowed = True
508
509 elif (
510 self._state == SSLProtocolState.FLUSHING and
511 new_state == SSLProtocolState.SHUTDOWN
512 ):
513 allowed = True
514
515 if allowed:
516 self._state = new_state
517
518 else:
519 raise RuntimeError(
520 'cannot switch state from {} to {}'.format(
521 self._state, new_state))
522
523 # Handshake flow
Victor Stinner231b4042015-01-14 00:19:09 +0100524
525 def _start_handshake(self):
526 if self._loop.get_debug():
527 logger.debug("%r starts SSL handshake", self)
528 self._handshake_start_time = self._loop.time()
529 else:
530 self._handshake_start_time = None
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300531
532 self._set_state(SSLProtocolState.DO_HANDSHAKE)
533
534 # start handshake timeout count down
Neil Aspinallf7686c12017-12-19 19:45:42 +0000535 self._handshake_timeout_handle = \
536 self._loop.call_later(self._ssl_handshake_timeout,
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300537 lambda: self._check_handshake_timeout())
538
539 self._do_handshake()
Neil Aspinallf7686c12017-12-19 19:45:42 +0000540
541 def _check_handshake_timeout(self):
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300542 if self._state == SSLProtocolState.DO_HANDSHAKE:
Yury Selivanov96026432018-06-04 11:32:35 -0400543 msg = (
544 f"SSL handshake is taking longer than "
545 f"{self._ssl_handshake_timeout} seconds: "
546 f"aborting the connection"
547 )
548 self._fatal_error(ConnectionAbortedError(msg))
Victor Stinner231b4042015-01-14 00:19:09 +0100549
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300550 def _do_handshake(self):
Victor Stinner231b4042015-01-14 00:19:09 +0100551 try:
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300552 self._sslobj.do_handshake()
553 except SSLAgainErrors:
554 self._process_outgoing()
555 except ssl.SSLError as exc:
556 self._on_handshake_complete(exc)
557 else:
558 self._on_handshake_complete(None)
559
560 def _on_handshake_complete(self, handshake_exc):
561 if self._handshake_timeout_handle is not None:
562 self._handshake_timeout_handle.cancel()
563 self._handshake_timeout_handle = None
564
565 sslobj = self._sslobj
566 try:
567 if handshake_exc is None:
568 self._set_state(SSLProtocolState.WRAPPED)
569 else:
Victor Stinner231b4042015-01-14 00:19:09 +0100570 raise handshake_exc
Victor Stinner177e9f02015-01-14 16:56:20 +0100571
572 peercert = sslobj.getpeercert()
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300573 except Exception as exc:
574 self._set_state(SSLProtocolState.UNWRAPPED)
Yury Selivanov96026432018-06-04 11:32:35 -0400575 if isinstance(exc, ssl.CertificateError):
576 msg = 'SSL handshake failed on verifying the certificate'
Victor Stinner231b4042015-01-14 00:19:09 +0100577 else:
Yury Selivanov96026432018-06-04 11:32:35 -0400578 msg = 'SSL handshake failed'
579 self._fatal_error(exc, msg)
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300580 self._wakeup_waiter(exc)
Yury Selivanov96026432018-06-04 11:32:35 -0400581 return
Victor Stinner231b4042015-01-14 00:19:09 +0100582
583 if self._loop.get_debug():
584 dt = self._loop.time() - self._handshake_start_time
585 logger.debug("%r: SSL handshake took %.1f ms", self, dt * 1e3)
586
587 # Add extra info that becomes available after handshake.
588 self._extra.update(peercert=peercert,
589 cipher=sslobj.cipher(),
590 compression=sslobj.compression(),
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300591 ssl_object=sslobj)
592 if self._app_state == AppProtocolState.STATE_INIT:
593 self._app_state = AppProtocolState.STATE_CON_MADE
594 self._app_protocol.connection_made(self._get_app_transport())
Victor Stinnerf07801b2015-01-29 00:36:35 +0100595 self._wakeup_waiter()
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300596 self._do_read()
Victor Stinner231b4042015-01-14 00:19:09 +0100597
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300598 # Shutdown flow
599
600 def _start_shutdown(self):
601 if (
602 self._state in (
603 SSLProtocolState.FLUSHING,
604 SSLProtocolState.SHUTDOWN,
605 SSLProtocolState.UNWRAPPED
606 )
607 ):
608 return
609 if self._app_transport is not None:
610 self._app_transport._closed = True
611 if self._state == SSLProtocolState.DO_HANDSHAKE:
612 self._abort()
613 else:
614 self._set_state(SSLProtocolState.FLUSHING)
615 self._shutdown_timeout_handle = self._loop.call_later(
616 self._ssl_shutdown_timeout,
617 lambda: self._check_shutdown_timeout()
618 )
619 self._do_flush()
620
621 def _check_shutdown_timeout(self):
622 if (
623 self._state in (
624 SSLProtocolState.FLUSHING,
625 SSLProtocolState.SHUTDOWN
626 )
627 ):
628 self._transport._force_close(
629 exceptions.TimeoutError('SSL shutdown timed out'))
630
631 def _do_flush(self):
632 self._do_read()
633 self._set_state(SSLProtocolState.SHUTDOWN)
634 self._do_shutdown()
635
636 def _do_shutdown(self):
637 try:
638 if not self._eof_received:
639 self._sslobj.unwrap()
640 except SSLAgainErrors:
641 self._process_outgoing()
642 except ssl.SSLError as exc:
643 self._on_shutdown_complete(exc)
644 else:
645 self._process_outgoing()
646 self._call_eof_received()
647 self._on_shutdown_complete(None)
648
649 def _on_shutdown_complete(self, shutdown_exc):
650 if self._shutdown_timeout_handle is not None:
651 self._shutdown_timeout_handle.cancel()
652 self._shutdown_timeout_handle = None
653
654 if shutdown_exc:
655 self._fatal_error(shutdown_exc)
656 else:
657 self._loop.call_soon(self._transport.close)
658
659 def _abort(self):
660 self._set_state(SSLProtocolState.UNWRAPPED)
661 if self._transport is not None:
662 self._transport.abort()
663
664 # Outgoing flow
665
666 def _write_appdata(self, list_of_data):
667 if (
668 self._state in (
669 SSLProtocolState.FLUSHING,
670 SSLProtocolState.SHUTDOWN,
671 SSLProtocolState.UNWRAPPED
672 )
673 ):
674 if self._conn_lost >= constants.LOG_THRESHOLD_FOR_CONNLOST_WRITES:
675 logger.warning('SSL connection is closed')
676 self._conn_lost += 1
Victor Stinner231b4042015-01-14 00:19:09 +0100677 return
678
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300679 for data in list_of_data:
680 self._write_backlog.append(data)
681 self._write_buffer_size += len(data)
682
Victor Stinner231b4042015-01-14 00:19:09 +0100683 try:
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300684 if self._state == SSLProtocolState.WRAPPED:
685 self._do_write()
686
687 except Exception as ex:
688 self._fatal_error(ex, 'Fatal error on SSL protocol')
689
690 def _do_write(self):
691 try:
692 while self._write_backlog:
693 data = self._write_backlog[0]
694 count = self._sslobj.write(data)
695 data_len = len(data)
696 if count < data_len:
697 self._write_backlog[0] = data[count:]
698 self._write_buffer_size -= count
Victor Stinner231b4042015-01-14 00:19:09 +0100699 else:
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300700 del self._write_backlog[0]
701 self._write_buffer_size -= data_len
702 except SSLAgainErrors:
703 pass
704 self._process_outgoing()
Victor Stinner231b4042015-01-14 00:19:09 +0100705
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300706 def _process_outgoing(self):
707 if not self._ssl_writing_paused:
708 data = self._outgoing.read()
709 if len(data):
710 self._transport.write(data)
711 self._control_app_writing()
Victor Stinner231b4042015-01-14 00:19:09 +0100712
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300713 # Incoming flow
714
715 def _do_read(self):
716 if (
717 self._state not in (
718 SSLProtocolState.WRAPPED,
719 SSLProtocolState.FLUSHING,
720 )
721 ):
722 return
723 try:
724 if not self._app_reading_paused:
725 if self._app_protocol_is_buffer:
726 self._do_read__buffered()
727 else:
728 self._do_read__copied()
729 if self._write_backlog:
730 self._do_write()
731 else:
732 self._process_outgoing()
733 self._control_ssl_reading()
734 except Exception as ex:
735 self._fatal_error(ex, 'Fatal error on SSL protocol')
736
737 def _do_read__buffered(self):
738 offset = 0
739 count = 1
740
741 buf = self._app_protocol_get_buffer(self._get_read_buffer_size())
742 wants = len(buf)
743
744 try:
745 count = self._sslobj.read(wants, buf)
746
747 if count > 0:
748 offset = count
749 while offset < wants:
750 count = self._sslobj.read(wants - offset, buf[offset:])
751 if count > 0:
752 offset += count
753 else:
754 break
755 else:
756 self._loop.call_soon(lambda: self._do_read())
757 except SSLAgainErrors:
758 pass
759 if offset > 0:
760 self._app_protocol_buffer_updated(offset)
761 if not count:
762 # close_notify
763 self._call_eof_received()
764 self._start_shutdown()
765
766 def _do_read__copied(self):
767 chunk = b'1'
768 zero = True
769 one = False
770
771 try:
772 while True:
773 chunk = self._sslobj.read(self.max_size)
774 if not chunk:
Victor Stinner231b4042015-01-14 00:19:09 +0100775 break
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300776 if zero:
777 zero = False
778 one = True
779 first = chunk
780 elif one:
781 one = False
782 data = [first, chunk]
783 else:
784 data.append(chunk)
785 except SSLAgainErrors:
786 pass
787 if one:
788 self._app_protocol.data_received(first)
789 elif not zero:
790 self._app_protocol.data_received(b''.join(data))
791 if not chunk:
792 # close_notify
793 self._call_eof_received()
794 self._start_shutdown()
Victor Stinner231b4042015-01-14 00:19:09 +0100795
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300796 def _call_eof_received(self):
797 try:
798 if self._app_state == AppProtocolState.STATE_CON_MADE:
799 self._app_state = AppProtocolState.STATE_EOF
800 keep_open = self._app_protocol.eof_received()
801 if keep_open:
802 logger.warning('returning true from eof_received() '
803 'has no effect when using ssl')
804 except (KeyboardInterrupt, SystemExit):
Yury Selivanov431b5402019-05-27 14:45:12 +0200805 raise
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300806 except BaseException as ex:
807 self._fatal_error(ex, 'Error calling eof_received()')
808
809 # Flow control for writes from APP socket
810
811 def _control_app_writing(self):
812 size = self._get_write_buffer_size()
813 if size >= self._outgoing_high_water and not self._app_writing_paused:
814 self._app_writing_paused = True
815 try:
816 self._app_protocol.pause_writing()
817 except (KeyboardInterrupt, SystemExit):
818 raise
819 except BaseException as exc:
820 self._loop.call_exception_handler({
821 'message': 'protocol.pause_writing() failed',
822 'exception': exc,
823 'transport': self._app_transport,
824 'protocol': self,
825 })
826 elif size <= self._outgoing_low_water and self._app_writing_paused:
827 self._app_writing_paused = False
828 try:
829 self._app_protocol.resume_writing()
830 except (KeyboardInterrupt, SystemExit):
831 raise
832 except BaseException as exc:
833 self._loop.call_exception_handler({
834 'message': 'protocol.resume_writing() failed',
835 'exception': exc,
836 'transport': self._app_transport,
837 'protocol': self,
838 })
839
840 def _get_write_buffer_size(self):
841 return self._outgoing.pending + self._write_buffer_size
842
843 def _set_write_buffer_limits(self, high=None, low=None):
844 high, low = add_flowcontrol_defaults(
845 high, low, constants.FLOW_CONTROL_HIGH_WATER_SSL_WRITE)
846 self._outgoing_high_water = high
847 self._outgoing_low_water = low
848
849 # Flow control for reads to APP socket
850
851 def _pause_reading(self):
852 self._app_reading_paused = True
853
854 def _resume_reading(self):
855 if self._app_reading_paused:
856 self._app_reading_paused = False
857
858 def resume():
859 if self._state == SSLProtocolState.WRAPPED:
860 self._do_read()
861 elif self._state == SSLProtocolState.FLUSHING:
862 self._do_flush()
863 elif self._state == SSLProtocolState.SHUTDOWN:
864 self._do_shutdown()
865 self._loop.call_soon(resume)
866
867 # Flow control for reads from SSL socket
868
869 def _control_ssl_reading(self):
870 size = self._get_read_buffer_size()
871 if size >= self._incoming_high_water and not self._ssl_reading_paused:
872 self._ssl_reading_paused = True
873 self._transport.pause_reading()
874 elif size <= self._incoming_low_water and self._ssl_reading_paused:
875 self._ssl_reading_paused = False
876 self._transport.resume_reading()
877
878 def _set_read_buffer_limits(self, high=None, low=None):
879 high, low = add_flowcontrol_defaults(
880 high, low, constants.FLOW_CONTROL_HIGH_WATER_SSL_READ)
881 self._incoming_high_water = high
882 self._incoming_low_water = low
883
884 def _get_read_buffer_size(self):
885 return self._incoming.pending
886
887 # Flow control for writes to SSL socket
888
889 def pause_writing(self):
890 """Called when the low-level transport's buffer goes over
891 the high-water mark.
892 """
893 assert not self._ssl_writing_paused
894 self._ssl_writing_paused = True
895
896 def resume_writing(self):
897 """Called when the low-level transport's buffer drains below
898 the low-water mark.
899 """
900 assert self._ssl_writing_paused
901 self._ssl_writing_paused = False
902 self._process_outgoing()
Victor Stinner231b4042015-01-14 00:19:09 +0100903
904 def _fatal_error(self, exc, message='Fatal error on transport'):
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300905 if self._transport:
906 self._transport._force_close(exc)
907
Andrew Svetlov1f39c282019-05-27 16:28:34 +0300908 if isinstance(exc, OSError):
Victor Stinner231b4042015-01-14 00:19:09 +0100909 if self._loop.get_debug():
910 logger.debug("%r: %s", self, message, exc_info=True)
Andrew Svetlov5fb06ed2021-05-03 00:34:15 +0300911 elif not isinstance(exc, exceptions.CancelledError):
Victor Stinner231b4042015-01-14 00:19:09 +0100912 self._loop.call_exception_handler({
913 'message': message,
914 'exception': exc,
915 'transport': self._transport,
916 'protocol': self,
917 })