added automatic ipv6 ipv4 detection

b0cb9829784a141e24c220437154ec847c3106ee

BPplays <andymalbp@gmail.com>

2 files changed, +71 -24Ignore whitespace
default/config.yaml+4 -2
@@ -7,9 +7,11 @@ cardsCacheCapacity: 100
7# Listen for incoming connections7# Listen for incoming connections
8listen: false8listen: false
9# Enables IPv6 and/or IPv4 protocols. Need to have at least one enabled!9# Enables IPv6 and/or IPv4 protocols. Need to have at least one enabled!
10# - Use option "auto" to automatically detect support
11# - Use "enabled" or "disabled" to enable or disabled each protocol
10protocol:12protocol:
11 ipv4: true13 ipv4: auto
12 ipv6: false14 ipv6: auto
13# Prefers IPv6 for DNS. Enable this on ISPs that don't have issues with IPv615# Prefers IPv6 for DNS. Enable this on ISPs that don't have issues with IPv6
14dnsPreferIPv6: false16dnsPreferIPv6: false
15# The hostname that autorun opens.17# The hostname that autorun opens.
server.js+67 -22
@@ -4,6 +4,7 @@
4import fs from 'node:fs';4import fs from 'node:fs';
5import http from 'node:http';5import http from 'node:http';
6import https from 'node:https';6import https from 'node:https';
7import os from 'os';
7import path from 'node:path';8import path from 'node:path';
8import util from 'node:util';9import util from 'node:util';
9import net from 'node:net';10import net from 'node:net';
@@ -133,8 +134,8 @@ const DEFAULT_CSRF_DISABLED = false;
133const DEFAULT_BASIC_AUTH = false;134const DEFAULT_BASIC_AUTH = false;
134const DEFAULT_PER_USER_BASIC_AUTH = false;135const DEFAULT_PER_USER_BASIC_AUTH = false;
135136
136const DEFAULT_ENABLE_IPV6 = false;137const DEFAULT_ENABLE_IPV6 = "auto";
137const DEFAULT_ENABLE_IPV4 = true;138const DEFAULT_ENABLE_IPV4 = "auto";
138139
139const DEFAULT_PREFER_IPV6 = false;140const DEFAULT_PREFER_IPV6 = false;
140141
@@ -149,11 +150,11 @@ const DEFAULT_PROXY_BYPASS = [];
149150
150const cliArguments = yargs(hideBin(process.argv))151const cliArguments = yargs(hideBin(process.argv))
151 .usage('Usage: <your-start-script> <command> [options]')152 .usage('Usage: <your-start-script> <command> [options]')
152 .option('enableIPv6', {153 .option('string', {
153 type: 'boolean',154 type: 'boolean',
154 default: null,155 default: null,
155 describe: `Enables IPv6.\n[config default: ${DEFAULT_ENABLE_IPV6}]`,156 describe: `Enables IPv6.\n[config default: ${DEFAULT_ENABLE_IPV6}]`,
156 }).option('enableIPv4', {157 }).option('string', {
157 type: 'boolean',158 type: 'boolean',
158 default: null,159 default: null,
159 describe: `Enables IPv4.\n[config default: ${DEFAULT_ENABLE_IPV4}]`,160 describe: `Enables IPv4.\n[config default: ${DEFAULT_ENABLE_IPV4}]`,
@@ -280,7 +281,7 @@ if (dnsPreferIPv6) {
280 console.log('Preferring IPv4 for DNS resolution');281 console.log('Preferring IPv4 for DNS resolution');
281}282}
282283
283if (!enableIPv6 && !enableIPv4) {284if (enableIPv6 == "disabled" && enableIPv4 == "disabled") {
284 console.error('error: You can\'t disable all internet protocols: at least IPv6 or IPv4 must be enabled.');285 console.error('error: You can\'t disable all internet protocols: at least IPv6 or IPv4 must be enabled.');
285 process.exit(1);286 process.exit(1);
286}287}
@@ -365,6 +366,28 @@ function getSessionCookieAge() {
365 return undefined;366 return undefined;
366}367}
367368
369async function getHasIP() {
370 let hasIPv6 = false;
371 let hasIPv4 = false;
372 const interfaces = os.networkInterfaces();
373
374 for (const iface of Object.values(interfaces)) {
375 if (iface === undefined) {
376 continue
377 }
378 for (const info of iface) {
379 if (info.family === 'IPv6') {
380 hasIPv6 = true;
381 }
382
383 if (info.family === 'IPv4') {
384 hasIPv4 = true;
385 }
386 }
387 }
388 return [hasIPv6, hasIPv4];
389}
390
368app.use(cookieSession({391app.use(cookieSession({
369 name: getCookieSessionName(),392 name: getCookieSessionName(),
370 sameSite: 'strict',393 sameSite: 'strict',
@@ -685,18 +708,18 @@ const preSetupTasks = async function () {
685 * Gets the hostname to use for autorun in the browser.708 * Gets the hostname to use for autorun in the browser.
686 * @returns {string} The hostname to use for autorun709 * @returns {string} The hostname to use for autorun
687 */710 */
688function getAutorunHostname() {711function getAutorunHostname(useIPv6, useIPv4) {
689 if (autorunHostname === 'auto') {712 if (autorunHostname === 'auto') {
690 if (enableIPv6 && enableIPv4) {713 if (useIPv6 && useIPv4) {
691 if (avoidLocalhost) return '[::1]';714 if (avoidLocalhost) return '[::1]';
692 return 'localhost';715 return 'localhost';
693 }716 }
694717
695 if (enableIPv6) {718 if (useIPv6) {
696 return '[::1]';719 return '[::1]';
697 }720 }
698721
699 if (enableIPv4) {722 if (useIPv4) {
700 return '127.0.0.1';723 return '127.0.0.1';
701 }724 }
702 }725 }
@@ -709,10 +732,10 @@ function getAutorunHostname() {
709 * @param {boolean} v6Failed If the server failed to start on IPv6732 * @param {boolean} v6Failed If the server failed to start on IPv6
710 * @param {boolean} v4Failed If the server failed to start on IPv4733 * @param {boolean} v4Failed If the server failed to start on IPv4
711 */734 */
712const postSetupTasks = async function (v6Failed, v4Failed) {735const postSetupTasks = async function (v6Failed, v4Failed, useIPv6, useIPv4) {
713 const autorunUrl = new URL(736 const autorunUrl = new URL(
714 (cliArguments.ssl ? 'https://' : 'http://') +737 (cliArguments.ssl ? 'https://' : 'http://') +
715 (getAutorunHostname()) +738 (getAutorunHostname(useIPv6, useIPv4)) +
716 (':') +739 (':') +
717 ((autorunPortOverride >= 0) ? autorunPortOverride : server_port),740 ((autorunPortOverride >= 0) ? autorunPortOverride : server_port),
718 );741 );
@@ -725,11 +748,11 @@ const postSetupTasks = async function (v6Failed, v4Failed) {
725748
726 let logListen = 'SillyTavern is listening on';749 let logListen = 'SillyTavern is listening on';
727750
728 if (enableIPv6 && !v6Failed) {751 if (useIPv6 && !v6Failed) {
729 logListen += color.green(' IPv6: ' + tavernUrlV6.host);752 logListen += color.green(' IPv6: ' + tavernUrlV6.host);
730 }753 }
731754
732 if (enableIPv4 && !v4Failed) {755 if (useIPv4 && !v4Failed) {
733 logListen += color.green(' IPv4: ' + tavernUrl.host);756 logListen += color.green(' IPv4: ' + tavernUrl.host);
734 }757 }
735758
@@ -805,13 +828,13 @@ function logSecurityAlert(message) {
805 * @param {boolean} v6Failed If the server failed to start on IPv6828 * @param {boolean} v6Failed If the server failed to start on IPv6
806 * @param {boolean} v4Failed If the server failed to start on IPv4829 * @param {boolean} v4Failed If the server failed to start on IPv4
807 */830 */
808function handleServerListenFail(v6Failed, v4Failed) {831function handleServerListenFail(v6Failed, v4Failed, useIPv6, useIPv4) {
809 if (v6Failed && !enableIPv4) {832 if (v6Failed && !useIPv4) {
810 console.error(color.red('fatal error: Failed to start server on IPv6 and IPv4 disabled'));833 console.error(color.red('fatal error: Failed to start server on IPv6 and IPv4 disabled'));
811 process.exit(1);834 process.exit(1);
812 }835 }
813836
814 if (v4Failed && !enableIPv6) {837 if (v4Failed && !useIPv6) {
815 console.error(color.red('fatal error: Failed to start server on IPv4 and IPv6 disabled'));838 console.error(color.red('fatal error: Failed to start server on IPv4 and IPv6 disabled'));
816 process.exit(1);839 process.exit(1);
817 }840 }
@@ -856,13 +879,13 @@ function createHttpServer(url) {
856 });879 });
857}880}
858881
859async function startHTTPorHTTPS() {882async function startHTTPorHTTPS(useIPv6, useIPv4) {
860 let v6Failed = false;883 let v6Failed = false;
861 let v4Failed = false;884 let v4Failed = false;
862885
863 const createFunc = cliArguments.ssl ? createHttpsServer : createHttpServer;886 const createFunc = cliArguments.ssl ? createHttpsServer : createHttpServer;
864887
865 if (enableIPv6) {888 if (useIPv6) {
866 try {889 try {
867 await createFunc(tavernUrlV6);890 await createFunc(tavernUrlV6);
868 } catch (error) {891 } catch (error) {
@@ -873,7 +896,7 @@ async function startHTTPorHTTPS() {
873 }896 }
874 }897 }
875898
876 if (enableIPv4) {899 if (useIPv4) {
877 try {900 try {
878 await createFunc(tavernUrl);901 await createFunc(tavernUrl);
879 } catch (error) {902 } catch (error) {
@@ -888,10 +911,32 @@ async function startHTTPorHTTPS() {
888}911}
889912
890async function startServer() {913async function startServer() {
891 const [v6Failed, v4Failed] = await startHTTPorHTTPS();914 let useIPv6 = (enableIPv6 == "enabled")
915 let useIPv4 = (enableIPv4 == "enabled")
916
917 const [hasIPv6, hasIPv4] = await getHasIP()
918 if (enableIPv6 == "auto") {
919 useIPv6 = hasIPv6;
920 if (useIPv6) {
921 console.log("IPv6 support detected")
922 }
923 }
924
925 if (enableIPv4 == "auto") {
926 useIPv4 = hasIPv4;
927 if (useIPv4) {
928 console.log("IPv4 support detected")
929 }
930 }
931
932 if (!useIPv6 && !useIPv4) {
933 console.log("No IPv6 and no IPv4 enabled")
934 }
935
936 const [v6Failed, v4Failed] = await startHTTPorHTTPS(useIPv6, useIPv4);
892937
893 handleServerListenFail(v6Failed, v4Failed);938 handleServerListenFail(v6Failed, v4Failed, useIPv6, useIPv4);
894 postSetupTasks(v6Failed, v4Failed);939 postSetupTasks(v6Failed, v4Failed, useIPv6, useIPv4);
895}940}
896941
897async function verifySecuritySettings() {942async function verifySecuritySettings() {