
    hFc                     F   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 	 ddlmZ dZ ej,                  ddddd	d
ddd	      ZdZdZdaej6                  dej8                  dej:                  dej<                  dej>                  dej@                  dejB                  dejD                  dejF                  di	Z$ej6                  dej8                  dej:                  dej<                  dej>                  dej@                  dejB                  dejD                  dejF                  d i	Z% G d! d" ejL                  d"g d#            Z'd3d$Z(d4d%Z)d5d&Z*d' Z+d( Z,d6d)Z-d6d*Z.d+ Z/d, Z0d- Z1d. Z2d/ Z3d0 Z4d1 Z5d2 Z6e*Z7y# e$ r ddlZY ]w xY w)7at  
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)NETSTATnetstat)NETSTAT_WINDOWSznetstat (windows))SSss)LSOF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                       e Zd ZdZy)
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__     ^/var/www/betterdocs.net/sherlock_api/venv/lib/python3.12/site-packages/stem/util/connection.pyr   r      s    r   r   )local_address
local_portremote_addressremote_portprotocolis_ipv6c                 P   |d}t        j                          }	 t        j                  | |      j                         S # t        j
                  $ r2}t        j                  | |t        j                         d   |      d}~w t        j                         dd \  }}||t        j                          |z
  z  }|dkD  r4||dkD  r-t        j                  d| ||fz         t        | ||dz
        cY S t        j                  d| d	|       t        j                  | ||      xY w)
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 : )timeurlliburlopenreadsocketr&   stemDownloadTimeoutsysexc_infor   debugdownloadDownloadFailed)urlr&   retries
start_timeexc
stacktraces         r   r5   r5      s   $ _Gyy{*6>>#16688	 E


sC):G
DD6llnQq)OCz))g{7Q;	iiG3PWY\J]]^c7GaK00	iiC=>S*55s   $? D%-A??A2D%32D%c                    | st               }|r|d   } nt        d      |s|st        d      d  d        d| d|d|       t        |t              r	 t        |      }|t        j                  j                  j                  |d      }t        |      dk(  rC| t        j                  t        j                  t        j                  fv rit        d|d| d      t        |      dk(  r|d   }nC| t        j                  t        j                  t        j                  fv rt        d|d| d      | t        j                  k(  r*t        j                  j                  j!                  |      S t"        |    j%                  |      }	 t        j                  j                  j'                  |      }t*        |    j%                  ddd|r|nd|r|nd      } d|z          ddj-                  |      z         g }	t/        j0                  |      }
fd}|D ]  }|
j3                  |      }|s|j5                         } |d|d   |      \  }} |d |d    |      \  }}|r|r|r|sP|d!   j7                         }|d"k(  rd#}|d$vr d%|d&|       }t9        |||||t;        |            }|	j=                  |        t	        |               d't        |	      z         |	st        d(|z        |	S # t        $ r t        d	|z        w xY w# t(        $ r}t        d|d|      d
}~ww xY w))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                 <    t         rt        j                  |        y y N)LOG_CONNECTION_RESOLUTIONr   r4   )msgs    r   _logzget_connections.<locals>._log  s     	iin !r   zP================================================================================z#Querying connections for resolver: z, pid: z, name: zProcess pid was non-numeric: %sNTz Unable to determine the pid of 'z'. z- requires the pid to provide the connections.r(   z"There's multiple processes named 'z2 requires a single pid to provide the connections.)pidzUnable to query '': z(?P<protocol>\S+)z(?P<local>[\[\]0-9a-f.:]+)z(?P<remote>[\[\]0-9a-f.:]+)z[0-9]*z\S*)r#   localremoterB   namezResolver regex: %szResolver results:
%s
c                 4   |j                  dd      \  }}t        |      st        |d      s d| d|d|       yt        |      s d| d	|d|       y d
|d|d|       |j	                  d      j                  d      t        |      fS )N:r(   T)allow_bracketszInvalid z
 address (): NNz port (zValid r*   [])rsplitis_valid_ipv4_addressis_valid_ipv6_addressis_valid_portlstriprstripint)	addr_typeaddr_strlineaddrportrA   s        r   _parse_address_strz+get_connections.<locals>._parse_address_str3  s    a(JD$ &/DT\`/a
ItTBC4 
D$?@
dD12[[$$S)3t944r   rD   rE   r#   tcp6tcp)r]   udpzUnrecognized protocol (rK   z%i connections foundzNo results found using: %s)system_resolversIOError
ValueError
isinstancestrrU   r0   utilsystempid_by_namelenResolverr   r   r   r   connectionsRESOLVER_COMMANDformatcallOSErrorRESOLVER_FILTERjoinrecompilematch	groupdictlowerr   rQ   append)resolverprocess_pidprocess_nameavailable_resolversall_pidsresolver_commandresultsr:   resolver_regex_strri   resolver_regexr[   rX   rr   attr
local_addrr    remote_addrr"   r#   connrA   s                        @r   get_connectionsr      s^   < 
*,$Q'h?@@	\
X
YY x.X{\hijS!H$k yy++L$?H
8}	h..x?T?TU	Up|  G  H  I  	I	X!	QKk	h..x?T?TU	U  xD  FN  O  P  	P99>>%%K%88%h/66[6IHii##$45G 'x077#*,$+('<V 8  0017!334+::01.5  d  &E__d1'4=$Oj*!3Hd8nd!Sk;ZKKj!'')h	V			'4@A
J[(TijtTuvd
3t9o/2 K 001	
.1AA
BB	e  H8;FGGH, 
 H
0@#F
GGHs$   L )L L	L>(L99L>c                    | ?t         j                  j                  j                         rd} nt	        j                         } | dk(  rt
        j                  g}n| dk(  rt
        j                  g}n| dk(  rt
        j                  g}ns| dk(  r0t
        j                  t
        j                  t
        j                  g}n>t
        j                  t
        j                  t
        j                  t
        j                  g}|D cg c]5  }t         j                  j                  j                  t        |         s4|7 }}t         j                  j                   j                         r\t#        j$                  dt"        j&                        r8t#        j$                  dt"        j&                        rt
        j(                  g|z   }|S c c}w )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
  GentooWindowsDarwinOpenBSDFreeBSDz/proc/net/tcpz/proc/net/udp)r0   rd   re   	is_gentooplatformrh   r   r   r   r   r   r	   r   r   is_availablerj   r   osaccessR_OKr   )re   	resolversrs      r   r_   r_   a  sD     ^yy!!#f fy))*II##$I
 &&(=(=x}}MI !!8#4#4hmmX[[QI $ZQtyy'7'7'D'DEUVWEX'YqZ)Z 
YY^^  "ryy"'''JryyYhjljqjqOr)+I	 [s   >5G 4G c                    t         t        j                         }t        j                  j                  t        j                  j                  t              d      }	 |j                  |       i }|j                  di       j                         D ]v  \  }}|j                         r||t        |      <   %d|v rA|j                  dd      \  }}t        t        |      t        |      dz         D ]  }|||<   	 jt        d|z         |a t         syt'        | t(              r| j                         rt        |       } t         j                  |       S # t         $ r%}	t#        j$                  d|d|	       Y d}	~	pd}	~	ww xY w)	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.cfgrZ   -r(   z'%s' is an invalid keyz>BUG: stem failed to load its internal port descriptions from 'rC   )	PORT_USESr   Configr   pathro   dirname__file__loadgetitemsisdigitrU   splitrangera   	Exceptionr   warnrb   rc   )
rZ   configconfig_path	port_useskeyvaluemin_portmax_port
port_entryr:   s
             r   
port_usager     sJ    [[]F'',,rwwx8+FKmkk+i

62.446 	;*#u;;=$3s8	CZ"yya0
(H!#h-X1BC *j$)Ij!* 3c9:
:	; i 
ct||~t9D	t	  m	hhZegjkllms   B.E 	FE<<Fc                 t   t        | t              rt        j                  |       } n t        j
                  j                  |       sy| j                  d      dk7  ry| j                  d      D ]I  }|j                         rt        |      dk  st        |      dkD  r y|d   dk(  s:t        |      dkD  sI y y)	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)rb   bytesr   _to_unicoder0   rd   _is_strcountr   r   rU   rg   )addressentrys     r   rP   rP     s     ##G,G99W% ]]31 }}S! e==?c%j1nE
S0@	qSSZ!^	 
r   c                    t        | t              rt        j                  |       } n t        j
                  j                  |       sy|r'| j                  d      r| j                  d      r| dd } | j                  d      dk(  r| j                  dd	| j                  d            dz   }| j                  d|dz         }|dk(  rd
}t        | ||       sy|d	k7  r| d
|dz
   nd
d|r| |dz   d
 nd
g}dj                  t        d
|            } | j                  d      }|dkD  ry|dk7  rd| vry| j                  d      dkD  sd| v ry| j                  d      D ]  }t!        j"                  d|      r y y)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
  FrM   rN   r(   r   r)   rI   r   Nzff:ff   ::z:::z^[0-9a-fA-f]{0,4}$T)rb   r   r   r   r0   rd   r   
startswithendswithr   rfindfindrP   ro   filterr   rp   rr   )r   rJ   
ipv4_startipv4_end	addr_compcolon_countr   s          r   rQ   rQ     s    ##G,G99W%#7#3#3C#8"g]]31 sAw||C'89A=J||Ca0H2~h H!=>-71_*q.)$ksQXYadeYeYfQgy}~IhhvdI./G c"+1_aD/}}TQ%7"2}}S! e88(%0 
r   c                     	 t        |       }t        |      t        |       k7  ry|r|dk(  ry|dkD  xr |dk  S # t        $ r1 t        | t        t
        f      r| D ]  }t        ||      r Y y Y yY yt        $ r Y yw xY 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   )rU   rc   	TypeErrorrb   tuplelistrR   ra   )r   
allow_zeror   rZ   s       r   rR   rR     s    JE
5zSZ	
QY(55=(	 %%' $T:. 	 s)   "9 9 9 0A=*A=-A=2A=<A=c                    t        |       st        d| z        | j                  d      s"| j                  d      s| j                  d      ry| j                  d      r(t        | j	                  d      d         }|d	k\  r|d
k  ryy)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.r   r(         F)rP   ra   r   rU   r   )r   second_octets     r   is_private_addressr   6  s    " 
w	'
6@
AA '"4"4Z"@GDVDVW]D^ w}}S)!,-Lrlb0	r   c                 ,    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'   )rU   _address_to_binary)r   s    r   address_to_intr   Z  s     
(!	,,r   c           
      ,   t        |       st        d| z        | j                  d      dk(  r| j                  dd| j	                  d            dz   }| j	                  d|dz         }|dk(  rd}t        | ||       }t        d	      D cg c]  }|d
|z  d
|dz   z    }}dj                  |D cg c]  }dt        |d	      z   c}      }|dk7  r| d|dz
   nd||r| |dz   d ndg}dj                  t        d|            } d| v r,d| j                  d      z
  }	| j                  ddd|	z  z         } t        d      D ]H  }
|
dz  }|
dk7  r| j                  d|      n
t        |       }d||z
  z
  }|dkD  s8| d| d|z  z   | |d z   } J | S c c}w c c}w )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 addressr   r)   rI   r   r(   r   Nr'   r   %04xr   r            r   )rQ   ra   r   r   r   r   r   ro   rU   r   replaceindexrg   )r   r   r   ipv4_bini	groupingsgroupipv6_snippetr   missing_groupsr   startendmissing_zeross                 r   expand_ipv6_addressr   l  s   * 
w	'
6@
AA ]]31sAw||C'89A=J||Ca0H2~h "'*X">?H8=aA1"q&q1u.AIA88KVc%m3KLL-71_*q.)$pxV]^fij^j^kVl  C  DIhhvdI./G 
W_s++NoodD3+?$?@G Qx HeAIE',z'--U
#s7|Cu%Mq#"55GgH 
.+ BKs   
F0Fc                 8   | dkD  s| dk  rt        d| z        | dk(  rt        S t        d| z  dz
  d      ddd   }t        d      D cg c]  }|d	|z  d	|dz   z    }}d
j	                  |D cg c]  }t        t        |d             c}      S c c}w c c}w )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   r   r   r   )ra   FULL_IPv4_MASK_get_binaryr   ro   rc   rU   )bitsmask_binr   octetsoctets        r   get_mask_ipv4r     s     
BY$(
;dB
CCrz dQ+DbD1( 27q:AHQU1A;':&: 
6:%3s5!}%:	;; ; ;s   	B/Bc           
      H   | dkD  s| dk  rt        d| z        | dk(  rt        S t        d| z  dz
  d      ddd   }t        d      D cg c]  }|d	|z  d	|dz   z    }}d
j	                  |D cg c]  }dt        |d      z   c}      j                         S c c}w c c}w )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   r   r   rI   r   )ra   FULL_IPv6_MASKr   r   ro   rU   upper)r   r   r   r   r   s        r   get_mask_ipv6r     s     
CZ4!8
<tC
DDs{ dQ,TrT2( 7<Ah?xQrQU|,?)? 
yAe6CqM)A	B	H	H	JJ @ Bs   	B/Bc                     t        |       st        d| z        t        |       }t        j                  d|      }|rdt        |j                         d         z
  S t        d| z        )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)rP   ra   r   rp   rr   rg   groups)maskr   
mask_matchs      r   _get_masked_bitsr     sk     
t	$
5<
==  %(xxh/*J%%'*+++
@4G
HHr   c           	          dj                  t        |dz
  dd      D cg c]  }t        | |z	  dz         c}      S c c}w )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   r(   r   )ro   r   rc   )r   r   ys      r   r   r     s<     
taxR1HIA#uzQ&'I	JJIs   =c                 z   t        |       rAdj                  | j                  d      D cg c]  }t        t	        |      d       c}      S t        |       rMt        |       } dj                  | j                  d      D cg c]  }t        t	        |d      d       c}      S t        d| z        c c}w c c}w )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   r   r   rI   r   z''%s' is neither an IPv4 or IPv6 address)rP   ro   r   r   rU   rQ   r   ra   )r   r   groupings      r   r   r     s     7#77GMM#<NO5KE
A.OPPW%!'*G77w}}UXGYZ8KHb 126Z[[
>H
II P [s   B3B8rL   )NNNr>   )F)8r   collectionsr   r   rp   r/   r2   r+   r0   	stem.utilstem.util.procstem.util.systemr   r   r   r   urllib.requestrequestr,   ImportErrorurllib2r?   Enumrh   r   r   r   r   r	   r   r   r   r   r   r   r   rj   rn   
namedtupler   r5   r   r_   r   rP   rQ   rR   r   r   r   r   r   r   r   r   get_system_resolversr   r   r   <module>r     s  4l  	  	  
      0 0! " 499*$$
 #:	 -- N N ++z --
Z  , &3 : -- b a ++ --_ e b [ q3:'''  7N  O  &6REP0f+\:4n@!H-$:z<4K6I2
K J, ( c  s   F 	F F 