refactor: Fix MITM

This commit is contained in:
decolua 2026-03-14 16:38:44 +07:00
parent 877eea8ebe
commit b98f4ce20e
5 changed files with 91 additions and 60 deletions

View file

@ -33,13 +33,17 @@ import os from "os";
// Multiple modules register SIGINT/SIGTERM handlers legitimately
process.setMaxListeners(20);
let signalHandlersRegistered = false;
let watchdogInterval = null;
let networkMonitorInterval = null;
let lastNetworkFingerprint = null;
let lastWatchdogTick = Date.now();
let lastTunnelRestartAt = 0;
let tunnelRestartInProgress = false;
// Use global to survive Next.js hot reload — prevents duplicate intervals
const g = global.__appSingleton ??= {
signalHandlersRegistered: false,
watchdogInterval: null,
networkMonitorInterval: null,
lastNetworkFingerprint: null,
lastWatchdogTick: Date.now(),
lastTunnelRestartAt: 0,
tunnelRestartInProgress: false,
};
const WATCHDOG_INTERVAL_MS = 60000;
const NETWORK_CHECK_INTERVAL_MS = 5000;
const NETWORK_RESTART_COOLDOWN_MS = 30000;
@ -68,14 +72,14 @@ export async function initializeApp() {
}
// Kill cloudflared on process exit (register once only)
if (!signalHandlersRegistered) {
if (!g.signalHandlersRegistered) {
const cleanup = () => {
killCloudflared();
process.exit();
};
process.on("SIGINT", cleanup);
process.on("SIGTERM", cleanup);
signalHandlersRegistered = true;
g.signalHandlersRegistered = true;
}
// Pre-download cloudflared binary in background
@ -127,8 +131,8 @@ async function autoStartMitm() {
/** Periodically check tunnel process health and reconnect if crashed */
function startWatchdog() {
if (watchdogInterval) return;
watchdogInterval = setInterval(async () => {
if (g.watchdogInterval) return;
g.watchdogInterval = setInterval(async () => {
try {
const settings = await getSettings();
if (!settings.tunnelEnabled) return;
@ -141,7 +145,7 @@ function startWatchdog() {
}
}, WATCHDOG_INTERVAL_MS);
if (watchdogInterval.unref) watchdogInterval.unref();
if (g.watchdogInterval.unref) g.watchdogInterval.unref();
}
/** Get network fingerprint from active interfaces (IPv4 only) */
@ -161,53 +165,53 @@ function getNetworkFingerprint() {
/** Monitor network changes + sleep/wake → kill and reconnect tunnel */
function startNetworkMonitor() {
if (networkMonitorInterval) return;
if (g.networkMonitorInterval) return;
lastNetworkFingerprint = getNetworkFingerprint();
lastWatchdogTick = Date.now();
g.lastNetworkFingerprint = getNetworkFingerprint();
g.lastWatchdogTick = Date.now();
networkMonitorInterval = setInterval(async () => {
g.networkMonitorInterval = setInterval(async () => {
try {
const settings = await getSettings();
if (!settings.tunnelEnabled) return;
const now = Date.now();
const elapsed = now - lastWatchdogTick;
lastWatchdogTick = now;
const elapsed = now - g.lastWatchdogTick;
g.lastWatchdogTick = now;
const currentFingerprint = getNetworkFingerprint();
const networkChanged = currentFingerprint !== lastNetworkFingerprint;
const networkChanged = currentFingerprint !== g.lastNetworkFingerprint;
const wasSleep = elapsed > NETWORK_CHECK_INTERVAL_MS * 3;
if (networkChanged) lastNetworkFingerprint = currentFingerprint;
if (networkChanged) g.lastNetworkFingerprint = currentFingerprint;
if (!networkChanged && !wasSleep) return;
// Skip if restart already in progress or restarted recently
if (tunnelRestartInProgress) return;
if (now - lastTunnelRestartAt < NETWORK_RESTART_COOLDOWN_MS) return;
if (g.tunnelRestartInProgress) return;
if (now - g.lastTunnelRestartAt < NETWORK_RESTART_COOLDOWN_MS) return;
const reason = wasSleep && networkChanged ? "sleep/wake + network change"
: wasSleep ? "sleep/wake" : "network change";
console.log(`[NetworkMonitor] ${reason} detected, restarting tunnel...`);
tunnelRestartInProgress = true;
lastTunnelRestartAt = now;
g.tunnelRestartInProgress = true;
g.lastTunnelRestartAt = now;
try {
killCloudflared();
await new Promise(r => setTimeout(r, 2000));
await enableTunnel();
console.log("[NetworkMonitor] Tunnel restarted");
lastNetworkFingerprint = getNetworkFingerprint();
g.lastNetworkFingerprint = getNetworkFingerprint();
} finally {
tunnelRestartInProgress = false;
g.tunnelRestartInProgress = false;
}
} catch (err) {
console.log("[NetworkMonitor] Tunnel restart failed:", err.message);
}
}, NETWORK_CHECK_INTERVAL_MS);
if (networkMonitorInterval.unref) networkMonitorInterval.unref();
if (g.networkMonitorInterval.unref) g.networkMonitorInterval.unref();
}
export default initializeApp;