
    aj                         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 ddl	m
Z
 d\  ZZ	 ddlZddlmZmZ dZ G d	 d
e      Zd Z G d de      Z G d de      Zy# e$ rZeZY dZ[;dZ[ww xY w)z2
Configuration file (aka ``ssh_config``) support.
    N)partial   )StringIO)NN)CouldNotCanonicalizeConfigParseError   c                       e Zd ZdZ ej
                  d      Zg ddgg dg dg ddZd	 Ze	d
        Z
e	d        Ze	d        Zd Zd ZddZd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zy)	SSHConfiga  
    Representation of config information as stored in the format used by
    OpenSSH. Queries can be made via `lookup`. The format is described in
    OpenSSH's ``ssh_config`` man page. This class is provided primarily as a
    convenience to posix users (since the OpenSSH format is a de-facto
    standard on posix) but should work fine on Windows too.

    .. versionadded:: 1.6
    z(\w+)(?:\s*=\s*|\s+)(.+))%h%l%L%n%p%r%ur   )~%dr   r   r   r   )r   r   r   r   )r   r   r   r   r   r   r   r   )controlpathhostnameidentityfileproxycommand
match-execc                     g | _         y)a  
        Create a new OpenSSH config object.

        Note: the newer alternate constructors `from_path`, `from_file` and
        `from_text` are simpler to use, as they parse on instantiation. For
        example, instead of::

            config = SSHConfig()
            config.parse(open("some-path.config")

        you could::

            config = SSHConfig.from_file(open("some-path.config"))
            # Or more directly:
            config = SSHConfig.from_path("some-path.config")
            # Or if you have arbitrary ssh_config text from some other source:
            config = SSHConfig.from_text("Host foo\n\tUser bar")
        N)_config)selfs    /lib/python3.12/site-packages/paramiko/config.py__init__zSSHConfig.__init__G   s    &     c                 6    | j                  t        |            S )zg
        Create a new, parsed `SSHConfig` from ``text`` string.

        .. versionadded:: 2.7
        )	from_filer   )clstexts     r   	from_textzSSHConfig.from_text\   s     }}Xd^,,r   c                 f    t        |      5 }| j                  |      cddd       S # 1 sw Y   yxY w)zr
        Create a new, parsed `SSHConfig` from the file found at ``path``.

        .. versionadded:: 2.7
        N)openr    )r!   pathflos      r   	from_pathzSSHConfig.from_pathe   s.     $Z 	&3==%	& 	& 	&s   '0c                 6     |        }|j                  |       |S )zp
        Create a new, parsed `SSHConfig` from file-like object ``flo``.

        .. versionadded:: 2.7
        )parse)r!   r'   objs      r   r    zSSHConfig.from_fileo   s     e		#
r   c                 .   dgi d}|D ]n  }|j                         }|r|j                  d      r(t        j                  | j                  |      }|st        dj                  |            |j                  d      j                         }|j                  d      }|dv rN| j                  j                  |       di i}|d	k(  r| j                  |      |d	<   | j                  |      |d
<   |dk(  r|j                         dk(  r
d|d   |<   |j                  d      r|j                  d      r|dd }|dv r+||d   v r|d   |   j                  |       S|g|d   |<   ^||d   vsg||d   |<   q | j                  j                  |       y)z
        Read an OpenSSH config from the given file object.

        :param file_obj: a file-like object to read the config file from
        *)hostconfig#zUnparsable line {}r      )r.   matchr/   r.   matchesr   noneN")r   localforwardremoteforward)strip
startswithrer2   SETTINGS_REGEXr   formatgrouplowerr   append
_get_hosts_get_matchesendswith)r   file_objcontextliner2   keyvalues          r   r*   zSSHConfig.parsez   s     5B/ 0	3D ::<D4??3/ HHT00$7E&';'B'B4'HII++a.&&(CKKNE ''##G,#R.&= '+ooe&<GFO)-):):5)AGI&&5;;=F+B *.!#& ##C(U^^C-@!!BKE
 KKgh//)#.55e<27)#. 11-2GH%c*a0	3d 	G$r   c                 D   | j                  |      }d|vr||d<   |j                  dd      dv }t        |j                  dd            }|rS|j                  d      |k  r?|d	   j	                         }| j                  |||      }||d<   | j                  ||d
      }|S )a  
        Return a dict (`SSHConfigDict`) of config options for a given hostname.

        The host-matching rules of OpenSSH's ``ssh_config`` man page are used:
        For each parameter, the first obtained value will be used.  The
        configuration files contain sections separated by ``Host`` and/or
        ``Match`` specifications, and that section is only applied for hosts
        which match the given patterns or keywords

        Since the first obtained value for each parameter is used, more host-
        specific declarations should be given near the beginning of the file,
        and general defaults at the end.

        The keys in the returned dict are all normalized to lowercase (look for
        ``"port"``, not ``"Port"``. The values are processed according to the
        rules for substitution variable expansion in ``ssh_config``.

        Finally, please see the docs for `SSHConfigDict` for deeper info on
        features such as optional type conversion methods, e.g.::

            conf = my_config.lookup('myhost')
            assert conf['passwordauthentication'] == 'yes'
            assert conf.as_bool('passwordauthentication') is True

        .. note::
            If there is no explicitly configured ``HostName`` value, it will be
            set to the being-looked-up hostname, which is as close as we can
            get to OpenSSH's behavior around that particular option.

        :param str hostname: the hostname to lookup

        .. versionchanged:: 2.5
            Returns `SSHConfigDict` objects instead of dict literals.
        .. versionchanged:: 2.7
            Added canonicalization support.
        .. versionchanged:: 2.7
            Added ``Match`` support.
        )r   r   canonicalizehostnameN)yesalwayscanonicalizemaxdotsr   .canonicaldomainsT)	canonical)_lookupgetintcountsplitcanonicalize)r   r   optionscanonmaxdotsdomainss         r   lookupzSSHConfig.lookup   s    P ,,,1 W$"*GJ2D9=NNgkk"7;<X^^C(G3 01779G((7GDH"*GJll8WlEGr   Nc                    
t               | j                  D ]  }| j                  |j                  dg       |      s%| j	                  |j                  dg       ||      sJ|d   j                         D ]:  \  }vr||d d  n|<   dk(  s   j                  fd|D               <  | j                  |      dv rd   d= S )Nr.   r3   r/   r   c              3   2   K   | ]  }|   vs|  y wN ).0xrG   rW   s     r   	<genexpr>z$SSHConfig._lookup.<locals>.<genexpr>  s!      (AWS\,A(s   r   )SSHConfigDictr   _pattern_matchesrR   _does_matchitemsextend_expand_variables)r   r   rW   rP   rE   rH   rG   s     `   @r   rQ   zSSHConfig._lookup   s    ?#oG || 	G%%gkk&"&=xH##KK	2.)W %h/557 

Ug%
 05/@58eGCLN*CL'' (#(( 
	* (((;W$)@)H'r   c                    d}|D ]D  }dj                  ||      }t        ||      }||d   }n	 t        j                  |      }|sB|c S  |j                  dd      dk(  r|S t        |      # t        j                  $ r Y ?w xY w)ag  
        Return canonicalized version of ``hostname``.

        :param str hostname: Target hostname.
        :param options: An `SSHConfigDict` from a previous lookup pass.
        :param domains: List of domains (e.g. ``["paramiko.org"]``).

        :returns: A canonicalized hostname if one was found, else ``None``.

        .. versionadded:: 2.7
        Fz{}.{}r   canonicalizefallbacklocalrK   )r=   _addressfamily_host_lookupsocketgethostbynamegaierrorrR   r   )r   r   rW   rZ   founddomain	candidatefamily_specifics           r   rV   zSSHConfig.canonicalize  s      	!Fx8I8GLO* (*"00;E  ! %	!, ;;2E:eCO #8,,  s   A..BBc                 d    t               }| j                  D ]  }|j                  |d           |S )z
        Return the set of literal hostnames defined in the SSH config (both
        explicit hostnames and wildcard entries).
        r.   )setr   update)r   hostsentrys      r   get_hostnameszSSHConfig.get_hostnames;  s3    
 \\ 	(ELLv'	(r   c                     t        |d      r|j                  d      }d}|D ]G  }|j                  d      rt        j                  ||dd        r yt        j                  ||      sFd}I |S )NrU   ,F!r   T)hasattrrU   r:   fnmatch)r   patternstargetr2   patterns        r   rd   zSSHConfig._pattern_matchesE  ss    8W%~~c*H 		G!!#&7??,  1		 r   c                 &    | j                  ||      S r^   )rd   )r   rv   r   s      r   _allowedzSSHConfig._allowedW  s    $$UH55r   c                    g }|d d  }t        j                         }|r<|j                  d      }d }	|j                  dd       }
|j                  dd       }|d   |d   }}|dk(  r| j	                  ||      ry|dk(  ry	|d
k(  r|
xs |}| j                  ||      }	n|dk(  r| j                  ||      }	n}|dk(  r|xs |}| j                  ||      }	n_|dk(  r| j                  ||      }	nG|dk(  rB| j                  ||d|      }t        t        t        j                  |dd	      j                  }	|	| j	                  |	|      ry|j                  |       |r<|S )Nr   r   usertypeparamrP   FallTr.   originalhost	localuserexecr   stdout)hidewarn)getpassgetuserpoprR   _should_failrd   	_tokenizeinvokeinvoke_import_errorrunokr@   )r   
match_listtarget_hostnamerP   rW   matched
candidateslocal_usernamerq   passedconfigured_hostconfigured_usertype_r   hostvalr   exec_cmds                    r   re   zSSHConfig._does_matchZ  s   ]
 *"q)IF &kk*d;O%kk&$7O$V,i.@5E #$$Y	:  % &)<_..ug>.(..uoF&&8...ud;+%..unE&>>_lE
 >--H8$GJJ!d&7&7	&JNN9%Y \ r   c                     |d   r|S | S )Nnegater_   )r   
would_passrq   s      r   r   zSSHConfig._should_fail  s    &x0zD*nDr   c           
         | j                  |      }|s|S |}|dk7  r|j                  d|      }d|v r|d   }nt        }t        j                         }d|v r|d   }	n|}	t        j                         j                  d      d   }
t        ||
      }t        j                  j                  d      }|||
||||	||d	}|}|j                         D ]%  \  }}||vr|j                  |t        |            }' |S )a  
        Tokenize a string based on current config/hostname data.

        :param config: Current config data.
        :param target_hostname: Original target connection hostname.
        :param key: Config key being tokenized (used to filter token list).
        :param value: Config value being tokenized.

        :returns: The tokenized version of the input ``value`` string.
        r   portr   rN   r   r   )	r   r   r   r   r   r   r   r   r   )_allowed_tokensrR   SSH_PORTr   r   rl   gethostnamerU   LazyFqdnosr&   
expanduserrf   replacestr)r   r/   r   rG   rH   allowed_tokensconfigured_hostnamer   r   
remoteuserlocal_hostname
local_fqdnhomedirreplacements	tokenizedfindr   s                    r   r   zSSHConfig._tokenize  s&    --c2L .*"(**Z9L"MV&>DD VJJ++-33C8;fn5
''$$S)
 %  "
$ 	)//1 	>MD'>)!))$G=I	>
 r   c                 :    | j                   j                  |g       S )aJ  
        Given config ``key``, return list of token strings to tokenize.

        .. note::
            This feels like it wants to eventually go away, but is used to
            preserve as-strict-as-possible compatibility with OpenSSH, which
            for whatever reason only applies some tokens to some config keys.
        )TOKENS_BY_CONFIG_KEYrR   r   rG   s     r   r   zSSHConfig._allowed_tokens  s     ((,,S"55r   c                     |D ]f  }||   	t        | j                  |||      }t        ||   t              r%t	        ||         D ]  \  }} ||      ||   |<    Y |||         ||<   h |S )aA  
        Return a dict of config options with expanded substitutions
        for a given original & current target hostname.

        Please refer to :doc:`/api/config` for details.

        :param dict config: the currently parsed config
        :param str hostname: the hostname whose config is being looked up
        )r   r   
isinstancelist	enumerate)r   r/   r   k	tokenizerirH   s          r   rh   zSSHConfig._expand_variables  s      	1Aay KI&)T* )&) 4 4HAu#,U#3F1IaL4 &fQi0q		1 r   c                 |    	 t        j                  |      S # t        $ r t        dj	                  |            w xY w)z>
        Return a list of host_names from host value.
        zUnparsable host {})shlexrU   
ValueErrorr   r=   )r   r.   s     r   rA   zSSHConfig._get_hosts  s?    	F;;t$$ 	F"#7#>#>t#DEE	Fs    $;c                   
 g }t        j                  |      }|rdddd}|j                  d      }|j                  d      r
d|d<   |dd }||d	<   |d
v r|j	                  |       O|st        dj                  |            |j                  d      |d<   |j	                  |       |r|D cg c]  }|d	   	 }}d|v rxd

t        t        
fd|            t        t        
fd|            }}d}	t        |      rd}	n)d|v r%|j                  d      |j                  d      kD  rd}	|	t        |	      |S c c}w )z
        Parse a specific Match config line into a list-of-dicts for its values.

        Performs some parse-time validation as well.
        NF)r   r   r   r   r{   Tr   r   r   )r   rP   z'Missing parameter to Match '{}' keywordr   r   c                     | v S r^   r_   ra   	allowables    r   <lambda>z(SSHConfig._get_matches.<locals>.<lambda>  s    a9n r   c                     | vS r^   r_   r   s    r   r   z(SSHConfig._get_matches.<locals>.<lambda>  s    ay&8 r   z>Match does not allow 'all' mixed with anything but 'canonical'rP   z-Match does not allow 'all' before 'canonical')r   rU   r   r:   r@   r   r=   r   filteranyindex)r   r2   r3   tokensr   ra   keywordsr   baderrr   s             @r   rB   zSSHConfig._get_matches  sX    U#!DEBEJJqME$"&hab	!E&M,,u%&=DDUK  $ZZ]E'NNN5!# ( (//!AfI//H,IV4h?@V8(CD B C3xV"rxx'<rxx'NE&s++ 0s   0D<)NF)__name__
__module____qualname____doc__r;   compiler<   r   r   classmethodr#   r(   r    r*   r[   rQ   rV   rx   rd   r   re   r   r   r   rh   rA   rB   r_   r   r   r
   r
   .   s      RZZ ;<N
 BF;/ G* - - & &  ;%z8tB'-R$62hE;z	6*F*r   r
   c                 T   |j                  dd      j                         }|dk(  ry	 t        j                  }|dk(  rt        j                  }t        j
                  | d|t        j                  t        j                  t        j                        S # t        j                  $ r Y yw xY w)a  
    Try looking up ``hostname`` in an IPv4 or IPv6 specific manner.

    This is an odd duck due to needing use in two divergent use cases. It looks
    up ``AddressFamily`` in ``options`` and if it is ``inet`` or ``inet6``,
    this function uses `socket.getaddrinfo` to perform a family-specific
    lookup, returning the result if successful.

    In any other situation -- lookup failure, or ``AddressFamily`` being
    unspecified or ``any`` -- ``None`` is returned instead and the caller is
    expected to do something situation-appropriate like calling
    `socket.gethostbyname`.

    :param str hostname: Hostname to look up.
    :param options: `SSHConfigDict` instance w/ parsed options.
    :returns: ``getaddrinfo``-style tuples, or ``None``, depending.
    addressfamilyr   Ninet)
rR   r?   rl   AF_INET6AF_INETgetaddrinfo
SOCK_DGRAM
IPPROTO_IPAI_CANONNAMErn   )r   rW   address_familyfamilys       r   rk   rk   $  s    $ [[%8>>@NV#^^F!!
 	
 ?? s   A(B B'&B'c                       e Zd ZdZddZd Zy)r   z7
    Returns the host's fqdn on request as string.
    Nc                 .    d | _         || _        || _        y r^   )fqdnr/   r.   )r   r/   r.   s      r   r   zLazyFqdn.__init__N  s    		r   c                     | j                   [d }t        | j                  | j                        }||D ]  }|\  }}}}}|sd|v s|} n |t	        j
                         }|| _         | j                   S )NrN   )r   rk   r.   r/   rl   getfqdn)	r   r   resultsresafsocktypeproto	canonnamesas	            r   __str__zLazyFqdn.__str__S  s    99 D0DKKHG"" C9<6B%B SI%5(	 |~~'DIyyr   r^   )r   r   r   r   r   r   r_   r   r   r   r   I  s    
r   r   c                   .     e Zd ZdZ fdZd Zd Z xZS )rc   a  
    A dictionary wrapper/subclass for per-host configuration structures.

    This class introduces some usage niceties for consumers of `SSHConfig`,
    specifically around the issue of variable type conversions: normal value
    access yields strings, but there are now methods such as `as_bool` and
    `as_int` that yield casted values instead.

    For example, given the following ``ssh_config`` file snippet::

        Host foo.example.com
            PasswordAuthentication no
            Compression yes
            ServerAliveInterval 60

    the following code highlights how you can access the raw strings as well as
    usefully Python type-casted versions (recalling that keys are all
    normalized to lowercase first)::

        my_config = SSHConfig()
        my_config.parse(open('~/.ssh/config'))
        conf = my_config.lookup('foo.example.com')

        assert conf['passwordauthentication'] == 'no'
        assert conf.as_bool('passwordauthentication') is False
        assert conf['compression'] == 'yes'
        assert conf.as_bool('compression') is True
        assert conf['serveraliveinterval'] == '60'
        assert conf.as_int('serveraliveinterval') == 60

    .. versionadded:: 2.5
    c                 ,    t        t        | 
  |i | y r^   )superrc   r   )r   argskwargs	__class__s      r   r   zSSHConfigDict.__init__  s    mT+T<V<r   c                 V    | |   }t        |t              r|S |j                         dk(  S )a  
        Express given key's value as a boolean type.

        Typically, this is used for ``ssh_config``'s pseudo-boolean values
        which are either ``"yes"`` or ``"no"``. In such cases, ``"yes"`` yields
        ``True`` and any other value becomes ``False``.

        .. note::
            If (for whatever reason) the stored value is already boolean in
            nature, it's simply returned.

        .. versionadded:: 2.5
        rK   )r   boolr?   )r   rG   vals      r   as_boolzSSHConfigDict.as_bool  s-     3ic4 Jyy{e##r   c                     t        | |         S )z
        Express given key's value as an integer, if possible.

        This method will raise ``ValueError`` or similar if the value is not
        int-appropriate, same as the builtin `int` type.

        .. versionadded:: 2.5
        )rS   r   s     r   as_intzSSHConfigDict.as_int  s     49~r   )r   r   r   r   r   r   r   __classcell__)r   s   @r   rc   rc   o  s    B=$&	r   rc   )r   r}   r   r   r;   r   rl   	functoolsr   	py3compatr   r   r   ImportErroressh_exceptionr   r   r   objectr
   rk   r   dictrc   r_   r   r   <module>r     s   (   	 	    (  B s sl"J#v #LBD BU  s   A   A1%A,,A1