237 lines
6.0 KiB
JavaScript
Executable File
237 lines
6.0 KiB
JavaScript
Executable File
'use strict';
|
|
|
|
var pkg = require('../package.json');
|
|
var remote;
|
|
var local;
|
|
var isPiped = !process.stdin.isTTY;
|
|
var localAddress;
|
|
var localPort;
|
|
|
|
function usage() {
|
|
console.info("");
|
|
console.info("sclient.js v" + pkg.version);
|
|
console.info("Usage: sclient [--servername <string>] [-k | --insecure] <servername:port> <port>");
|
|
console.info(" ex: sclient whatever.com 3000");
|
|
console.info(" (whatever.com:443 localhost:3000)");
|
|
console.info(" ex: sclient whatever.com:4080 0.0.0.0:3000");
|
|
console.info("");
|
|
}
|
|
|
|
function parseFlags(argv) {
|
|
var args = argv.slice();
|
|
var flags = {};
|
|
|
|
args.sort(function (a, b) {
|
|
if ('-' === a[0]) {
|
|
if ('-' === b[0]) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
if ('-' === b[0]) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
});
|
|
|
|
args.some(function (arg, i) {
|
|
if (/^-k|--?insecure$/.test(arg)) {
|
|
flags.rejectUnauthorized = false;
|
|
args.splice(i, 1);
|
|
return true;
|
|
}
|
|
});
|
|
args.some(function (arg, i) {
|
|
if (/^--?servername$/.test(arg)) {
|
|
flags.servername = args[i + 1];
|
|
if (!flags.servername || /^-/.test(flags.servername)) {
|
|
usage();
|
|
process.exit(2);
|
|
}
|
|
args.splice(i, 2);
|
|
return true;
|
|
}
|
|
});
|
|
args.some(function (arg, i) {
|
|
if (/^--?p(ort)?$/.test(arg)) {
|
|
flags.port = args[i + 1];
|
|
if (!flags.port || /^-/.test(flags.port)) {
|
|
usage();
|
|
process.exit(201);
|
|
}
|
|
args.splice(i, 2);
|
|
return true;
|
|
}
|
|
});
|
|
args.some(function (arg, i) {
|
|
if (/^--?ssh$/.test(arg)) {
|
|
flags.wrapSsh = true;
|
|
args.splice(i, 1);
|
|
return true;
|
|
}
|
|
});
|
|
|
|
return {
|
|
flags: flags
|
|
, args: args
|
|
};
|
|
}
|
|
|
|
var sclient = require('../');
|
|
|
|
function testRemote(opts) {
|
|
var emitter = new (require('events').EventEmitter)();
|
|
|
|
sclient._test(opts).then(function () {
|
|
// connected successfully (and closed)
|
|
sclient._listen(emitter, opts);
|
|
}).catch(function (err) {
|
|
// did not connect succesfully
|
|
sclient._listen(emitter, opts);
|
|
console.warn("[warn] '" + opts.remoteAddr + ":" + opts.remotePort
|
|
+ "' may not be accepting connections: ", err.toString(), '\n');
|
|
});
|
|
|
|
emitter.once('listening', function (opts) {
|
|
console.info('[listening] ' + opts.remoteAddr + ":" + opts.remotePort
|
|
+ " <= " + opts.localAddress + ":" + opts.localPort);
|
|
|
|
if (opts.command) {
|
|
var args = [
|
|
opts.remoteUser + 'localhost'
|
|
, '-p', opts.localPort
|
|
// we're _inverse_ proxying ssh, so we must alias the serveranem and ignore the IP
|
|
, '-o', 'HostKeyAlias=' + opts.remoteAddr
|
|
, '-o', 'CheckHostIP=no'
|
|
];
|
|
var spawn = require('child_process').spawn;
|
|
if ('rsync' === opts.command) {
|
|
var remote = args.shift() + ':' + opts.remotePath;
|
|
args = [ remote, '-e', 'ssh ' + args.join(' ') ];
|
|
}
|
|
args = args.concat(opts.args);
|
|
var child = spawn(opts.command, args, { stdio: 'inherit' });
|
|
child.on('exit', function () {
|
|
console.info('closing...');
|
|
});
|
|
child.on('close', function () {
|
|
opts.server.close();
|
|
});
|
|
}
|
|
});
|
|
emitter.on('connect', function (sock) {
|
|
console.info('[connect] ' + sock.localAddress.replace('::1', 'localhost') + ":" + sock.localPort
|
|
+ " => " + opts.remoteAddr + ":" + opts.remotePort);
|
|
});
|
|
emitter.on('remote-error', function (err) {
|
|
console.error('[error] (remote) ' + err.toString());
|
|
});
|
|
emitter.on('local-error', function (err) {
|
|
console.error('[error] (local) ' + err.toString());
|
|
});
|
|
}
|
|
|
|
function main() {
|
|
var cmd = parseFlags(process.argv);
|
|
var binParam;
|
|
var remoteUser;
|
|
|
|
// Re-arrange argument order for ssh
|
|
if (cmd.flags.wrapSsh) {
|
|
cmd.args.splice(3, 0, 'ssh');
|
|
} else if (-1 !== [ 'ssh', 'rsync' ].indexOf((cmd.args[2]||'').split(':')[0])) {
|
|
cmd.flags.wrapSsh = true;
|
|
binParam = cmd.args.splice(2, 1);
|
|
cmd.args.splice(3, 0, binParam[0]);
|
|
}
|
|
|
|
remoteUser = (cmd.args[2]||'').split('@');
|
|
if (remoteUser[1]) {
|
|
// has 'user@' in front
|
|
remote = (remoteUser[1]||'').split(':');
|
|
remoteUser = remoteUser[0] + '@';
|
|
} else {
|
|
// no 'user@' in front
|
|
remote = (remoteUser[0]||'').split(':');
|
|
remoteUser = '';
|
|
}
|
|
local = (cmd.args[3]||'').split(':');
|
|
|
|
if (-1 !== [ 'ssh', 'rsync' ].indexOf(local[0])) {
|
|
cmd.flags.wrapSsh = true;
|
|
}
|
|
|
|
if (cmd.flags.wrapSsh) {
|
|
process.argv = cmd.args;
|
|
} else if (4 !== cmd.args.length) {
|
|
// arg 0 is node
|
|
// arg 1 is sclient
|
|
// arg 2 is remote
|
|
// arg 3 is local (or ssh or rsync)
|
|
if (isPiped) {
|
|
local = ['|'];
|
|
} else {
|
|
usage();
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Check for the first argument (what to connect to)
|
|
if (!remote[0]) {
|
|
usage();
|
|
return;
|
|
}
|
|
|
|
if (!local[0]) {
|
|
usage();
|
|
return;
|
|
}
|
|
|
|
// check if it looks like a port number
|
|
if (local[0] === String(parseInt(local[0], 10))) {
|
|
localPort = parseInt(local[0], 10);
|
|
localAddress = 'localhost';
|
|
} else {
|
|
localAddress = local[0]; // potentially '-' or '|' or '$'
|
|
localPort = parseInt(local[1], 10);
|
|
}
|
|
|
|
var opts = {
|
|
remoteUser: remoteUser
|
|
, remoteAddr: remote[0]
|
|
, remotePort: remote[1] || 443
|
|
, localAddress: localAddress
|
|
, localPort: localPort
|
|
, rejectUnauthorized: cmd.flags.rejectUnauthorized
|
|
, servername: cmd.flags.servername
|
|
, stdin: null
|
|
, stdout: null
|
|
};
|
|
|
|
if ('-' === localAddress || '|' === localAddress) {
|
|
opts.stdin = process.stdin;
|
|
opts.stdout = process.stdout;
|
|
// no need for port
|
|
} else if (-1 !== [ 'ssh', 'rsync' ].indexOf(localAddress)) {
|
|
cmd.flags.wrapSsh = true;
|
|
opts.localAddress = 'localhost';
|
|
opts.localPort = local[1] || 0; // choose at random
|
|
opts.command = localAddress;
|
|
opts.args = cmd.args.slice(4); // node, sclient, ssh, addr
|
|
if ('rsync' === opts.command) {
|
|
opts.remotePath = opts.remotePort;
|
|
opts.remotePort = 0;
|
|
}
|
|
if (!opts.remotePort) {
|
|
opts.remotePort = cmd.flags.port || 443;
|
|
}
|
|
} else if (!localPort) {
|
|
usage();
|
|
return;
|
|
}
|
|
|
|
testRemote(opts);
|
|
}
|
|
|
|
main();
|