o
    fX]Ģ                     @   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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ZddlmZmZ zddlmZ W n eyW   ddlZY nw ejjddddZejjjejjjejjjejjjdZ d	Z!d
Z"da#dZ$dZ%dd Z&dd Z'd,ddZ(d,ddZ)dd Z*d-ddZ+dd Z,dd Z-G dd de.Z/G d d! d!e.Z0d"d# Z1d$d% Z2d&d' Z3d(d) Z4d*d+ Z5ej6j7Z7ej6j8Z9ej6j:Z;dS ).a  
Module for remotely retrieving descriptors from directory authorities and
mirrors. This is the simplest method for getting current tor descriptor
information...

::

  import stem.descriptor.remote

  for desc in stem.descriptor.remote.get_server_descriptors():
    if desc.exit_policy.is_exiting_allowed():
      print('  %s (%s)' % (desc.nickname, desc.fingerprint))

More custom downloading behavior can be done through the
:class:`~stem.descriptor.remote.DescriptorDownloader` class, which issues
:class:`~stem.descriptor.remote.Query` instances to get you descriptor
content. For example...

::

  from stem.descriptor.remote import DescriptorDownloader

  downloader = DescriptorDownloader(
    use_mirrors = True,
    timeout = 10,
  )

  query = downloader.get_server_descriptors()

  print('Exit Relays:')

  try:
    for desc in query.run():
      if desc.exit_policy.is_exiting_allowed():
        print('  %s (%s)' % (desc.nickname, desc.fingerprint))

    print
    print('Query took %0.2f seconds' % query.runtime)
  except Exception as exc:
    print('Unable to retrieve the server descriptors: %s' % exc)

::

  get_instance - Provides a singleton DescriptorDownloader used for...
    |- their_server_descriptor - provides the server descriptor of the relay we download from
    |- get_server_descriptors - provides present server descriptors
    |- get_extrainfo_descriptors - provides present extrainfo descriptors
    |- get_microdescriptors - provides present microdescriptors with the given digests
    |- get_consensus - provides the present consensus or router status entries
    |- get_bandwidth_file - provides bandwidth heuristics used to make the next consensus
    +- get_detached_signatures - authority signatures used to make the next consensus

  Query - Asynchronous request to download tor descriptors
    |- start - issues the query if it isn't already running
    +- run - blocks until the request is finished and provides the results

  DescriptorDownloader - Configurable class for issuing queries
    |- use_directory_mirrors - use directory mirrors to download future descriptors
    |- their_server_descriptor - provides the server descriptor of the relay we download from
    |- get_server_descriptors - provides present server descriptors
    |- get_extrainfo_descriptors - provides present extrainfo descriptors
    |- get_microdescriptors - provides present microdescriptors with the given digests
    |- get_consensus - provides the present consensus or router status entries
    |- get_vote - provides an authority's vote for the next consensus
    |- get_key_certificates - provides present authority key certificates
    |- get_bandwidth_file - provides bandwidth heuristics used to make the next consensus
    |- get_detached_signatures - authority signatures used to make the next consensus
    +- query - request an arbitrary descriptor resource

.. versionadded:: 1.1.0

.. data:: MAX_FINGERPRINTS

  Maximum number of descriptors that can requested at a time by their
  fingerprints.

.. data:: MAX_MICRODESCRIPTOR_HASHES

  Maximum number of microdescriptors that can requested at a time by their
  hashes.

.. data:: Compression (enum)

  Compression when downloading descriptors.

  .. versionadded:: 1.7.0

  =============== ===========
  Compression     Description
  =============== ===========
  **PLAINTEXT**   Uncompressed data.
  **GZIP**        `GZip compression <https://www.gnu.org/software/gzip/>`_.
  **ZSTD**        `Zstandard compression <https://www.zstd.net>`_, this requires the `zstandard module <https://pypi.org/project/zstandard/>`_.
  **LZMA**        `LZMA compression <https://en.wikipedia.org/wiki/LZMA>`_, this requires the 'lzma module <https://docs.python.org/3/library/lzma.html>`_.
  =============== ===========
    N)log	str_tools)	PLAINTEXTidentity)GZIPgzip)ZSTDx-zstd)LZMA
x-tor-lzma)r   r   r	   r   `   Z   zdetached-signature)Ztor26ZSergec                   C   s   t du rt a t S )z
  Provides the singleton :class:`~stem.descriptor.remote.DescriptorDownloader`
  used for this module's shorthand functions.

  .. versionadded:: 1.5.0

  :returns: singleton :class:`~stem.descriptor.remote.DescriptorDownloader` instance
  N)SINGLETON_DOWNLOADERDescriptorDownloader r   r   8/usr/lib/python3/dist-packages/stem/descriptor/remote.pyget_instance   s   r   c                  K      t  jdi | S )a  
  Provides the server descriptor of the relay we're downloading from.

  .. versionadded:: 1.7.0

  :param query_args: additional arguments for the
    :class:`~stem.descriptor.remote.Query` constructor

  :returns: :class:`~stem.descriptor.remote.Query` for the server descriptors
  Nr   )r   their_server_descriptor
query_argsr   r   r   r      s   r   c                 K      t  j| fi |S )z
  Shorthand for
  :func:`~stem.descriptor.remote.DescriptorDownloader.get_server_descriptors`
  on our singleton instance.

  .. versionadded:: 1.5.0
  )r   get_server_descriptorsfingerprintsr   r   r   r   r         	r   c                 K   r   )z
  Shorthand for
  :func:`~stem.descriptor.remote.DescriptorDownloader.get_extrainfo_descriptors`
  on our singleton instance.

  .. versionadded:: 1.5.0
  )r   get_extrainfo_descriptorsr   r   r   r   r      r   r   c                 K   r   )z
  Shorthand for
  :func:`~stem.descriptor.remote.DescriptorDownloader.get_microdescriptors`
  on our singleton instance.

  .. versionadded:: 1.8.0
  )r   get_microdescriptors)hashesr   r   r   r   r      r   r   Fc                 K   s   t  j| |fi |S )z
  Shorthand for
  :func:`~stem.descriptor.remote.DescriptorDownloader.get_consensus`
  on our singleton instance.

  .. versionadded:: 1.5.0
  )r   get_consensus)authority_v3identmicrodescriptorr   r   r   r   r      s   	r   c                  K   r   )z
  Shorthand for
  :func:`~stem.descriptor.remote.DescriptorDownloader.get_bandwidth_file`
  on our singleton instance.

  .. versionadded:: 1.8.0
  Nr   )r   get_bandwidth_filer   r   r   r   r"         	r"   c                  K   r   )z
  Shorthand for
  :func:`~stem.descriptor.remote.DescriptorDownloader.get_detached_signatures`
  on our singleton instance.

  .. versionadded:: 1.8.0
  Nr   )r   get_detached_signaturesr   r   r   r   r$     r#   r$   c                
   @   sl   e Zd ZdZddejfddddddejjj	f
ddZ
dd	 Zdd
dZdd Zdd ZdddZdd ZdS )Querya  
  Asynchronous request for descriptor content from a directory authority or
  mirror. These can either be made through the
  :class:`~stem.descriptor.remote.DescriptorDownloader` or directly for more
  advanced usage.

  To block on the response and get results either call
  :func:`~stem.descriptor.remote.Query.run` or iterate over the Query. The
  :func:`~stem.descriptor.remote.Query.run` method pass along any errors that
  arise...

  ::

    from stem.descriptor.remote import Query

    query = Query(
      '/tor/server/all',
      timeout = 30,
    )

    print('Current relays:')

    try:
      for desc in Query('/tor/server/all', 'server-descriptor 1.0').run():
        print(desc.fingerprint)
    except Exception as exc:
      print('Unable to retrieve the server descriptors: %s' % exc)

  ... while iterating fails silently...

  ::

    print('Current relays:')

    for desc in Query('/tor/server/all', 'server-descriptor 1.0'):
      print(desc.fingerprint)

  In either case exceptions are available via our 'error' attribute.

  Tor provides quite a few different descriptor resources via its directory
  protocol (see section 4.2 and later of the `dir-spec
  <https://gitweb.torproject.org/torspec.git/tree/dir-spec.txt>`_).
  Commonly useful ones include...

  =============================================== ===========
  Resource                                        Description
  =============================================== ===========
  /tor/server/all                                 all present server descriptors
  /tor/server/fp/<fp1>+<fp2>+<fp3>                server descriptors with the given fingerprints
  /tor/extra/all                                  all present extrainfo descriptors
  /tor/extra/fp/<fp1>+<fp2>+<fp3>                 extrainfo descriptors with the given fingerprints
  /tor/micro/d/<hash1>-<hash2>                    microdescriptors with the given hashes
  /tor/status-vote/current/consensus              present consensus
  /tor/status-vote/current/consensus-microdesc    present microdescriptor consensus
  /tor/status-vote/next/bandwidth                 bandwidth authority heuristics for the next consenus
  /tor/status-vote/next/consensus-signatures      detached signature, used for making the next consenus
  /tor/keys/all                                   key certificates for the authorities
  /tor/keys/fp/<v3ident1>+<v3ident2>              key certificates for specific authorities
  =============================================== ===========

  **ZSTD** compression requires `zstandard
  <https://pypi.org/project/zstandard/>`_, and **LZMA** requires the `lzma
  module <https://docs.python.org/3/library/lzma.html>`_.

  For legacy reasons if our resource has a '.z' suffix then our **compression**
  argument is overwritten with Compression.GZIP.

  .. versionchanged:: 1.7.0
     Added support for downloading from ORPorts.

  .. versionchanged:: 1.7.0
     Added the compression argument.

  .. versionchanged:: 1.7.0
     Added the reply_headers attribute.

     The class this provides changed between Python versions. In python2
     this was called httplib.HTTPMessage, whereas in python3 the class was
     renamed to http.client.HTTPMessage.

  .. versionchanged:: 1.7.0
     Endpoints are now expected to be :class:`~stem.DirPort` or
     :class:`~stem.ORPort` instances. Usage of tuples for this
     argument is deprecated and will be removed in the future.

  .. versionchanged:: 1.7.0
     Avoid downloading from tor26. This directory authority throttles its
     DirPort to such an extent that requests either time out or take on the
     order of minutes.

  .. versionchanged:: 1.7.0
     Avoid downloading from Bifroest. This is the bridge authority so it
     doesn't vote in the consensus, and apparently times out frequently.

  .. versionchanged:: 1.8.0
     Serge has replaced Bifroest as our bridge authority. Avoiding descriptor
     downloads from it instead.

  .. versionchanged:: 1.8.0
     Defaulting to gzip compression rather than plaintext downloads.

  .. versionchanged:: 1.8.0
     Using :class:`~stem.descriptor.__init__.Compression` for our compression
     argument, usage of strings or this module's Compression enum is deprecated
     and will be removed in stem 2.x.

  :var str resource: resource being fetched, such as '/tor/server/all'
  :var str descriptor_type: type of descriptors being fetched (for options see
    :func:`~stem.descriptor.__init__.parse_file`), this is guessed from the
    resource if **None**

  :var list endpoints: :class:`~stem.DirPort` or :class:`~stem.ORPort` of the
    authority or mirror we're querying, this uses authorities if undefined
  :var list compression: list of :data:`stem.descriptor.Compression`
    we're willing to accept, when none are mutually supported downloads fall
    back to Compression.PLAINTEXT
  :var int retries: number of times to attempt the request if downloading it
    fails
  :var bool fall_back_to_authority: when retrying request issues the last
    request to a directory authority if **True**

  :var str content: downloaded descriptor content
  :var Exception error: exception if a problem occured
  :var bool is_done: flag that indicates if our request has finished

  :var float start_time: unix timestamp when we first started running
  :var http.client.HTTPMessage reply_headers: headers provided in the response,
    **None** if we haven't yet made our request
  :var float runtime: time our query took, this is **None** if it's not yet
    finished

  :var bool validate: checks the validity of the descriptor's content if
    **True**, skips these checks otherwise
  :var stem.descriptor.__init__.DocumentHandler document_handler: method in
    which to parse a :class:`~stem.descriptor.networkstatus.NetworkStatusDocument`
  :var dict kwargs: additional arguments for the descriptor constructor

  Following are only applicable when downloading from a
  :class:`~stem.DirPort`...

  :var float timeout: duration before we'll time out our request
  :var str download_url: last url used to download the descriptor, this is
    unset until we've actually made a download attempt

  :param bool start: start making the request when constructed (default is **True**)
  :param bool block: only return after the request has been completed, this is
    the same as running **query.run(True)** (default is **False**)
  N   FTc                 K   s  | dstd| |drtjg}|d d }n5|s"tjg}n.t|tr*|g}tj|v r:t	j
 s:|tj tj|v rJt	j
 sJ|tj |sPtjg}g }|D ]%}t|t	jjrc|| qT|tv ro|t|  qTtd|t|jf |r|| _nt|| _g | _|r|D ]6}t|trt|dkr| jt	|d |d  qt|t	jt	jfr| j| qtd	|t|jf || _|| _|| _|| _d | _ d | _!d
| _"d | _#d | _$|| _%d | _&|
| _'|| _(d | _)|| _*d | _+t,- | _.|r| /  |	r| 0d d S d S )N/z%Resources should start with a '/': %sz.zz1'%s' (%s) is not a recognized type of compressionr&   r      zQEndpoints must be an stem.ORPort, stem.DirPort, or two value tuple. '%s' is a %s.FT)1
startswith
ValueErrorendswithCompressionr   r   
isinstancestrr   stemprereqZis_zstd_availableremover
   Zis_lzma_available
descriptorZ_CompressionappendCOMPRESSION_MIGRATIONtype__name__descriptor_type_guess_descriptor_type	endpointstuplelenDirPortORPortresourcecompressionretriesfall_back_to_authoritycontenterroris_donedownload_url
start_timetimeoutruntimevalidatedocument_handlerreply_headerskwargs_downloader_thread	threadingRLock_downloader_thread_lockstartrun)selfr?   r8   r:   r@   rA   rB   rH   rR   blockrJ   rK   rM   Znew_compressionZlegacy_compressionendpointr   r   r   __init__  sn   





zQuery.__init__c                 C   sx   | j / | jdu r*tjd| j| j| jfd| _| jd | j  W d   dS W d   dS 1 s5w   Y  dS )zI
    Starts downloading the scriptors if we haven't started already.
    NzDescriptor query)nametargetargsT)	rQ   rN   rO   ZThread_download_descriptorsrA   rH   Z	setDaemonrR   )rT   r   r   r   rR     s   

"zQuery.startc                 C   s   t | |S )aS  
    Blocks until our request is complete then provides the descriptors. If we
    haven't yet started our request then this does so.

    :param bool suppress: avoids raising exceptions if **True**

    :returns: list for the requested :class:`~stem.descriptor.__init__.Descriptor` instances

    :raises:
      Using the iterator can fail with the following if **suppress** is
      **False**...

        * **ValueError** if the descriptor contents is malformed
        * :class:`~stem.DownloadTimeout` if our request timed out
        * :class:`~stem.DownloadFailed` if our request fails
    )list_run)rT   suppressr   r   r   rS     s   z	Query.runc                 c   s8   | j  |   | j  | jr|r	 W d    d S | j| jd u r3|r/	 W d    d S tdz5| jt	rIt
jjjt| j| jd}nt
jjt| j| jf| j| jd| j}|D ]}|V  qaW n! ty } z|| _|rW Y d }~W d    d S | jd }~ww W d    d S 1 sw   Y  d S )NzHBUG: _download_descriptors() finished without either results or an error)rJ   )rJ   rK   )rQ   rR   rN   joinrD   rC   r+   r8   r*   DETACHED_SIGNATURE_TYPEr0   r3   ZnetworkstatusZ_parse_file_detached_sigsioBytesIOrJ   Z
parse_filerK   rM   )rT   r^   resultsdescexcr   r   r   r]     sT   




,"z
Query._runc                 c   s    |  dD ]}|V  qd S )NT)r]   )rT   rd   r   r   r   __iter__I  s   zQuery.__iter__c                 C   sF   |s| j stdd tjj  D }t|j	|j
S t| j S )a.  
    Provides an endpoint to query. If we have multiple endpoints then one
    is picked at random.

    :param bool use_authority: ignores our endpoints and uses a directory
      authority instead

    :returns: :class:`stem.Endpoint` for the location to be downloaded
      from by this request
    c                 S      g | ]	}|j tvr|qS r   ZnicknameDIR_PORT_BLACKLIST.0Zauthr   r   r   
<listcomp>Z      z(Query._pick_endpoint.<locals>.<listcomp>)r:   randomchoicer0   	directory	Authority
from_cachevaluesr=   addressdir_port)rT   use_authorityZpickedr   r   r   _pick_endpointM  s   
 zQuery._pick_endpointc              	   C   s  zzpt   | _| j|dko| jd}t|tjr/d|j|j| j	f }t
|| j| j	\| _| _n/t|tjrSd|j|j| j	df | _| j}t| j| j|\| _| _ntd|t|jf t   | j | _td|| jf  W nO   t d }|d ur|t   | j 8 }|dkr|d u s|dkrtd	| j||f  | |d | Y W d
| _S td| j|f  || _Y W d
| _d S W d
| _d S d
| _w )Nr   )rv   zORPort %s:%s (resource %s)zhttp://%s:%i/%sr'   z=BUG: endpoints can only be ORPorts or DirPorts, '%s' was a %sz'Descriptors retrieved from %s in %0.2fsr)   zCUnable to download descriptors from '%s' (%i retries remaining): %sTz,Unable to download descriptors from '%s': %s)timerG   rw   rB   r.   r0   r>   rt   portr?   _download_from_orportr@   rC   rL   r=   lstriprF   _download_from_dirportr+   r6   r7   rI   r   Ztracesysexc_infodebugr[   rE   rD   )rT   rA   rH   rV   Zdownloaded_fromre   r   r   r   r[   _  s4   


zQuery._download_descriptorsF)r7   
__module____qualname____doc__r-   r   r0   r3   DocumentHandlerZENTRIESrW   rR   rS   r]   rf   rw   r[   r   r   r   r   r%     s     (O
/
r%   c                   @   sz   e Zd ZdZdddZdd Zdd Zdd
dZdddZdd Z	dddZ
dd ZdddZdd Zdd Zdd Zd	S )r   a  
  Configurable class that issues :class:`~stem.descriptor.remote.Query`
  instances on your behalf.

  :param bool use_mirrors: downloads the present consensus and uses the directory
    mirrors to fetch future requests, this fails silently if the consensus
    cannot be downloaded
  :param default_args: default arguments for the
    :class:`~stem.descriptor.remote.Query` constructor
  Fc              
   K   sv   || _ d | _|r9zt }|   tdt |   W d S  ty8 } ztd|  W Y d }~d S d }~ww d S )Nz)Retrieved directory mirrors (took %0.2fs)z(Unable to retrieve directory mirrors: %s)_default_args
_endpointsrx   use_directory_mirrorsr   r   	Exception)rT   Zuse_mirrorsZdefault_argsrG   re   r   r   r   rW     s   zDescriptorDownloader.__init__c                 C   s   dd t jj  D }tdd |D }t| jt jj	j
d d }|j D ]}t jj|jv r?|jr?||j|jf q*t|| _|S )aB  
    Downloads the present consensus and configures ourselves to use directory
    mirrors, in addition to authorities.

    :returns: :class:`~stem.descriptor.networkstatus.NetworkStatusDocumentV3`
      from which we got the directory mirrors

    :raises: **Exception** if unable to determine the directory mirrors
    c                 S   rg   r   rh   rj   r   r   r   rl     rm   z>DescriptorDownloader.use_directory_mirrors.<locals>.<listcomp>c                 S   s   g | ]}|j |jfqS r   )rt   ru   )rk   rp   r   r   r   rl     s    )rK   r   )r0   rp   rq   rr   rs   setr\   r   r3   r   DOCUMENTrS   ZroutersFlagZV2DIRflagsru   addrt   r   )rT   ZdirectoriesZnew_endpoints	consensusrd   r   r   r   r     s   
z*DescriptorDownloader.use_directory_mirrorsc                 K      | j di |S )a)  
    Provides the server descriptor of the relay we're downloading from.

    .. versionadded:: 1.7.0

    :param query_args: additional arguments for the
      :class:`~stem.descriptor.remote.Query` constructor

    :returns: :class:`~stem.descriptor.remote.Query` for the server descriptors
    /tor/server/authorityN)r   queryrT   r   r   r   r   r     s   z,DescriptorDownloader.their_server_descriptorNc                 K   P   d}t |tr
|g}|rt|tkrtdt dd| }| j|fi |S )a~  
    Provides the server descriptors with the given fingerprints. If no
    fingerprints are provided then this returns all descriptors known
    by the relay.

    :param str,list fingerprints: fingerprint or list of fingerprints to be
      retrieved, gets all descriptors if **None**
    :param query_args: additional arguments for the
      :class:`~stem.descriptor.remote.Query` constructor

    :returns: :class:`~stem.descriptor.remote.Query` for the server descriptors

    :raises: **ValueError** if we request more than 96 descriptors by their
      fingerprints (this is due to a limit on the url length by squid proxies).
    z/tor/server/allJUnable to request more than %i descriptors at a time by their fingerprintsz/tor/server/fp/%s+r.   r/   r<   MAX_FINGERPRINTSr+   r_   r   rT   r   r   r?   r   r   r   r        
z+DescriptorDownloader.get_server_descriptorsc                 K   r   )a  
    Provides the extrainfo descriptors with the given fingerprints. If no
    fingerprints are provided then this returns all descriptors in the present
    consensus.

    :param str,list fingerprints: fingerprint or list of fingerprints to be
      retrieved, gets all descriptors if **None**
    :param query_args: additional arguments for the
      :class:`~stem.descriptor.remote.Query` constructor

    :returns: :class:`~stem.descriptor.remote.Query` for the extrainfo descriptors

    :raises: **ValueError** if we request more than 96 descriptors by their
      fingerprints (this is due to a limit on the url length by squid proxies).
    z/tor/extra/allr   z/tor/extra/fp/%sr   r   r   r   r   r   r     r   z.DescriptorDownloader.get_extrainfo_descriptorsc                 K   sD   t |tr|g}t|tkrtdt | jdd| fi |S )a  
    Provides the microdescriptors with the given hashes. To get these see the
    **microdescriptor_digest** attribute of
    :class:`~stem.descriptor.router_status_entry.RouterStatusEntryMicroV3`.
    Note that these are only provided via the **microdescriptor consensus**.
    For exampe...

    ::

      >>> import stem.descriptor.remote
      >>> consensus = stem.descriptor.remote.get_consensus(microdescriptor = True).run()
      >>> my_router_status_entry = list(filter(lambda desc: desc.nickname == 'caersidi', consensus))[0]
      >>> print(my_router_status_entry.microdescriptor_digest)
      IQI5X2A5p0WVN/MgwncqOaHF2f0HEGFEaxSON+uKRhU

      >>> my_microdescriptor = stem.descriptor.remote.get_microdescriptors([my_router_status_entry.microdescriptor_digest]).run()[0]
      >>> print(my_microdescriptor)
      onion-key
      -----BEGIN RSA PUBLIC KEY-----
      MIGJAoGBAOJo9yyVgG8ksEHQibqPIEbLieI6rh1EACRPiDiV21YObb+9QEHaR3Cf
      FNAzDbGhbvADLBB7EzuViL8w+eXQUOaIsJRdymh/wuUJ78bv5oEIJhthKq/Uqa4P
      wKHXSZixwAHfy8NASTX3kxu9dAHWU3Owb+4W4lR2hYM0ZpoYYkThAgMBAAE=
      -----END RSA PUBLIC KEY-----
      ntor-onion-key kWOHNd+2uBlMpcIUbbpFLiq/rry66Ep6MlwmNpwzcBg=
      id ed25519 xE/GeYImYAIB0RbzJXFL8kDLpDrj/ydCuCdvOgC4F/4

    :param str,list hashes: microdescriptor hash or list of hashes to be
      retrieved
    :param query_args: additional arguments for the
      :class:`~stem.descriptor.remote.Query` constructor

    :returns: :class:`~stem.descriptor.remote.Query` for the microdescriptors

    :raises: **ValueError** if we request more than 92 microdescriptors by their
      hashes (this is due to a limit on the url length by squid proxies).
    zIUnable to request more than %i microdescriptors at a time by their hashesz/tor/micro/d/%s-)r.   r/   r<   MAX_MICRODESCRIPTOR_HASHESr+   r   r_   )rT   r   r   r   r   r   r     s
   
&z)DescriptorDownloader.get_microdescriptorsc                 K   s   |rd}nd}|r|d| 7 }| j |fi |}|jr?|jtjjjkr?tj r?t	|
 d }| jdi |
 }|| |S )a  
    Provides the present router status entries.

    .. versionchanged:: 1.5.0
       Added the microdescriptor argument.

    :param str authority_v3ident: fingerprint of the authority key for which
      to get the consensus, see `'v3ident' in tor's config.c
      <https://gitweb.torproject.org/tor.git/tree/src/or/config.c>`_
      for the values.
    :param bool microdescriptor: provides the microdescriptor consensus if
      **True**, standard consensus otherwise
    :param query_args: additional arguments for the
      :class:`~stem.descriptor.remote.Query` constructor

    :returns: :class:`~stem.descriptor.remote.Query` for the router status
      entries
    z,/tor/status-vote/current/consensus-microdescz"/tor/status-vote/current/consensusz/%sr   Nr   )r   rJ   rK   r0   r3   r   r   r1   Zis_crypto_availabler\   rS   get_key_certificatesZvalidate_signatures)rT   r    r!   r   r?   Zconsensus_queryr   Z	key_certsr   r   r   r   +  s    
z"DescriptorDownloader.get_consensusc                 K   s0   d}d|vr|j |jfg|d< | j|fi |S )af  
    Provides the present vote for a given directory authority.

    :param stem.directory.Authority authority: authority for which to retrieve a vote for
    :param query_args: additional arguments for the
      :class:`~stem.descriptor.remote.Query` constructor

    :returns: :class:`~stem.descriptor.remote.Query` for the router status
      entries
    z"/tor/status-vote/current/authorityrV   r:   )rt   ru   r   )rT   Z	authorityr   r?   r   r   r   get_voteS  s   zDescriptorDownloader.get_votec                 K   r   )a  
    Provides the key certificates for authorities with the given fingerprints.
    If no fingerprints are provided then this returns all present key
    certificates.

    :param str authority_v3idents: fingerprint or list of fingerprints of the
      authority keys, see `'v3ident' in tor's config.c
      <https://gitweb.torproject.org/tor.git/tree/src/or/config.c#n819>`_
      for the values.
    :param query_args: additional arguments for the
      :class:`~stem.descriptor.remote.Query` constructor

    :returns: :class:`~stem.descriptor.remote.Query` for the key certificates

    :raises: **ValueError** if we request more than 96 key certificates by
      their identity fingerprints (this is due to a limit on the url length by
      squid proxies).
    z/tor/keys/allzXUnable to request more than %i key certificates at a time by their identity fingerprintsz/tor/keys/fp/%sr   r   )rT   Zauthority_v3identsr   r?   r   r   r   r   f  s   
z)DescriptorDownloader.get_key_certificatesc                 K   r   )aH  
    Provides the bandwidth authority heuristics used to make the next
    consensus.

    .. versionadded:: 1.8.0

    :param query_args: additional arguments for the
      :class:`~stem.descriptor.remote.Query` constructor

    :returns: :class:`~stem.descriptor.remote.Query` for the bandwidth
      authority heuristics
    /tor/status-vote/next/bandwidthN)r   r   r   r   r   r   r"     s   z'DescriptorDownloader.get_bandwidth_filec                 K   r   )a  
    Provides the detached signatures that will be used to make the next
    consensus. Please note that **these are only available during minutes 55-60
    each hour**. If requested during minutes 0-55 tor will not service these
    requests, and this will fail with a 404.

    For example...

    ::

      import stem.descriptor.remote

      detached_sigs = stem.descriptor.remote.get_detached_signatures().run()[0]

      for i, sig in enumerate(detached_sigs.signatures):
        print('Signature %i is from %s' % (i + 1, sig.identity))

    **When available (minutes 55-60 of the hour)**

    ::

      % python demo.py
      Signature 1 is from 0232AF901C31A04EE9848595AF9BB7620D4C5B2E
      Signature 2 is from 14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4
      Signature 3 is from 23D15D965BC35114467363C165C4F724B64B4F66
      ...

    **When unavailable (minutes 0-55 of the hour)**

    ::

      % python demo.py
      Traceback (most recent call last):
        File "demo.py", line 3, in
          detached_sigs = stem.descriptor.remote.get_detached_signatures().run()[0]
        File "/home/atagar/Desktop/stem/stem/descriptor/remote.py", line 533, in run
          return list(self._run(suppress))
        File "/home/atagar/Desktop/stem/stem/descriptor/remote.py", line 544, in _run
          raise self.error
      stem.DownloadFailed: Failed to download from http://154.35.175.225:80/tor/status-vote/next/consensus-signatures (HTTPError): Not found

    .. versionadded:: 1.8.0

    :param query_args: additional arguments for the
      :class:`~stem.descriptor.remote.Query` constructor

    :returns: :class:`~stem.descriptor.remote.Query` for the detached
      signatures
    */tor/status-vote/next/consensus-signaturesN)r   r   r   r   r   r   r$     s   3z,DescriptorDownloader.get_detached_signaturesc                 K   s6   t | j}|| d|vr| j|d< t|fi |S )ar  
    Issues a request for the given resource.

    .. versionchanged:: 1.7.0
       The **fall_back_to_authority** default when using this method is now
       **False**, like the :class:`~stem.descriptor.Query` class.

    :param str resource: resource being fetched, such as '/tor/server/all'
    :param query_args: additional arguments for the
      :class:`~stem.descriptor.remote.Query` constructor

    :returns: :class:`~stem.descriptor.remote.Query` for the descriptors

    :raises: **ValueError** if resource is clearly invalid or the descriptor
      type can't be determined when 'descriptor_type' is **None**
    r:   )dictr   updater   r%   )rT   r?   r   rZ   r   r   r   r     s
   


zDescriptorDownloader.queryr   NNF)r7   r   r   r   rW   r   r   r   r   r   r   r   r   r"   r$   r   r   r   r   r   r     s    



.(
!5r   c                 C   s\  | j r| j ndg}tjj| j| j|}| {}dd| ddt	dd | dtj
 fd	 }|j|d
d}|dd
\}}	|	dd
\}
}|dsYtdt| i }t|
 D ]}d|vrotd| |dd
\}}|||< qbt||d|fW  d   W  d   S 1 sw   Y  W d   dS 1 sw   Y  dS )a1  
  Downloads descriptors from the given orport. Payload is just like an http
  response (headers and all)...

  ::

    HTTP/1.0 200 OK
    Date: Mon, 23 Apr 2018 18:43:47 GMT
    Content-Type: text/plain
    X-Your-Address-Is: 216.161.254.25
    Content-Encoding: identity
    Expires: Wed, 25 Apr 2018 18:43:47 GMT

    router dannenberg 193.23.244.244 443 0 80
    identity-ed25519
    ... rest of the descriptor content...

  :param stem.ORPort endpoint: endpoint to download from
  :param list compression: compression methods for the request
  :param str resource: descriptor resource to download

  :returns: two value tuple of the form (data, reply_headers)

  :raises:
    * :class:`stem.ProtocolError` if not a valid descriptor response
    * :class:`stem.SocketError` if unable to establish a connection
     z
zGET %s HTTP/1.0zAccept-Encoding: %s, c                 S      | j S r   encodingcr   r   r   <lambda>
      z'_download_from_orport.<locals>.<lambda>zUser-Agent: %sz

r)   )Z	stream_ids   
s   

s
   HTTP/1.0 2z5Response should begin with HTTP success, but was '%s'z: z'%s' is not a HTTP header:

%sContent-EncodingN)link_protocolsr0   ZclientZRelayZconnectrt   ry   Zcreate_circuitr_   map
USER_AGENTrp   splitr*   ZProtocolErrorr   Z_to_unicode
splitlines_decompressget)rV   r@   r?   r   ZrelayZcircrequestresponseZ
first_linedataZheader_dataZ	body_dataheaderslinekeyvaluer   r   r   rz     s4   


"rz   c              
   C   s   zt jt j| dtdd |tjdd|d}W n, tjy3 } zt	| |t
 d |d}~w   t
 d	d
 \}}t| ||t| |jd|jfS )a  
  Downloads descriptors from the given url.

  :param str url: dirport url from which to download from
  :param list compression: compression methods for the request
  :param float timeout: duration before we'll time out our request

  :returns: two value tuple of the form (data, reply_headers)

  :raises:
    * :class:`~stem.DownloadTimeout` if our request timed out
    * :class:`~stem.DownloadFailed` if our request fails
  r   c                 S   r   r   r   r   r   r   r   r   5  r   z(_download_from_dirport.<locals>.<lambda>)zAccept-Encodingz
User-Agent)r   )rH   r&   Nr)   r   r   )urllibZurlopenZRequestr_   r   r0   r   socketrH   ZDownloadTimeoutr}   r~   ZDownloadFailedr   readr   r   )Zurlr@   rH   r   re   Z
stacktracer   r   r   r|   !  s"   

r|   c                 C   sJ   |dkrt jjj| S t jjD ]}||jkr||   S qtd| )a:  
  Decompresses descriptor data.

  Tor doesn't include compression headers. As such when using gzip we
  need to include '32' for automatic header detection...

    https://stackoverflow.com/questions/3122145/zlib-error-error-3-while-decompressing-incorrect-header-check/22310760#22310760

  ... and with zstd we need to use the streaming API.

  :param bytes data: data we received
  :param str encoding: 'Content-Encoding' header of the response

  :raises:
    * **ValueError** if encoding is unrecognized
    * **ImportError** if missing the decompression module
  Zdeflatez('%s' isn't a recognized type of encoding)r0   r3   r-   r   
decompressr   r+   )r   r   r@   r   r   r   r   D  s   
r   c                 C   s   |  drdS |  drdS |  drdS |  drdS |  d	rR| d
s+| dr-dS | dr4dS | dr=dt S tjj| dd rKdS | drRdS td|  )Nz/tor/server/zserver-descriptor 1.0z/tor/extra/zextra-info 1.0z/tor/micro/zmicrodescriptor 1.0z
/tor/keys/zdir-key-certificate-3 1.0z/tor/status-vote/z
/consensusz
/authorityznetwork-status-consensus-3 1.0z/consensus-microdescz(network-status-microdesc-consensus-3 1.0z/consensus-signaturesz%s 1.0r'   z
/bandwidthzbandwidth-file 1.0z0Unable to determine the descriptor type for '%s')	r*   r,   r`   r0   utilZ	tor_toolsZis_valid_fingerprintr   r+   )r?   r   r   r   r9   a  s(   







r9   c                   C   s   t  S )a  
  Provides cached Tor directory authority information. The directory
  information hardcoded into Tor and occasionally changes, so the information
  this provides might not necessarily match your version of tor.

  .. deprecated:: 1.7.0
     Use stem.directory.Authority.from_cache() instead.

  :returns: **dict** of **str** nicknames to :class:`~stem.directory.Authority` instances
  )DirectoryAuthorityrr   r   r   r   r   get_authorities  s   r   r   r   )<r   ra   rn   r   r}   rO   rx   r0   Zstem.clientZstem.descriptorZstem.descriptor.networkstatusZstem.directoryZstem.prereqZstem.util.enumZstem.util.tor_toolsZ	stem.utilr   r   Zurllib.requestr   r   ImportErrorZurllib2r   enumEnumr-   r3   r   r   r   r
   r5   r   r   r   r`   ri   r   r   r   r   r   r   r"   r$   objectr%   r   rz   r|   r   r9   r   rp   Z	Directoryrq   r   ZFallbackZFallbackDirectoryr   r   r   r   <module>   sv   a




  q  i:#