bdy 1.23.6-dev → 1.23.7-dev

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 (205) hide show
  1. package/distTs/package.json +10 -2
  2. package/distTs/src/agent/linux.js +53 -3
  3. package/distTs/src/agent/manager.js +3 -6
  4. package/distTs/src/agent/osx.js +51 -3
  5. package/distTs/src/agent/system.js +400 -21
  6. package/distTs/src/agent/windows.js +4 -7
  7. package/distTs/src/command/agent/debug.js +2 -2
  8. package/distTs/src/command/agent/install.js +37 -10
  9. package/distTs/src/command/agent/restart.js +2 -2
  10. package/distTs/src/command/agent/run.js +3 -0
  11. package/distTs/src/command/agent/start.js +2 -2
  12. package/distTs/src/command/agent/status.js +19 -0
  13. package/distTs/src/command/agent/stop.js +2 -2
  14. package/distTs/src/command/agent/uninstall.js +5 -8
  15. package/distTs/src/command/agent/update.js +2 -2
  16. package/distTs/src/command/artifact/publish.js +3 -1
  17. package/distTs/src/command/version.js +13 -5
  18. package/distTs/src/logger.js +40 -4
  19. package/distTs/src/texts.js +35 -19
  20. package/distTs/src/utils.js +98 -3
  21. package/node_modules/asn1/Jenkinsfile +65 -0
  22. package/node_modules/asn1/LICENSE +19 -0
  23. package/node_modules/asn1/README.md +50 -0
  24. package/node_modules/asn1/lib/ber/errors.js +13 -0
  25. package/node_modules/asn1/lib/ber/index.js +27 -0
  26. package/node_modules/asn1/lib/ber/reader.js +262 -0
  27. package/node_modules/asn1/lib/ber/types.js +36 -0
  28. package/node_modules/asn1/lib/ber/writer.js +317 -0
  29. package/node_modules/asn1/lib/index.js +20 -0
  30. package/node_modules/asn1/package.json +31 -0
  31. package/node_modules/bcrypt-pbkdf/CONTRIBUTING.md +13 -0
  32. package/node_modules/bcrypt-pbkdf/LICENSE +66 -0
  33. package/node_modules/bcrypt-pbkdf/README.md +45 -0
  34. package/node_modules/bcrypt-pbkdf/index.js +556 -0
  35. package/node_modules/bcrypt-pbkdf/package.json +15 -0
  36. package/node_modules/safer-buffer/LICENSE +21 -0
  37. package/node_modules/safer-buffer/Porting-Buffer.md +268 -0
  38. package/node_modules/safer-buffer/Readme.md +156 -0
  39. package/node_modules/safer-buffer/dangerous.js +58 -0
  40. package/node_modules/safer-buffer/package.json +34 -0
  41. package/node_modules/safer-buffer/safer.js +77 -0
  42. package/node_modules/safer-buffer/tests.js +406 -0
  43. package/node_modules/ssh2/.eslintignore +4 -0
  44. package/node_modules/ssh2/.eslintrc.js +5 -0
  45. package/node_modules/ssh2/.github/workflows/ci.yml +110 -0
  46. package/node_modules/ssh2/.github/workflows/lint.yml +27 -0
  47. package/node_modules/ssh2/LICENSE +19 -0
  48. package/node_modules/ssh2/README.md +1529 -0
  49. package/node_modules/ssh2/SFTP.md +413 -0
  50. package/node_modules/ssh2/examples/server-chat.js +238 -0
  51. package/node_modules/ssh2/examples/sftp-server-download-only.js +134 -0
  52. package/node_modules/ssh2/install.js +27 -0
  53. package/node_modules/ssh2/lib/Channel.js +295 -0
  54. package/node_modules/ssh2/lib/agent.js +1123 -0
  55. package/node_modules/ssh2/lib/client.js +2176 -0
  56. package/node_modules/ssh2/lib/http-agents.js +84 -0
  57. package/node_modules/ssh2/lib/index.js +44 -0
  58. package/node_modules/ssh2/lib/keygen.js +582 -0
  59. package/node_modules/ssh2/lib/protocol/Protocol.js +2136 -0
  60. package/node_modules/ssh2/lib/protocol/SFTP.js +4052 -0
  61. package/node_modules/ssh2/lib/protocol/constants.js +356 -0
  62. package/node_modules/ssh2/lib/protocol/crypto/binding.gyp +23 -0
  63. package/node_modules/ssh2/lib/protocol/crypto/poly1305.js +43 -0
  64. package/node_modules/ssh2/lib/protocol/crypto/src/binding.cc +2300 -0
  65. package/node_modules/ssh2/lib/protocol/crypto.js +1602 -0
  66. package/node_modules/ssh2/lib/protocol/handlers.js +16 -0
  67. package/node_modules/ssh2/lib/protocol/handlers.misc.js +1285 -0
  68. package/node_modules/ssh2/lib/protocol/kex.js +1908 -0
  69. package/node_modules/ssh2/lib/protocol/keyParser.js +1484 -0
  70. package/node_modules/ssh2/lib/protocol/node-fs-compat.js +115 -0
  71. package/node_modules/ssh2/lib/protocol/utils.js +356 -0
  72. package/node_modules/ssh2/lib/protocol/zlib.js +255 -0
  73. package/node_modules/ssh2/lib/server.js +1380 -0
  74. package/node_modules/ssh2/lib/utils.js +336 -0
  75. package/node_modules/ssh2/package.json +49 -0
  76. package/node_modules/ssh2/test/common.js +316 -0
  77. package/node_modules/ssh2/test/fixtures/bad_rsa_private_key +26 -0
  78. package/node_modules/ssh2/test/fixtures/https_cert.pem +33 -0
  79. package/node_modules/ssh2/test/fixtures/https_key.pem +52 -0
  80. package/node_modules/ssh2/test/fixtures/id_dsa +12 -0
  81. package/node_modules/ssh2/test/fixtures/id_ecdsa +5 -0
  82. package/node_modules/ssh2/test/fixtures/id_rsa +15 -0
  83. package/node_modules/ssh2/test/fixtures/id_rsa.ppk +26 -0
  84. package/node_modules/ssh2/test/fixtures/id_rsa_enc +30 -0
  85. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa +21 -0
  86. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa.pub +1 -0
  87. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa.pub.result +7 -0
  88. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa.result +7 -0
  89. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc +22 -0
  90. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc.pub +1 -0
  91. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc.pub.result +7 -0
  92. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc.result +7 -0
  93. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc_gcm +23 -0
  94. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc_gcm.pub +1 -0
  95. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc_gcm.pub.result +7 -0
  96. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_dsa_enc_gcm.result +7 -0
  97. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa +9 -0
  98. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa.pub +1 -0
  99. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa.pub.result +7 -0
  100. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa.result +7 -0
  101. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc +10 -0
  102. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc.pub +1 -0
  103. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc.pub.result +8 -0
  104. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc.result +7 -0
  105. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc_gcm +10 -0
  106. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc_gcm.pub +1 -0
  107. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc_gcm.pub.result +8 -0
  108. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ecdsa_enc_gcm.result +7 -0
  109. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ed25519 +7 -0
  110. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ed25519.pub +1 -0
  111. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ed25519.pub.result +7 -0
  112. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_ed25519.result +7 -0
  113. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa +27 -0
  114. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa.pub +1 -0
  115. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa.pub.result +7 -0
  116. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa.result +7 -0
  117. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc +28 -0
  118. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc.pub +1 -0
  119. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc.pub.result +8 -0
  120. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc.result +7 -0
  121. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc_gcm +29 -0
  122. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc_gcm.pub +1 -0
  123. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc_gcm.pub.result +8 -0
  124. package/node_modules/ssh2/test/fixtures/keyParser/openssh_new_rsa_enc_gcm.result +7 -0
  125. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa +12 -0
  126. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa.pub +1 -0
  127. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa.pub.result +7 -0
  128. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa.result +7 -0
  129. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa_enc +15 -0
  130. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa_enc.pub +1 -0
  131. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa_enc.pub.result +8 -0
  132. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_dsa_enc.result +7 -0
  133. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa +5 -0
  134. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa.pub +1 -0
  135. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa.pub.result +7 -0
  136. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa.result +7 -0
  137. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa_enc +8 -0
  138. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa_enc.pub +1 -0
  139. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa_enc.pub.result +8 -0
  140. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_ecdsa_enc.result +7 -0
  141. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa +27 -0
  142. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa.pub +1 -0
  143. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa.pub.result +7 -0
  144. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa.result +7 -0
  145. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc +30 -0
  146. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc.pub +1 -0
  147. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc.pub.result +8 -0
  148. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc.result +7 -0
  149. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc_aes256 +54 -0
  150. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc_aes256.pub +1 -0
  151. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc_aes256.pub.result +6 -0
  152. package/node_modules/ssh2/test/fixtures/keyParser/openssh_old_rsa_enc_aes256.result +6 -0
  153. package/node_modules/ssh2/test/fixtures/keyParser/ppk_dsa_enc +17 -0
  154. package/node_modules/ssh2/test/fixtures/keyParser/ppk_dsa_enc.result +7 -0
  155. package/node_modules/ssh2/test/fixtures/keyParser/ppk_rsa +26 -0
  156. package/node_modules/ssh2/test/fixtures/keyParser/ppk_rsa.result +7 -0
  157. package/node_modules/ssh2/test/fixtures/keyParser/ppk_rsa_enc +18 -0
  158. package/node_modules/ssh2/test/fixtures/keyParser/ppk_rsa_enc.result +7 -0
  159. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa.pub +9 -0
  160. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa.pub.result +7 -0
  161. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa2.pub +10 -0
  162. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa2.pub.result +7 -0
  163. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa3.pub +11 -0
  164. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa3.pub.result +7 -0
  165. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa4.pub +11 -0
  166. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa4.pub.result +7 -0
  167. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa5.pub +8 -0
  168. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa5.pub.result +7 -0
  169. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa6.pub +13 -0
  170. package/node_modules/ssh2/test/fixtures/keyParser/rfc4716_rsa6.pub.result +7 -0
  171. package/node_modules/ssh2/test/fixtures/openssh_new_rsa +27 -0
  172. package/node_modules/ssh2/test/fixtures/ssh_host_dsa_key +12 -0
  173. package/node_modules/ssh2/test/fixtures/ssh_host_ecdsa_key +5 -0
  174. package/node_modules/ssh2/test/fixtures/ssh_host_rsa_key +15 -0
  175. package/node_modules/ssh2/test/test-exec.js +578 -0
  176. package/node_modules/ssh2/test/test-integration-openssh.js +486 -0
  177. package/node_modules/ssh2/test/test-keygen.js +60 -0
  178. package/node_modules/ssh2/test/test-misc-client-server.js +1460 -0
  179. package/node_modules/ssh2/test/test-openssh.js +261 -0
  180. package/node_modules/ssh2/test/test-protocol-crypto.js +631 -0
  181. package/node_modules/ssh2/test/test-protocol-keyparser.js +177 -0
  182. package/node_modules/ssh2/test/test-server-hostkeys.js +138 -0
  183. package/node_modules/ssh2/test/test-sftp.js +842 -0
  184. package/node_modules/ssh2/test/test-shell.js +109 -0
  185. package/node_modules/ssh2/test/test-userauth-agent-openssh.js +110 -0
  186. package/node_modules/ssh2/test/test-userauth-agent.js +171 -0
  187. package/node_modules/ssh2/test/test-userauth.js +611 -0
  188. package/node_modules/ssh2/test/test-worker-imports.js +25 -0
  189. package/node_modules/ssh2/test/test.js +20 -0
  190. package/node_modules/ssh2/util/build_pagent.bat +2 -0
  191. package/node_modules/ssh2/util/pagent.c +88 -0
  192. package/node_modules/ssh2/util/pagent.exe +0 -0
  193. package/node_modules/tweetnacl/.npmignore +4 -0
  194. package/node_modules/tweetnacl/AUTHORS.md +28 -0
  195. package/node_modules/tweetnacl/CHANGELOG.md +221 -0
  196. package/node_modules/tweetnacl/LICENSE +24 -0
  197. package/node_modules/tweetnacl/PULL_REQUEST_TEMPLATE.md +20 -0
  198. package/node_modules/tweetnacl/README.md +459 -0
  199. package/node_modules/tweetnacl/nacl-fast.js +2388 -0
  200. package/node_modules/tweetnacl/nacl-fast.min.js +2 -0
  201. package/node_modules/tweetnacl/nacl.d.ts +98 -0
  202. package/node_modules/tweetnacl/nacl.js +1175 -0
  203. package/node_modules/tweetnacl/nacl.min.js +1 -0
  204. package/node_modules/tweetnacl/package.json +58 -0
  205. package/package.json +10 -2
@@ -0,0 +1,1460 @@
1
+ 'use strict';
2
+
3
+ const assert = require('assert');
4
+ const { createHash } = require('crypto');
5
+ const http = require('http');
6
+ const https = require('https');
7
+ const net = require('net');
8
+ const { Transform } = require('stream');
9
+ const { inspect } = require('util');
10
+
11
+ const Client = require('../lib/client.js');
12
+ const {
13
+ SSHTTPAgent: HTTPAgent,
14
+ SSHTTPSAgent: HTTPSAgent,
15
+ } = require('../lib/http-agents.js');
16
+ const Server = require('../lib/server.js');
17
+ const { KexInit } = require('../lib/protocol/kex.js');
18
+
19
+ const {
20
+ fixture,
21
+ mustCall,
22
+ mustCallAtLeast,
23
+ mustNotCall,
24
+ setup: setup_,
25
+ setupSimple,
26
+ } = require('./common.js');
27
+
28
+ const KEY_RSA_BAD = fixture('bad_rsa_private_key');
29
+ const HOST_RSA_MD5 = '64254520742d3d0792e918f3ce945a64';
30
+ const clientCfg = { username: 'foo', password: 'bar' };
31
+ const serverCfg = { hostKeys: [ fixture('ssh_host_rsa_key') ] };
32
+
33
+ const debug = false;
34
+
35
+ const setup = setupSimple.bind(undefined, debug);
36
+
37
+
38
+ {
39
+ const { server } = setup_(
40
+ 'Verify host fingerprint (sync success, hostHash set)',
41
+ {
42
+ client: {
43
+ ...clientCfg,
44
+ hostHash: 'md5',
45
+ hostVerifier: mustCall((hash) => {
46
+ assert(hash === HOST_RSA_MD5, 'Host fingerprint mismatch');
47
+ return true;
48
+ }),
49
+ },
50
+ server: serverCfg,
51
+ },
52
+ );
53
+
54
+ server.on('connection', mustCall((conn) => {
55
+ conn.on('authentication', mustCall((ctx) => {
56
+ ctx.accept();
57
+ })).on('ready', mustCall(() => {
58
+ conn.end();
59
+ }));
60
+ }));
61
+ }
62
+
63
+ {
64
+ const { server } = setup_(
65
+ 'Verify host fingerprint (sync success, hostHash not set)',
66
+ {
67
+ client: {
68
+ ...clientCfg,
69
+ hostVerifier: mustCall((key) => {
70
+ assert(Buffer.isBuffer(key), 'Expected buffer');
71
+ let hash = createHash('md5');
72
+ hash.update(key);
73
+ hash = hash.digest('hex');
74
+ assert(hash === HOST_RSA_MD5, 'Host fingerprint mismatch');
75
+ return true;
76
+ }),
77
+ },
78
+ server: serverCfg,
79
+ }
80
+ );
81
+
82
+ server.on('connection', mustCall((conn) => {
83
+ conn.on('authentication', mustCall((ctx) => {
84
+ ctx.accept();
85
+ })).on('ready', mustCall(() => {
86
+ conn.end();
87
+ }));
88
+ }));
89
+ }
90
+
91
+ {
92
+ const { server } = setup_(
93
+ 'Verify host fingerprint (async success)',
94
+ {
95
+ client: {
96
+ ...clientCfg,
97
+ hostVerifier: mustCall((key, cb) => {
98
+ assert(Buffer.isBuffer(key), 'Expected buffer');
99
+ let hash = createHash('md5');
100
+ hash.update(key);
101
+ hash = hash.digest('hex');
102
+ assert(hash === HOST_RSA_MD5, 'Host fingerprint mismatch');
103
+ process.nextTick(cb, true);
104
+ }),
105
+ },
106
+ server: serverCfg,
107
+ }
108
+ );
109
+
110
+ server.on('connection', mustCall((conn) => {
111
+ conn.on('authentication', mustCall((ctx) => {
112
+ ctx.accept();
113
+ })).on('ready', mustCall(() => {
114
+ conn.end();
115
+ }));
116
+ }));
117
+ }
118
+
119
+ {
120
+ const { client, server } = setup_(
121
+ 'Verify host fingerprint (sync failure)',
122
+ {
123
+ client: {
124
+ ...clientCfg,
125
+ hostVerifier: mustCall((key) => {
126
+ return false;
127
+ }),
128
+ },
129
+ server: serverCfg,
130
+
131
+ noForceClientReady: true,
132
+ noForceServerReady: true,
133
+ },
134
+ );
135
+
136
+ client.removeAllListeners('error');
137
+ client.on('ready', mustNotCall())
138
+ .on('error', mustCall((err) => {
139
+ assert(/verification failed/.test(err.message),
140
+ 'Wrong client error message');
141
+ }));
142
+
143
+ server.on('connection', mustCall((conn) => {
144
+ conn.removeAllListeners('error');
145
+
146
+ conn.on('authentication', mustNotCall())
147
+ .on('ready', mustNotCall())
148
+ .on('error', mustCall((err) => {
149
+ assert(/KEY_EXCHANGE_FAILED/.test(err.message),
150
+ 'Wrong server error message');
151
+ }));
152
+ }));
153
+ }
154
+
155
+ {
156
+ // connect() on connected client
157
+
158
+ const clientCfg_ = { ...clientCfg };
159
+ const client = new Client();
160
+ const server = new Server(serverCfg);
161
+
162
+ server.listen(0, 'localhost', mustCall(() => {
163
+ clientCfg_.host = 'localhost';
164
+ clientCfg_.port = server.address().port;
165
+ client.connect(clientCfg_);
166
+ }));
167
+
168
+ let connections = 0;
169
+ server.on('connection', mustCall((conn) => {
170
+ if (++connections === 2)
171
+ server.close();
172
+ conn.on('authentication', mustCall((ctx) => {
173
+ ctx.accept();
174
+ })).on('ready', mustCall(() => {}));
175
+ }, 2)).on('close', mustCall(() => {}));
176
+
177
+ let reconnect = false;
178
+ client.on('ready', mustCall(() => {
179
+ if (reconnect) {
180
+ client.end();
181
+ } else {
182
+ reconnect = true;
183
+ client.connect(clientCfg_);
184
+ }
185
+ }, 2)).on('close', mustCall(() => {}, 2));
186
+ }
187
+
188
+ {
189
+ // Throw when not connected
190
+
191
+ const client = new Client({
192
+ username: 'foo',
193
+ password: 'bar',
194
+ });
195
+
196
+ assert.throws(mustCall(() => {
197
+ client.exec('uptime', mustNotCall());
198
+ }));
199
+ }
200
+
201
+ {
202
+ const { client, server } = setup(
203
+ 'Outstanding callbacks called on disconnect'
204
+ );
205
+
206
+ server.on('connection', mustCall((conn) => {
207
+ conn.on('session', mustCall(() => {}, 3));
208
+ }));
209
+
210
+ client.on('ready', mustCall(() => {
211
+ function callback(err, stream) {
212
+ assert(err, 'Expected error');
213
+ assert(err.message === 'No response from server',
214
+ `Wrong error message: ${err.message}`);
215
+ }
216
+ client.exec('uptime', mustCall(callback));
217
+ client.shell(mustCall(callback));
218
+ client.sftp(mustCall(callback));
219
+ client.end();
220
+ }));
221
+ }
222
+
223
+ {
224
+ const { client, server } = setup('Pipelined requests');
225
+
226
+ server.on('connection', mustCall((conn) => {
227
+ conn.on('ready', mustCall(() => {
228
+ conn.on('session', mustCall((accept, reject) => {
229
+ const session = accept();
230
+ session.on('exec', mustCall((accept, reject, info) => {
231
+ const stream = accept();
232
+ stream.exit(0);
233
+ stream.end();
234
+ }));
235
+ }, 3));
236
+ }));
237
+ }));
238
+
239
+ client.on('ready', mustCall(() => {
240
+ let calledBack = 0;
241
+ function callback(err, stream) {
242
+ assert(!err, `Unexpected error: ${err}`);
243
+ stream.resume();
244
+ if (++calledBack === 3)
245
+ client.end();
246
+ }
247
+ client.exec('foo', mustCall(callback));
248
+ client.exec('bar', mustCall(callback));
249
+ client.exec('baz', mustCall(callback));
250
+ }));
251
+ }
252
+
253
+ {
254
+ const { client, server } = setup(
255
+ 'Pipelined requests with intermediate rekeying'
256
+ );
257
+
258
+ server.on('connection', mustCall((conn) => {
259
+ conn.on('ready', mustCall(() => {
260
+ const reqs = [];
261
+ conn.on('session', mustCall((accept, reject) => {
262
+ if (reqs.length === 0) {
263
+ conn.rekey(mustCall((err) => {
264
+ assert(!err, `Unexpected rekey error: ${err}`);
265
+ reqs.forEach((accept) => {
266
+ const session = accept();
267
+ session.on('exec', mustCall((accept, reject, info) => {
268
+ const stream = accept();
269
+ stream.exit(0);
270
+ stream.end();
271
+ }));
272
+ });
273
+ }));
274
+ }
275
+ reqs.push(accept);
276
+ }, 3));
277
+ }));
278
+ }));
279
+
280
+ client.on('ready', mustCall(() => {
281
+ let calledBack = 0;
282
+ function callback(err, stream) {
283
+ assert(!err, `Unexpected error: ${err}`);
284
+ stream.resume();
285
+ if (++calledBack === 3)
286
+ client.end();
287
+ }
288
+ client.exec('foo', mustCall(callback));
289
+ client.exec('bar', mustCall(callback));
290
+ client.exec('baz', mustCall(callback));
291
+ }));
292
+ }
293
+
294
+ {
295
+ const { client, server } = setup('Ignore outgoing after stream close');
296
+
297
+ server.on('connection', mustCall((conn) => {
298
+ conn.on('ready', mustCall(() => {
299
+ conn.on('session', mustCall((accept, reject) => {
300
+ const session = accept();
301
+ session.on('exec', mustCall((accept, reject, info) => {
302
+ const stream = accept();
303
+ stream.exit(0);
304
+ stream.end();
305
+ }));
306
+ }));
307
+ }));
308
+ }));
309
+
310
+ client.on('ready', mustCall(() => {
311
+ client.exec('foo', mustCall((err, stream) => {
312
+ assert(!err, `Unexpected error: ${err}`);
313
+ stream.on('exit', mustCall((code, signal) => {
314
+ client.end();
315
+ }));
316
+ }));
317
+ }));
318
+ }
319
+
320
+ {
321
+ const { client, server } = setup_(
322
+ 'Double pipe on unconnected, passed in net.Socket',
323
+ {
324
+ client: {
325
+ ...clientCfg,
326
+ sock: new net.Socket(),
327
+ },
328
+ server: serverCfg,
329
+ },
330
+ );
331
+
332
+ server.on('connection', mustCall((conn) => {
333
+ conn.on('authentication', mustCall((ctx) => {
334
+ ctx.accept();
335
+ })).on('ready', mustCall(() => {}));
336
+ }));
337
+ client.on('ready', mustCall(() => {
338
+ client.end();
339
+ }));
340
+ }
341
+
342
+ {
343
+ const { client, server } = setup(
344
+ 'Client auto-rejects inbound connections to unknown bound address'
345
+ );
346
+
347
+ const assignedPort = 31337;
348
+
349
+ server.on('connection', mustCall((conn) => {
350
+ conn.on('ready', mustCall(() => {
351
+ conn.on('request', mustCall((accept, reject, name, info) => {
352
+ assert(name === 'tcpip-forward', 'Wrong request name');
353
+ assert.deepStrictEqual(
354
+ info,
355
+ { bindAddr: 'good', bindPort: 0 },
356
+ 'Wrong request info'
357
+ );
358
+ accept(assignedPort);
359
+ conn.forwardOut(info.bindAddr,
360
+ assignedPort,
361
+ 'remote',
362
+ 12345,
363
+ mustCall((err, ch) => {
364
+ assert(!err, `Unexpected error: ${err}`);
365
+ conn.forwardOut('bad',
366
+ assignedPort,
367
+ 'remote',
368
+ 12345,
369
+ mustCall((err, ch) => {
370
+ assert(err, 'Should receive error');
371
+ client.end();
372
+ }));
373
+ }));
374
+ }));
375
+ }));
376
+ }));
377
+
378
+ client.on('ready', mustCall(() => {
379
+ // request forwarding
380
+ client.forwardIn('good', 0, mustCall((err, port) => {
381
+ assert(!err, `Unexpected error: ${err}`);
382
+ assert(port === assignedPort, 'Wrong assigned port');
383
+ }));
384
+ })).on('tcp connection', mustCall((details, accept, reject) => {
385
+ assert.deepStrictEqual(
386
+ details,
387
+ { destIP: 'good',
388
+ destPort: assignedPort,
389
+ srcIP: 'remote',
390
+ srcPort: 12345
391
+ },
392
+ 'Wrong connection details'
393
+ );
394
+ accept();
395
+ }));
396
+ }
397
+
398
+ {
399
+ const { client, server } = setup(
400
+ 'Client auto-rejects inbound connections to unknown bound port'
401
+ );
402
+
403
+ const assignedPort = 31337;
404
+
405
+ server.on('connection', mustCall((conn) => {
406
+ conn.on('ready', mustCall(() => {
407
+ conn.on('request', mustCall((accept, reject, name, info) => {
408
+ assert(name === 'tcpip-forward', 'Wrong request name');
409
+ assert.deepStrictEqual(
410
+ info,
411
+ { bindAddr: 'good', bindPort: 0 },
412
+ 'Wrong request info'
413
+ );
414
+ accept(assignedPort);
415
+ conn.forwardOut(info.bindAddr,
416
+ assignedPort,
417
+ 'remote',
418
+ 12345,
419
+ mustCall((err, ch) => {
420
+ assert(!err, `Unexpected error: ${err}`);
421
+ conn.forwardOut(info.bindAddr,
422
+ 99999,
423
+ 'remote',
424
+ 12345,
425
+ mustCall((err, ch) => {
426
+ assert(err, 'Should receive error');
427
+ client.end();
428
+ }));
429
+ }));
430
+ }));
431
+ }));
432
+ }));
433
+
434
+ client.on('ready', mustCall(() => {
435
+ // request forwarding
436
+ client.forwardIn('good', 0, mustCall((err, port) => {
437
+ assert(!err, `Unexpected error: ${err}`);
438
+ assert(port === assignedPort, 'Wrong assigned port');
439
+ }));
440
+ })).on('tcp connection', mustCall((details, accept, reject) => {
441
+ assert.deepStrictEqual(
442
+ details,
443
+ { destIP: 'good',
444
+ destPort: assignedPort,
445
+ srcIP: 'remote',
446
+ srcPort: 12345
447
+ },
448
+ 'Wrong connection details'
449
+ );
450
+ accept();
451
+ }));
452
+ }
453
+
454
+ {
455
+ const GREETING = 'Hello world!';
456
+
457
+ const { client, server } = setup_(
458
+ 'Server greeting',
459
+ {
460
+ client: {
461
+ ...clientCfg,
462
+ ident: 'node.js rules',
463
+ },
464
+ server: {
465
+ ...serverCfg,
466
+ greeting: GREETING,
467
+ }
468
+ },
469
+ );
470
+
471
+ let sawGreeting = false;
472
+
473
+ server.on('connection', mustCall((conn, info) => {
474
+ assert.deepStrictEqual(info.header, {
475
+ identRaw: 'SSH-2.0-node.js rules',
476
+ greeting: '',
477
+ versions: {
478
+ protocol: '2.0',
479
+ software: 'node.js'
480
+ },
481
+ comments: 'rules'
482
+ });
483
+ conn.on('handshake', mustCall((details) => {
484
+ assert(sawGreeting, 'Client did not see greeting before handshake');
485
+ })).on('authentication', mustCall((ctx) => {
486
+ ctx.accept();
487
+ })).on('ready', mustCall(() => {
488
+ conn.end();
489
+ }));
490
+ }));
491
+
492
+ client.on('greeting', mustCall((greeting) => {
493
+ assert.strictEqual(greeting, `${GREETING}\r\n`);
494
+ sawGreeting = true;
495
+ })).on('banner', mustNotCall());
496
+ }
497
+
498
+ {
499
+ const { client, server } = setup_(
500
+ 'Correct ident parsing',
501
+ {
502
+ client: {
503
+ ...clientCfg,
504
+ ident: 'node.js rules\n',
505
+ },
506
+ server: serverCfg,
507
+
508
+ noServerError: true,
509
+ noClientError: true,
510
+ noForceServerReady: true,
511
+ noForceClientReady: true,
512
+ },
513
+ );
514
+
515
+ server.on('connection', mustCall((conn, info) => {
516
+ assert.deepStrictEqual(info.header, {
517
+ identRaw: 'SSH-2.0-node.js rules',
518
+ greeting: '',
519
+ versions: {
520
+ protocol: '2.0',
521
+ software: 'node.js'
522
+ },
523
+ comments: 'rules'
524
+ });
525
+ conn.once('error', mustCall((err) => {
526
+ assert(/bad packet length/i.test(err.message), 'Wrong error message');
527
+ }));
528
+ conn.on('handshake', mustNotCall())
529
+ .on('authentication', mustNotCall())
530
+ .on('ready', mustNotCall());
531
+ }));
532
+
533
+ client.on('greeting', mustNotCall())
534
+ .on('banner', mustNotCall())
535
+ .on('ready', mustNotCall());
536
+ }
537
+
538
+ {
539
+ const BANNER = 'Hello world!';
540
+
541
+ let authCb;
542
+ const { client, server } = setup_(
543
+ 'Server banner',
544
+ {
545
+ client: {
546
+ ...clientCfg,
547
+ // This test uses a custom auth handler to avoid a race condition where
548
+ // we don't get the complete banner packet before the default auth
549
+ // handler immediately sends the initial auth method
550
+ authHandler: (authsLeft, partialSuccess, cb) => {
551
+ authCb = cb;
552
+ },
553
+ },
554
+ server: {
555
+ ...serverCfg,
556
+ banner: BANNER,
557
+ }
558
+ },
559
+ );
560
+
561
+ let sawBanner = false;
562
+
563
+ server.on('connection', mustCall((conn) => {
564
+ conn.on('handshake', mustCall((details) => {
565
+ assert(!sawBanner, 'Client saw banner too early');
566
+ })).on('authentication', mustCall((ctx) => {
567
+ assert(sawBanner, 'Client did not see banner before auth');
568
+ ctx.accept();
569
+ })).on('ready', mustCall(() => {
570
+ conn.end();
571
+ }));
572
+ }));
573
+
574
+ client.on('greeting', mustNotCall())
575
+ .on('banner', mustCall((message) => {
576
+ assert.strictEqual(message, 'Hello world!\r\n');
577
+ sawBanner = true;
578
+ authCb('password');
579
+ }));
580
+ }
581
+
582
+ {
583
+ const { client, server } = setup(
584
+ 'Server responds to global requests in the right order'
585
+ );
586
+
587
+ function sendAcceptLater(accept) {
588
+ if (fastRejectSent)
589
+ accept();
590
+ else
591
+ setImmediate(sendAcceptLater, accept);
592
+ }
593
+
594
+ let fastRejectSent = false;
595
+
596
+ server.on('connection', mustCall((conn) => {
597
+ conn.on('ready', mustCall(() => {
598
+ conn.on('request', mustCall((accept, reject, name, info) => {
599
+ if (info.bindAddr === 'fastReject') {
600
+ // Will call reject on 'fastReject' soon ...
601
+ reject();
602
+ fastRejectSent = true;
603
+ } else {
604
+ // ... but accept on 'slowAccept' later
605
+ sendAcceptLater(accept);
606
+ }
607
+ }, 2));
608
+ }));
609
+ }));
610
+
611
+ client.on('ready', mustCall(() => {
612
+ let replyCnt = 0;
613
+
614
+ client.forwardIn('slowAccept', 0, mustCall((err) => {
615
+ assert(!err, `Unexpected error: ${err}`);
616
+ if (++replyCnt === 2)
617
+ client.end();
618
+ }));
619
+
620
+ client.forwardIn('fastReject', 0, mustCall((err) => {
621
+ assert(err, 'Expected error');
622
+ if (++replyCnt === 2)
623
+ client.end();
624
+ }));
625
+ }));
626
+ }
627
+
628
+ {
629
+ const { client, server } = setup(
630
+ 'Cleanup outstanding channel requests on channel close'
631
+ );
632
+
633
+ server.on('connection', mustCall((conn) => {
634
+ conn.on('ready', mustCall(() => {
635
+ conn.on('session', mustCall((accept, reject) => {
636
+ const session = accept();
637
+ session.on('subsystem', mustCall((accept, reject, info) => {
638
+ assert(info.name === 'netconf', `Wrong subsystem name: ${info.name}`);
639
+
640
+ // XXX: hack to prevent success reply from being sent
641
+ conn._protocol.channelSuccess = () => {};
642
+
643
+ accept().close();
644
+ }));
645
+ }));
646
+ }));
647
+ }));
648
+
649
+ client.on('ready', mustCall(() => {
650
+ client.subsys('netconf', mustCall((err, stream) => {
651
+ assert(err, 'Expected error');
652
+ client.end();
653
+ }));
654
+ }));
655
+ }
656
+
657
+ {
658
+ const { client, server } = setup_(
659
+ 'Handshake errors are emitted',
660
+ {
661
+ client: {
662
+ ...clientCfg,
663
+ algorithms: { cipher: [ 'aes128-cbc' ] },
664
+ },
665
+ server: {
666
+ ...serverCfg,
667
+ algorithms: { cipher: [ 'aes128-ctr' ] },
668
+ },
669
+
670
+ noForceClientReady: true,
671
+ noForceServerReady: true,
672
+ },
673
+ );
674
+
675
+ client.removeAllListeners('error');
676
+
677
+ function onError(err) {
678
+ assert.strictEqual(err.level, 'handshake');
679
+ assert(/handshake failed/i.test(err.message), 'Wrong error message');
680
+ }
681
+
682
+ server.on('connection', mustCall((conn) => {
683
+ conn.removeAllListeners('error');
684
+
685
+ conn.on('authentication', mustNotCall())
686
+ .on('ready', mustNotCall())
687
+ .on('handshake', mustNotCall())
688
+ .on('error', mustCall(onError))
689
+ .on('close', mustCall(() => {}));
690
+ }));
691
+
692
+ client.on('ready', mustNotCall())
693
+ .on('error', mustCall(onError))
694
+ .on('close', mustCall(() => {}));
695
+ }
696
+
697
+ {
698
+ const { client, server } = setup_(
699
+ 'Client signing errors are caught and emitted',
700
+ {
701
+ client: {
702
+ username: 'foo',
703
+ privateKey: KEY_RSA_BAD,
704
+ },
705
+ server: serverCfg,
706
+
707
+ noForceClientReady: true,
708
+ noForceServerReady: true,
709
+ },
710
+ );
711
+
712
+ client.removeAllListeners('error');
713
+
714
+ server.on('connection', mustCall((conn) => {
715
+ let authAttempt = 0;
716
+ conn.on('authentication', mustCall((ctx) => {
717
+ assert(!ctx.signature, 'Unexpected signature');
718
+ switch (++authAttempt) {
719
+ case 1:
720
+ assert(ctx.method === 'none', `Wrong auth method: ${ctx.method}`);
721
+ return ctx.reject();
722
+ case 2:
723
+ assert(ctx.method === 'publickey',
724
+ `Wrong auth method: ${ctx.method}`);
725
+ ctx.accept();
726
+ break;
727
+ }
728
+ }, 2)).on('ready', mustNotCall()).on('close', mustCall(() => {}));
729
+ }));
730
+
731
+ let cliError;
732
+ client.on('ready', mustNotCall()).on('error', mustCall((err) => {
733
+ if (cliError) {
734
+ assert(/all configured/i.test(err.message), 'Wrong error message');
735
+ } else {
736
+ cliError = err;
737
+ assert(/signing/i.test(err.message), 'Wrong error message');
738
+ }
739
+ }, 2)).on('close', mustCall(() => {}));
740
+ }
741
+
742
+ {
743
+ const { client, server } = setup_(
744
+ 'Server signing errors are caught and emitted',
745
+ {
746
+ client: clientCfg,
747
+ server: { hostKeys: [KEY_RSA_BAD] },
748
+
749
+ noForceClientReady: true,
750
+ noForceServerReady: true,
751
+ },
752
+ );
753
+
754
+ client.removeAllListeners('error');
755
+
756
+ server.on('connection', mustCall((conn) => {
757
+ conn.removeAllListeners('error');
758
+
759
+ conn.on('error', mustCall((err) => {
760
+ assert(/signature generation failed/i.test(err.message),
761
+ 'Wrong error message');
762
+ })).on('authentication', mustNotCall())
763
+ .on('ready', mustNotCall())
764
+ .on('close', mustCall(() => {}));
765
+ }));
766
+
767
+ client.on('ready', mustNotCall()).on('error', mustCall((err) => {
768
+ assert(/KEY_EXCHANGE_FAILED/.test(err.message), 'Wrong error message');
769
+ })).on('close', mustCall(() => {}));
770
+ }
771
+
772
+ {
773
+ const { client, server } = setup_(
774
+ 'Rekeying with AES-GCM',
775
+ {
776
+ client: {
777
+ ...clientCfg,
778
+ algorithms: { cipher: [ 'aes128-gcm@openssh.com' ] },
779
+ },
780
+ server: {
781
+ ...serverCfg,
782
+ algorithms: { cipher: [ 'aes128-gcm@openssh.com' ] },
783
+ },
784
+ },
785
+ );
786
+
787
+ server.on('connection', mustCall((conn) => {
788
+ conn.on('authentication', mustCall((ctx) => {
789
+ ctx.accept();
790
+ })).on('ready', mustCall(() => {
791
+ const reqs = [];
792
+ conn.on('session', mustCall((accept, reject) => {
793
+ if (reqs.length === 0) {
794
+ conn.rekey(mustCall((err) => {
795
+ assert(!err, `Unexpected rekey error: ${err}`);
796
+ reqs.forEach((accept) => {
797
+ const session = accept();
798
+ session.on('exec', mustCall((accept, reject, info) => {
799
+ const stream = accept();
800
+ stream.exit(0);
801
+ stream.end();
802
+ }));
803
+ });
804
+ }));
805
+ }
806
+ reqs.push(accept);
807
+ }, 3));
808
+ }));
809
+ }));
810
+
811
+ client.on('ready', mustCall(() => {
812
+ let calledBack = 0;
813
+ function callback(err, stream) {
814
+ assert(!err, `Unexpected error: ${err}`);
815
+ stream.resume();
816
+ if (++calledBack === 3)
817
+ client.end();
818
+ }
819
+ client.exec('foo', mustCall(callback));
820
+ client.exec('bar', mustCall(callback));
821
+ client.exec('baz', mustCall(callback));
822
+ }));
823
+ }
824
+
825
+ {
826
+ const { client, server } = setup_(
827
+ 'Switch from no compression to compression',
828
+ {
829
+ client: {
830
+ ...clientCfg,
831
+ algorithms: { compress: [ 'none' ] },
832
+ },
833
+ server: {
834
+ ...serverCfg,
835
+ algorithms: { compress: [ 'none', 'zlib@openssh.com' ] },
836
+ },
837
+ },
838
+ );
839
+
840
+ server.on('connection', mustCall((conn) => {
841
+ conn.on('authentication', mustCall((ctx) => {
842
+ ctx.accept();
843
+ })).on('ready', mustCall(() => {
844
+ const reqs = [];
845
+ conn.on('session', mustCall((accept, reject) => {
846
+ if (reqs.length === 0) {
847
+ // XXX: hack to change algorithms after initial handshake
848
+ client._protocol._offer = new KexInit({
849
+ kex: [ 'ecdh-sha2-nistp256' ],
850
+ serverHostKey: [ 'rsa-sha2-256' ],
851
+ cs: {
852
+ cipher: [ 'aes128-gcm@openssh.com' ],
853
+ mac: [],
854
+ compress: [ 'zlib@openssh.com' ],
855
+ lang: [],
856
+ },
857
+ sc: {
858
+ cipher: [ 'aes128-gcm@openssh.com' ],
859
+ mac: [],
860
+ compress: [ 'zlib@openssh.com' ],
861
+ lang: [],
862
+ },
863
+ });
864
+
865
+ conn.rekey(mustCall((err) => {
866
+ assert(!err, `Unexpected rekey error: ${err}`);
867
+ reqs.forEach((accept) => {
868
+ const session = accept();
869
+ session.on('exec', mustCall((accept, reject, info) => {
870
+ const stream = accept();
871
+ stream.exit(0);
872
+ stream.end();
873
+ }));
874
+ });
875
+ }));
876
+ }
877
+ reqs.push(accept);
878
+ }, 3));
879
+ }));
880
+ }));
881
+
882
+ let handshakes = 0;
883
+ client.on('handshake', mustCall((info) => {
884
+ switch (++handshakes) {
885
+ case 1:
886
+ assert(info.cs.compress === 'none', 'wrong compress value');
887
+ assert(info.sc.compress === 'none', 'wrong compress value');
888
+ break;
889
+ case 2:
890
+ assert(info.cs.compress === 'zlib@openssh.com',
891
+ 'wrong compress value');
892
+ assert(info.sc.compress === 'zlib@openssh.com',
893
+ 'wrong compress value');
894
+ break;
895
+ }
896
+ }, 2)).on('ready', mustCall(() => {
897
+ let calledBack = 0;
898
+ function callback(err, stream) {
899
+ assert(!err, `Unexpected error: ${err}`);
900
+ stream.resume();
901
+ if (++calledBack === 3)
902
+ client.end();
903
+ }
904
+ client.exec('foo', mustCall(callback));
905
+ client.exec('bar', mustCall(callback));
906
+ client.exec('baz', mustCall(callback));
907
+ }));
908
+ }
909
+
910
+ {
911
+ const { client, server } = setup_(
912
+ 'Switch from compression to no compression',
913
+ {
914
+ client: {
915
+ ...clientCfg,
916
+ algorithms: { compress: [ 'zlib' ] },
917
+ },
918
+ server: {
919
+ ...serverCfg,
920
+ algorithms: { compress: [ 'zlib', 'none' ] },
921
+ }
922
+ },
923
+ );
924
+
925
+ server.on('connection', mustCall((conn) => {
926
+ conn.on('authentication', mustCall((ctx) => {
927
+ ctx.accept();
928
+ })).on('ready', mustCall(() => {
929
+ const reqs = [];
930
+ conn.on('session', mustCall((accept, reject) => {
931
+ if (reqs.length === 0) {
932
+ // XXX: hack to change algorithms after initial handshake
933
+ client._protocol._offer = new KexInit({
934
+ kex: [ 'ecdh-sha2-nistp256' ],
935
+ serverHostKey: [ 'rsa-sha2-256' ],
936
+ cs: {
937
+ cipher: [ 'aes128-gcm@openssh.com' ],
938
+ mac: [],
939
+ compress: [ 'none' ],
940
+ lang: [],
941
+ },
942
+ sc: {
943
+ cipher: [ 'aes128-gcm@openssh.com' ],
944
+ mac: [],
945
+ compress: [ 'none' ],
946
+ lang: [],
947
+ },
948
+ });
949
+
950
+ conn.rekey(mustCall((err) => {
951
+ assert(!err, `Unexpected rekey error: ${err}`);
952
+ reqs.forEach((accept) => {
953
+ const session = accept();
954
+ session.on('exec', mustCall((accept, reject, info) => {
955
+ const stream = accept();
956
+ stream.exit(0);
957
+ stream.end();
958
+ }));
959
+ });
960
+ }));
961
+ }
962
+ reqs.push(accept);
963
+ }, 3));
964
+ }));
965
+ }));
966
+
967
+ let handshakes = 0;
968
+ client.on('handshake', mustCall((info) => {
969
+ switch (++handshakes) {
970
+ case 1:
971
+ assert(info.cs.compress === 'zlib', 'wrong compress value');
972
+ assert(info.sc.compress === 'zlib', 'wrong compress value');
973
+ break;
974
+ case 2:
975
+ assert(info.cs.compress === 'none', 'wrong compress value');
976
+ assert(info.sc.compress === 'none', 'wrong compress value');
977
+ break;
978
+ }
979
+ }, 2)).on('ready', mustCall(() => {
980
+ let calledBack = 0;
981
+ function callback(err, stream) {
982
+ assert(!err, `Unexpected error: ${err}`);
983
+ stream.resume();
984
+ if (++calledBack === 3)
985
+ client.end();
986
+ }
987
+ client.exec('foo', mustCall(callback));
988
+ client.exec('bar', mustCall(callback));
989
+ client.exec('baz', mustCall(callback));
990
+ }));
991
+ }
992
+
993
+ {
994
+ const { client, server } = setup_(
995
+ 'Large data compression',
996
+ {
997
+ client: {
998
+ ...clientCfg,
999
+ algorithms: { compress: [ 'zlib' ] },
1000
+ },
1001
+ server: {
1002
+ ...serverCfg,
1003
+ algorithms: { compress: [ 'zlib' ] },
1004
+ }
1005
+ },
1006
+ );
1007
+
1008
+ const chunk = Buffer.alloc(1024 * 1024, 'a');
1009
+ const chunkCount = 10;
1010
+
1011
+ server.on('connection', mustCall((conn) => {
1012
+ conn.on('authentication', mustCall((ctx) => {
1013
+ ctx.accept();
1014
+ })).on('ready', mustCall(() => {
1015
+ conn.on('session', mustCall((accept, reject) => {
1016
+ accept().on('exec', mustCall((accept, reject, info) => {
1017
+ const stream = accept();
1018
+ for (let i = 0; i < chunkCount; ++i)
1019
+ stream.write(chunk);
1020
+ stream.exit(0);
1021
+ stream.end();
1022
+ }));
1023
+ }));
1024
+ }));
1025
+ }));
1026
+
1027
+ client.on('ready', mustCall(() => {
1028
+ client.exec('foo', mustCall((err, stream) => {
1029
+ assert(!err, `Unexpected exec error: ${err}`);
1030
+ let nb = 0;
1031
+ stream.on('data', mustCallAtLeast((data) => {
1032
+ nb += data.length;
1033
+ })).on('end', mustCall(() => {
1034
+ assert(nb === (chunkCount * chunk.length),
1035
+ `Wrong stream byte count: ${nb}`);
1036
+ client.end();
1037
+ }));
1038
+ }));
1039
+ }));
1040
+ }
1041
+
1042
+ {
1043
+ const { client, server } = setup_(
1044
+ 'Debug output',
1045
+ {
1046
+ client: {
1047
+ ...clientCfg,
1048
+ debug: mustCallAtLeast((msg) => {
1049
+ assert(typeof msg === 'string',
1050
+ `Wrong debug argument type: ${typeof msg}`);
1051
+ assert(msg.length > 0, 'Unexpected empty debug message');
1052
+ }),
1053
+ },
1054
+ server: {
1055
+ ...serverCfg,
1056
+ debug: mustCallAtLeast((msg) => {
1057
+ assert(typeof msg === 'string',
1058
+ `Wrong debug argument type: ${typeof msg}`);
1059
+ assert(msg.length > 0, 'Unexpected empty debug message');
1060
+ }),
1061
+ },
1062
+ },
1063
+ );
1064
+
1065
+ server.on('connection', mustCall((conn) => {
1066
+ conn.on('authentication', mustCall((ctx) => {
1067
+ ctx.accept();
1068
+ })).on('ready', mustCall(() => {
1069
+ conn.on('session', mustCall((accept, reject) => {
1070
+ accept().on('exec', mustCall((accept, reject, info) => {
1071
+ assert(info.command === 'foo --bar',
1072
+ `Wrong exec command: ${info.command}`);
1073
+ const stream = accept();
1074
+ stream.exit(100);
1075
+ stream.end();
1076
+ conn.end();
1077
+ }));
1078
+ }));
1079
+ }));
1080
+ }));
1081
+
1082
+ client.on('ready', mustCall(() => {
1083
+ client.exec('foo --bar', mustCall((err, stream) => {
1084
+ assert(!err, `Unexpected exec error: ${err}`);
1085
+ stream.resume();
1086
+ }));
1087
+ }));
1088
+ }
1089
+
1090
+ {
1091
+ const { server } = setup_(
1092
+ 'HTTP agent',
1093
+ {
1094
+ // No automatic client, the agent will create one
1095
+
1096
+ server: serverCfg,
1097
+
1098
+ debug,
1099
+ },
1100
+ );
1101
+
1102
+ let httpServer;
1103
+ server.on('listening', () => {
1104
+ httpServer = http.createServer((req, res) => {
1105
+ httpServer.close();
1106
+ res.end('hello world!');
1107
+ });
1108
+ httpServer.listen(0, 'localhost', () => {
1109
+ const agent = new HTTPAgent({
1110
+ host: 'localhost',
1111
+ port: server.address().port,
1112
+ username: 'foo',
1113
+ password: 'bar',
1114
+ });
1115
+ http.get({
1116
+ host: 'localhost',
1117
+ port: httpServer.address().port,
1118
+ agent,
1119
+ headers: { Connection: 'close' },
1120
+ }, (res) => {
1121
+ assert(res.statusCode === 200,
1122
+ `Wrong http status code: ${res.statusCode}`);
1123
+ let buf = '';
1124
+ res.on('data', mustCallAtLeast((chunk) => {
1125
+ buf += chunk;
1126
+ })).on('end', mustCall(() => {
1127
+ assert(buf === 'hello world!',
1128
+ `Wrong http response body: ${inspect(buf)}`);
1129
+ }));
1130
+ });
1131
+ });
1132
+ });
1133
+
1134
+ server.on('connection', mustCall((conn) => {
1135
+ conn.on('authentication', mustCall((ctx) => {
1136
+ ctx.accept();
1137
+ })).on('ready', mustCall(() => {
1138
+ conn.on('tcpip', mustCall((accept, reject, info) => {
1139
+ assert(info.destIP === 'localhost', `Wrong destIP: ${info.destIP}`);
1140
+ assert(info.destPort === httpServer.address().port,
1141
+ `Wrong destPort: ${info.destPort}`);
1142
+ assert(info.srcIP === 'localhost', `Wrong srcIP: ${info.srcIP}`);
1143
+
1144
+ const stream = accept();
1145
+ const tcp = new net.Socket();
1146
+ tcp.pipe(stream).pipe(tcp);
1147
+ tcp.connect(httpServer.address().port, 'localhost');
1148
+ }));
1149
+ }));
1150
+ }));
1151
+ }
1152
+
1153
+ {
1154
+ const { server } = setup_(
1155
+ 'HTTPS agent',
1156
+ {
1157
+ // No automatic client, the agent will create one
1158
+
1159
+ server: serverCfg,
1160
+
1161
+ debug,
1162
+ },
1163
+ );
1164
+
1165
+ let httpsServer;
1166
+ server.on('listening', () => {
1167
+ httpsServer = https.createServer({
1168
+ key: fixture('https_key.pem'),
1169
+ cert: fixture('https_cert.pem'),
1170
+ }, (req, res) => {
1171
+ httpsServer.close();
1172
+ res.end('hello world!');
1173
+ });
1174
+ httpsServer.listen(0, 'localhost', () => {
1175
+ const agent = new HTTPSAgent({
1176
+ host: 'localhost',
1177
+ port: server.address().port,
1178
+ username: 'foo',
1179
+ password: 'bar',
1180
+ });
1181
+ https.get({
1182
+ host: 'localhost',
1183
+ port: httpsServer.address().port,
1184
+ agent,
1185
+ headers: { Connection: 'close' },
1186
+ ca: fixture('https_cert.pem'),
1187
+ }, (res) => {
1188
+ assert(res.statusCode === 200,
1189
+ `Wrong http status code: ${res.statusCode}`);
1190
+ let buf = '';
1191
+ res.on('data', mustCallAtLeast((chunk) => {
1192
+ buf += chunk;
1193
+ })).on('end', mustCall(() => {
1194
+ assert(buf === 'hello world!',
1195
+ `Wrong http response body: ${inspect(buf)}`);
1196
+ }));
1197
+ }).on('error', (err) => {
1198
+ // This workaround is necessary for some reason on node < v14.x
1199
+ if (!/write after end/i.test(err.message))
1200
+ throw err;
1201
+ });
1202
+ });
1203
+ });
1204
+
1205
+ server.on('connection', mustCall((conn) => {
1206
+ conn.on('authentication', mustCall((ctx) => {
1207
+ ctx.accept();
1208
+ })).on('ready', mustCall(() => {
1209
+ conn.on('tcpip', mustCall((accept, reject, info) => {
1210
+ assert(info.destIP === 'localhost', `Wrong destIP: ${info.destIP}`);
1211
+ assert(info.destPort === httpsServer.address().port,
1212
+ `Wrong destPort: ${info.destPort}`);
1213
+ assert(info.srcIP === 'localhost', `Wrong srcIP: ${info.srcIP}`);
1214
+
1215
+ const stream = accept();
1216
+ const tcp = new net.Socket();
1217
+ tcp.pipe(stream).pipe(tcp);
1218
+ tcp.connect(httpsServer.address().port, 'localhost');
1219
+ }));
1220
+ }));
1221
+ }));
1222
+ }
1223
+
1224
+ [
1225
+ { desc: 'remove/append/prepend (regexps)',
1226
+ config: {
1227
+ remove: /.*/,
1228
+ append: /gcm/,
1229
+ prepend: /ctr/,
1230
+ },
1231
+ expected: [
1232
+ 'aes128-ctr',
1233
+ 'aes192-ctr',
1234
+ 'aes256-ctr',
1235
+ 'aes128-gcm@openssh.com',
1236
+ 'aes256-gcm@openssh.com',
1237
+ 'aes128-gcm',
1238
+ 'aes256-gcm',
1239
+ ],
1240
+ },
1241
+ { desc: 'remove/append/prepend (strings)',
1242
+ config: {
1243
+ remove: /.*/,
1244
+ append: 'aes256-ctr',
1245
+ prepend: [ 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com' ],
1246
+ },
1247
+ expected: [
1248
+ 'aes256-gcm@openssh.com',
1249
+ 'aes128-gcm@openssh.com',
1250
+ 'aes256-ctr',
1251
+ ],
1252
+ },
1253
+ ].forEach((info) => {
1254
+ const { client, server } = setup_(
1255
+ `Client algorithms option (${info.desc})`,
1256
+ {
1257
+ client: {
1258
+ ...clientCfg,
1259
+ algorithms: { cipher: info.config },
1260
+ },
1261
+ server: serverCfg,
1262
+
1263
+ debug,
1264
+ },
1265
+ );
1266
+
1267
+ server.on('connection', mustCall((conn) => {
1268
+ conn.on('authentication', mustCall((ctx) => {
1269
+ ctx.accept();
1270
+ })).on('ready', mustCall(() => {
1271
+ conn.end();
1272
+ }));
1273
+ }));
1274
+ client.on('ready', mustCall(() => {
1275
+ // XXX: hack to easily verify computed offer
1276
+ const offer = client._protocol._offer.lists;
1277
+ assert.deepStrictEqual(
1278
+ offer.cs.cipher.array,
1279
+ info.expected,
1280
+ `Wrong algorithm list: ${offer.cs.cipher.array}`
1281
+ );
1282
+ }));
1283
+ });
1284
+
1285
+ {
1286
+ const { client } = setup_(
1287
+ `Safely end() from Client 'error' event handler`,
1288
+ {
1289
+ client: clientCfg,
1290
+ noClientError: true,
1291
+ noForceClientReady: true,
1292
+ },
1293
+ );
1294
+
1295
+ const badServer = net.createServer((s) => {});
1296
+ badServer.listen(0, 'localhost', mustCall(() => {
1297
+ badServer.unref();
1298
+
1299
+ client.on('error', mustCallAtLeast((err) => {
1300
+ client.end();
1301
+ })).on('ready', mustNotCall()).on('close', mustCall(() => {}));
1302
+ client.connect({
1303
+ host: 'localhost',
1304
+ port: badServer.address().port,
1305
+ user: 'foo',
1306
+ password: 'bar',
1307
+ readyTimeout: 1,
1308
+ });
1309
+ }));
1310
+ }
1311
+
1312
+ {
1313
+ const { client } = setup_(
1314
+ 'Client error should be emitted on bad/nonexistent greeting',
1315
+ {
1316
+ client: clientCfg,
1317
+ noClientError: true,
1318
+ noForceClientReady: true,
1319
+ },
1320
+ );
1321
+
1322
+ const badServer = net.createServer(mustCall((s) => {
1323
+ badServer.close();
1324
+ s.end();
1325
+ })).listen(0, 'localhost', mustCall(() => {
1326
+ client.on('error', mustCall((err) => {
1327
+ client.end();
1328
+ })).on('ready', mustNotCall()).on('close', mustCall(() => {}));
1329
+ client.connect({
1330
+ host: 'localhost',
1331
+ port: badServer.address().port,
1332
+ user: 'foo',
1333
+ password: 'bar',
1334
+ });
1335
+ }));
1336
+ }
1337
+
1338
+ {
1339
+ const { client } = setup_(
1340
+ 'Only one client error on connection failure',
1341
+ {
1342
+ client: clientCfg,
1343
+ noClientError: true,
1344
+ noForceClientReady: true,
1345
+ },
1346
+ );
1347
+
1348
+ client.on('error', mustCall((err) => {
1349
+ assert.strictEqual(err.syscall, 'getaddrinfo');
1350
+ }));
1351
+ client.connect({
1352
+ host: 'blerbblubblubblerb',
1353
+ port: 9999,
1354
+ user: 'foo',
1355
+ password: 'bar'
1356
+ });
1357
+ }
1358
+
1359
+ {
1360
+ const { client, server } = setup(
1361
+ 'Client should remove reserved channels on incoming channel rejection'
1362
+ );
1363
+
1364
+ const assignedPort = 31337;
1365
+
1366
+ server.on('connection', mustCall((conn) => {
1367
+ conn.on('ready', mustCall(() => {
1368
+ conn.on('request', mustCall((accept, reject, name, info) => {
1369
+ assert(name === 'tcpip-forward', 'Wrong request name');
1370
+ assert.deepStrictEqual(
1371
+ info,
1372
+ { bindAddr: 'good', bindPort: 0 },
1373
+ 'Wrong request info'
1374
+ );
1375
+ accept(assignedPort);
1376
+ conn.forwardOut(info.bindAddr,
1377
+ assignedPort,
1378
+ 'remote',
1379
+ 12345,
1380
+ mustCall((err, ch) => {
1381
+ assert(err, 'Should receive error');
1382
+ client.end();
1383
+ }));
1384
+ }));
1385
+ }));
1386
+ }));
1387
+
1388
+ client.on('ready', mustCall(() => {
1389
+ // request forwarding
1390
+ client.forwardIn('good', 0, mustCall((err, port) => {
1391
+ assert(!err, `Unexpected error: ${err}`);
1392
+ assert(port === assignedPort, 'Wrong assigned port');
1393
+ }));
1394
+ })).on('tcp connection', mustCall((details, accept, reject) => {
1395
+ assert.deepStrictEqual(
1396
+ details,
1397
+ { destIP: 'good',
1398
+ destPort: assignedPort,
1399
+ srcIP: 'remote',
1400
+ srcPort: 12345
1401
+ },
1402
+ 'Wrong connection details'
1403
+ );
1404
+ assert.strictEqual(Object.keys(client._chanMgr._channels).length, 1);
1405
+ assert.strictEqual(client._chanMgr._count, 1);
1406
+ reject();
1407
+ assert.strictEqual(Object.keys(client._chanMgr._channels).length, 0);
1408
+ assert.strictEqual(client._chanMgr._count, 0);
1409
+ }));
1410
+ }
1411
+
1412
+ {
1413
+ // Allow injected sockets
1414
+
1415
+ const socket = new Transform({
1416
+ emitClose: true,
1417
+ autoDestroy: true,
1418
+ transform: (chunk, encoding, cb) => {
1419
+ cb();
1420
+ },
1421
+ });
1422
+ socket.remoteAddress = '127.0.0.1';
1423
+ socket.remotePort = '12345';
1424
+ socket.remoteFamily = 'IPv4';
1425
+ socket.push(Buffer.from('SSH-2.0-foo\r\n'));
1426
+
1427
+ const server = new Server(serverCfg);
1428
+ server.on('connection', mustCall((conn, info) => {
1429
+ assert.strictEqual(info.header.versions.software, 'foo');
1430
+ assert.strictEqual(info.ip, '127.0.0.1');
1431
+ assert.strictEqual(info.port, '12345');
1432
+ assert.strictEqual(info.family, 'IPv4');
1433
+ conn.on('ready', mustNotCall());
1434
+ conn.on('close', mustCall());
1435
+ socket.end();
1436
+ }));
1437
+ server.injectSocket(socket);
1438
+ }
1439
+
1440
+ {
1441
+ const { client, server } = setup(
1442
+ 'Server should not error when cleaning up client bare session channels'
1443
+ );
1444
+
1445
+ server.on('connection', mustCall((conn) => {
1446
+ conn.on('session', mustCall((accept, reject) => {
1447
+ accept().on('exec', mustCall((accept, reject, info) => {
1448
+ assert(info.command === 'uptime',
1449
+ `Wrong exec command: ${info.command}`);
1450
+ client.end();
1451
+ }));
1452
+ }));
1453
+ }));
1454
+
1455
+ client.on('ready', mustCall(() => {
1456
+ client.exec('uptime', mustCall((err) => {
1457
+ assert(err instanceof Error);
1458
+ }));
1459
+ }));
1460
+ }