From b0cb9829784a141e24c220437154ec847c3106ee Mon Sep 17 00:00:00 2001 From: BPplays Date: Wed, 25 Dec 2024 16:42:58 -0800 Subject: [PATCH] added automatic ipv6 ipv4 detection --- default/config.yaml | 6 ++- server.js | 89 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 71 insertions(+), 24 deletions(-) diff --git a/default/config.yaml b/default/config.yaml index a3de53021..fcff9c76f 100644 --- a/default/config.yaml +++ b/default/config.yaml @@ -7,9 +7,11 @@ cardsCacheCapacity: 100 # Listen for incoming connections listen: false # Enables IPv6 and/or IPv4 protocols. Need to have at least one enabled! +# - Use option "auto" to automatically detect support +# - Use "enabled" or "disabled" to enable or disabled each protocol protocol: - ipv4: true - ipv6: false + ipv4: auto + ipv6: auto # Prefers IPv6 for DNS. Enable this on ISPs that don't have issues with IPv6 dnsPreferIPv6: false # The hostname that autorun opens. diff --git a/server.js b/server.js index db7ba9306..9c62f68f2 100644 --- a/server.js +++ b/server.js @@ -4,6 +4,7 @@ import fs from 'node:fs'; import http from 'node:http'; import https from 'node:https'; +import os from 'os'; import path from 'node:path'; import util from 'node:util'; import net from 'node:net'; @@ -133,8 +134,8 @@ const DEFAULT_CSRF_DISABLED = false; const DEFAULT_BASIC_AUTH = false; const DEFAULT_PER_USER_BASIC_AUTH = false; -const DEFAULT_ENABLE_IPV6 = false; -const DEFAULT_ENABLE_IPV4 = true; +const DEFAULT_ENABLE_IPV6 = "auto"; +const DEFAULT_ENABLE_IPV4 = "auto"; const DEFAULT_PREFER_IPV6 = false; @@ -149,11 +150,11 @@ const DEFAULT_PROXY_BYPASS = []; const cliArguments = yargs(hideBin(process.argv)) .usage('Usage: [options]') - .option('enableIPv6', { + .option('string', { type: 'boolean', default: null, describe: `Enables IPv6.\n[config default: ${DEFAULT_ENABLE_IPV6}]`, - }).option('enableIPv4', { + }).option('string', { type: 'boolean', default: null, describe: `Enables IPv4.\n[config default: ${DEFAULT_ENABLE_IPV4}]`, @@ -280,7 +281,7 @@ if (dnsPreferIPv6) { console.log('Preferring IPv4 for DNS resolution'); } -if (!enableIPv6 && !enableIPv4) { +if (enableIPv6 == "disabled" && enableIPv4 == "disabled") { console.error('error: You can\'t disable all internet protocols: at least IPv6 or IPv4 must be enabled.'); process.exit(1); } @@ -365,6 +366,28 @@ function getSessionCookieAge() { return undefined; } +async function getHasIP() { + let hasIPv6 = false; + let hasIPv4 = false; + const interfaces = os.networkInterfaces(); + + for (const iface of Object.values(interfaces)) { + if (iface === undefined) { + continue + } + for (const info of iface) { + if (info.family === 'IPv6') { + hasIPv6 = true; + } + + if (info.family === 'IPv4') { + hasIPv4 = true; + } + } + } + return [hasIPv6, hasIPv4]; +} + app.use(cookieSession({ name: getCookieSessionName(), sameSite: 'strict', @@ -685,18 +708,18 @@ const preSetupTasks = async function () { * Gets the hostname to use for autorun in the browser. * @returns {string} The hostname to use for autorun */ -function getAutorunHostname() { +function getAutorunHostname(useIPv6, useIPv4) { if (autorunHostname === 'auto') { - if (enableIPv6 && enableIPv4) { + if (useIPv6 && useIPv4) { if (avoidLocalhost) return '[::1]'; return 'localhost'; } - if (enableIPv6) { + if (useIPv6) { return '[::1]'; } - if (enableIPv4) { + if (useIPv4) { return '127.0.0.1'; } } @@ -709,10 +732,10 @@ function getAutorunHostname() { * @param {boolean} v6Failed If the server failed to start on IPv6 * @param {boolean} v4Failed If the server failed to start on IPv4 */ -const postSetupTasks = async function (v6Failed, v4Failed) { +const postSetupTasks = async function (v6Failed, v4Failed, useIPv6, useIPv4) { const autorunUrl = new URL( (cliArguments.ssl ? 'https://' : 'http://') + - (getAutorunHostname()) + + (getAutorunHostname(useIPv6, useIPv4)) + (':') + ((autorunPortOverride >= 0) ? autorunPortOverride : server_port), ); @@ -725,11 +748,11 @@ const postSetupTasks = async function (v6Failed, v4Failed) { let logListen = 'SillyTavern is listening on'; - if (enableIPv6 && !v6Failed) { + if (useIPv6 && !v6Failed) { logListen += color.green(' IPv6: ' + tavernUrlV6.host); } - if (enableIPv4 && !v4Failed) { + if (useIPv4 && !v4Failed) { logListen += color.green(' IPv4: ' + tavernUrl.host); } @@ -805,13 +828,13 @@ function logSecurityAlert(message) { * @param {boolean} v6Failed If the server failed to start on IPv6 * @param {boolean} v4Failed If the server failed to start on IPv4 */ -function handleServerListenFail(v6Failed, v4Failed) { - if (v6Failed && !enableIPv4) { +function handleServerListenFail(v6Failed, v4Failed, useIPv6, useIPv4) { + if (v6Failed && !useIPv4) { console.error(color.red('fatal error: Failed to start server on IPv6 and IPv4 disabled')); process.exit(1); } - if (v4Failed && !enableIPv6) { + if (v4Failed && !useIPv6) { console.error(color.red('fatal error: Failed to start server on IPv4 and IPv6 disabled')); process.exit(1); } @@ -856,13 +879,13 @@ function createHttpServer(url) { }); } -async function startHTTPorHTTPS() { +async function startHTTPorHTTPS(useIPv6, useIPv4) { let v6Failed = false; let v4Failed = false; const createFunc = cliArguments.ssl ? createHttpsServer : createHttpServer; - if (enableIPv6) { + if (useIPv6) { try { await createFunc(tavernUrlV6); } catch (error) { @@ -873,7 +896,7 @@ async function startHTTPorHTTPS() { } } - if (enableIPv4) { + if (useIPv4) { try { await createFunc(tavernUrl); } catch (error) { @@ -888,10 +911,32 @@ async function startHTTPorHTTPS() { } async function startServer() { - const [v6Failed, v4Failed] = await startHTTPorHTTPS(); + let useIPv6 = (enableIPv6 == "enabled") + let useIPv4 = (enableIPv4 == "enabled") - handleServerListenFail(v6Failed, v4Failed); - postSetupTasks(v6Failed, v4Failed); + const [hasIPv6, hasIPv4] = await getHasIP() + if (enableIPv6 == "auto") { + useIPv6 = hasIPv6; + if (useIPv6) { + console.log("IPv6 support detected") + } + } + + if (enableIPv4 == "auto") { + useIPv4 = hasIPv4; + if (useIPv4) { + console.log("IPv4 support detected") + } + } + + if (!useIPv6 && !useIPv4) { + console.log("No IPv6 and no IPv4 enabled") + } + + const [v6Failed, v4Failed] = await startHTTPorHTTPS(useIPv6, useIPv4); + + handleServerListenFail(v6Failed, v4Failed, useIPv6, useIPv4); + postSetupTasks(v6Failed, v4Failed, useIPv6, useIPv4); } async function verifySecuritySettings() {