o
    US`c                     @   s   d dl Zd dlZd dlmZ d dlZddlm	Z	m
Z
 ddlmZ ddlmZ ddlmZmZ dZd	d
 ZG dd deZG dd dZedg dZG dd de	edZG dd de
e edZdS )    N)Enum   )StreamListener)aclose_forcefully)_sync)ConflictDetectorFinali @  c                 C   s    t | tjpt| dod| jv S )NstrerrorZUNEXPECTED_EOF_WHILE_READING)
isinstance_stdlib_sslSSLEOFErrorhasattrr
   )exc r   4/usr/local/lib/python3.10/dist-packages/trio/_ssl.py_is_eof   s   r   c                   @   s   e Zd ZdZdS )NeedHandshakeErrorzSome :class:`SSLStream` methods can't return any meaningful data until
    after the handshake. If you call them before the handshake, they raise
    this error.

    N)__name__
__module____qualname____doc__r   r   r   r   r      s    r   c                   @   s(   e Zd Zdd Zdd Zedd ZdS )_Oncec                 G   s    || _ || _d| _t | _d S )NF)_afn_argsstartedr   Event_done)selfZafnargsr   r   r   __init__   s   z_Once.__init__c                   sT   | j sd| _ | j| j I d H  | j  d S |s | j r d S | j I d H  d S )NT)r   r   r   r   setis_setwait)r   
checkpointr   r   r   ensure   s   z_Once.ensurec                 C   s
   | j  S N)r   r"   r   r   r   r   done   s   
z
_Once.doneN)r   r   r   r    r%   propertyr(   r   r   r   r   r      s
    
r   _State)OKBROKENCLOSEDc                       s   e Zd ZdZddddddZh dZh dZd	d
 Z fddZ fddZ	dd Z
dddddZdd Zdd Zd"ddZdd Zdd Zdd Zd d! Z  ZS )#	SSLStreama  Encrypted communication using SSL/TLS.

    :class:`SSLStream` wraps an arbitrary :class:`~trio.abc.Stream`, and
    allows you to perform encrypted communication over it using the usual
    :class:`~trio.abc.Stream` interface. You pass regular data to
    :meth:`send_all`, then it encrypts it and sends the encrypted data on the
    underlying :class:`~trio.abc.Stream`; :meth:`receive_some` takes encrypted
    data out of the underlying :class:`~trio.abc.Stream` and decrypts it
    before returning it.

    You should read the standard library's :mod:`ssl` documentation carefully
    before attempting to use this class, and probably other general
    documentation on SSL/TLS as well. SSL/TLS is subtle and quick to
    anger. Really. I'm not kidding.

    Args:
      transport_stream (~trio.abc.Stream): The stream used to transport
          encrypted data. Required.

      ssl_context (~ssl.SSLContext): The :class:`~ssl.SSLContext` used for
          this connection. Required. Usually created by calling
          :func:`ssl.create_default_context`.

      server_hostname (str or None): The name of the server being connected
          to. Used for `SNI
          <https://en.wikipedia.org/wiki/Server_Name_Indication>`__ and for
          validating the server's certificate (if hostname checking is
          enabled). This is effectively mandatory for clients, and actually
          mandatory if ``ssl_context.check_hostname`` is ``True``.

      server_side (bool): Whether this stream is acting as a client or
          server. Defaults to False, i.e. client mode.

      https_compatible (bool): There are two versions of SSL/TLS commonly
          encountered in the wild: the standard version, and the version used
          for HTTPS (HTTP-over-SSL/TLS).

          Standard-compliant SSL/TLS implementations always send a
          cryptographically signed ``close_notify`` message before closing the
          connection. This is important because if the underlying transport
          were simply closed, then there wouldn't be any way for the other
          side to know whether the connection was intentionally closed by the
          peer that they negotiated a cryptographic connection to, or by some
          `man-in-the-middle
          <https://en.wikipedia.org/wiki/Man-in-the-middle_attack>`__ attacker
          who can't manipulate the cryptographic stream, but can manipulate
          the transport layer (a so-called "truncation attack").

          However, this part of the standard is widely ignored by real-world
          HTTPS implementations, which means that if you want to interoperate
          with them, then you NEED to ignore it too.

          Fortunately this isn't as bad as it sounds, because the HTTP
          protocol already includes its own equivalent of ``close_notify``, so
          doing this again at the SSL/TLS level is redundant. But not all
          protocols do! Therefore, by default Trio implements the safer
          standard-compliant version (``https_compatible=False``). But if
          you're speaking HTTPS or some other protocol where
          ``close_notify``\s are commonly skipped, then you should set
          ``https_compatible=True``; with this setting, Trio will neither
          expect nor send ``close_notify`` messages.

          If you have code that was written to use :class:`ssl.SSLSocket` and
          now you're porting it to Trio, then it may be useful to know that a
          difference between :class:`SSLStream` and :class:`ssl.SSLSocket` is
          that :class:`~ssl.SSLSocket` implements the
          ``https_compatible=True`` behavior by default.

    Attributes:
      transport_stream (trio.abc.Stream): The underlying transport stream
          that was passed to ``__init__``. An example of when this would be
          useful is if you're using :class:`SSLStream` over a
          :class:`~trio.SocketStream` and want to call the
          :class:`~trio.SocketStream`'s :meth:`~trio.SocketStream.setsockopt`
          method.

    Internally, this class is implemented using an instance of
    :class:`ssl.SSLObject`, and all of :class:`~ssl.SSLObject`'s methods and
    attributes are re-exported as methods and attributes on this class.
    However, there is one difference: :class:`~ssl.SSLObject` has several
    methods that return information about the encrypted connection, like
    :meth:`~ssl.SSLSocket.cipher` or
    :meth:`~ssl.SSLSocket.selected_alpn_protocol`. If you call them before the
    handshake, when they can't possibly return useful data, then
    :class:`ssl.SSLObject` returns None, but :class:`trio.SSLStream`
    raises :exc:`NeedHandshakeError`.

    This also means that if you register a SNI callback using
    `~ssl.SSLContext.sni_callback`, then the first argument your callback
    receives will be a :class:`ssl.SSLObject`.

    NF)server_hostnameserver_sidehttps_compatiblec                C   s   || _ tj| _|| _t | _d | _t | _	|j
| j	| j||d| _t| j| _t | _d| _t | _td| _td| _t| _d S )N)r0   r/   r   z8another task is currently sending data on this SSLStreamz:another task is currently receiving data on this SSLStream)transport_streamr*   r+   _state_https_compatibler   	MemoryBIO	_outgoing_delayed_outgoing	_incomingwrap_bio_ssl_objectr   _do_handshake
_handshookr   ZStrictFIFOLock_inner_send_lock_inner_recv_countLock_inner_recv_lockr   _outer_send_conflict_detector_outer_recv_conflict_detectorSTARTING_RECEIVE_SIZE_estimated_receive_size)r   r2   ssl_contextr/   r0   r1   r   r   r   r    L  s.   	




zSSLStream.__init__>   ciphercontextpendingsessionversioncompressiongetpeercertr0   session_reusedshared_ciphersr/   get_channel_bindingselected_npn_protocolselected_alpn_protocol>	   rF   rJ   rK   rL   rM   rN   rO   rP   rQ   c                 C   s>   || j v r|| jv r| jjstd|t| j|S t|)Nz'call do_handshake() before calling {!r})	
_forwarded_after_handshaker<   r(   r   formatgetattrr:   AttributeError)r   namer   r   r   __getattr__  s   
zSSLStream.__getattr__c                    s.   || j v rt| j|| d S t || d S r&   )rR   setattrr:   super__setattr__)r   rW   value	__class__r   r   r[     s   
zSSLStream.__setattr__c                    s   t   t| j S r&   )rZ   __dir__listrR   r'   r]   r   r   r_     s   zSSLStream.__dir__c                 C   s8   | j tju rd S | j tju rtj| j tju rtjJ r&   )r3   r*   r+   r,   trioBrokenResourceErrorr-   ZClosedResourceErrorr'   r   r   r   _check_status  s   zSSLStream._check_status)ignore_want_readis_handshakec             
      s  t j I d H  d}d}|sd}d }z|| }W n# tjy%   d}Y n tjtjfy< }	 ztj| _	t j
|	d }	~	ww d}|rEd}d}| j }
|re|se| jjre| j dkre| jd u s`J |
| _d}
|
r| j4 I d H . d}z| jd ur| j|
 }
d | _| j|
I d H  W n   tj| _	 W d   I d H  n1 I d H sw   Y  nO|r| j}| j4 I d H 8 d}|| jkr| j I d H }|s| j  nt| jt|| _| j| |  jd7  _W d   I d H  n1 I d H sw   Y  |r|st j I d H  |S )NFTzTLSv1.3    r   )ra   lowlevelZcheckpoint_if_cancelledr   SSLWantReadErrorSSLErrorCertificateErrorr*   r,   r3   rb   r6   readr:   r0   rJ   r7   r=   r2   send_allr>   r@   receive_somer8   	write_eofmaxrD   lenwriteZcancel_shielded_checkpoint)r   fnrd   re   r   yieldedfinishedZ	want_readretr   to_sendZ
recv_countdatar   r   r   _retry  s   
;

(

(  %zSSLStream._retryc                    s2   z| j | jjddI d H  W d S    tj| _ )NT)re   )rx   r:   do_handshaker*   r,   r3   r'   r   r   r   r;   `  s   zSSLStream._do_handshakec                    s"   |    | jjddI dH  dS )u  Ensure that the initial handshake has completed.

        The SSL protocol requires an initial handshake to exchange
        certificates, select cryptographic keys, and so forth, before any
        actual data can be sent or received. You don't have to call this
        method; if you don't, then :class:`SSLStream` will automatically
        perform the handshake as needed, the first time you try to send or
        receive data. But if you want to trigger it manually – for example,
        because you want to look at the peer's certificate before you start
        talking to them – then you can call this method.

        If the initial handshake is already in progress in another task, this
        waits for it to complete and then returns.

        If the initial handshake has already completed, this returns
        immediately without doing anything (except executing a checkpoint).

        .. warning:: If this method is cancelled, then it may leave the
           :class:`SSLStream` in an unusable state. If this happens then any
           future attempt to use the object will raise
           :exc:`trio.BrokenResourceError`.

        Tr$   N)rc   r<   r%   r'   r   r   r   ry   g  s   zSSLStream.do_handshakec                    sV  | j  |   z| jjddI dH  W n2 tjyG } z%| jrBt|jt	j
s-t|jrBtj I dH  W Y d}~W d   dS  d}~ww |du rUt| j| jj}nt|}|dk rbtdz| | jj|I dH W W  d   S  tjy } z| jrt|jrtj I dH  W Y d}~W d   dS  d}~ww 1 sw   Y  dS )a  Read some data from the underlying transport, decrypt it, and
        return it.

        See :meth:`trio.abc.ReceiveStream.receive_some` for details.

        .. warning:: If this method is cancelled while the initial handshake
           or a renegotiation are in progress, then it may leave the
           :class:`SSLStream` in an unusable state. If this happens then any
           future attempt to use the object will raise
           :exc:`trio.BrokenResourceError`.

        Frz   Nrf   r   zmax_bytes must be >= 1)rB   rc   r<   r%   ra   rb   r4   r   	__cause__r   SSLSyscallErrorr   rg   r$   ro   rD   r8   rH   	_operatorindex
ValueErrorrx   r:   rk   )r   Z	max_bytesr   r   r   r   rm     sD   


&zSSLStream.receive_somec                    s   | j 5 |   | jjddI dH  |s&tj I dH  	 W d   dS | | jj	|I dH  W d   dS 1 s<w   Y  dS )az  Encrypt some data and then send it on the underlying transport.

        See :meth:`trio.abc.SendStream.send_all` for details.

        .. warning:: If this method is cancelled, then it may leave the
           :class:`SSLStream` in an unusable state. If this happens then any
           attempt to use the object will raise
           :exc:`trio.BrokenResourceError`.

        Frz   N)
