kaven-utils
Version:
Utils for Node.js.
98 lines (97 loc) • 3.69 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: 2024-11-01 10:48:07.309
* @version: 5.4.5
* @times: 34
* @lines: 122
* @copyright: Copyright © 2022-2024 Kaven. All Rights Reserved.
* @description: [description]
* @license: [license]
********************************************************************/
import { Strings_CR, Strings_CR_LF } from "kaven-basic";
import { createConnection } from "node:net";
import { createServer } from "node:tls";
import { InternalLogger } from "../../KavenUtility.Internal.js";
export class TlsProxyServer {
server;
EnableKeyLog = false;
Authenticate;
constructor(options) {
this.server = createServer(options, socket => {
this.onConnected(socket);
});
this.server.on("keylog", (line, tlsSocket) => {
if (this.EnableKeyLog) {
InternalLogger()?.Info(`[${tlsSocket.remoteAddress}] ${line.toString()}`);
}
});
this.server.on("tlsClientError", (err, tlsSocket) => {
InternalLogger()?.Error(`[${tlsSocket.remoteAddress}] ${err.toString()}`);
});
}
Start(port, host) {
if (host === undefined) {
host = "0.0.0.0";
}
this.server.listen(port, host, () => {
InternalLogger()?.Info(`Server bound to ${host}:${port}`);
});
}
Stop() {
this.server.close(err => {
InternalLogger()?.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 = (...args) => {
InternalLogger()?.Info(logPrefix, ...args);
};
const logError = (...args) => {
InternalLogger()?.Error(logPrefix, ...args);
};
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 = data.toString().TrimEnd(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(`${err}`.ReplaceAll(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);
});
}
}