'use strict'; const assert = require('assert'); const { inspect } = require('util'); const { fixture, mustCall, mustCallAtLeast, setup: setup_, } = require('./common.js'); const debug = false; const clientCfg = { username: 'foo', password: 'bar' }; const serverCfg = { hostKeys: [ fixture('ssh_host_rsa_key') ] }; { const { client, server } = setup_( 'Exec with OpenSSH agent forwarding', { client: { ...clientCfg, agent: '/path/to/agent', }, server: serverCfg, debug, }, ); server.on('connection', mustCall((conn) => { conn.on('authentication', mustCall((ctx) => { ctx.accept(); })).on('ready', mustCall(() => { conn.on('session', mustCall((accept, reject) => { let sawAuthAgent = false; accept().on('auth-agent', mustCall((accept, reject) => { sawAuthAgent = true; accept && accept(); })).on('exec', mustCall((accept, reject, info) => { assert(sawAuthAgent, 'Expected auth-agent before exec'); 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', { agentForward: true }, mustCall((err, stream) => { assert(!err, `Unexpected exec error: ${err}`); stream.resume(); })); })); } { const { client, server } = setup_( 'OpenSSH forwarded UNIX socket connection', { client: clientCfg, server: { ...serverCfg, ident: 'OpenSSH_7.1', }, debug, }, ); const socketPath = '/foo'; const events = []; const expected = [ ['client', 'openssh_forwardInStreamLocal'], ['server', 'streamlocal-forward@openssh.com', { socketPath }], ['client', 'forward callback'], ['client', 'unix connection', { socketPath }], ['client', 'socket data', '1'], ['server', 'socket data', '2'], ['client', 'socket end'], ['server', 'cancel-streamlocal-forward@openssh.com', { socketPath }], ['client', 'cancel callback'] ]; server.on('connection', mustCall((conn) => { conn.on('authentication', mustCall((ctx) => { ctx.accept(); })).on('ready', mustCall(() => { conn.once('request', mustCall((accept, reject, name, info) => { events.push(['server', name, info]); assert(name === 'streamlocal-forward@openssh.com', `Wrong request name: ${name}`); accept(); conn.openssh_forwardOutStreamLocal(socketPath, mustCall((err, ch) => { assert(!err, `Unexpected error: ${err}`); ch.write('1'); ch.on('data', mustCallAtLeast((data) => { events.push(['server', 'socket data', data.toString()]); ch.close(); })); })); conn.on('request', mustCall((accept, reject, name, info) => { events.push(['server', name, info]); assert(name === 'cancel-streamlocal-forward@openssh.com', `Wrong request name: ${name}`); accept(); })); })); })); })); client.on('ready', mustCall(() => { // request forwarding events.push(['client', 'openssh_forwardInStreamLocal']); client.openssh_forwardInStreamLocal(socketPath, mustCall((err) => { assert(!err, `Unexpected error: ${err}`); events.push(['client', 'forward callback']); })); client.on('unix connection', mustCall((info, accept, reject) => { events.push(['client', 'unix connection', info]); const stream = accept(); stream.on('data', mustCallAtLeast((data) => { events.push(['client', 'socket data', data.toString()]); stream.write('2'); })).on('end', mustCall(() => { events.push(['client', 'socket end']); client.openssh_unforwardInStreamLocal(socketPath, mustCall((err) => { assert(!err, `Unexpected error: ${err}`); events.push(['client', 'cancel callback']); client.end(); })); })); })); })).on('close', mustCall(() => { assert.deepStrictEqual( events, expected, 'Events mismatch\n' + `Actual:\n${inspect(events)}\n` + `Expected:\n${inspect(expected)}` ); })); } { const { client, server } = setup_( 'OpenSSH UNIX socket connection', { client: clientCfg, server: { ...serverCfg, ident: 'OpenSSH_8.0', }, debug, }, ); const socketPath = '/foo/bar/baz'; const response = 'Hello World'; server.on('connection', mustCall((conn) => { conn.on('authentication', mustCall((ctx) => { ctx.accept(); })).on('ready', mustCall(() => { conn.on('openssh.streamlocal', mustCall((accept, reject, info) => { assert.deepStrictEqual( info, { socketPath }, `Wrong info: ${inspect(info)}` ); const stream = accept(); stream.on('close', mustCall(() => { client.end(); })).end(response); stream.resume(); })); })); })); client.on('ready', mustCall(() => { client.openssh_forwardOutStreamLocal(socketPath, mustCall((err, stream) => { assert(!err, `Unexpected error: ${err}`); let buf = ''; stream.on('data', mustCallAtLeast((data) => { buf += data; })).on('close', mustCall(() => { assert(buf === response, `Wrong response: ${inspect(buf)}`); })); })); })); } { const { client, server } = setup_( 'OpenSSH 5.x workaround for binding on port 0', { client: clientCfg, server: { ...serverCfg, ident: 'OpenSSH_5.3', }, debug, }, ); const boundAddr = 'good'; const boundPort = 1337; const tcpInfo = { destIP: boundAddr, destPort: boundPort, srcIP: 'remote', srcPort: 12345, }; server.on('connection', mustCall((conn) => { conn.on('authentication', mustCall((ctx) => { ctx.accept(); })).on('ready', mustCall(() => { conn.on('request', mustCall((accept, reject, name, info) => { assert(name === 'tcpip-forward', `Unexpected request: ${name}`); assert(info.bindAddr === boundAddr, `Wrong addr: ${info.bindAddr}`); assert(info.bindPort === 0, `Wrong port: ${info.bindPort}`); accept(boundPort); conn.forwardOut(boundAddr, 0, tcpInfo.srcIP, tcpInfo.srcPort, mustCall((err, ch) => { assert(!err, `Unexpected error: ${err}`); client.end(); })); })); })); })); client.on('ready', mustCall(() => { // request forwarding client.forwardIn(boundAddr, 0, mustCall((err, port) => { assert(!err, `Unexpected error: ${err}`); assert(port === boundPort, `Bad bound port: ${port}`); })); })).on('tcp connection', mustCall((details, accept, reject) => { assert.deepStrictEqual( details, tcpInfo, `Wrong tcp details: ${inspect(details)}` ); accept(); })); }