From 06712f35b5ef6ade91676547d84daf28bb406971 Mon Sep 17 00:00:00 2001 From: Dorian Niemiec Date: Fri, 2 Feb 2024 20:07:11 +0100 Subject: [PATCH] Update to SVR.JS 3.14.1 --- index.html | 9 +- licenses/index.html | 8 +- mods/easteregg.tar.gz | Bin 6052 -> 0 bytes svr.js | 235 +++++++++++++++++++++++++++++------------- tests.html | 4 +- 5 files changed, 175 insertions(+), 81 deletions(-) delete mode 100644 mods/easteregg.tar.gz diff --git a/index.html b/index.html index e572dbe..69a1c2f 100644 --- a/index.html +++ b/index.html @@ -1,7 +1,7 @@ - SVR.JS 3.14.0 + SVR.JS 3.14.1 -

Welcome to SVR.JS 3.14.0

+

Welcome to SVR.JS 3.14.1



@@ -134,9 +134,8 @@

Changes:


Tests
diff --git a/licenses/index.html b/licenses/index.html index 811d661..e016068 100644 --- a/licenses/index.html +++ b/licenses/index.html @@ -1,7 +1,7 @@ - SVR.JS 3.14.0 Licenses + SVR.JS 3.14.1 Licenses -

SVR.JS 3.14.0 Licenses

-

SVR.JS 3.14.0

+

SVR.JS 3.14.1 Licenses

+

SVR.JS 3.14.1

MIT License

@@ -37,7 +37,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-

Packages used by SVR.JS 3.14.0

+

Packages used by SVR.JS 3.14.1

License: MIT
diff --git a/mods/easteregg.tar.gz b/mods/easteregg.tar.gz deleted file mode 100644 index 83c2ef0bb8287cb49c199f5773f7d19bdcb82a80..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6052 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 diff --git a/svr.js b/svr.js index 874c646..7f9f01b 100644 --- a/svr.js +++ b/svr.js @@ -69,7 +69,7 @@ function deleteFolderRecursive(path) { } var os = require("os"); -var version = "3.14.0"; +var version = "3.14.1"; var singlethreaded = false; if (process.versions) process.versions.svrjs = version; // Inject SVR.JS into process.versions @@ -306,7 +306,7 @@ function generateETag(filePath, stat) { return ETagDB[filePath + "-" + stat.size + "-" + stat.mtime]; } -// Brute force-related +// Brute force protection-related var bruteForceDb = {}; // PBKDF2/scrypt cache @@ -321,6 +321,7 @@ var reallyExiting = false; var crashed = false; var threadLimitWarned = false; +// SVR.JS worker forking function function SVRJSFork() { // Log if (SVRJSInitialized) serverconsole.locmessage("Starting next thread, because previous one hung up/crashed..."); @@ -531,7 +532,85 @@ function createRegex(regex, isPath) { return new RegExp(searchString, modifiers); } -function checkForEnabledDirectoryListing(hostname) { +// Function to check if IPs are equal +function ipMatch(IP1, IP2) { + + if (!IP1) return true; + if (!IP2) return false; + + // Function to normalize IPv4 address (remove leading zeros) + function normalizeIPv4Address(address) { + return address.replace(/(^|\.)(?:0(?!\.|$))+/g, ""); + } + + // Function to expand IPv6 address to full format + function expandIPv6Address(address) { + var fullAddress = ""; + var expandedAddress = ""; + var validGroupCount = 8; + var validGroupSize = 4; + + var ipv4 = ""; + var extractIpv4 = /([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/; + var validateIpv4 = /((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})/; + + if (validateIpv4.test(address)) { + var oldGroups = address.match(extractIpv4); + for (var i = 1; i < oldGroups.length; i++) { + ipv4 += ("00" + (parseInt(oldGroups[i], 10).toString(16))).slice(-2) + (i == 2 ? ":" : ""); + } + address = address.replace(extractIpv4, ipv4); + } + + if (address.indexOf("::") == -1) { + fullAddress = address; + } else { + var sides = address.split("::"); + var groupsPresent = 0; + sides.forEach(function (side) { + groupsPresent += side.split(":").length; + }); + fullAddress += sides[0] + ":"; + if (validGroupCount - groupsPresent > 1) { + fullAddress += "0000:".repeat(validGroupCount - groupsPresent); + } + fullAddress += sides[1]; + } + var groups = fullAddress.split(":"); + for (var i = 0; i < validGroupCount; i++) { + if (groups[i].length < validGroupSize) { + groups[i] = "0".repeat(validGroupSize - groups[i].length) + groups[i]; + } + expandedAddress += (i != validGroupCount - 1) ? groups[i] + ":" : groups[i]; + } + return expandedAddress; + } + + // Normalize or expand IP addresses + IP1 = IP1.toLowerCase(); + if (IP1 == "localhost") IP1 = "::1"; + if (IP1.indexOf("::ffff:") == 0) IP1 = IP1.substr(7); + if (IP1.indexOf(":") > -1) { + IP1 = expandIPv6Address(IP1); + } else { + IP1 = normalizeIPv4Address(IP1); + } + + IP2 = IP2.toLowerCase(); + if (IP2 == "localhost") IP2 = "::1"; + if (IP2.indexOf("::ffff:") == 0) IP2 = IP2.substr(7); + if (IP2.indexOf(":") > -1) { + IP2 = expandIPv6Address(IP2); + } else { + IP2 = normalizeIPv4Address(IP2); + } + + // Check if processed IPs are equal + if (IP1 == IP2) return true; + else return false; +} + +function checkForEnabledDirectoryListing(hostname, localAddress) { function matchHostname(hostnameM) { if (typeof hostnameM == "undefined" || hostnameM == "*") { return true; @@ -550,7 +629,7 @@ function checkForEnabledDirectoryListing(hostname) { if (!configJSON.enableDirectoryListingVHost) return main; var vhostP = null; configJSON.enableDirectoryListingVHost.every(function (vhost) { - if (matchHostname(vhost.host)) { + if (matchHostname(vhost.host) && ipMatch(vhost.ip, localAddress)) { vhostP = vhost; return false; } else { @@ -757,7 +836,7 @@ function ipBlockList(rawBlockList) { // Normalize or expand the IP address rawValue = rawValue.toLowerCase(); - if (rawValue == "localhost") rawValue = "127.0.0.1"; + if (rawValue == "localhost") rawValue = "::1"; if (rawValue.indexOf("::ffff:") == 0) rawValue = rawValue.substr(7); if (rawValue.indexOf(":") > -1) { isIPv6 = true; @@ -794,8 +873,8 @@ function generateErrorStack(errorObject) { // If the error stack starts with the error name, return the original stack (it is V8-style then). if (errorStack.some(function (errorStackLine) { - return (errorStackLine.indexOf(errorObject.name) == 0); - })) { + return (errorStackLine.indexOf(errorObject.name) == 0); + })) { return errorObject.stack; } @@ -854,6 +933,7 @@ function calculateNetworkIPv4FromCidr(ipWithCidr) { }).join("."); } +// IP and network inteface-related var ifaces = {}; var ifaceEx = null; try { @@ -864,11 +944,7 @@ try { var ips = []; var brdIPs = ["255.255.255.255", "127.255.255.255", "0.255.255.255"]; var netIPs = ["127.0.0.0"]; -var attmts = 5; -var attmtsRedir = 5; -var errors = os.constants.errno; -var timestamp = new Date().getTime(); -var wwwredirect = false; + Object.keys(ifaces).forEach(function (ifname) { var alias = 0; ifaces[ifname].forEach(function (iface) { @@ -885,6 +961,7 @@ Object.keys(ifaces).forEach(function (ifname) { alias++; }); }); + if (ips.length == 0) { Object.keys(ifaces).forEach(function (ifname) { var alias = 0; @@ -901,9 +978,20 @@ if (ips.length == 0) { }); }); } + +// Server startup attempt counter +var attmts = 5; +var attmtsRedir = 5; + +// Some variables... +var errors = os.constants.errno; +var timestamp = new Date().getTime(); + +// Server IP address var host = ips[(ips.length) - 1]; if (!host) host = "[offline]"; +// Public IP address-related var ipRequestCompleted = false; var ipRequestGotError = false; if (host != "[offline]" || ifaceEx) { @@ -1060,11 +1148,11 @@ if (fs.existsSync(__dirname + "/config.json")) { } } catch (err) { configJSONRErr = err2; - // throw new Error("Cannot read JSON file."); } } // Default server configuration properties +var wwwredirect = false; var rawBlackList = []; var users = []; var page404 = "404.html"; @@ -1199,6 +1287,7 @@ try { if (vnum === undefined) vnum = 0; if (process.isBun) vnum = 64; +// SVR.JS path sanitizer function function sanitizeURL(resource) { if (resource == "*") return "*"; if (resource == "") return ""; @@ -1231,6 +1320,7 @@ function sanitizeURL(resource) { else return sanitizedResource; } +// Node.JS mojibake URL fixing function function fixNodeMojibakeURL(string) { var encoded = ""; @@ -1249,6 +1339,7 @@ function fixNodeMojibakeURL(string) { }); } +// SSL-related var key = ""; var cert = ""; @@ -1277,7 +1368,7 @@ if (secure) { var sniNames = Object.keys(sni); var sniCredentials = []; sniNames.forEach(function (sniName) { - if(typeof sniName === "string" && sniName.match(/\*[^*.:]*\*[^*.:]*(?:\.|:|$)/)) { + if (typeof sniName === "string" && sniName.match(/\*[^*.:]*\*[^*.:]*(?:\.|:|$)/)) { sniReDos = true; } sniCredentials.push({ @@ -1306,7 +1397,7 @@ function LOG(s) { flags: "a", autoClose: false }); - logFile.on("error", function(err) { + logFile.on("error", function (err) { if (!s.match(/^SERVER WARNING MESSAGE(?: \[Request Id: [0-9a-f]{6}\])?: There was a problem while saving logs! Logs will not be kept in log file\. Reason: /) && !reallyExiting) serverconsole.locwarnmessage("There was a problem while saving logs! Logs will not be kept in log file. Reason: " + err.message); }); } @@ -1403,7 +1494,7 @@ var serverconsole = { } }; -// Wrap around process.exit, so log contents can flush. +// Wrap around process.exit, so that log contents can be flushed. process.unsafeExit = process.exit; process.exit = function (code) { if (logFile && logFile.writable && !logFile.pending) { @@ -1438,6 +1529,7 @@ process.exit = function (code) { } }; +// SVR.JS mod loader var modLoadingErrors = []; var SSJSError = undefined; @@ -1742,6 +1834,7 @@ function sha256(s) { } } +// Function to get URL path for use in forbidden path adding. function getInitializePath(to) { var cwd = process.cwd(); if (os.platform() == "win32") { @@ -1758,6 +1851,7 @@ function getInitializePath(to) { } } +// Function to check if URL path name is a forbidden path. function isForbiddenPath(decodedHref, match) { var forbiddenPath = forbiddenPaths[match]; if (!forbiddenPath) return false; @@ -1772,6 +1866,7 @@ function isForbiddenPath(decodedHref, match) { return false; } +// Function to check if URL path name is index of one of defined forbidden paths. function isIndexOfForbiddenPath(decodedHref, match) { var forbiddenPath = forbiddenPaths[match]; if (!forbiddenPath) return false; @@ -1786,7 +1881,6 @@ function isIndexOfForbiddenPath(decodedHref, match) { return false; } - // Set up forbidden paths var forbiddenPaths = {}; @@ -1864,7 +1958,7 @@ var serverErrorDescs = { 599: "The server couldn't connect in time while it was acting as a proxy." }; -// Create server +// Create server instances if (!cluster.isPrimary) { var reqcounter = 0; var malformedcounter = 0; @@ -2118,9 +2212,9 @@ if (!cluster.isPrimary) { }); try { var snMatches = sniCredentialsSingle.name.match(/^([^:[]*|\[[^]]*\]?)((?::.*)?)$/); - if(!snMatches[1][0].match(/^\.+$/)) snMatches[1][0] = snMatches[1][0].replace(/\.+$/,""); - server._contexts[server._contexts.length-1][0] = new RegExp("^" + snMatches[1].replace(/([.^$+?\-\\[\]{}])/g, "\\$1").replace(/\*/g, "[^.:]*") + ((snMatches[1][0] == "[" || snMatches[1].match(/^(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$/)) ? "" : "\.?") + snMatches[2].replace(/([.^$+?\-\\[\]{}])/g, "\\$1").replace(/\*/g, "[^.]*") + "$", "i"); - } catch(ex) { + if (!snMatches[1][0].match(/^\.+$/)) snMatches[1][0] = snMatches[1][0].replace(/\.+$/, ""); + server._contexts[server._contexts.length - 1][0] = new RegExp("^" + snMatches[1].replace(/([.^$+?\-\\[\]{}])/g, "\\$1").replace(/\*/g, "[^.:]*") + ((snMatches[1][0] == "[" || snMatches[1].match(/^(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$/)) ? "" : "\.?") + snMatches[2].replace(/([.^$+?\-\\[\]{}])/g, "\\$1").replace(/\*/g, "[^.]*") + "$", "i"); + } catch (ex) { // Can't replace regex, ignoring... } }); @@ -2226,8 +2320,8 @@ if (!cluster.isPrimary) { }); }; res.writeHead = function (code, name, headers) { - if(code >= 400 && code <= 499) err4xxcounter++; - if(code >= 500 && code <= 599) err5xxcounter++; + if (code >= 400 && code <= 499) err4xxcounter++; + if (code >= 500 && code <= 599) err5xxcounter++; var head = ("HTTP/1.1 " + code.toString() + " " + name + "\r\n"); var headers = JSON.parse(JSON.stringify(headers)); headers["Date"] = (new Date()).toGMTString(); @@ -2799,7 +2893,7 @@ if (!cluster.isPrimary) { if (configJSON.customHeadersVHost) { var vhostP = null; configJSON.customHeadersVHost.every(function (vhost) { - if (matchHostname(vhost.host)) { + if (matchHostname(vhost.host) && ipMatch(vhost.ip, req.socket ? req.socket.localAddress : undefined)) { vhostP = vhost; return false; } else { @@ -2875,7 +2969,7 @@ if (!cluster.isPrimary) { } } - if (req.headers["x-svr-js-from-main-thread"] == "true" && (!req.socket.remoteAddress || req.socket.remoteAddress == "::1" || req.socket.remoteAddress == "::ffff:127.0.0.1" || req.socket.remoteAddress == "127.0.0.1" || req.socket.remoteAddress == "localhost" || req.socket.remoteAddress == host || req.socket.remoteAddress == "::ffff:" + host)) { + if (req.headers["x-svr-js-from-main-thread"] == "true" && req.socket && (!req.socket.remoteAddress || req.socket.remoteAddress == "::1" || req.socket.remoteAddress == "::ffff:127.0.0.1" || req.socket.remoteAddress == "127.0.0.1" || req.socket.remoteAddress == "localhost" || req.socket.remoteAddress == host || req.socket.remoteAddress == "::ffff:" + host)) { var headers = getCustomHeaders(); res.writeHead(204, "No Content", headers); res.end(); @@ -2975,7 +3069,7 @@ if (!cluster.isPrimary) { var oldHostHeader = req.headers.host; if (typeof req.headers.host == "string") { req.headers.host = req.headers.host.toLowerCase(); - if(!req.headers.host.match(/^\.+$/)) req.headers.host = req.headers.host.replace(/\.$/g,""); + if (!req.headers.host.match(/^\.+$/)) req.headers.host = req.headers.host.replace(/\.$/g, ""); } if (!isProxy) serverconsole.reqmessage("Client " + ((!reqip || reqip == "") ? "[unknown client]" : (reqip + ((reqport && reqport !== 0) && reqport != "" ? ":" + reqport : ""))) + " wants " + (req.method == "GET" ? "content in " : (req.method == "POST" ? "to post content in " : (req.method == "PUT" ? "to add content in " : (req.method == "DELETE" ? "to delete content in " : (req.method == "PATCH" ? "to patch content in " : "to access content using " + req.method + " method in "))))) + (req.headers.host == undefined ? "" : req.headers.host) + req.url); @@ -3067,7 +3161,7 @@ if (!cluster.isPrimary) { return; } - if (list[_i].scode != errorCode || !matchHostname(list[_i].host)) { + if (list[_i].scode != errorCode || !(matchHostname(list[_i].host) && ipMatch(list[_i].ip, req.socket ? req.socket.localAddress : undefined))) { getErrorFileName(list, callback, _i + 1); return; } else { @@ -3286,7 +3380,7 @@ if (!cluster.isPrimary) { } } - + // URL-related objects. var uobject = parseURL(req.url); var search = uobject.search; var href = uobject.pathname; @@ -3303,6 +3397,7 @@ if (!cluster.isPrimary) { } if (req.headers["expect"] && req.headers["expect"] != "100-continue") { + // Expectations not met. callServerError(417); return; } @@ -3416,8 +3511,8 @@ if (!cluster.isPrimary) { statusBody += "Server errors (5xx): " + err5xxcounter + "
"; statusBody += "Average error rate: " + (Math.round(((err4xxcounter + err5xxcounter) / reqcounter) * 10000) / 100) + "%
"; statusBody += "Malformed HTTP requests: " + malformedcounter; - if(process.memoryUsage) statusBody += "
Memory usage of thread: " + sizify(process.memoryUsage().rss, true) + "B"; - if(process.cpuUsage) statusBody += "
Total CPU usage by thread: u" + (process.cpuUsage().user / 1000) + "ms s" + (process.cpuUsage().system / 1000) + "ms - " + (Math.round((((process.cpuUsage().user + process.cpuUsage().system) / 1000000) / process.uptime()) * 1000) / 1000) + "%"; + if (process.memoryUsage) statusBody += "
Memory usage of thread: " + sizify(process.memoryUsage().rss, true) + "B"; + if (process.cpuUsage) statusBody += "
Total CPU usage by thread: u" + (process.cpuUsage().user / 1000) + "ms s" + (process.cpuUsage().system / 1000) + "ms - " + (Math.round((((process.cpuUsage().user + process.cpuUsage().system) / 1000000) / process.uptime()) * 1000) / 1000) + "%"; statusBody += "
Thread PID: " + process.pid + "
"; var hdhds = getCustomHeaders(); @@ -3427,11 +3522,6 @@ if (!cluster.isPrimary) { return; } - ///////////////////////////////////////////// - ////THERE IS NO MORE "THE BOOK OF ZSOIE"!//// - //// But it's in easteregg.tar.gz mod... //// - ///////////////////////////////////////////// - var pth = decodeURIComponent(href).replace(/\/+/g, "/").substr(1); var readFrom = "./" + pth; fs.stat(readFrom, function (err, stats) { @@ -3516,7 +3606,7 @@ if (!cluster.isPrimary) { function properDirectoryListingAndStaticFileServe() { if (stats.isDirectory()) { // Check if directory listing is enabled in the configuration - if (checkForEnabledDirectoryListing(req.headers.host)) { + if (checkForEnabledDirectoryListing(req.headers.host, req.socket ? req.socket.localAddress : undefined)) { var customHeaders = getCustomHeaders(); customHeaders["Content-Type"] = "text/html; charset=utf-8"; res.writeHead(200, http.STATUS_CODES[200], customHeaders); @@ -3525,23 +3615,23 @@ if (!cluster.isPrimary) { var customDirListingHeader = fs.existsSync(("." + decodeURIComponent(href) + "/.dirhead").replace(/\/+/g, "/")) ? fs.readFileSync(("." + decodeURIComponent(href) + "/.dirhead").replace(/\/+/g, "/")).toString() : (fs.existsSync(("." + decodeURIComponent(href) + "/HEAD.html").replace(/\/+/g, "/")) && (os.platform != "win32" || href != "/")) ? - fs.readFileSync(("." + decodeURIComponent(href) + "/HEAD.html").replace(/\/+/g, "/")).toString() : - ""; + fs.readFileSync(("." + decodeURIComponent(href) + "/HEAD.html").replace(/\/+/g, "/")).toString() : + ""; var customDirListingFooter = fs.existsSync(("." + decodeURIComponent(href) + "/.dirfoot").replace(/\/+/g, "/")) ? fs.readFileSync(("." + decodeURIComponent(href) + "/.dirfoot").replace(/\/+/g, "/")).toString() : (fs.existsSync(("." + decodeURIComponent(href) + "/FOOT.html").replace(/\/+/g, "/")) && (os.platform != "win32" || href != "/")) ? - fs.readFileSync(("." + decodeURIComponent(href) + "/FOOT.html").replace(/\/+/g, "/")).toString() : - ""; + fs.readFileSync(("." + decodeURIComponent(href) + "/FOOT.html").replace(/\/+/g, "/")).toString() : + ""; // Check if custom header has HTML tag var headerHasHTMLTag = customDirListingHeader.replace(/|$)/g, "").match(/])*(?:>|$)/i); // Generate HTML head and footer based on configuration and custom content var htmlHead = (!configJSON.enableDirectoryListingWithDefaultHead || head == "" ? - (!headerHasHTMLTag ? - "Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "" : - customDirListingHeader.replace(//i, "Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "")) : - head.replace(//i, "Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "")) + + (!headerHasHTMLTag ? + "Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "" : + customDirListingHeader.replace(//i, "Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "")) : + head.replace(//i, "Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "")) + (!headerHasHTMLTag ? customDirListingHeader : "") + "

Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(//g, ">") + "

" + (checkPathLevel(decodeURIComponent(origHref)) < 1 ? "" : ""); @@ -4185,7 +4275,7 @@ if (!cluster.isPrimary) { return; } - // Handle redirects to addresses with www. + // Handle redirects to addresses with "www." prefix if (wwwredirect) { var hostname = req.headers.host.split[":"]; var hostport = null; @@ -4203,14 +4293,14 @@ if (!cluster.isPrimary) { if (!isProxy) { for (var i = (_mapBegIndex ? _mapBegIndex : 0); i < map.length; i++) { var mapEntry = map[i]; - if(href != "/" && (mapEntry.isNotDirectory || mapEntry.isNotFile) && !_fileState) { - fs.stat("." + decodeURIComponent(href), function(err, stats) { + if (href != "/" && (mapEntry.isNotDirectory || mapEntry.isNotFile) && !_fileState) { + fs.stat("." + decodeURIComponent(href), function (err, stats) { var _fileState = 3; - if(err) { + if (err) { _fileState = 3; - } else if(stats.isDirectory()) { + } else if (stats.isDirectory()) { _fileState = 2; - } else if(stats.isFile()) { + } else if (stats.isFile()) { _fileState = 1; } else { _fileState = 3; @@ -4220,7 +4310,7 @@ if (!cluster.isPrimary) { doCallback = false; break; } - if (matchHostname(mapEntry.host) && address.match(createRegex(mapEntry.definingRegex)) && !(mapEntry.isNotDirectory && _fileState == 2) && !(mapEntry.isNotFile && _fileState == 1)) { + if (matchHostname(mapEntry.host) && ipMatch(mapEntry.ip, req.socket ? req.socket.localAddress : undefined) && address.match(createRegex(mapEntry.definingRegex)) && !(mapEntry.isNotDirectory && _fileState == 2) && !(mapEntry.isNotFile && _fileState == 1)) { try { mapEntry.replacements.forEach(function (replacement) { rewrittenURL = rewrittenURL.replace(createRegex(replacement.regex), replacement.replacement); @@ -4234,7 +4324,7 @@ if (!cluster.isPrimary) { } } } - if(doCallback) callback(null, rewrittenURL); + if (doCallback) callback(null, rewrittenURL); } // Trailing slash redirection @@ -4269,12 +4359,12 @@ if (!cluster.isPrimary) { var origHref = href; // Add web root postfixes - if(!isProxy) { + if (!isProxy) { var urlWithPostfix = req.url; var postfixPrefix = ""; wwwrootPostfixPrefixesVHost.every(function (currentPostfixPrefix) { if (req.url.indexOf(currentPostfixPrefix) == 0) { - if (currentPostfixPrefix.match(/\/+$/)) postfixPrefix = currentPostfixPrefix.replace(/\/+$/,""); + if (currentPostfixPrefix.match(/\/+$/)) postfixPrefix = currentPostfixPrefix.replace(/\/+$/, ""); else if (urlWithPostfix.length == currentPostfixPrefix.length || urlWithPostfix[currentPostfixPrefix.length] == "?" || urlWithPostfix[currentPostfixPrefix.length] == "/" || urlWithPostfix[currentPostfixPrefix.length] == "#") postfixPrefix = currentPostfixPrefix; else return true; urlWithPostfix = urlWithPostfix.substr(postfixPrefix.length); @@ -4284,7 +4374,7 @@ if (!cluster.isPrimary) { } }); wwwrootPostfixesVHost.every(function (postfixEntry) { - if (matchHostname(postfixEntry.host) && !(postfixEntry.skipRegex && req.url.match(createRegex(postfixEntry.skipRegex)))) { + if (matchHostname(postfixEntry.host) && ipMatch(postfixEntry.ip, req.socket ? req.socket.localAddress : undefined) && !(postfixEntry.skipRegex && req.url.match(createRegex(postfixEntry.skipRegex)))) { urlWithPostfix = postfixPrefix + "/" + postfixEntry.postfix + urlWithPostfix; return false; } else { @@ -4345,9 +4435,9 @@ if (!cluster.isPrimary) { } } } - + // Rewrite URLs - rewriteURL(req.url, rewriteMap, function(err, rewrittenURL) { + rewriteURL(req.url, rewriteMap, function (err, rewrittenURL) { if (err) { callServerError(500, undefined, err); return; @@ -4412,7 +4502,7 @@ if (!cluster.isPrimary) { try { res.setHeader(hkS, hkh[hkS]); } catch (err) { - // Headers will not be set. + // Headers will not be set. } }); } @@ -4446,20 +4536,20 @@ if (!cluster.isPrimary) { // Scan for non-standard codes if (!isProxy && nonStandardCodes != undefined) { for (var i = 0; i < nonStandardCodes.length; i++) { - if (matchHostname(nonStandardCodes[i].host)) { + if (matchHostname(nonStandardCodes[i].host) && ipMatch(nonStandardCodes[i].ip, req.socket ? req.socket.localAddress : undefined)) { var isMatch = false; if (nonStandardCodes[i].regex) { - // Regex match + // Regex match var createdRegex = createRegex(nonStandardCodes[i].regex, true); isMatch = req.url.match(createdRegex) || href.match(createdRegex); regexI[i] = createdRegex; } else { - // Non-regex match + // Non-regex match isMatch = nonStandardCodes[i].url == href || (os.platform() == "win32" && nonStandardCodes[i].url.toLowerCase() == href.toLowerCase()); } if (isMatch) { if (nonStandardCodes[i].scode == 401) { - // HTTP authentication + // HTTP authentication if (authIndex == -1) { authIndex = i; } @@ -4670,10 +4760,10 @@ if (!cluster.isPrimary) { } } if (authcode.disableBruteProtection) { - // Don't brute-force protect it, just do HTTP authentication + // Don't brute-force protect it, just do HTTP authentication authorizedCallback(false); } else if (!process.send) { - // Query data from JS object database + // Query data from JS object database if (!bruteForceDb[reqip] || !bruteForceDb[reqip].lastAttemptDate || (new Date() - 300000 >= bruteForceDb[reqip].lastAttemptDate)) { if (bruteForceDb[reqip] && bruteForceDb[reqip].invalidAttempts >= 10) bruteForceDb[reqip] = { invalidAttempts: 5 @@ -4794,12 +4884,14 @@ if (!cluster.isPrimary) { var closedMaster = true; +// IPC listener for server listening signalization function listenConnListener(msg) { if (msg == "\x12LISTEN") { listeningMessage(); } } +// IPC listener for brue force protection function bruteForceListenerWrapper(worker) { return function bruteForceListener(message) { var ip = ""; @@ -4834,6 +4926,7 @@ function bruteForceListenerWrapper(worker) { var isWorkerHungUpBuff = true; var isWorkerHungUpBuff2 = true; +// General IPC message listener function msgListener(msg) { for (var i = 0; i < Object.keys(cluster.workers).length; i++) { if (msg == "\x12END") { @@ -4978,11 +5071,11 @@ function start(init) { if (process.isBun) { serverconsole.locwarnmessage("Bun support is experimental. Some features of SVR.JS, SVR.JS mods and SVR.JS server-side JavaScript may not work as expected."); if (users.some(function (entry) { - return entry.pbkdf2; - })) serverconsole.locwarnmessage("PBKDF2 password hashing function in Bun blocks the event loop, which may result in denial of service."); + return entry.pbkdf2; + })) serverconsole.locwarnmessage("PBKDF2 password hashing function in Bun blocks the event loop, which may result in denial of service."); if (users.some(function (entry) { - return entry.scrypt; - })) serverconsole.locwarnmessage("scrypt password hashing function in Bun blocks the event loop, which may result in denial of service."); + return entry.scrypt; + })) serverconsole.locwarnmessage("scrypt password hashing function in Bun blocks the event loop, which may result in denial of service."); } if (cluster.isPrimary === undefined) serverconsole.locwarnmessage("You're running SVR.JS on single thread. Reliability may suffer, as the server is stopped after crash."); if (crypto.__disabled__ !== undefined) serverconsole.locwarnmessage("Your Node.JS version doesn't have crypto support! The 'crypto' module is essential for providing cryptographic functionality in Node.JS. Without crypto support, certain security features may be unavailable, and some functionality may not work as expected. It's recommended to use a Node.JS version that includes crypto support to ensure the security and proper functioning of your server."); @@ -5813,20 +5906,22 @@ if (cluster.isPrimary || cluster.isPrimary === undefined) { process.exit(); }); } else { + // Crash handler process.on("uncaughtException", function (err) { - // CRASH HANDLER serverconsole.locerrmessage("SVR.JS worker just crashed!!!"); serverconsole.locerrmessage("Stack:"); serverconsole.locerrmessage(generateErrorStack(err)); process.exit(err.errno); }); + process.on("unhandledRejection", function (err) { - // CRASH HANDLER serverconsole.locerrmessage("SVR.JS worker just crashed!!!"); serverconsole.locerrmessage("Stack:"); serverconsole.locerrmessage(err.stack ? generateErrorStack(err) : String(err)); process.exit(err.errno); }); + + // Warning handler process.on("warning", function (warning) { serverconsole.locwarnmessage(warning.message); if (warning.stack) { diff --git a/tests.html b/tests.html index c9ddd91..e347035 100644 --- a/tests.html +++ b/tests.html @@ -1,7 +1,7 @@ - SVR.JS 3.14.0 Tests + SVR.JS 3.14.1 Tests -

SVR.JS 3.14.0 Tests

+

SVR.JS 3.14.1 Tests

Directory (without trailing slash)

Directory (with query)

Filename Size Date
\"[RET]\"Return