o
    u]4                     @   s   d Z ddlZddlZddlZddlZddlZddlZddlmZmZm	Z	 ddl
mZmZmZmZmZmZ ddgZdZG dd	 d	eZG d
d deZdS )a  
Interaction with a Tor relay's ORPort. :class:`~stem.client.Relay` is
a wrapper for :class:`~stem.socket.RelaySocket`, much the same way as
:class:`~stem.control.Controller` provides higher level functions for
:class:`~stem.socket.ControlSocket`.

.. versionadded:: 1.7.0

::

  Relay - Connection with a tor relay's ORPort.
    | +- connect - Establishes a connection with a relay.
    |
    |- is_alive - reports if our connection is open or closed
    |- connection_time - time when we last connected or disconnected
    |- close - shuts down our connection
    |
    +- create_circuit - establishes a new circuit

  Circuit - Circuit we've established through a relay.
    |- send - sends a message through this circuit
    +- close - closes this circuit
    N)CELL_TYPE_SIZEFIXED_PAYLOAD_LENCell)ZEROAddressKDFLinkProtocolRelayCommandsplitcellZdatatype)         c                   @   sr   e Zd ZdZdd ZeefddZdddZd	d
 Z	dd Z
dd Zdd Zdd Zdd Zdd Zdd ZdS )Relayzk
  Connection with a Tor relay's ORPort.

  :var int link_protocol: link protocol version we established
  c                 C   s*   t || _|| _d| _t | _i | _d S )N    )r   link_protocol_orport_orport_buffer	threadingRLock_orport_lock	_circuits)selfZorportr    r   6/usr/lib/python3/dist-packages/stem/client/__init__.py__init__B   s
   
	

zRelay.__init__c           
   
   C   sj  t | }tjj|std| |stdz	tj| |}W n2 tjyR } z%dt	|v r8td| |f dt	|v sDdt	|v rMtd| |f  d}~ww |
tjj|d	 | }|ss|  td
| |f tjjj|d	d }t||j}|s|  tdd|| |d|jf t|}	|
tjj|g |	 t||	S )af  
    Establishes a connection with the given ORPort.

    :param str address: ip address of the relay
    :param int port: ORPort of the relay
    :param tuple link_protocols: acceptable link protocol versions

    :raises:
      * **ValueError** if address or port are invalid
      * :class:`stem.SocketError` if we're unable to establish a connection
    z'%s' isn't a valid portz8Connection can't be established without a link protocol.zConnection refusedz5Failed to connect to %s:%i. Maybe it isn't an ORPort?zunknown protocolzwrong version numberz>Failed to SSL authenticate to %s:%i. Maybe it isn't an ORPort?N   z5Unable to establish a common link protocol with %s:%ir   zKUnable to find a common link protocol. We support %s but %s:%i supports %s.z, )r   stemutilZ
connectionZis_valid_port
ValueErrorZsocketZRelaySocketZSocketErrorstrsendclientr   ZVersionsCellpackrecvcloser   popsetintersectionZversionsjoinmaxZNetinfoCellr   )
ZaddressZportZlink_protocolsZ
relay_addrZconnexcresponseZversions_replyZcommon_protocolsr   r   r   r   connectQ   s8   
$
zRelay.connectFc                 C   s  | j  | jjj}t| j|tj k r'|  j| j 7  _t| j|tj k st	
t| j|d d }|jrA|tj t }n=t| j|tj tj k re|  j| j 7  _t| j|tj tj k sNt| j|tj d d }|tj tj | }t| j|k r|  j| j 7  _t| j|k s|rt| j|\}| _|W  d   S t	| j| j\}| _|W  d   S 1 sw   Y  dS )z
    Reads the next cell from our ORPort. If none is present this blocks
    until one is available.

    :param bool raw: provides bytes rather than parsing as a cell if **True**

    :returns: next :class:`~stem.client.cell.Cell`
    Nr   )r   r   circ_id_sizesizelenr   r   r   r$   r   Zby_valuer&   ZIS_FIXED_SIZEr   r
   )r   rawr.   Z	cell_typeZ	cell_sizeZpayload_lenZcontentr   r   r   r   _recv   s.   

$zRelay._recvc                 c   sV    | j jdd | j || j | j jdd}tjjj	|| jD ]}|V  q#dS )ae  
    Sends a cell on the ORPort and provides the response we receive in reply.

    Unfortunately unlike control sockets, ORPorts don't have generalized rules
    for predictable message IO. With control sockets...

      * Each message we send receives a single reply.
      * We may also receive asynchronous events marked with a 650 status.

    ORPorts by contrast receive variable length cells with differing rules on
    their arrival. As such making a best effort attempt at a send-and-receive
    method in which we do the following...

      * Discard any existing unread data from the socket.
      * Send our request.
      * Await up to a second for a reply.

    It's quite possible this is a stupid approach. If so, patches welcome.

    :param stem.client.cell.Cell cell: cell to be sent

    :returns: **generator** with the cells received in reply
    r   )Ztimeout   N)
r   r$   r!   r#   r   r   r"   r   r   r&   )r   r   r,   Zreceived_cellr   r   r   _msg   s   z
Relay._msgc                 C   
   | j  S )z
    Checks if our socket is currently connected. This is a pass-through for our
    socket's :func:`~stem.socket.BaseSocket.is_alive` method.

    :returns: **bool** that's **True** if our socket is connected and **False** otherwise
    )r   is_aliver   r   r   r   r6      s   
zRelay.is_alivec                 C   r5   )aH  
    Provides the unix timestamp for when our socket was either connected or
    disconnected. That is to say, the time we connected if we're currently
    connected and the time we disconnected if we're not connected.

    :returns: **float** for when we last connected or disconnected, zero if
      we've never connected
    )r   connection_timer7   r   r   r   r8      s   

