| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | import collections | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  | import enum | 
					
						
							| 
									
										
										
										
											2015-01-29 17:50:58 +01:00
										 |  |  | import warnings | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | try: | 
					
						
							|  |  |  |     import ssl | 
					
						
							|  |  |  | except ImportError:  # pragma: no cover | 
					
						
							|  |  |  |     ssl = None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-19 19:45:42 +00:00
										 |  |  | from . import constants | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  | from . import exceptions | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | from . import protocols | 
					
						
							|  |  |  | from . import transports | 
					
						
							|  |  |  | from .log import logger | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  | if ssl is not None: | 
					
						
							|  |  |  |     SSLAgainErrors = (ssl.SSLWantReadError, ssl.SSLSyscallError) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SSLProtocolState(enum.Enum): | 
					
						
							|  |  |  |     UNWRAPPED = "UNWRAPPED" | 
					
						
							|  |  |  |     DO_HANDSHAKE = "DO_HANDSHAKE" | 
					
						
							|  |  |  |     WRAPPED = "WRAPPED" | 
					
						
							|  |  |  |     FLUSHING = "FLUSHING" | 
					
						
							|  |  |  |     SHUTDOWN = "SHUTDOWN" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class AppProtocolState(enum.Enum): | 
					
						
							|  |  |  |     # This tracks the state of app protocol (https://git.io/fj59P): | 
					
						
							|  |  |  |     # | 
					
						
							|  |  |  |     #     INIT -cm-> CON_MADE [-dr*->] [-er-> EOF?] -cl-> CON_LOST | 
					
						
							|  |  |  |     # | 
					
						
							|  |  |  |     # * cm: connection_made() | 
					
						
							|  |  |  |     # * dr: data_received() | 
					
						
							|  |  |  |     # * er: eof_received() | 
					
						
							|  |  |  |     # * cl: connection_lost() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     STATE_INIT = "STATE_INIT" | 
					
						
							|  |  |  |     STATE_CON_MADE = "STATE_CON_MADE" | 
					
						
							|  |  |  |     STATE_EOF = "STATE_EOF" | 
					
						
							|  |  |  |     STATE_CON_LOST = "STATE_CON_LOST" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | def _create_transport_context(server_side, server_hostname): | 
					
						
							|  |  |  |     if server_side: | 
					
						
							|  |  |  |         raise ValueError('Server side SSL needs a valid SSLContext') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Client side may pass ssl=True to use a default | 
					
						
							|  |  |  |     # context; in that case the sslcontext passed is None. | 
					
						
							|  |  |  |     # The default is secure for client connections. | 
					
						
							| 
									
										
										
										
											2017-11-18 18:54:05 +02:00
										 |  |  |     # Python 3.4+: use up-to-date strong settings. | 
					
						
							|  |  |  |     sslcontext = ssl.create_default_context() | 
					
						
							|  |  |  |     if not server_hostname: | 
					
						
							|  |  |  |         sslcontext.check_hostname = False | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |     return sslcontext | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  | def add_flowcontrol_defaults(high, low, kb): | 
					
						
							|  |  |  |     if high is None: | 
					
						
							|  |  |  |         if low is None: | 
					
						
							|  |  |  |             hi = kb * 1024 | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             lo = low | 
					
						
							|  |  |  |             hi = 4 * lo | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         hi = high | 
					
						
							|  |  |  |     if low is None: | 
					
						
							|  |  |  |         lo = hi // 4 | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         lo = low | 
					
						
							| 
									
										
										
										
											2021-05-03 16:21:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |     if not hi >= lo >= 0: | 
					
						
							|  |  |  |         raise ValueError('high (%r) must be >= low (%r) must be >= 0' % | 
					
						
							|  |  |  |                          (hi, lo)) | 
					
						
							| 
									
										
										
										
											2021-05-03 16:21:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |     return hi, lo | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _SSLProtocolTransport(transports._FlowControlMixin, | 
					
						
							|  |  |  |                             transports.Transport): | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |     _start_tls_compatible = True | 
					
						
							| 
									
										
										
										
											2018-01-27 21:22:47 +02:00
										 |  |  |     _sendfile_compatible = constants._SendfileMode.FALLBACK | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-19 19:49:57 +02:00
										 |  |  |     def __init__(self, loop, ssl_protocol): | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |         self._loop = loop | 
					
						
							|  |  |  |         self._ssl_protocol = ssl_protocol | 
					
						
							| 
									
										
										
										
											2015-01-29 17:50:58 +01:00
										 |  |  |         self._closed = False | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def get_extra_info(self, name, default=None): | 
					
						
							|  |  |  |         """Get optional transport information.""" | 
					
						
							|  |  |  |         return self._ssl_protocol._get_extra_info(name, default) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-11 21:11:02 -04:00
										 |  |  |     def set_protocol(self, protocol): | 
					
						
							| 
									
										
										
										
											2018-05-29 05:02:40 -04:00
										 |  |  |         self._ssl_protocol._set_app_protocol(protocol) | 
					
						
							| 
									
										
										
										
											2016-09-11 21:11:02 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def get_protocol(self): | 
					
						
							| 
									
										
										
										
											2017-10-19 19:49:57 +02:00
										 |  |  |         return self._ssl_protocol._app_protocol | 
					
						
							| 
									
										
										
										
											2016-09-11 21:11:02 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-16 12:43:21 -05:00
										 |  |  |     def is_closing(self): | 
					
						
							|  |  |  |         return self._closed | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |     def close(self): | 
					
						
							|  |  |  |         """Close the transport.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Buffered data will be flushed asynchronously.  No more data | 
					
						
							|  |  |  |         will be received.  After all buffered data is flushed, the | 
					
						
							|  |  |  |         protocol's connection_lost() method will (eventually) called | 
					
						
							|  |  |  |         with None as its argument. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2022-10-22 21:41:27 +05:30
										 |  |  |         if not self._closed: | 
					
						
							|  |  |  |             self._closed = True | 
					
						
							|  |  |  |             self._ssl_protocol._start_shutdown() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self._ssl_protocol = None | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |     def __del__(self, _warnings=warnings): | 
					
						
							| 
									
										
										
										
											2017-04-25 10:57:18 +09:00
										 |  |  |         if not self._closed: | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |             self._closed = True | 
					
						
							|  |  |  |             _warnings.warn( | 
					
						
							|  |  |  |                 "unclosed transport <asyncio._SSLProtocolTransport " | 
					
						
							|  |  |  |                 "object>", ResourceWarning) | 
					
						
							| 
									
										
										
										
											2015-01-29 17:50:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-18 17:03:23 -05:00
										 |  |  |     def is_reading(self): | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         return not self._ssl_protocol._app_reading_paused | 
					
						
							| 
									
										
										
										
											2017-12-18 17:03:23 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |     def pause_reading(self): | 
					
						
							|  |  |  |         """Pause the receiving end.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         No data will be passed to the protocol's data_received() | 
					
						
							|  |  |  |         method until resume_reading() is called. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         self._ssl_protocol._pause_reading() | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def resume_reading(self): | 
					
						
							|  |  |  |         """Resume the receiving end.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Data received will once again be passed to the protocol's | 
					
						
							|  |  |  |         data_received() method. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         self._ssl_protocol._resume_reading() | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def set_write_buffer_limits(self, high=None, low=None): | 
					
						
							|  |  |  |         """Set the high- and low-water limits for write flow control.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         These two values control when to call the protocol's | 
					
						
							|  |  |  |         pause_writing() and resume_writing() methods.  If specified, | 
					
						
							|  |  |  |         the low-water limit must be less than or equal to the | 
					
						
							|  |  |  |         high-water limit.  Neither value can be negative. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         The defaults are implementation-specific.  If only the | 
					
						
							| 
									
										
										
										
											2015-11-02 14:10:23 +02:00
										 |  |  |         high-water limit is given, the low-water limit defaults to an | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |         implementation-specific value less than or equal to the | 
					
						
							|  |  |  |         high-water limit.  Setting high to zero forces low to zero as | 
					
						
							|  |  |  |         well, and causes pause_writing() to be called whenever the | 
					
						
							|  |  |  |         buffer becomes non-empty.  Setting low to zero causes | 
					
						
							|  |  |  |         resume_writing() to be called only once the buffer is empty. | 
					
						
							|  |  |  |         Use of zero for either limit is generally sub-optimal as it | 
					
						
							|  |  |  |         reduces opportunities for doing I/O and computation | 
					
						
							|  |  |  |         concurrently. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         self._ssl_protocol._set_write_buffer_limits(high, low) | 
					
						
							|  |  |  |         self._ssl_protocol._control_app_writing() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_write_buffer_limits(self): | 
					
						
							|  |  |  |         return (self._ssl_protocol._outgoing_low_water, | 
					
						
							|  |  |  |                 self._ssl_protocol._outgoing_high_water) | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def get_write_buffer_size(self): | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         """Return the current size of the write buffers.""" | 
					
						
							|  |  |  |         return self._ssl_protocol._get_write_buffer_size() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def set_read_buffer_limits(self, high=None, low=None): | 
					
						
							|  |  |  |         """Set the high- and low-water limits for read flow control.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         These two values control when to call the upstream transport's | 
					
						
							|  |  |  |         pause_reading() and resume_reading() methods.  If specified, | 
					
						
							|  |  |  |         the low-water limit must be less than or equal to the | 
					
						
							|  |  |  |         high-water limit.  Neither value can be negative. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         The defaults are implementation-specific.  If only the | 
					
						
							|  |  |  |         high-water limit is given, the low-water limit defaults to an | 
					
						
							|  |  |  |         implementation-specific value less than or equal to the | 
					
						
							|  |  |  |         high-water limit.  Setting high to zero forces low to zero as | 
					
						
							|  |  |  |         well, and causes pause_reading() to be called whenever the | 
					
						
							|  |  |  |         buffer becomes non-empty.  Setting low to zero causes | 
					
						
							|  |  |  |         resume_reading() to be called only once the buffer is empty. | 
					
						
							|  |  |  |         Use of zero for either limit is generally sub-optimal as it | 
					
						
							|  |  |  |         reduces opportunities for doing I/O and computation | 
					
						
							|  |  |  |         concurrently. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         self._ssl_protocol._set_read_buffer_limits(high, low) | 
					
						
							|  |  |  |         self._ssl_protocol._control_ssl_reading() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_read_buffer_limits(self): | 
					
						
							|  |  |  |         return (self._ssl_protocol._incoming_low_water, | 
					
						
							|  |  |  |                 self._ssl_protocol._incoming_high_water) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_read_buffer_size(self): | 
					
						
							|  |  |  |         """Return the current size of the read buffer.""" | 
					
						
							|  |  |  |         return self._ssl_protocol._get_read_buffer_size() | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-27 21:22:47 +02:00
										 |  |  |     @property | 
					
						
							|  |  |  |     def _protocol_paused(self): | 
					
						
							|  |  |  |         # Required for sendfile fallback pause_writing/resume_writing logic | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         return self._ssl_protocol._app_writing_paused | 
					
						
							| 
									
										
										
										
											2018-01-27 21:22:47 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |     def write(self, data): | 
					
						
							|  |  |  |         """Write some data bytes to the transport.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         This does not block; it buffers the data and arranges for it | 
					
						
							|  |  |  |         to be sent out asynchronously. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         if not isinstance(data, (bytes, bytearray, memoryview)): | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |             raise TypeError(f"data: expecting a bytes-like instance, " | 
					
						
							|  |  |  |                             f"got {type(data).__name__}") | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |         if not data: | 
					
						
							|  |  |  |             return | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         self._ssl_protocol._write_appdata((data,)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def writelines(self, list_of_data): | 
					
						
							|  |  |  |         """Write a list (or any iterable) of data bytes to the transport.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         The default implementation concatenates the arguments and | 
					
						
							|  |  |  |         calls write() on the result. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         self._ssl_protocol._write_appdata(list_of_data) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def write_eof(self): | 
					
						
							|  |  |  |         """Close the write end after flushing buffered data.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         This raises :exc:`NotImplementedError` right now. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def can_write_eof(self): | 
					
						
							|  |  |  |         """Return True if this transport supports write_eof(), False if not.""" | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def abort(self): | 
					
						
							|  |  |  |         """Close the transport immediately.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Buffered data will be lost.  No more data will be received. | 
					
						
							|  |  |  |         The protocol's connection_lost() method will (eventually) be | 
					
						
							|  |  |  |         called with None as its argument. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         self._closed = True | 
					
						
							| 
									
										
										
										
											2023-05-14 19:58:13 +01:00
										 |  |  |         if self._ssl_protocol is not None: | 
					
						
							|  |  |  |             self._ssl_protocol._abort() | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  |     def _force_close(self, exc): | 
					
						
							| 
									
										
										
										
											2018-06-05 08:59:58 -04:00
										 |  |  |         self._closed = True | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         self._ssl_protocol._abort(exc) | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |     def _test__append_write_backlog(self, data): | 
					
						
							|  |  |  |         # for test only | 
					
						
							|  |  |  |         self._ssl_protocol._write_backlog.append(data) | 
					
						
							|  |  |  |         self._ssl_protocol._write_buffer_size += len(data) | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  | class SSLProtocol(protocols.BufferedProtocol): | 
					
						
							|  |  |  |     max_size = 256 * 1024   # Buffer size passed to read() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _handshake_start_time = None | 
					
						
							|  |  |  |     _handshake_timeout_handle = None | 
					
						
							|  |  |  |     _shutdown_timeout_handle = None | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, loop, app_protocol, sslcontext, waiter, | 
					
						
							| 
									
										
										
										
											2016-10-05 19:39:54 -04:00
										 |  |  |                  server_side=False, server_hostname=None, | 
					
						
							| 
									
										
										
										
											2017-12-19 19:45:42 +00:00
										 |  |  |                  call_connection_made=True, | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |                  ssl_handshake_timeout=None, | 
					
						
							|  |  |  |                  ssl_shutdown_timeout=None): | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |         if ssl is None: | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |             raise RuntimeError("stdlib ssl module not available") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self._ssl_buffer = bytearray(self.max_size) | 
					
						
							|  |  |  |         self._ssl_buffer_view = memoryview(self._ssl_buffer) | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-20 20:24:43 +02:00
										 |  |  |         if ssl_handshake_timeout is None: | 
					
						
							|  |  |  |             ssl_handshake_timeout = constants.SSL_HANDSHAKE_TIMEOUT | 
					
						
							|  |  |  |         elif ssl_handshake_timeout <= 0: | 
					
						
							|  |  |  |             raise ValueError( | 
					
						
							|  |  |  |                 f"ssl_handshake_timeout should be a positive number, " | 
					
						
							|  |  |  |                 f"got {ssl_handshake_timeout}") | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         if ssl_shutdown_timeout is None: | 
					
						
							|  |  |  |             ssl_shutdown_timeout = constants.SSL_SHUTDOWN_TIMEOUT | 
					
						
							|  |  |  |         elif ssl_shutdown_timeout <= 0: | 
					
						
							|  |  |  |             raise ValueError( | 
					
						
							|  |  |  |                 f"ssl_shutdown_timeout should be a positive number, " | 
					
						
							|  |  |  |                 f"got {ssl_shutdown_timeout}") | 
					
						
							| 
									
										
										
										
											2017-12-20 20:24:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |         if not sslcontext: | 
					
						
							| 
									
										
										
										
											2017-12-10 18:36:12 -05:00
										 |  |  |             sslcontext = _create_transport_context( | 
					
						
							|  |  |  |                 server_side, server_hostname) | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         self._server_side = server_side | 
					
						
							|  |  |  |         if server_hostname and not server_side: | 
					
						
							|  |  |  |             self._server_hostname = server_hostname | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self._server_hostname = None | 
					
						
							|  |  |  |         self._sslcontext = sslcontext | 
					
						
							|  |  |  |         # SSL-specific extra info. More info are set when the handshake | 
					
						
							|  |  |  |         # completes. | 
					
						
							|  |  |  |         self._extra = dict(sslcontext=sslcontext) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # App data write buffering | 
					
						
							|  |  |  |         self._write_backlog = collections.deque() | 
					
						
							|  |  |  |         self._write_buffer_size = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self._waiter = waiter | 
					
						
							|  |  |  |         self._loop = loop | 
					
						
							| 
									
										
										
										
											2018-05-29 05:02:40 -04:00
										 |  |  |         self._set_app_protocol(app_protocol) | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         self._app_transport = None | 
					
						
							|  |  |  |         self._app_transport_created = False | 
					
						
							| 
									
										
										
										
											2015-09-21 18:06:17 +02:00
										 |  |  |         # transport, ex: SelectorSocketTransport | 
					
						
							| 
									
										
										
										
											2015-01-15 13:16:27 +01:00
										 |  |  |         self._transport = None | 
					
						
							| 
									
										
										
										
											2017-12-19 19:45:42 +00:00
										 |  |  |         self._ssl_handshake_timeout = ssl_handshake_timeout | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         self._ssl_shutdown_timeout = ssl_shutdown_timeout | 
					
						
							|  |  |  |         # SSL and state machine | 
					
						
							|  |  |  |         self._incoming = ssl.MemoryBIO() | 
					
						
							|  |  |  |         self._outgoing = ssl.MemoryBIO() | 
					
						
							|  |  |  |         self._state = SSLProtocolState.UNWRAPPED | 
					
						
							|  |  |  |         self._conn_lost = 0  # Set when connection_lost called | 
					
						
							|  |  |  |         if call_connection_made: | 
					
						
							|  |  |  |             self._app_state = AppProtocolState.STATE_INIT | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self._app_state = AppProtocolState.STATE_CON_MADE | 
					
						
							|  |  |  |         self._sslobj = self._sslcontext.wrap_bio( | 
					
						
							|  |  |  |             self._incoming, self._outgoing, | 
					
						
							|  |  |  |             server_side=self._server_side, | 
					
						
							|  |  |  |             server_hostname=self._server_hostname) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Flow Control | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self._ssl_writing_paused = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self._app_reading_paused = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self._ssl_reading_paused = False | 
					
						
							|  |  |  |         self._incoming_high_water = 0 | 
					
						
							|  |  |  |         self._incoming_low_water = 0 | 
					
						
							|  |  |  |         self._set_read_buffer_limits() | 
					
						
							|  |  |  |         self._eof_received = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self._app_writing_paused = False | 
					
						
							|  |  |  |         self._outgoing_high_water = 0 | 
					
						
							|  |  |  |         self._outgoing_low_water = 0 | 
					
						
							|  |  |  |         self._set_write_buffer_limits() | 
					
						
							|  |  |  |         self._get_app_transport() | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-29 05:02:40 -04:00
										 |  |  |     def _set_app_protocol(self, app_protocol): | 
					
						
							|  |  |  |         self._app_protocol = app_protocol | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         # Make fast hasattr check first | 
					
						
							|  |  |  |         if (hasattr(app_protocol, 'get_buffer') and | 
					
						
							|  |  |  |                 isinstance(app_protocol, protocols.BufferedProtocol)): | 
					
						
							|  |  |  |             self._app_protocol_get_buffer = app_protocol.get_buffer | 
					
						
							|  |  |  |             self._app_protocol_buffer_updated = app_protocol.buffer_updated | 
					
						
							|  |  |  |             self._app_protocol_is_buffer = True | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self._app_protocol_is_buffer = False | 
					
						
							| 
									
										
										
										
											2018-05-29 05:02:40 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-29 00:36:35 +01:00
										 |  |  |     def _wakeup_waiter(self, exc=None): | 
					
						
							|  |  |  |         if self._waiter is None: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         if not self._waiter.cancelled(): | 
					
						
							|  |  |  |             if exc is not None: | 
					
						
							|  |  |  |                 self._waiter.set_exception(exc) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self._waiter.set_result(None) | 
					
						
							|  |  |  |         self._waiter = None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |     def _get_app_transport(self): | 
					
						
							|  |  |  |         if self._app_transport is None: | 
					
						
							|  |  |  |             if self._app_transport_created: | 
					
						
							|  |  |  |                 raise RuntimeError('Creating _SSLProtocolTransport twice') | 
					
						
							|  |  |  |             self._app_transport = _SSLProtocolTransport(self._loop, self) | 
					
						
							|  |  |  |             self._app_transport_created = True | 
					
						
							|  |  |  |         return self._app_transport | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |     def connection_made(self, transport): | 
					
						
							|  |  |  |         """Called when the low-level connection is made.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Start the SSL handshake. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         self._transport = transport | 
					
						
							|  |  |  |         self._start_handshake() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def connection_lost(self, exc): | 
					
						
							|  |  |  |         """Called when the low-level connection is lost or closed.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         The argument is an exception object or None (the latter | 
					
						
							|  |  |  |         meaning a regular EOF is received or the connection was | 
					
						
							|  |  |  |         aborted or closed). | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         self._write_backlog.clear() | 
					
						
							|  |  |  |         self._outgoing.read() | 
					
						
							|  |  |  |         self._conn_lost += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Just mark the app transport as closed so that its __dealloc__ | 
					
						
							|  |  |  |         # doesn't complain. | 
					
						
							|  |  |  |         if self._app_transport is not None: | 
					
						
							|  |  |  |             self._app_transport._closed = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if self._state != SSLProtocolState.DO_HANDSHAKE: | 
					
						
							|  |  |  |             if ( | 
					
						
							|  |  |  |                 self._app_state == AppProtocolState.STATE_CON_MADE or | 
					
						
							|  |  |  |                 self._app_state == AppProtocolState.STATE_EOF | 
					
						
							|  |  |  |             ): | 
					
						
							|  |  |  |                 self._app_state = AppProtocolState.STATE_CON_LOST | 
					
						
							|  |  |  |                 self._loop.call_soon(self._app_protocol.connection_lost, exc) | 
					
						
							|  |  |  |         self._set_state(SSLProtocolState.UNWRAPPED) | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |         self._transport = None | 
					
						
							|  |  |  |         self._app_transport = None | 
					
						
							| 
									
										
										
										
											2021-05-03 16:21:59 +01:00
										 |  |  |         self._app_protocol = None | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         self._wakeup_waiter(exc) | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         if self._shutdown_timeout_handle: | 
					
						
							|  |  |  |             self._shutdown_timeout_handle.cancel() | 
					
						
							|  |  |  |             self._shutdown_timeout_handle = None | 
					
						
							|  |  |  |         if self._handshake_timeout_handle: | 
					
						
							|  |  |  |             self._handshake_timeout_handle.cancel() | 
					
						
							|  |  |  |             self._handshake_timeout_handle = None | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |     def get_buffer(self, n): | 
					
						
							|  |  |  |         want = n | 
					
						
							|  |  |  |         if want <= 0 or want > self.max_size: | 
					
						
							|  |  |  |             want = self.max_size | 
					
						
							|  |  |  |         if len(self._ssl_buffer) < want: | 
					
						
							|  |  |  |             self._ssl_buffer = bytearray(want) | 
					
						
							|  |  |  |             self._ssl_buffer_view = memoryview(self._ssl_buffer) | 
					
						
							|  |  |  |         return self._ssl_buffer_view | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |     def buffer_updated(self, nbytes): | 
					
						
							|  |  |  |         self._incoming.write(self._ssl_buffer_view[:nbytes]) | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         if self._state == SSLProtocolState.DO_HANDSHAKE: | 
					
						
							|  |  |  |             self._do_handshake() | 
					
						
							| 
									
										
										
										
											2018-03-10 17:48:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         elif self._state == SSLProtocolState.WRAPPED: | 
					
						
							|  |  |  |             self._do_read() | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         elif self._state == SSLProtocolState.FLUSHING: | 
					
						
							|  |  |  |             self._do_flush() | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         elif self._state == SSLProtocolState.SHUTDOWN: | 
					
						
							|  |  |  |             self._do_shutdown() | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def eof_received(self): | 
					
						
							|  |  |  |         """Called when the other end of the low-level stream
 | 
					
						
							|  |  |  |         is half-closed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         If this returns a false value (including None), the transport | 
					
						
							|  |  |  |         will close itself.  If it returns a true value, closing the | 
					
						
							|  |  |  |         transport is up to the protocol. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         self._eof_received = True | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |         try: | 
					
						
							|  |  |  |             if self._loop.get_debug(): | 
					
						
							|  |  |  |                 logger.debug("%r received EOF", self) | 
					
						
							| 
									
										
										
										
											2015-01-29 00:35:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |             if self._state == SSLProtocolState.DO_HANDSHAKE: | 
					
						
							|  |  |  |                 self._on_handshake_complete(ConnectionResetError) | 
					
						
							| 
									
										
										
										
											2021-05-03 00:34:15 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |             elif self._state == SSLProtocolState.WRAPPED: | 
					
						
							|  |  |  |                 self._set_state(SSLProtocolState.FLUSHING) | 
					
						
							|  |  |  |                 if self._app_reading_paused: | 
					
						
							|  |  |  |                     return True | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     self._do_flush() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             elif self._state == SSLProtocolState.FLUSHING: | 
					
						
							|  |  |  |                 self._do_write() | 
					
						
							|  |  |  |                 self._set_state(SSLProtocolState.SHUTDOWN) | 
					
						
							|  |  |  |                 self._do_shutdown() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             elif self._state == SSLProtocolState.SHUTDOWN: | 
					
						
							|  |  |  |                 self._do_shutdown() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         except Exception: | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |             self._transport.close() | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |             raise | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _get_extra_info(self, name, default=None): | 
					
						
							|  |  |  |         if name in self._extra: | 
					
						
							|  |  |  |             return self._extra[name] | 
					
						
							| 
									
										
										
										
											2017-03-12 12:23:30 -07:00
										 |  |  |         elif self._transport is not None: | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |             return self._transport.get_extra_info(name, default) | 
					
						
							| 
									
										
										
										
											2017-03-12 12:23:30 -07:00
										 |  |  |         else: | 
					
						
							|  |  |  |             return default | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |     def _set_state(self, new_state): | 
					
						
							|  |  |  |         allowed = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if new_state == SSLProtocolState.UNWRAPPED: | 
					
						
							|  |  |  |             allowed = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         elif ( | 
					
						
							|  |  |  |             self._state == SSLProtocolState.UNWRAPPED and | 
					
						
							|  |  |  |             new_state == SSLProtocolState.DO_HANDSHAKE | 
					
						
							|  |  |  |         ): | 
					
						
							|  |  |  |             allowed = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         elif ( | 
					
						
							|  |  |  |             self._state == SSLProtocolState.DO_HANDSHAKE and | 
					
						
							|  |  |  |             new_state == SSLProtocolState.WRAPPED | 
					
						
							|  |  |  |         ): | 
					
						
							|  |  |  |             allowed = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         elif ( | 
					
						
							|  |  |  |             self._state == SSLProtocolState.WRAPPED and | 
					
						
							|  |  |  |             new_state == SSLProtocolState.FLUSHING | 
					
						
							|  |  |  |         ): | 
					
						
							|  |  |  |             allowed = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         elif ( | 
					
						
							|  |  |  |             self._state == SSLProtocolState.FLUSHING and | 
					
						
							|  |  |  |             new_state == SSLProtocolState.SHUTDOWN | 
					
						
							|  |  |  |         ): | 
					
						
							|  |  |  |             allowed = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if allowed: | 
					
						
							|  |  |  |             self._state = new_state | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-09 14:46:14 -07:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |             raise RuntimeError( | 
					
						
							|  |  |  |                 'cannot switch state from {} to {}'.format( | 
					
						
							|  |  |  |                     self._state, new_state)) | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |     # Handshake flow | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _start_handshake(self): | 
					
						
							|  |  |  |         if self._loop.get_debug(): | 
					
						
							|  |  |  |             logger.debug("%r starts SSL handshake", self) | 
					
						
							|  |  |  |             self._handshake_start_time = self._loop.time() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self._handshake_start_time = None | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  |         self._set_state(SSLProtocolState.DO_HANDSHAKE) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # start handshake timeout count down | 
					
						
							| 
									
										
										
										
											2017-12-19 19:45:42 +00:00
										 |  |  |         self._handshake_timeout_handle = \ | 
					
						
							|  |  |  |             self._loop.call_later(self._ssl_handshake_timeout, | 
					
						
							| 
									
										
										
										
											2023-09-05 18:11:12 +03:00
										 |  |  |                                   self._check_handshake_timeout) | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  |         self._do_handshake() | 
					
						
							| 
									
										
										
										
											2017-12-19 19:45:42 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _check_handshake_timeout(self): | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         if self._state == SSLProtocolState.DO_HANDSHAKE: | 
					
						
							| 
									
										
										
										
											2018-06-04 11:32:35 -04:00
										 |  |  |             msg = ( | 
					
						
							|  |  |  |                 f"SSL handshake is taking longer than " | 
					
						
							|  |  |  |                 f"{self._ssl_handshake_timeout} seconds: " | 
					
						
							|  |  |  |                 f"aborting the connection" | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |             self._fatal_error(ConnectionAbortedError(msg)) | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |     def _do_handshake(self): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             self._sslobj.do_handshake() | 
					
						
							|  |  |  |         except SSLAgainErrors: | 
					
						
							|  |  |  |             self._process_outgoing() | 
					
						
							|  |  |  |         except ssl.SSLError as exc: | 
					
						
							|  |  |  |             self._on_handshake_complete(exc) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self._on_handshake_complete(None) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |     def _on_handshake_complete(self, handshake_exc): | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         if self._handshake_timeout_handle is not None: | 
					
						
							|  |  |  |             self._handshake_timeout_handle.cancel() | 
					
						
							|  |  |  |             self._handshake_timeout_handle = None | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         sslobj = self._sslobj | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |             if handshake_exc is None: | 
					
						
							|  |  |  |                 self._set_state(SSLProtocolState.WRAPPED) | 
					
						
							|  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |                 raise handshake_exc | 
					
						
							| 
									
										
										
										
											2015-01-14 16:56:20 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             peercert = sslobj.getpeercert() | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         except Exception as exc: | 
					
						
							|  |  |  |             self._set_state(SSLProtocolState.UNWRAPPED) | 
					
						
							| 
									
										
										
										
											2018-06-04 11:32:35 -04:00
										 |  |  |             if isinstance(exc, ssl.CertificateError): | 
					
						
							|  |  |  |                 msg = 'SSL handshake failed on verifying the certificate' | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2018-06-04 11:32:35 -04:00
										 |  |  |                 msg = 'SSL handshake failed' | 
					
						
							|  |  |  |             self._fatal_error(exc, msg) | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |             self._wakeup_waiter(exc) | 
					
						
							| 
									
										
										
										
											2018-06-04 11:32:35 -04:00
										 |  |  |             return | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if self._loop.get_debug(): | 
					
						
							|  |  |  |             dt = self._loop.time() - self._handshake_start_time | 
					
						
							|  |  |  |             logger.debug("%r: SSL handshake took %.1f ms", self, dt * 1e3) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Add extra info that becomes available after handshake. | 
					
						
							|  |  |  |         self._extra.update(peercert=peercert, | 
					
						
							|  |  |  |                            cipher=sslobj.cipher(), | 
					
						
							|  |  |  |                            compression=sslobj.compression(), | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |                            ssl_object=sslobj) | 
					
						
							|  |  |  |         if self._app_state == AppProtocolState.STATE_INIT: | 
					
						
							|  |  |  |             self._app_state = AppProtocolState.STATE_CON_MADE | 
					
						
							|  |  |  |             self._app_protocol.connection_made(self._get_app_transport()) | 
					
						
							| 
									
										
										
										
											2015-01-29 00:36:35 +01:00
										 |  |  |         self._wakeup_waiter() | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         self._do_read() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Shutdown flow | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _start_shutdown(self): | 
					
						
							|  |  |  |         if ( | 
					
						
							|  |  |  |             self._state in ( | 
					
						
							|  |  |  |                 SSLProtocolState.FLUSHING, | 
					
						
							|  |  |  |                 SSLProtocolState.SHUTDOWN, | 
					
						
							|  |  |  |                 SSLProtocolState.UNWRAPPED | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |         ): | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         if self._app_transport is not None: | 
					
						
							|  |  |  |             self._app_transport._closed = True | 
					
						
							|  |  |  |         if self._state == SSLProtocolState.DO_HANDSHAKE: | 
					
						
							|  |  |  |             self._abort() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self._set_state(SSLProtocolState.FLUSHING) | 
					
						
							|  |  |  |             self._shutdown_timeout_handle = self._loop.call_later( | 
					
						
							|  |  |  |                 self._ssl_shutdown_timeout, | 
					
						
							| 
									
										
										
										
											2023-09-05 18:11:12 +03:00
										 |  |  |                 self._check_shutdown_timeout | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |             ) | 
					
						
							|  |  |  |             self._do_flush() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _check_shutdown_timeout(self): | 
					
						
							|  |  |  |         if ( | 
					
						
							|  |  |  |             self._state in ( | 
					
						
							|  |  |  |                 SSLProtocolState.FLUSHING, | 
					
						
							|  |  |  |                 SSLProtocolState.SHUTDOWN | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |         ): | 
					
						
							|  |  |  |             self._transport._force_close( | 
					
						
							|  |  |  |                 exceptions.TimeoutError('SSL shutdown timed out')) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _do_flush(self): | 
					
						
							|  |  |  |         self._do_read() | 
					
						
							|  |  |  |         self._set_state(SSLProtocolState.SHUTDOWN) | 
					
						
							|  |  |  |         self._do_shutdown() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _do_shutdown(self): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             if not self._eof_received: | 
					
						
							|  |  |  |                 self._sslobj.unwrap() | 
					
						
							|  |  |  |         except SSLAgainErrors: | 
					
						
							|  |  |  |             self._process_outgoing() | 
					
						
							|  |  |  |         except ssl.SSLError as exc: | 
					
						
							|  |  |  |             self._on_shutdown_complete(exc) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self._process_outgoing() | 
					
						
							|  |  |  |             self._call_eof_received() | 
					
						
							|  |  |  |             self._on_shutdown_complete(None) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _on_shutdown_complete(self, shutdown_exc): | 
					
						
							|  |  |  |         if self._shutdown_timeout_handle is not None: | 
					
						
							|  |  |  |             self._shutdown_timeout_handle.cancel() | 
					
						
							|  |  |  |             self._shutdown_timeout_handle = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if shutdown_exc: | 
					
						
							|  |  |  |             self._fatal_error(shutdown_exc) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self._loop.call_soon(self._transport.close) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _abort(self): | 
					
						
							|  |  |  |         self._set_state(SSLProtocolState.UNWRAPPED) | 
					
						
							|  |  |  |         if self._transport is not None: | 
					
						
							|  |  |  |             self._transport.abort() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Outgoing flow | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _write_appdata(self, list_of_data): | 
					
						
							|  |  |  |         if ( | 
					
						
							|  |  |  |             self._state in ( | 
					
						
							|  |  |  |                 SSLProtocolState.FLUSHING, | 
					
						
							|  |  |  |                 SSLProtocolState.SHUTDOWN, | 
					
						
							|  |  |  |                 SSLProtocolState.UNWRAPPED | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |         ): | 
					
						
							|  |  |  |             if self._conn_lost >= constants.LOG_THRESHOLD_FOR_CONNLOST_WRITES: | 
					
						
							|  |  |  |                 logger.warning('SSL connection is closed') | 
					
						
							|  |  |  |             self._conn_lost += 1 | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |             return | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         for data in list_of_data: | 
					
						
							|  |  |  |             self._write_backlog.append(data) | 
					
						
							|  |  |  |             self._write_buffer_size += len(data) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |             if self._state == SSLProtocolState.WRAPPED: | 
					
						
							|  |  |  |                 self._do_write() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         except Exception as ex: | 
					
						
							|  |  |  |             self._fatal_error(ex, 'Fatal error on SSL protocol') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _do_write(self): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             while self._write_backlog: | 
					
						
							|  |  |  |                 data = self._write_backlog[0] | 
					
						
							|  |  |  |                 count = self._sslobj.write(data) | 
					
						
							|  |  |  |                 data_len = len(data) | 
					
						
							|  |  |  |                 if count < data_len: | 
					
						
							|  |  |  |                     self._write_backlog[0] = data[count:] | 
					
						
							|  |  |  |                     self._write_buffer_size -= count | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |                 else: | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |                     del self._write_backlog[0] | 
					
						
							|  |  |  |                     self._write_buffer_size -= data_len | 
					
						
							|  |  |  |         except SSLAgainErrors: | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  |         self._process_outgoing() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _process_outgoing(self): | 
					
						
							|  |  |  |         if not self._ssl_writing_paused: | 
					
						
							|  |  |  |             data = self._outgoing.read() | 
					
						
							|  |  |  |             if len(data): | 
					
						
							|  |  |  |                 self._transport.write(data) | 
					
						
							|  |  |  |         self._control_app_writing() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Incoming flow | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _do_read(self): | 
					
						
							|  |  |  |         if ( | 
					
						
							|  |  |  |             self._state not in ( | 
					
						
							|  |  |  |                 SSLProtocolState.WRAPPED, | 
					
						
							|  |  |  |                 SSLProtocolState.FLUSHING, | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |         ): | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             if not self._app_reading_paused: | 
					
						
							|  |  |  |                 if self._app_protocol_is_buffer: | 
					
						
							|  |  |  |                     self._do_read__buffered() | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     self._do_read__copied() | 
					
						
							|  |  |  |                 if self._write_backlog: | 
					
						
							|  |  |  |                     self._do_write() | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     self._process_outgoing() | 
					
						
							|  |  |  |             self._control_ssl_reading() | 
					
						
							|  |  |  |         except Exception as ex: | 
					
						
							|  |  |  |             self._fatal_error(ex, 'Fatal error on SSL protocol') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _do_read__buffered(self): | 
					
						
							|  |  |  |         offset = 0 | 
					
						
							|  |  |  |         count = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         buf = self._app_protocol_get_buffer(self._get_read_buffer_size()) | 
					
						
							|  |  |  |         wants = len(buf) | 
					
						
							| 
									
										
										
										
											2021-05-03 00:34:15 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         try: | 
					
						
							|  |  |  |             count = self._sslobj.read(wants, buf) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if count > 0: | 
					
						
							|  |  |  |                 offset = count | 
					
						
							|  |  |  |                 while offset < wants: | 
					
						
							|  |  |  |                     count = self._sslobj.read(wants - offset, buf[offset:]) | 
					
						
							|  |  |  |                     if count > 0: | 
					
						
							|  |  |  |                         offset += count | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         break | 
					
						
							|  |  |  |                 else: | 
					
						
							| 
									
										
										
										
											2023-09-05 18:11:12 +03:00
										 |  |  |                     self._loop.call_soon(self._do_read) | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         except SSLAgainErrors: | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  |         if offset > 0: | 
					
						
							|  |  |  |             self._app_protocol_buffer_updated(offset) | 
					
						
							|  |  |  |         if not count: | 
					
						
							|  |  |  |             # close_notify | 
					
						
							|  |  |  |             self._call_eof_received() | 
					
						
							|  |  |  |             self._start_shutdown() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _do_read__copied(self): | 
					
						
							|  |  |  |         chunk = b'1' | 
					
						
							|  |  |  |         zero = True | 
					
						
							|  |  |  |         one = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             while True: | 
					
						
							|  |  |  |                 chunk = self._sslobj.read(self.max_size) | 
					
						
							|  |  |  |                 if not chunk: | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  |                 if zero: | 
					
						
							|  |  |  |                     zero = False | 
					
						
							|  |  |  |                     one = True | 
					
						
							|  |  |  |                     first = chunk | 
					
						
							|  |  |  |                 elif one: | 
					
						
							|  |  |  |                     one = False | 
					
						
							|  |  |  |                     data = [first, chunk] | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     data.append(chunk) | 
					
						
							|  |  |  |         except SSLAgainErrors: | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  |         if one: | 
					
						
							|  |  |  |             self._app_protocol.data_received(first) | 
					
						
							|  |  |  |         elif not zero: | 
					
						
							|  |  |  |             self._app_protocol.data_received(b''.join(data)) | 
					
						
							|  |  |  |         if not chunk: | 
					
						
							|  |  |  |             # close_notify | 
					
						
							|  |  |  |             self._call_eof_received() | 
					
						
							|  |  |  |             self._start_shutdown() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _call_eof_received(self): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             if self._app_state == AppProtocolState.STATE_CON_MADE: | 
					
						
							|  |  |  |                 self._app_state = AppProtocolState.STATE_EOF | 
					
						
							|  |  |  |                 keep_open = self._app_protocol.eof_received() | 
					
						
							|  |  |  |                 if keep_open: | 
					
						
							|  |  |  |                     logger.warning('returning true from eof_received() ' | 
					
						
							|  |  |  |                                    'has no effect when using ssl') | 
					
						
							|  |  |  |         except (KeyboardInterrupt, SystemExit): | 
					
						
							| 
									
										
										
										
											2021-05-03 16:21:59 +01:00
										 |  |  |             raise | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         except BaseException as ex: | 
					
						
							|  |  |  |             self._fatal_error(ex, 'Error calling eof_received()') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Flow control for writes from APP socket | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _control_app_writing(self): | 
					
						
							|  |  |  |         size = self._get_write_buffer_size() | 
					
						
							|  |  |  |         if size >= self._outgoing_high_water and not self._app_writing_paused: | 
					
						
							|  |  |  |             self._app_writing_paused = True | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 self._app_protocol.pause_writing() | 
					
						
							|  |  |  |             except (KeyboardInterrupt, SystemExit): | 
					
						
							|  |  |  |                 raise | 
					
						
							|  |  |  |             except BaseException as exc: | 
					
						
							|  |  |  |                 self._loop.call_exception_handler({ | 
					
						
							|  |  |  |                     'message': 'protocol.pause_writing() failed', | 
					
						
							|  |  |  |                     'exception': exc, | 
					
						
							|  |  |  |                     'transport': self._app_transport, | 
					
						
							|  |  |  |                     'protocol': self, | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  |         elif size <= self._outgoing_low_water and self._app_writing_paused: | 
					
						
							|  |  |  |             self._app_writing_paused = False | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 self._app_protocol.resume_writing() | 
					
						
							|  |  |  |             except (KeyboardInterrupt, SystemExit): | 
					
						
							|  |  |  |                 raise | 
					
						
							|  |  |  |             except BaseException as exc: | 
					
						
							|  |  |  |                 self._loop.call_exception_handler({ | 
					
						
							|  |  |  |                     'message': 'protocol.resume_writing() failed', | 
					
						
							|  |  |  |                     'exception': exc, | 
					
						
							|  |  |  |                     'transport': self._app_transport, | 
					
						
							|  |  |  |                     'protocol': self, | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _get_write_buffer_size(self): | 
					
						
							|  |  |  |         return self._outgoing.pending + self._write_buffer_size | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _set_write_buffer_limits(self, high=None, low=None): | 
					
						
							|  |  |  |         high, low = add_flowcontrol_defaults( | 
					
						
							|  |  |  |             high, low, constants.FLOW_CONTROL_HIGH_WATER_SSL_WRITE) | 
					
						
							|  |  |  |         self._outgoing_high_water = high | 
					
						
							|  |  |  |         self._outgoing_low_water = low | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Flow control for reads to APP socket | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _pause_reading(self): | 
					
						
							|  |  |  |         self._app_reading_paused = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _resume_reading(self): | 
					
						
							|  |  |  |         if self._app_reading_paused: | 
					
						
							|  |  |  |             self._app_reading_paused = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             def resume(): | 
					
						
							|  |  |  |                 if self._state == SSLProtocolState.WRAPPED: | 
					
						
							|  |  |  |                     self._do_read() | 
					
						
							|  |  |  |                 elif self._state == SSLProtocolState.FLUSHING: | 
					
						
							|  |  |  |                     self._do_flush() | 
					
						
							|  |  |  |                 elif self._state == SSLProtocolState.SHUTDOWN: | 
					
						
							|  |  |  |                     self._do_shutdown() | 
					
						
							|  |  |  |             self._loop.call_soon(resume) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Flow control for reads from SSL socket | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _control_ssl_reading(self): | 
					
						
							|  |  |  |         size = self._get_read_buffer_size() | 
					
						
							|  |  |  |         if size >= self._incoming_high_water and not self._ssl_reading_paused: | 
					
						
							|  |  |  |             self._ssl_reading_paused = True | 
					
						
							|  |  |  |             self._transport.pause_reading() | 
					
						
							|  |  |  |         elif size <= self._incoming_low_water and self._ssl_reading_paused: | 
					
						
							|  |  |  |             self._ssl_reading_paused = False | 
					
						
							|  |  |  |             self._transport.resume_reading() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _set_read_buffer_limits(self, high=None, low=None): | 
					
						
							|  |  |  |         high, low = add_flowcontrol_defaults( | 
					
						
							|  |  |  |             high, low, constants.FLOW_CONTROL_HIGH_WATER_SSL_READ) | 
					
						
							|  |  |  |         self._incoming_high_water = high | 
					
						
							|  |  |  |         self._incoming_low_water = low | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _get_read_buffer_size(self): | 
					
						
							|  |  |  |         return self._incoming.pending | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Flow control for writes to SSL socket | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def pause_writing(self): | 
					
						
							|  |  |  |         """Called when the low-level transport's buffer goes over
 | 
					
						
							|  |  |  |         the high-water mark. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         assert not self._ssl_writing_paused | 
					
						
							|  |  |  |         self._ssl_writing_paused = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def resume_writing(self): | 
					
						
							|  |  |  |         """Called when the low-level transport's buffer drains below
 | 
					
						
							|  |  |  |         the low-water mark. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         assert self._ssl_writing_paused | 
					
						
							|  |  |  |         self._ssl_writing_paused = False | 
					
						
							|  |  |  |         self._process_outgoing() | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _fatal_error(self, exc, message='Fatal error on transport'): | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         if self._transport: | 
					
						
							|  |  |  |             self._transport._force_close(exc) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-27 16:28:34 +03:00
										 |  |  |         if isinstance(exc, OSError): | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |             if self._loop.get_debug(): | 
					
						
							|  |  |  |                 logger.debug("%r: %s", self, message, exc_info=True) | 
					
						
							| 
									
										
										
										
											2022-02-15 18:34:00 +05:30
										 |  |  |         elif not isinstance(exc, exceptions.CancelledError): | 
					
						
							| 
									
										
										
										
											2015-01-14 00:19:09 +01:00
										 |  |  |             self._loop.call_exception_handler({ | 
					
						
							|  |  |  |                 'message': message, | 
					
						
							|  |  |  |                 'exception': exc, | 
					
						
							|  |  |  |                 'transport': self._transport, | 
					
						
							|  |  |  |                 'protocol': self, | 
					
						
							|  |  |  |             }) |