rA   rc   r<   r%   ra   rg   r$   rx   r:   rq   )r   rw   r   r   r   rl     s   "zSSLStream.send_allc              	      s   | j L | j8 |   | jjddI dH  | | jjI dH  | j}d| _t	j
| _|| j fW  d   W  d   S 1 sCw   Y  W d   dS 1 sSw   Y  dS )a  Cleanly close down the SSL/TLS encryption layer, allowing the
        underlying stream to be used for unencrypted communication.

        You almost certainly don't need this.

        Returns:
          A pair ``(transport_stream, trailing_bytes)``, where
          ``transport_stream`` is the underlying transport stream, and
          ``trailing_bytes`` is a byte string. Since :class:`SSLStream`
          doesn't necessarily know where the end of the encrypted data will
          be, it can happen that it accidentally reads too much from the
          underlying stream. ``trailing_bytes`` contains this extra data; you
          should process it as if it was returned from a call to
          ``transport_stream.receive_some(...)``.

        Frz   N)rB   rA   rc   r<   r%   rx   r:   unwrapr2   r*   r-   r3   r8   rk   r   r2   r   r   r   r     s   RzSSLStream.unwrapc              	      s   | j tju rtj I dH  dS | j tju s| jr(tj| _ | j	 I dH  dS zDz(| j
jddI dH  z| j| jjddI dH  W n tjtjfyO   Y nw W n   t| jI dH   | j	 I dH  W tj| _ dS tj| _ w )a  Gracefully shut down this connection, and close the underlying
        transport.

        If ``https_compatible`` is False (the default), then this attempts to
        first send a ``close_notify`` and then close the underlying stream by
        calling its :meth:`~trio.abc.AsyncResource.aclose` method.

        If ``https_compatible`` is set to True, then this simply closes the
        underlying stream and marks this stream as closed.

        NFrz   T)rd   )r3   r*   r-   ra   rg   r$   r,   r4   r2   acloser<   r%   rx   r:   r   rb   ZBusyResourceErrorr   r'   r   r   r   r     s*   0zSSLStream.aclosec              
      s   | j : |   | j4 I dH  | j I dH  W d  I dH  n1 I dH s)w   Y  W d   dS W d   dS 1 sAw   Y  dS )z>See :meth:`trio.abc.SendStream.wait_send_all_might_not_block`.N)rA   rc   r=   r2   wait_send_all_might_not_blockr'   r   r   r   r   E  s   *"z'SSLStream.wait_send_all_might_not_blockr&   )r   r   r   r   r    rR   rS   rX   r[   r_   rc   rx   r;   ry   rm   rl   r   r   r   __classcell__r   r   r]   r   r.      s*    d) -
#5Wr.   )	metaclassc                   @   s.   e Zd ZdZddddZdd Zdd	 Zd
S )SSLListenera  A :class:`~trio.abc.Listener` for SSL/TLS-encrypted servers.

    :class:`SSLListener` wraps around another Listener, and converts
    all incoming connections to encrypted connections by wrapping them
    in a :class:`SSLStream`.

    Args:
      transport_listener (~trio.abc.Listener): The listener whose incoming
          connections will be wrapped in :class:`SSLStream`.

      ssl_context (~ssl.SSLContext): The :class:`~ssl.SSLContext` that will be
          used for incoming connections.

      https_compatible (bool): Passed on to :class:`SSLStream`.

    Attributes:
      transport_listener (trio.abc.Listener): The underlying listener that was
          passed to ``__init__``.

    F)r1   c                C   s   || _ || _|| _d S r&   )transport_listener_ssl_contextr4   )r   r   rE   r1   r   r   r   r      s   
zSSLListener.__init__c                    s&   | j  I dH }t|| jd| jdS )zAccept the next connection and wrap it in an :class:`SSLStream`.

        See :meth:`trio.abc.Listener.accept` for details.

        NT)r0   r1   )r   acceptr.   r   r4   r   r   r   r   r     s   zSSLListener.acceptc                    s   | j  I dH  dS )zClose the transport listener.N)r   r   r'   r   r   r   r     s   zSSLListener.acloseN)r   r   r   r   r    r   r   r   r   r   r   r   n  s    r   )operatorr}   sslr   enumr   _Enumra   abcr   r   Z_highlevel_genericr    r   Z_utilr   r	   rC   r   	Exceptionr   r   r*   r.   r   r   r   r   r   <module>   s*    
     