zRelay.connection_timec                 C   s4   | j  | j W  d   S 1 sw   Y  dS )z
    Closes our socket connection. This is a pass-through for our socket's
    :func:`~stem.socket.BaseSocket.close` method.
    N)r   r   r%   r7   r   r   r   r%      s   $zRelay.closec                 C   s   | j Z | jrt| jd n| jj}tjj|}d}| 	|D ]}t
|tjjjr.|} nq |s5tdt|j|j }|j|jkrHtdt| ||}|| j|j< |W  d   S 1 s`w   Y  dS )z$
    Establishes a new circuit.
    r3   Nz@We should get a CREATED_FAST response from a CREATE_FAST requestz3Remote failed to prove that it knows our shared key)r   r   r*   r   Zfirst_circ_idr   r"   r   ZCreateFastCellr4   
isinstanceZCreatedFastCellr   r   Z
from_valueZkey_materialZderivative_keyZkey_hashCircuitid)r   circ_idZcreate_fast_cellZcreated_fast_cellr   kdfcircr   r   r   create_circuit  s$   $zRelay.create_circuitc                 c   sD    | j  | j D ]}|V  q
W d    d S 1 sw   Y  d S N)r   r   values)r   r>   r   r   r   __iter__   s   "zRelay.__iter__c                 C      | S r@   r   r7   r   r   r   	__enter__%     zRelay.__enter__c                 C      |    d S r@   r%   r   Z	exit_typevalue	tracebackr   r   r   __exit__(     zRelay.__exit__N)F)__name__
__module____qualname____doc__r   staticmethodDEFAULT_LINK_PROTOCOLSr-   r2   r4   r6   r8   r%   r?   rB   rD   rK   r   r   r   r   r   ;   s    
I) 
	r   c                   @   sD   e Zd ZdZdd ZdddZddd	Zd
d Zdd Zdd Z	dS )r:   a  
  Circuit through which requests can be made of a `Tor relay's ORPort
  <https://gitweb.torproject.org/torspec.git/tree/tor-spec.txt>`_.

  :var stem.client.Relay relay: relay through which this circuit has been established
  :var int id: circuit id
  :var hashlib.sha1 forward_digest: digest for forward integrity check
  :var hashlib.sha1 backward_digest: digest for backward integrity check
  :var bytes forward_key: forward encryption key
  :var bytes backward_key: backward encryption key
  c           	      C   s   t j s	tdddlm}m}m} ddlm	} |
t|jjd  }|| _|| _t|j| _t|j| _|||j||  | _|||j||  | _d S )Nz5Circuit construction requires the cryptography moduler   )Cipher
algorithmsmodes)default_backend   )r   ZprereqZis_crypto_availableImportErrorZ&cryptography.hazmat.primitives.ciphersrS   rT   rU   Zcryptography.hazmat.backendsrV   ZCTRr   ZAESZ
block_sizerelayr;   hashlibZsha1forward_digestbackward_digestforward_keyZ	encryptorbackward_keyZ	decryptor)	r   rY   r<   r=   rS   rT   rU   rV   Zctrr   r   r   r   9  s   
 zCircuit.__init__r   c                 C   s   | j jb | jtj|d | jtj||d g }	 | j jdd}tjj	j
| j j|| j| j\}}}| j|jkrCtd| j|jf || _|| _|jtjkr`ddd |D W  d   S || q1 siw   Y  dS )	z
    Request descriptors from the relay.

    :param str request: directory request to make
    :param int stream_id: specific stream this concerns

    :returns: **str** with the requested descriptor data
    	stream_idT)r1   z,Response should be for circuit id %i, not %ir   c                 S   s   g | ]}|j qS r   )data).0r   r   r   r   
<listcomp>h  s    z%Circuit.directory.<locals>.<listcomp>N)rY   r   _sendr	   Z	BEGIN_DIRZDATAr2   r   r"   r   	RelayCellZdecryptr   r^   r\   r;   r<   ZProtocolErrorcommandZENDr)   append)r   Zrequestr`   r,   Zencrypted_cellZdecrypted_cellr^   r\   r   r   r   	directoryI  s"   

$
zCircuit.directory c                 C   s|   | j j0 tjjj| j|||d}|| j j| j	| j
\}}}| j j| || _
|| _	W d   dS 1 s7w   Y  dS )z
    Sends a message over the circuit.

    :param stem.client.datatype.RelayCommand command: command to be issued
    :param bytes data: message payload
    :param int stream_id: specific stream this concerns
    r_   N)rY   r   r   r"   r   re   r;   Zencryptr   r]   r[   r   r!   )r   rf   ra   r`   r   Zpayloadr]   r[   r   r   r   rd   l  s   
	"zCircuit._sendc                 C   s^   | j j! | j jtjj| j	| j j
 | j j| j= W d    d S 1 s(w   Y  d S r@   )rY   r   r   r!   r   r"   r   ZDestroyCellr;   r#   r   r   r7   r   r   r   r%     s   
$"zCircuit.closec                 C   rC   r@   r   r7   r   r   r   rD     rE   zCircuit.__enter__c                 C   rF   r@   rG   rH   r   r   r   rK     rL   zCircuit.__exit__N)r   )ri   r   )
rM   rN   rO   rP   r   rh   rd   r%   rD   rK   r   r   r   r   r:   ,  s    

#r:   )rP   rZ   r   r   Zstem.client.cellZstem.socketZstem.util.connectionr   r   r   Zstem.client.datatyper   r   r   r   r	   r
   __all__rR   objectr   r:   r   r   r   r   <module>   s     
 r