asyncssh 2.18.0__tar.gz → 2.19.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {asyncssh-2.18.0/asyncssh.egg-info → asyncssh-2.19.0}/PKG-INFO +6 -7
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/config.py +86 -32
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/connection.py +255 -37
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/misc.py +1 -1
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/public_key.py +7 -4
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/sftp.py +199 -32
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/sk.py +90 -19
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/sk_ecdsa.py +45 -22
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/sk_eddsa.py +11 -12
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/version.py +1 -1
- {asyncssh-2.18.0 → asyncssh-2.19.0/asyncssh.egg-info}/PKG-INFO +6 -7
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh.egg-info/SOURCES.txt +1 -1
- {asyncssh-2.18.0 → asyncssh-2.19.0}/docs/api.rst +30 -6
- {asyncssh-2.18.0 → asyncssh-2.19.0}/docs/changes.rst +33 -0
- asyncssh-2.19.0/pyproject.toml +59 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/sk_stub.py +100 -18
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_config.py +41 -5
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_connection.py +123 -1
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_public_key.py +10 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_sftp.py +114 -12
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_sk.py +18 -2
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/util.py +25 -0
- asyncssh-2.18.0/setup.py +0 -92
- {asyncssh-2.18.0 → asyncssh-2.19.0}/.coveragerc +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/.github/workflows/run_tests.yml +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/.gitignore +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/.readthedocs.yaml +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/CONTRIBUTING.rst +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/COPYRIGHT +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/LICENSE +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/MANIFEST.in +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/README.rst +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/__init__.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/agent.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/agent_unix.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/agent_win32.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/asn1.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/auth.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/auth_keys.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/channel.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/client.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/compression.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/constants.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/crypto/__init__.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/crypto/chacha.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/crypto/cipher.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/crypto/dh.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/crypto/dsa.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/crypto/ec.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/crypto/ec_params.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/crypto/ed.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/crypto/kdf.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/crypto/misc.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/crypto/pq.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/crypto/rsa.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/crypto/umac.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/crypto/x509.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/dsa.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/ecdsa.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/eddsa.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/editor.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/encryption.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/forward.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/gss.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/gss_unix.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/gss_win32.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/kex.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/kex_dh.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/kex_rsa.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/keysign.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/known_hosts.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/listener.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/logging.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/mac.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/packet.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/pattern.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/pbe.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/pkcs11.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/process.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/py.typed +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/rsa.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/saslprep.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/scp.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/server.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/session.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/socks.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/stream.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/subprocess.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/tuntap.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh/x11.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh.egg-info/dependency_links.txt +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh.egg-info/requires.txt +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/asyncssh.egg-info/top_level.txt +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/docs/_templates/sidebarbottom.html +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/docs/_templates/sidebartop.html +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/docs/conf.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/docs/contributing.rst +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/docs/index.rst +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/docs/requirements.txt +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/docs/rftheme/layout.html +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/docs/rftheme/static/rftheme.css_t +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/docs/rftheme/theme.conf +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/docs/rtd-req.txt +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/callback_client.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/callback_client2.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/callback_client3.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/callback_math_server.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/chat_server.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/check_exit_status.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/chroot_sftp_server.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/direct_client.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/direct_server.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/editor.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/gather_results.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/listening_client.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/local_forwarding_client.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/local_forwarding_client2.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/local_forwarding_server.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/math_client.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/math_server.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/redirect_input.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/redirect_local_pipe.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/redirect_remote_pipe.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/redirect_server.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/remote_forwarding_client.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/remote_forwarding_client2.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/remote_forwarding_server.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/reverse_client.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/reverse_server.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/scp_client.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/set_environment.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/set_terminal.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/sftp_client.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/show_environment.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/show_terminal.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/simple_cert_server.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/simple_client.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/simple_keyed_server.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/simple_scp_server.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/simple_server.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/simple_sftp_server.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/stream_direct_client.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/stream_direct_server.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/examples/stream_listening_client.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/mypy.ini +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/pylintrc +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/setup.cfg +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/__init__.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/gss_stub.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/gssapi_stub.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/keysign_stub.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/pkcs11_stub.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/server.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/sspi_stub.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_agent.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_asn1.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_auth.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_auth_keys.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_channel.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_compression.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_connection_auth.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_editor.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_encryption.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_forward.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_kex.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_known_hosts.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_logging.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_mac.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_packet.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_pkcs11.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_process.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_saslprep.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_stream.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_subprocess.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_tuntap.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_x11.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tests/test_x509.py +0 -0
- {asyncssh-2.18.0 → asyncssh-2.19.0}/tox.ini +0 -0
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: asyncssh
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.19.0
|
|
4
4
|
Summary: AsyncSSH: Asynchronous SSHv2 client and server library
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
License: Eclipse Public License v2.0
|
|
5
|
+
Author-email: Ron Frederick <ronf@timeheart.net>
|
|
6
|
+
License: EPL-2.0 OR GPL-2.0-or-later
|
|
7
|
+
Project-URL: Homepage, http://asyncssh.timeheart.net
|
|
9
8
|
Project-URL: Documentation, https://asyncssh.readthedocs.io
|
|
10
9
|
Project-URL: Source, https://github.com/ronf/asyncssh
|
|
11
10
|
Project-URL: Tracker, https://github.com/ronf/asyncssh/issues
|
|
12
|
-
Platform: Any
|
|
13
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
14
12
|
Classifier: Environment :: Console
|
|
15
13
|
Classifier: Intended Audience :: Developers
|
|
@@ -26,7 +24,8 @@ Classifier: Topic :: Internet
|
|
|
26
24
|
Classifier: Topic :: Security :: Cryptography
|
|
27
25
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
28
26
|
Classifier: Topic :: System :: Networking
|
|
29
|
-
Requires-Python: >=
|
|
27
|
+
Requires-Python: >=3.6
|
|
28
|
+
Description-Content-Type: text/x-rst
|
|
30
29
|
License-File: LICENSE
|
|
31
30
|
Requires-Dist: cryptography>=39.0
|
|
32
31
|
Requires-Dist: typing_extensions>=4.0.0
|
|
@@ -60,12 +60,15 @@ class SSHConfig:
|
|
|
60
60
|
_percent_expand = {'AuthorizedKeysFile'}
|
|
61
61
|
_handlers: Dict[str, Tuple[str, Callable]] = {}
|
|
62
62
|
|
|
63
|
-
def __init__(self, last_config: Optional['SSHConfig'], reload: bool
|
|
63
|
+
def __init__(self, last_config: Optional['SSHConfig'], reload: bool,
|
|
64
|
+
canonical: bool, final: bool):
|
|
64
65
|
if last_config:
|
|
65
66
|
self._last_options = last_config.get_options(reload)
|
|
66
67
|
else:
|
|
67
68
|
self._last_options = {}
|
|
68
69
|
|
|
70
|
+
self._canonical = canonical
|
|
71
|
+
self._final = True if final else None
|
|
69
72
|
self._default_path = Path('~', '.ssh').expanduser()
|
|
70
73
|
self._path = Path()
|
|
71
74
|
self._line_no = 0
|
|
@@ -153,35 +156,53 @@ class SSHConfig:
|
|
|
153
156
|
|
|
154
157
|
# pylint: disable=unused-argument
|
|
155
158
|
|
|
159
|
+
matching = True
|
|
160
|
+
|
|
156
161
|
while args:
|
|
157
162
|
match = args.pop(0).lower()
|
|
158
163
|
|
|
164
|
+
if match[0] == '!':
|
|
165
|
+
match = match[1:]
|
|
166
|
+
negated = True
|
|
167
|
+
else:
|
|
168
|
+
negated = False
|
|
169
|
+
|
|
170
|
+
if match == 'final' and self._final is None:
|
|
171
|
+
self._final = False
|
|
172
|
+
|
|
159
173
|
if match == 'all':
|
|
160
|
-
|
|
161
|
-
|
|
174
|
+
result = True
|
|
175
|
+
elif match == 'canonical':
|
|
176
|
+
result = self._canonical
|
|
177
|
+
elif match == 'final':
|
|
178
|
+
result = cast(bool, self._final)
|
|
179
|
+
else:
|
|
180
|
+
match_val = self._match_val(match)
|
|
162
181
|
|
|
163
|
-
|
|
182
|
+
if match != 'exec' and match_val is None:
|
|
183
|
+
self._error(f'Invalid match condition {match}')
|
|
164
184
|
|
|
165
|
-
|
|
166
|
-
|
|
185
|
+
try:
|
|
186
|
+
arg = args.pop(0)
|
|
187
|
+
except IndexError:
|
|
188
|
+
self._error(f'Missing {match} match pattern')
|
|
189
|
+
|
|
190
|
+
if matching:
|
|
191
|
+
if match == 'exec':
|
|
192
|
+
result = _exec(arg)
|
|
193
|
+
elif match in ('address', 'localaddress'):
|
|
194
|
+
host_pat = HostPatternList(arg)
|
|
195
|
+
ip = ip_address(cast(str, match_val)) \
|
|
196
|
+
if match_val else None
|
|
197
|
+
result = host_pat.matches(None, match_val, ip)
|
|
198
|
+
else:
|
|
199
|
+
wild_pat = WildcardPatternList(arg)
|
|
200
|
+
result = wild_pat.matches(match_val)
|
|
167
201
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
self._matching = _exec(args.pop(0))
|
|
171
|
-
elif match in ('address', 'localaddress'):
|
|
172
|
-
host_pat = HostPatternList(args.pop(0))
|
|
173
|
-
ip = ip_address(cast(str, match_val)) \
|
|
174
|
-
if match_val else None
|
|
175
|
-
self._matching = host_pat.matches(None, match_val, ip)
|
|
176
|
-
else:
|
|
177
|
-
wild_pat = WildcardPatternList(args.pop(0))
|
|
178
|
-
self._matching = wild_pat.matches(match_val)
|
|
179
|
-
except IndexError:
|
|
180
|
-
self._error(f'Missing {match} match pattern')
|
|
202
|
+
if matching and result == negated:
|
|
203
|
+
matching = False
|
|
181
204
|
|
|
182
|
-
|
|
183
|
-
args.clear()
|
|
184
|
-
break
|
|
205
|
+
self._matching = matching
|
|
185
206
|
|
|
186
207
|
def _set_bool(self, option: str, args: List[str]) -> None:
|
|
187
208
|
"""Set a boolean config option"""
|
|
@@ -276,6 +297,23 @@ class SSHConfig:
|
|
|
276
297
|
if option not in self._options:
|
|
277
298
|
self._options[option] = value
|
|
278
299
|
|
|
300
|
+
def _set_canonicalize_host(self, option: str, args: List[str]) -> None:
|
|
301
|
+
"""Set a canonicalize host config option"""
|
|
302
|
+
|
|
303
|
+
value_str = args.pop(0).lower()
|
|
304
|
+
|
|
305
|
+
if value_str in ('yes', 'true'):
|
|
306
|
+
value: Union[bool, str] = True
|
|
307
|
+
elif value_str in ('no', 'false'):
|
|
308
|
+
value = False
|
|
309
|
+
elif value_str == 'always':
|
|
310
|
+
value = value_str
|
|
311
|
+
else:
|
|
312
|
+
self._error(f'Invalid {option} value: {value_str}')
|
|
313
|
+
|
|
314
|
+
if option not in self._options:
|
|
315
|
+
self._options[option] = value
|
|
316
|
+
|
|
279
317
|
def _set_rekey_limits(self, option: str, args: List[str]) -> None:
|
|
280
318
|
"""Set rekey limits config option"""
|
|
281
319
|
|
|
@@ -295,6 +333,11 @@ class SSHConfig:
|
|
|
295
333
|
if option not in self._options:
|
|
296
334
|
self._options[option] = byte_limit, time_limit
|
|
297
335
|
|
|
336
|
+
def has_match_final(self) -> bool:
|
|
337
|
+
"""Return whether this config includes a 'Match final' block"""
|
|
338
|
+
|
|
339
|
+
return self._final is not None
|
|
340
|
+
|
|
298
341
|
def parse(self, path: Path) -> None:
|
|
299
342
|
"""Parse an OpenSSH config file and return matching declarations"""
|
|
300
343
|
|
|
@@ -384,10 +427,10 @@ class SSHConfig:
|
|
|
384
427
|
@classmethod
|
|
385
428
|
def load(cls, last_config: Optional['SSHConfig'],
|
|
386
429
|
config_paths: ConfigPaths, reload: bool,
|
|
387
|
-
*args: object) -> 'SSHConfig':
|
|
430
|
+
canonical: bool, final: bool, *args: object) -> 'SSHConfig':
|
|
388
431
|
"""Load a list of OpenSSH config files into a config object"""
|
|
389
432
|
|
|
390
|
-
config = cls(last_config, reload, *args)
|
|
433
|
+
config = cls(last_config, reload, canonical, final, *args)
|
|
391
434
|
|
|
392
435
|
if config_paths:
|
|
393
436
|
if isinstance(config_paths, (str, PurePath)):
|
|
@@ -429,8 +472,9 @@ class SSHClientConfig(SSHConfig):
|
|
|
429
472
|
'IdentityFile', 'ProxyCommand', 'RemoteCommand'}
|
|
430
473
|
|
|
431
474
|
def __init__(self, last_config: 'SSHConfig', reload: bool,
|
|
432
|
-
|
|
433
|
-
|
|
475
|
+
canonical: bool, final: bool, local_user: str,
|
|
476
|
+
user: str, host: str, port: int) -> None:
|
|
477
|
+
super().__init__(last_config, reload, canonical, final)
|
|
434
478
|
|
|
435
479
|
self._local_user = local_user
|
|
436
480
|
self._orig_host = host
|
|
@@ -485,10 +529,10 @@ class SSHClientConfig(SSHConfig):
|
|
|
485
529
|
value: Union[bool, str] = True
|
|
486
530
|
elif value_str in ('no', 'false'):
|
|
487
531
|
value = False
|
|
488
|
-
elif value_str
|
|
489
|
-
self._error(f'Invalid {option} value: {value_str}')
|
|
490
|
-
else:
|
|
532
|
+
elif value_str in ('force', 'auto'):
|
|
491
533
|
value = value_str
|
|
534
|
+
else:
|
|
535
|
+
self._error(f'Invalid {option} value: {value_str}')
|
|
492
536
|
|
|
493
537
|
if option not in self._options:
|
|
494
538
|
self._options[option] = value
|
|
@@ -531,6 +575,11 @@ class SSHClientConfig(SSHConfig):
|
|
|
531
575
|
|
|
532
576
|
('AddressFamily', SSHConfig._set_address_family),
|
|
533
577
|
('BindAddress', SSHConfig._set_string),
|
|
578
|
+
('CanonicalDomains', SSHConfig._set_string_list),
|
|
579
|
+
('CanonicalizeFallbackLocal', SSHConfig._set_bool),
|
|
580
|
+
('CanonicalizeHostname', SSHConfig._set_canonicalize_host),
|
|
581
|
+
('CanonicalizeMaxDots', SSHConfig._set_int),
|
|
582
|
+
('CanonicalizePermittedCNAMEs', SSHConfig._set_string_list),
|
|
534
583
|
('CASignatureAlgorithms', SSHConfig._set_string),
|
|
535
584
|
('CertificateFile', SSHConfig._append_string),
|
|
536
585
|
('ChallengeResponseAuthentication', SSHConfig._set_bool),
|
|
@@ -579,9 +628,9 @@ class SSHServerConfig(SSHConfig):
|
|
|
579
628
|
"""Settings from an OpenSSH server config file"""
|
|
580
629
|
|
|
581
630
|
def __init__(self, last_config: 'SSHConfig', reload: bool,
|
|
582
|
-
|
|
583
|
-
host: str, addr: str) -> None:
|
|
584
|
-
super().__init__(last_config, reload)
|
|
631
|
+
canonical: bool, final: bool, local_addr: str,
|
|
632
|
+
local_port: int, user: str, host: str, addr: str) -> None:
|
|
633
|
+
super().__init__(last_config, reload, canonical, final)
|
|
585
634
|
|
|
586
635
|
self._local_addr = local_addr
|
|
587
636
|
self._local_port = local_port
|
|
@@ -618,6 +667,11 @@ class SSHServerConfig(SSHConfig):
|
|
|
618
667
|
('AuthorizedKeysFile', SSHConfig._set_string_list),
|
|
619
668
|
('AllowAgentForwarding', SSHConfig._set_bool),
|
|
620
669
|
('BindAddress', SSHConfig._set_string),
|
|
670
|
+
('CanonicalDomains', SSHConfig._set_string_list),
|
|
671
|
+
('CanonicalizeFallbackLocal', SSHConfig._set_bool),
|
|
672
|
+
('CanonicalizeHostname', SSHConfig._set_canonicalize_host),
|
|
673
|
+
('CanonicalizeMaxDots', SSHConfig._set_int),
|
|
674
|
+
('CanonicalizePermittedCNAMEs', SSHConfig._set_string_list),
|
|
621
675
|
('CASignatureAlgorithms', SSHConfig._set_string),
|
|
622
676
|
('ChallengeResponseAuthentication', SSHConfig._set_bool),
|
|
623
677
|
('Ciphers', SSHConfig._set_string),
|