From 46207328051195ab0dd151d9374ffe9251720682 Mon Sep 17 00:00:00 2001 From: Zachary Boyd Date: Thu, 7 Dec 2017 17:21:42 -0800 Subject: [PATCH] finished http proxy --- Dockerfile | 4 +- README.md | 2 +- bin/get-timezone.sh | 1 - bin/tor-router | 6 ++ package.json | 3 +- src/ControlServer.js | 9 +++ src/HTTPServer.js | 166 +++++++++++++++++++++++++++++++++++++++++++ src/index.js | 1 + 8 files changed, 186 insertions(+), 6 deletions(-) delete mode 100644 bin/get-timezone.sh create mode 100644 src/HTTPServer.js diff --git a/Dockerfile b/Dockerfile index f206192..91d6be0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,7 +26,7 @@ ADD tor-sources.list /etc/apt/sources.list.d/tor.list RUN bash /tmp/nodejs_install -RUN apt install -y --allow-unauthenticated deb.torproject.org-keyring nodejs tor git tzdata +RUN apt install -y nodejs tor git ADD package.json /app/package.json @@ -34,8 +34,6 @@ RUN npm install ADD . /app -# Grab the current local timezone from an external api and save it into /etc/timezone, otherwise Tor will complain and won't start - ENTRYPOINT [ "tor-router" ] CMD [ "-s", "-d", "-j", "1" ] \ No newline at end of file diff --git a/README.md b/README.md index 13f9331..e64c944 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ To install globally run: `npm install -g` Alternatively docker can be used. The build will retrieve the latest version of Tor from the offical Tor Project repository. To build run: `docker build -t znetstar/tor-router .` -To start run: `docker run --rm -it -p 9050:9050 znetstar/tor-router tor-router --help` +To start run: `docker run --rm -it -p 9050:9050 znetstar/tor-router` ## Usage diff --git a/bin/get-timezone.sh b/bin/get-timezone.sh deleted file mode 100644 index d1f0d73..0000000 --- a/bin/get-timezone.sh +++ /dev/null @@ -1 +0,0 @@ -curl -sL http://ip-api.com/json | node -e "process.stdin.resume(); process.stdin.on('data', (data) => { process.stdout.write(JSON.parse(data.toString('utf8')).timezone); process.exit(0); });" \ No newline at end of file diff --git a/bin/tor-router b/bin/tor-router index 510aac8..38eb565 100755 --- a/bin/tor-router +++ b/bin/tor-router @@ -16,6 +16,7 @@ program .option('-j, --instances <1>', 'Number of tor instances', Number) .option('-s, --socksPort [9050]', 'SOCKS Server port', Number) .option('-d, --dnsPort [9053]', 'DNS Server port', Number) + .option('-h, --httpPort [9080]', 'HTTP Server port', Number) .option('-l, --logLevel [info]', 'Log level (defaults to "info") set to "null" to disable logging', Number) .parse(process.argv); @@ -29,6 +30,7 @@ let instances = program.instances || Number(process.env.INSTANCES); let log_level = program.logLevel || process.env.LOG_LEVEL; let socks_port = (program.socksPort === true ? 9050 : program.socksPort) || Number(process.env.SOCKS_PORT); let dns_port = (program.dnsPort === true ? 9053 : program.dnsPort) || Number(process.env.DNS_PORT); +let http_port = (program.httpPort === true ? 9080 : program.httpPort) || Number(process.env.HTTP_PORT); let control_port = (program.controlPort === true ? 9077 : program.controlPort) || Number(process.env.CONTROL_PORT) || 9077; if (log_level === 'null') @@ -44,6 +46,10 @@ if (socks_port) { let socks = control.createSOCKSServer(socks_port); } +if (http_port) { + let http = control.createHTTPServer(http_port); +} + if (dns_port) { let dns = control.createDNSServer(dns_port); } diff --git a/package.json b/package.json index 65848e6..6bfca91 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tor-router", - "version": "3.0.7", + "version": "3.1.0", "main": "src/index.js", "repository": "git@github.com:znetstar/tor-router.git", "author": "Zachary Boyd ", @@ -27,6 +27,7 @@ "lodash": "^4.17.4", "native-dns": "git+https://github.com/znetstar/node-dns.git", "socket.io": "^1.7.3", + "socks-proxy-agent": "^3.0.1", "socksv5": "git+https://github.com/lee-elenbaas/socksv5.git", "temp": "^0.8.3", "winston": "^2.3.1" diff --git a/src/ControlServer.js b/src/ControlServer.js index 33c18a7..c7b98d1 100644 --- a/src/ControlServer.js +++ b/src/ControlServer.js @@ -2,6 +2,7 @@ const HTTPServer = require('http').Server; const TorPool = require('./TorPool'); const SOCKSServer = require('./SOCKSServer'); const DNSServer = require('./DNSServer'); +const HTTPProxyServer = require('./HTTPServer'); class ControlServer { constructor(logger) { @@ -21,6 +22,7 @@ class ControlServer { socket.on('createTorPool', this.createTorPool.bind(this)); socket.on('createSOCKSServer', this.createSOCKSServer.bind(this)); socket.on('createDNSServer', this.createDNSServer.bind(this)); + socket.on('createHTTPServer', this.createHTTPServer.bind(this)); socket.on('queryInstances', (callback) => { if (!this.torPool) return callback({ message: 'No pool created' }); @@ -51,6 +53,13 @@ class ControlServer { return this.socksServer; } + createHTTPServer(port) { + this.httpServer = new HTTPProxyServer(this.torPool, this.logger); + this.httpServer.listen(port || 9080); + this.logger && this.logger.info(`[http]: Listening on ${port}`); + return this.httpServer; + } + createDNSServer(port) { this.dnsServer = new DNSServer(this.torPool, this.logger); this.dnsServer.serve(port || 9053); diff --git a/src/HTTPServer.js b/src/HTTPServer.js new file mode 100644 index 0000000..5909f0d --- /dev/null +++ b/src/HTTPServer.js @@ -0,0 +1,166 @@ +const http = require('http'); +const Server = http.Server; +const domain = require('domain'); +const socks = require('socksv5'); +const URL = require('url'); +const SocksProxyAgent = require('socks-proxy-agent'); + +class HTTPServer extends Server { + constructor(tor_pool, logger) { + let handle_http_connections = (req, res) => { + let d = domain.create(); + + d.add(req); + d.add(res); + + let url = URL.parse(req.url); + url.port = url.port || 80; + + var buffer = []; + + function onIncomingData(chunk) { + buffer.push(chunk); + } + + function preConnectClosed() { + req.finished = true; + } + + req.on('data', onIncomingData); + req.on('end', preConnectClosed); + + + let connect = (tor_instance) => { + let socks_port = tor_instance.socks_port; + logger && logger.info(`[http]: ${req.connection.remoteAddress}:${req.connection.remotePort} → 127.0.0.1:${socks_port} → ${url.hostname}:${url.port}`); + + d.run(() => { + let proxy_req = http.request({ + method: req.method, + hostname: url.hostname, + port: url.port, + path: url.path, + headers: req.headers, + agent: new SocksProxyAgent(`socks://127.0.0.1:${socks_port}`) + }, (proxy_res) => { + d.add(proxy_res); + proxy_res.on('data', (chunk) => { + res.write(chunk); + }); + + proxy_res.on('end', () => { + res.end(); + }); + + res.writeHead(proxy_res.statusCode, proxy_res.headers); + }); + + req.removeListener('data', onIncomingData); + + req.on('data', (chunk) => { + proxy_req.write(chunk); + }) + + req.on('end', () => { + proxy_req.end(); + }) + + while (buffer.length) { + proxy_req.write(buffer.shift()); + } + + if (req.finished) + proxy_req.end(); + + d.add(proxy_req); + }); + }; + if (tor_pool.instances.length) { + connect(tor_pool.next()); + } else { + logger.debug(`[http]: a connection has been attempted, but no tor instances are live... waiting for an instance to come online`); + tor_pool.once('instance_created', connect); + } + }; + + let handle_tcp_connections = (req, inbound_socket, head) => { + let d = domain.create(); + + d.add(socket); + + let url = URL.parse(req.url); + let hostname = url.host.split(':').shift(); + let port = url.host.split(':').pop(); + + let connect = (tor_instance) => { + let socks_port = tor_instance.socks_port; + logger && logger.info(`[http]: ${req.connection.remoteAddress}:${req.connection.remotePort} → 127.0.0.1:${socks_port} → ${hostname}:${port}`) + + d.on('error', onClose); + + d.add(inbound_socket); + + var buffer = []; + let onInboundData = function (data) { + buffer.push(data); + }; + + let onClose = (error) => { + inbound_socket && inbound_socket.end(); + outbound_socket && outbound_socket.end(); + + inbound_socket = outbound_socket = buffer = void(0); + + if (error) + this.logger.error(`[http]: an error occured: ${error.message}`) + + d.exit(); + }; + + d.run(() => { + socks.connect({ + host: hostname, + port: port, + proxyHost: '127.0.0.1', + proxyPort: socks_port, + localDNS: false, + auths: [ socks.auth.None() ] + }, ($outbound_socket) => { + outbound_socket = $outbound_socket; + d.add(outbound_socket); + outbound_socket && outbound_socket.on('close', onClose); + + inbound_socket && inbound_socket.removeListener('data', onInboundData); + inbound_socket && inbound_socket.on('data', (data) => { + outbound_socket && outbound_socket.write(data); + }); + + outbound_socket && outbound_socket.on('data', (data) => { + inbound_socket && inbound_socket.write(data); + }); + + outbound_socket && outbound_socket.on('error', onClose); + + while (buffer && buffer.length && outbound_socket) { + outbound_socket.write(buffer.shift()); + } + }) + }); + }; + if (tor_pool.instances.length) { + connect(tor_pool.next()); + } else { + logger.debug(`[http]: a connection has been attempted, but no tor instances are live... waiting for an instance to come online`); + tor_pool.once('instance_created', connect); + } + }; + + super(handle_http_connections); + this.on('connect', handle_tcp_connections); + + this.logger = logger; + this.tor_pool = tor_pool; + } +}; + +module.exports = HTTPServer; \ No newline at end of file diff --git a/src/index.js b/src/index.js index 71288ce..cc80eb4 100644 --- a/src/index.js +++ b/src/index.js @@ -3,5 +3,6 @@ module.exports = { TorPool: require('./TorPool'), DNSServer: require('./DNSServer'), SOCKSServer: require('./SOCKSServer'), + HTTPServer: require('./HTTPServer'), ControlServer: require('./ControlServer') }; \ No newline at end of file