Compare commits
	
		
			17 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 6e796da80a | |||
| 415ed10b99 | |||
| 6fdf889b0b | |||
| c345d9ec69 | |||
| 58cbe914c1 | |||
| 7add115e5f | |||
| d390df175a | |||
| d095381a40 | |||
| 8b641db470 | |||
| 0361e5762d | |||
| eba2b4e5b2 | |||
| a1a16005c1 | |||
| fa3de139a4 | |||
| 1287ccc1ce | |||
| f14708495a | |||
| a052db3a23 | |||
| 5439f89482 | 
							
								
								
									
										77
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										77
									
								
								README.md
									
									
									
									
									
								
							@ -5,11 +5,11 @@ Secure Client for exposing TLS (aka SSL) secured services as plain-text connecti
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Also ideal for multiplexing a single port with multiple protocols using SNI.
 | 
					Also ideal for multiplexing a single port with multiple protocols using SNI.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Unwrap a TLS connection
 | 
					Unwrap a TLS connection:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```bash
 | 
					```bash
 | 
				
			||||||
$ sclient whatever.com:443 localhost:3000
 | 
					$ sclient whatever.com:443 localhost:3000
 | 
				
			||||||
> [listening] telebit.cloud:443 <= localhost:3000
 | 
					> [listening] whatever.com:443 <= localhost:3000
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Connect via Telnet
 | 
					Connect via Telnet
 | 
				
			||||||
@ -21,9 +21,35 @@ $ telnet localhost 3000
 | 
				
			|||||||
Connect via netcat (nc)
 | 
					Connect via netcat (nc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```bash
 | 
					```bash
 | 
				
			||||||
$ nc telnet localhost 3000
 | 
					$ nc localhost 3000
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cURL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					$ curl http://localhost:3000 -H 'Host: whatever.com'
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Inverse SSH proxy (ssh over https):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					$ sclient ssh user@example.com
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(this is the same as a normal SSH Proxy, just easier to type):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					$ ssh -o ProxyCommand="sclient %h" user@example.com
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Inverse rsync proxy (rsync over https):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					$ sclient rsync user@example.com:path/ path/
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A poor man's (or Windows user's) makeshift replacement for `openssl s_client`, `stunnel`, or `socat`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Install
 | 
					Install
 | 
				
			||||||
=======
 | 
					=======
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -43,9 +69,12 @@ Usage
 | 
				
			|||||||
=====
 | 
					=====
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```bash
 | 
					```bash
 | 
				
			||||||
sclient <remote> <local> [-k | --insecure]
 | 
					sclient [flags] [ssh|rsync] <remote> [local]
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* flags
 | 
				
			||||||
 | 
					  * `-k, --insecure` ignore invalid TLS (SSL/HTTPS) certificates
 | 
				
			||||||
 | 
					  * `--servername <string>` spoof SNI (to disable use IP as <remote> and do not use this option)
 | 
				
			||||||
* remote
 | 
					* remote
 | 
				
			||||||
  * must have servername (i.e. example.com)
 | 
					  * must have servername (i.e. example.com)
 | 
				
			||||||
  * port is optional (default is 443)
 | 
					  * port is optional (default is 443)
 | 
				
			||||||
@ -71,5 +100,43 @@ sclient telebit.cloud:443 localhost:3000
 | 
				
			|||||||
Ignore a bad TLS/SSL/HTTPS certificate and connect anyway.
 | 
					Ignore a bad TLS/SSL/HTTPS certificate and connect anyway.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```bash
 | 
					```bash
 | 
				
			||||||
sclient badtls.telebit.cloud:443 localhost:3000 -k
 | 
					sclient -k badtls.telebit.cloud:443 localhost:3000
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Reading from stdin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					sclient telebit.cloud:443 -
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					sclient telebit.cloud:443 - </path/to/file
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### ssh over https
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					sclient ssh user@telebit.cloud
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### rsync over https
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					sclient rsync -av user@telebit.cloud:my-project/ ~/my-project/
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Piping
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					printf "GET / HTTP/1.1\r\nHost: telebit.cloud\r\n\r\n" | sclient telebit.cloud:443
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Testing for security vulnerabilities on the remote:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					sclient --servername "Robert'); DROP TABLE Students;" -k example.com localhost:3000
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					sclient --servername "../../../.hidden/private.txt" -k example.com localhost:3000
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										245
									
								
								bin/sclient.js
									
									
									
									
									
								
							
							
						
						
									
										245
									
								
								bin/sclient.js
									
									
									
									
									
								
							@ -1,57 +1,262 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env node
 | 
				
			||||||
'use strict';
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var pkg = require('../package.json');
 | 
					var pkg = require('../package.json');
 | 
				
			||||||
var remote = (process.argv[2]||'').split(':');
 | 
					var remote;
 | 
				
			||||||
var local = (process.argv[3]||'').split(':');
 | 
					var local;
 | 
				
			||||||
 | 
					var isPiped = !process.stdin.isTTY;
 | 
				
			||||||
var localAddress;
 | 
					var localAddress;
 | 
				
			||||||
var localPort;
 | 
					var localPort;
 | 
				
			||||||
var rejectUnauthorized;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
function usage() {
 | 
					function usage() {
 | 
				
			||||||
  console.info("");
 | 
					  console.info("");
 | 
				
			||||||
  console.info("sclient.js v" + pkg.version);
 | 
					  console.info("sclient.js v" + pkg.version);
 | 
				
			||||||
  console.info("Usage: sclient <servername:port> <port> [-k | --insecure]");
 | 
					  console.info("Usage: sclient  [--servername <string>] [-k | --insecure] <servername:port> <port>");
 | 
				
			||||||
  console.info("   ex: sclient whatever.com 3000");
 | 
					  console.info("   ex: sclient whatever.com 3000");
 | 
				
			||||||
  console.info("       (whatever.com:443 localhost:3000)");
 | 
					  console.info("       (whatever.com:443 localhost:3000)");
 | 
				
			||||||
  console.info("   ex: sclient whatever.com:4080 0.0.0.0:3000");
 | 
					  console.info("   ex: sclient whatever.com:4080 0.0.0.0:3000");
 | 
				
			||||||
  console.info("");
 | 
					  console.info("");
 | 
				
			||||||
  process.exit(0);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function parseFlags(argv) {
 | 
				
			||||||
 | 
					  var args = argv.slice();
 | 
				
			||||||
 | 
					  var flags = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  args.some(function (arg, i) {
 | 
				
			||||||
 | 
					    if (/^--?socks5$/.test(arg)) {
 | 
				
			||||||
 | 
					      flags.socks5 = args[i + 1];
 | 
				
			||||||
 | 
					      if (!flags.socks5 || /^-/.test(flags.socks5)) {
 | 
				
			||||||
 | 
					        usage();
 | 
				
			||||||
 | 
					        process.exit(202);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      args.splice(i, 2);
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // This works for most (but not all)
 | 
				
			||||||
 | 
					  // of the ssh and rsync flags - because they mostly don't have arguments
 | 
				
			||||||
 | 
					  args.sort(function (a, b) {
 | 
				
			||||||
 | 
					    if ('-' === a[0]) {
 | 
				
			||||||
 | 
					      if ('-' === b[0]) {
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if ('-' === b[0]) {
 | 
				
			||||||
 | 
					      return -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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(' ') ];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (opts.socks5) {
 | 
				
			||||||
 | 
					        args.push('-D');
 | 
				
			||||||
 | 
					        args.push('localhost:' + opts.socks5);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      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', 'vpn' ].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', 'vpn' ].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]) {
 | 
					  if (!remote[0]) {
 | 
				
			||||||
    usage();
 | 
					    usage();
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
if (!remote[1]) {
 | 
					 | 
				
			||||||
  remote[1] = 443;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!local[0]) {
 | 
					  if (!local[0]) {
 | 
				
			||||||
    usage();
 | 
					    usage();
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // check if it looks like a port number
 | 
				
			||||||
  if (local[0] === String(parseInt(local[0], 10))) {
 | 
					  if (local[0] === String(parseInt(local[0], 10))) {
 | 
				
			||||||
    localPort = parseInt(local[0], 10);
 | 
					    localPort = parseInt(local[0], 10);
 | 
				
			||||||
    localAddress = 'localhost';
 | 
					    localAddress = 'localhost';
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
  localAddress = local[0];
 | 
					    localAddress = local[0]; // potentially '-' or '|' or '$'
 | 
				
			||||||
    localPort = parseInt(local[1], 10);
 | 
					    localPort = parseInt(local[1], 10);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if (!localPort) {
 | 
					  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', 'vpn' ].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
 | 
				
			||||||
 | 
					    opts.socks5 = cmd.flags.socks5;
 | 
				
			||||||
 | 
					    if ('rsync' === opts.command) {
 | 
				
			||||||
 | 
					      opts.remotePath = opts.remotePort;
 | 
				
			||||||
 | 
					      opts.remotePort = 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if ('vpn' === opts.command) {
 | 
				
			||||||
 | 
					      opts.command = 'ssh';
 | 
				
			||||||
 | 
					      if (!opts.socks5) {
 | 
				
			||||||
 | 
					        usage();
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (!opts.remotePort) {
 | 
				
			||||||
 | 
					      opts.remotePort = cmd.flags.port || 443;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } else if (!localPort) {
 | 
				
			||||||
    usage();
 | 
					    usage();
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if (/^-k|--insecure$/.test(process.argv[4])) {
 | 
					  testRemote(opts);
 | 
				
			||||||
  rejectUnauthorized = false;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var opts = {
 | 
					main();
 | 
				
			||||||
  remoteAddr: remote[0]
 | 
					 | 
				
			||||||
, remotePort: remote[1]
 | 
					 | 
				
			||||||
, localAddress: localAddress
 | 
					 | 
				
			||||||
, localPort: localPort
 | 
					 | 
				
			||||||
, rejectUnauthorized: rejectUnauthorized
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
require('../')(opts);
 | 
					 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										59
									
								
								index.js
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								index.js
									
									
									
									
									
								
							@ -1,26 +1,39 @@
 | 
				
			|||||||
'use strict';
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var PromiseA = global.Promise;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var net = require('net');
 | 
					var net = require('net');
 | 
				
			||||||
var tls = require('tls');
 | 
					var tls = require('tls');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function listenForConns(opts) {
 | 
					function listenForConns(emitter, opts) {
 | 
				
			||||||
  var server = net.createServer(function (c) {
 | 
					  function pipeConn(c, out) {
 | 
				
			||||||
    var sclient = tls.connect({
 | 
					    var sclient = tls.connect({
 | 
				
			||||||
      servername: opts.remoteAddr, host: opts.remoteAddr, port: opts.remotePort
 | 
					      servername: opts.remoteAddr, host: opts.remoteAddr, port: opts.remotePort
 | 
				
			||||||
    , rejectUnauthorized: opts.rejectUnauthorized
 | 
					    , rejectUnauthorized: opts.rejectUnauthorized
 | 
				
			||||||
    }, function () {
 | 
					    }, function () {
 | 
				
			||||||
      console.info('[connect] ' + sclient.localAddress.replace('::1', 'localhost') + ":" + sclient.localPort
 | 
					      emitter.emit('connect', sclient);
 | 
				
			||||||
        + " => " + opts.remoteAddr + ":" + opts.remotePort);
 | 
					 | 
				
			||||||
      c.pipe(sclient);
 | 
					      c.pipe(sclient);
 | 
				
			||||||
      sclient.pipe(c);
 | 
					      sclient.pipe(out || c);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    sclient.on('error', function (err) {
 | 
					    sclient.on('error', function (err) {
 | 
				
			||||||
      console.error('[error] (remote) ' + err.toString());
 | 
					      emitter.emit('remote-error', err);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    c.on('error', function (err) {
 | 
					    c.on('error', function (err) {
 | 
				
			||||||
      console.error('[error] (local) ' + err.toString());
 | 
					      emitter.emit('local-error', err);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					    if (out) {
 | 
				
			||||||
 | 
					      out.on('error', function (err) {
 | 
				
			||||||
 | 
					        emitter.emit('local-error', err);
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if ('-' === opts.localAddress || '|' === opts.localAddress) {
 | 
				
			||||||
 | 
					    pipeConn(opts.stdin, opts.stdout);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  var server = net.createServer(pipeConn);
 | 
				
			||||||
  server.on('error', function (err) {
 | 
					  server.on('error', function (err) {
 | 
				
			||||||
    console.error('[error] ' + err.toString());
 | 
					    console.error('[error] ' + err.toString());
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
@ -28,24 +41,38 @@ function listenForConns(opts) {
 | 
				
			|||||||
    host: opts.localAddress
 | 
					    host: opts.localAddress
 | 
				
			||||||
  , port: opts.localPort
 | 
					  , port: opts.localPort
 | 
				
			||||||
  }, function () {
 | 
					  }, function () {
 | 
				
			||||||
    console.info('[listening] ' + opts.remoteAddr + ":" + opts.remotePort
 | 
					    opts.localPort = this.address().port;
 | 
				
			||||||
      + " <= " + opts.localAddress + ":" + opts.localPort);
 | 
					    opts.server = this;
 | 
				
			||||||
 | 
					    emitter.emit('listening', opts);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function testConn(opts) {
 | 
					function testConn(opts) {
 | 
				
			||||||
 | 
					  return new PromiseA(function (resolve, reject) {
 | 
				
			||||||
    // Test connection first
 | 
					    // Test connection first
 | 
				
			||||||
  var tlsSock = tls.connect({
 | 
					    var tlsOpts = {
 | 
				
			||||||
    servername: opts.remoteAddr, host: opts.remoteAddr, port: opts.remotePort
 | 
					      host: opts.remoteAddr, port: opts.remotePort
 | 
				
			||||||
    , rejectUnauthorized: opts.rejectUnauthorized
 | 
					    , rejectUnauthorized: opts.rejectUnauthorized
 | 
				
			||||||
  }, function () {
 | 
					    };
 | 
				
			||||||
 | 
					    if (opts.servername) {
 | 
				
			||||||
 | 
					      tlsOpts.servername = opts.servername;
 | 
				
			||||||
 | 
					    } else if (/^[\w\.\-]+\.[a-z]{2,}$/i.test(opts.remoteAddr)) {
 | 
				
			||||||
 | 
					      tlsOpts.servername = opts.remoteAddr.toLowerCase();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (opts.alpn) {
 | 
				
			||||||
 | 
					      tlsOpts.ALPNProtocols = [ 'http', 'h2' ];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var tlsSock = tls.connect(tlsOpts, function () {
 | 
				
			||||||
      tlsSock.end();
 | 
					      tlsSock.end();
 | 
				
			||||||
    listenForConns(opts);
 | 
					      resolve();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    tlsSock.on('error', function (err) {
 | 
					    tlsSock.on('error', function (err) {
 | 
				
			||||||
    console.warn("[warn] '" + opts.remoteAddr + ":" + opts.remotePort + "' may not be accepting connections: ", err.toString(), '\n');
 | 
					      reject(err);
 | 
				
			||||||
    listenForConns(opts);
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = testConn;
 | 
					// no public exports yet
 | 
				
			||||||
 | 
					// the API is for the commandline only
 | 
				
			||||||
 | 
					module.exports._test = testConn;
 | 
				
			||||||
 | 
					module.exports._listen = listenForConns;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,9 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "sclient",
 | 
					  "name": "sclient",
 | 
				
			||||||
  "version": "1.0.1",
 | 
					  "version": "1.4.3",
 | 
				
			||||||
  "description": "Secure Client for exposing TLS (aka SSL) secured services as plain-text connections locally. Also ideal for multiplexing a single port with multiple protocols using SNI.",
 | 
					  "description": "Secure Client for exposing TLS (aka SSL) secured services as plain-text connections locally. Also ideal for multiplexing a single port with multiple protocols using SNI.",
 | 
				
			||||||
  "main": "index.js",
 | 
					  "main": "index.js",
 | 
				
			||||||
 | 
					  "homepage": "https://telebit.cloud/sclient/",
 | 
				
			||||||
  "bin": {
 | 
					  "bin": {
 | 
				
			||||||
    "sclient": "bin/sclient.js"
 | 
					    "sclient": "bin/sclient.js"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user