'use strict'; const assert = require('assert'); const { inspect } = require('util'); const { mustCall, mustCallAtLeast, setupSimple, } = require('./common.js'); const DEBUG = false; const setup = setupSimple.bind(undefined, DEBUG); { const { client, server } = setup('Simple exec()'); const COMMAND = 'foo --bar'; const STDOUT_DATA = 'stdout data!\n'; const STDERR_DATA = 'stderr data!\n'; server.on('connection', mustCall((conn) => { conn.on('ready', mustCall(() => { conn.on('session', mustCall((accept, reject) => { accept().on('exec', mustCall((accept, reject, info) => { assert(info.command === COMMAND, `Wrong exec command: ${info.command}`); const stream = accept(); stream.stderr.write(STDERR_DATA); stream.write(STDOUT_DATA); stream.exit(100); stream.end(); conn.end(); })); })); })); })); client.on('ready', mustCall(() => { let out = ''; let outErr = ''; const events = []; const EXPECTED_EVENTS = [ 'exit', 'close' ]; const EXPECTED_EXIT_CLOSE_ARGS = [ 100 ]; client.on('close', mustCall(() => { assert(out === STDOUT_DATA, `Wrong stdout data: ${inspect(out)}`); assert(outErr === STDERR_DATA, `Wrong stderr data: ${inspect(outErr)}`); assert.deepStrictEqual( events, EXPECTED_EVENTS, `Wrong command event order: ${events}` ); })).exec(COMMAND, mustCall((err, stream) => { assert(!err, `Unexpected exec error: ${err}`); stream.on('data', mustCallAtLeast((d) => { out += d; })).on('exit', mustCall((...args) => { assert.deepStrictEqual(args, EXPECTED_EXIT_CLOSE_ARGS, `Wrong exit args: ${inspect(args)}`); events.push('exit'); })).on('close', mustCall((...args) => { assert.deepStrictEqual(args, EXPECTED_EXIT_CLOSE_ARGS, `Wrong close args: ${inspect(args)}`); events.push('close'); })).stderr.on('data', mustCallAtLeast((d) => { outErr += d; })); })); })); } { const { client, server } = setup('Simple exec() (exit signal)'); const COMMAND = 'foo --bar'; const STDOUT_DATA = 'stdout data!\n'; const STDERR_DATA = 'stderr data!\n'; server.on('connection', mustCall((conn) => { conn.on('ready', mustCall(() => { conn.on('session', mustCall((accept, reject) => { accept().on('exec', mustCall((accept, reject, info) => { assert(info.command === COMMAND, `Wrong exec command: ${info.command}`); const stream = accept(); stream.stderr.write(STDERR_DATA); stream.write(STDOUT_DATA); assert.throws(() => stream.exit('SIGFAKE')); stream.exit('SIGKILL'); stream.end(); conn.end(); })); })); })); })); client.on('ready', mustCall(() => { let out = ''; let outErr = ''; const events = []; const EXPECTED_EVENTS = [ 'exit', 'close' ]; const EXPECTED_EXIT_CLOSE_ARGS = [ null, 'SIGKILL', false, '' ]; client.on('close', mustCall(() => { assert(out === STDOUT_DATA, `Wrong stdout data: ${inspect(out)}`); assert(outErr === STDERR_DATA, `Wrong stderr data: ${inspect(outErr)}`); assert.deepStrictEqual( events, EXPECTED_EVENTS, `Wrong command event order: ${events}` ); })).exec(COMMAND, mustCall((err, stream) => { assert(!err, `Unexpected exec error: ${err}`); stream.on('data', mustCallAtLeast((d) => { out += d; })).on('exit', mustCall((...args) => { assert.deepStrictEqual(args, EXPECTED_EXIT_CLOSE_ARGS, `Wrong exit args: ${inspect(args)}`); events.push('exit'); })).on('close', mustCall((...args) => { assert.deepStrictEqual(args, EXPECTED_EXIT_CLOSE_ARGS, `Wrong close args: ${inspect(args)}`); events.push('close'); })).stderr.on('data', mustCallAtLeast((d) => { outErr += d; })); })); })); } { const { client, server } = setup('Simple exec() (exit signal -- no "SIG")'); const COMMAND = 'foo --bar'; const STDOUT_DATA = 'stdout data!\n'; const STDERR_DATA = 'stderr data!\n'; server.on('connection', mustCall((conn) => { conn.on('ready', mustCall(() => { conn.on('session', mustCall((accept, reject) => { accept().on('exec', mustCall((accept, reject, info) => { assert(info.command === COMMAND, `Wrong exec command: ${info.command}`); const stream = accept(); stream.stderr.write(STDERR_DATA); stream.write(STDOUT_DATA); assert.throws(() => stream.exit('FAKE')); stream.exit('KILL'); stream.end(); conn.end(); })); })); })); })); client.on('ready', mustCall(() => { let out = ''; let outErr = ''; const events = []; const EXPECTED_EVENTS = [ 'exit', 'close' ]; const EXPECTED_EXIT_CLOSE_ARGS = [ null, 'SIGKILL', false, '' ]; client.on('close', mustCall(() => { assert(out === STDOUT_DATA, `Wrong stdout data: ${inspect(out)}`); assert(outErr === STDERR_DATA, `Wrong stderr data: ${inspect(outErr)}`); assert.deepStrictEqual( events, EXPECTED_EVENTS, `Wrong command event order: ${events}` ); })).exec(COMMAND, mustCall((err, stream) => { assert(!err, `Unexpected exec error: ${err}`); stream.on('data', mustCallAtLeast((d) => { out += d; })).on('exit', mustCall((...args) => { assert.deepStrictEqual(args, EXPECTED_EXIT_CLOSE_ARGS, `Wrong exit args: ${inspect(args)}`); events.push('exit'); })).on('close', mustCall((...args) => { assert.deepStrictEqual(args, EXPECTED_EXIT_CLOSE_ARGS, `Wrong close args: ${inspect(args)}`); events.push('close'); })).stderr.on('data', mustCallAtLeast((d) => { outErr += d; })); })); })); } { const { client, server } = setup('Exec with signal()'); const COMMAND = 'foo --bar'; const STDOUT_DATA = 'stdout data!\n'; const STDERR_DATA = 'stderr data!\n'; server.on('connection', mustCall((conn) => { conn.on('ready', mustCall(() => { conn.on('session', mustCall((accept, reject) => { let stream; accept().on('exec', mustCall((accept, reject, info) => { assert(info.command === COMMAND, `Wrong exec command: ${info.command}`); stream = accept(); stream.stderr.write(STDERR_DATA); stream.write(STDOUT_DATA); })).on('signal', mustCall((accept, reject, info) => { assert(info.name === 'INT', `Wrong client signal name: ${info.name}`); stream.exit(100); stream.end(); conn.end(); })); })); })); })); client.on('ready', mustCall(() => { let out = ''; let outErr = ''; const events = []; const EXPECTED_EVENTS = [ 'exit', 'close' ]; const EXPECTED_EXIT_CLOSE_ARGS = [ 100 ]; client.on('close', mustCall(() => { assert(out === STDOUT_DATA, `Wrong stdout data: ${inspect(out)}`); assert(outErr === STDERR_DATA, `Wrong stderr data: ${inspect(outErr)}`); assert.deepStrictEqual( events, EXPECTED_EVENTS, `Wrong command event order: ${events}` ); })).exec(COMMAND, mustCall((err, stream) => { assert(!err, `Unexpected exec error: ${err}`); const sendSignal = (() => { let sent = false; return () => { if (sent) return; sent = true; assert.throws(() => stream.signal('FAKE')); assert.throws(() => stream.signal('SIGFAKE')); stream.signal('SIGINT'); }; })(); stream.on('data', mustCallAtLeast((d) => { out += d; sendSignal(); })).on('exit', mustCall((...args) => { assert.deepStrictEqual(args, EXPECTED_EXIT_CLOSE_ARGS, `Wrong exit args: ${inspect(args)}`); events.push('exit'); })).on('close', mustCall((...args) => { assert.deepStrictEqual(args, EXPECTED_EXIT_CLOSE_ARGS, `Wrong close args: ${inspect(args)}`); events.push('close'); })).stderr.on('data', mustCallAtLeast((d) => { outErr += d; })); })); })); } { const { client, server } = setup('Exec with signal() -- no "SIG"'); const COMMAND = 'foo --bar'; const STDOUT_DATA = 'stdout data!\n'; const STDERR_DATA = 'stderr data!\n'; server.on('connection', mustCall((conn) => { conn.on('ready', mustCall(() => { conn.on('session', mustCall((accept, reject) => { let stream; accept().on('exec', mustCall((accept, reject, info) => { assert(info.command === COMMAND, `Wrong exec command: ${info.command}`); stream = accept(); stream.stderr.write(STDERR_DATA); stream.write(STDOUT_DATA); })).on('signal', mustCall((accept, reject, info) => { assert(info.name === 'INT', `Wrong client signal name: ${info.name}`); stream.exit(100); stream.end(); conn.end(); })); })); })); })); client.on('ready', mustCall(() => { let out = ''; let outErr = ''; const events = []; const EXPECTED_EVENTS = [ 'exit', 'close' ]; const EXPECTED_EXIT_CLOSE_ARGS = [ 100 ]; client.on('close', mustCall(() => { assert(out === STDOUT_DATA, `Wrong stdout data: ${inspect(out)}`); assert(outErr === STDERR_DATA, `Wrong stderr data: ${inspect(outErr)}`); assert.deepStrictEqual( events, EXPECTED_EVENTS, `Wrong command event order: ${events}` ); })).exec(COMMAND, mustCall((err, stream) => { assert(!err, `Unexpected exec error: ${err}`); const sendSignal = (() => { let sent = false; return () => { if (sent) return; sent = true; stream.signal('INT'); }; })(); stream.on('data', mustCallAtLeast((d) => { out += d; sendSignal(); })).on('exit', mustCall((...args) => { assert.deepStrictEqual(args, EXPECTED_EXIT_CLOSE_ARGS, `Wrong exit args: ${inspect(args)}`); events.push('exit'); })).on('close', mustCall((...args) => { assert.deepStrictEqual(args, EXPECTED_EXIT_CLOSE_ARGS, `Wrong close args: ${inspect(args)}`); events.push('close'); })).stderr.on('data', mustCallAtLeast((d) => { outErr += d; })); })); })); } { const { client, server } = setup('Exec with environment set'); const env = { SSH2NODETEST: 'foo' }; server.on('connection', mustCall((conn) => { conn.on('ready', mustCall(() => { conn.on('session', mustCall((accept, reject) => { accept().on('env', mustCall((accept, reject, info) => { accept && accept(); assert(info.key === Object.keys(env)[0], 'Wrong env key'); assert(info.val === Object.values(env)[0], 'Wrong env value'); })).on('exec', mustCall((accept, reject, info) => { assert(info.command === 'foo --bar', `Wrong exec command: ${info.command}`); const stream = accept(); stream.exit(100); stream.end(); conn.end(); })); })); })); })); client.on('ready', mustCall(() => { client.exec('foo --bar', { env }, mustCall((err, stream) => { assert(!err, `Unexpected exec error: ${err}`); stream.resume(); })); })); } { const { client, server } = setup('Exec with setWindow()'); const dimensions = { rows: 60, cols: 115, height: 480, width: 640, }; server.on('connection', mustCall((conn) => { conn.on('ready', mustCall(() => { conn.on('session', mustCall((accept, reject) => { accept().on('window-change', mustCall((accept, reject, info) => { accept && accept(); assert.deepStrictEqual(info, dimensions, 'Wrong dimensions'); })).on('exec', mustCall((accept, reject, info) => { assert(info.command === 'foo --bar', `Wrong exec command: ${info.command}`); const stream = accept(); stream.exit(100); stream.end(); conn.end(); })); })); })); })); client.on('ready', mustCall(() => { client.exec('foo --bar', mustCall((err, stream) => { assert(!err, `Unexpected exec error: ${err}`); stream.setWindow(...Object.values(dimensions)); stream.resume(); })); })); } { const { client, server } = setup('Exec with pty set'); const pty = { rows: 2, cols: 4, width: 0, height: 0, term: 'vt220', modes: {}, }; server.on('connection', mustCall((conn) => { conn.on('ready', mustCall(() => { conn.on('session', mustCall((accept, reject) => { let sawPty = false; accept().on('pty', mustCall((accept, reject, info) => { assert.deepStrictEqual(info, pty, 'Wrong pty info'); sawPty = true; accept && accept(); })).on('exec', mustCall((accept, reject, info) => { assert(sawPty, 'Expected pty to be set up'); assert(info.command === 'foo --bar', `Wrong exec command: ${info.command}`); const stream = accept(); stream.exit(100); stream.end(); conn.end(); })); })); })); })); client.on('ready', mustCall(() => { client.exec('foo --bar', { pty }, mustCall((err, stream) => { assert(!err, `Unexpected exec error: ${err}`); stream.resume(); })); })); } { const { client, server } = setup('Exec with X11 forwarding'); server.on('connection', mustCall((conn) => { conn.on('ready', mustCall(() => { conn.on('session', mustCall((accept, reject) => { let sawX11 = false; accept().on('x11', mustCall((accept, reject, info) => { assert.strictEqual(info.single, false, `Wrong x11 single: ${info.single}`); assert.strictEqual(info.screen, 0, `Wrong x11 screen: ${info.screen}`); assert.strictEqual(info.protocol, 'MIT-MAGIC-COOKIE-1', `Wrong x11 protocol: ${info.protocol}`); assert(Buffer.isBuffer(info.cookie), 'Expected cookie Buffer'); assert.strictEqual( info.cookie.length, 32, `Invalid x11 cookie length: ${info.cookie.length}` ); sawX11 = true; accept && accept(); })).on('exec', mustCall((accept, reject, info) => { assert(sawX11, 'Expected x11 before exec'); assert(info.command === 'foo --bar', `Wrong exec command: ${info.command}`); const stream = accept(); conn.x11('127.0.0.1', 4321, mustCall((err, xstream) => { assert(!err, `Unexpected x11() error: ${err}`); xstream.resume(); xstream.on('end', mustCall(() => { stream.exit(100); stream.end(); conn.end(); })).end(); })); })); })); })); })); client.on('ready', mustCall(() => { client.on('x11', mustCall((info, accept, reject) => { assert.strictEqual(info.srcIP, '127.0.0.1', `Invalid x11 srcIP: ${info.srcIP}`); assert.strictEqual(info.srcPort, 4321, `Invalid x11 srcPort: ${info.srcPort}`); accept(); })).exec('foo --bar', { x11: true }, mustCall((err, stream) => { assert(!err, `Unexpected exec error: ${err}`); stream.resume(); })); })); } { const { client, server } = setup( 'Exec with X11 forwarding (custom X11 settings)' ); const x11 = { single: true, screen: 1234, protocol: 'YUMMY-MAGIC-COOKIE-1', cookie: '00112233445566778899001122334455', }; server.on('connection', mustCall((conn) => { conn.on('ready', mustCall(() => { conn.on('session', mustCall((accept, reject) => { let sawX11 = false; accept().on('x11', mustCall((accept, reject, info) => { assert.strictEqual(info.single, x11.single, `Wrong x11 single: ${info.single}`); assert.strictEqual(info.screen, x11.screen, `Wrong x11 screen: ${info.screen}`); assert.strictEqual(info.protocol, x11.protocol, `Wrong x11 protocol: ${info.protocol}`); assert(Buffer.isBuffer(info.cookie), 'Expected cookie Buffer'); assert.strictEqual(info.cookie.toString(), x11.cookie, `Wrong x11 cookie: ${info.cookie}`); sawX11 = true; accept && accept(); })).on('exec', mustCall((accept, reject, info) => { assert(sawX11, 'Expected x11 before exec'); assert(info.command === 'foo --bar', `Wrong exec command: ${info.command}`); const stream = accept(); conn.x11('127.0.0.1', 4321, mustCall((err, xstream) => { assert(!err, `Unexpected x11() error: ${err}`); xstream.resume(); xstream.on('end', mustCall(() => { stream.exit(100); stream.end(); conn.end(); })).end(); })); })); })); })); })); client.on('ready', mustCall(() => { client.on('x11', mustCall((info, accept, reject) => { assert.strictEqual(info.srcIP, '127.0.0.1', `Invalid x11 srcIP: ${info.srcIP}`); assert.strictEqual(info.srcPort, 4321, `Invalid x11 srcPort: ${info.srcPort}`); accept(); })).exec('foo --bar', { x11 }, mustCall((err, stream) => { assert(!err, `Unexpected exec error: ${err}`); stream.resume(); })); })); }