@mesh-tech/mesh-cli
Version:
CLI for Mesh platform development utilities
75 lines (74 loc) • 2.63 kB
JavaScript
import * as net from "node:net";
export function ipv4ToBytes(ip) {
const parts = ip.split(".");
if (parts.length !== 4)
throw new Error(`not an IPv4 address: ${ip}`);
return parts.map((p) => {
const n = Number(p);
if (!Number.isInteger(n) || n < 0 || n > 255 || !/^\d+$/.test(p)) {
throw new Error(`not an IPv4 address: ${ip}`);
}
return n;
});
}
export function socks5ConnectRequest(ip, port) {
return Buffer.from([0x05, 0x01, 0x00, 0x01, ...ipv4ToBytes(ip), (port >> 8) & 0xff, port & 0xff]);
}
const SOCKS_REPLY_MESSAGES = {
0: "succeeded",
1: "general SOCKS server failure",
2: "connection not allowed by ruleset",
3: "network unreachable",
4: "host unreachable",
5: "connection refused",
6: "TTL expired",
7: "command not supported",
8: "address type not supported",
};
export function interpretSocksReply(code) {
return { ok: code === 0, message: SOCKS_REPLY_MESSAGES[code] ?? `unknown SOCKS reply ${code}` };
}
export function createForwarder(opts) {
const socksHost = opts.socksHost ?? "127.0.0.1";
const server = net.createServer((client) => {
const up = net.connect(opts.socksPort, socksHost);
let buf = Buffer.alloc(0);
let phase = 0;
up.on("connect", () => up.write(Buffer.from([0x05, 0x01, 0x00])));
up.on("data", (d) => {
if (phase === 2)
return;
buf = Buffer.concat([buf, d]);
if (phase === 0) {
if (buf.length < 2)
return;
buf = buf.subarray(2);
phase = 1;
up.write(socks5ConnectRequest(opts.targetIp, opts.targetPort));
}
if (phase === 1) {
if (buf.length < 10)
return;
const reply = interpretSocksReply(buf[1]);
const leftover = buf.subarray(10);
phase = 2;
if (!reply.ok) {
up.destroy();
client.destroy();
return;
}
if (leftover.length)
client.write(leftover);
client.pipe(up);
up.pipe(client);
}
});
client.on("error", () => up.destroy());
up.on("error", () => client.destroy());
client.on("close", () => up.destroy());
up.on("close", () => client.destroy());
});
server.on("error", (err) => opts.onError?.(err));
server.listen(opts.listenPort, "127.0.0.1");
return server;
}