Skip to content

Instantly share code, notes, and snippets.

@yume-chan
Last active February 4, 2024 05:48
Show Gist options
  • Save yume-chan/b174cbcbc9e59f912884693952fa53b1 to your computer and use it in GitHub Desktop.
Save yume-chan/b174cbcbc9e59f912884693952fa53b1 to your computer and use it in GitHub Desktop.
reverse tunnel
const net = require("net");
const http = require("http");
const https = require("https");
const http2 = require("http2");
const req = https.request({
method: "CONNECT",
host: "server",
port: 443,
path: "magic.string"
});
req.end();
req.on("connect", (res, socket) => {
if (res.statusCode === 200) {
createServer(socket);
}
});
function createServer(socket) {
socket.ref();
const server = http2.createServer((req, res) => {
const [host, port = "80"] = req.headers.host.split(":");
const req2 = http.request({
host: host,
port: parseInt(port, 10),
path: req.url,
headers: req.headers,
});
req2.on("response", (res2) => {
delete res2.headers["connection"];
delete res2.headers["keep-alive"];
delete res2.headers["transfer-encoding"];
delete res2.headers["upgrade"];
res.writeHead(res.statusCode, res2.headers);
res2.pipe(res);
});
req.pipe(req2);
});
server.on("connect", (req, res) => {
const [host, port = "80"] = req.url.split(":");
const connection = net.connect(parseInt(port, 10), host, () => {
res.writeHead(200);
req.pipe(connection);
connection.pipe(req);
});
});
server.emit("connection", socket);
}
const http = require("http");
const https = require("https");
const http2 = require("http2");
const server = https.createServer({
// TODO: add certificates
}, (req, res) => {
if (client) {
delete req.headers["transfer-encoding"];
const { host, ...headers } = req.headers;
const stream = client.request({
":method": req.method,
":scheme": "http",
":authority": host,
":path": req.url,
...headers,
});
stream.on("response", (headers) => {
const statusCode = headers[http2.constants.HTTP2_HEADER_STATUS];
delete headers[http2.constants.HTTP2_HEADER_STATUS];
res.writeHead(statusCode, http.STATUS_CODES[statusCode], headers);
req.pipe(stream);
stream.pipe(req);
});
}
});
let client;
server.on("connect", (req, socket, head) => {
if (req.url === "magic.string") {
socket.write("HTTP/1.1 200 OK\r\n\r\n");
client = http2.connect("client", {
protocol: "http",
createConnection() {
return socket;
}
});
} else if (client) {
delete req.headers["Host"];
const stream = client.request({
":method": "CONNECT",
":authority": req.url,
...req.headers,
});
stream.on("response", (headers) => {
const statusCode = headers[http2.constants.HTTP2_HEADER_STATUS];
socket.write(`HTTP/1.1 ${statusCode} ${http.STATUS_CODES[statusCode]}\r\n`);
delete headers[http2.constants.HTTP2_HEADER_STATUS];
for (const [key, value] of Object.entries(headers)) {
socket.write(`${key}: ${value}\r\n`);
}
socket.write("\r\n");
stream.write(head);
socket.pipe(stream);
stream.pipe(socket);
});
}
});
server.listen(443);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment