kaven-utils
Version:
Utils for Node.js.
98 lines (97 loc) • 3.63 kB
JavaScript
/********************************************************************
* @author: Kaven
* @email: kaven@wuwenkai.com
* @website: http://blog.kaven.xyz
* @file: [Kaven-Utils] /src/net/proxy/TlsProxyServer.ts
* @create: 2022-04-20 13:48:35.117
* @modify: 2025-10-14 22:58:04.829
* @version: 6.1.0
* @times: 40
* @lines: 122
* @copyright: Copyright © 2022-2025 Kaven. All Rights Reserved.
* @description: [description]
* @license: [license]
********************************************************************/
import { ReplaceAll, Strings_CR, Strings_CR_LF, TrimEnd } from "kaven-basic";
import { createConnection } from "node:net";
import { createServer } from "node:tls";
export class TlsProxyServer {
server;
EnableKeyLog = false;
Authenticate;
Logger;
constructor(options) {
this.server = createServer(options, socket => {
this.onConnected(socket);
});
this.server.on("keylog", (line, tlsSocket) => {
if (this.EnableKeyLog) {
this.Logger?.Info(`[${tlsSocket.remoteAddress}] ${line.toString()}`);
}
});
this.server.on("tlsClientError", (err, tlsSocket) => {
this.Logger?.Error(`[${tlsSocket.remoteAddress}] ${err.toString()}`);
});
}
Start(port, host) {
if (host === undefined) {
host = "0.0.0.0";
}
this.server.listen(port, host, () => {
this.Logger?.Info(`Server bound to ${host}:${port}`);
});
}
Stop() {
this.server.close(err => {
this.Logger?.Info(`Server closed: ${err}`);
});
}
onConnected(socket) {
const remote = `${socket.remoteAddress}:${socket.remotePort}`;
const local = `${socket.localAddress}:${socket.localPort}`;
const remoteToLocal = `[${remote} -> ${local}]`;
const logPrefix = `[TlsProxyServer]${remoteToLocal}`;
const log = (data) => {
this.Logger?.Info(`${logPrefix}${data}`);
};
const logError = (data) => {
this.Logger?.Error(`${logPrefix}${data}`);
};
if (this.Authenticate) {
if (!socket.remoteAddress || !this.Authenticate(socket.remoteAddress)) {
log(`Invalid client connected, authorized: ${socket.authorized}`);
socket.destroy();
return;
}
}
log(`New client connected, authorized: ${socket.authorized}`);
socket.once("data", (data) => {
const line = TrimEnd(data.toString(), Strings_CR_LF);
log(`Try connect to: ${line}`);
const [host, port] = line.split(":");
let isConnected = false;
const destSocket = createConnection(Number(port), host, () => {
isConnected = true;
log(`Successfully connected to ${line}`);
socket.write("OK" + Strings_CR_LF);
socket.pipe(destSocket);
destSocket.pipe(socket);
});
destSocket.on("error", err => {
if (!isConnected) {
socket.write(ReplaceAll(`${err}`, Strings_CR_LF, Strings_CR) + Strings_CR_LF);
}
logError(`destSocket error: ${err}`);
});
destSocket.once("close", () => {
socket.end();
});
socket.once("close", () => {
destSocket.end();
});
});
socket.on("error", (err) => {
logError(err);
});
}
}