test-exec.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. 'use strict';
  2. const assert = require('assert');
  3. const { inspect } = require('util');
  4. const {
  5. mustCall,
  6. mustCallAtLeast,
  7. setupSimple,
  8. } = require('./common.js');
  9. const DEBUG = false;
  10. const setup = setupSimple.bind(undefined, DEBUG);
  11. {
  12. const { client, server } = setup('Simple exec()');
  13. const COMMAND = 'foo --bar';
  14. const STDOUT_DATA = 'stdout data!\n';
  15. const STDERR_DATA = 'stderr data!\n';
  16. server.on('connection', mustCall((conn) => {
  17. conn.on('ready', mustCall(() => {
  18. conn.on('session', mustCall((accept, reject) => {
  19. accept().on('exec', mustCall((accept, reject, info) => {
  20. assert(info.command === COMMAND,
  21. `Wrong exec command: ${info.command}`);
  22. const stream = accept();
  23. stream.stderr.write(STDERR_DATA);
  24. stream.write(STDOUT_DATA);
  25. stream.exit(100);
  26. stream.end();
  27. conn.end();
  28. }));
  29. }));
  30. }));
  31. }));
  32. client.on('ready', mustCall(() => {
  33. let out = '';
  34. let outErr = '';
  35. const events = [];
  36. const EXPECTED_EVENTS = [ 'exit', 'close' ];
  37. const EXPECTED_EXIT_CLOSE_ARGS = [ 100 ];
  38. client.on('close', mustCall(() => {
  39. assert(out === STDOUT_DATA, `Wrong stdout data: ${inspect(out)}`);
  40. assert(outErr === STDERR_DATA, `Wrong stderr data: ${inspect(outErr)}`);
  41. assert.deepStrictEqual(
  42. events,
  43. EXPECTED_EVENTS,
  44. `Wrong command event order: ${events}`
  45. );
  46. })).exec(COMMAND, mustCall((err, stream) => {
  47. assert(!err, `Unexpected exec error: ${err}`);
  48. stream.on('data', mustCallAtLeast((d) => {
  49. out += d;
  50. })).on('exit', mustCall((...args) => {
  51. assert.deepStrictEqual(args,
  52. EXPECTED_EXIT_CLOSE_ARGS,
  53. `Wrong exit args: ${inspect(args)}`);
  54. events.push('exit');
  55. })).on('close', mustCall((...args) => {
  56. assert.deepStrictEqual(args,
  57. EXPECTED_EXIT_CLOSE_ARGS,
  58. `Wrong close args: ${inspect(args)}`);
  59. events.push('close');
  60. })).stderr.on('data', mustCallAtLeast((d) => {
  61. outErr += d;
  62. }));
  63. }));
  64. }));
  65. }
  66. {
  67. const { client, server } = setup('Simple exec() (exit signal)');
  68. const COMMAND = 'foo --bar';
  69. const STDOUT_DATA = 'stdout data!\n';
  70. const STDERR_DATA = 'stderr data!\n';
  71. server.on('connection', mustCall((conn) => {
  72. conn.on('ready', mustCall(() => {
  73. conn.on('session', mustCall((accept, reject) => {
  74. accept().on('exec', mustCall((accept, reject, info) => {
  75. assert(info.command === COMMAND,
  76. `Wrong exec command: ${info.command}`);
  77. const stream = accept();
  78. stream.stderr.write(STDERR_DATA);
  79. stream.write(STDOUT_DATA);
  80. assert.throws(() => stream.exit('SIGFAKE'));
  81. stream.exit('SIGKILL');
  82. stream.end();
  83. conn.end();
  84. }));
  85. }));
  86. }));
  87. }));
  88. client.on('ready', mustCall(() => {
  89. let out = '';
  90. let outErr = '';
  91. const events = [];
  92. const EXPECTED_EVENTS = [ 'exit', 'close' ];
  93. const EXPECTED_EXIT_CLOSE_ARGS = [ null, 'SIGKILL', false, '' ];
  94. client.on('close', mustCall(() => {
  95. assert(out === STDOUT_DATA, `Wrong stdout data: ${inspect(out)}`);
  96. assert(outErr === STDERR_DATA, `Wrong stderr data: ${inspect(outErr)}`);
  97. assert.deepStrictEqual(
  98. events,
  99. EXPECTED_EVENTS,
  100. `Wrong command event order: ${events}`
  101. );
  102. })).exec(COMMAND, mustCall((err, stream) => {
  103. assert(!err, `Unexpected exec error: ${err}`);
  104. stream.on('data', mustCallAtLeast((d) => {
  105. out += d;
  106. })).on('exit', mustCall((...args) => {
  107. assert.deepStrictEqual(args,
  108. EXPECTED_EXIT_CLOSE_ARGS,
  109. `Wrong exit args: ${inspect(args)}`);
  110. events.push('exit');
  111. })).on('close', mustCall((...args) => {
  112. assert.deepStrictEqual(args,
  113. EXPECTED_EXIT_CLOSE_ARGS,
  114. `Wrong close args: ${inspect(args)}`);
  115. events.push('close');
  116. })).stderr.on('data', mustCallAtLeast((d) => {
  117. outErr += d;
  118. }));
  119. }));
  120. }));
  121. }
  122. {
  123. const { client, server } = setup('Simple exec() (exit signal -- no "SIG")');
  124. const COMMAND = 'foo --bar';
  125. const STDOUT_DATA = 'stdout data!\n';
  126. const STDERR_DATA = 'stderr data!\n';
  127. server.on('connection', mustCall((conn) => {
  128. conn.on('ready', mustCall(() => {
  129. conn.on('session', mustCall((accept, reject) => {
  130. accept().on('exec', mustCall((accept, reject, info) => {
  131. assert(info.command === COMMAND,
  132. `Wrong exec command: ${info.command}`);
  133. const stream = accept();
  134. stream.stderr.write(STDERR_DATA);
  135. stream.write(STDOUT_DATA);
  136. assert.throws(() => stream.exit('FAKE'));
  137. stream.exit('KILL');
  138. stream.end();
  139. conn.end();
  140. }));
  141. }));
  142. }));
  143. }));
  144. client.on('ready', mustCall(() => {
  145. let out = '';
  146. let outErr = '';
  147. const events = [];
  148. const EXPECTED_EVENTS = [ 'exit', 'close' ];
  149. const EXPECTED_EXIT_CLOSE_ARGS = [ null, 'SIGKILL', false, '' ];
  150. client.on('close', mustCall(() => {
  151. assert(out === STDOUT_DATA, `Wrong stdout data: ${inspect(out)}`);
  152. assert(outErr === STDERR_DATA, `Wrong stderr data: ${inspect(outErr)}`);
  153. assert.deepStrictEqual(
  154. events,
  155. EXPECTED_EVENTS,
  156. `Wrong command event order: ${events}`
  157. );
  158. })).exec(COMMAND, mustCall((err, stream) => {
  159. assert(!err, `Unexpected exec error: ${err}`);
  160. stream.on('data', mustCallAtLeast((d) => {
  161. out += d;
  162. })).on('exit', mustCall((...args) => {
  163. assert.deepStrictEqual(args,
  164. EXPECTED_EXIT_CLOSE_ARGS,
  165. `Wrong exit args: ${inspect(args)}`);
  166. events.push('exit');
  167. })).on('close', mustCall((...args) => {
  168. assert.deepStrictEqual(args,
  169. EXPECTED_EXIT_CLOSE_ARGS,
  170. `Wrong close args: ${inspect(args)}`);
  171. events.push('close');
  172. })).stderr.on('data', mustCallAtLeast((d) => {
  173. outErr += d;
  174. }));
  175. }));
  176. }));
  177. }
  178. {
  179. const { client, server } = setup('Exec with signal()');
  180. const COMMAND = 'foo --bar';
  181. const STDOUT_DATA = 'stdout data!\n';
  182. const STDERR_DATA = 'stderr data!\n';
  183. server.on('connection', mustCall((conn) => {
  184. conn.on('ready', mustCall(() => {
  185. conn.on('session', mustCall((accept, reject) => {
  186. let stream;
  187. accept().on('exec', mustCall((accept, reject, info) => {
  188. assert(info.command === COMMAND,
  189. `Wrong exec command: ${info.command}`);
  190. stream = accept();
  191. stream.stderr.write(STDERR_DATA);
  192. stream.write(STDOUT_DATA);
  193. })).on('signal', mustCall((accept, reject, info) => {
  194. assert(info.name === 'INT', `Wrong client signal name: ${info.name}`);
  195. stream.exit(100);
  196. stream.end();
  197. conn.end();
  198. }));
  199. }));
  200. }));
  201. }));
  202. client.on('ready', mustCall(() => {
  203. let out = '';
  204. let outErr = '';
  205. const events = [];
  206. const EXPECTED_EVENTS = [ 'exit', 'close' ];
  207. const EXPECTED_EXIT_CLOSE_ARGS = [ 100 ];
  208. client.on('close', mustCall(() => {
  209. assert(out === STDOUT_DATA, `Wrong stdout data: ${inspect(out)}`);
  210. assert(outErr === STDERR_DATA, `Wrong stderr data: ${inspect(outErr)}`);
  211. assert.deepStrictEqual(
  212. events,
  213. EXPECTED_EVENTS,
  214. `Wrong command event order: ${events}`
  215. );
  216. })).exec(COMMAND, mustCall((err, stream) => {
  217. assert(!err, `Unexpected exec error: ${err}`);
  218. const sendSignal = (() => {
  219. let sent = false;
  220. return () => {
  221. if (sent)
  222. return;
  223. sent = true;
  224. assert.throws(() => stream.signal('FAKE'));
  225. assert.throws(() => stream.signal('SIGFAKE'));
  226. stream.signal('SIGINT');
  227. };
  228. })();
  229. stream.on('data', mustCallAtLeast((d) => {
  230. out += d;
  231. sendSignal();
  232. })).on('exit', mustCall((...args) => {
  233. assert.deepStrictEqual(args,
  234. EXPECTED_EXIT_CLOSE_ARGS,
  235. `Wrong exit args: ${inspect(args)}`);
  236. events.push('exit');
  237. })).on('close', mustCall((...args) => {
  238. assert.deepStrictEqual(args,
  239. EXPECTED_EXIT_CLOSE_ARGS,
  240. `Wrong close args: ${inspect(args)}`);
  241. events.push('close');
  242. })).stderr.on('data', mustCallAtLeast((d) => {
  243. outErr += d;
  244. }));
  245. }));
  246. }));
  247. }
  248. {
  249. const { client, server } = setup('Exec with signal() -- no "SIG"');
  250. const COMMAND = 'foo --bar';
  251. const STDOUT_DATA = 'stdout data!\n';
  252. const STDERR_DATA = 'stderr data!\n';
  253. server.on('connection', mustCall((conn) => {
  254. conn.on('ready', mustCall(() => {
  255. conn.on('session', mustCall((accept, reject) => {
  256. let stream;
  257. accept().on('exec', mustCall((accept, reject, info) => {
  258. assert(info.command === COMMAND,
  259. `Wrong exec command: ${info.command}`);
  260. stream = accept();
  261. stream.stderr.write(STDERR_DATA);
  262. stream.write(STDOUT_DATA);
  263. })).on('signal', mustCall((accept, reject, info) => {
  264. assert(info.name === 'INT', `Wrong client signal name: ${info.name}`);
  265. stream.exit(100);
  266. stream.end();
  267. conn.end();
  268. }));
  269. }));
  270. }));
  271. }));
  272. client.on('ready', mustCall(() => {
  273. let out = '';
  274. let outErr = '';
  275. const events = [];
  276. const EXPECTED_EVENTS = [ 'exit', 'close' ];
  277. const EXPECTED_EXIT_CLOSE_ARGS = [ 100 ];
  278. client.on('close', mustCall(() => {
  279. assert(out === STDOUT_DATA, `Wrong stdout data: ${inspect(out)}`);
  280. assert(outErr === STDERR_DATA, `Wrong stderr data: ${inspect(outErr)}`);
  281. assert.deepStrictEqual(
  282. events,
  283. EXPECTED_EVENTS,
  284. `Wrong command event order: ${events}`
  285. );
  286. })).exec(COMMAND, mustCall((err, stream) => {
  287. assert(!err, `Unexpected exec error: ${err}`);
  288. const sendSignal = (() => {
  289. let sent = false;
  290. return () => {
  291. if (sent)
  292. return;
  293. sent = true;
  294. stream.signal('INT');
  295. };
  296. })();
  297. stream.on('data', mustCallAtLeast((d) => {
  298. out += d;
  299. sendSignal();
  300. })).on('exit', mustCall((...args) => {
  301. assert.deepStrictEqual(args,
  302. EXPECTED_EXIT_CLOSE_ARGS,
  303. `Wrong exit args: ${inspect(args)}`);
  304. events.push('exit');
  305. })).on('close', mustCall((...args) => {
  306. assert.deepStrictEqual(args,
  307. EXPECTED_EXIT_CLOSE_ARGS,
  308. `Wrong close args: ${inspect(args)}`);
  309. events.push('close');
  310. })).stderr.on('data', mustCallAtLeast((d) => {
  311. outErr += d;
  312. }));
  313. }));
  314. }));
  315. }
  316. {
  317. const { client, server } = setup('Exec with environment set');
  318. const env = { SSH2NODETEST: 'foo' };
  319. server.on('connection', mustCall((conn) => {
  320. conn.on('ready', mustCall(() => {
  321. conn.on('session', mustCall((accept, reject) => {
  322. accept().on('env', mustCall((accept, reject, info) => {
  323. accept && accept();
  324. assert(info.key === Object.keys(env)[0],
  325. 'Wrong env key');
  326. assert(info.val === Object.values(env)[0],
  327. 'Wrong env value');
  328. })).on('exec', mustCall((accept, reject, info) => {
  329. assert(info.command === 'foo --bar',
  330. `Wrong exec command: ${info.command}`);
  331. const stream = accept();
  332. stream.exit(100);
  333. stream.end();
  334. conn.end();
  335. }));
  336. }));
  337. }));
  338. }));
  339. client.on('ready', mustCall(() => {
  340. client.exec('foo --bar', { env }, mustCall((err, stream) => {
  341. assert(!err, `Unexpected exec error: ${err}`);
  342. stream.resume();
  343. }));
  344. }));
  345. }
  346. {
  347. const { client, server } = setup('Exec with setWindow()');
  348. const dimensions = {
  349. rows: 60,
  350. cols: 115,
  351. height: 480,
  352. width: 640,
  353. };
  354. server.on('connection', mustCall((conn) => {
  355. conn.on('ready', mustCall(() => {
  356. conn.on('session', mustCall((accept, reject) => {
  357. accept().on('window-change', mustCall((accept, reject, info) => {
  358. accept && accept();
  359. assert.deepStrictEqual(info, dimensions, 'Wrong dimensions');
  360. })).on('exec', mustCall((accept, reject, info) => {
  361. assert(info.command === 'foo --bar',
  362. `Wrong exec command: ${info.command}`);
  363. const stream = accept();
  364. stream.exit(100);
  365. stream.end();
  366. conn.end();
  367. }));
  368. }));
  369. }));
  370. }));
  371. client.on('ready', mustCall(() => {
  372. client.exec('foo --bar', mustCall((err, stream) => {
  373. assert(!err, `Unexpected exec error: ${err}`);
  374. stream.setWindow(...Object.values(dimensions));
  375. stream.resume();
  376. }));
  377. }));
  378. }
  379. {
  380. const { client, server } = setup('Exec with pty set');
  381. const pty = {
  382. rows: 2,
  383. cols: 4,
  384. width: 0,
  385. height: 0,
  386. term: 'vt220',
  387. modes: {},
  388. };
  389. server.on('connection', mustCall((conn) => {
  390. conn.on('ready', mustCall(() => {
  391. conn.on('session', mustCall((accept, reject) => {
  392. let sawPty = false;
  393. accept().on('pty', mustCall((accept, reject, info) => {
  394. assert.deepStrictEqual(info, pty, 'Wrong pty info');
  395. sawPty = true;
  396. accept && accept();
  397. })).on('exec', mustCall((accept, reject, info) => {
  398. assert(sawPty, 'Expected pty to be set up');
  399. assert(info.command === 'foo --bar',
  400. `Wrong exec command: ${info.command}`);
  401. const stream = accept();
  402. stream.exit(100);
  403. stream.end();
  404. conn.end();
  405. }));
  406. }));
  407. }));
  408. }));
  409. client.on('ready', mustCall(() => {
  410. client.exec('foo --bar', { pty }, mustCall((err, stream) => {
  411. assert(!err, `Unexpected exec error: ${err}`);
  412. stream.resume();
  413. }));
  414. }));
  415. }
  416. {
  417. const { client, server } = setup('Exec with X11 forwarding');
  418. server.on('connection', mustCall((conn) => {
  419. conn.on('ready', mustCall(() => {
  420. conn.on('session', mustCall((accept, reject) => {
  421. let sawX11 = false;
  422. accept().on('x11', mustCall((accept, reject, info) => {
  423. assert.strictEqual(info.single,
  424. false,
  425. `Wrong x11 single: ${info.single}`);
  426. assert.strictEqual(info.screen,
  427. 0,
  428. `Wrong x11 screen: ${info.screen}`);
  429. assert.strictEqual(info.protocol,
  430. 'MIT-MAGIC-COOKIE-1',
  431. `Wrong x11 protocol: ${info.protocol}`);
  432. assert(Buffer.isBuffer(info.cookie), 'Expected cookie Buffer');
  433. assert.strictEqual(
  434. info.cookie.length,
  435. 32,
  436. `Invalid x11 cookie length: ${info.cookie.length}`
  437. );
  438. sawX11 = true;
  439. accept && accept();
  440. })).on('exec', mustCall((accept, reject, info) => {
  441. assert(sawX11, 'Expected x11 before exec');
  442. assert(info.command === 'foo --bar',
  443. `Wrong exec command: ${info.command}`);
  444. const stream = accept();
  445. conn.x11('127.0.0.1', 4321, mustCall((err, xstream) => {
  446. assert(!err, `Unexpected x11() error: ${err}`);
  447. xstream.resume();
  448. xstream.on('end', mustCall(() => {
  449. stream.exit(100);
  450. stream.end();
  451. conn.end();
  452. })).end();
  453. }));
  454. }));
  455. }));
  456. }));
  457. }));
  458. client.on('ready', mustCall(() => {
  459. client.on('x11', mustCall((info, accept, reject) => {
  460. assert.strictEqual(info.srcIP,
  461. '127.0.0.1',
  462. `Invalid x11 srcIP: ${info.srcIP}`);
  463. assert.strictEqual(info.srcPort,
  464. 4321,
  465. `Invalid x11 srcPort: ${info.srcPort}`);
  466. accept();
  467. })).exec('foo --bar', { x11: true }, mustCall((err, stream) => {
  468. assert(!err, `Unexpected exec error: ${err}`);
  469. stream.resume();
  470. }));
  471. }));
  472. }
  473. {
  474. const { client, server } = setup(
  475. 'Exec with X11 forwarding (custom X11 settings)'
  476. );
  477. const x11 = {
  478. single: true,
  479. screen: 1234,
  480. protocol: 'YUMMY-MAGIC-COOKIE-1',
  481. cookie: '00112233445566778899001122334455',
  482. };
  483. server.on('connection', mustCall((conn) => {
  484. conn.on('ready', mustCall(() => {
  485. conn.on('session', mustCall((accept, reject) => {
  486. let sawX11 = false;
  487. accept().on('x11', mustCall((accept, reject, info) => {
  488. assert.strictEqual(info.single,
  489. x11.single,
  490. `Wrong x11 single: ${info.single}`);
  491. assert.strictEqual(info.screen,
  492. x11.screen,
  493. `Wrong x11 screen: ${info.screen}`);
  494. assert.strictEqual(info.protocol,
  495. x11.protocol,
  496. `Wrong x11 protocol: ${info.protocol}`);
  497. assert(Buffer.isBuffer(info.cookie), 'Expected cookie Buffer');
  498. assert.strictEqual(info.cookie.toString(),
  499. x11.cookie,
  500. `Wrong x11 cookie: ${info.cookie}`);
  501. sawX11 = true;
  502. accept && accept();
  503. })).on('exec', mustCall((accept, reject, info) => {
  504. assert(sawX11, 'Expected x11 before exec');
  505. assert(info.command === 'foo --bar',
  506. `Wrong exec command: ${info.command}`);
  507. const stream = accept();
  508. conn.x11('127.0.0.1', 4321, mustCall((err, xstream) => {
  509. assert(!err, `Unexpected x11() error: ${err}`);
  510. xstream.resume();
  511. xstream.on('end', mustCall(() => {
  512. stream.exit(100);
  513. stream.end();
  514. conn.end();
  515. })).end();
  516. }));
  517. }));
  518. }));
  519. }));
  520. }));
  521. client.on('ready', mustCall(() => {
  522. client.on('x11', mustCall((info, accept, reject) => {
  523. assert.strictEqual(info.srcIP,
  524. '127.0.0.1',
  525. `Invalid x11 srcIP: ${info.srcIP}`);
  526. assert.strictEqual(info.srcPort,
  527. 4321,
  528. `Invalid x11 srcPort: ${info.srcPort}`);
  529. accept();
  530. })).exec('foo --bar', { x11 }, mustCall((err, stream) => {
  531. assert(!err, `Unexpected exec error: ${err}`);
  532. stream.resume();
  533. }));
  534. }));
  535. }