localtunnel-socks-client
Advanced tools
Comparing version 2.1.0 to 3.0.0
@@ -47,7 +47,5 @@ #!/usr/bin/env node | ||
describe: 'Socks proxy hostname', | ||
implies: 'socks-port' | ||
}) | ||
.option('socks-port', { | ||
describe: 'Socks proxy port', | ||
implies: 'socks-host' | ||
}) | ||
@@ -62,2 +60,4 @@ .options('o', { | ||
.require('port') | ||
.require('socks-host') | ||
.require('socks-port') | ||
.boolean('local-https') | ||
@@ -64,0 +64,0 @@ .boolean('allow-invalid-cert') |
@@ -0,1 +1,5 @@ | ||
# 3.0.0 (2021-07-14) | ||
- Make SOCKS proxy required | ||
# 2.0.1 (2021-01-09) | ||
@@ -2,0 +6,0 @@ |
@@ -17,8 +17,4 @@ /* eslint-disable consistent-return, no-underscore-dangle */ | ||
this.closed = false; | ||
this.socksHttpAgent = opts.socks_host && opts.socks_port | ||
? new SocksHttpAgent({ socksHost: opts.socks_host, socksPort: opts.socks_port }) | ||
: null; | ||
this.socksHttpsAgent = opts.socks_host && opts.socks_port | ||
? new SocksHttpsAgent({ socksHost: opts.socks_host, socksPort: opts.socks_port }) | ||
: null; | ||
this.socksHttpAgent = new SocksHttpAgent({ socksHost: opts.socks_host, socksPort: opts.socks_port }) | ||
this.socksHttpsAgent = new SocksHttpsAgent({ socksHost: opts.socks_host, socksPort: opts.socks_port }) | ||
if (!this.opts.host) { | ||
@@ -25,0 +21,0 @@ this.opts.host = 'https://localtunnel.me'; |
@@ -1,2 +0,2 @@ | ||
const { EventEmitter } = require('events'); | ||
const {EventEmitter} = require('events'); | ||
const debug = require('debug')('localtunnel:client'); | ||
@@ -6,156 +6,153 @@ const fs = require('fs'); | ||
const tls = require('tls'); | ||
const { Socket } = require('socks5-client'); | ||
const SocksClient = require('socks').SocksClient; | ||
const HeaderHostTransformer = require('./HeaderHostTransformer'); | ||
// manages groups of tunnels | ||
module.exports = class TunnelCluster extends EventEmitter { | ||
constructor(opts = {}) { | ||
super(opts); | ||
this.opts = opts; | ||
} | ||
constructor(opts = {}) { | ||
super(opts); | ||
this.opts = opts; | ||
} | ||
open() { | ||
const opt = this.opts; | ||
open() { | ||
const opt = this.opts; | ||
// Prefer IP if returned by the server | ||
const remoteHostOrIp = opt.remote_ip || opt.remote_host; | ||
const remotePort = opt.remote_port; | ||
const localHost = opt.local_host || 'localhost'; | ||
const localPort = opt.local_port; | ||
const localProtocol = opt.local_https ? 'https' : 'http'; | ||
const allowInvalidCert = opt.allow_invalid_cert; | ||
const socksHost = opt.socks_host; | ||
const socksPort = opt.socks_port; | ||
// Prefer IP if returned by the server | ||
const remoteHostOrIp = opt.remote_ip || opt.remote_host; | ||
const remotePort = opt.remote_port; | ||
const localHost = opt.local_host || 'localhost'; | ||
const localPort = opt.local_port; | ||
const localProtocol = opt.local_https ? 'https' : 'http'; | ||
const allowInvalidCert = opt.allow_invalid_cert; | ||
const socksHost = opt.socks_host; | ||
const socksPort = opt.socks_port; | ||
const socksSocket = socksHost && socksPort | ||
? new Socket({ socksHost, socksPort }) | ||
: null; | ||
const socksClientOptions = { | ||
proxy: { | ||
ipaddress: socksHost, | ||
port: socksPort, | ||
type: 5 | ||
}, | ||
debug( | ||
'establishing tunnel %s://%s:%s <> %s:%s', | ||
localProtocol, | ||
localHost, | ||
localPort, | ||
remoteHostOrIp, | ||
remotePort | ||
); | ||
destination: { | ||
host: remoteHostOrIp, | ||
port: remotePort | ||
}, | ||
if (socksSocket) debug('using socks proxy for connection to localtunnel server'); | ||
command: 'connect' | ||
}; | ||
// connection to localtunnel server | ||
const remote = socksSocket | ||
? socksSocket.connect(remotePort, remoteHostOrIp) | ||
: net.connect({ host: remoteHostOrIp, port: remotePort }) | ||
debug( | ||
'establishing tunnel %s://%s:%s <> %s:%s', | ||
localProtocol, | ||
localHost, | ||
localPort, | ||
remoteHostOrIp, | ||
remotePort | ||
); | ||
remote.setKeepAlive(true); | ||
SocksClient.createConnection(socksClientOptions, (err, info) => { | ||
if (err) throw err; | ||
remote.on('error', err => { | ||
debug('got remote connection error', err.message); | ||
const remote = info.socket; | ||
// emit connection refused errors immediately, because they | ||
// indicate that the tunnel can't be established. | ||
if (err.code === 'ECONNREFUSED') { | ||
this.emit( | ||
'error', | ||
new Error( | ||
`connection refused: ${remoteHostOrIp}:${remotePort} (check your firewall settings)` | ||
) | ||
); | ||
} | ||
remote.setKeepAlive(true); | ||
remote.end(); | ||
}); | ||
remote.on('error', err => { | ||
debug('got remote connection error', err.message); | ||
const connLocal = () => { | ||
if (remote.destroyed) { | ||
debug('remote destroyed'); | ||
this.emit('dead'); | ||
return; | ||
} | ||
// emit connection refused errors immediately, because they | ||
// indicate that the tunnel can't be established. | ||
if (err.code === 'ECONNREFUSED') { | ||
this.emit( | ||
'error', | ||
new Error( | ||
`connection refused: ${remoteHostOrIp}:${remotePort} (check your firewall settings)` | ||
) | ||
); | ||
} | ||
debug('connecting locally to %s://%s:%d', localProtocol, localHost, localPort); | ||
remote.pause(); | ||
remote.end(); | ||
}); | ||
if (allowInvalidCert) { | ||
debug('allowing invalid certificates'); | ||
} | ||
const connLocal = () => { | ||
if (remote.destroyed) { | ||
debug('remote destroyed'); | ||
this.emit('dead'); | ||
return; | ||
} | ||
const getLocalCertOpts = () => | ||
allowInvalidCert | ||
? { rejectUnauthorized: false } | ||
: { | ||
cert: fs.readFileSync(opt.local_cert), | ||
key: fs.readFileSync(opt.local_key), | ||
ca: opt.local_ca ? [fs.readFileSync(opt.local_ca)] : undefined, | ||
}; | ||
debug('connecting locally to %s://%s:%d', localProtocol, localHost, localPort); | ||
remote.pause(); | ||
// connection to local http server | ||
const local = opt.local_https | ||
? tls.connect({ host: localHost, port: localPort, ...getLocalCertOpts() }) | ||
: net.connect({ host: localHost, port: localPort }); | ||
if (allowInvalidCert) { | ||
debug('allowing invalid certificates'); | ||
} | ||
const remoteClose = () => { | ||
debug('remote close'); | ||
this.emit('dead'); | ||
local.end(); | ||
}; | ||
const getLocalCertOpts = () => | ||
allowInvalidCert | ||
? {rejectUnauthorized: false} | ||
: { | ||
cert: fs.readFileSync(opt.local_cert), | ||
key: fs.readFileSync(opt.local_key), | ||
ca: opt.local_ca ? [fs.readFileSync(opt.local_ca)] : undefined, | ||
}; | ||
remote.once('close', remoteClose); | ||
// connection to local http server | ||
const local = opt.local_https | ||
? tls.connect({host: localHost, port: localPort, ...getLocalCertOpts()}) | ||
: net.connect({host: localHost, port: localPort}); | ||
// TODO some languages have single threaded servers which makes opening up | ||
// multiple local connections impossible. We need a smarter way to scale | ||
// and adjust for such instances to avoid beating on the door of the server | ||
local.once('error', err => { | ||
debug('local error %s', err.message); | ||
local.end(); | ||
const remoteClose = () => { | ||
debug('remote close'); | ||
this.emit('dead'); | ||
local.end(); | ||
}; | ||
remote.removeListener('close', remoteClose); | ||
remote.once('close', remoteClose); | ||
if (err.code !== 'ECONNREFUSED') { | ||
return remote.end(); | ||
} | ||
// TODO some languages have single threaded servers which makes opening up | ||
// multiple local connections impossible. We need a smarter way to scale | ||
// and adjust for such instances to avoid beating on the door of the server | ||
local.once('error', err => { | ||
debug('local error %s', err.message); | ||
local.end(); | ||
// retrying connection to local server | ||
setTimeout(connLocal, 1000); | ||
}); | ||
remote.removeListener('close', remoteClose); | ||
local.once('connect', () => { | ||
debug('connected locally'); | ||
remote.resume(); | ||
if (err.code !== 'ECONNREFUSED') { | ||
return remote.end(); | ||
} | ||
let stream = remote; | ||
// retrying connection to local server | ||
setTimeout(connLocal, 1000); | ||
}); | ||
// if user requested specific local host | ||
// then we use host header transform to replace the host header | ||
if (opt.local_host) { | ||
debug('transform Host header to %s', opt.local_host); | ||
stream = remote.pipe(new HeaderHostTransformer({ host: opt.local_host })); | ||
} | ||
local.once('connect', () => { | ||
debug('connected locally'); | ||
remote.resume(); | ||
stream.pipe(local).pipe(remote); | ||
remote.pipe(local).pipe(remote); | ||
// when local closes, also get a new remote | ||
local.once('close', hadError => { | ||
debug('local connection closed [%s]', hadError); | ||
}); | ||
}); | ||
}; | ||
// when local closes, also get a new remote | ||
local.once('close', hadError => { | ||
debug('local connection closed [%s]', hadError); | ||
}); | ||
}); | ||
}; | ||
remote.on('data', data => { | ||
const match = data.toString().match(/^(\w+) (\S+)/); | ||
if (match) { | ||
this.emit('request', { | ||
method: match[1], | ||
path: match[2], | ||
remote.on('data', data => { | ||
const match = data.toString().match(/^(\w+) (\S+)/); | ||
if (match) { | ||
this.emit('request', { | ||
method: match[1], | ||
path: match[2], | ||
}); | ||
} | ||
}); | ||
// tunnel is considered open when remote connects | ||
this.emit('open', remote); | ||
connLocal(); | ||
}); | ||
} | ||
}); | ||
// tunnel is considered open when remote connects | ||
remote.once('connect', () => { | ||
this.emit('open', remote); | ||
connLocal(); | ||
}); | ||
} | ||
} | ||
}; |
{ | ||
"name": "localtunnel-socks-client", | ||
"description": "Expose localhost to the world", | ||
"version": "2.1.0", | ||
"version": "3.0.0", | ||
"license": "MIT", | ||
"repository": { | ||
"type": "git", | ||
"url": "git@code.samourai.io:dojo/localtunnel-socks-client.git" | ||
"url": "https://code.samourai.io/dojo/localtunnel-socks-client.git" | ||
}, | ||
@@ -20,11 +20,11 @@ "author": "Pavel Ševčík <pavel.sevcik@protonmail.com>", | ||
"axios": "0.21.1", | ||
"debug": "4.3.1", | ||
"debug": "4.3.2", | ||
"openurl": "1.1.1", | ||
"socks5-client": "^1.2.8", | ||
"socks": "^2.6.1", | ||
"socks5-http-client": "^1.0.4", | ||
"socks5-https-client": "^1.2.1", | ||
"yargs": "16.2.0" | ||
"yargs": "17.0.1" | ||
}, | ||
"devDependencies": { | ||
"mocha": "~8.2.1" | ||
"mocha": "~9.0.2" | ||
}, | ||
@@ -31,0 +31,0 @@ "engines": { |
@@ -91,4 +91,4 @@ # localtunnel-socks-client | ||
- `allow_invalid_cert` (boolean) Disable certificate checks for your local HTTPS server (ignore cert/key/ca options). | ||
- `socks_host` (string) Define socks host for use of socks proxy for localtunnel server connection | ||
- `socks_port` (string) Define socks port for use of socks proxy for localtunnel server connection | ||
- `socks_host` (string) [required] Define socks host for use of socks proxy for localtunnel server connection | ||
- `socks_port` (string) [required] Define socks port for use of socks proxy for localtunnel server connection | ||
@@ -95,0 +95,0 @@ Refer to [tls.createSecureContext](https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options) for details on the certificate options. |
Sorry, the diff of this file is not supported yet
27786
548
+ Addedsocks@^2.6.1
+ Addeddebug@4.3.2(transitive)
+ Addedip-address@9.0.5(transitive)
+ Addedsmart-buffer@4.2.0(transitive)
+ Addedsocks@2.8.4(transitive)
+ Addedsprintf-js@1.1.3(transitive)
+ Addedyargs@17.0.1(transitive)
- Removedsocks5-client@^1.2.8
- Removeddebug@4.3.1(transitive)
- Removedyargs@16.2.0(transitive)
Updateddebug@4.3.2
Updatedyargs@17.0.1