a
   
i;                
   @   s   d Z ddlZddlZddlZddlZddlZddlZddlmZ ddl	m
Z
 d\ZZzddlZW n( ey Z zeZW Y dZ[n
dZ[0 0 ddlmZmZ dZG d	d
 d
eZdd ZG dd deZG dd deZdS )z2
Configuration file (aka ``ssh_config``) support.
    N)partial   )StringIO)NN)CouldNotCanonicalizeConfigParseError   c                   @   s   e Zd ZdZedZg ddgg dg dg ddZd	d
 Ze	dd Z
e	dd Ze	dd Zdd Zdd Zd/ddZdd Zdd Zdd Zdd  Zd!d" Zd#d$ Zd%d& Zd'd( Zd)d* Zd+d, Zd-d. ZdS )0	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   )Zcontrolpathhostnameidentityfileproxycommand
match-execc                 C   s
   g | _ dS )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)self r   3/usr/lib/python3.9/site-packages/paramiko/config.py__init__G   s    zSSHConfig.__init__c                 C   s   |  t|S )zg
        Create a new, parsed `SSHConfig` from ``text`` string.

        .. versionadded:: 2.7
        )	from_filer   )clstextr   r   r   	from_text\   s    zSSHConfig.from_textc                 C   s6   t |}| |W  d   S 1 s(0    Y  dS )zr
        Create a new, parsed `SSHConfig` from the file found at ``path``.

        .. versionadded:: 2.7
        N)openr   )r   pathflor   r   r   	from_pathe   s    
zSSHConfig.from_pathc                 C   s   |  }| | |S )zp
        Create a new, parsed `SSHConfig` from file-like object ``flo``.

        .. versionadded:: 2.7
        )parse)r   r!   objr   r   r   r   o   s    
zSSHConfig.from_filec                 C   sR  dgi d}|D ].}|  }|r|dr.qt| j|}|sNtd||d }|d}|dv r| j	
| di i}|d	kr| ||d	< n| ||d
< q|dkr| dkrd|d |< q|dr|dr|dd }|dv r(||d v r|d | 
| n|g|d |< q||d vr||d |< q| j	
| dS )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   ZnoneN")r   ZlocalforwardZremoteforward)strip
startswithrer*   SETTINGS_REGEXr   formatgrouplowerr   append
_get_hosts_get_matchesendswith)r   Zfile_objcontextliner*   keyvaluer   r   r   r#   z   s6    


zSSHConfig.parsec                 C   s   | j |d}d|vr||d< |dddv }t|dd}|r|d|kr|d	  }| |||}||d< | j ||d
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   ZcanonicalizehostnameN)yesalwaysZcanonicalizemaxdotsr   .ZcanonicaldomainsT)	canonical)_lookupgetintcountsplitcanonicalize)r   r   optionsZcanonZmaxdotsdomainsr   r   r   lookup   s    (zSSHConfig.lookupNFc                    s   d u rt  | jD ]}| |dg |sF| |dg ||sFq|d  D ]T\ } vr|d urv|d d  n| < qR dkrR   fdd|D  qRq| |dv rЈd d u rЈd= S )Nr&   r+   r'   r   c                 3   s   | ]}|  vr|V  qd S Nr   .0xr;   rG   r   r   	<genexpr>  s   z$SSHConfig._lookup.<locals>.<genexpr>r   )SSHConfigDictr   _pattern_matchesrB   _does_matchitemsextend_expand_variables)r   r   rG   r@   r9   r<   r   rN   r   rA      s*    
zSSHConfig._lookupc              	   C   s   d}|D ]\}d ||}t||}|dur4|d }n$zt|}W n tjyV   Y n0 |r|  S q|dddkrz|S t|dS )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{}.{}Nr   Zcanonicalizefallbacklocalr=   )r2   _addressfamily_host_lookupsocketZgethostbynamegaierrorrB   r   )r   r   rG   rH   founddomain	candidateZfamily_specificr   r   r   rF     s    


zSSHConfig.canonicalizec                 C   s$   t  }| jD ]}||d  q|S )z
        Return the set of literal hostnames defined in the SSH config (both
        explicit hostnames and wildcard entries).
        r&   )setr   update)r   hostsentryr   r   r   get_hostnames;  s    
zSSHConfig.get_hostnamesc                 C   sZ   t |dr|d}d}|D ]8}|drDt||dd  rD dS t||rd}q|S )NrE   ,F!r   T)hasattrrE   r/   fnmatch)r   patternstargetr*   patternr   r   r   rQ   E  s    

zSSHConfig._pattern_matchesc                 C   s   |  ||S rJ   )rQ   )r   r^   r   r   r   r   _allowedW  s    zSSHConfig._allowedc                 C   sJ  g }|d d  }t  }|rF|d}d }	|dd }
|dd }|d |d  }}|dkrp| ||rndS n|dkr|d	S |d
kr|
p|}| ||}	n|dkr| ||}	nn|dkr|p|}| ||}	nP|dkr| ||}	n:|dkr| ||d|}td u rttj	|dd	dj
}	|	d ur:| |	|r:dS || q|S )Nr   r   usertypeparamr@   FallTr&   ZoriginalhostZ	localuserexecr   stdout)hidewarn)getpassgetuserpoprB   _should_failrQ   	_tokenizeinvokeinvoke_import_errorrunokr5   )r   Z
match_listtarget_hostnamer@   rG   ZmatchedZ
candidatesZlocal_usernamer[   ZpassedZconfigured_hostZconfigured_usertype_rk   Zhostvalri   Zexec_cmdr   r   r   rR   Z  sF    


zSSHConfig._does_matchc                 C   s   |d r|S | S )Nnegater   )r   Z
would_passr[   r   r   r   rt     s    zSSHConfig._should_failc              
   C   s   |  |}|s|S |}|dkr*|d|}d|v r<|d }nt}t }d|v rZ|d }	n|}	t dd }
t||
}t	j
d}|||
||||	||d	}|}| D ]"\}}||vrq||t|}q|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   portri   r?   r   r   )	r   r	   r   r
   r   r   r   r   r   )_allowed_tokensrB   SSH_PORTrq   rr   rW   ZgethostnamerE   LazyFqdnosr    
expanduserrS   replacestr)r   r'   rz   r;   r<   Zallowed_tokensZconfigured_hostnamer}   ri   Z
remoteuserZlocal_hostnameZ
local_fqdnhomedirZreplacementsZ	tokenizedfindr   r   r   r   ru     s@    



zSSHConfig._tokenizec                 C   s   | 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_KEYrB   r   r;   r   r   r   r~     s    	zSSHConfig._allowed_tokensc                 C   sr   |D ]h}|| du rqt | j|||}t|| tr\t|| D ]\}}|||| |< q@q||| ||< q|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
        N)r   ru   
isinstancelist	enumerate)r   r'   rz   kZ	tokenizerir<   r   r   r   rU     s    
zSSHConfig._expand_variablesc                 C   s2   zt |W S  ty,   td|Y n0 dS )z>
        Return a list of host_names from host value.
        zUnparsable host {}N)shlexrE   
ValueErrorr   r2   )r   r&   r   r   r   r6     s    zSSHConfig._get_hostsc           	         s$  g }t |}|rdddd}|d}|drFd|d< |dd }||d	< |d
v rb|| q|sttd||d|d< || qdd |D }d|v r d
 tt fdd|tt fdd| }}d}t	|rd}n$d|v r|
d|
dkrd}|dur t||S )z
        Parse a specific Match config line into a list-of-dicts for its values.

        Performs some parse-time validation as well.
        NF)rj   rk   r|   r   rb   Tr|   r   rj   )rl   r@   z'Missing parameter to Match '{}' keywordrk   c                 S   s   g | ]}|d  qS )rj   r   rK   r   r   r   
<listcomp>      z*SSHConfig._get_matches.<locals>.<listcomp>rl   c                    s   |  v S rJ   r   rM   Z	allowabler   r   <lambda>  r   z(SSHConfig._get_matches.<locals>.<lambda>c                    s   |  vS rJ   r   r   r   r   r   r     r   z>Match does not allow 'all' mixed with anything but 'canonical'r@   z-Match does not allow 'all' before 'canonical')r   rE   rs   r/   r5   r   r2   r   filteranyindex)	r   r*   r+   tokensr{   keywordsry   Zbaderrr   r   r   r7     s@    




 
zSSHConfig._get_matches)NF)__name__
__module____qualname____doc__r0   compiler1   r   r   classmethodr   r"   r   r#   rI   rA   rF   r`   rQ   rh   rR   rt   ru   r~   rU   r6   r7   r   r   r   r   r   .   s:   




	

=:
!)
4=	r   c                 C   sf   | dd }|dkrdS z0tj}|dkr2tj}t| d|tjtjtjW S  tj	y`   Y n0 dS )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.
    Zaddressfamilyr   NZinet)
rB   r4   rW   ZAF_INET6ZAF_INETZgetaddrinfoZ
SOCK_DGRAMZ
IPPROTO_IPZAI_CANONNAMErX   )r   rG   Zaddress_familyZfamilyr   r   r   rV   $  s"    rV   c                   @   s"   e Zd ZdZdddZdd ZdS )r   z7
    Returns the host's fqdn on request as string.
    Nc                 C   s   d | _ || _|| _d S rJ   )fqdnr'   r&   )r   r'   r&   r   r   r   r   N  s    zLazyFqdn.__init__c           	      C   sl   | j d u rfd }t| j| j}|d urP|D ]&}|\}}}}}|r(d|v r(|} qPq(|d u r`t }|| _ | j S )Nr?   )r   rV   r&   r'   rW   Zgetfqdn)	r   r   resultsresafZsocktypeprotoZ	canonnameZsar   r   r   __str__S  s    
zLazyFqdn.__str__)N)r   r   r   r   r   r   r   r   r   r   r   I  s   
r   c                       s0   e Zd ZdZ fddZdd Zdd Z  ZS )rP   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                    s   t t| j|i | d S rJ   )superrP   r   )r   argskwargs	__class__r   r   r     s    zSSHConfigDict.__init__c                 C   s"   | | }t |tr|S | 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
        r=   )r   boolr4   )r   r;   valr   r   r   as_bool  s    
zSSHConfigDict.as_boolc                 C   s   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
        )rC   r   r   r   r   as_int  s    	zSSHConfigDict.as_int)r   r   r   r   r   r   r   __classcell__r   r   r   r   rP   o  s   !rP   )r   rd   rq   r   r0   r   rW   	functoolsr   Z	py3compatr   rv   rw   ImportErroreZssh_exceptionr   r   r   objectr   rV   r   dictrP   r   r   r   r   <module>   s,      y%&