From d8151b64769e730f504a69ff6c52d090b3f06a62 Mon Sep 17 00:00:00 2001 From: Paul Bargewell Date: Mon, 13 Jul 2026 14:32:17 +0100 Subject: [PATCH] fix: Detect interfaces without external connectivity probe Interface detection previously bound an HTTPS request to speed.cloudflare.com per interface and only listed interfaces where that request succeeded. In many valid Docker setups (custom bridge networks, ipvlan, restrictive DNS) that probe fails even though the interface itself is fine, so it gets reported as "not found" and the saved config is silently overwritten. Since requestInterfaces() reruns hourly, a transient probe failure could keep resetting the user's chosen interface. List interfaces directly from os.networkInterfaces() instead, and only fall back/reset the config when the interface has actually disappeared from that list. Fixes #806 --- server/util/loadInterfaces.js | 50 +++++++++-------------------------- 1 file changed, 12 insertions(+), 38 deletions(-) diff --git a/server/util/loadInterfaces.js b/server/util/loadInterfaces.js index 0f896a021..7d280869f 100644 --- a/server/util/loadInterfaces.js +++ b/server/util/loadInterfaces.js @@ -1,5 +1,4 @@ import os from 'node:os'; -import https from 'node:https'; import * as config from '../controller/config.js'; export let interfaces = {}; @@ -15,40 +14,14 @@ export const requestInterfaces = async () => { if (address.internal) continue; - let options = {hostname: "speed.cloudflare.com", path: "/__down?bytes=1", method: "GET", - family: address.family === "IPv4" ? 4 : 6, timeout: 5000}; - - options.agent = new https.Agent(options); - options.localAddress = address.address; - - await new Promise((resolve) => { - - const req = https.request(options, () => { - if (!interfacesResult[i]) interfacesResult[i] = []; - interfacesResult[i].push(address.address); - req.destroy(); - resolve(); - }); - - req.on('error', () => resolve()); - req.on('timeout', () => req.destroy()); - - req.end(); - }); + if (!interfacesResult[i]) interfacesResult[i] = []; + interfacesResult[i].push(address.address); } - - if (!interfacesResult[i]) delete interfacesResult[i]; } + interfaces = {}; for (let i in interfacesResult) { - for (let j in interfacesResult[i]) { - if (interfacesResult[i][j].includes(".")) { - interfaces[i] = interfacesResult[i][j]; - break; - } - } - - if (!interfaces[i]) interfaces[i] = interfacesResult[i][0]; + interfaces[i] = interfacesResult[i].find((address) => address.includes(".")) || interfacesResult[i][0]; } for (let i in interfaces) { @@ -57,12 +30,13 @@ export const requestInterfaces = async () => { const currentInterface = await config.getValue("interface"); - if (!interfaces[currentInterface]) { - if (!currentInterface) { - console.warn("No interface set. Falling back to default."); - } else { - console.warn(`Interface ${currentInterface} not found. Falling back to default.`); - } - await config.updateValue("interface", Object.keys(interfaces)[0]); + if (currentInterface && interfaces[currentInterface]) return; + + if (!currentInterface) { + console.warn("No interface set. Falling back to default."); + } else { + console.warn(`Interface ${currentInterface} not found. Falling back to default.`); } + + await config.updateValue("interface", Object.keys(interfaces)[0] || "none"); }; \ No newline at end of file