asyncssh 2.16.0__tar.gz → 2.17.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.16.0/asyncssh.egg-info → asyncssh-2.17.0}/PKG-INFO +5 -12
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/channel.py +5 -1
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/client.py +9 -1
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/connection.py +28 -5
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/gss.py +7 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/gss_unix.py +9 -5
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/gss_win32.py +10 -2
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/listener.py +12 -2
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/misc.py +1 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/public_key.py +2 -5
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/server.py +8 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/version.py +1 -1
- {asyncssh-2.16.0 → asyncssh-2.17.0/asyncssh.egg-info}/PKG-INFO +5 -12
- {asyncssh-2.16.0 → asyncssh-2.17.0}/docs/changes.rst +26 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/callback_client.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/callback_client2.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/callback_client3.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/callback_math_server.py +6 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/chat_server.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/check_exit_status.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/chroot_sftp_server.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/direct_client.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/direct_server.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/editor.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/gather_results.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/listening_client.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/local_forwarding_client.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/local_forwarding_client2.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/local_forwarding_server.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/math_client.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/math_server.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/redirect_input.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/redirect_local_pipe.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/redirect_remote_pipe.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/redirect_server.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/remote_forwarding_client.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/remote_forwarding_client2.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/remote_forwarding_server.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/reverse_client.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/reverse_server.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/scp_client.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/set_environment.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/set_terminal.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/sftp_client.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/show_environment.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/show_terminal.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/simple_cert_server.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/simple_client.py +11 -5
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/simple_keyed_server.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/simple_scp_server.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/simple_server.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/simple_sftp_server.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/stream_direct_client.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/stream_direct_server.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/examples/stream_listening_client.py +3 -3
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/gssapi_stub.py +3 -1
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_auth.py +2 -2
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_connection_auth.py +13 -1
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_kex.py +2 -2
- {asyncssh-2.16.0 → asyncssh-2.17.0}/.coveragerc +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/.github/workflows/run_tests.yml +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/.gitignore +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/.readthedocs.yaml +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/CONTRIBUTING.rst +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/COPYRIGHT +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/LICENSE +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/MANIFEST.in +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/README.rst +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/__init__.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/agent.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/agent_unix.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/agent_win32.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/asn1.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/auth.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/auth_keys.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/compression.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/config.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/constants.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/crypto/__init__.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/crypto/chacha.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/crypto/cipher.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/crypto/dh.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/crypto/dsa.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/crypto/ec.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/crypto/ec_params.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/crypto/ed.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/crypto/kdf.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/crypto/misc.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/crypto/rsa.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/crypto/sntrup.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/crypto/umac.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/crypto/x509.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/dsa.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/ecdsa.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/eddsa.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/editor.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/encryption.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/forward.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/kex.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/kex_dh.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/kex_rsa.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/keysign.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/known_hosts.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/logging.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/mac.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/packet.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/pattern.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/pbe.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/pkcs11.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/process.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/py.typed +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/rsa.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/saslprep.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/scp.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/session.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/sftp.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/sk.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/sk_ecdsa.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/sk_eddsa.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/socks.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/stream.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/subprocess.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/tuntap.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh/x11.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh.egg-info/SOURCES.txt +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh.egg-info/dependency_links.txt +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh.egg-info/requires.txt +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/asyncssh.egg-info/top_level.txt +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/docs/_templates/sidebarbottom.html +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/docs/_templates/sidebartop.html +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/docs/api.rst +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/docs/conf.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/docs/contributing.rst +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/docs/index.rst +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/docs/requirements.txt +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/docs/rftheme/layout.html +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/docs/rftheme/static/rftheme.css_t +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/docs/rftheme/theme.conf +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/docs/rtd-req.txt +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/mypy.ini +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/pylintrc +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/setup.cfg +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/setup.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/__init__.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/gss_stub.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/keysign_stub.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/pkcs11_stub.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/server.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/sk_stub.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/sspi_stub.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_agent.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_asn1.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_auth_keys.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_channel.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_compression.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_config.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_connection.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_editor.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_encryption.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_forward.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_known_hosts.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_logging.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_mac.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_packet.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_pkcs11.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_process.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_public_key.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_saslprep.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_sftp.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_sk.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_stream.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_subprocess.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_tuntap.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_x11.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/test_x509.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tests/util.py +0 -0
- {asyncssh-2.16.0 → asyncssh-2.17.0}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: asyncssh
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.17.0
|
|
4
4
|
Summary: AsyncSSH: Asynchronous SSHv2 client and server library
|
|
5
5
|
Home-page: http://asyncssh.timeheart.net
|
|
6
6
|
Author: Ron Frederick
|
|
@@ -27,23 +27,14 @@ Classifier: Topic :: Security :: Cryptography
|
|
|
27
27
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
28
28
|
Classifier: Topic :: System :: Networking
|
|
29
29
|
Requires-Python: >= 3.6
|
|
30
|
-
License-File: LICENSE
|
|
31
|
-
Requires-Dist: cryptography>=39.0
|
|
32
|
-
Requires-Dist: typing_extensions>=4.0.0
|
|
33
30
|
Provides-Extra: bcrypt
|
|
34
|
-
Requires-Dist: bcrypt>=3.1.3; extra == "bcrypt"
|
|
35
31
|
Provides-Extra: fido2
|
|
36
|
-
Requires-Dist: fido2>=0.9.2; extra == "fido2"
|
|
37
32
|
Provides-Extra: gssapi
|
|
38
|
-
Requires-Dist: gssapi>=1.2.0; extra == "gssapi"
|
|
39
33
|
Provides-Extra: libnacl
|
|
40
|
-
Requires-Dist: libnacl>=1.4.2; extra == "libnacl"
|
|
41
34
|
Provides-Extra: pkcs11
|
|
42
|
-
|
|
43
|
-
Provides-Extra: pyopenssl
|
|
44
|
-
Requires-Dist: pyOpenSSL>=23.0.0; extra == "pyopenssl"
|
|
35
|
+
Provides-Extra: pyOpenSSL
|
|
45
36
|
Provides-Extra: pywin32
|
|
46
|
-
|
|
37
|
+
License-File: LICENSE
|
|
47
38
|
|
|
48
39
|
.. image:: https://readthedocs.org/projects/asyncssh/badge/?version=latest
|
|
49
40
|
:target: https://asyncssh.readthedocs.io/en/latest/?badge=latest
|
|
@@ -276,3 +267,5 @@ Three mailing lists are available for AsyncSSH:
|
|
|
276
267
|
__ http://groups.google.com/d/forum/asyncssh-announce
|
|
277
268
|
__ http://groups.google.com/d/forum/asyncssh-dev
|
|
278
269
|
__ http://groups.google.com/d/forum/asyncssh-users
|
|
270
|
+
|
|
271
|
+
|
|
@@ -413,7 +413,11 @@ class SSHChannel(Generic[AnyStr], SSHPacketHandler):
|
|
|
413
413
|
handler = cast(_RequestHandler, getattr(self, name, None))
|
|
414
414
|
|
|
415
415
|
if handler:
|
|
416
|
-
|
|
416
|
+
if self._session:
|
|
417
|
+
result = cast(Optional[bool], handler(packet))
|
|
418
|
+
else:
|
|
419
|
+
# Ignore requests received after application closes the channel
|
|
420
|
+
result = True
|
|
417
421
|
else:
|
|
418
422
|
self.logger.debug1('Received unknown channel request: %s', request)
|
|
419
423
|
result = False
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c) 2013-
|
|
1
|
+
# Copyright (c) 2013-2024 by Ron Frederick <ronf@timeheart.net> and others.
|
|
2
2
|
#
|
|
3
3
|
# This program and the accompanying materials are made available under
|
|
4
4
|
# the terms of the Eclipse Public License v2.0 which accompanies this
|
|
@@ -39,6 +39,14 @@ class SSHClient:
|
|
|
39
39
|
to receive callbacks when certain events occur on the SSH
|
|
40
40
|
connection.
|
|
41
41
|
|
|
42
|
+
Whenever a new SSH client connection is opened, a corresponding
|
|
43
|
+
SSHClient object is created and the method :meth:`connection_made`
|
|
44
|
+
is called, passing in the :class:`SSHClientConnection` object.
|
|
45
|
+
|
|
46
|
+
When the connection is closed, the method :meth:`connection_lost`
|
|
47
|
+
is called with an exception representing the reason for the
|
|
48
|
+
disconnect, or `None` if the connection was closed cleanly.
|
|
49
|
+
|
|
42
50
|
For simple password or public key based authentication, nothing
|
|
43
51
|
needs to be defined here if the password or client keys are passed
|
|
44
52
|
in when the connection is created. However, to prompt interactively
|
|
@@ -105,8 +105,8 @@ from .logging import SSHLogger, logger
|
|
|
105
105
|
|
|
106
106
|
from .mac import get_mac_algs, get_default_mac_algs
|
|
107
107
|
|
|
108
|
-
from .misc import BytesOrStr, DefTuple, FilePath, HostPort
|
|
109
|
-
from .misc import MaybeAwait, OptExcInfo, Options, SockAddr
|
|
108
|
+
from .misc import BytesOrStr, BytesOrStrDict, DefTuple, FilePath, HostPort
|
|
109
|
+
from .misc import IPNetwork, MaybeAwait, OptExcInfo, Options, SockAddr
|
|
110
110
|
from .misc import ChannelListenError, ChannelOpenError, CompressionError
|
|
111
111
|
from .misc import DisconnectError, ConnectionLost, HostKeyNotVerifiable
|
|
112
112
|
from .misc import KeyExchangeFailed, IllegalUserName, MACError
|
|
@@ -1083,7 +1083,7 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
|
|
|
1083
1083
|
|
|
1084
1084
|
self._loop.call_soon(self._cleanup, exc)
|
|
1085
1085
|
|
|
1086
|
-
def _reap_task(self, task_logger: SSHLogger,
|
|
1086
|
+
def _reap_task(self, task_logger: Optional[SSHLogger],
|
|
1087
1087
|
task: 'asyncio.Task[None]') -> None:
|
|
1088
1088
|
"""Collect result of an async task, reporting errors"""
|
|
1089
1089
|
|
|
@@ -3315,7 +3315,8 @@ class SSHClientConnection(SSHConnection):
|
|
|
3315
3315
|
|
|
3316
3316
|
if gss_host:
|
|
3317
3317
|
try:
|
|
3318
|
-
self._gss = GSSClient(gss_host, options.
|
|
3318
|
+
self._gss = GSSClient(gss_host, options.gss_store,
|
|
3319
|
+
options.gss_delegate_creds)
|
|
3319
3320
|
self._gss_kex = options.gss_kex
|
|
3320
3321
|
self._gss_auth = options.gss_auth
|
|
3321
3322
|
self._gss_mic_auth = self._gss_auth
|
|
@@ -5713,7 +5714,7 @@ class SSHServerConnection(SSHConnection):
|
|
|
5713
5714
|
|
|
5714
5715
|
if options.gss_host:
|
|
5715
5716
|
try:
|
|
5716
|
-
self._gss = GSSServer(options.gss_host)
|
|
5717
|
+
self._gss = GSSServer(options.gss_host, options.gss_store)
|
|
5717
5718
|
self._gss_kex = options.gss_kex
|
|
5718
5719
|
self._gss_auth = options.gss_auth
|
|
5719
5720
|
self._gss_mic_auth = self._gss_auth
|
|
@@ -7443,6 +7444,8 @@ class SSHClientConnectionOptions(SSHConnectionOptions):
|
|
|
7443
7444
|
authentication. If not specified, this value will be the same
|
|
7444
7445
|
as the `host` argument. If this argument is explicitly set to
|
|
7445
7446
|
`None`, GSS key exchange and authentication will not be performed.
|
|
7447
|
+
:param gss_store: (optional)
|
|
7448
|
+
The GSS credential store from which to acquire credentials.
|
|
7446
7449
|
:param gss_kex: (optional)
|
|
7447
7450
|
Whether or not to allow GSS key exchange. By default, GSS
|
|
7448
7451
|
key exchange is enabled.
|
|
@@ -7672,6 +7675,8 @@ class SSHClientConnectionOptions(SSHConnectionOptions):
|
|
|
7672
7675
|
:type kbdint_auth: `bool`
|
|
7673
7676
|
:type password_auth: `bool`
|
|
7674
7677
|
:type gss_host: `str`
|
|
7678
|
+
:type gss_store:
|
|
7679
|
+
`str`, `bytes`, or a `dict` with `str` or `bytes` keys and values
|
|
7675
7680
|
:type gss_kex: `bool`
|
|
7676
7681
|
:type gss_auth: `bool`
|
|
7677
7682
|
:type gss_delegate_creds: `bool`
|
|
@@ -7734,6 +7739,7 @@ class SSHClientConnectionOptions(SSHConnectionOptions):
|
|
|
7734
7739
|
client_certs: Sequence[FilePath]
|
|
7735
7740
|
ignore_encrypted: bool
|
|
7736
7741
|
gss_host: DefTuple[Optional[str]]
|
|
7742
|
+
gss_store: Optional[Dict[BytesOrStr, BytesOrStr]]
|
|
7737
7743
|
gss_kex: bool
|
|
7738
7744
|
gss_auth: bool
|
|
7739
7745
|
gss_delegate_creds: bool
|
|
@@ -7802,6 +7808,7 @@ class SSHClientConnectionOptions(SSHConnectionOptions):
|
|
|
7802
7808
|
passphrase: Optional[BytesOrStr] = None,
|
|
7803
7809
|
ignore_encrypted: DefTuple[bool] = (),
|
|
7804
7810
|
gss_host: DefTuple[Optional[str]] = (),
|
|
7811
|
+
gss_store: Optional[Union[BytesOrStr, BytesOrStrDict]] = None,
|
|
7805
7812
|
gss_kex: DefTuple[bool] = (), gss_auth: DefTuple[bool] = (),
|
|
7806
7813
|
gss_delegate_creds: DefTuple[bool] = (),
|
|
7807
7814
|
preferred_auth: DefTuple[Union[str, Sequence[str]]] = (),
|
|
@@ -7933,6 +7940,11 @@ class SSHClientConnectionOptions(SSHConnectionOptions):
|
|
|
7933
7940
|
|
|
7934
7941
|
self.gss_host = gss_host
|
|
7935
7942
|
|
|
7943
|
+
if isinstance(gss_store, (bytes, str)):
|
|
7944
|
+
self.gss_store = {'ccache': gss_store}
|
|
7945
|
+
else:
|
|
7946
|
+
self.gss_store = gss_store
|
|
7947
|
+
|
|
7936
7948
|
self.gss_kex = cast(bool, gss_kex if gss_kex != () else
|
|
7937
7949
|
config.get('GSSAPIKeyExchange', True))
|
|
7938
7950
|
|
|
@@ -8169,6 +8181,8 @@ class SSHServerConnectionOptions(SSHConnectionOptions):
|
|
|
8169
8181
|
name. Otherwise, the value used by :func:`socket.getfqdn` will be
|
|
8170
8182
|
used. If this argument is explicitly set to `None`, GSS
|
|
8171
8183
|
key exchange and authentication will not be performed.
|
|
8184
|
+
:param gss_store: (optional)
|
|
8185
|
+
The GSS credential store from which to acquire credentials.
|
|
8172
8186
|
:param gss_kex: (optional)
|
|
8173
8187
|
Whether or not to allow GSS key exchange. By default, GSS
|
|
8174
8188
|
key exchange is enabled.
|
|
@@ -8344,6 +8358,8 @@ class SSHServerConnectionOptions(SSHConnectionOptions):
|
|
|
8344
8358
|
:type kbdint_auth: `bool`
|
|
8345
8359
|
:type password_auth: `bool`
|
|
8346
8360
|
:type gss_host: `str`
|
|
8361
|
+
:type gss_store:
|
|
8362
|
+
`str`, `bytes`, or a `dict` with `str` or `bytes` keys and values
|
|
8347
8363
|
:type gss_kex: `bool`
|
|
8348
8364
|
:type gss_auth: `bool`
|
|
8349
8365
|
:type allow_pty: `bool`
|
|
@@ -8391,6 +8407,7 @@ class SSHServerConnectionOptions(SSHConnectionOptions):
|
|
|
8391
8407
|
trust_client_host: bool
|
|
8392
8408
|
authorized_client_keys: DefTuple[Optional[SSHAuthorizedKeys]]
|
|
8393
8409
|
gss_host: Optional[str]
|
|
8410
|
+
gss_store: Optional[Dict[BytesOrStr, BytesOrStr]]
|
|
8394
8411
|
gss_kex: bool
|
|
8395
8412
|
gss_auth: bool
|
|
8396
8413
|
allow_pty: bool
|
|
@@ -8449,6 +8466,7 @@ class SSHServerConnectionOptions(SSHConnectionOptions):
|
|
|
8449
8466
|
trust_client_host: bool = False,
|
|
8450
8467
|
authorized_client_keys: _AuthKeysArg = (),
|
|
8451
8468
|
gss_host: DefTuple[Optional[str]] = (),
|
|
8469
|
+
gss_store: Optional[Union[BytesOrStr, BytesOrStrDict]] = None,
|
|
8452
8470
|
gss_kex: DefTuple[bool] = (),
|
|
8453
8471
|
gss_auth: DefTuple[bool] = (),
|
|
8454
8472
|
allow_pty: DefTuple[bool] = (),
|
|
@@ -8554,6 +8572,11 @@ class SSHServerConnectionOptions(SSHConnectionOptions):
|
|
|
8554
8572
|
|
|
8555
8573
|
self.gss_host = gss_host
|
|
8556
8574
|
|
|
8575
|
+
if isinstance(gss_store, (bytes, str)):
|
|
8576
|
+
self.gss_store = {'ccache': gss_store}
|
|
8577
|
+
else:
|
|
8578
|
+
self.gss_store = gss_store
|
|
8579
|
+
|
|
8557
8580
|
self.gss_kex = cast(bool, gss_kex if gss_kex != () else
|
|
8558
8581
|
config.get('GSSAPIKeyExchange', True))
|
|
8559
8582
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c) 2017-
|
|
1
|
+
# Copyright (c) 2017-2024 by Ron Frederick <ronf@timeheart.net> and others.
|
|
2
2
|
#
|
|
3
3
|
# This program and the accompanying materials are made available under
|
|
4
4
|
# the terms of the Eclipse Public License v2.0 which accompanies this
|
|
@@ -24,6 +24,9 @@ import sys
|
|
|
24
24
|
|
|
25
25
|
from typing import Optional
|
|
26
26
|
|
|
27
|
+
from .misc import BytesOrStrDict
|
|
28
|
+
|
|
29
|
+
|
|
27
30
|
try:
|
|
28
31
|
# pylint: disable=unused-import
|
|
29
32
|
|
|
@@ -53,11 +56,12 @@ except ImportError: # pragma: no cover
|
|
|
53
56
|
class GSSClient(GSSBase): # type: ignore
|
|
54
57
|
"""Stub client class for reporting that GSS is not available"""
|
|
55
58
|
|
|
56
|
-
def __init__(self, _host: str,
|
|
59
|
+
def __init__(self, _host: str, _store: Optional[BytesOrStrDict],
|
|
60
|
+
_delegate_creds: bool):
|
|
57
61
|
raise GSSError(0, 0)
|
|
58
62
|
|
|
59
63
|
class GSSServer(GSSBase): # type: ignore
|
|
60
64
|
"""Stub client class for reporting that GSS is not available"""
|
|
61
65
|
|
|
62
|
-
def __init__(self, _host: str):
|
|
66
|
+
def __init__(self, _host: str, _store: Optional[BytesOrStrDict]):
|
|
63
67
|
raise GSSError(0, 0)
|
|
@@ -27,6 +27,7 @@ from gssapi import RequirementFlag, SecurityContext
|
|
|
27
27
|
from gssapi.exceptions import GSSError
|
|
28
28
|
|
|
29
29
|
from .asn1 import OBJECT_IDENTIFIER
|
|
30
|
+
from .misc import BytesOrStrDict
|
|
30
31
|
|
|
31
32
|
|
|
32
33
|
def _mech_to_oid(mech: OID) -> bytes:
|
|
@@ -39,12 +40,14 @@ def _mech_to_oid(mech: OID) -> bytes:
|
|
|
39
40
|
class GSSBase:
|
|
40
41
|
"""GSS base class"""
|
|
41
42
|
|
|
42
|
-
def __init__(self, host: str):
|
|
43
|
+
def __init__(self, host: str, store: Optional[BytesOrStrDict]):
|
|
43
44
|
if '@' in host:
|
|
44
45
|
self._host = Name(host)
|
|
45
46
|
else:
|
|
46
47
|
self._host = Name('host@' + host, NameType.hostbased_service)
|
|
47
48
|
|
|
49
|
+
self._store = store
|
|
50
|
+
|
|
48
51
|
self._mechs = [_mech_to_oid(mech) for mech in self._creds.mechs]
|
|
49
52
|
self._ctx: Optional[SecurityContext] = None
|
|
50
53
|
|
|
@@ -141,8 +144,9 @@ class GSSBase:
|
|
|
141
144
|
class GSSClient(GSSBase):
|
|
142
145
|
"""GSS client"""
|
|
143
146
|
|
|
144
|
-
def __init__(self, host: str,
|
|
145
|
-
|
|
147
|
+
def __init__(self, host: str, store: Optional[BytesOrStrDict],
|
|
148
|
+
delegate_creds: bool):
|
|
149
|
+
super().__init__(host, store)
|
|
146
150
|
|
|
147
151
|
flags = RequirementFlag.mutual_authentication | \
|
|
148
152
|
RequirementFlag.integrity
|
|
@@ -156,7 +160,7 @@ class GSSClient(GSSBase):
|
|
|
156
160
|
def _creds(self) -> Credentials:
|
|
157
161
|
"""Abstract method to construct GSS credentials"""
|
|
158
162
|
|
|
159
|
-
return Credentials(usage='initiate')
|
|
163
|
+
return Credentials(usage='initiate', store=self._store)
|
|
160
164
|
|
|
161
165
|
def _init_context(self) -> None:
|
|
162
166
|
"""Construct GSS client security context"""
|
|
@@ -172,7 +176,7 @@ class GSSServer(GSSBase):
|
|
|
172
176
|
def _creds(self) -> Credentials:
|
|
173
177
|
"""Abstract method to construct GSS credentials"""
|
|
174
178
|
|
|
175
|
-
return Credentials(name=self._host, usage='accept')
|
|
179
|
+
return Credentials(name=self._host, usage='accept', store=self._store)
|
|
176
180
|
|
|
177
181
|
def _init_context(self) -> None:
|
|
178
182
|
"""Construct GSS server security context"""
|
|
@@ -35,6 +35,7 @@ from sspicon import ASC_RET_INTEGRITY, ASC_RET_MUTUAL_AUTH
|
|
|
35
35
|
from sspicon import SECPKG_ATTR_NATIVE_NAMES
|
|
36
36
|
|
|
37
37
|
from .asn1 import ObjectIdentifier, der_encode
|
|
38
|
+
from .misc import BytesOrStrDict
|
|
38
39
|
|
|
39
40
|
|
|
40
41
|
_krb5_oid = der_encode(ObjectIdentifier('1.2.840.113554.1.2.2'))
|
|
@@ -156,7 +157,11 @@ class GSSClient(GSSBase):
|
|
|
156
157
|
_mutual_auth_flag = ISC_RET_MUTUAL_AUTH
|
|
157
158
|
_integrity_flag = ISC_RET_INTEGRITY
|
|
158
159
|
|
|
159
|
-
def __init__(self, host: str,
|
|
160
|
+
def __init__(self, host: str, store: Optional[BytesOrStrDict],
|
|
161
|
+
delegate_creds: bool):
|
|
162
|
+
if store is not None:
|
|
163
|
+
raise GSSError(details='GSS store not supported on Windows')
|
|
164
|
+
|
|
160
165
|
super().__init__(host)
|
|
161
166
|
|
|
162
167
|
flags = ISC_REQ_MUTUAL_AUTH | ISC_REQ_INTEGRITY
|
|
@@ -179,7 +184,10 @@ class GSSServer(GSSBase):
|
|
|
179
184
|
_mutual_auth_flag = ASC_RET_MUTUAL_AUTH
|
|
180
185
|
_integrity_flag = ASC_RET_INTEGRITY
|
|
181
186
|
|
|
182
|
-
def __init__(self, host: str):
|
|
187
|
+
def __init__(self, host: str, store: Optional[BytesOrStrDict]):
|
|
188
|
+
if store is not None:
|
|
189
|
+
raise GSSError(details='GSS store not supported on Windows')
|
|
190
|
+
|
|
183
191
|
super().__init__(host)
|
|
184
192
|
|
|
185
193
|
flags = ASC_REQ_MUTUAL_AUTH | ASC_REQ_INTEGRITY
|
|
@@ -25,7 +25,7 @@ import errno
|
|
|
25
25
|
import socket
|
|
26
26
|
from types import TracebackType
|
|
27
27
|
from typing import TYPE_CHECKING, AnyStr, Callable, Generic, List, Optional
|
|
28
|
-
from typing import Sequence, Tuple, Type, Union
|
|
28
|
+
from typing import Sequence, Set, Tuple, Type, Union
|
|
29
29
|
from typing_extensions import Self
|
|
30
30
|
|
|
31
31
|
from .forward import SSHForwarderCoro
|
|
@@ -285,9 +285,19 @@ async def create_tcp_local_listener(
|
|
|
285
285
|
if not addrinfo: # pragma: no cover
|
|
286
286
|
raise OSError('getaddrinfo() returned empty list')
|
|
287
287
|
|
|
288
|
+
seen_addrinfo: Set[Tuple] = set()
|
|
288
289
|
servers: List[asyncio.AbstractServer] = []
|
|
289
290
|
|
|
290
|
-
for
|
|
291
|
+
for addrinfo_entry in addrinfo:
|
|
292
|
+
# Work around an issue where getaddrinfo() on some systems may
|
|
293
|
+
# return duplicate results, causing bind to fail.
|
|
294
|
+
if addrinfo_entry in seen_addrinfo: # pragma: no cover
|
|
295
|
+
continue
|
|
296
|
+
|
|
297
|
+
seen_addrinfo.add(addrinfo_entry)
|
|
298
|
+
|
|
299
|
+
family, socktype, proto, _, sa = addrinfo_entry
|
|
300
|
+
|
|
291
301
|
try:
|
|
292
302
|
sock = socket.socket(family, socktype, proto)
|
|
293
303
|
except OSError: # pragma: no cover
|
|
@@ -101,6 +101,7 @@ ExcInfo = Tuple[Type[BaseException], BaseException, TracebackType]
|
|
|
101
101
|
OptExcInfo = Union[ExcInfo, Tuple[None, None, None]]
|
|
102
102
|
|
|
103
103
|
BytesOrStr = Union[bytes, str]
|
|
104
|
+
BytesOrStrDict = Dict[BytesOrStr, BytesOrStr]
|
|
104
105
|
FilePath = Union[str, PurePath]
|
|
105
106
|
HostPort = Tuple[str, int]
|
|
106
107
|
IPAddress = Union[ipaddress.IPv4Address, ipaddress.IPv6Address]
|
|
@@ -2541,7 +2541,7 @@ def _decode_openssh_private(
|
|
|
2541
2541
|
'encrypted private keys')
|
|
2542
2542
|
|
|
2543
2543
|
try:
|
|
2544
|
-
key_size, iv_size,
|
|
2544
|
+
key_size, iv_size, _, _, _, _ = \
|
|
2545
2545
|
get_encryption_params(cipher_name)
|
|
2546
2546
|
except KeyError:
|
|
2547
2547
|
raise KeyEncryptionError('Unknown cipher: %s' %
|
|
@@ -2579,9 +2579,6 @@ def _decode_openssh_private(
|
|
|
2579
2579
|
raise KeyEncryptionError('Incorrect passphrase')
|
|
2580
2580
|
|
|
2581
2581
|
key_data = decrypted_key
|
|
2582
|
-
block_size = max(block_size, 8)
|
|
2583
|
-
else:
|
|
2584
|
-
block_size = 8
|
|
2585
2582
|
|
|
2586
2583
|
packet = SSHPacket(key_data)
|
|
2587
2584
|
|
|
@@ -2602,7 +2599,7 @@ def _decode_openssh_private(
|
|
|
2602
2599
|
comment = packet.get_string()
|
|
2603
2600
|
pad = packet.get_remaining_payload()
|
|
2604
2601
|
|
|
2605
|
-
if len(pad) >=
|
|
2602
|
+
if len(pad) >= 256 or pad != bytes(range(1, len(pad) + 1)):
|
|
2606
2603
|
raise KeyImportError('Invalid OpenSSH private key')
|
|
2607
2604
|
|
|
2608
2605
|
if alg == b'ssh-rsa':
|
|
@@ -59,6 +59,14 @@ class SSHServer:
|
|
|
59
59
|
Applications may subclass this when implementing an SSH server to
|
|
60
60
|
provide custom authentication and request handlers.
|
|
61
61
|
|
|
62
|
+
Whenever a new SSH server connection is accepted, a corresponding
|
|
63
|
+
SSHServer object is created and the method :meth:`connection_made`
|
|
64
|
+
is called, passing in the :class:`SSHServerConnection` object.
|
|
65
|
+
|
|
66
|
+
When the connection is closed, the method :meth:`connection_lost`
|
|
67
|
+
is called with an exception representing the reason for the
|
|
68
|
+
disconnect, or `None` if the connection was closed cleanly.
|
|
69
|
+
|
|
62
70
|
The method :meth:`begin_auth` can be overridden decide whether
|
|
63
71
|
or not authentication is required, and additional callbacks are
|
|
64
72
|
provided for each form of authentication in cases where authentication
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: asyncssh
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.17.0
|
|
4
4
|
Summary: AsyncSSH: Asynchronous SSHv2 client and server library
|
|
5
5
|
Home-page: http://asyncssh.timeheart.net
|
|
6
6
|
Author: Ron Frederick
|
|
@@ -27,23 +27,14 @@ Classifier: Topic :: Security :: Cryptography
|
|
|
27
27
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
28
28
|
Classifier: Topic :: System :: Networking
|
|
29
29
|
Requires-Python: >= 3.6
|
|
30
|
-
License-File: LICENSE
|
|
31
|
-
Requires-Dist: cryptography>=39.0
|
|
32
|
-
Requires-Dist: typing_extensions>=4.0.0
|
|
33
30
|
Provides-Extra: bcrypt
|
|
34
|
-
Requires-Dist: bcrypt>=3.1.3; extra == "bcrypt"
|
|
35
31
|
Provides-Extra: fido2
|
|
36
|
-
Requires-Dist: fido2>=0.9.2; extra == "fido2"
|
|
37
32
|
Provides-Extra: gssapi
|
|
38
|
-
Requires-Dist: gssapi>=1.2.0; extra == "gssapi"
|
|
39
33
|
Provides-Extra: libnacl
|
|
40
|
-
Requires-Dist: libnacl>=1.4.2; extra == "libnacl"
|
|
41
34
|
Provides-Extra: pkcs11
|
|
42
|
-
|
|
43
|
-
Provides-Extra: pyopenssl
|
|
44
|
-
Requires-Dist: pyOpenSSL>=23.0.0; extra == "pyopenssl"
|
|
35
|
+
Provides-Extra: pyOpenSSL
|
|
45
36
|
Provides-Extra: pywin32
|
|
46
|
-
|
|
37
|
+
License-File: LICENSE
|
|
47
38
|
|
|
48
39
|
.. image:: https://readthedocs.org/projects/asyncssh/badge/?version=latest
|
|
49
40
|
:target: https://asyncssh.readthedocs.io/en/latest/?badge=latest
|
|
@@ -276,3 +267,5 @@ Three mailing lists are available for AsyncSSH:
|
|
|
276
267
|
__ http://groups.google.com/d/forum/asyncssh-announce
|
|
277
268
|
__ http://groups.google.com/d/forum/asyncssh-dev
|
|
278
269
|
__ http://groups.google.com/d/forum/asyncssh-users
|
|
270
|
+
|
|
271
|
+
|
|
@@ -3,6 +3,32 @@
|
|
|
3
3
|
Change Log
|
|
4
4
|
==========
|
|
5
5
|
|
|
6
|
+
Release 2.17.0 (2 Sep 2024)
|
|
7
|
+
---------------------------
|
|
8
|
+
|
|
9
|
+
* Add support for specifying a per-connection credential store for GSSAPI
|
|
10
|
+
authentication. Thanks go to GitHub user zarganum for suggesting this
|
|
11
|
+
feature and proposing a detailed design.
|
|
12
|
+
|
|
13
|
+
* Fixed a regression introduced in AsyncSSH 2.15.0 which could cause
|
|
14
|
+
connections to be closed with an uncaught exception when a session
|
|
15
|
+
on the connection was closed. Thanks go to Wilson Conley for being
|
|
16
|
+
the first to help reproduce this issue, and others who also helped
|
|
17
|
+
to confirm the fix.
|
|
18
|
+
|
|
19
|
+
* Added a workaround where getaddrinfo() on some systems may return duplicate
|
|
20
|
+
entries, causing bind() to fail when opening a listener. Thanks go to
|
|
21
|
+
Colin Watson for reporting this issue and suggesting a fix.
|
|
22
|
+
|
|
23
|
+
* Relaxed padding length check on OpenSSH private keys to provide better
|
|
24
|
+
compatibility with keys generated by PuTTYgen.
|
|
25
|
+
|
|
26
|
+
* Improved documentation on SSHClient and SSHServer classes to explain
|
|
27
|
+
when they are created and their relationship to the SSHClientConnection
|
|
28
|
+
and SSHServerConnection classes.
|
|
29
|
+
|
|
30
|
+
* Updated examples to use Python 3.7 and made some minor improvements.
|
|
31
|
+
|
|
6
32
|
Release 2.16.0 (17 Aug 2024)
|
|
7
33
|
----------------------------
|
|
8
34
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#!/usr/bin/env python3.
|
|
1
|
+
#!/usr/bin/env python3.7
|
|
2
2
|
#
|
|
3
|
-
# Copyright (c) 2013-
|
|
3
|
+
# Copyright (c) 2013-2024 by Ron Frederick <ronf@timeheart.net> and others.
|
|
4
4
|
#
|
|
5
5
|
# This program and the accompanying materials are made available under
|
|
6
6
|
# the terms of the Eclipse Public License v2.0 which accompanies this
|
|
@@ -46,6 +46,6 @@ async def run_client() -> None:
|
|
|
46
46
|
await chan.wait_closed()
|
|
47
47
|
|
|
48
48
|
try:
|
|
49
|
-
asyncio.
|
|
49
|
+
asyncio.run(run_client())
|
|
50
50
|
except (OSError, asyncssh.Error) as exc:
|
|
51
51
|
sys.exit('SSH connection failed: ' + str(exc))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#!/usr/bin/env python3.
|
|
1
|
+
#!/usr/bin/env python3.7
|
|
2
2
|
#
|
|
3
|
-
# Copyright (c) 2013-
|
|
3
|
+
# Copyright (c) 2013-2024 by Ron Frederick <ronf@timeheart.net> and others.
|
|
4
4
|
#
|
|
5
5
|
# This program and the accompanying materials are made available under
|
|
6
6
|
# the terms of the Eclipse Public License v2.0 which accompanies this
|
|
@@ -37,6 +37,6 @@ async def run_client() -> None:
|
|
|
37
37
|
await chan.wait_closed()
|
|
38
38
|
|
|
39
39
|
try:
|
|
40
|
-
asyncio.
|
|
40
|
+
asyncio.run(run_client())
|
|
41
41
|
except (OSError, asyncssh.Error) as exc:
|
|
42
42
|
sys.exit('SSH connection failed: ' + str(exc))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#!/usr/bin/env python3.
|
|
1
|
+
#!/usr/bin/env python3.7
|
|
2
2
|
#
|
|
3
|
-
# Copyright (c) 2013-
|
|
3
|
+
# Copyright (c) 2013-2024 by Ron Frederick <ronf@timeheart.net> and others.
|
|
4
4
|
#
|
|
5
5
|
# This program and the accompanying materials are made available under
|
|
6
6
|
# the terms of the Eclipse Public License v2.0 which accompanies this
|
|
@@ -40,6 +40,6 @@ async def run_client() -> None:
|
|
|
40
40
|
await chan.wait_closed()
|
|
41
41
|
|
|
42
42
|
try:
|
|
43
|
-
asyncio.
|
|
43
|
+
asyncio.run(run_client())
|
|
44
44
|
except (OSError, asyncssh.Error) as exc:
|
|
45
45
|
sys.exit('SSH connection failed: ' + str(exc))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#!/usr/bin/env python3.
|
|
1
|
+
#!/usr/bin/env python3.7
|
|
2
2
|
#
|
|
3
|
-
# Copyright (c) 2013-
|
|
3
|
+
# Copyright (c) 2013-2024 by Ron Frederick <ronf@timeheart.net> and others.
|
|
4
4
|
#
|
|
5
5
|
# This program and the accompanying materials are made available under
|
|
6
6
|
# the terms of the Eclipse Public License v2.0 which accompanies this
|
|
@@ -64,6 +64,9 @@ class MySSHServerSession(asyncssh.SSHServerSession):
|
|
|
64
64
|
def break_received(self, msec: int) -> bool:
|
|
65
65
|
return self.eof_received()
|
|
66
66
|
|
|
67
|
+
def soft_eof_received(self) -> None:
|
|
68
|
+
self.eof_received()
|
|
69
|
+
|
|
67
70
|
class MySSHServer(asyncssh.SSHServer):
|
|
68
71
|
def session_requested(self) -> asyncssh.SSHServerSession:
|
|
69
72
|
return MySSHServerSession()
|
|
@@ -73,7 +76,7 @@ async def start_server() -> None:
|
|
|
73
76
|
server_host_keys=['ssh_host_key'],
|
|
74
77
|
authorized_client_keys='ssh_user_ca')
|
|
75
78
|
|
|
76
|
-
loop = asyncio.
|
|
79
|
+
loop = asyncio.new_event_loop()
|
|
77
80
|
|
|
78
81
|
try:
|
|
79
82
|
loop.run_until_complete(start_server())
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#!/usr/bin/env python3.
|
|
1
|
+
#!/usr/bin/env python3.7
|
|
2
2
|
#
|
|
3
|
-
# Copyright (c) 2016-
|
|
3
|
+
# Copyright (c) 2016-2024 by Ron Frederick <ronf@timeheart.net> and others.
|
|
4
4
|
#
|
|
5
5
|
# This program and the accompanying materials are made available under
|
|
6
6
|
# the terms of the Eclipse Public License v2.0 which accompanies this
|
|
@@ -76,7 +76,7 @@ async def start_server() -> None:
|
|
|
76
76
|
authorized_client_keys='ssh_user_ca',
|
|
77
77
|
process_factory=ChatClient.handle_client)
|
|
78
78
|
|
|
79
|
-
loop = asyncio.
|
|
79
|
+
loop = asyncio.new_event_loop()
|
|
80
80
|
|
|
81
81
|
try:
|
|
82
82
|
loop.run_until_complete(start_server())
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#!/usr/bin/env python3.
|
|
1
|
+
#!/usr/bin/env python3.7
|
|
2
2
|
#
|
|
3
|
-
# Copyright (c) 2013-
|
|
3
|
+
# Copyright (c) 2013-2024 by Ron Frederick <ronf@timeheart.net> and others.
|
|
4
4
|
#
|
|
5
5
|
# This program and the accompanying materials are made available under
|
|
6
6
|
# the terms of the Eclipse Public License v2.0 which accompanies this
|
|
@@ -34,6 +34,6 @@ async def run_client() -> None:
|
|
|
34
34
|
file=sys.stderr)
|
|
35
35
|
|
|
36
36
|
try:
|
|
37
|
-
asyncio.
|
|
37
|
+
asyncio.run(run_client())
|
|
38
38
|
except (OSError, asyncssh.Error) as exc:
|
|
39
39
|
sys.exit('SSH connection failed: ' + str(exc))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#!/usr/bin/env python3.
|
|
1
|
+
#!/usr/bin/env python3.7
|
|
2
2
|
#
|
|
3
|
-
# Copyright (c) 2016-
|
|
3
|
+
# Copyright (c) 2016-2024 by Ron Frederick <ronf@timeheart.net> and others.
|
|
4
4
|
#
|
|
5
5
|
# This program and the accompanying materials are made available under
|
|
6
6
|
# the terms of the Eclipse Public License v2.0 which accompanies this
|
|
@@ -40,7 +40,7 @@ async def start_server() -> None:
|
|
|
40
40
|
authorized_client_keys='ssh_user_ca',
|
|
41
41
|
sftp_factory=MySFTPServer)
|
|
42
42
|
|
|
43
|
-
loop = asyncio.
|
|
43
|
+
loop = asyncio.new_event_loop()
|
|
44
44
|
|
|
45
45
|
try:
|
|
46
46
|
loop.run_until_complete(start_server())
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#!/usr/bin/env python3.
|
|
1
|
+
#!/usr/bin/env python3.7
|
|
2
2
|
#
|
|
3
|
-
# Copyright (c) 2013-
|
|
3
|
+
# Copyright (c) 2013-2024 by Ron Frederick <ronf@timeheart.net> and others.
|
|
4
4
|
#
|
|
5
5
|
# This program and the accompanying materials are made available under
|
|
6
6
|
# the terms of the Eclipse Public License v2.0 which accompanies this
|
|
@@ -44,6 +44,6 @@ async def run_client() -> None:
|
|
|
44
44
|
await chan.wait_closed()
|
|
45
45
|
|
|
46
46
|
try:
|
|
47
|
-
asyncio.
|
|
47
|
+
asyncio.run(run_client())
|
|
48
48
|
except (OSError, asyncssh.Error) as exc:
|
|
49
49
|
sys.exit('SSH connection failed: ' + str(exc))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#!/usr/bin/env python3.
|
|
1
|
+
#!/usr/bin/env python3.7
|
|
2
2
|
#
|
|
3
|
-
# Copyright (c) 2013-
|
|
3
|
+
# Copyright (c) 2013-2024 by Ron Frederick <ronf@timeheart.net> and others.
|
|
4
4
|
#
|
|
5
5
|
# This program and the accompanying materials are made available under
|
|
6
6
|
# the terms of the Eclipse Public License v2.0 which accompanies this
|
|
@@ -52,7 +52,7 @@ async def start_server() -> None:
|
|
|
52
52
|
server_host_keys=['ssh_host_key'],
|
|
53
53
|
authorized_client_keys='ssh_user_ca')
|
|
54
54
|
|
|
55
|
-
loop = asyncio.
|
|
55
|
+
loop = asyncio.new_event_loop()
|
|
56
56
|
|
|
57
57
|
try:
|
|
58
58
|
loop.run_until_complete(start_server())
|