Skip to content

Instantly share code, notes, and snippets.

@mdzzohrabi
Created March 24, 2022 03:01
Show Gist options
  • Save mdzzohrabi/6dccdf160fb1586625d6a3e5fbb051ff to your computer and use it in GitHub Desktop.
Save mdzzohrabi/6dccdf160fb1586625d6a3e5fbb051ff to your computer and use it in GitHub Desktop.
Forward local port to an external port through a local network proxy
/**
* Forward local port to an external port through a local network proxy
*
* @author Masoud Zohrabi <mdzzohrabi@gmail.com>
*/
let net = require('net');
let invariant = (expr, error) => { if (!expr) throw Error(error); };
let log = console.log.bind(console);
function main() {
let [nodePath, scriptPath, ...args] = process.argv;
/** @type {{[name: string]: any}} */
let Options = {};
// Parse cli arguments
args.forEach(arg => {
let sub = 0;
if (arg.startsWith('--')) sub = 2;
else if (arg.startsWith('-')) sub = 1;
let [name, value] = arg.substring(sub).split('=');
Options[name] = value || true;
});
let { proxy, dest, port } = { proxy: null, dest: null, port: 9090, ...Options };
if (!proxy || !dest || !port) {
return log(`Usage: node forward --port=9090 --proxy=127.0.0.0:8000 --dest=192.168.1.1:1234`);
}
let [proxyHost, proxyPort] = proxy.split(':');
let [destHost, destPort = 80] = dest.split(':');
invariant(proxyHost && proxyPort, `Invalid proxy address (e.g: 127.0.0.0:9090)`);
invariant(destHost && destPort, `Invalid destination address (e.g: 127.0.0.0:9090)`);
// Start forwarding server
net.createServer(async client => {
/** @type {net.Socket} */
let proxy;
let terminateSession = (err) => {
if (err) log('Close client connection because of exception:', err);
if (!client.destroyed) client.end();
if (proxy && !proxy.destroyed) proxy.end();
}
// Client connected
client.on('error', terminateSession);
try {
// Create tunnel to server
proxy = await createConnectionThroughProxy(proxyHost, proxyPort, destHost, destPort);
proxy.on('error', terminateSession);
client.pipe(proxy);
proxy.pipe(client);
} catch (err) {
terminateSession(err);
}
}).listen(Number(port), () => {
log(`Listen to ${port} for new connection`)
});
}
/**
* Connect to a target through proxy
* @param {string} proxyHost
* @param {number} proxyPort
* @param {string} remoteHost
* @param {number} remotePort
* @param {any} request
* @returns {Promise<net.Socket>}
*/
function createConnectionThroughProxy(proxyHost, proxyPort, remoteHost, remotePort, request = '') {
return new Promise((resolve, reject) => {
let connection = net.createConnection(proxyPort, proxyHost, () => {
connection.write(`CONNECT ${remoteHost}:${remotePort} HTTP/1.1\n\n${request}`);
connection.once('data', buffer => {
if (buffer.toString().includes('Connection established')) resolve(connection);
else reject(buffer.toString());
});
});
connection.on('error', reject);
});
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment