o
    yK™]Fc  ã                   @   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	mZmZmZmZ zddlmZ W n eyO   ddlZY nw dZe ddddd	d
ddd¡	ZdZdZdaejdejdejdejdejdej dej!dej"dej#di	Z$ejdejdejdejdejdej dej!dej"dej#d i	Z%G d!d"„ d"e &d"g d#¢¡ƒZ'dBd$d%„Z(dCd&d'„Z)dDd(d)„Z*d*d+„ Z+d,d-„ Z,dEd.d/„Z-dEd0d1„Z.d2d3„ Z/d4d5„ Z0d6d7„ Z1d8d9„ Z2d:d;„ Z3d<d=„ Z4d>d?„ Z5d@dA„ Z6e*Z7dS )Fat  
Connection and networking based utility functions.

**Module Overview:**

::

  download - download from a given url
  get_connections - quieries the connections belonging to a given process
  system_resolvers - provides connection resolution methods that are likely to be available
  port_usage - brief description of the common usage for a port

  is_valid_ipv4_address - checks if a string is a valid IPv4 address
  is_valid_ipv6_address - checks if a string is a valid IPv6 address
  is_valid_port - checks if something is a valid representation for a port
  is_private_address - checks if an IPv4 address belongs to a private range or not

  address_to_int - provides an integer representation of an IP address

  expand_ipv6_address - provides an IPv6 address with its collapsed portions expanded
  get_mask_ipv4 - provides the mask representation for a given number of bits
  get_mask_ipv6 - provides the IPv6 mask representation for a given number of bits

.. data:: Resolver (enum)

  Method for resolving a process' connections.

  .. versionadded:: 1.1.0

  .. versionchanged:: 1.4.0
     Added **NETSTAT_WINDOWS**.

  .. versionchanged:: 1.6.0
     Added **BSD_FSTAT**.

  .. deprecated:: 1.6.0
     The SOCKSTAT connection resolver is proving to be unreliable
     (:trac:`23057`), and will be dropped in the 2.0.0 release unless fixed.

  ====================  ===========
  Resolver              Description
  ====================  ===========
  **PROC**              /proc contents
  **NETSTAT**           netstat
  **NETSTAT_WINDOWS**   netstat command under Windows
  **SS**                ss command
  **LSOF**              lsof command
  **SOCKSTAT**          sockstat command under \*nix
  **BSD_SOCKSTAT**      sockstat command under FreeBSD
  **BSD_PROCSTAT**      procstat command under FreeBSD
  **BSD_FSTAT**         fstat command under OpenBSD
  ====================  ===========
é    N)ÚconfÚenumÚlogÚ	str_toolsF)ÚPROCÚproc)ÚNETSTATZnetstat)ÚNETSTAT_WINDOWSznetstat (windows))ÚSSÚss)ÚLSOFZlsof)ÚSOCKSTATÚsockstat)ÚBSD_SOCKSTATzsockstat (bsd))ÚBSD_PROCSTATzprocstat (bsd))Ú	BSD_FSTATzfstat (bsd)z255.255.255.255z'FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFFÚ znetstat -npWznetstat -anozss -nptuz
lsof -wnPir   zsockstat -4czprocstat -f {pid}zfstat -p {pid}zF^{protocol}\s+.*\s+{local}\s+{remote}\s+ESTABLISHED\s+{pid}/{name}\s*$z=^\s*{protocol}\s+{local}\s+{remote}\s+ESTABLISHED\s+{pid}\s*$ze^{protocol}\s+ESTAB\s+.*\s+{local}\s+{remote}\s+users:\(\("{name}",(?:pid=)?{pid},(?:fd=)?[0-9]+\)\)$zF^{name}\s+{pid}\s+.*\s+{protocol}\s+{local}->{remote} \(ESTABLISHED\)$zG^\S+\s+{name}\s+{pid}\s+{protocol}4\s+{local}\s+{remote}\s+ESTABLISHED$z?^\S+\s+{name}\s+{pid}\s+\S+\s+{protocol}4\s+{local}\s+{remote}$z:^\s*{pid}\s+{name}\s+.*\s+{protocol}\s+{local}\s+{remote}$zO^\S+\s+{name}\s+{pid}\s+.*\s+{protocol}\s+\S+\s+{local}\s+[-<]-[->]\s+{remote}$c                   @   s   e Zd ZdZdS )Ú
ConnectionaÌ  
  Network connection information.

  .. versionchanged:: 1.5.0
     Added the **is_ipv6** attribute.

  :var str local_address: ip address the connection originates from
  :var int local_port: port the connection originates from
  :var str remote_address: destionation ip address
  :var int remote_port: destination port
  :var str protocol: protocol of the connection ('tcp', 'udp', etc)
  :var bool is_ipv6: addresses are ipv6 if true, and ipv4 otherwise
  N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__© r   r   ú6/usr/lib/python3/dist-packages/stem/util/connection.pyr       s    r   )Zlocal_addressÚ
local_portZremote_addressÚremote_portÚprotocolZis_ipv6c              
   C   sì   |du rd}t   ¡ }z
tj| |d ¡ W S  tjy- } zt | |t 	¡ d |¡‚d}~w   t 	¡ dd… \}}|durF|t   ¡ | 8 }|dkrf|du sR|dkrft
 d| ||f ¡ t| ||d ƒ Y S t
 d| |f ¡ t | ||¡‚)	aµ  
  Download from the given url.

  .. versionadded:: 1.8.0

  :param str url: uncompressed url to download from
  :param int timeout: timeout when connection becomes idle, no timeout applied
    if **None**
  :param int retires: maximum attempts to impose

  :returns: **bytes** content of the given url

  :raises:
    * :class:`~stem.DownloadTimeout` if our request timed out
    * :class:`~stem.DownloadFailed` if our request fails
  Nr   )Útimeouté   é   é   z5Failed to download from %s (%i retries remaining): %szFailed to download from %s: %s)ÚtimeÚurllibZurlopenÚreadÚsocketr   ÚstemZDownloadTimeoutÚsysÚexc_infor   ÚdebugÚdownloadZDownloadFailed)Zurlr   ZretriesZ
start_timeÚexcZ
stacktracer   r   r   r)   °   s"   €r)   c              
      sº  | st ƒ }|r|d } ntdƒ‚|s|stdƒ‚dd„ ‰ ˆ dƒ ˆ d| ||f ƒ t|tƒrBzt|ƒ}W n tyA   td| ƒ‚w |d	u r„tjj 	|d
¡}t
|ƒdkrg| tjtjtjfv rftd|| f ƒ‚nt
|ƒdkrr|d }n| tjtjtjfv r„td|| f ƒ‚| tjkr‘tjjj|dS t|  j|d}z	tjj |¡}W n ty¶ } ztd||f ƒ‚d	}~ww t|  jddd|rÂ|nd|rÇ|ndd}ˆ d| ƒ ˆ dd |¡ ƒ g }	t |¡}
‡ fdd„}|D ]`}|
 |¡}|rI| ¡ }|d|d |ƒ\}}|d|d |ƒ\}}|r|r|r|sqé|d  ¡ }|dkr%d}|d vr3ˆ d!||f ƒ qét|||||t|ƒƒ}|	 |¡ ˆ t|ƒƒ qéˆ d"t
|	ƒ ƒ |	s[td#| ƒ‚|	S )$a&  
  Retrieves a list of the current connections for a given process. This
  provides a list of :class:`~stem.util.connection.Connection`. Note that
  addresses may be IPv4 *or* IPv6 depending on what the platform supports.

  .. versionadded:: 1.1.0

  .. versionchanged:: 1.5.0
     Made our resolver argument optional.

  .. versionchanged:: 1.5.0
     IPv6 support when resolving via proc, netstat, lsof, or ss.

  :param Resolver resolver: method of connection resolution to use, if not
    provided then one is picked from among those that should likely be
    available for the system
  :param int process_pid: pid of the process to retrieve
  :param str process_name: name of the process to retrieve

  :returns: **list** of :class:`~stem.util.connection.Connection` instances

  :raises:
    * **ValueError** if neither a process_pid nor process_name is provided

    * **IOError** if no connections are available or resolution fails
      (generally they're indistinguishable). The common causes are the
      command being unavailable or permissions.
  r   z)Unable to determine a connection resolverzAYou must provide a pid or process name to provide connections forc                 S   s   t r	t | ¡ d S d S ©N)ÚLOG_CONNECTION_RESOLUTIONr   r(   )Úmsgr   r   r   Ú_log  s   ÿzget_connections.<locals>._logzP================================================================================z8Querying connections for resolver: %s, pid: %s, name: %szProcess pid was non-numeric: %sNTzTUnable to determine the pid of '%s'. %s requires the pid to provide the connections.r   z[There's multiple processes named '%s'. %s requires a single pid to provide the connections.)ÚpidzUnable to query '%s': %sz(?P<protocol>\S+)z(?P<local>[\[\]0-9a-f.:]+)z(?P<remote>[\[\]0-9a-f.:]+)z[0-9]*z\S*)r   ÚlocalÚremoter/   ÚnamezResolver regex: %szResolver results:
%sÚ
c                    s‚   |  dd¡\}}t|ƒst|ddsˆ d| ||f ƒ dS t|ƒs,ˆ d| ||f ƒ dS ˆ d|||f ƒ | d	¡ d
¡t|ƒfS )Nú:r   T)Úallow_bracketszInvalid %s address (%s): %s©NNzInvalid %s port (%s): %szValid %s:%s: %sú[ú])ÚrsplitÚis_valid_ipv4_addressÚis_valid_ipv6_addressÚis_valid_portÚlstripÚrstripÚint)Z	addr_typeZaddr_strÚlineZaddrÚport©r.   r   r   Ú_parse_address_str3  s   z+get_connections.<locals>._parse_address_strr0   r1   r   Ztcp6Útcp)rD   ZudpzUnrecognized protocol (%s): %sz%i connections foundzNo results found using: %s)Úsystem_resolversÚIOErrorÚ
ValueErrorÚ
isinstanceÚstrr?   r%   ÚutilÚsystemZpid_by_nameÚlenÚResolverr	   r   r   r   ÚconnectionsÚRESOLVER_COMMANDÚformatÚcallÚOSErrorÚRESOLVER_FILTERÚjoinÚreÚcompileÚmatchÚ	groupdictÚlowerr   r;   Úappend)ZresolverZprocess_pidZprocess_nameZavailable_resolversZall_pidsZresolver_commandÚresultsr*   Zresolver_regex_strrN   Zresolver_regexrC   r@   rW   ÚattrZ
local_addrr   Zremote_addrr   r   Zconnr   rB   r   Úget_connectionsÙ   sŠ   

ÿÿ

€ÿ

û




€r]   c                 C   sÌ   | du rt jj ¡ rd} nt ¡ } | dkrtjg}n)| dkr#tjg}n | dkr,tjg}n| dkr9tj	tj
tjg}n
tjtjtjtjg}dd„ |D ƒ}t jj ¡ rdt d	tj¡rdt d
tj¡rdtjg| }|S )aû  
  Provides the types of connection resolvers likely to be available on this platform.

  .. versionadded:: 1.1.0

  .. versionchanged:: 1.3.0
     Renamed from get_system_resolvers() to system_resolvers(). The old name
     still works as an alias, but will be dropped in Stem version 2.0.0.

  :param str system: system to get resolvers for, this is determined by
    platform.system() if not provided

  :returns: **list** of :data:`~stem.util.connection.Resolver` instances available on this platform
  NZGentooZWindowsZDarwinZOpenBSDZFreeBSDc                 S   s"   g | ]}t jj t| ¡r|‘qS r   )r%   rJ   rK   Úis_availablerO   )Ú.0Úrr   r   r   Ú
<listcomp>Š  s   " z$system_resolvers.<locals>.<listcomp>z/proc/net/tcpz/proc/net/udp)r%   rJ   rK   Z	is_gentooÚplatformrM   r	   r   r   r   r   r   r   r
   r   r^   ÚosÚaccessÚR_OKr   )rK   Z	resolversr   r   r   rE   a  s"   


(rE   c           
   
   C   s  t du rvt ¡ }tj tj t¡d¡}zG| |¡ i }| 	di ¡ 
¡ D ]3\}}| ¡ r2||t|ƒ< q#d|v rQ| dd¡\}}tt|ƒt|ƒd ƒD ]}|||< qIq#td| ƒ‚|a W n tyu }	 zt d||	f ¡ W Y d}	~	nd}	~	ww t szdS t| tƒr‡|  ¡ r‡t| ƒ} t  	| ¡S )z÷
  Provides the common use of a given port. For example, 'HTTP' for port 80 or
  'SSH' for 22.

  .. versionadded:: 1.2.0

  :param int port: port number to look up

  :returns: **str** with a description for the port, **None** if none is known
  Nz	ports.cfgrA   ú-r   z'%s' is an invalid keyzEBUG: stem failed to load its internal port descriptions from '%s': %s)Ú	PORT_USESr   ZConfigrc   ÚpathrT   ÚdirnameÚ__file__ÚloadÚgetÚitemsÚisdigitr?   ÚsplitÚrangerG   Ú	Exceptionr   ÚwarnrH   rI   )
rA   ZconfigZconfig_pathZ	port_usesÚkeyÚvalueZmin_portZmax_portZ
port_entryr*   r   r   r   Ú
port_usage”  s2   

ÿ€ÿ
ru   c                 C   s   t | tƒrt | ¡} ntj | ¡sdS |  d¡dkrdS |  d¡D ]$}| 	¡ r3t
|ƒdk s3t
|ƒdkr6 dS |d dkrEt|ƒdkrE dS q!dS )	z©
  Checks if a string is a valid IPv4 address.

  :param str address: string to be checked

  :returns: **True** if input is a valid IPv4 address, **False** otherwise
  FÚ.r    r   éÿ   Ú0r   T)rH   Úbytesr   Ú_to_unicoder%   rJ   Ú_is_strÚcountro   rn   r?   rL   )ÚaddressÚentryr   r   r   r:   Â  s   
	 €r:   c                 C   sP  t | tƒrt | ¡} ntj | ¡sdS |r%|  d¡r%|  d¡r%| dd… } |  	d¡dkrs|  
dd	|  d¡¡d }|  d|d ¡}|dkrFd
}t| ||… ƒsPdS |d	kr\| d
|d … nd
d|rh| |d d
… nd
g}d td
|ƒ¡} |  	d¡}|dkr~dS |dkrˆd| vrˆdS |  	d¡dks“d| v r•dS |  d¡D ]}t d|¡s¥ dS qšdS )zî
  Checks if a string is a valid IPv6 address.

  :param str address: string to be checked
  :param bool allow_brackets: ignore brackets which form '[address]'

  :returns: **True** if input is a valid IPv6 address, **False** otherwise
  Fr7   r8   r   éÿÿÿÿrv   r    r4   r   Nzff:ffé   ú::z:::z^[0-9a-fA-f]{0,4}$T)rH   ry   r   rz   r%   rJ   r{   Ú
startswithÚendswithr|   ÚrfindÚfindr:   rT   Úfilterro   rU   rW   )r}   r5   Ú
ipv4_startÚipv4_endÚ	addr_compZcolon_countr~   r   r   r   r;   ß  s8   

6
ÿr;   c                 C   sš   z!t | ƒ}t|ƒt| ƒkrW dS |r|dkrW dS |dko |dk W S  tyC   t| ttfƒr@| D ]}t||ƒs< Y dS q1Y dS Y dS  tyL   Y dS w )a+  
  Checks if a string or int is a valid port number.

  :param list,str,int entry: string, integer or list to be checked
  :param bool allow_zero: accept port number of zero (reserved by definition)

  :returns: **True** if input is an integer and within the valid port range, **False** otherwise
  Fr   Ti   )r?   rI   Ú	TypeErrorrH   ÚtupleÚlistr<   rG   )r~   Z
allow_zerort   rA   r   r   r   r<     s$   

ÿÿr<   c                 C   sj   t | ƒs
td|  ƒ‚|  d¡s|  d¡s|  d¡rdS |  d¡r3t|  d¡d ƒ}|d	kr3|d
kr3dS dS )a“  
  Checks if the IPv4 address is in a range belonging to the local network or
  loopback. These include:

    * Private ranges: 10.*, 172.16.* - 172.31.*, 192.168.*
    * Loopback: 127.*

  .. versionadded:: 1.1.0

  :param str address: string to be checked

  :returns: **True** if input is in a private range, **False** otherwise

  :raises: **ValueError** if the address isn't a valid IPv4 address
  z'%s' isn't a valid IPv4 addressz10.z192.168.z127.Tz172.rv   r   é   é   F)r:   rG   r‚   r?   ro   )r}   Zsecond_octetr   r   r   Úis_private_address6  s   
r   c                 C   s   t t| ƒdƒS )zÜ
  Provides an integer representation of a IPv4 or IPv6 address that can be used
  for sorting.

  .. versionadded:: 1.5.0

  :param str address: IPv4 or IPv6 address

  :returns: **int** representation of the address
  r   )r?   Ú_address_to_binary©r}   r   r   r   Úaddress_to_intZ  s   r’   c                    sj  t | ƒs
td|  ƒ‚|  d¡dkrk|  dd|  d¡¡d }|  d|d ¡}|dkr+d}t| ||… ƒ‰ ‡ fd	d
„tdƒD ƒ}d dd
„ |D ƒ¡}|dkrT| d|d … nd||r`| |d d… ndg}d td|ƒ¡} d| v r€d|  d¡ }|  	ddd|  ¡} tdƒD ].}|d }|dkr”|  
d|¡nt| ƒ}	d|	|  }
|
dkr²| d|… d|
  | |d…  } q„| S )a  
  Expands abbreviated IPv6 addresses to their full colon separated hex format.
  For instance...

  ::

    >>> expand_ipv6_address('2001:db8::ff00:42:8329')
    '2001:0db8:0000:0000:0000:ff00:0042:8329'

    >>> expand_ipv6_address('::')
    '0000:0000:0000:0000:0000:0000:0000:0000'

    >>> expand_ipv6_address('::ffff:5.9.158.75')
    '0000:0000:0000:0000:0000:ffff:0509:9e4b'

  :param str address: IPv6 address to be expanded

  :raises: **ValueError** if the address can't be expanded due to being malformed
  z'%s' isn't a valid IPv6 addressrv   r    r4   r   r   r   Nc                    ó$   g | ]}ˆ d | d |d  … ‘qS ©r   r   r   ©r_   Úi©Zipv4_binr   r   ra   ‘  ó   $ z'expand_ipv6_address.<locals>.<listcomp>r   c                 S   ó   g | ]	}d t |dƒ ‘qS ©z%04xr   ©r?   ©r_   Úgroupr   r   r   ra   ’  ó    r   r€   é   é   é   rx   )r;   rG   r|   r„   r…   r   rp   rT   r†   ÚreplaceÚindexrL   )r}   r‡   rˆ   Ú	groupingsZipv6_snippetr‰   Zmissing_groupsr£   ÚstartÚendZmissing_zerosr   r—   r   Úexpand_ipv6_addressl  s.   6 €r§   c                    sn   | dks| dk rt d|  ƒ‚| dkrtS td|  d dƒddd… ‰ ‡ fdd	„td
ƒD ƒ}d dd	„ |D ƒ¡S )a!  
  Provides the IPv4 mask for a given number of bits, in the dotted-quad format.

  :param int bits: number of bits to be converted

  :returns: **str** with the subnet mask representation for this many bits

  :raises: **ValueError** if given a number of bits outside the range of 0-32
  é    r   z$A mask can only be 0-32 bits, got %ir   r   Nr   c                    r“   )rŸ   r   r   r•   ©Úmask_binr   r   ra   ½  r˜   z!get_mask_ipv4.<locals>.<listcomp>r¡   rv   c                 S   s   g | ]	}t t|d ƒƒ‘qS )r   )rI   r?   ©r_   Zoctetr   r   r   ra   À  rž   )rG   ÚFULL_IPv4_MASKÚ_get_binaryrp   rT   )ÚbitsZoctetsr   r©   r   Úget_mask_ipv4©  s   r¯   c                    sr   | dks| dk rt d|  ƒ‚| dkrtS td|  d dƒddd… ‰ ‡ fdd	„td
ƒD ƒ}d dd	„ |D ƒ¡ ¡ S )a,  
  Provides the IPv6 mask for a given number of bits, in the hex colon-delimited
  format.

  :param int bits: number of bits to be converted

  :returns: **str** with the subnet mask representation for this many bits

  :raises: **ValueError** if given a number of bits outside the range of 0-128
  é€   r   z%A mask can only be 0-128 bits, got %ir   r   Nr   c                    r“   r”   r   r•   r©   r   r   ra   Ø  r˜   z!get_mask_ipv6.<locals>.<listcomp>rŸ   r4   c                 S   r™   rš   r›   rœ   r   r   r   ra   Û  rž   )rG   ÚFULL_IPv6_MASKr­   rp   rT   Úupper)r®   r¤   r   r©   r   Úget_mask_ipv6Ã  s   r³   c                 C   sL   t | ƒs
td|  ƒ‚t| ƒ}t d|¡}|r dt| ¡ d ƒ S td|  ƒ‚)a9  
  Provides the number of bits that an IPv4 subnet mask represents. Note that
  not all masks can be represented by a bit count.

  :param str mask: mask to be converted

  :returns: **int** with the number of bits represented by the mask

  :raises: **ValueError** if the mask is invalid or can't be converted
  z'%s' is an invalid subnet maskz
^(1*)(0*)$r¨   r   z)Unable to convert mask to a bit count: %s)r:   rG   r   rU   rW   rL   Úgroups)Úmaskrª   Z
mask_matchr   r   r   Ú_get_masked_bitsÞ  s   r¶   c                    s$   d  ‡ fdd„t|d ddƒD ƒ¡S )zº
  Provides the given value as a binary string, padded with zeros to the given
  number of bits.

  :param int value: value to be converted
  :param int bits: number of bits to pad to
  r   c                    s   g | ]
}t ˆ |? d @ ƒ‘qS )r   )rI   )r_   Úy©rt   r   r   ra     ó    z_get_binary.<locals>.<listcomp>r   r   )rT   rp   )rt   r®   r   r¸   r   r­   ÷  s   $
r­   c                 C   sX   t | ƒrd dd„ |  d¡D ƒ¡S t| ƒr&t| ƒ} d dd„ |  d¡D ƒ¡S td|  ƒ‚)zÊ
  Provides the binary value for an IPv4 or IPv6 address.

  :returns: **str** with the binary representation of this address

  :raises: **ValueError** if address is neither an IPv4 nor IPv6 address
  r   c                 S   s   g | ]	}t t|ƒd ƒ‘qS )rŸ   ©r­   r?   r«   r   r   r   ra     rž   z&_address_to_binary.<locals>.<listcomp>rv   c                 S   s   g | ]
}t t|d ƒd ƒ‘qS )r   rº   )r_   Úgroupingr   r   r   ra     r¹   r4   z''%s' is neither an IPv4 or IPv6 address)r:   rT   ro   r;   r§   rG   r‘   r   r   r   r     s   	r   r6   )NNNr+   )F)8r   Úcollectionsrc   rb   rU   r$   r&   r!   r%   Z	stem.utilZstem.util.procZstem.util.systemr   r   r   r   Zurllib.requestZrequestr"   ÚImportErrorZurllib2r,   ÚEnumrM   r¬   r±   rg   r   r   r	   r
   r   r   r   r   r   rO   rS   Ú
namedtupler   r)   r]   rE   ru   r:   r;   r<   r   r’   r§   r¯   r³   r¶   r­   r   Zget_system_resolversr   r   r   r   Ú<module>   sŽ   6ÿ÷çç

) 
	3.

7 $=