asyncssh 2.14.1__tar.gz → 2.15.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.14.1 → asyncssh-2.15.0}/.github/workflows/run_tests.yml +29 -11
- {asyncssh-2.14.1 → asyncssh-2.15.0}/PKG-INFO +13 -5
- {asyncssh-2.14.1 → asyncssh-2.15.0}/README.rst +1 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/__init__.py +18 -17
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/agent.py +3 -3
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/agent_unix.py +1 -1
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/agent_win32.py +1 -1
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/channel.py +75 -18
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/client.py +1 -1
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/config.py +27 -16
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/connection.py +585 -169
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/dsa.py +1 -1
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/ecdsa.py +1 -1
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/editor.py +1 -1
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/encryption.py +1 -1
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/forward.py +33 -3
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/gss_unix.py +1 -1
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/kex.py +1 -1
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/kex_dh.py +1 -1
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/kex_rsa.py +1 -1
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/listener.py +3 -2
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/misc.py +26 -1
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/process.py +115 -32
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/public_key.py +34 -7
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/rsa.py +1 -1
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/scp.py +23 -9
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/server.py +122 -1
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/session.py +41 -8
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/sftp.py +194 -81
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/stream.py +54 -12
- asyncssh-2.15.0/asyncssh/tuntap.py +431 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/version.py +2 -2
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh.egg-info/PKG-INFO +13 -5
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh.egg-info/SOURCES.txt +2 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh.egg-info/requires.txt +1 -1
- {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/api.rst +122 -339
- {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/changes.rst +85 -2
- {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/index.rst +20 -1
- {asyncssh-2.14.1 → asyncssh-2.15.0}/mypy.ini +0 -1
- {asyncssh-2.14.1 → asyncssh-2.15.0}/setup.py +3 -1
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_agent.py +2 -2
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_channel.py +44 -1
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_config.py +12 -1
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_connection.py +139 -22
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_connection_auth.py +82 -2
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_forward.py +30 -12
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_process.py +126 -30
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_sftp.py +158 -3
- asyncssh-2.15.0/tests/test_tuntap.py +703 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/util.py +23 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/.coveragerc +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/.gitignore +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/.readthedocs.yaml +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/CONTRIBUTING.rst +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/COPYRIGHT +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/LICENSE +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/MANIFEST.in +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/asn1.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/auth.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/auth_keys.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/compression.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/constants.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/__init__.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/chacha.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/cipher.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/dh.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/dsa.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/ec.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/ec_params.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/ed.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/kdf.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/misc.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/rsa.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/sntrup.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/umac.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/x509.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/eddsa.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/gss.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/gss_win32.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/keysign.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/known_hosts.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/logging.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/mac.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/packet.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/pattern.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/pbe.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/pkcs11.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/py.typed +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/saslprep.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/sk.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/sk_ecdsa.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/sk_eddsa.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/socks.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/subprocess.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/x11.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh.egg-info/dependency_links.txt +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh.egg-info/top_level.txt +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/_templates/sidebarbottom.html +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/_templates/sidebartop.html +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/conf.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/contributing.rst +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/requirements.txt +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/rftheme/layout.html +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/rftheme/static/rftheme.css_t +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/rftheme/theme.conf +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/rtd-req.txt +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/callback_client.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/callback_client2.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/callback_client3.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/callback_math_server.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/chat_server.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/check_exit_status.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/chroot_sftp_server.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/direct_client.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/direct_server.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/editor.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/gather_results.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/listening_client.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/local_forwarding_client.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/local_forwarding_client2.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/local_forwarding_server.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/math_client.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/math_server.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/redirect_input.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/redirect_local_pipe.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/redirect_remote_pipe.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/redirect_server.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/remote_forwarding_client.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/remote_forwarding_client2.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/remote_forwarding_server.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/reverse_client.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/reverse_server.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/scp_client.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/set_environment.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/set_terminal.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/sftp_client.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/show_environment.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/show_terminal.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/simple_cert_server.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/simple_client.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/simple_keyed_server.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/simple_scp_server.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/simple_server.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/simple_sftp_server.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/stream_direct_client.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/stream_direct_server.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/stream_listening_client.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/pylintrc +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/setup.cfg +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/__init__.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/gss_stub.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/gssapi_stub.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/keysign_stub.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/pkcs11_stub.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/server.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/sk_stub.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/sspi_stub.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_asn1.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_auth.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_auth_keys.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_compression.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_editor.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_encryption.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_kex.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_known_hosts.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_logging.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_mac.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_packet.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_pkcs11.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_public_key.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_saslprep.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_sk.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_stream.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_subprocess.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_x11.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_x509.py +0 -0
- {asyncssh-2.14.1 → asyncssh-2.15.0}/tox.ini +0 -0
|
@@ -20,6 +20,10 @@ jobs:
|
|
|
20
20
|
python-version: "3.12"
|
|
21
21
|
openssl-version: "3"
|
|
22
22
|
exclude:
|
|
23
|
+
# having trouble with arch arm64 on macos-ltest on Python 3.7
|
|
24
|
+
- os: macos-latest
|
|
25
|
+
python-version: "3.7"
|
|
26
|
+
|
|
23
27
|
# test hangs on these combination
|
|
24
28
|
- os: windows-latest
|
|
25
29
|
python-version: "3.8"
|
|
@@ -39,19 +43,19 @@ jobs:
|
|
|
39
43
|
|
|
40
44
|
steps:
|
|
41
45
|
- name: Checkout asyncssh
|
|
42
|
-
uses: actions/checkout@
|
|
46
|
+
uses: actions/checkout@v4
|
|
43
47
|
with:
|
|
44
48
|
path: asyncssh
|
|
45
49
|
|
|
46
50
|
- name: Checkout liboqs
|
|
47
51
|
if: ${{ runner.os != 'macOS' }}
|
|
48
|
-
uses: actions/checkout@
|
|
52
|
+
uses: actions/checkout@v4
|
|
49
53
|
with:
|
|
50
54
|
repository: open-quantum-safe/liboqs
|
|
51
55
|
ref: ${{ env.liboqs_version }}
|
|
52
56
|
path: liboqs
|
|
53
57
|
|
|
54
|
-
- uses: actions/setup-python@
|
|
58
|
+
- uses: actions/setup-python@v5
|
|
55
59
|
with:
|
|
56
60
|
python-version: ${{ matrix.python-version }}
|
|
57
61
|
cache: pip
|
|
@@ -67,7 +71,9 @@ jobs:
|
|
|
67
71
|
|
|
68
72
|
- name: Install Linux dependencies
|
|
69
73
|
if: ${{ runner.os == 'Linux' }}
|
|
70
|
-
run:
|
|
74
|
+
run: |
|
|
75
|
+
sudo apt update
|
|
76
|
+
sudo apt install -y --no-install-recommends libnettle8 libsodium-dev libssl-dev libkrb5-dev ssh cmake ninja-build
|
|
71
77
|
|
|
72
78
|
- name: Install macOS dependencies
|
|
73
79
|
if: ${{ runner.os == 'macOS' }}
|
|
@@ -120,23 +126,34 @@ jobs:
|
|
|
120
126
|
check=True)
|
|
121
127
|
|
|
122
128
|
- name: Upload coverage data
|
|
123
|
-
uses: actions/upload-artifact@
|
|
129
|
+
uses: actions/upload-artifact@v4
|
|
124
130
|
with:
|
|
125
|
-
name: coverage
|
|
131
|
+
name: coverage-${{ matrix.os }}-${{ matrix.python-version }}
|
|
126
132
|
path: asyncssh/.coverage.*
|
|
127
133
|
retention-days: 1
|
|
128
134
|
|
|
135
|
+
merge-coverage:
|
|
136
|
+
runs-on: ubuntu-latest
|
|
137
|
+
needs: run-tests
|
|
138
|
+
if: ${{ always() }}
|
|
139
|
+
steps:
|
|
140
|
+
- name: Merge coverage
|
|
141
|
+
uses: actions/upload-artifact/merge@v4
|
|
142
|
+
with:
|
|
143
|
+
name: coverage
|
|
144
|
+
pattern: coverage-*
|
|
145
|
+
|
|
129
146
|
report-coverage:
|
|
130
147
|
name: Report coverage
|
|
131
148
|
runs-on: ubuntu-latest
|
|
132
|
-
needs:
|
|
149
|
+
needs: merge-coverage
|
|
133
150
|
if: ${{ always() }}
|
|
134
151
|
steps:
|
|
135
|
-
- uses: actions/checkout@
|
|
136
|
-
- uses: actions/setup-python@
|
|
152
|
+
- uses: actions/checkout@v4
|
|
153
|
+
- uses: actions/setup-python@v5
|
|
137
154
|
with:
|
|
138
155
|
python-version: "3.7"
|
|
139
|
-
- uses: actions/download-artifact@
|
|
156
|
+
- uses: actions/download-artifact@v4
|
|
140
157
|
with:
|
|
141
158
|
name: coverage
|
|
142
159
|
- name: Install dependencies
|
|
@@ -150,6 +167,7 @@ jobs:
|
|
|
150
167
|
sqlite3 "$f" "update file set path = replace(path, '\\', '/');"
|
|
151
168
|
done
|
|
152
169
|
tox -e report
|
|
153
|
-
- uses: codecov/codecov-action@
|
|
170
|
+
- uses: codecov/codecov-action@v4
|
|
154
171
|
with:
|
|
155
172
|
files: coverage.xml
|
|
173
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: asyncssh
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.15.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,14 +27,23 @@ 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
|
|
30
33
|
Provides-Extra: bcrypt
|
|
34
|
+
Requires-Dist: bcrypt>=3.1.3; extra == "bcrypt"
|
|
31
35
|
Provides-Extra: fido2
|
|
36
|
+
Requires-Dist: fido2>=0.9.2; extra == "fido2"
|
|
32
37
|
Provides-Extra: gssapi
|
|
38
|
+
Requires-Dist: gssapi>=1.2.0; extra == "gssapi"
|
|
33
39
|
Provides-Extra: libnacl
|
|
40
|
+
Requires-Dist: libnacl>=1.4.2; extra == "libnacl"
|
|
34
41
|
Provides-Extra: pkcs11
|
|
35
|
-
|
|
42
|
+
Requires-Dist: python-pkcs11>=0.7.0; extra == "pkcs11"
|
|
43
|
+
Provides-Extra: pyopenssl
|
|
44
|
+
Requires-Dist: pyOpenSSL>=23.0.0; extra == "pyopenssl"
|
|
36
45
|
Provides-Extra: pywin32
|
|
37
|
-
|
|
46
|
+
Requires-Dist: pywin32>=227; extra == "pywin32"
|
|
38
47
|
|
|
39
48
|
.. image:: https://readthedocs.org/projects/asyncssh/badge/?version=latest
|
|
40
49
|
:target: https://asyncssh.readthedocs.io/en/latest/?badge=latest
|
|
@@ -79,6 +88,7 @@ Features
|
|
|
79
88
|
* Environment variables, terminal type, and window size
|
|
80
89
|
* Direct and forwarded TCP/IP channels
|
|
81
90
|
* OpenSSH-compatible direct and forwarded UNIX domain socket channels
|
|
91
|
+
* OpenSSH-compatible TUN/TAP channels and packet forwarding
|
|
82
92
|
* Local and remote TCP/IP port forwarding
|
|
83
93
|
* Local and remote UNIX domain socket forwarding
|
|
84
94
|
* Dynamic TCP/IP port forwarding via SOCKS
|
|
@@ -266,5 +276,3 @@ Three mailing lists are available for AsyncSSH:
|
|
|
266
276
|
__ http://groups.google.com/d/forum/asyncssh-announce
|
|
267
277
|
__ http://groups.google.com/d/forum/asyncssh-dev
|
|
268
278
|
__ http://groups.google.com/d/forum/asyncssh-users
|
|
269
|
-
|
|
270
|
-
|
|
@@ -41,6 +41,7 @@ Features
|
|
|
41
41
|
* Environment variables, terminal type, and window size
|
|
42
42
|
* Direct and forwarded TCP/IP channels
|
|
43
43
|
* OpenSSH-compatible direct and forwarded UNIX domain socket channels
|
|
44
|
+
* OpenSSH-compatible TUN/TAP channels and packet forwarding
|
|
44
45
|
* Local and remote TCP/IP port forwarding
|
|
45
46
|
* Local and remote UNIX domain socket forwarding
|
|
46
47
|
* Dynamic TCP/IP port forwarding via SOCKS
|
|
@@ -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
|
|
@@ -34,7 +34,7 @@ from .auth_keys import SSHAuthorizedKeys
|
|
|
34
34
|
from .auth_keys import import_authorized_keys, read_authorized_keys
|
|
35
35
|
|
|
36
36
|
from .channel import SSHClientChannel, SSHServerChannel
|
|
37
|
-
from .channel import SSHTCPChannel, SSHUNIXChannel
|
|
37
|
+
from .channel import SSHTCPChannel, SSHUNIXChannel, SSHTunTapChannel
|
|
38
38
|
|
|
39
39
|
from .client import SSHClient
|
|
40
40
|
|
|
@@ -92,7 +92,7 @@ from .rsa import set_default_skip_rsa_key_validation
|
|
|
92
92
|
from .scp import scp
|
|
93
93
|
|
|
94
94
|
from .session import DataType, SSHClientSession, SSHServerSession
|
|
95
|
-
from .session import SSHTCPSession, SSHUNIXSession
|
|
95
|
+
from .session import SSHTCPSession, SSHUNIXSession, SSHTunTapSession
|
|
96
96
|
|
|
97
97
|
from .server import SSHServer
|
|
98
98
|
|
|
@@ -154,18 +154,19 @@ __all__ = [
|
|
|
154
154
|
'SSHServerSessionFactory', 'SSHSocketSessionFactory',
|
|
155
155
|
'SSHSubprocessProtocol', 'SSHSubprocessReadPipe',
|
|
156
156
|
'SSHSubprocessTransport', 'SSHSubprocessWritePipe', 'SSHTCPChannel',
|
|
157
|
-
'SSHTCPSession', '
|
|
158
|
-
'
|
|
159
|
-
'
|
|
160
|
-
'
|
|
161
|
-
'
|
|
162
|
-
'
|
|
163
|
-
'
|
|
164
|
-
'
|
|
165
|
-
'
|
|
166
|
-
'
|
|
167
|
-
'
|
|
168
|
-
'
|
|
169
|
-
'
|
|
170
|
-
'
|
|
157
|
+
'SSHTCPSession', 'SSHTunTapChannel', 'SSHTunTapSession',
|
|
158
|
+
'SSHUNIXChannel', 'SSHUNIXSession', 'SSHWriter',
|
|
159
|
+
'STDOUT', 'ServiceNotAvailable', 'SignalReceived', 'TerminalSizeChanged',
|
|
160
|
+
'TimeoutError', 'connect', 'connect_agent', 'connect_reverse',
|
|
161
|
+
'create_connection', 'create_server', 'generate_private_key',
|
|
162
|
+
'get_server_auth_methods', 'get_server_host_key',
|
|
163
|
+
'import_authorized_keys', 'import_certificate', 'import_known_hosts',
|
|
164
|
+
'import_private_key', 'import_public_key', 'listen', 'listen_reverse',
|
|
165
|
+
'load_certificates', 'load_keypairs', 'load_pkcs11_keys',
|
|
166
|
+
'load_public_keys', 'load_resident_keys', 'logger', 'match_known_hosts',
|
|
167
|
+
'read_authorized_keys', 'read_certificate', 'read_certificate_list',
|
|
168
|
+
'read_known_hosts', 'read_private_key', 'read_private_key_list',
|
|
169
|
+
'read_public_key', 'read_public_key_list', 'run_client', 'run_server',
|
|
170
|
+
'scp', 'set_debug_level', 'set_default_skip_rsa_key_validation',
|
|
171
|
+
'set_log_level', 'set_sftp_log_level'
|
|
171
172
|
]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c) 2016-
|
|
1
|
+
# Copyright (c) 2016-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
|
|
@@ -26,7 +26,7 @@ import os
|
|
|
26
26
|
import sys
|
|
27
27
|
from types import TracebackType
|
|
28
28
|
from typing import TYPE_CHECKING, List, Optional, Sequence, Tuple, Type, Union
|
|
29
|
-
from typing_extensions import Protocol
|
|
29
|
+
from typing_extensions import Protocol, Self
|
|
30
30
|
|
|
31
31
|
from .listener import SSHForwardListener
|
|
32
32
|
from .misc import async_context_manager, maybe_wait_closed
|
|
@@ -198,7 +198,7 @@ class SSHAgentClient:
|
|
|
198
198
|
self._writer: Optional[AgentWriter] = None
|
|
199
199
|
self._lock = asyncio.Lock()
|
|
200
200
|
|
|
201
|
-
async def __aenter__(self) ->
|
|
201
|
+
async def __aenter__(self) -> Self:
|
|
202
202
|
"""Allow SSHAgentClient to be used as an async context manager"""
|
|
203
203
|
|
|
204
204
|
return self
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c) 2016-
|
|
1
|
+
# Copyright (c) 2016-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
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c) 2016-
|
|
1
|
+
# Copyright (c) 2016-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
|
|
@@ -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
|
|
@@ -46,18 +46,22 @@ from .editor import SSHLineEditorChannel, SSHLineEditorSession
|
|
|
46
46
|
from .logging import SSHLogger
|
|
47
47
|
|
|
48
48
|
from .misc import ChannelOpenError, MaybeAwait, ProtocolError
|
|
49
|
+
from .misc import TermModes, TermSize, TermSizeArg
|
|
49
50
|
from .misc import get_symbol_names, map_handler_name
|
|
50
51
|
|
|
51
52
|
from .packet import Boolean, Byte, String, UInt32, SSHPacket, SSHPacketHandler
|
|
52
53
|
|
|
53
|
-
from .session import TermModes, TermSize, TermSizeArg
|
|
54
54
|
from .session import SSHSession, SSHClientSession, SSHServerSession
|
|
55
|
-
from .session import SSHTCPSession, SSHUNIXSession
|
|
55
|
+
from .session import SSHTCPSession, SSHUNIXSession, SSHTunTapSession
|
|
56
56
|
from .session import SSHSessionFactory, SSHClientSessionFactory
|
|
57
57
|
from .session import SSHTCPSessionFactory, SSHUNIXSessionFactory
|
|
58
|
+
from .session import SSHTunTapSessionFactory
|
|
58
59
|
|
|
59
60
|
from .stream import DataType
|
|
60
61
|
|
|
62
|
+
from .tuntap import SSH_TUN_MODE_POINTTOPOINT, SSH_TUN_UNIT_ANY
|
|
63
|
+
from .tuntap import SSH_TUN_AF_INET, SSH_TUN_AF_INET6
|
|
64
|
+
|
|
61
65
|
|
|
62
66
|
if TYPE_CHECKING:
|
|
63
67
|
# pylint: disable=cyclic-import
|
|
@@ -225,7 +229,7 @@ class SSHChannel(Generic[AnyStr], SSHPacketHandler):
|
|
|
225
229
|
|
|
226
230
|
self._close_event.set()
|
|
227
231
|
|
|
228
|
-
if self._conn: # pragma: no branch
|
|
232
|
+
if self._conn and self._recv_state == 'closed': # pragma: no branch
|
|
229
233
|
self.logger.info('Channel closed%s',
|
|
230
234
|
': ' + str(exc) if exc else '')
|
|
231
235
|
|
|
@@ -259,7 +263,8 @@ class SSHChannel(Generic[AnyStr], SSHPacketHandler):
|
|
|
259
263
|
# If recv is close_pending, we know send is already closed
|
|
260
264
|
if self._recv_state == 'close_pending':
|
|
261
265
|
self._recv_state = 'closed'
|
|
262
|
-
|
|
266
|
+
|
|
267
|
+
self._loop.call_soon(self._cleanup)
|
|
263
268
|
|
|
264
269
|
async def _start_reading(self) -> None:
|
|
265
270
|
"""Start processing data on a new connection"""
|
|
@@ -394,19 +399,6 @@ class SSHChannel(Generic[AnyStr], SSHPacketHandler):
|
|
|
394
399
|
if self._send_state in {'close_pending', 'closed'}:
|
|
395
400
|
return
|
|
396
401
|
|
|
397
|
-
datalen = len(data)
|
|
398
|
-
|
|
399
|
-
if datalen > self._recv_window:
|
|
400
|
-
raise ProtocolError('Window exceeded')
|
|
401
|
-
|
|
402
|
-
if datatype:
|
|
403
|
-
typename = ' from %s' % _data_type_names[datatype]
|
|
404
|
-
else:
|
|
405
|
-
typename = ''
|
|
406
|
-
|
|
407
|
-
self.logger.debug2('Received %d data byte%s%s', datalen,
|
|
408
|
-
's' if datalen > 1 else '', typename)
|
|
409
|
-
|
|
410
402
|
if self._recv_paused:
|
|
411
403
|
self._recv_buf.append((data, datatype))
|
|
412
404
|
else:
|
|
@@ -579,6 +571,14 @@ class SSHChannel(Generic[AnyStr], SSHPacketHandler):
|
|
|
579
571
|
data = packet.get_string()
|
|
580
572
|
packet.check_end()
|
|
581
573
|
|
|
574
|
+
datalen = len(data)
|
|
575
|
+
|
|
576
|
+
if datalen > self._recv_window:
|
|
577
|
+
raise ProtocolError('Window exceeded')
|
|
578
|
+
|
|
579
|
+
self.logger.debug2('Received %d data byte%s', datalen,
|
|
580
|
+
's' if datalen > 1 else '')
|
|
581
|
+
|
|
582
582
|
self._accept_data(data)
|
|
583
583
|
|
|
584
584
|
def _process_extended_data(self, _pkttype: int, _pktid: int,
|
|
@@ -595,6 +595,15 @@ class SSHChannel(Generic[AnyStr], SSHPacketHandler):
|
|
|
595
595
|
if datatype not in self._read_datatypes:
|
|
596
596
|
raise ProtocolError('Invalid extended data type')
|
|
597
597
|
|
|
598
|
+
datalen = len(data)
|
|
599
|
+
|
|
600
|
+
if datalen > self._recv_window:
|
|
601
|
+
raise ProtocolError('Window exceeded')
|
|
602
|
+
|
|
603
|
+
self.logger.debug2('Received %d data byte%s from %s', datalen,
|
|
604
|
+
's' if datalen > 1 else '',
|
|
605
|
+
_data_type_names[datatype])
|
|
606
|
+
|
|
598
607
|
self._accept_data(data, datatype)
|
|
599
608
|
|
|
600
609
|
def _process_eof(self, _pkttype: int, _pktid: int,
|
|
@@ -2080,6 +2089,54 @@ class SSHUNIXChannel(SSHForwardChannel, Generic[AnyStr]):
|
|
|
2080
2089
|
self.set_extra_info(local_peername=dest_path, remote_peername='')
|
|
2081
2090
|
|
|
2082
2091
|
|
|
2092
|
+
class SSHTunTapChannel(SSHForwardChannel[bytes]):
|
|
2093
|
+
"""SSH TunTap channel"""
|
|
2094
|
+
|
|
2095
|
+
def __init__(self, conn: 'SSHConnection',
|
|
2096
|
+
loop: asyncio.AbstractEventLoop, encoding: Optional[str],
|
|
2097
|
+
errors: str, window: int, max_pktsize: int):
|
|
2098
|
+
super().__init__(conn, loop, encoding, errors, window, max_pktsize)
|
|
2099
|
+
|
|
2100
|
+
self._mode: Optional[int] = None
|
|
2101
|
+
|
|
2102
|
+
def _accept_data(self, data: bytes, datatype: DataType = None) -> None:
|
|
2103
|
+
"""Strip off address family on incoming packets in TUN mode"""
|
|
2104
|
+
|
|
2105
|
+
if self._mode == SSH_TUN_MODE_POINTTOPOINT:
|
|
2106
|
+
data = data[4:]
|
|
2107
|
+
|
|
2108
|
+
super()._accept_data(data, datatype)
|
|
2109
|
+
|
|
2110
|
+
def write(self, data: bytes, datatype: DataType = None) -> None:
|
|
2111
|
+
"""Add address family in outbound packets in TUN mode"""
|
|
2112
|
+
|
|
2113
|
+
if self._mode == SSH_TUN_MODE_POINTTOPOINT:
|
|
2114
|
+
version = data[0] >> 4
|
|
2115
|
+
family = SSH_TUN_AF_INET if version == 4 else SSH_TUN_AF_INET6
|
|
2116
|
+
data = UInt32(family) + data
|
|
2117
|
+
|
|
2118
|
+
super().write(data, datatype)
|
|
2119
|
+
|
|
2120
|
+
async def open(self, session_factory: SSHTunTapSessionFactory,
|
|
2121
|
+
mode: int, unit: Optional[int]) -> SSHTunTapSession:
|
|
2122
|
+
"""Open a TUN/TAP channel"""
|
|
2123
|
+
|
|
2124
|
+
self._mode = mode
|
|
2125
|
+
|
|
2126
|
+
if unit is None:
|
|
2127
|
+
unit = SSH_TUN_UNIT_ANY
|
|
2128
|
+
|
|
2129
|
+
return cast(SSHTunTapSession,
|
|
2130
|
+
await self._open_forward(session_factory,
|
|
2131
|
+
b'tun@openssh.com',
|
|
2132
|
+
UInt32(mode), UInt32(unit)))
|
|
2133
|
+
|
|
2134
|
+
def set_mode(self, mode: int) -> None:
|
|
2135
|
+
"""Set mode for inbound connections"""
|
|
2136
|
+
|
|
2137
|
+
self._mode = mode
|
|
2138
|
+
|
|
2139
|
+
|
|
2083
2140
|
class SSHX11Channel(SSHForwardChannel[bytes]):
|
|
2084
2141
|
"""SSH X11 channel"""
|
|
2085
2142
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c) 2013-
|
|
1
|
+
# Copyright (c) 2013-2023 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
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c) 2020-
|
|
1
|
+
# Copyright (c) 2020-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
|
|
@@ -315,23 +315,31 @@ class SSHConfig:
|
|
|
315
315
|
continue
|
|
316
316
|
|
|
317
317
|
try:
|
|
318
|
-
|
|
318
|
+
split_args = shlex.split(line)
|
|
319
319
|
except ValueError as exc:
|
|
320
320
|
self._error(str(exc))
|
|
321
321
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
322
|
+
args = []
|
|
323
|
+
loption = ''
|
|
324
|
+
|
|
325
|
+
for i, arg in enumerate(split_args, 1):
|
|
326
|
+
if arg.startswith('='):
|
|
327
|
+
if len(arg) > 1:
|
|
328
|
+
args.append(arg[1:])
|
|
329
|
+
elif arg.endswith('='):
|
|
330
|
+
args.append(arg[:-1])
|
|
331
|
+
elif '=' in arg:
|
|
332
|
+
arg, val = arg.split('=', 1)
|
|
333
|
+
args.append(arg)
|
|
334
|
+
args.append(val)
|
|
335
|
+
else:
|
|
336
|
+
args.append(arg)
|
|
337
|
+
|
|
338
|
+
if i == 1:
|
|
339
|
+
loption = args.pop(0).lower()
|
|
340
|
+
elif i > 1 and loption not in self._conditionals:
|
|
341
|
+
args.extend(split_args[i:])
|
|
342
|
+
break
|
|
335
343
|
|
|
336
344
|
if loption in self._no_split:
|
|
337
345
|
args = [line.lstrip()[len(loption):].strip()]
|
|
@@ -443,6 +451,8 @@ class SSHClientConfig(SSHConfig):
|
|
|
443
451
|
return self._local_user
|
|
444
452
|
elif match == 'user':
|
|
445
453
|
return self._options.get('User', self._local_user)
|
|
454
|
+
elif match == 'tagged':
|
|
455
|
+
return self._options.get('Tag', '')
|
|
446
456
|
else:
|
|
447
457
|
return None
|
|
448
458
|
|
|
@@ -556,7 +566,8 @@ class SSHClientConfig(SSHConfig):
|
|
|
556
566
|
('SendEnv', SSHConfig._append_string_list),
|
|
557
567
|
('ServerAliveCountMax', SSHConfig._set_int),
|
|
558
568
|
('ServerAliveInterval', SSHConfig._set_int),
|
|
559
|
-
('SetEnv', SSHConfig.
|
|
569
|
+
('SetEnv', SSHConfig._set_string_list),
|
|
570
|
+
('Tag', SSHConfig._set_string),
|
|
560
571
|
('TCPKeepAlive', SSHConfig._set_bool),
|
|
561
572
|
('User', SSHConfig._set_string),
|
|
562
573
|
('UserKnownHostsFile', SSHConfig._set_string_list)
|