Advertisement
Guest User

NodeJS proxy

a guest
Feb 17th, 2020
1,016
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/usr/bin/env node
  2. 'use strict';
  3. var net = require('net'), tls = require('tls');
  4. var HTTPParser = process.binding('http_parser').HTTPParser;
  5.  
  6. function main() {
  7.     //convert `-key value` to cfg[key]=value
  8.     var cfg = process.argv.slice(2/*skip ["node", "xxx.js"]*/).reduce(function (cfg, arg, i, argv) {
  9.         return (i % 2 === 0 && (arg.slice(0, 1) === '-' && (cfg[arg.slice(1)] = argv[i + 1])), cfg);
  10.     }, {local_host: '', local_port: 0, remote_host: '', remote_port: 0, usr: '', pwd: ''});
  11.     cfg.local_host = cfg.local_host || 'localhost';
  12.     cfg.local_port = (cfg.local_port & 0xffff) || 8080;
  13.     cfg.remote_port = (cfg.remote_port & 0xffff) || 8080;
  14.     cfg.is_remote_https = cfg.is_remote_https === 'true';
  15.     cfg.ignore_https_cert = cfg.ignore_https_cert === 'true';
  16.  
  17.     if (!cfg.local_host || !cfg.local_port || !cfg.remote_host || !cfg.remote_port || !cfg.usr || !cfg.pwd)
  18.         return console.error('Usage of parameters:\n'
  19.             + '-local_host host\t' + 'Listening address. Default: localhost. (* means all interfaces)\n'
  20.             + '-local_port port\t' + 'Listening port. Default: 8080\n'
  21.             + '-remote_host host\t' + 'Real proxy/PAC server address\n'
  22.             + '-remote_port port\t' + 'Real proxy/PAC server port. Default: 8080\n'
  23.             + '-usr user\t\t' + 'Real proxy/PAC server user id\n'
  24.             + '-pwd password\t\t' + 'Real proxy/PAC user password\n'
  25.             + '\n'
  26.             + '-is_remote_https true/false\t' + 'Talk to real proxy/PAC server with HTTPS. Default: false\n'
  27.             + '-ignore_https_cert true/false\t' + 'ignore error when verify certificate of real proxy/PAC server. Default: false\n'
  28.         );
  29.     console.log('Using parameters: ' + JSON.stringify(cfg, null, '  '));
  30.     cfg.buf_proxy_basic_auth = new Buffer('Proxy-Authorization: Basic ' + new Buffer(cfg.usr + ':' + cfg.pwd).toString('base64'));
  31.  
  32.     createPortForwarder(cfg.local_host, cfg.local_port, cfg.remote_host, cfg.remote_port, cfg.buf_proxy_basic_auth, cfg.is_remote_https, cfg.ignore_https_cert);
  33. }
  34.  
  35. var CR = 0xd, LF = 0xa, BUF_CR = new Buffer([0xd]), BUF_CR_LF_CR_LF = new Buffer([0xd, 0xa, 0xd, 0xa]),
  36.     BUF_LF_LF = new Buffer([0xa, 0xa]), BUF_PROXY_CONNECTION_CLOSE = new Buffer('Proxy-Connection: close');
  37. var STATE_NONE = 0, STATE_FOUND_LF = 1, STATE_FOUND_LF_CR = 2;
  38.  
  39. function createPortForwarder(local_host, local_port, remote_host, remote_port, buf_proxy_basic_auth, is_remote_https, ignore_https_cert) {
  40.     net.createServer({allowHalfOpen: true}, function (socket) {
  41.         var realCon = (is_remote_https ? tls : net).connect({
  42.             port: remote_port, host: remote_host, allowHalfOpen: true,
  43.             rejectUnauthorized: !ignore_https_cert /*not used when is_remote_https false*/
  44.         });
  45.         realCon.on('data', function (buf) {
  46.             //console.log('<<<<' + (Date.t=new Date()) + '.' + Date.t.getMilliseconds() + '\n' + buf.toString('ascii'));
  47.             socket.write(buf);
  48.             realCon.__haveGotData = true;
  49.         }).on('end', function () {
  50.             socket.end();
  51.             if (!realCon.__haveGotData && !realCon.__haveShownError) {
  52.                 console.error('[LocalProxy(:' + local_port + ')][Connection to ' + remote_host + ':' + remote_port + '] Error: ended by remote peer');
  53.                 realCon.__haveShownError = true;
  54.             }
  55.         }).on('close', function () {
  56.             socket.end();
  57.             if (!realCon.__haveGotData && !realCon.__haveShownError) {
  58.                 console.error('[LocalProxy(:' + local_port + ')][Connection to ' + remote_host + ':' + remote_port + '] Error: reset by remote peer');
  59.                 realCon.__haveShownError = true;
  60.             }
  61.         }).on('error', function (err) {
  62.             console.error('[LocalProxy(:' + local_port + ')][Connection to ' + remote_host + ':' + remote_port + '] ' + err);
  63.             realCon.__haveShownError = true;
  64.         });
  65.  
  66.         var parser = new HTTPParser(HTTPParser.REQUEST);
  67.         parser[HTTPParser.kOnHeadersComplete] = function (versionMajor, versionMinor, headers, method,
  68.                                                           url, statusCode, statusMessage, upgrade,
  69.                                                           shouldKeepAlive) {
  70.             parser.__is_headers_complete = true;
  71.             parser.__upgrade = upgrade;
  72.             parser.__method = method;
  73.         };
  74.  
  75.         var state = STATE_NONE;
  76.  
  77.         socket.on('data', function (buf) {
  78.             if (!parser) {
  79.                 realCon.write(buf);
  80.                 return
  81.             }
  82.  
  83.             var buf_ary = [], unsavedStart = 0, buf_len = buf.length;
  84.  
  85.             for (var i = 0; i < buf_len; i++) {
  86.                 //find first LF
  87.                 if (state === STATE_NONE) {
  88.                     if (buf[i] === LF) {
  89.                         state = STATE_FOUND_LF;
  90.                     }
  91.                     continue;
  92.                 }
  93.  
  94.                 //find second CR LF or LF
  95.                 if (buf[i] === LF) {
  96.                     parser.__is_headers_complete = false;
  97.                     parser.execute(buf.slice(unsavedStart, i + 1));
  98.  
  99.                     if (parser.__is_headers_complete) {
  100.                         buf_ary.push(buf.slice(unsavedStart, buf[i - 1] === CR ? i - 1 : i));
  101.                         //console.log('insert auth header');
  102.                         buf_ary.push(buf_proxy_basic_auth);
  103.                         buf_ary.push(state === STATE_FOUND_LF_CR ? BUF_CR_LF_CR_LF : BUF_LF_LF);
  104.  
  105.                         // stop intercepting packets if encountered TLS and WebSocket handshake
  106.                         if (parser.__method === 5 /*CONNECT*/ || parser.__upgrade) {
  107.                             parser.close();
  108.                             parser = null;
  109.  
  110.                             buf_ary.push(buf.slice(i + 1));
  111.                             realCon.write(Buffer.concat(buf_ary));
  112.  
  113.                             state = STATE_NONE;
  114.                             return;
  115.                         }
  116.  
  117.                         unsavedStart = i + 1;
  118.                         state = STATE_NONE;
  119.                     } else {
  120.                         state = STATE_FOUND_LF;
  121.                     }
  122.                 } else if (buf[i] === CR && state === STATE_FOUND_LF) {
  123.                     state = STATE_FOUND_LF_CR;
  124.                 } else {
  125.                     state = STATE_NONE;
  126.                 }
  127.             }
  128.  
  129.             if (unsavedStart < buf_len) {
  130.                 buf = buf.slice(unsavedStart, buf_len);
  131.                 parser.execute(buf);
  132.                 buf_ary.push(buf);
  133.             }
  134.  
  135.             realCon.write(Buffer.concat(buf_ary));
  136.  
  137.         }).on('end', cleanup).on('close', cleanup).on('error', function (err) {
  138.             if (!socket.__cleanup) {
  139.                 console.error('[LocalProxy(:' + local_port + ')][Incoming connection] ' + err);
  140.             }
  141.         });
  142.  
  143.         function cleanup() {
  144.             socket.__cleanup = true;
  145.             if (parser) {
  146.                 parser.close();
  147.                 parser = null;
  148.             }
  149.             realCon.end();
  150.         }
  151.     }).on('error', function (err) {
  152.         console.error('[LocalProxy(:' + local_port + ')] ' + err);
  153.         process.exit(1);
  154.     }).listen(local_port, local_host === '*' ? undefined : local_host, function () {
  155.         console.log('[LocalProxy(:' + local_port + ')] OK: forward http://' + local_host + ':' + local_port + ' to ' + ' to http' + (is_remote_https ? 's' : '') + '://' + remote_host + ':' + remote_port);
  156.     });
  157. }
  158.  
  159. main();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement