hexo/node_modules/ssh2/test/test-client-server.js

2136 lines
65 KiB
JavaScript

var Client = require('../lib/client');
var Server = require('../lib/server');
var OPEN_MODE = require('ssh2-streams').SFTPStream.OPEN_MODE;
var STATUS_CODE = require('ssh2-streams').SFTPStream.STATUS_CODE;
var utils = require('ssh2-streams').utils;
var semver = require('semver');
var net = require('net');
var fs = require('fs');
var crypto = require('crypto');
var path = require('path');
var join = path.join;
var inspect = require('util').inspect;
var assert = require('assert');
var t = -1;
var group = path.basename(__filename, '.js') + '/';
var fixturesdir = join(__dirname, 'fixtures');
var USER = 'nodejs';
var PASSWORD = 'FLUXCAPACITORISTHEPOWER';
var MD5_HOST_FINGERPRINT = '64254520742d3d0792e918f3ce945a64';
var KEY_RSA_BAD = fs.readFileSync(join(fixturesdir, 'bad_rsa_private_key'));
var HOST_KEY_RSA = fs.readFileSync(join(fixturesdir, 'ssh_host_rsa_key'));
var HOST_KEY_DSA = fs.readFileSync(join(fixturesdir, 'ssh_host_dsa_key'));
var HOST_KEY_ECDSA = fs.readFileSync(join(fixturesdir, 'ssh_host_ecdsa_key'));
var CLIENT_KEY_ENC_RSA = fs.readFileSync(join(fixturesdir, 'id_rsa_enc'));
var CLIENT_KEY_ENC_RSA_PUB = utils.parseKey(CLIENT_KEY_ENC_RSA);
utils.decryptKey(CLIENT_KEY_ENC_RSA_PUB, 'foobarbaz');
CLIENT_KEY_ENC_RSA_PUB = utils.genPublicKey(CLIENT_KEY_ENC_RSA_PUB);
var CLIENT_KEY_PPK_RSA = fs.readFileSync(join(fixturesdir, 'id_rsa.ppk'));
var CLIENT_KEY_PPK_RSA_PUB = utils.parseKey(CLIENT_KEY_PPK_RSA);
var CLIENT_KEY_RSA = fs.readFileSync(join(fixturesdir, 'id_rsa'));
var CLIENT_KEY_RSA_PUB = utils.genPublicKey(utils.parseKey(CLIENT_KEY_RSA));
var CLIENT_KEY_DSA = fs.readFileSync(join(fixturesdir, 'id_dsa'));
var CLIENT_KEY_DSA_PUB = utils.genPublicKey(utils.parseKey(CLIENT_KEY_DSA));
if (semver.gte(process.version, '5.2.0')) {
var CLIENT_KEY_ECDSA = fs.readFileSync(join(fixturesdir, 'id_ecdsa'));
var CLIENT_KEY_ECDSA_PUB = utils.genPublicKey(
utils.parseKey(CLIENT_KEY_ECDSA)
);
}
var DEBUG = false;
var DEFAULT_TEST_TIMEOUT = 5 * 1000;
var tests = [
{ run: function() {
var client;
var server;
var r;
r = setup(
this,
{ username: USER,
privateKey: CLIENT_KEY_RSA
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
if (ctx.method === 'none')
return ctx.reject();
assert(ctx.method === 'publickey',
makeMsg('Unexpected auth method: ' + ctx.method));
assert(ctx.username === USER,
makeMsg('Unexpected username: ' + ctx.username));
assert(ctx.key.algo === 'ssh-rsa',
makeMsg('Unexpected key algo: ' + ctx.key.algo));
assert.deepEqual(CLIENT_KEY_RSA_PUB.public,
ctx.key.data,
makeMsg('Public key mismatch'));
if (ctx.signature) {
var verifier = crypto.createVerify('RSA-SHA1');
var pem = CLIENT_KEY_RSA_PUB.publicOrig;
verifier.update(ctx.blob);
assert(verifier.verify(pem, ctx.signature),
makeMsg('Could not verify PK signature'));
ctx.accept();
} else
ctx.accept();
}).on('ready', function() {
conn.end();
});
});
},
what: 'Authenticate with an RSA key'
},
{ run: function() {
var client;
var server;
var r;
r = setup(
this,
{ username: USER,
privateKey: CLIENT_KEY_ENC_RSA,
passphrase: 'foobarbaz',
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
if (ctx.method === 'none')
return ctx.reject();
assert(ctx.method === 'publickey',
makeMsg('Unexpected auth method: ' + ctx.method));
assert(ctx.username === USER,
makeMsg('Unexpected username: ' + ctx.username));
assert(ctx.key.algo === 'ssh-rsa',
makeMsg('Unexpected key algo: ' + ctx.key.algo));
assert.deepEqual(CLIENT_KEY_ENC_RSA_PUB.public,
ctx.key.data,
makeMsg('Public key mismatch'));
if (ctx.signature) {
var verifier = crypto.createVerify('RSA-SHA1');
var pem = CLIENT_KEY_ENC_RSA_PUB.publicOrig;
verifier.update(ctx.blob);
assert(verifier.verify(pem, ctx.signature),
makeMsg('Could not verify PK signature'));
ctx.accept();
} else
ctx.accept();
}).on('ready', function() {
conn.end();
});
});
},
what: 'Authenticate with an encrypted RSA key'
},
{ run: function() {
var client;
var server;
var r;
r = setup(
this,
{ username: USER,
privateKey: CLIENT_KEY_PPK_RSA
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
if (ctx.method === 'none')
return ctx.reject();
assert(ctx.method === 'publickey',
makeMsg('Unexpected auth method: ' + ctx.method));
assert(ctx.username === USER,
makeMsg('Unexpected username: ' + ctx.username));
assert(ctx.key.algo === 'ssh-rsa',
makeMsg('Unexpected key algo: ' + ctx.key.algo));
if (ctx.signature) {
var verifier = crypto.createVerify('RSA-SHA1');
var pem = CLIENT_KEY_PPK_RSA_PUB.publicOrig;
verifier.update(ctx.blob);
assert(verifier.verify(pem, ctx.signature),
makeMsg('Could not verify PK signature'));
ctx.accept();
} else
ctx.accept();
}).on('ready', function() {
conn.end();
});
});
},
what: 'Authenticate with an RSA key (PPK)'
},
{ run: function() {
var client;
var server;
var r;
r = setup(
this,
{ username: USER,
privateKey: CLIENT_KEY_DSA
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
if (ctx.method === 'none')
return ctx.reject();
assert(ctx.method === 'publickey',
makeMsg('Unexpected auth method: ' + ctx.method));
assert(ctx.username === USER,
makeMsg('Unexpected username: ' + ctx.username));
assert(ctx.key.algo === 'ssh-dss',
makeMsg('Unexpected key algo: ' + ctx.key.algo));
assert.deepEqual(CLIENT_KEY_DSA_PUB.public,
ctx.key.data,
makeMsg('Public key mismatch'));
if (ctx.signature) {
var verifier = crypto.createVerify('DSA-SHA1');
var pem = CLIENT_KEY_DSA_PUB.publicOrig;
verifier.update(ctx.blob);
assert(verifier.verify(pem, ctx.signature),
makeMsg('Could not verify PK signature'));
ctx.accept();
} else
ctx.accept();
}).on('ready', function() {
conn.end();
});
});
},
what: 'Authenticate with a DSA key'
},
{ run: function() {
if (semver.lt(process.version, '5.2.0'))
return next();
var client;
var server;
var r;
r = setup(
this,
{ username: USER,
privateKey: CLIENT_KEY_ECDSA
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
if (ctx.method === 'none')
return ctx.reject();
assert(ctx.method === 'publickey',
makeMsg('Unexpected auth method: ' + ctx.method));
assert(ctx.username === USER,
makeMsg('Unexpected username: ' + ctx.username));
assert(ctx.key.algo === 'ecdsa-sha2-nistp256',
makeMsg('Unexpected key algo: ' + ctx.key.algo));
assert.deepEqual(CLIENT_KEY_ECDSA_PUB.public,
ctx.key.data,
makeMsg('Public key mismatch'));
if (ctx.signature) {
var verifier = crypto.createVerify('sha256');
var pem = CLIENT_KEY_ECDSA_PUB.publicOrig;
verifier.update(ctx.blob);
assert(verifier.verify(pem, ctx.signature),
makeMsg('Could not verify PK signature'));
ctx.accept();
} else
ctx.accept();
}).on('ready', function() {
conn.end();
});
});
},
what: 'Authenticate with a ECDSA key'
},
{ run: function() {
var client;
var server;
var r;
r = setup(
this,
{ username: USER,
password: 'asdf',
algorithms: {
serverHostKey: ['ssh-dss']
}
},
{ hostKeys: [HOST_KEY_DSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
if (ctx.method === 'none')
return ctx.reject();
assert(ctx.method === 'password',
makeMsg('Unexpected auth method: ' + ctx.method));
assert(ctx.username === USER,
makeMsg('Unexpected username: ' + ctx.username));
assert(ctx.password === 'asdf',
makeMsg('Unexpected password: ' + ctx.password));
ctx.accept();
}).on('ready', function() {
conn.end();
});
});
},
what: 'Server with DSA host key'
},
{ run: function() {
if (semver.lt(process.version, '5.2.0'))
return next();
var client;
var server;
var r;
r = setup(
this,
{ username: USER,
password: 'asdf'
},
{ hostKeys: [HOST_KEY_ECDSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
if (ctx.method === 'none')
return ctx.reject();
assert(ctx.method === 'password',
makeMsg('Unexpected auth method: ' + ctx.method));
assert(ctx.username === USER,
makeMsg('Unexpected username: ' + ctx.username));
assert(ctx.password === 'asdf',
makeMsg('Unexpected password: ' + ctx.password));
ctx.accept();
}).on('ready', function() {
conn.end();
});
});
},
what: 'Server with ECDSA host key'
},
{ run: function() {
var client;
var server;
var r;
r = setup(
this,
{ username: USER,
password: 'asdf',
algorithms: {
serverHostKey: 'ssh-rsa'
}
},
{ hostKeys: [HOST_KEY_RSA, HOST_KEY_DSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
if (ctx.method === 'none')
return ctx.reject();
assert(ctx.method === 'password',
makeMsg('Unexpected auth method: ' + ctx.method));
assert(ctx.username === USER,
makeMsg('Unexpected username: ' + ctx.username));
assert(ctx.password === 'asdf',
makeMsg('Unexpected password: ' + ctx.password));
ctx.accept();
}).on('ready', function() {
conn.end();
});
});
},
what: 'Server with multiple host keys (RSA selected)'
},
{ run: function() {
var client;
var server;
var r;
r = setup(
this,
{ username: USER,
password: 'asdf',
algorithms: {
serverHostKey: 'ssh-dss'
}
},
{ hostKeys: [HOST_KEY_RSA, HOST_KEY_DSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
if (ctx.method === 'none')
return ctx.reject();
assert(ctx.method === 'password',
makeMsg('Unexpected auth method: ' + ctx.method));
assert(ctx.username === USER,
makeMsg('Unexpected username: ' + ctx.username));
assert(ctx.password === 'asdf',
makeMsg('Unexpected password: ' + ctx.password));
ctx.accept();
}).on('ready', function() {
conn.end();
});
});
},
what: 'Server with multiple host keys (DSA selected)'
},
{ run: function() {
var client;
var server;
var r;
var hostname = 'foo';
var username = 'bar';
r = setup(
this,
{ username: USER,
privateKey: CLIENT_KEY_RSA,
localHostname: hostname,
localUsername: username
},
{ hostKeys: [ HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
if (ctx.method !== 'hostbased')
return ctx.reject();
assert(ctx.method === 'hostbased',
makeMsg('Unexpected auth method: ' + ctx.method));
assert(ctx.username === USER,
makeMsg('Unexpected username: ' + ctx.username));
assert(ctx.key.algo === 'ssh-rsa',
makeMsg('Unexpected key algo: ' + ctx.key.algo));
assert.deepEqual(CLIENT_KEY_RSA_PUB.public,
ctx.key.data,
makeMsg('Public key mismatch'));
assert(ctx.signature,
makeMsg('Expected signature'));
assert(ctx.localHostname === hostname,
makeMsg('Wrong local hostname'));
assert(ctx.localUsername === username,
makeMsg('Wrong local username'));
var verifier = crypto.createVerify('RSA-SHA1');
var pem = CLIENT_KEY_RSA_PUB.publicOrig;
verifier.update(ctx.blob);
assert(verifier.verify(pem, ctx.signature),
makeMsg('Could not verify hostbased signature'));
ctx.accept();
}).on('ready', function() {
conn.end();
});
});
},
what: 'Authenticate with hostbased'
},
{ run: function() {
var client;
var server;
var r;
r = setup(
this,
{ username: USER,
password: PASSWORD
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
if (ctx.method === 'none')
return ctx.reject();
assert(ctx.method === 'password',
makeMsg('Unexpected auth method: ' + ctx.method));
assert(ctx.username === USER,
makeMsg('Unexpected username: ' + ctx.username));
assert(ctx.password === PASSWORD,
makeMsg('Unexpected password: ' + ctx.password));
ctx.accept();
}).on('ready', function() {
conn.end();
});
});
},
what: 'Authenticate with a password'
},
{ run: function() {
var client;
var server;
var r;
var verified = false;
r = setup(
this,
{ username: USER,
password: PASSWORD,
hostHash: 'md5',
hostVerifier: function(hash) {
assert(hash === MD5_HOST_FINGERPRINT,
makeMsg('Host fingerprint mismatch'));
return (verified = true);
}
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
ctx.accept();
}).on('ready', function() {
conn.end();
});
}).on('close', function() {
assert(verified, makeMsg('Failed to verify host fingerprint'));
});
},
what: 'Verify host fingerprint'
},
{ run: function() {
var client;
var server;
var r;
var out = '';
var outErr = '';
var exitArgs;
var closeArgs;
r = setup(
this,
{ username: USER,
password: PASSWORD
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
ctx.accept();
}).on('ready', function() {
conn.once('session', function(accept, reject) {
var session = accept();
session.once('exec', function(accept, reject, info) {
assert(info.command === 'foo --bar',
makeMsg('Wrong exec command: ' + info.command));
var stream = accept();
stream.stderr.write('stderr data!\n');
stream.write('stdout data!\n');
stream.exit(100);
stream.end();
conn.end();
});
});
});
});
client.on('ready', function() {
client.exec('foo --bar', function(err, stream) {
assert(!err, makeMsg('Unexpected exec error: ' + err));
stream.on('data', function(d) {
out += d;
}).on('exit', function(code) {
exitArgs = new Array(arguments.length);
for (var i = 0; i < exitArgs.length; ++i)
exitArgs[i] = arguments[i];
}).on('close', function(code) {
closeArgs = new Array(arguments.length);
for (var i = 0; i < closeArgs.length; ++i)
closeArgs[i] = arguments[i];
}).stderr.on('data', function(d) {
outErr += d;
});
});
}).on('end', function() {
assert.deepEqual(exitArgs,
[100],
makeMsg('Wrong exit args: ' + inspect(exitArgs)));
assert.deepEqual(closeArgs,
[100],
makeMsg('Wrong close args: ' + inspect(closeArgs)));
assert(out === 'stdout data!\n',
makeMsg('Wrong stdout data: ' + inspect(out)));
assert(outErr === 'stderr data!\n',
makeMsg('Wrong stderr data: ' + inspect(outErr)));
});
},
what: 'Simple exec'
},
{ run: function() {
var client;
var server;
var r;
var serverEnv = {};
var clientEnv = { SSH2NODETEST: 'foo' };
r = setup(
this,
{ username: USER,
password: PASSWORD
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
ctx.accept();
}).on('ready', function() {
conn.once('session', function(accept, reject) {
var session = accept();
session.once('env', function(accept, reject, info) {
serverEnv[info.key] = info.val;
accept && accept();
}).once('exec', function(accept, reject, info) {
assert(info.command === 'foo --bar',
makeMsg('Wrong exec command: ' + info.command));
var stream = accept();
stream.exit(100);
stream.end();
conn.end();
});
});
});
});
client.on('ready', function() {
client.exec('foo --bar',
{ env: clientEnv },
function(err, stream) {
assert(!err, makeMsg('Unexpected exec error: ' + err));
stream.resume();
});
}).on('end', function() {
assert.deepEqual(serverEnv, clientEnv,
makeMsg('Environment mismatch'));
});
},
what: 'Exec with environment set'
},
{ run: function() {
var client;
var server;
var r;
var out = '';
r = setup(
this,
{ username: USER,
password: PASSWORD
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
ctx.accept();
}).on('ready', function() {
conn.once('session', function(accept, reject) {
var session = accept();
var ptyInfo;
session.once('pty', function(accept, reject, info) {
ptyInfo = info;
accept && accept();
}).once('exec', function(accept, reject, info) {
assert(info.command === 'foo --bar',
makeMsg('Wrong exec command: ' + info.command));
var stream = accept();
stream.write(JSON.stringify(ptyInfo));
stream.exit(100);
stream.end();
conn.end();
});
});
});
});
var pty = {
rows: 2,
cols: 4,
width: 0,
height: 0,
term: 'vt220',
modes: {}
};
client.on('ready', function() {
client.exec('foo --bar',
{ pty: pty },
function(err, stream) {
assert(!err, makeMsg('Unexpected exec error: ' + err));
stream.on('data', function(d) {
out += d;
});
});
}).on('end', function() {
assert.deepEqual(JSON.parse(out),
pty,
makeMsg('Wrong stdout data: ' + inspect(out)));
});
},
what: 'Exec with pty set'
},
{ run: function() {
var client;
var server;
var r;
var out = '';
r = setup(
this,
{ username: USER,
password: PASSWORD,
agent: '/foo/bar/baz'
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
ctx.accept();
}).on('ready', function() {
conn.once('session', function(accept, reject) {
var session = accept();
var authAgentReq = false;
session.once('auth-agent', function(accept, reject) {
authAgentReq = true;
accept && accept();
}).once('exec', function(accept, reject, info) {
assert(info.command === 'foo --bar',
makeMsg('Wrong exec command: ' + info.command));
var stream = accept();
stream.write(inspect(authAgentReq));
stream.exit(100);
stream.end();
conn.end();
});
});
});
});
client.on('ready', function() {
client.exec('foo --bar',
{ agentForward: true },
function(err, stream) {
assert(!err, makeMsg('Unexpected exec error: ' + err));
stream.on('data', function(d) {
out += d;
});
});
}).on('end', function() {
assert(out === 'true',
makeMsg('Wrong stdout data: ' + inspect(out)));
});
},
what: 'Exec with OpenSSH agent forwarding'
},
{ run: function() {
var client;
var server;
var r;
var out = '';
r = setup(
this,
{ username: USER,
password: PASSWORD
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
ctx.accept();
}).on('ready', function() {
conn.once('session', function(accept, reject) {
var session = accept();
var x11 = false;
session.once('x11', function(accept, reject, info) {
x11 = true;
accept && accept();
}).once('exec', function(accept, reject, info) {
assert(info.command === 'foo --bar',
makeMsg('Wrong exec command: ' + info.command));
var stream = accept();
stream.write(inspect(x11));
stream.exit(100);
stream.end();
conn.end();
});
});
});
});
client.on('ready', function() {
client.exec('foo --bar',
{ x11: true },
function(err, stream) {
assert(!err, makeMsg('Unexpected exec error: ' + err));
stream.on('data', function(d) {
out += d;
});
});
}).on('end', function() {
assert(out === 'true',
makeMsg('Wrong stdout data: ' + inspect(out)));
});
},
what: 'Exec with X11 forwarding'
},
{ run: function() {
var client;
var server;
var r;
var out = '';
r = setup(
this,
{ username: USER,
password: PASSWORD
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
ctx.accept();
}).on('ready', function() {
conn.once('session', function(accept, reject) {
var session = accept();
var sawPty = false;
session.once('pty', function(accept, reject, info) {
sawPty = true;
accept && accept();
}).once('shell', function(accept, reject) {
var stream = accept();
stream.write('Cowabunga dude! ' + inspect(sawPty));
stream.end();
conn.end();
});
});
});
});
client.on('ready', function() {
client.shell(function(err, stream) {
assert(!err, makeMsg('Unexpected shell error: ' + err));
stream.on('data', function(d) {
out += d;
});
});
}).on('end', function() {
assert(out === 'Cowabunga dude! true',
makeMsg('Wrong stdout data: ' + inspect(out)));
});
},
what: 'Simple shell'
},
{ run: function() {
var client;
var server;
var r;
var serverEnv = {};
var clientEnv = { SSH2NODETEST: 'foo' };
var sawPty = false;
r = setup(
this,
{ username: USER,
password: PASSWORD
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
ctx.accept();
}).on('ready', function() {
conn.once('session', function(accept, reject) {
var session = accept();
session.once('env', function(accept, reject, info) {
serverEnv[info.key] = info.val;
accept && accept();
}).once('pty', function(accept, reject, info) {
sawPty = true;
accept && accept();
}).once('shell', function(accept, reject) {
var stream = accept();
stream.end();
conn.end();
});
});
});
});
client.on('ready', function() {
client.shell({ env: clientEnv }, function(err, stream) {
assert(!err, makeMsg('Unexpected shell error: ' + err));
stream.resume();
});
}).on('end', function() {
assert.deepEqual(serverEnv, clientEnv,
makeMsg('Environment mismatch'));
assert.strictEqual(sawPty, true);
});
},
what: 'Shell with environment set'
},
{ run: function() {
var client;
var server;
var r;
var expHandle = new Buffer([1, 2, 3, 4]);
var sawOpenS = false;
var sawCloseS = false;
var sawOpenC = false;
var sawCloseC = false;
r = setup(
this,
{ username: USER,
password: PASSWORD
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
ctx.accept();
}).on('ready', function() {
conn.once('session', function(accept, reject) {
var session = accept();
session.once('sftp', function(accept, reject) {
if (accept) {
var sftp = accept();
sftp.once('OPEN', function(id, filename, flags, attrs) {
assert(id === 0,
makeMsg('Unexpected sftp request ID: ' + id));
assert(filename === 'node.js',
makeMsg('Unexpected filename: ' + filename));
assert(flags === OPEN_MODE.READ,
makeMsg('Unexpected flags: ' + flags));
sawOpenS = true;
sftp.handle(id, expHandle);
sftp.once('CLOSE', function(id, handle) {
assert(id === 1,
makeMsg('Unexpected sftp request ID: ' + id));
assert.deepEqual(handle,
expHandle,
makeMsg('Wrong sftp file handle: '
+ inspect(handle)));
sawCloseS = true;
sftp.status(id, STATUS_CODE.OK);
conn.end();
});
});
}
});
});
});
});
client.on('ready', function() {
client.sftp(function(err, sftp) {
assert(!err, makeMsg('Unexpected sftp error: ' + err));
sftp.open('node.js', 'r', function(err, handle) {
assert(!err, makeMsg('Unexpected sftp error: ' + err));
assert.deepEqual(handle,
expHandle,
makeMsg('Wrong sftp file handle: '
+ inspect(handle)));
sawOpenC = true;
sftp.close(handle, function(err) {
assert(!err, makeMsg('Unexpected sftp error: ' + err));
sawCloseC = true;
});
});
});
}).on('end', function() {
assert(sawOpenS, makeMsg('Expected sftp open()'));
assert(sawOpenC, makeMsg('Expected sftp open() callback'));
assert(sawCloseS, makeMsg('Expected sftp open()'));
assert(sawOpenC, makeMsg('Expected sftp close() callback'));
});
},
what: 'Simple SFTP'
},
{ run: function() {
var client;
var server;
var state = {
readies: 0,
closes: 0
};
var clientcfg = {
username: USER,
password: PASSWORD
};
var servercfg = {
hostKeys: [HOST_KEY_RSA]
};
var reconnect = false;
client = new Client(),
server = new Server(servercfg);
function onReady() {
assert(++state.readies <= 4,
makeMsg('Wrong ready count: ' + state.readies));
}
function onClose() {
assert(++state.closes <= 3,
makeMsg('Wrong close count: ' + state.closes));
if (state.closes === 2)
server.close();
else if (state.closes === 3)
next();
}
server.listen(0, 'localhost', function() {
clientcfg.host = 'localhost';
clientcfg.port = server.address().port;
client.connect(clientcfg);
});
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
ctx.accept();
}).on('ready', onReady);
}).on('close', onClose);
client.on('ready', function() {
onReady();
if (reconnect)
client.end();
else {
reconnect = true;
client.connect(clientcfg);
}
}).on('close', onClose);
},
what: 'connect() on connected client'
},
{ run: function() {
var client = new Client({
username: USER,
password: PASSWORD
});
assert.throws(function() {
client.exec('uptime', function(err, stream) {
assert(false, makeMsg('Callback unexpectedly called'));
});
});
next();
},
what: 'Throw when not connected'
},
{ run: function() {
var client;
var server;
var r;
var calledBack = 0;
r = setup(
this,
{ username: USER,
password: PASSWORD
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
ctx.accept();
});
});
client.on('ready', function() {
function callback(err, stream) {
assert(err, makeMsg('Expected error'));
assert(err.message === 'No response from server',
makeMsg('Wrong error message: ' + err.message));
++calledBack;
}
client.exec('uptime', callback);
client.shell(callback);
client.sftp(callback);
client.end();
}).on('close', function() {
// give the callbacks a chance to execute
process.nextTick(function() {
assert(calledBack === 3,
makeMsg('Only '
+ calledBack
+ '/3 outstanding callbacks called'));
});
});
},
what: 'Outstanding callbacks called on disconnect'
},
{ run: function() {
var client = new Client({
username: USER,
password: PASSWORD
});
assert.throws(function() {
client.exec('uptime', function(err, stream) {
assert(false, makeMsg('Callback unexpectedly called'));
});
});
next();
},
what: 'Throw when not connected'
},
{ run: function() {
var client;
var server;
var r;
var calledBack = 0;
r = setup(
this,
{ username: USER,
password: PASSWORD
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
ctx.accept();
}).on('ready', function() {
conn.on('session', function(accept, reject) {
var session = accept();
session.once('exec', function(accept, reject, info) {
var stream = accept();
stream.exit(0);
stream.end();
});
});
});
});
client.on('ready', function() {
function callback(err, stream) {
assert(!err, makeMsg('Unexpected error: ' + err));
stream.resume();
if (++calledBack === 3)
client.end();
}
client.exec('foo', callback);
client.exec('bar', callback);
client.exec('baz', callback);
}).on('end', function() {
assert(calledBack === 3,
makeMsg('Only '
+ calledBack
+ '/3 callbacks called'));
});
},
what: 'Pipelined requests'
},
{ run: function() {
var client;
var server;
var r;
var calledBack = 0;
r = setup(
this,
{ username: USER,
password: PASSWORD,
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
ctx.accept();
}).on('ready', function() {
var reqs = [];
conn.on('session', function(accept, reject) {
if (reqs.length === 0) {
conn.rekey(function(err) {
assert(!err, makeMsg('Unexpected rekey error: ' + err));
reqs.forEach(function(accept) {
var session = accept();
session.once('exec', function(accept, reject, info) {
var stream = accept();
stream.exit(0);
stream.end();
});
});
});
}
reqs.push(accept);
});
});
});
client.on('ready', function() {
function callback(err, stream) {
assert(!err, makeMsg('Unexpected error: ' + err));
stream.resume();
if (++calledBack === 3)
client.end();
}
client.exec('foo', callback);
client.exec('bar', callback);
client.exec('baz', callback);
}).on('end', function() {
assert(calledBack === 3,
makeMsg('Only '
+ calledBack
+ '/3 callbacks called'));
});
},
what: 'Pipelined requests with intermediate rekeying'
},
{ run: function() {
var client;
var server;
var r;
r = setup(
this,
{ username: USER,
password: PASSWORD
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
ctx.accept();
}).on('ready', function() {
conn.on('session', function(accept, reject) {
var session = accept();
session.once('exec', function(accept, reject, info) {
var stream = accept();
stream.exit(0);
stream.end();
});
});
});
});
client.on('ready', function() {
client.exec('foo', function(err, stream) {
assert(!err, makeMsg('Unexpected error: ' + err));
stream.on('exit', function(code, signal) {
client.end();
});
});
});
},
what: 'Ignore outgoing after stream close'
},
{ run: function() {
var client;
var server;
var r;
r = setup(
this,
{ username: USER,
password: PASSWORD
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
ctx.accept();
}).on('ready', function() {
conn.on('session', function(accept, reject) {
accept().on('sftp', function(accept, reject) {
var sftp = accept();
// XXX: hack to get channel ...
var channel = sftp._readableState.pipes;
channel.unpipe(sftp);
sftp.unpipe(channel);
channel.exit(127);
channel.close();
});
});
});
});
client.on('ready', function() {
var timeout = setTimeout(function() {
assert(false, makeMsg('Unexpected SFTP timeout'));
}, 1000);
client.sftp(function(err, sftp) {
clearTimeout(timeout);
assert(err, makeMsg('Expected error'));
assert(err.code === 127,
makeMsg('Expected exit code 127, saw: ' + err.code));
client.end();
});
});
},
what: 'SFTP server aborts with exit-status'
},
{ run: function() {
var client;
var server;
var r;
r = setup(
this,
{ username: USER,
password: PASSWORD,
sock: new net.Socket()
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
ctx.accept();
}).on('ready', function() {});
});
client.on('ready', function() {
client.end();
});
},
what: 'Double pipe on unconnected, passed in net.Socket'
},
{ run: function() {
var client;
var server;
var r;
r = setup(
this,
{ username: USER },
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
ctx.accept();
});
conn.on('request', function(accept, reject, name, info) {
accept();
conn.forwardOut('good', 0, 'remote', 12345, function(err, ch) {
if (err) {
assert(!err, makeMsg('Unexpected error: ' + err));
}
conn.forwardOut('bad', 0, 'remote', 12345, function(err, ch) {
assert(err, makeMsg('Should receive error'));
client.end();
});
});
});
});
client.on('ready', function() {
// request forwarding
client.forwardIn('good', 0, function(err, port) {
if (err) {
assert(!err, makeMsg('Unexpected error: ' + err));
}
});
});
client.on('tcp connection', function(details, accept, reject) {
accept();
});
},
what: 'Client auto-rejects unrequested, allows requested forwarded-tcpip'
},
{ run: function() {
var client;
var server;
var r;
r = setup(
this,
{ username: USER,
password: PASSWORD
},
{ hostKeys: [HOST_KEY_RSA],
greeting: 'Hello world!'
}
);
client = r.client;
server = r.server;
var sawGreeting = false;
client.on('greeting', function(greeting) {
assert.strictEqual(greeting, 'Hello world!\r\n');
sawGreeting = true;
});
client.on('banner', function(message) {
assert.fail(null, null, makeMsg('Unexpected banner'));
});
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
assert(sawGreeting, makeMsg('Client did not see greeting'));
if (ctx.method === 'none')
return ctx.reject();
assert(ctx.method === 'password',
makeMsg('Unexpected auth method: ' + ctx.method));
assert(ctx.username === USER,
makeMsg('Unexpected username: ' + ctx.username));
assert(ctx.password === PASSWORD,
makeMsg('Unexpected password: ' + ctx.password));
ctx.accept();
}).on('ready', function() {
conn.end();
});
});
},
what: 'Server greeting'
},
{ run: function() {
var client;
var server;
var r;
r = setup(
this,
{ username: USER,
password: PASSWORD
},
{ hostKeys: [HOST_KEY_RSA],
banner: 'Hello world!'
}
);
client = r.client;
server = r.server;
var sawBanner = false;
client.on('greeting', function(greeting) {
assert.fail(null, null, makeMsg('Unexpected greeting'));
});
client.on('banner', function(message) {
assert.strictEqual(message, 'Hello world!\r\n');
sawBanner = true;
});
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
assert(sawBanner, makeMsg('Client did not see banner'));
if (ctx.method === 'none')
return ctx.reject();
assert(ctx.method === 'password',
makeMsg('Unexpected auth method: ' + ctx.method));
assert(ctx.username === USER,
makeMsg('Unexpected username: ' + ctx.username));
assert(ctx.password === PASSWORD,
makeMsg('Unexpected password: ' + ctx.password));
ctx.accept();
}).on('ready', function() {
conn.end();
});
});
},
what: 'Server banner'
},
{ run: function() {
var client;
var server;
var r;
var fastRejectSent = false;
function sendAcceptLater(accept) {
if (fastRejectSent)
accept();
else
setImmediate(sendAcceptLater, accept);
}
r = setup(
this,
{ username: USER },
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
ctx.accept();
});
conn.on('request', function(accept, reject, name, info) {
if (info.bindAddr === 'fastReject') {
// Will call reject on 'fastReject' soon
reject();
fastRejectSent = true;
} else
// but accept on 'slowAccept' later
sendAcceptLater(accept);
});
});
client.on('ready', function() {
var replyCnt = 0;
client.forwardIn('slowAccept', 0, function(err) {
assert(!err, makeMsg('Unexpected error: ' + err));
if (++replyCnt === 2)
client.end();
});
client.forwardIn('fastReject', 0, function(err) {
assert(err, makeMsg('Should receive error'));
if (++replyCnt === 2)
client.end();
});
});
},
what: 'Server responds to global requests in the right order'
},
{ run: function() {
var client;
var server;
var r;
r = setup(
this,
{ username: USER,
password: PASSWORD
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
var timer;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
ctx.accept();
}).on('ready', function() {
conn.on('session', function(accept, reject) {
var session = accept();
session.once('subsystem', function(accept, reject, info) {
assert.equal(info.name, 'netconf');
// Prevent success reply from being sent
conn._sshstream.channelSuccess = function() {};
var stream = accept();
stream.close();
timer = setTimeout(function() {
throw new Error(makeMsg('Expected client callback'));
}, 50);
});
});
});
});
client.on('ready', function() {
client.subsys('netconf', function(err, stream) {
clearTimeout(timer);
assert(err);
client.end();
});
});
},
what: 'Cleanup outstanding channel requests on channel close'
},
{ run: function() {
var client;
var server;
var r;
r = setup(
this,
{ username: USER,
password: PASSWORD
},
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
var timer;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
ctx.accept();
}).on('ready', function() {
conn.on('session', function(accept, reject) {
var session = accept();
session.once('exec', function(accept, reject, info) {
var stream = accept();
// Write enough to bring the Client's channel window to 0
// (currently 1MB)
var buf = new Buffer(2048);
for (var i = 0; i < 1000; ++i)
stream.write(buf);
stream.exit(0);
stream.close();
});
});
});
});
client.on('ready', function() {
client.exec('foo', function(err, stream) {
var sawClose = false;
assert(!err, makeMsg('Unexpected error'));
client._sshstream.on('CHANNEL_CLOSE:' + stream.incoming.id, onClose);
function onClose() {
// This handler gets called *after* the internal handler, so we
// should have seen `stream`'s `close` event already if the bug
// exists
assert(!sawClose, makeMsg('Premature close event'));
client.end();
}
stream.on('close', function() {
sawClose = true;
});
});
});
},
what: 'Channel emits close prematurely'
},
{ run: function() {
var client;
var server;
var r;
r = setup(
this,
{ username: USER },
{ hostKeys: [HOST_KEY_RSA], ident: 'OpenSSH_5.3' }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
ctx.accept();
});
conn.once('request', function(accept, reject, name, info) {
assert(name === 'tcpip-forward',
makeMsg('Unexpected request: ' + name));
accept(1337);
conn.forwardOut('good', 0, 'remote', 12345, function(err, ch) {
assert(!err, makeMsg('Unexpected error: ' + err));
client.end();
});
});
});
client.on('ready', function() {
// request forwarding
client.forwardIn('good', 0, function(err, port) {
assert(!err, makeMsg('Unexpected error: ' + err));
assert(port === 1337, makeMsg('Bad bound port: ' + port));
});
});
client.on('tcp connection', function(details, accept, reject) {
assert(details.destIP === 'good',
makeMsg('Bad incoming destIP: ' + details.destIP));
assert(details.destPort === 1337,
makeMsg('Bad incoming destPort: ' + details.destPort));
assert(details.srcIP === 'remote',
makeMsg('Bad incoming srcIP: ' + details.srcIP));
assert(details.srcPort === 12345,
makeMsg('Bad incoming srcPort: ' + details.srcPort));
accept();
});
},
what: 'OpenSSH 5.x workaround for binding on port 0'
},
{ run: function() {
var client;
var server;
var r;
var srvError;
var cliError;
r = setup(
this,
{ username: USER,
algorithms: {
cipher: [ 'aes128-cbc' ]
}
},
{ hostKeys: [HOST_KEY_RSA],
algorithms: {
cipher: [ 'aes128-ctr' ]
}
}
);
client = r.client;
server = r.server;
// Remove default client error handler added by `setup()` since we are
// expecting an error in this case
client.removeAllListeners('error');
function onError(err) {
if (this === client) {
assert(!cliError, makeMsg('Unexpected multiple client errors'));
cliError = err;
} else {
assert(!srvError, makeMsg('Unexpected multiple server errors'));
srvError = err;
}
assert(/handshake failed/i.test(err.message),
makeMsg('Wrong error message'));
}
server.on('connection', function(conn) {
// Remove default server connection error handler added by `setup()`
// since we are expecting an error in this case
conn.removeAllListeners('error');
function onGoodHandshake() {
assert(false, makeMsg('Handshake should have failed'));
}
conn.on('authentication', onGoodHandshake);
conn.on('ready', onGoodHandshake);
conn.on('error', onError);
});
client.on('ready', function() {
assert(false, makeMsg('Handshake should have failed'));
});
client.on('error', onError);
client.on('close', function() {
assert(cliError, makeMsg('Expected client error'));
assert(srvError, makeMsg('Expected client error'));
});
},
what: 'Handshake errors are emitted'
},
{ run: function() {
var client;
var server;
var r;
var cliError;
r = setup(
this,
{ username: USER, privateKey: KEY_RSA_BAD },
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
// Remove default client error handler added by `setup()` since we are
// expecting an error in this case
client.removeAllListeners('error');
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
assert(ctx.method === 'publickey' || ctx.method === 'none',
makeMsg('Unexpected auth method: ' + ctx.method));
assert(!ctx.signature, makeMsg('Unexpected signature'));
if (ctx.method === 'none')
return ctx.reject();
ctx.accept();
});
conn.on('ready', function() {
assert(false, makeMsg('Authentication should have failed'));
});
});
client.on('ready', function() {
assert(false, makeMsg('Authentication should have failed'));
});
client.on('error', function(err) {
if (cliError) {
assert(/all configured/i.test(err.message),
makeMsg('Wrong error message'));
} else {
cliError = err;
assert(/signing/i.test(err.message), makeMsg('Wrong error message'));
}
});
client.on('close', function() {
assert(cliError, makeMsg('Expected client error'));
});
},
what: 'Client signing errors are caught and emitted'
},
{ run: function() {
var client;
var server;
var r;
var srvError;
var cliError;
r = setup(
this,
{ username: USER, password: 'foo' },
{ hostKeys: [KEY_RSA_BAD] }
);
client = r.client;
server = r.server;
// Remove default client error handler added by `setup()` since we are
// expecting an error in this case
client.removeAllListeners('error');
server.on('connection', function(conn) {
// Remove default server connection error handler added by `setup()`
// since we are expecting an error in this case
conn.removeAllListeners('error');
conn.once('error', function(err) {
assert(/signing/i.test(err.message), makeMsg('Wrong error message'));
srvError = err;
});
conn.on('authentication', function(ctx) {
assert(false, makeMsg('Handshake should have failed'));
});
conn.on('ready', function() {
assert(false, makeMsg('Authentication should have failed'));
});
});
client.on('ready', function() {
assert(false, makeMsg('Handshake should have failed'));
});
client.on('error', function(err) {
assert(!cliError, makeMsg('Unexpected multiple client errors'));
assert(/KEY_EXCHANGE_FAILED/.test(err.message),
makeMsg('Wrong error message'));
cliError = err;
});
client.on('close', function() {
assert(srvError, makeMsg('Expected server error'));
assert(cliError, makeMsg('Expected client error'));
});
},
what: 'Server signing errors are caught and emitted'
},
{ run: function() {
var client;
var server;
var r;
var sawReady = false;
r = setup(
this,
{ username: '', password: 'foo' },
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
assert.strictEqual(ctx.username, '',
makeMsg('Expected empty username'));
ctx.accept();
}).on('ready', function() {
conn.end();
});
});
client.on('ready', function() {
sawReady = true;
}).on('close', function() {
assert.strictEqual(sawReady, true, makeMsg('Expected ready event'));
});
},
what: 'Empty username string works'
},
{ run: function() {
var client;
var server;
var r;
var sawReady = false;
r = setup(
this,
{ user: '', password: 'foo' },
{ hostKeys: [HOST_KEY_RSA] }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
assert.strictEqual(ctx.username, '',
makeMsg('Expected empty username'));
ctx.accept();
}).on('ready', function() {
conn.end();
});
});
client.on('ready', function() {
sawReady = true;
}).on('close', function() {
assert.strictEqual(sawReady, true, makeMsg('Expected ready event'));
});
},
what: 'Empty user string works'
},
{ run: function() {
var client;
var server;
var r;
var socketPath = '/foo';
var events = [];
var expected = [
['client', 'openssh_forwardInStreamLocal'],
['server',
'streamlocal-forward@openssh.com',
{ socketPath: socketPath }],
['client', 'forward callback'],
['client', 'unix connection', { socketPath: socketPath }],
['client', 'socket data', '1'],
['server', 'socket data', '2'],
['client', 'socket end'],
['server',
'cancel-streamlocal-forward@openssh.com',
{ socketPath: socketPath }],
['client', 'cancel callback']
];
r = setup(
this,
{ username: USER },
{ hostKeys: [HOST_KEY_RSA], ident: 'OpenSSH_7.1' }
);
client = r.client;
server = r.server;
server.on('connection', function(conn) {
conn.on('authentication', function(ctx) {
ctx.accept();
});
conn.on('request', function(accept, reject, name, info) {
events.push(['server', name, info]);
if (name === 'streamlocal-forward@openssh.com') {
accept();
conn.openssh_forwardOutStreamLocal(socketPath, function(err, ch) {
assert(!err, makeMsg('Unexpected error: ' + err));
ch.write('1');
ch.on('data', function(data) {
events.push(['server', 'socket data', data.toString()]);
ch.close();
});
});
} else if (name === 'cancel-streamlocal-forward@openssh.com') {
accept();
} else {
reject();
}
});
});
client.on('ready', function() {
// request forwarding
events.push(['client', 'openssh_forwardInStreamLocal']);
client.openssh_forwardInStreamLocal(socketPath, function(err) {
assert(!err, makeMsg('Unexpected error: ' + err));
events.push(['client', 'forward callback']);
});
client.on('unix connection', function(info, accept, reject) {
events.push(['client', 'unix connection', info]);
var stream = accept();
stream.on('data', function(data) {
events.push(['client', 'socket data', data.toString()]);
stream.write('2');
}).on('end', function() {
events.push(['client', 'socket end']);
client.openssh_unforwardInStreamLocal(socketPath, function(err) {
assert(!err, makeMsg('Unexpected error: ' + err));
events.push(['client', 'cancel callback']);
client.end();
});
});
});
});
client.on('end', function() {
var msg = 'Events mismatch\nActual:\n' + inspect(events)
+ '\nExpected:\n' + inspect(expected);
assert.deepEqual(events, expected, makeMsg(msg));
});
},
what: 'OpenSSH forwarded UNIX socket connection'
},
];
function setup(self, clientcfg, servercfg, timeout) {
self.state = {
clientReady: false,
serverReady: false,
clientClose: false,
serverClose: false
};
if (DEBUG) {
console.log('========================================================\n'
+ '[TEST] '
+ self.what
+ '\n========================================================');
clientcfg.debug = function(str) {
console.log('[CLIENT] ' + str);
};
servercfg.debug = function(str) {
console.log('[SERVER] ' + str);
};
}
var client = new Client();
var server = new Server(servercfg);
if (timeout === undefined)
timeout = DEFAULT_TEST_TIMEOUT;
var timer;
server.on('error', onError)
.on('connection', function(conn) {
conn.on('error', onError)
.on('ready', onReady);
server.close();
})
.on('close', onClose);
client.on('error', onError)
.on('ready', onReady)
.on('close', onClose);
function onError(err) {
var which = (this === client ? 'client' : 'server');
assert(false, makeMsg('Unexpected ' + which + ' error: ' + err));
}
function onReady() {
if (this === client) {
assert(!self.state.clientReady,
makeMsg('Received multiple ready events for client'));
self.state.clientReady = true;
} else {
assert(!self.state.serverReady,
makeMsg('Received multiple ready events for server'));
self.state.serverReady = true;
}
if (self.state.clientReady && self.state.serverReady)
self.onReady && self.onReady();
}
function onClose() {
if (this === client) {
assert(!self.state.clientClose,
makeMsg('Received multiple close events for client'));
self.state.clientClose = true;
} else {
assert(!self.state.serverClose,
makeMsg('Received multiple close events for server'));
self.state.serverClose = true;
}
if (self.state.clientClose && self.state.serverClose) {
clearTimeout(timer);
next();
}
}
process.nextTick(function() {
server.listen(0, 'localhost', function() {
if (timeout >= 0) {
timer = setTimeout(function() {
assert(false, makeMsg('Test timed out'));
}, timeout);
}
if (clientcfg.sock)
clientcfg.sock.connect(server.address().port, 'localhost');
else {
clientcfg.host = 'localhost';
clientcfg.port = server.address().port;
}
client.connect(clientcfg);
});
});
return { client: client, server: server };
}
function next() {
if (Array.isArray(process._events.exit))
process._events.exit = process._events.exit[1];
if (++t === tests.length)
return;
var v = tests[t];
v.run.call(v);
}
function makeMsg(what, msg) {
if (msg === undefined)
msg = what;
if (tests[t])
what = tests[t].what;
else
what = '<Unknown>';
return '[' + group + what + ']: ' + msg;
}
process.once('uncaughtException', function(err) {
if (t > -1 && !/(?:^|\n)AssertionError: /i.test(''+err))
console.log(makeMsg('Unexpected Exception:'));
throw err;
});
process.once('exit', function() {
assert(t === tests.length,
makeMsg('_exit',
'Only finished ' + t + '/' + tests.length + ' tests'));
});
next();