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.
Files changed (177) hide show
  1. {asyncssh-2.14.1 → asyncssh-2.15.0}/.github/workflows/run_tests.yml +29 -11
  2. {asyncssh-2.14.1 → asyncssh-2.15.0}/PKG-INFO +13 -5
  3. {asyncssh-2.14.1 → asyncssh-2.15.0}/README.rst +1 -0
  4. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/__init__.py +18 -17
  5. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/agent.py +3 -3
  6. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/agent_unix.py +1 -1
  7. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/agent_win32.py +1 -1
  8. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/channel.py +75 -18
  9. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/client.py +1 -1
  10. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/config.py +27 -16
  11. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/connection.py +585 -169
  12. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/dsa.py +1 -1
  13. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/ecdsa.py +1 -1
  14. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/editor.py +1 -1
  15. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/encryption.py +1 -1
  16. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/forward.py +33 -3
  17. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/gss_unix.py +1 -1
  18. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/kex.py +1 -1
  19. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/kex_dh.py +1 -1
  20. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/kex_rsa.py +1 -1
  21. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/listener.py +3 -2
  22. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/misc.py +26 -1
  23. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/process.py +115 -32
  24. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/public_key.py +34 -7
  25. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/rsa.py +1 -1
  26. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/scp.py +23 -9
  27. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/server.py +122 -1
  28. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/session.py +41 -8
  29. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/sftp.py +194 -81
  30. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/stream.py +54 -12
  31. asyncssh-2.15.0/asyncssh/tuntap.py +431 -0
  32. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/version.py +2 -2
  33. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh.egg-info/PKG-INFO +13 -5
  34. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh.egg-info/SOURCES.txt +2 -0
  35. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh.egg-info/requires.txt +1 -1
  36. {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/api.rst +122 -339
  37. {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/changes.rst +85 -2
  38. {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/index.rst +20 -1
  39. {asyncssh-2.14.1 → asyncssh-2.15.0}/mypy.ini +0 -1
  40. {asyncssh-2.14.1 → asyncssh-2.15.0}/setup.py +3 -1
  41. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_agent.py +2 -2
  42. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_channel.py +44 -1
  43. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_config.py +12 -1
  44. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_connection.py +139 -22
  45. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_connection_auth.py +82 -2
  46. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_forward.py +30 -12
  47. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_process.py +126 -30
  48. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_sftp.py +158 -3
  49. asyncssh-2.15.0/tests/test_tuntap.py +703 -0
  50. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/util.py +23 -0
  51. {asyncssh-2.14.1 → asyncssh-2.15.0}/.coveragerc +0 -0
  52. {asyncssh-2.14.1 → asyncssh-2.15.0}/.gitignore +0 -0
  53. {asyncssh-2.14.1 → asyncssh-2.15.0}/.readthedocs.yaml +0 -0
  54. {asyncssh-2.14.1 → asyncssh-2.15.0}/CONTRIBUTING.rst +0 -0
  55. {asyncssh-2.14.1 → asyncssh-2.15.0}/COPYRIGHT +0 -0
  56. {asyncssh-2.14.1 → asyncssh-2.15.0}/LICENSE +0 -0
  57. {asyncssh-2.14.1 → asyncssh-2.15.0}/MANIFEST.in +0 -0
  58. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/asn1.py +0 -0
  59. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/auth.py +0 -0
  60. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/auth_keys.py +0 -0
  61. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/compression.py +0 -0
  62. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/constants.py +0 -0
  63. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/__init__.py +0 -0
  64. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/chacha.py +0 -0
  65. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/cipher.py +0 -0
  66. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/dh.py +0 -0
  67. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/dsa.py +0 -0
  68. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/ec.py +0 -0
  69. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/ec_params.py +0 -0
  70. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/ed.py +0 -0
  71. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/kdf.py +0 -0
  72. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/misc.py +0 -0
  73. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/rsa.py +0 -0
  74. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/sntrup.py +0 -0
  75. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/umac.py +0 -0
  76. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/crypto/x509.py +0 -0
  77. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/eddsa.py +0 -0
  78. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/gss.py +0 -0
  79. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/gss_win32.py +0 -0
  80. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/keysign.py +0 -0
  81. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/known_hosts.py +0 -0
  82. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/logging.py +0 -0
  83. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/mac.py +0 -0
  84. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/packet.py +0 -0
  85. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/pattern.py +0 -0
  86. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/pbe.py +0 -0
  87. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/pkcs11.py +0 -0
  88. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/py.typed +0 -0
  89. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/saslprep.py +0 -0
  90. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/sk.py +0 -0
  91. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/sk_ecdsa.py +0 -0
  92. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/sk_eddsa.py +0 -0
  93. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/socks.py +0 -0
  94. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/subprocess.py +0 -0
  95. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh/x11.py +0 -0
  96. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh.egg-info/dependency_links.txt +0 -0
  97. {asyncssh-2.14.1 → asyncssh-2.15.0}/asyncssh.egg-info/top_level.txt +0 -0
  98. {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/_templates/sidebarbottom.html +0 -0
  99. {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/_templates/sidebartop.html +0 -0
  100. {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/conf.py +0 -0
  101. {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/contributing.rst +0 -0
  102. {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/requirements.txt +0 -0
  103. {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/rftheme/layout.html +0 -0
  104. {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/rftheme/static/rftheme.css_t +0 -0
  105. {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/rftheme/theme.conf +0 -0
  106. {asyncssh-2.14.1 → asyncssh-2.15.0}/docs/rtd-req.txt +0 -0
  107. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/callback_client.py +0 -0
  108. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/callback_client2.py +0 -0
  109. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/callback_client3.py +0 -0
  110. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/callback_math_server.py +0 -0
  111. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/chat_server.py +0 -0
  112. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/check_exit_status.py +0 -0
  113. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/chroot_sftp_server.py +0 -0
  114. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/direct_client.py +0 -0
  115. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/direct_server.py +0 -0
  116. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/editor.py +0 -0
  117. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/gather_results.py +0 -0
  118. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/listening_client.py +0 -0
  119. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/local_forwarding_client.py +0 -0
  120. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/local_forwarding_client2.py +0 -0
  121. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/local_forwarding_server.py +0 -0
  122. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/math_client.py +0 -0
  123. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/math_server.py +0 -0
  124. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/redirect_input.py +0 -0
  125. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/redirect_local_pipe.py +0 -0
  126. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/redirect_remote_pipe.py +0 -0
  127. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/redirect_server.py +0 -0
  128. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/remote_forwarding_client.py +0 -0
  129. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/remote_forwarding_client2.py +0 -0
  130. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/remote_forwarding_server.py +0 -0
  131. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/reverse_client.py +0 -0
  132. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/reverse_server.py +0 -0
  133. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/scp_client.py +0 -0
  134. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/set_environment.py +0 -0
  135. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/set_terminal.py +0 -0
  136. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/sftp_client.py +0 -0
  137. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/show_environment.py +0 -0
  138. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/show_terminal.py +0 -0
  139. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/simple_cert_server.py +0 -0
  140. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/simple_client.py +0 -0
  141. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/simple_keyed_server.py +0 -0
  142. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/simple_scp_server.py +0 -0
  143. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/simple_server.py +0 -0
  144. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/simple_sftp_server.py +0 -0
  145. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/stream_direct_client.py +0 -0
  146. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/stream_direct_server.py +0 -0
  147. {asyncssh-2.14.1 → asyncssh-2.15.0}/examples/stream_listening_client.py +0 -0
  148. {asyncssh-2.14.1 → asyncssh-2.15.0}/pylintrc +0 -0
  149. {asyncssh-2.14.1 → asyncssh-2.15.0}/setup.cfg +0 -0
  150. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/__init__.py +0 -0
  151. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/gss_stub.py +0 -0
  152. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/gssapi_stub.py +0 -0
  153. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/keysign_stub.py +0 -0
  154. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/pkcs11_stub.py +0 -0
  155. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/server.py +0 -0
  156. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/sk_stub.py +0 -0
  157. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/sspi_stub.py +0 -0
  158. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_asn1.py +0 -0
  159. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_auth.py +0 -0
  160. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_auth_keys.py +0 -0
  161. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_compression.py +0 -0
  162. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_editor.py +0 -0
  163. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_encryption.py +0 -0
  164. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_kex.py +0 -0
  165. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_known_hosts.py +0 -0
  166. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_logging.py +0 -0
  167. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_mac.py +0 -0
  168. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_packet.py +0 -0
  169. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_pkcs11.py +0 -0
  170. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_public_key.py +0 -0
  171. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_saslprep.py +0 -0
  172. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_sk.py +0 -0
  173. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_stream.py +0 -0
  174. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_subprocess.py +0 -0
  175. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_x11.py +0 -0
  176. {asyncssh-2.14.1 → asyncssh-2.15.0}/tests/test_x509.py +0 -0
  177. {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@v3
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@v3
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@v4
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: sudo apt install -y --no-install-recommends libnettle8 libsodium-dev libssl-dev libkrb5-dev ssh cmake ninja-build
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@v3
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: run-tests
149
+ needs: merge-coverage
133
150
  if: ${{ always() }}
134
151
  steps:
135
- - uses: actions/checkout@v3
136
- - uses: actions/setup-python@v4
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@v3
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@v3
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.14.1
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
- Provides-Extra: pyOpenSSL
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
- License-File: LICENSE
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-2022 by Ron Frederick <ronf@timeheart.net> and others.
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', 'SSHUNIXChannel', 'SSHUNIXSession', 'SSHWriter',
158
- 'STDOUT', 'ServiceNotAvailable', 'SignalReceived',
159
- 'TerminalSizeChanged', 'TimeoutError', 'connect', 'connect_agent',
160
- 'connect_reverse', 'create_connection', 'create_server',
161
- 'generate_private_key', 'get_server_auth_methods',
162
- 'get_server_host_key', 'import_authorized_keys', 'import_certificate',
163
- 'import_known_hosts', 'import_private_key', 'import_public_key',
164
- 'listen', 'listen_reverse', 'load_certificates', 'load_keypairs',
165
- 'load_pkcs11_keys', 'load_public_keys', 'load_resident_keys', 'logger',
166
- 'match_known_hosts', 'read_authorized_keys', 'read_certificate',
167
- 'read_certificate_list', 'read_known_hosts', 'read_private_key',
168
- 'read_private_key_list', 'read_public_key', 'read_public_key_list',
169
- 'run_client', 'run_server', 'scp', 'set_debug_level', 'set_log_level',
170
- 'set_sftp_log_level', 'set_default_skip_rsa_key_validation',
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-2023 by Ron Frederick <ronf@timeheart.net> and others.
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) -> 'SSHAgentClient':
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-2021 by Ron Frederick <ronf@timeheart.net> and others.
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-2021 by Ron Frederick <ronf@timeheart.net> and others.
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-2023 by Ron Frederick <ronf@timeheart.net> and others.
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
- self._loop.call_soon(self._cleanup)
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-2021 by Ron Frederick <ronf@timeheart.net> and others.
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-2022 by Ron Frederick <ronf@timeheart.net> and others.
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
- args = shlex.split(line)
318
+ split_args = shlex.split(line)
319
319
  except ValueError as exc:
320
320
  self._error(str(exc))
321
321
 
322
- option = args.pop(0)
323
-
324
- if option.endswith('='):
325
- option = option[:-1]
326
- elif '=' in option:
327
- option, arg = option.split('=', 1)
328
- args[:0] =[arg]
329
- elif args and args[0] == '=':
330
- del args[0]
331
- elif args and args[0].startswith('='):
332
- args[0] = args[0][1:]
333
-
334
- loption = option.lower()
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._append_string_list),
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)