1
0
Fork 0
forked from svrjs/svrjs
This repository has been archived on 2024-11-10. You can view files and clone it, but cannot push or open issues or pull requests.
svrjs/svr.js

5502 lines
271 KiB
JavaScript
Raw Normal View History

2023-07-29 20:44:13 +02:00
/////////////////////////////////////////
// //
// S V R . J S //
// S O U R C E C O D E //
// //
/////////////////////////////////////////
/////////////////////////////////////////
////// DorianTech SVR.JS //////
//////web server running on Node.JS//////
/////////////////////////////////////////
/////////////////////////////////////////
/*
* MIT License
*
* Copyright (c) 2020 DorianTech S.A.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//Check if SVR.JS is running on Node.JS-compatible runtime. If not, just error it out.
2023-07-29 20:32:17 +02:00
if (typeof require === "undefined") {
if (typeof ActiveXObject !== "undefined" && typeof WScript !== "undefined") {
2023-08-09 21:09:23 +02:00
// If it runs on Windows Script Host, display an error message.
2023-07-29 20:32:17 +02:00
var shell = new ActiveXObject("WScript.Shell");
shell.Popup("SVR.JS doesn't work on Windows Script Host. SVR.JS requires use of Node.JS (or compatible JS runtime).", undefined, "Can't start SVR.JS", 16);
WScript.quit();
} else {
if (typeof alert !== "undefined" && typeof document !== "undefined") {
2023-08-09 21:09:23 +02:00
// If it runs on web browser, display an alert.
2023-07-29 20:32:17 +02:00
alert("SVR.JS doesn't work on web browser. SVR.JS requires use of Node.JS (or compatible JS runtime).");
}
2023-08-09 21:09:23 +02:00
// If it's not, throw an error.
2023-07-29 20:32:17 +02:00
if (typeof document !== "undefined") {
throw new Error("SVR.JS doesn't work on web browser. SVR.JS requires use of Node.JS (or compatible JS runtime).");
} else {
throw new Error("SVR.JS doesn't work on Deno/QuickJS. SVR.JS requires use of Node.JS (or compatible JS runtime).");
}
}
}
2023-08-09 21:09:23 +02:00
2023-07-29 20:32:17 +02:00
var secure = false;
var disableMods = false;
2023-07-29 20:44:13 +02:00
//ASCII art SVR.JS logo ;)
2023-07-29 20:32:17 +02:00
var logo = ["", "", "", " \x1b[38;5;002m&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&", " &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&", " &&&\x1b[38;5;243m(((((((((((((((((((((((((((((((((((((((((((((((((((\x1b[38;5;002m&&&", " \x1b[38;5;002m&&\x1b[38;5;243m((((((\x1b[38;5;241m###########\x1b[38;5;243m(((((((((((((((((((((((\x1b[38;5;011m***\x1b[38;5;243m(\x1b[38;5;011m***\x1b[38;5;243m(\x1b[38;5;011m***\x1b[38;5;243m((\x1b[38;5;002m&&", " \x1b[38;5;002m&&&\x1b[38;5;243m(((((((((((((((((((((((((((((((((((((((((((((((((((\x1b[38;5;002m&&&", " \x1b[38;5;002m&&&\x1b[38;5;243m(((((((((((((((((((((((((((((((((((((((((((((((((((\x1b[38;5;002m&&&", " \x1b[38;5;002m&&\x1b[38;5;243m((((((\x1b[38;5;241m###########\x1b[38;5;243m(((((((((((((((((((((((\x1b[38;5;011m***\x1b[38;5;243m(\x1b[38;5;015m \x1b[38;5;243m(\x1b[38;5;011m***\x1b[38;5;243m((\x1b[38;5;002m&&", " \x1b[38;5;002m&&&\x1b[38;5;243m(((((((((((((((((((((((((((((((((((((((((((((((((((\x1b[38;5;002m&&&", " \x1b[38;5;002m&&&\x1b[38;5;243m(((((((((((((((((((((((((((((((((((((((((((((((((((\x1b[38;5;002m&&&", " \x1b[38;5;002m&&\x1b[38;5;243m((((((\x1b[38;5;241m###########\x1b[38;5;243m(((((((((((((((((((((((\x1b[38;5;015m \x1b[38;5;243m(\x1b[38;5;015m \x1b[38;5;243m(\x1b[38;5;015m \x1b[38;5;243m((\x1b[38;5;002m&&", " \x1b[38;5;002m&&&\x1b[38;5;243m(((((((((((((((((((((((((((((((((((((((((((((((((((\x1b[38;5;002m&&&", " \x1b[38;5;002m&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&", " \x1b[38;5;002m&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&", " \x1b[38;5;002m&&&&&&&&\x1b[38;5;010m#########################################\x1b[38;5;002m&&&&&&&&", " \x1b[38;5;002m&&&&&\x1b[38;5;010m###############################################\x1b[38;5;002m&&&&&", " \x1b[38;5;002m&&&\x1b[38;5;010m###################################################\x1b[38;5;002m&&&", " \x1b[38;5;002m&&\x1b[38;5;010m####\x1b[38;5;016m@@@@@@\x1b[38;5;010m#\x1b[38;5;016m@@@\x1b[38;5;010m###\x1b[38;5;016m@@@\x1b[38;5;010m#\x1b[38;5;016m@@@@@@@\x1b[38;5;010m###########\x1b[38;5;016m@@\x1b[38;5;010m##\x1b[38;5;016m@@@@@@\x1b[38;5;010m####\x1b[38;5;002m&&", " \x1b[38;5;002m&&\x1b[38;5;010m###\x1b[38;5;016m@@\x1b[38;5;010m#######\x1b[38;5;016m@@\x1b[38;5;010m###\x1b[38;5;016m@@\x1b[38;5;010m##\x1b[38;5;016m@@\x1b[38;5;010m####\x1b[38;5;016m@@\x1b[38;5;010m##########\x1b[38;5;016m@@\x1b[38;5;010m#\x1b[38;5;016m@@\x1b[38;5;010m#########\x1b[38;5;002m&&", " \x1b[38;5;002m&&\x1b[38;5;010m######\x1b[38;5;040m#\x1b[38;5;016m@@@@\x1b[38;5;010m##\x1b[38;5;016m@@\x1b[38;5;010m#\x1b[38;5;016m@@\x1b[38;5;010m###\x1b[38;5;016m@@@@@@@\x1b[38;5;010m#######\x1b[38;5;016m@@\x1b[38;5;010m##\x1b[38;5;016m@@\x1b[38;5;010m####\x1b[38;5;040m#\x1b[38;5;016m@@@@\x1b[38;5;010m###\x1b[38;5;002m&&", " \x1b[38;5;002m&&\x1b[38;5;010m###\x1b[38;5;016m@@\x1b[38;5;034m%\x1b[38;5;010m###\x1b[38;5;016m@@\x1b[38;5;010m###\x1b[38;5;016m@@@\x1b[38;5;010m####\x1b[38;5;016m@@\x1b[38;5;010m####\x1b[38;5;016m@@\x1b[38;5;010m##\x1b[38;5;016m@@\x1b[38;5;010m###\x1b[38;5;016m@@@@\x1b[38;5;010m##\x1b[38;5;016m@@\x1b[38;5;034m%\x1b[38;5;010m###\x1b[38;5;016m@@\x1b[38;5;010m###\x1b[38;5;002m&&", " \x1b[38;5;002m&&\x1b[38;5;010m#####################################################\x1b[38;5;002m&&", " \x1b[38;5;002m&&&\x1b[38;5;010m###################################################\x1b[38;5;002m&&&", " \x1b[38;5;002m&&&&&\x1b[38;5;010m###############################################\x1b[38;5;002m&&&&&", " \x1b[38;5;002m&&&&&&&&\x1b[38;5;010m#########################################\x1b[38;5;002m&&&&&&&&", " \x1b[38;5;002m&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&", " &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&", " &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&", " \x1b[38;5;246m///////", " ///////",
var fs = require("fs");
function factoryReset() {
console.log("Removing logs...");
deleteFolderRecursive(__dirname + "/log");
fs.mkdirSync(__dirname + "/log");
console.log("Removing temp folder...");
deleteFolderRecursive(__dirname + "/temp");
fs.mkdirSync(__dirname + "/temp");
console.log("Removing configuration file...");
fs.unlinkSync("config.json");
console.log("Done!");
process.exit(0);
}
function deleteFolderRecursive(path) {
if (fs.existsSync(path)) {
fs.readdirSync(path).forEach(function (file) {
var curPath = path + "/" + file;
if (fs.statSync(curPath).isDirectory()) { // recurse
deleteFolderRecursive(curPath);
} else { // delete file
fs.unlinkSync(curPath);
}
});
fs.rmdirSync(path);
}
}
var os = require("os");
var version = "Nightly-GitMain";
2023-07-29 20:32:17 +02:00
var singlethreaded = false;
if (process.versions) process.versions.svrjs = version; //Inject SVR.JS into process.versions
var args = process.argv;
for (var i = (process.argv[0].indexOf("node") > -1 || process.argv[0].indexOf("bun") > -1 ? 2 : 1); i < args.length; i++) {
if (args[i] == "-h" || args[i] == "--help" || args[i] == "-?" || args[i] == "/h" || args[i] == "/?") {
console.log("SVR.JS usage:");
console.log("node svr.js [-h] [--help] [-?] [/h] [/?] [--secure] [--reset] [--clean] [--disable-mods] [--single-threaded] [-v] [--version]");
console.log("-h -? /h /? --help -- Displays help");
console.log("--clean -- Cleans files, that SVR.JS created");
console.log("--reset -- Resets SVR.JS to factory settings (WARNING: DANGEROUS)");
console.log("--secure -- Runs HTTPS server");
console.log("--disable-mods -- Disables mods (safe mode)");
console.log("--single-threaded -- Run single-threaded");
console.log("-v --version -- Display server version");
process.exit(0);
} else if (args[i] == "--secure") {
secure = true;
} else if (args[i] == "-v" || args[i] == "--version") {
console.log("SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")");
process.exit(0);
} else if (args[i] == "--clean") {
console.log("Removing logs...");
deleteFolderRecursive(__dirname + "/log");
fs.mkdirSync(__dirname + "/log");
console.log("Removing temp folder...");
deleteFolderRecursive(__dirname + "/temp");
fs.mkdirSync(__dirname + "/temp");
console.log("Done!");
process.exit(0);
} else if (args[i] == "--reset") {
factoryReset();
} else if (args[i] == "--disable-mods") {
disableMods = true;
} else if (args[i] == "--single-threaded") {
singlethreaded = true;
} else {
console.log("Unrecognized argument: " + args[i]);
console.log("SVR.JS usage:");
console.log("node svr.js [-h] [--help] [-?] [/h] [/?] [--secure] [--reset] [--clean] [--disable-mods] [--single-threaded] [-v] [--version]");
console.log("-h -? /h /? --help -- Displays help");
console.log("--clean -- Cleans files, that SVR.JS created");
console.log("--reset -- Resets SVR.JS to factory settings (WARNING: DANGEROUS)");
console.log("--secure -- Runs HTTPS server");
console.log("--disable-mods -- Disables mods (safe mode)");
console.log("--single-threaded -- Run single-threaded");
console.log("-v --version -- Display server version");
process.exit(1);
}
}
var readline = require("readline");
var net = require("net");
var cluster = {};
if (!singlethreaded) {
try {
// Import cluster module
var cluster = require("cluster");
} catch (err) {
2023-07-29 20:32:17 +02:00
// Clustering is not supported!
}
// Cluster & IPC shim for Bun
cluster.bunShim = function () {
2023-07-29 20:32:17 +02:00
cluster.isMaster = !process.env.NODE_UNIQUE_ID;
cluster.isPrimary = cluster.isMaster;
2023-07-29 20:32:17 +02:00
cluster.isWorker = !cluster.isMaster;
if (cluster.isWorker) {
// Shim the cluster.worker object for worker processes
cluster.worker = {
id: parseInt(process.env.NODE_UNIQUE_ID),
process: process,
isDead: function () {
return false;
},
send: function (message, b, c, d) {
process.send(message, b, c, d);
}
};
if (!process.send) {
// Shim the process.send function for worker processes
var net = require("net");
var os = require("os");
var path = require("path");
// Create a fake IPC server to receive messages
var fakeIPCServer = net.createServer(function (socket) {
var receivedData = "";
socket.on("data", function (data) {
receivedData += data.toString();
});
socket.on("end", function () {
process.emit("message", receivedData);
});
2023-07-29 20:44:13 +02:00
});
fakeIPCServer.listen(os.platform() === "win32" ? path.join("\\\\?\\pipe", __dirname, "temp/.W" + process.pid + ".ipc") : (__dirname + "/temp/.W" + process.pid + ".ipc"));
2023-07-29 20:32:17 +02:00
process.send = function (message) {
// Create a fake IPC connection to send messages
var fakeIPCConnection = net.createConnection(os.platform() === "win32" ? path.join("\\\\?\\pipe", __dirname, "temp/.P" + process.pid + ".ipc") : (__dirname + "/temp/.P" + process.pid + ".ipc"), function () {
fakeIPCConnection.end(message);
});
};
}
}
// Custom implementation for cluster.fork()
cluster._workersCounter = 1;
cluster.workers = {};
cluster.fork = function (env) {
var child_process = require("child_process");
var newEnvironment = JSON.parse(JSON.stringify(env ? env : process.env));
newEnvironment.NODE_UNIQUE_ID = cluster._workersCounter;
var newArguments = JSON.parse(JSON.stringify(process.argv));
var command = newArguments.shift();
var newWorker = child_process.spawn(command, newArguments, {
env: newEnvironment,
stdio: ["inherit", "inherit", "inherit", "ipc"]
});
newWorker.process = newWorker;
newWorker.isDead = function () {
return newWorker.exitCode !== null || newWorker.killed;
};
newWorker.id = newEnvironment.NODE_UNIQUE_ID;
function checkSendImplementation(worker) {
var sendImplemented = true;
if (!worker.send) {
sendImplemented = false;
}
oldLog = console.log;
console.log = function(a,b,c,d,e,f) {
if(a == "ChildProcess.prototype.send() - Sorry! Not implemented yet") {
throw new Error("NOT IMPLEMENTED");
} else {
oldLog(a,b,c,d,e,f);
}
}
2023-07-29 20:32:17 +02:00
try {
worker.send(undefined);
} catch (err) {
if (err.message === "NOT IMPLEMENTED") {
2023-07-29 20:32:17 +02:00
sendImplemented = false;
}
}
console.log = oldLog;
2023-07-29 20:32:17 +02:00
return sendImplemented;
}
if (!checkSendImplementation(newWorker)) {
var net = require("net");
var os = require("os");
2023-07-29 20:32:17 +02:00
// Create a fake IPC server for worker process to receive messages
var fakeWorkerIPCServer = net.createServer(function (socket) {
var receivedData = "";
socket.on("data", function (data) {
receivedData += data.toString();
});
socket.on("end", function () {
newWorker.emit("message", receivedData);
});
}).listen(os.platform() === "win32" ? path.join("\\\\?\\pipe", __dirname, "temp/.P" + newWorker.process.pid + ".ipc") : (__dirname + "/temp/.P" + newWorker.process.pid + ".ipc"));
// Cleanup when worker process exits
newWorker.on("exit", function () {
fakeWorkerIPCServer.close();
delete cluster.workers[newWorker.id];
});
newWorker.send = function (message, fakeParam2, fakeParam3, fakeParam4, tries) {
if (!tries) tries = 0;
try {
// Create a fake IPC connection to send messages to worker process
var fakeWorkerIPCConnection = net.createConnection(os.platform() === "win32" ? path.join("\\\\?\\pipe", __dirname, "temp/.W" + newWorker.process.pid + ".ipc") : (__dirname + "/temp/.W" + newWorker.process.pid + ".ipc"), function () {
fakeWorkerIPCConnection.end(message);
});
} catch (err) {
if (tries > 25) throw err;
2023-07-29 20:32:17 +02:00
setTimeout(function () {
newWorker.send(message, undefined, undefined, tries + 1);
}, 10);
}
};
}
cluster.workers[newWorker.id] = newWorker;
cluster._workersCounter++;
return newWorker;
};
}
if (process.isBun && (cluster.isMaster === undefined || (cluster.isMaster && process.env.NODE_UNIQUE_ID))) {
cluster.bunShim();
}
// Shim cluster.isPrimary field
if (cluster.isPrimary === undefined && cluster.isMaster !== undefined) cluster.isPrimary = cluster.isMaster;
2023-07-29 20:32:17 +02:00
}
2023-08-09 21:09:23 +02:00
// ETag-related
2023-07-29 20:44:13 +02:00
var ETagDB = {};
function generateETag(filePath, stat) {
if(!ETagDB[filePath + "-" + stat.size + "-" + stat.mtime]) ETagDB[filePath + "-" + stat.size + "-" + stat.mtime] = sha256(filePath + "-" + stat.size + "-" + stat.mtime);
return ETagDB[filePath + "-" + stat.size + "-" + stat.mtime];
}
2023-08-09 21:09:23 +02:00
// Brute force-related
var bruteForceDb = {};
// SVR.JS worker spawn-related
2023-07-29 20:32:17 +02:00
var SVRJSInitialized = false;
var exiting = false;
2023-07-29 20:44:13 +02:00
var reallyExiting = false;
2023-07-29 20:32:17 +02:00
var crashed = false;
function SVRJSFork() {
//Log
if (SVRJSInitialized) serverconsole.locmessage("Starting next thread, because previous one hung up/crashed...");
//Fork new worker
var newWorker = {};
try {
newWorker = cluster.fork();
} catch (err) {
if(err.name == "NotImplementedError") {
// If cluster.fork throws a NotImplementedError, shim cluster module
cluster.bunShim();
newWorker = cluster.fork();
} else {
throw err;
}
}
2023-07-29 20:32:17 +02:00
newWorker.on("error", function (err) {
2023-07-29 20:44:13 +02:00
if(!reallyExiting) serverconsole.locwarnmessage("There was a problem when handling SVR.JS worker! (from master process side) Reason: " + err.message);
2023-07-29 20:32:17 +02:00
});
newWorker.on("exit", function () {
if (!exiting && Object.keys(cluster.workers).length == 0) {
crashed = true;
SVRJSFork();
}
});
newWorker.on("message", bruteForceListenerWrapper(newWorker));
newWorker.on("message", listenConnListener);
}
2023-07-29 20:44:13 +02:00
2023-07-29 20:32:17 +02:00
var http = require("http");
http.STATUS_CODES[497] = "HTTP Request Sent to HTTPS Port";
http.STATUS_CODES[598] = "Network Read Timeout Error";
http.STATUS_CODES[599] = "Network Connect Timeout Error";
var url = require("url");
try {
var gracefulFs = require("graceful-fs");
if (!process.isBun) gracefulFs.gracefulify(fs); //Bun + graceful-fs + SVR.JS + requests about static content = 500 Internal Server Error!
} catch (err) {
2023-07-29 20:32:17 +02:00
//Don't use graceful-fs
}
var path = require("path");
var hexstrbase64 = undefined;
try {
hexstrbase64 = require("./hexstrbase64/index.js");
} catch (err) {
2023-07-29 20:32:17 +02:00
//Don't use hexstrbase64
}
var svrmodpack = undefined;
try {
svrmodpack = require("svrmodpack");
} catch (err) {
2023-07-29 20:32:17 +02:00
svrmodpack = {
_errored: err
2023-07-29 20:32:17 +02:00
};
}
var zlib = require("zlib");
var tar = undefined;
try {
tar = require("tar");
} catch (err) {
2023-07-29 20:32:17 +02:00
tar = {
_errored: err
2023-07-29 20:32:17 +02:00
};
}
var formidable = undefined;
try {
formidable = require("formidable");
} catch (err) {
2023-07-29 20:32:17 +02:00
formidable = {
_errored: err
2023-07-29 20:32:17 +02:00
};
}
var ocsp = undefined;
var ocspCache = undefined;
try {
ocsp = require("ocsp");
ocspCache = new ocsp.Cache();
} catch (err) {
2023-07-29 20:32:17 +02:00
ocsp = {
_errored: err
2023-07-29 20:32:17 +02:00
};
}
var prettyBytes = undefined;
try {
prettyBytes = require("pretty-bytes");
} catch (err) {
2023-07-29 20:32:17 +02:00
prettyBytes = {
_errored: err
2023-07-29 20:32:17 +02:00
};
}
var http2 = {};
try {
http2 = require("http2");
if (process.isBun) {
try {
http2.Http2ServerRequest();
} catch (err) {
if (err.name == "NotImplementedError" || err.code == "ERR_NOT_IMPLEMENTED") throw err;
2023-07-29 20:32:17 +02:00
}
}
} catch (err) {
2023-07-29 20:32:17 +02:00
http2.__disabled__ = null;
http2.createServer = function () {
2023-07-29 20:44:13 +02:00
throw new Error("HTTP/2 support is not present");
2023-07-29 20:32:17 +02:00
};
http2.createSecureServer = function () {
2023-07-29 20:44:13 +02:00
throw new Error("HTTP/2 support is not present");
2023-07-29 20:32:17 +02:00
};
http2.connect = function () {
2023-07-29 20:44:13 +02:00
throw new Error("HTTP/2 support is not present");
2023-07-29 20:32:17 +02:00
};
http2.get = function () {
2023-07-29 20:44:13 +02:00
throw new Error("HTTP/2 support is not present");
2023-07-29 20:32:17 +02:00
};
}
var crypto = {};
var https = {};
try {
crypto = require("crypto");
https = require("https");
} catch (err) {
2023-07-29 20:32:17 +02:00
crypto = {};
https = {};
crypto.__disabled__ = null;
https.createServer = function () {
2023-07-29 20:44:13 +02:00
throw new Error("Crypto support is not present");
2023-07-29 20:32:17 +02:00
};
http2.createSecureServer = function () {
2023-07-29 20:44:13 +02:00
throw new Error("Crypto support is not present");
2023-07-29 20:32:17 +02:00
};
https.connect = function () {
2023-07-29 20:44:13 +02:00
throw new Error("Crypto support is not present");
2023-07-29 20:32:17 +02:00
};
https.get = function () {
2023-07-29 20:44:13 +02:00
throw new Error("Crypto support is not present");
2023-07-29 20:32:17 +02:00
};
}
var mime = require("mime-types");
var pubip = "";
var pubport = 80;
var port = 80;
var domain = "";
var spubport = 443;
var sport = 443;
var mods = [];
if (!fs.existsSync(__dirname + "/log")) fs.mkdirSync(__dirname + "/log");
if (!fs.existsSync(__dirname + "/mods")) fs.mkdirSync(__dirname + "/mods");
if (!fs.existsSync(__dirname + "/temp")) fs.mkdirSync(__dirname + "/temp");
var modFiles = fs.readdirSync(__dirname + "/mods").sort();
var modInfos = [];
function sizify(x) {
try {
if (prettyBytes._errored) throw prettyBytes._errored;
return prettyBytes(parseInt(x), {
minimumFractionDigits: 0,
maximumFractionDigits: 2
}).replace(/ /g, "").replace(/B/ig, "").replace(/k/g, "K");
} catch (err) {
2023-07-29 20:32:17 +02:00
if (x < 1000) return x.toString();
if (x < 10000) return (Math.round(x / 10) / 100).toString() + "K";
if (x < 100000) return (Math.round(x / 100) / 10).toString() + "K";
if (x < 1000000) return (Math.round(x / 1000)).toString() + "K";
if (x < 10000000) return (Math.round(x / 10000) / 100).toString() + "M";
if (x < 100000000) return (Math.round(x / 100000) / 10).toString() + "M";
if (x < 1000000000) return (Math.round(x / 1000000)).toString() + "M";
if (x < 10000000000) return (Math.round(x / 10000000) / 100).toString() + "G";
if (x < 100000000000) return (Math.round(x / 100000000) / 10).toString() + "G";
if (x < 1000000000000) return (Math.round(x / 1000000000)).toString() + "G";
if (x < 10000000000000) return (Math.round(x / 10000000000) / 100).toString() + "T";
if (x < 100000000000000) return (Math.round(x / 100000000000) / 10).toString() + "T";
if (x < 1000000000000000) return (Math.round(x / 1000000000000)).toString() + "T";
if (x < 10000000000000000) return (Math.round(x / 10000000000000) / 100).toString() + "P";
if (x < 100000000000000000) return (Math.round(x / 100000000000000) / 10).toString() + "P";
return (Math.round(x / 1000000000000000)).toString() + "P";
}
}
function getOS() {
var osType = os.type();
var platform = os.platform();
if (platform == "android") {
return "Android";
} else if (osType == "Windows_NT" || osType == "WindowsNT") {
var arch = os.arch();
if (arch == "ia32") {
return "Win32";
} else if (arch == "x64") {
return "Win64";
} else {
return "Win" + arch.toUpperCase();
}
} else if (osType.indexOf("CYGWIN") == 0) {
return "Cygwin";
} else if (osType.indexOf("MINGW") == 0) {
return "MinGW";
} else if (osType.indexOf("MSYS") == 0) {
return "MSYS";
} else if (osType.indexOf("UWIN") == 0) {
return "UWIN";
} else if (osType == "GNU") {
return "GNU Hurd";
} else {
return osType;
}
}
function createRegex(regex, isPath) {
var regexStrMatch = regex.match(/^\/((?:\\.|[^\/\\])*)\/([a-zA-Z0-9]*)$/);
if (!regexStrMatch) throw new Error("Invalid regex!");
var searchString = regexStrMatch[1];
var modifiers = regexStrMatch[2];
if (isPath && !modifiers.match(/i/i) && os.platform() == "win32") modifiers += "i";
2023-07-29 20:32:17 +02:00
return new RegExp(searchString, modifiers);
}
//IP Block list object
function ipBlockList(rawBlockList) {
2023-07-29 20:44:13 +02:00
// Initialize the instance with empty arrays
if (rawBlockList === undefined) rawBlockList = [];
var instance = {
raw: [],
rawtoPreparedMap: [],
prepared: [],
cidrs: []
};
// Function to normalize IPv4 address (remove leading zeros)
2023-07-29 20:32:17 +02:00
function normalizeIPv4Address(address) {
return address.replace(/(^|\.)(?:0(?!\.|$))+/g, "");
}
2023-07-29 20:44:13 +02:00
// Function to expand IPv6 address to full format
2023-07-29 20:32:17 +02:00
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)) {
groups = address.match(extractIpv4);
for (var i = 1; i < groups.length; i++) {
ipv4 += ("00" + (parseInt(groups[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;
for (var i = 0; i < sides.length; i++) {
groupsPresent += sides[i].split(":").length;
}
fullAddress += sides[0] + ":";
for (var i = 0; i < validGroupCount - groupsPresent; i++) {
fullAddress += "0000:";
}
fullAddress += sides[1];
}
var groups = fullAddress.split(":");
for (var i = 0; i < validGroupCount; i++) {
while (groups[i].length < validGroupSize) {
groups[i] = "0" + groups[i];
}
expandedAddress += (i != validGroupCount - 1) ? groups[i] + ":" : groups[i];
}
return expandedAddress;
}
2023-07-29 20:44:13 +02:00
// Convert IPv4 address to an integer representation
2023-07-29 20:32:17 +02:00
function ipv4ToInt(ip) {
var ips = ip.split(".");
return parseInt(ips[0]) * 16777216 + parseInt(ips[1]) * 65536 + parseInt(ips[2]) * 256 + parseInt(ips[3]);
}
2023-07-29 20:44:13 +02:00
// Get IPv4 CIDR block limits (min and max)
2023-07-29 20:32:17 +02:00
function getIPv4CIDRLimits(ip, cidrMask) {
var ipInt = ipv4ToInt(ip);
var exp = Math.pow(2, 32 - cidrMask);
var ipMin = Math.floor(ipInt / exp) * exp;
var ipMax = ipMin + exp - 1;
return {
min: ipMin,
max: ipMax
};
}
2023-07-29 20:44:13 +02:00
// Convert IPv6 address to an array of blocks
2023-07-29 20:32:17 +02:00
function ipv6ToBlocks(ip) {
var ips = ip.split(":");
var ip2s = [];
for (var i = 0; i < ips.length; i++) {
ip2s.push(parseInt(ips[i]));
}
return ip2s;
}
2023-07-29 20:44:13 +02:00
// Get IPv6 CIDR block limits (min and max)
2023-07-29 20:32:17 +02:00
function getIPv6CIDRLimits(ip, cidrMask) {
var ipBlocks = ipv6ToBlocks(ip);
var fieldsToDelete = Math.floor((128 - cidrMask) / 16);
var fieldMaskModify = (128 - cidrMask) % 16;
var ipBlockMin = [];
var ipBlockMax = [];
for (var i = 0; i < 8; i++) {
ipBlockMin.push((i < 8 - fieldsToDelete) ? ((i < 7 - fieldsToDelete) ? ipBlocks[i] : (ipBlocks[i] >> fieldMaskModify << fieldMaskModify)) : 0);
}
for (var i = 0; i < 8; i++) {
ipBlockMax.push((i < 8 - fieldsToDelete) ? ((i < 7 - fieldsToDelete) ? ipBlocks[i] : ((ipBlocks[i] >> fieldMaskModify << fieldMaskModify) + Math.pow(2, fieldMaskModify) - 1)) : 65535);
}
return {
min: ipBlockMin,
max: ipBlockMax
};
}
2023-07-29 20:44:13 +02:00
// Check if the IPv4 address matches the given CIDR block
2023-07-29 20:32:17 +02:00
function checkIfIPv4CIDRMatches(ipInt, cidrObject) {
if (cidrObject.v6) return false;
return ipInt >= cidrObject.min && ipInt <= cidrObject.max;
}
2023-07-29 20:44:13 +02:00
// Check if the IPv6 address matches the given CIDR block
2023-07-29 20:32:17 +02:00
function checkIfIPv6CIDRMatches(ipBlock, cidrObject) {
if (!cidrObject.v6) return false;
for (var i = 0; i < 8; i++) {
if (ipBlock[i] < cidrObject.min[i] || ipBlock[i] > cidrObject.max[i]) return true;
}
return false;
}
2023-07-29 20:44:13 +02:00
// Function to add an IP or CIDR block to the block list
2023-07-29 20:32:17 +02:00
instance.add = function (rawValue) {
2023-07-29 20:44:13 +02:00
// Initialize variables
2023-07-29 20:32:17 +02:00
var beginIndex = instance.prepared.length;
var cidrIndex = instance.cidrs.length;
var cidrMask = null;
var isIPv6 = false;
2023-07-29 20:44:13 +02:00
// Check if the input contains CIDR notation
2023-07-29 20:32:17 +02:00
if (rawValue.indexOf("/") > -1) {
var rwArray = rawValue.split("/");
cidrMask = rwArray.pop();
rawValue = rwArray.join("/");
}
2023-07-29 20:44:13 +02:00
// Normalize the IP address or expand the IPv6 address
2023-07-29 20:32:17 +02:00
rawValue = rawValue.toLowerCase();
if (rawValue.indexOf("::ffff:") == 0) rawValue = rawValue.substr(7);
if (rawValue.indexOf(":") > -1) {
isIPv6 = true;
rawValue = expandIPv6Address(rawValue);
} else {
rawValue = normalizeIPv4Address(rawValue);
}
2023-07-29 20:44:13 +02:00
// Add the IP or CIDR block to the appropriate list
2023-07-29 20:32:17 +02:00
if (cidrMask) {
var cidrLimits = {};
if (isIPv6) {
cidrLimits = getIPv6CIDRLimits(rawValue, cidrMask);
cidrLimits.v6 = true;
} else {
cidrLimits = getIPv4CIDRLimits(rawValue, cidrMask);
cidrLimits.v6 = false;
}
instance.cidrs.push(cidrLimits);
instance.rawtoPreparedMap.push({
cidr: true,
index: cidrIndex
});
} else {
instance.prepared.push(rawValue);
instance.rawtoPreparedMap.push({
cidr: false,
index: beginIndex
});
}
};
2023-07-29 20:44:13 +02:00
// Function to remove an IP or CIDR block from the block list
2023-07-29 20:32:17 +02:00
instance.remove = function (ip) {
var index = instance.raw.indexOf(ip);
if (index == -1) return false;
var map = instance.rawtoPreparedMap[index];
instance.raw.splice(index, 1);
instance.rawtoPreparedMap.splice(index, 1);
if (map.cidr) {
instance.cidrs.splice(map.index, 1);
} else {
instance.prepared.splice(map.index, 1);
}
return true;
};
2023-07-29 20:44:13 +02:00
// Function to check if an IP is blocked by the block list
2023-07-29 20:32:17 +02:00
instance.check = function (rawValue) {
if (instance.raw.length == 0) return false;
var isIPv6 = false;
2023-07-29 20:44:13 +02:00
// Normalize or expand the IP address
2023-07-29 20:32:17 +02:00
rawValue = rawValue.toLowerCase();
if (rawValue == "localhost") rawValue = "127.0.0.1";
if (rawValue.indexOf("::ffff:") == 0) rawValue = rawValue.substr(7);
if (rawValue.indexOf(":") > -1) {
isIPv6 = true;
rawValue = expandIPv6Address(rawValue);
} else {
rawValue = normalizeIPv4Address(rawValue);
}
2023-07-29 20:44:13 +02:00
// Check if the IP is in the prepared list
2023-07-29 20:32:17 +02:00
if (instance.prepared.indexOf(rawValue) > -1) return true;
2023-07-29 20:44:13 +02:00
// Check if the IP is within any CIDR block in the block list
2023-07-29 20:32:17 +02:00
if (instance.cidrs.length == 0) return false;
var ipParsedObject = (!isIPv6 ? ipv4ToInt : ipv6ToBlocks)(rawValue);
var checkMethod = (!isIPv6 ? checkIfIPv4CIDRMatches : checkIfIPv6CIDRMatches);
for (var i = 0; i < instance.cidrs.length; i++) {
if (checkMethod(ipParsedObject, instance.cidrs[i])) return true;
}
2023-07-29 20:44:13 +02:00
2023-07-29 20:32:17 +02:00
return false;
};
2023-07-29 20:44:13 +02:00
// Add initial raw block list values to the instance
2023-07-29 20:32:17 +02:00
for (var i = 0; i < rawBlockList.length; i++) {
instance.add(rawBlockList[i]);
}
2023-07-29 20:44:13 +02:00
2023-07-29 20:32:17 +02:00
return instance;
}
2023-07-29 20:44:13 +02:00
// Generate V8-style error stack from Error object.
function generateErrorStack(errorObject) {
// Split the error stack by newlines.
var errorStack = errorObject.stack ? errorObject.stack.split("\n") : [];
// If the error stack starts with the error name, return the original stack (it is V8-style then).
for (var i = 0; i < errorStack.length; i++) {
if (errorStack[i].indexOf(errorObject.name) == 0) return errorObject.stack;
2023-07-29 20:32:17 +02:00
}
2023-07-29 20:44:13 +02:00
// Create a new error stack with the error name and code (if available).
var newErrorStack = [errorObject.name + (errorObject.code ? ": " + errorObject.code : "") + (errorObject.message == "" ? "" : ": " + errorObject.message)];
// Process each line of the original error stack.
for (var i = 0; i < errorStack.length; i++) {
if (errorStack[i] != "") {
// Split the line into function and location parts (if available).
var errorFrame = errorStack[i].split("@");
2023-07-29 20:32:17 +02:00
var location = "";
2023-07-29 20:44:13 +02:00
if (errorFrame.length > 1) location = errorFrame.pop();
var func = errorFrame.join("@");
// Build the new error stack entry with function and location information.
newErrorStack.push(" at " + (func == "" ? (!location || location == "" ? "<anonymous>" : location) : (func + (!location || location == "" ? "" : " (" + location + ")"))));
2023-07-29 20:32:17 +02:00
}
}
2023-07-29 20:44:13 +02:00
// Join the new error stack entries with newlines and return the final stack.
return newErrorStack.join("\n");
2023-07-29 20:32:17 +02:00
}
2023-07-29 20:44:13 +02:00
2023-07-29 20:32:17 +02:00
var ifaces = {};
var ifaceEx = null;
try {
ifaces = os.networkInterfaces();
} catch (err) {
ifaceEx = err;
2023-07-29 20:32:17 +02:00
}
var ips = [];
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) {
if (iface.family !== "IPv4" || iface.internal !== false) {
return;
}
if (alias >= 1) {
ips.push(ifname + ":" + alias, iface.address);
} else {
ips.push(ifname, iface.address);
}
2023-07-29 20:44:13 +02:00
alias++;
2023-07-29 20:32:17 +02:00
});
});
if (ips.length == 0) {
Object.keys(ifaces).forEach(function (ifname) {
var alias = 0;
ifaces[ifname].forEach(function (iface) {
if (iface.family !== "IPv6" || iface.internal !== false) {
return;
}
if (alias >= 1) {
ips.push(ifname + ":" + alias, iface.address);
} else {
ips.push(ifname, iface.address);
}
2023-07-29 20:44:13 +02:00
alias++;
2023-07-29 20:32:17 +02:00
});
});
}
var host = ips[(ips.length) - 1];
if (!host) host = "[offline]";
var hp = host + ":" + port.toString();
var ipRequestCompleted = false;
var ipRequestGotError = false;
if (host != "[offline]" || ifaceEx) {
var ipRequest = (crypto.__disabled__ !== undefined ? http : https).get({
host: "api64.ipify.org",
port: (crypto.__disabled__ !== undefined ? 80 : 443),
path: "/",
headers: {
"User-Agent": (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS")
},
timeout: 5000
}, function (res) {
ipRequest.removeAllListeners("timeout");
res.on("data", function (d) {
if (res.statusCode != 200) {
ipRequestCompleted = true;
process.emit("ipRequestCompleted");
return;
}
pubip = d.toString();
if (!domain) {
if (pubip.indexOf(":") == -1) {
var parts = pubip.split(".");
var p1 = parseInt(parts[0]).toString(16);
var p2 = parseInt(parts[1]).toString(16);
var p3 = parseInt(parts[2]).toString(16);
var p4 = parseInt(parts[3]).toString(16);
var pp = parseInt(pubport).toString(16);
domain = p1 + p2 + p3 + p4 + pp + ".nodesvr.doriantech.com";
} else {
domain = pubip.replace(/[^0-9a-zA-Z]/gi, "").toLowerCase() + ".nodesvrip6.doriantech.com";
}
}
ipRequestCompleted = true;
process.emit("ipRequestCompleted");
});
});
ipRequest.on("error", function () {
if (crypto.__disabled__ || ipRequestGotError) {
ipRequestCompleted = true;
process.emit("ipRequestCompleted");
} else {
ipRequestGotError = true;
}
});
ipRequest.on("timeout", function () {
if (crypto.__disabled__ || ipRequestGotError) {
ipRequestCompleted = true;
process.emit("ipRequestCompleted");
} else {
ipRequestGotError = true;
}
});
if (!crypto.__disabled) {
var ipRequest2 = https.get({
host: "api.seeip.org",
port: 443,
path: "/",
headers: {
"User-Agent": (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS")
},
timeout: 5000
}, function (res) {
ipRequest2.removeAllListeners("timeout");
res.on("data", function (d) {
if (res.statusCode != 200) {
ipRequestCompleted = true;
process.emit("ipRequestCompleted");
return;
}
pubip = d.toString();
if (!domain) {
if (pubip.indexOf(":") == -1) {
var parts = pubip.split(".");
var p1 = parseInt(parts[0]).toString(16);
var p2 = parseInt(parts[1]).toString(16);
var p3 = parseInt(parts[2]).toString(16);
var p4 = parseInt(parts[3]).toString(16);
var pp = parseInt(pubport).toString(16);
domain = p1 + p2 + p3 + p4 + pp + ".nodesvr.doriantech.com";
} else {
domain = pubip.replace(/[^0-9a-zA-Z]/gi, "").toLowerCase() + ".nodesvrip6.doriantech.com";
}
}
ipRequestCompleted = true;
process.emit("ipRequestCompleted");
});
});
ipRequest2.on("error", function () {
if (crypto.__disabled__ || ipRequestGotError) {
ipRequestCompleted = true;
process.emit("ipRequestCompleted");
} else {
ipRequestGotError = true;
}
});
ipRequest2.on("timeout", function () {
if (crypto.__disabled__ || ipRequestGotError) {
ipRequestCompleted = true;
process.emit("ipRequestCompleted");
} else {
ipRequestGotError = true;
}
});
}
} else {
ipRequestCompleted = true;
}
function ipStatusCallback(callback) {
if (ipRequestCompleted) {
callback();
} else {
process.once("ipRequestCompleted", callback);
}
}
var configJSON = {};
if (fs.existsSync(__dirname + "/config.json")) {
var configJSONf = "";
try {
configJSONf = fs.readFileSync(__dirname + "/config.json"); //Read JSON File
} catch (err) {
2023-07-29 20:32:17 +02:00
throw new Error("Cannot read JSON file.");
}
try {
configJSON = JSON.parse(configJSONf); //Parse JSON
} catch (err) {
2023-07-29 20:32:17 +02:00
throw new Error("JSON Parse error.");
}
}
2023-07-29 20:44:13 +02:00
//Default server configuration properties
2023-07-29 20:32:17 +02:00
var rawBlackList = [];
var users = [];
var page404 = "404.html";
var serverAdmin = "[no contact information]";
var stackHidden = false;
var exposeServerVersion = true;
var rewriteMap = [];
var allowStatus = true;
var dontCompress = [];
var enableIPSpoofing = false;
var sni = {};
var disableNonEncryptedServer = false;
var disableToHTTPSRedirect = false;
var nonStandardCodesRaw = [];
2023-07-29 20:44:13 +02:00
//Get properties from config.json
2023-07-29 20:32:17 +02:00
if (configJSON.blacklist != undefined) rawBlackList = configJSON.blacklist;
if (configJSON.wwwredirect != undefined) wwwredirect = configJSON.wwwredirect;
if (configJSON.port != undefined) port = configJSON.port;
if (configJSON.pubport != undefined) pubport = configJSON.pubport;
if (configJSON.domian != undefined) domain = configJSON.domian;
if (configJSON.domain != undefined) domain = configJSON.domain;
if (configJSON.sport != undefined) sport = configJSON.sport;
if (configJSON.spubport != undefined) spubport = configJSON.spubport;
if (configJSON.page404 != undefined) page404 = configJSON.page404;
if (configJSON.serverAdministratorEmail != undefined) serverAdmin = configJSON.serverAdministratorEmail;
if (configJSON.nonStandardCodes != undefined) nonStandardCodesRaw = configJSON.nonStandardCodes;
if (configJSON.stackHidden != undefined) stackHidden = configJSON.stackHidden;
if (configJSON.users != undefined) users = configJSON.users;
if (configJSON.exposeServerVersion != undefined) exposeServerVersion = configJSON.exposeServerVersion;
if (configJSON.rewriteMap != undefined) rewriteMap = configJSON.rewriteMap;
if (configJSON.allowStatus != undefined) allowStatus = configJSON.allowStatus;
if (configJSON.dontCompress != undefined) dontCompress = configJSON.dontCompress;
if (configJSON.enableIPSpoofing != undefined) enableIPSpoofing = configJSON.enableIPSpoofing;
if (configJSON.secure != undefined) secure = secure || configJSON.secure;
if (configJSON.sni != undefined) sni = configJSON.sni;
if (configJSON.disableNonEncryptedServer != undefined) disableNonEncryptedServer = configJSON.disableNonEncryptedServer;
if (configJSON.disableToHTTPSRedirect != undefined) disableToHTTPSRedirect = configJSON.disableToHTTPSRedirect;
if (configJSON.wwwroot != undefined) {
var wwwroot = configJSON.wwwroot;
if (cluster.isPrimary || cluster.isPrimary === undefined) process.chdir(wwwroot);
2023-07-29 20:32:17 +02:00
} else {
if (cluster.isPrimary || cluster.isPrimary === undefined) process.chdir(__dirname);
2023-07-29 20:32:17 +02:00
}
//Compability for older mods
configJSON.version = version;
configJSON.productName = "SVR.JS";
var blacklist = ipBlockList(rawBlackList);
var nonStandardCodes = [];
for (var i = 0; i < nonStandardCodesRaw.length; i++) {
var nO = {};
var nsKeys = Object.keys(nonStandardCodesRaw[i]);
for (var j = 0; j < nsKeys.length; j++) {
if (nsKeys[j] != "users") {
nO[nsKeys[j]] = nonStandardCodesRaw[i][nsKeys[j]];
} else {
nO["users"] = ipBlockList(nonStandardCodesRaw[i].users);
}
}
nonStandardCodes.push(nO);
}
var customHeaders = (configJSON.customHeaders == undefined ? {} : JSON.parse(JSON.stringify(configJSON.customHeaders)));
if (exposeServerVersion) {
customHeaders["Server"] = "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")";
} else {
customHeaders["Server"] = "SVR.JS";
}
function getCustomHeaders() {
return JSON.parse(JSON.stringify(customHeaders));
}
var vnum = 0;
try {
vnum = process.config.variables.node_module_version;
} catch (err) {
2023-07-29 20:32:17 +02:00
//Version number not retrieved
}
2023-07-29 20:44:13 +02:00
2023-07-29 20:32:17 +02:00
if (vnum === undefined) vnum = 0;
2023-07-29 20:44:13 +02:00
if (process.isBun) vnum = 64;
2023-07-29 20:32:17 +02:00
2023-07-29 20:44:13 +02:00
// NOTE: One of regexes used here is reported as vulnerable according to Devina ReDOS checker, but it's actually just FALSE POSITIVE.
2023-07-29 20:32:17 +02:00
function sanitizeURL(resource) {
if (resource == "*") return "*";
if (resource == "") return "";
// Remove null characters
resource = resource.replace(/%00/ig, "").replace(/\0/g,"");
// Check if URL is malformed (e.g. %c0%af or %u002f or simply %as)
if (resource.match(/%(?:c[01]|f[ef]|(?![0-9a-f]{2}).{2}|.{0,1}$)/gi)) throw new URIError("URI malformed");
// Decode URL-encoded characters while preserving certain characters
resource = resource.replace(/%([0-9a-f]{2})/gi, function (match, hex) {
var decodedChar = String.fromCharCode(parseInt(hex, 16));
return /(?![;?:@&=+$,#%])[!-~]/.test(decodedChar) ? decodedChar : "%" + hex;
});
var sanitizedResource = resource;
// Ensure the resource starts with a slash
if (resource[0] != "/") sanitizedResource = "/" + sanitizedResource;
// Convert backslashes to slashes and remove duplicate slashes
sanitizedResource = sanitizedResource.replace(/\\/g, "/").replace(/\/+/g, "/");
// Handle relative navigation (e.g., "/./", "/../", "../", "./"), also remove trailing dots in paths
sanitizedResource = sanitizedResource.replace(/\/\.(?:\.{2,})?(?=($|\/))/g, "$1").replace(/([^.\/])\.+(?=($|\/))/g, "$1$2").replace(/\/+/g, "/");
while (sanitizedResource.match(/\/(?!\.\.\/)[^\/]+\/\.\.(?=(\/|$))/g)) {
sanitizedResource = sanitizedResource.replace(/\/(?!\.\.\/)[^\/]+\/\.\.(?=(\/|$))/g, "$1").replace(/\/+/g, "/");
}
sanitizedResource = sanitizedResource.replace(/\/\.\.(?=(\/|$))/g, "$1").replace(/\/+/g, "/");
if (sanitizedResource.length == 0) return "/";
else return sanitizedResource;
}
var key = "";
var cert = "";
if (secure) {
if (!configJSON.key) configJSON.key = "cert/key.key";
if (!configJSON.cert) configJSON.cert = "cert/cert.crt";
} else {
key = "SSL DISABLED";
cert = "SSL DISABLED";
configJSON.cert = "SSL DISABLED";
configJSON.key = "SSL DISABLED";
}
if (!fs.existsSync(__dirname + "/config.json")) {
saveConfig();
}
2023-07-29 20:44:13 +02:00
//Load SNI
2023-07-29 20:32:17 +02:00
if (secure) {
key = fs.readFileSync((configJSON.key[0] != "/" && !configJSON.key.match(/^[A-Z0-9]:\\/)) ? __dirname + "/" + configJSON.key : configJSON.key).toString();
cert = fs.readFileSync((configJSON.cert[0] != "/" && !configJSON.cert.match(/^[A-Z0-9]:\\/)) ? __dirname + "/" + configJSON.cert : configJSON.cert).toString();
var sniNames = Object.keys(sni);
var sniCredentials = [];
for (var i = 0; i < sniNames.length; i++) {
sniCredentials.push({
name: sniNames[i],
cert: fs.readFileSync((sni[sniNames[i]].cert[0] != "/" && !sni[sniNames[i]].cert.match(/^[A-Z0-9]:\\/)) ? __dirname + "/" + sni[sniNames[i]].cert : sni[sniNames[i]].cert).toString(),
key: fs.readFileSync((sni[sniNames[i]].key[0] != "/" && !sni[sniNames[i]].key.match(/^[A-Z0-9]:\\/)) ? __dirname + "/" + sni[sniNames[i]].key : sni[sniNames[i]].key).toString()
});
}
}
var logFile = undefined;
var logSync = false;
2023-07-29 20:44:13 +02:00
//Logging function
2023-07-29 20:32:17 +02:00
function LOG(s) {
try {
if (configJSON.enableLogging || configJSON.enableLogging == undefined) {
if (logSync) {
fs.appendFileSync(__dirname + "/log/" + (cluster.isPrimary ? "master" : (cluster.isPrimary === undefined ? "singlethread" : "worker")) + "-" + timestamp + ".log", "[" + new Date().toISOString() + "] " + s + "\r\n");
2023-07-29 20:32:17 +02:00
} else {
if (!logFile) {
logFile = fs.createWriteStream(__dirname + "/log/" + (cluster.isPrimary ? "master" : (cluster.isPrimary === undefined ? "singlethread" : "worker")) + "-" + timestamp + ".log", {
2023-07-29 20:32:17 +02:00
flags: "a",
autoClose: false
});
}
if (logFile.writable) {
logFile.write("[" + new Date().toISOString() + "] " + s + "\r\n");
} else {
throw new Error("Log file stream is closed.");
}
}
}
} catch (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);
2023-07-29 20:32:17 +02:00
}
}
var serverconsole = {
climessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.climessage(nmsg[i]);
}
return;
}
console.log("SERVER CLI MESSAGE: " + msg);
LOG("SERVER CLI MESSAGE: " + msg);
return;
},
reqmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.reqmessage(nmsg[i]);
}
return;
}
console.log("\x1b[34mSERVER REQUEST MESSAGE: " + msg + "\x1b[37m\x1b[0m");
LOG("SERVER REQUEST MESSAGE: " + msg);
return;
},
resmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.resmessage(nmsg[i]);
}
return;
}
console.log("\x1b[32mSERVER RESPONSE MESSAGE: " + msg + "\x1b[37m\x1b[0m");
LOG("SERVER RESPONSE MESSAGE: " + msg);
return;
},
errmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.errmessage(nmsg[i]);
}
return;
}
console.log("\x1b[31mSERVER RESPONSE ERROR MESSAGE: " + msg + "\x1b[37m\x1b[0m");
LOG("SERVER RESPONSE ERROR MESSAGE: " + msg);
return;
},
locerrmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.locerrmessage(nmsg[i]);
}
return;
}
console.log("\x1b[41mSERVER ERROR MESSAGE: " + msg + "\x1b[40m\x1b[0m");
LOG("SERVER ERROR MESSAGE: " + msg);
return;
},
locwarnmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.locwarnmessage(nmsg[i]);
}
return;
}
console.log("\x1b[43mSERVER WARNING MESSAGE: " + msg + "\x1b[40m\x1b[0m");
LOG("SERVER WARNING MESSAGE: " + msg);
return;
},
locmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.locmessage(nmsg[i]);
}
return;
}
console.log("SERVER MESSAGE: " + msg);
LOG("SERVER MESSAGE: " + msg);
return;
}
};
//Wrap around process.exit, so log contents can flush.
process.unsafeExit = process.exit;
process.exit = function (code) {
if (logFile && logFile.writable && !logFile.pending) {
try {
logFile.close(function () {
logFile = undefined;
logSync = true;
process.unsafeExit(code);
});
2023-07-29 20:44:13 +02:00
if (process.isBun) {
setInterval(function () {
if (!logFile.writable) {
logFile = undefined;
logSync = true;
process.unsafeExit(code);
}
}, 50); //Interval
}
2023-07-29 20:32:17 +02:00
setTimeout(function () {
logFile = undefined;
logSync = true;
process.unsafeExit(code);
}, 10000); //timeout
} catch (err) {
2023-07-29 20:32:17 +02:00
logFile = undefined;
logSync = true;
process.unsafeExit(code);
}
} else {
logSync = true;
process.unsafeExit(code);
}
};
2023-07-29 20:44:13 +02:00
// Load mods if the `disableMods` flag is not set
2023-07-29 20:32:17 +02:00
if (!disableMods) {
2023-07-29 20:44:13 +02:00
// Define the modloader folder name
2023-07-29 20:32:17 +02:00
var modloaderFolderName = "modloader";
if (cluster.isPrimary === false) {
2023-07-29 20:44:13 +02:00
// If not the master process, create a unique modloader folder name for each worker
2023-07-29 20:32:17 +02:00
modloaderFolderName = ".modloader_w" + Math.floor(Math.random() * 65536);
}
// Define the temporary server-side JavaScript file name
var tempServerSideScriptName = "serverSideScript.js";
if (!process.isBun && cluster.isPrimary === false) {
// If not the master process and it's not Bun, create a unique temporary server-side JavaScript file name for each worker
tempServerSideScriptName = ".serverSideScript_w" + Math.floor(Math.random() * 65536) + ".js";
}
2023-07-29 20:44:13 +02:00
// Iterate through the list of mod files
2023-07-29 20:32:17 +02:00
for (var i = 0; i < modFiles.length; i++) {
2023-07-29 20:44:13 +02:00
// Build the path to the current mod file
2023-07-29 20:32:17 +02:00
var modFile = __dirname + "/mods/" + modFiles[i];
2023-07-29 20:44:13 +02:00
2023-07-29 20:32:17 +02:00
try {
2023-07-29 20:44:13 +02:00
// Try creating the modloader folder (if not already exists)
2023-07-29 20:32:17 +02:00
try {
fs.mkdirSync(__dirname + "/temp/" + modloaderFolderName);
} catch (err) {
2023-07-29 20:44:13 +02:00
// If the folder already exists, continue to the next step
if (err.code != "EEXIST") {
2023-07-29 20:44:13 +02:00
// If there was another error, try creating the temp folder and then the modloader folder again
2023-07-29 20:32:17 +02:00
fs.mkdirSync(__dirname + "/temp");
try {
fs.mkdirSync(__dirname + "/temp/" + modloaderFolderName);
} catch (err) {
2023-07-29 20:44:13 +02:00
// If there was another error, throw it
if (err.code != "EEXIST") throw err;
2023-07-29 20:32:17 +02:00
}
}
}
2023-07-29 20:44:13 +02:00
// Create a subfolder for the current mod within the modloader folder
2023-07-29 20:32:17 +02:00
fs.mkdirSync(__dirname + "/temp/" + modloaderFolderName + "/" + modFiles[i]);
} catch (err) {
2023-07-29 20:44:13 +02:00
// If there was an error creating the folder, ignore it if it's a known error
if (err.code != "EEXIST" && err.code != "ENOENT") throw err;
2023-07-29 20:44:13 +02:00
// Some other SVR.JS process may have created the files.
2023-07-29 20:32:17 +02:00
}
2023-07-29 20:44:13 +02:00
// Check if the current mod file is a regular file
2023-07-29 20:32:17 +02:00
if (fs.statSync(modFile).isFile()) {
try {
2023-07-29 20:44:13 +02:00
// Determine if the mod file is a ".tar.gz" file or not
if (modFile.indexOf(".tar.gz") == modFile.length - 7) {
// If it's a ".tar.gz" file, extract its contents using `tar`
if (tar._errored) throw tar._errored;
tar.x({
file: modFile,
sync: true,
C: __dirname + "/temp/" + modloaderFolderName + "/" + modFiles[i]
});
} else {
// If it's not a ".tar.gz" file, unpack it using `svrmodpack`
if (svrmodpack._errored) throw svrmodpack._errored;
svrmodpack.unpack(modFile, __dirname + "/temp/" + modloaderFolderName + "/" + modFiles[i]);
2023-07-29 20:32:17 +02:00
}
2023-07-29 20:44:13 +02:00
// Initialize variables for mod loading
2023-07-29 20:32:17 +02:00
var Mod = undefined;
var mod = undefined;
2023-07-29 20:44:13 +02:00
// Attempt to require the mod's index.js file, retrying up to 3 times in case of syntax errors
2023-07-29 20:32:17 +02:00
for (var j = 0; j < 3; j++) {
try {
Mod = require("./temp/" + modloaderFolderName + "/" + modFiles[i] + "/index.js");
mod = new Mod();
break;
} catch (err) {
if (j >= 2 || err.name == "SyntaxError") throw err;
2023-07-29 20:44:13 +02:00
// Wait for a short time before retrying
2023-07-29 20:32:17 +02:00
var now = Date.now();
while (Date.now() - now < 2);
2023-07-29 20:44:13 +02:00
// Try reloading mod
2023-07-29 20:32:17 +02:00
}
}
2023-07-29 20:44:13 +02:00
// Add the loaded mod to the mods list
2023-07-29 20:32:17 +02:00
mods.push(mod);
2023-07-29 20:44:13 +02:00
// Attempt to read the mod's info file, retrying up to 3 times
2023-07-29 20:32:17 +02:00
for (var j = 0; j < 3; j++) {
try {
modInfos.push(JSON.parse(fs.readFileSync(__dirname + "/temp/" + modloaderFolderName + "/" + modFiles[i] + "/mod.info")));
break;
} catch (err) {
2023-07-29 20:32:17 +02:00
if (j >= 2) {
2023-07-29 20:44:13 +02:00
// If failed to read info file, add a placeholder entry to modInfos with an error message
2023-07-29 20:32:17 +02:00
modInfos.push({
name: "Unknown mod (" + modFiles[i] + ";" + err.message + ")",
2023-07-29 20:32:17 +02:00
version: "ERROR"
});
}
2023-07-29 20:44:13 +02:00
// Wait for a short time before retrying
var now = Date.now();
while (Date.now() - now < 2);
// Try reloading mod info
2023-07-29 20:32:17 +02:00
}
}
} catch (err) {
2023-07-29 20:44:13 +02:00
// If there was an error during mod loading, log it to the console
if (cluster.isPrimary || cluster.isPrimary === undefined) {
2023-07-29 20:32:17 +02:00
serverconsole.locwarnmessage("There was a problem while loading a \"" + modFiles[i] + "\" mod.");
serverconsole.locwarnmessage("Stack:");
serverconsole.locwarnmessage(generateErrorStack(err));
2023-07-29 20:32:17 +02:00
}
}
}
}
2023-07-29 20:44:13 +02:00
// Check if a custom server side script file exists
2023-07-29 20:32:17 +02:00
if (fs.existsSync("./serverSideScript.js") && fs.statSync("./serverSideScript.js").isFile()) {
try {
2023-07-29 20:44:13 +02:00
// Prepend necessary modules and variables to the custom server side script
2023-07-29 20:32:17 +02:00
var modhead = "var readline = require('readline');\r\nvar os = require('os');\r\nvar http = require('http');\r\nvar url = require('url');\r\nvar fs = require('fs');\r\nvar path = require('path');\r\n" + (hexstrbase64 === undefined ? "" : "var hexstrbase64 = require('../hexstrbase64/index.js');\r\n") + (crypto.__disabled__ === undefined ? "var crypto = require('crypto');\r\nvar https = require('https');\r\n" : "") + "var stream = require('stream');\r\nvar customvar1;\r\nvar customvar2;\r\nvar customvar3;\r\nvar customvar4;\r\n\r\nfunction Mod() {}\r\nMod.prototype.callback = function callback(req, res, serverconsole, responseEnd, href, ext, uobject, search, defaultpage, users, page404, head, foot, fd, elseCallback, configJSON, callServerError, getCustomHeaders, origHref, redirect, parsePostData) {\r\nreturn function () {\r\nvar disableEndElseCallbackExecute = false;\r\nfunction filterHeaders(headers){for(var jsn=JSON.stringify(headers,null,2).split('\\n'),njsn=[\"{\"],i=1;i<jsn.length-1;i++)0!==jsn[i].replace(/ /g,\"\").indexOf('\":')&&(eval(\"var value = \"+(\",\"==jsn[i][jsn[i].length-1]?jsn[i].substring(0,jsn[i].length-1):jsn[i]).split('\": ')[1]),\",\"==jsn[i][jsn[i].length-1]&&i==jsn.length-2?njsn.push(jsn[i].substring(0,jsn[i].length-1)):null!=value&&njsn.push(jsn[i]));return njsn.push(\"}\"),JSON.parse(njsn.join(os.EOL))}\r\n";
var modfoot = "\r\nif(!disableEndElseCallbackExecute) {\r\ntry{\r\nelseCallback();\r\n} catch(err) {\r\n}\r\n}\r\n}\r\n}\r\nmodule.exports = Mod;";
2023-07-29 20:44:13 +02:00
// Write the modified server side script to the temp folder
fs.writeFileSync(__dirname + "/temp/" + tempServerSideScriptName, modhead + fs.readFileSync("./serverSideScript.js") + modfoot);
2023-07-29 20:44:13 +02:00
// Initialize variables for server side script loading
2023-07-29 20:32:17 +02:00
var aMod = undefined;
var amod = undefined;
2023-07-29 20:44:13 +02:00
// Attempt to require the custom server side script, retrying up to 5 times
for (var i = 0; i < 5; i++) {
2023-07-29 20:32:17 +02:00
try {
aMod = require("./temp/" + tempServerSideScriptName);
2023-07-29 20:32:17 +02:00
amod = new aMod();
break;
} catch (err) {
if (i >= 4 || err.name == "SyntaxError") throw err;
2023-07-29 20:44:13 +02:00
// Wait for a short time before retrying
2023-07-29 20:32:17 +02:00
var now = Date.now();
while (Date.now() - now < 2);
2023-07-29 20:44:13 +02:00
// Try reloading mod
2023-07-29 20:32:17 +02:00
}
}
2023-07-29 20:44:13 +02:00
// Add the loaded server side script to the mods list
2023-07-29 20:32:17 +02:00
mods.push(amod);
} catch (err) {
2023-07-29 20:44:13 +02:00
// If there was an error during server side script loading, log it to the console
if (cluster.isPrimary || cluster.isPrimary === undefined) {
2023-07-29 20:32:17 +02:00
serverconsole.locwarnmessage("There was a problem while loading server side JavaScript.");
serverconsole.locwarnmessage("Stack:");
serverconsole.locwarnmessage(generateErrorStack(err));
2023-07-29 20:32:17 +02:00
}
}
}
}
2023-07-29 20:44:13 +02:00
2023-07-29 20:32:17 +02:00
function sha256(s) {
if (crypto.__disabled__ === undefined) {
var hash = crypto.createHash("SHA256");
hash.update(s);
return hash.digest("hex");
} else {
var chrsz = 8;
var hexcase = 0;
function safeAdd(x, y) {
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
}
function S(X, n) {
return (X >>> n) | (X << (32 - n));
}
function R(X, n) {
return (X >>> n);
}
function Ch(x, y, z) {
return ((x & y) ^ ((~x) & z));
}
function Maj(x, y, z) {
return ((x & y) ^ (x & z) ^ (y & z));
}
function Sigma0256(x) {
return (S(x, 2) ^ S(x, 13) ^ S(x, 22));
}
function Sigma1256(x) {
return (S(x, 6) ^ S(x, 11) ^ S(x, 25));
}
function Gamma0256(x) {
return (S(x, 7) ^ S(x, 18) ^ R(x, 3));
}
function Gamma1256(x) {
return (S(x, 17) ^ S(x, 19) ^ R(x, 10));
}
function coreSha256(m, l) {
var K = new Array(0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 0xE49B69C1, 0xEFBE4786, 0xFC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x6CA6351, 0x14292967, 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2);
var HASH = new Array(0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19);
var W = new Array(64);
var a, b, c, d, e, f, g, h, i, j;
var T1, T2;
m[l >> 5] |= 0x80 << (24 - l % 32);
m[((l + 64 >> 9) << 4) + 15] = l;
for (var i = 0; i < m.length; i += 16) {
a = HASH[0];
b = HASH[1];
c = HASH[2];
d = HASH[3];
e = HASH[4];
f = HASH[5];
g = HASH[6];
h = HASH[7];
for (var j = 0; j < 64; j++) {
if (j < 16) W[j] = m[j + i];
else W[j] = safeAdd(safeAdd(safeAdd(Gamma1256(W[j - 2]), W[j - 7]), Gamma0256(W[j - 15])), W[j - 16]);
T1 = safeAdd(safeAdd(safeAdd(safeAdd(h, Sigma1256(e)), Ch(e, f, g)), K[j]), W[j]);
T2 = safeAdd(Sigma0256(a), Maj(a, b, c));
h = g;
g = f;
f = e;
e = safeAdd(d, T1);
d = c;
c = b;
b = a;
a = safeAdd(T1, T2);
}
HASH[0] = safeAdd(a, HASH[0]);
HASH[1] = safeAdd(b, HASH[1]);
HASH[2] = safeAdd(c, HASH[2]);
HASH[3] = safeAdd(d, HASH[3]);
HASH[4] = safeAdd(e, HASH[4]);
HASH[5] = safeAdd(f, HASH[5]);
HASH[6] = safeAdd(g, HASH[6]);
HASH[7] = safeAdd(h, HASH[7]);
}
return HASH;
}
function str2binb(str) {
var bin = Array();
var mask = (1 << chrsz) - 1;
for (var i = 0; i < str.length * chrsz; i += chrsz) {
bin[i >> 5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i % 32);
}
return bin;
}
function Utf8Encode(string) {
string = string.replace(/\r\n/g, "\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if ((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
function binb2hex(binarray) {
var hexTab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
var str = "";
for (var i = 0; i < binarray.length * 4; i++) {
str += hexTab.charAt((binarray[i >> 2] >> ((3 - i % 4) * 8 + 4)) & 0xF) +
hexTab.charAt((binarray[i >> 2] >> ((3 - i % 4) * 8)) & 0xF);
}
return str;
}
s = Utf8Encode(s);
return binb2hex(coreSha256(str2binb(s), s.length * chrsz));
}
}
function getInitializePath(to) {
var cwd = process.cwd();
if (os.platform() == "win32") {
to = to.replace(/\//g, "\\");
if (to[0] == "\\") to = cwd.split("\\")[0] + to;
}
var absoluteTo = path.isAbsolute(to) ? to : (__dirname + (os.platform() == "win32" ? "\\" : "/") + to);
if (os.platform() == "win32" && cwd[0] != absoluteTo[0]) return "";
var relative = path.relative(cwd, absoluteTo);
if (os.platform() == "win32") {
return "/" + relative.replace(/\\/g, "/");
} else {
return "/" + relative;
}
}
2023-07-29 20:44:13 +02:00
function isForbiddenPath(decodedHref, match) {
var forbiddenPath = forbiddenPaths[match];
if (!forbiddenPath) return false;
if (typeof forbiddenPath === "string") {
return decodedHref === forbiddenPath || (os.platform() === "win32" && decodedHref.toLowerCase() === forbiddenPath.toLowerCase());
}
if (typeof forbiddenPath === "object") {
for (var i = 0; i < forbiddenPath.length; i++) {
if (decodedHref === forbiddenPath[i] || (os.platform() === "win32" && decodedHref.toLowerCase() === forbiddenPath[i].toLowerCase())) {
return true;
}
2023-07-29 20:32:17 +02:00
}
}
return false;
}
2023-07-29 20:44:13 +02:00
function isIndexOfForbiddenPath(decodedHref, match) {
var forbiddenPath = forbiddenPaths[match];
if (!forbiddenPath) return false;
if (typeof forbiddenPath === "string") {
return decodedHref === forbiddenPath || decodedHref.indexOf(forbiddenPath + "/") === 0 || (os.platform() === "win32" && (decodedHref.toLowerCase() === forbiddenPath.toLowerCase() || decodedHref.toLowerCase().indexOf(forbiddenPath.toLowerCase() + "/") === 0));
2023-07-29 20:44:13 +02:00
}
if (typeof forbiddenPath === "object") {
for (var i = 0; i < forbiddenPath.length; i++) {
if (decodedHref === forbiddenPath[i] || decodedHref.indexOf(forbiddenPath[i] + "/") === 0 || (os.platform() === "win32" && (decodedHref.toLowerCase() === forbiddenPath[i].toLowerCase() || decodedHref.toLowerCase().indexOf(forbiddenPath[i].toLowerCase() + "/") === 0))) {
2023-07-29 20:44:13 +02:00
return true;
}
2023-07-29 20:32:17 +02:00
}
}
return false;
}
2023-07-29 20:44:13 +02:00
2023-07-29 20:32:17 +02:00
var forbiddenPaths = {};
forbiddenPaths.config = getInitializePath("./config.json");
forbiddenPaths.certificates = [];
if (secure) {
forbiddenPaths.certificates.push(getInitializePath(configJSON.cert));
forbiddenPaths.certificates.push(getInitializePath(configJSON.key));
for (var i = 0; i < Object.keys(sni).length; i++) {
forbiddenPaths.certificates.push(getInitializePath(sni[Object.keys(sni)[i]].cert));
forbiddenPaths.certificates.push(getInitializePath(sni[Object.keys(sni)[i]].key));
}
}
forbiddenPaths.svrjs = getInitializePath("./" + ((__dirname[__dirname.length - 1] != "/") ? __filename.replace(__dirname + "/", "") : __filename.replace(__dirname, "")));
forbiddenPaths.serverSideScripts = [];
forbiddenPaths.serverSideScripts.push("/serverSideScript.js");
forbiddenPaths.serverSideScripts.push(getInitializePath("./temp/serverSideScript.js"));
forbiddenPaths.serverSideScriptDirectories = [];
forbiddenPaths.serverSideScriptDirectories.push(getInitializePath("./temp/modloader"));
forbiddenPaths.serverSideScriptDirectories.push(getInitializePath("./node_modules"));
forbiddenPaths.serverSideScriptDirectories.push(getInitializePath("./mods"));
forbiddenPaths.log = getInitializePath("./log");
//Create server
if (!cluster.isPrimary) {
2023-07-29 20:32:17 +02:00
var reqcounter = 0;
2023-08-09 21:09:23 +02:00
var reqcounterKillReq = 0;
2023-07-29 20:32:17 +02:00
var server = {};
var server2 = {};
try {
server2 = http.createServer({
requireHostHeader: false
});
} catch (err) {
2023-07-29 20:32:17 +02:00
server2 = http.createServer();
}
if (!disableToHTTPSRedirect) {
server2.on("request", redirhandler);
server2.on("connect", function (request, socket) {
var reqIdInt = Math.round(Math.random() * 16777216);
var reqId = "0".repeat(6 - reqIdInt.toString(16).length) + reqIdInt.toString(16);
var serverconsole = {
climessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.climessage(nmsg[i]);
}
return;
}
console.log("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg);
LOG("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
reqmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.reqmessage(nmsg[i]);
}
return;
}
console.log("\x1b[34mSERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
LOG("SERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
resmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.resmessage(nmsg[i]);
}
return;
}
console.log("\x1b[32mSERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
LOG("SERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
errmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.errmessage(nmsg[i]);
}
return;
}
console.log("\x1b[31mSERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
LOG("SERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
locerrmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.locerrmessage(nmsg[i]);
}
return;
}
console.log("\x1b[41mSERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m");
LOG("SERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
locwarnmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.locwarnmessage(nmsg[i]);
}
return;
}
console.log("\x1b[43mSERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m");
LOG("SERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
locmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.locmessage(nmsg[i]);
}
return;
}
console.log("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg);
LOG("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
}
};
socket.on("close", function (hasError) {
if (!hasError) serverconsole.locmessage("Client disconnected.");
else serverconsole.locmessage("Client disconnected due to error.");
});
socket.on("error", function () {});
var reqip = socket.remoteAddress;
var reqport = socket.remotePort;
2023-07-29 20:44:13 +02:00
serverconsole.locmessage("Somebody connected to " + (typeof port == "number" ? "port " : "socket ") + port + "...");
2023-08-09 21:09:23 +02:00
reqcounter++;
2023-07-29 20:32:17 +02:00
serverconsole.reqmessage("Client " + ((!reqip || reqip == "") ? "[unknown client]" : (reqip + ((reqport && reqport !== 0) && reqport != "" ? ":" + reqport : ""))) + " wants to proxy " + request.url + " through this server");
if (request.headers["user-agent"] != undefined) serverconsole.reqmessage("Client uses " + request.headers["user-agent"]);
serverconsole.errmessage("This server will never be a proxy.");
if (!socket.destroyed) socket.end("HTTP/1.1 501 Not Implemented\n\n");
});
server2.on("clientError", function (err, socket) {
reqerrhandler(err, socket, false);
});
server2.on("checkExpectation", redirhandler);
} else {
server2.on("request", function (req, res) {
reqhandler(req, res, false);
});
server2.on("checkExpectation", reqhandler);
server2.on("connect", connhandler);
server2.on("clientError", function (err, socket) {
reqerrhandler(err, socket, false);
});
}
server2.on("error", function (err) {
if (err.code == "EADDRINUSE" || err.code == "EADDRNOTAVAIL" || err.code == "EACCES") {
attmtsRedir--;
if (cluster.isPrimary === undefined) {
2023-07-29 20:32:17 +02:00
if (err.code == "EADDRINUSE") {
2023-07-29 20:44:13 +02:00
serverconsole.locerrmessage("Address is already in use by another process.");
2023-07-29 20:32:17 +02:00
} else if (err.code == "EADDRNOTAVAIL") {
2023-07-29 20:44:13 +02:00
serverconsole.locerrmessage("Address is not available on this machine.");
2023-07-29 20:32:17 +02:00
} else if (err.code == "EACCES") {
2023-07-29 20:44:13 +02:00
serverconsole.locerrmessage("Permission denied. You may not have sufficient privileges to access the requested address.");
} else if (err.code == "EAFNOSUPPORT") {
serverconsole.locerrmessage("Address family not supported. The address family (IPv4 or IPv6) of the requested address is not supported.");
} else if (err.code == "EALREADY") {
serverconsole.locerrmessage("Operation already in progress. The server is already in the process of establishing a connection on the requested address.");
} else if (err.code == "ECONNABORTED") {
serverconsole.locerrmessage("Connection aborted. The connection to the server was terminated abruptly.");
} else if (err.code == "ECONNREFUSED") {
serverconsole.locerrmessage("Connection refused. The server refused the connection attempt.");
} else if (err.code == "ECONNRESET") {
serverconsole.locerrmessage("Connection reset by peer. The connection to the server was reset by the remote host.");
} else if (err.code == "EDESTADDRREQ") {
serverconsole.locerrmessage("Destination address required. The destination address must be specified.");
} else if (err.code == "ENETDOWN") {
serverconsole.locerrmessage("Network is down. The network interface used for the connection is not available.");
} else if (err.code == "ENETUNREACH") {
serverconsole.locerrmessage("Network is unreachable. The network destination is not reachable from this host.");
} else if (err.code == "ENOBUFS") {
serverconsole.locerrmessage("No buffer space available. Insufficient buffer space is available for the server to process the request.");
} else if (err.code == "ENOTSOCK") {
serverconsole.locerrmessage("Not a socket. The file descriptor provided is not a valid socket.");
} else if (err.code == "EPROTO") {
serverconsole.locerrmessage("Protocol error. An unspecified protocol error occurred.");
} else if (err.code == "EPROTONOSUPPORT") {
serverconsole.locerrmessage("Protocol not supported. The requested network protocol is not supported.");
} else if (err.code == "ETIMEDOUT") {
serverconsole.locerrmessage("Connection timed out. The server did not respond within the specified timeout period.");
} else {
serverconsole.locerrmessage("There was an unknown error with the server.");
2023-07-29 20:32:17 +02:00
}
serverconsole.locmessage(attmtsRedir + " attempts left.");
} else {
process.send("\x12ERRLIST" + attmtsRedir + err.code);
}
if (attmtsRedir > 0) {
server2.close();
setTimeout(start, 900);
} else {
if (cluster.isPrimary !== undefined) process.send("\x12" + err.code);
2023-07-29 20:32:17 +02:00
process.exit(errors[err.code]);
}
} else {
serverconsole.locerrmessage("There was a problem starting SVR.JS!!!");
serverconsole.locerrmessage("Stack:");
serverconsole.locerrmessage(generateErrorStack(err));
if (cluster.isPrimary !== undefined) process.send("\x12CRASH");
2023-07-29 20:32:17 +02:00
process.exit(err.code ? errors[err.code] : 1);
}
});
server2.on("listening", function () {
2023-07-29 20:32:17 +02:00
listeningMessage();
});
if (configJSON.enableHTTP2 == true) {
if (secure) {
server = http2.createSecureServer({
allowHTTP1: true,
requireHostHeader: false,
key: key,
cert: cert
});
} else {
server = http2.createServer({
allowHTTP1: true,
requireHostHeader: false
});
}
} else {
if (secure) {
server = https.createServer({
key: key,
cert: cert,
requireHostHeader: false
});
} else {
try {
server = http.createServer({
requireHostHeader: false
});
} catch (err) {
2023-07-29 20:32:17 +02:00
server = http.createServer();
}
}
}
if (secure) {
for (var i = 0; i < sniCredentials.length; i++) {
server.addContext(sniCredentials[i].name, {
cert: sniCredentials[i].cert,
key: sniCredentials[i].key
});
}
}
server.on("request", reqhandler);
server.on("checkExpectation", reqhandler);
server.on("connect", connhandler);
server.on("clientError", reqerrhandler);
if (secure) {
server.prependListener("connection", function (sock) {
sock.reallyDestroy = sock.destroy;
sock.destroy = function () {
sock.toDestroy = true;
};
});
server.prependListener("tlsClientError", function (err, sock) {
if (err.code == "ERR_SSL_HTTP_REQUEST" || err.message.indexOf("http request") != -1) {
sock._parent.destroy = sock._parent.reallyDestroy;
sock._readableState = sock._parent._readableState;
sock._writableState = sock._parent._writableState;
sock._parent.toDestroy = false;
sock.pipe = function (a, b, c) {
sock._parent.pipe(a, b, c);
};
sock.write = function (a, b, c) {
sock._parent.write(a, b, c);
};
sock.end = function (a, b, c) {
sock._parent.end(a, b, c);
};
sock.destroyed = sock._parent.destroyed;
sock.readable = sock._parent.readable;
sock.writable = sock._parent.writable;
sock.remoteAddress = sock._parent.remoteAddress;
sock.remotePort = sock._parent.remoteAddress;
sock.destroy = function (a, b, c) {
try {
sock._parent.destroy(a, b, c);
sock.destroyed = sock._parent.destroyed;
} catch (err) {
2023-07-29 20:32:17 +02:00
//Socket is probably already destroyed.
}
};
} else {
sock._parent.destroy = sock._parent.reallyDestroy;
try {
if (sock._parent.toDestroy) sock._parent.destroy();
} catch (err) {
2023-07-29 20:32:17 +02:00
//Socket is probably already destroyed.
}
}
});
server.prependListener("secureConnection", function (sock) {
sock._parent.destroy = sock._parent.reallyDestroy;
delete sock._parent.reallyDestroy;
});
if (configJSON.enableOCSPStapling && !ocsp._errored) {
server.on("OCSPRequest", function (cert, issuer, callback) {
ocsp.getOCSPURI(cert, function (err, uri) {
if (err) return callback(err);
var req = ocsp.request.generate(cert, issuer);
var options = {
url: uri,
ocsp: req.data
};
ocspCache.request(req.id, options, callback);
});
});
}
}
//Patches from Node.JS v18.0.0
if (server.requestTimeout !== undefined && server.requestTimeout === 0) server.requestTimeout = 300000;
if (server2.requestTimeout !== undefined && server2.requestTimeout === 0) server2.requestTimeout = 300000;
function redirhandler(req, res) {
if (req.headers["force-insecure"] == "true" || req.headers["x-force-insecure"] == "true" || req.headers["x-svr-js-force-insecure"] == "true") {
reqhandler(req, res, false);
} else {
var reqIdInt = Math.round(Math.random() * 16777216);
var reqId = "0".repeat(6 - reqIdInt.toString(16).length) + reqIdInt.toString(16);
var serverconsole = {
climessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.climessage(nmsg[i]);
}
return;
}
console.log("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg);
LOG("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
reqmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.reqmessage(nmsg[i]);
}
return;
}
console.log("\x1b[34mSERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
LOG("SERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
resmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.resmessage(nmsg[i]);
}
return;
}
console.log("\x1b[32mSERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
LOG("SERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
errmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.errmessage(nmsg[i]);
}
return;
}
console.log("\x1b[31mSERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
LOG("SERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
locerrmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.locerrmessage(nmsg[i]);
}
return;
}
console.log("\x1b[41mSERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m");
LOG("SERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
locwarnmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.locwarnmessage(nmsg[i]);
}
return;
}
console.log("\x1b[43mSERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m");
LOG("SERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
locmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.locmessage(nmsg[i]);
}
return;
}
console.log("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg);
LOG("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
}
};
function getCustomHeaders() {
var ph = JSON.parse(JSON.stringify(customHeaders));
var phk = Object.keys(ph);
for (var i = 0; i < phk.length; i++) {
if (typeof ph[phk[i]] == "string") ph[phk[i]] = ph[phk[i]].replace(/\{path\}/g, req.url);
}
return ph;
}
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")) {
2023-08-09 21:09:23 +02:00
var headers = getCustomHeaders();
res.writeHead(204, "No Content", headers);
2023-07-29 20:32:17 +02:00
res.end();
return;
}
try {
req.url = encodeURI(Buffer.from(req.url, "latin1").toString("utf8")).replace(/%25/gi, "%");
} catch (err) {
2023-07-29 20:32:17 +02:00
//URL not converted...
}
res.writeHeadNative = res.writeHead;
res.writeHead = function (a, b, c) {
if (parseInt(a) >= 400 && parseInt(a) <= 599) {
serverconsole.errmessage("Server responded with " + a.toString() + " code.");
} else {
serverconsole.resmessage("Server responded with " + a.toString() + " code.");
}
res.writeHeadNative(a, b, c);
};
var finished = false;
res.on("finish", function () {
if (!finished) {
finished = true;
serverconsole.locmessage("Client disconnected.");
}
});
res.on("close", function () {
if (!finished) {
finished = true;
serverconsole.locmessage("Client disconnected.");
}
});
2023-07-29 20:44:13 +02:00
serverconsole.locmessage("Somebody connected to " + (typeof port == "number" ? "port " : "socket ") + port + "...");
2023-07-29 20:32:17 +02:00
if (req.socket == null) {
serverconsole.errmessage("Client socket is null!!!");
return;
}
var isProxy = false;
if (req.url.indexOf("/") != 0 && req.url != "*") isProxy = true;
var head = fs.existsSync("./.head") ? fs.readFileSync("./.head").toString() : (fs.existsSync("./head.html") ? fs.readFileSync("./head.html").toString() : ""); // header
var foot = fs.existsSync("./.foot") ? fs.readFileSync("./.foot").toString() : (fs.existsSync("./foot.html") ? fs.readFileSync("./foot.html").toString() : ""); // footer
var fd = "";
function responseEnd(d) {
if (d === undefined) d = fd;
res.write(head + d + foot);
res.end();
}
//Error descriptions
var serverErrorDescs = {
400: "The request you made is invalid.",
417: "Expectation in Expect property couldn't be satisfied.",
500: "The server had an unexpected error. Below, the error stack is shown: </p><code>{stack}</code><p>Please contact with developer/administrator at <i>{contact}</i>.",
501: "The request requires use of a function, which isn't implemented (yet) by the server."
};
//Server error calling method
function callServerError(errorCode, extName, stack, ch) {
var errorFile = errorCode.toString() + ".html";
var errorFile2 = "." + errorCode.toString();
if (fs.existsSync(errorFile2)) errorFile = errorFile2;
if (errorCode == 404 && fs.existsSync(page404)) errorFile = page404;
if (Object.prototype.toString.call(stack) === "[object Error]") stack = generateErrorStack(stack);
if (stack === undefined) stack = generateErrorStack(new Error("Unknown error"));
if (errorCode == 500 || errorCode == 502) {
serverconsole.errmessage("There was an error while processing the request!");
serverconsole.errmessage("Stack:");
serverconsole.errmessage(stack);
}
if (stackHidden) stack = "[error stack hidden]";
if (serverErrorDescs[errorCode] === undefined) {
callServerError(501, extName, stack);
} else {
var cheaders = getCustomHeaders();
if (ch) {
var chon = Object.keys(cheaders);
var chn = Object.keys(ch);
for (var i = 0; i < chn.length; i++) {
var nhn = chn[i];
for (var j = 0; j < chon.length; j++) {
if (chon[j].toLowerCase() == chn[i].toLowerCase()) {
nhn = chon[j];
break;
}
}
if (ch[chn[i]]) cheaders[nhn] = ch[chn[i]];
}
}
cheaders["Content-Type"] = "text/html; charset=utf-8";
if (errorCode == 405 && !cheaders["Allow"]) cheaders["Allow"] = "GET, POST, HEAD, OPTIONS";
fs.readFile(errorFile, function (err, data) {
try {
if (err) throw err;
res.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders);
fd += data.toString().replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode]).replace(/{errorDesc}/g, serverErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, "&nbsp;&nbsp;")).replace(/{path}/g, req.url.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")).replace(/{server}/g, "" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (extName == undefined ? "" : " " + extName) + ((req.headers.host == undefined || isProxy) ? "" : " on " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"))).replace(/{contact}/g, serverAdmin.replace(/\./g, "[dot]").replace(/@/g, "[at]"));
responseEnd();
} catch (err) {
2023-07-29 20:32:17 +02:00
var additionalError = 500;
if (err.code == "ENOENT") {
2023-07-29 20:32:17 +02:00
additionalError = 404;
} else if (err.code == "ENOTDIR") {
additionalError = 404; // Assume that file doesn't exist
} else if (err.code == "EACCES") {
2023-07-29 20:32:17 +02:00
additionalError = 403;
} else if (err.code == "EMFILE") {
2023-07-29 20:32:17 +02:00
additionalError = 429;
} else if (err.code == "ELOOP") {
2023-07-29 20:32:17 +02:00
additionalError = 508;
}
res.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders);
res.write(("<html><head><title>{errorMessage}</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body><h1>{errorMessage}</h1><p>{errorDesc}</p>" + ((additionalError == 404) ? "" : "<p>Additionally, a {additionalError} error occurred while loading an error page.</p>") + "<p><i>{server}</i></p></body></html>").replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode]).replace(/{errorDesc}/g, serverErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, "&nbsp;&nbsp;")).replace(/{path}/g, req.url.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")).replace(/{server}/g, "" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (extName == undefined ? "" : " " + extName) + ((req.headers.host == undefined || isProxy) ? "" : " on " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"))).replace(/{contact}/g, serverAdmin.replace(/\./g, "[dot]").replace(/@/g, "[at]")).replace(/{additionalError}/g, additionalError.toString()));
res.end();
}
});
}
}
var reqport = "";
var reqip = "";
var oldport = "";
var oldip = "";
if (req.headers["x-svr-js-client"] != undefined && enableIPSpoofing) {
var kl = req.headers["x-svr-js-client"].split(":");
reqport = kl.pop();
reqip = kl.join(":");
try {
oldport = req.socket.remotePort;
oldip = req.socket.remoteAddress;
req.socket.realRemotePort = reqport;
req.socket.realRemoteAddress = reqip;
req.socket.originalRemotePort = oldport;
req.socket.originalRemoteAddress = oldip;
res.socket.realRemotePort = reqport;
res.socket.realRemoteAddress = reqip;
res.socket.originalRemotePort = oldport;
res.socket.originalRemoteAddress = oldip;
} catch (err) {
2023-07-29 20:32:17 +02:00
//Nevermind...
}
} else if (req.headers["x-forwarded-for"] != undefined && enableIPSpoofing) {
reqport = null;
reqip = req.headers["x-forwarded-for"].split(",")[0].replace(/ /g, "");
if (reqip.indexOf(":") == -1) reqip = "::ffff:" + reqip;
try {
oldport = req.socket.remotePort;
oldip = req.socket.remoteAddress;
req.socket.realRemotePort = reqport;
req.socket.realRemoteAddress = reqip;
req.socket.originalRemotePort = oldport;
req.socket.originalRemoteAddress = oldip;
res.socket.realRemotePort = reqport;
res.socket.realRemoteAddress = reqip;
res.socket.originalRemotePort = oldport;
res.socket.originalRemoteAddress = oldip;
} catch (err) {
2023-07-29 20:32:17 +02:00
//Nevermind...
}
} else {
reqip = req.socket.remoteAddress;
reqport = req.socket.remotePort;
}
2023-08-09 21:09:23 +02:00
reqcounter++;
2023-07-29 20:32:17 +02:00
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);
else 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.url);
if (req.headers["user-agent"] != undefined) serverconsole.reqmessage("Client uses " + req.headers["user-agent"]);
try {
if (req.headers["expect"] && req.headers["expect"] != "100-continue") {
serverconsole.errmessage("Expectation not satified!");
callServerError(417);
return;
}
var hostx = req.headers.host;
if (hostx === undefined) {
serverconsole.errmessage("Bad request!");
callServerError(400);
return;
}
if (req.method == "CONNECT") {
callServerError(501);
serverconsole.errmessage("CONNECT requests aren't supported. Your JS runtime probably doesn't support 'connect' handler for HTTP library.");
return;
}
if (isProxy) {
callServerError(501);
serverconsole.errmessage("This server will never be a proxy.");
return;
}
2023-07-29 20:44:13 +02:00
function parseURL(uri) {
2023-07-29 20:32:17 +02:00
if (typeof URL != "undefined" && url.Url) {
try {
var uobject = new URL(uri, "http" + (req.socket.encrypted ? "s" : "") + "://" + (req.headers.host ? req.headers.host : (domain ? domain : "unknown.invalid")));
var nuobject = new url.Url();
if (uri.indexOf("/") != -1) nuobject.slashes = true;
if (uobject.protocol != "") nuobject.protocol = uobject.protocol;
if (uobject.username != "" && uobject.password != "") nuobject.auth = uobject.username + ":" + uobject.password;
if (uobject.host != "") nuobject.host = uobject.host;
if (uobject.hostname != "") nuobject.hostname = uobject.hostname;
if (uobject.port != "") nuobject.port = uobject.port;
if (uobject.pathname != "") nuobject.pathname = uobject.pathname;
if (uobject.search != "") nuobject.search = uobject.search;
if (uobject.hash != "") nuobject.hash = uobject.hash;
if (uobject.href != "") nuobject.href = uobject.href;
if (uri.indexOf("/") != 0) {
if (nuobject.pathname) {
nuobject.pathname = nuobject.pathname.substr(1);
nuobject.href = nuobject.pathname + (nuobject.search ? nuobject.search : "");
}
}
if (nuobject.pathname) {
nuobject.path = nuobject.pathname + (nuobject.search ? nuobject.search : "");
}
//if(nuobject.path != "" && uobject.password != "") nuobject.path = nuobject.pathname + nuobject.href;
nuobject.query = {};
uobject.searchParams.forEach(function (value, key) {
nuobject.query[key] = value;
});
return nuobject;
} catch (err) {
2023-07-29 20:32:17 +02:00
return url.parse(uri, true);
}
} else {
return url.parse(uri, true);
}
}
2023-07-29 20:44:13 +02:00
var urlp = parseURL("http://" + hostx);
2023-07-29 20:32:17 +02:00
try {
if (urlp.path.indexOf("//") == 0) {
2023-07-29 20:44:13 +02:00
urlp = parseURL("http:" + url.path);
2023-07-29 20:32:17 +02:00
}
} catch (err) {
2023-07-29 20:32:17 +02:00
//URL parse error...
}
if (urlp.host == "localhost" || urlp.host == "localhost:" + port.toString() || urlp.host == "127.0.0.1" || urlp.host == "127.0.0.1:" + port.toString() || urlp.host == "::1" || urlp.host == "::1:" + port.toString()) {
urlp.protocol = "https:";
if (sport == 443) {
urlp.host = urlp.hostname;
} else {
urlp.host = urlp.hostname + ":" + sport.toString();
urlp.port = sport.toString();
}
} else if (urlp.host == host || urlp.host == host + ":" + port.toString()) {
urlp.protocol = "https:";
if (sport == 443) {
urlp.host = urlp.hostname;
} else {
urlp.host = urlp.hostname + ":" + sport.toString();
urlp.port = sport.toString();
}
} else if (urlp.host == pubip || urlp.host == pubip + ":" + pubport.toString()) {
urlp.protocol = "https:";
if (spubport == 443) {
urlp.host = urlp.hostname;
} else {
urlp.host = urlp.hostname + ":" + spubport.toString();
urlp.port = spubport.toString();
}
} else if (urlp.hostname == domain || urlp.hostname.indexOf(domain) != -1) {
urlp.protocol = "https:";
if (spubport == 443) {
urlp.host = urlp.hostname;
} else {
urlp.host = urlp.hostname + ":" + spubport.toString();
urlp.port = spubport.toString();
}
} else {
urlp.protocol = "https:";
}
urlp.path = null;
urlp.pathname = null;
var lloc = url.format(urlp);
var requestURL = req.url;
try {
if (requestURL.split("/")[1].indexOf(".onion") != -1) {
requestURL = requestURL.split("/");
requestURL.shift();
requestURL.shift();
requestURL.unshift("");
requestURL = requestURL.join("/");
}
} catch (err) {
2023-07-29 20:32:17 +02:00
//Leave URL as it is...
}
var rheaders = getCustomHeaders();
rheaders["Location"] = lloc + requestURL;
res.writeHead(301, "Redirect to HTTPS", rheaders);
res.end();
} catch (err) {
2023-07-29 20:32:17 +02:00
serverconsole.errmessage("There was an error while processing the request!");
serverconsole.errmessage("Stack:");
serverconsole.errmessage(generateErrorStack(err));
callServerError(500, undefined, generateErrorStack(err));
2023-07-29 20:32:17 +02:00
}
}
}
function reqerrhandler(err, socket, fromMain) {
if (fromMain === undefined) fromMain = true;
//Define response object similar to Node.JS native one
var res = {};
res.socket = socket;
res.write = function (x) {
if (err.code === "ECONNRESET" || !socket.writable) {
return;
}
socket.write(x);
};
res.end = function (x) {
if (err.code === "ECONNRESET" || !socket.writable) {
return;
}
socket.end(x, function () {
try {
socket.destroy();
} catch (err) {
2023-07-29 20:32:17 +02:00
//Socket is probably already destroyed
}
});
};
res.writeHead = function (code, name, headers) {
var head = ("HTTP/1.1 " + code.toString() + " " + name + "\r\n");
var headers = JSON.parse(JSON.stringify(headers));
headers["Date"] = (new Date()).toGMTString();
headers["Connection"] = "close";
var headernames = Object.keys(headers);
for (var i = 0; i < headernames.length; i++) {
if (headernames[i].toLowerCase() == "set-cookie") {
for (var j = 0; j < headers[headernames[i]]; j++) {
if (headernames[i].match(/[^\x09\x20-\x7e\x80-\xff]|.:/) || headers[headernames[i]][j].match(/[^\x09\x20-\x7e\x80-\xff]/)) throw new Error("Invalid header!!! (" + headernames[i] + ")");
head += (headernames[i] + ": " + headers[headernames[i]][j]);
}
} else {
if (headernames[i].match(/[^\x09\x20-\x7e\x80-\xff]|.:/) || headers[headernames[i]].match(/[^\x09\x20-\x7e\x80-\xff]/)) throw new Error("Invalid header!!! (" + headernames[i] + ")");
head += (headernames[i] + ": " + headers[headernames[i]]);
}
head += "\r\n";
}
head += ("\r\n");
res.write(head);
};
var reqIdInt = Math.round(Math.random() * 16777216);
var reqId = "0".repeat(6 - reqIdInt.toString(16).length) + reqIdInt.toString(16);
var serverconsole = {
climessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.climessage(nmsg[i]);
}
return;
}
console.log("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg);
LOG("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
reqmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.reqmessage(nmsg[i]);
}
return;
}
console.log("\x1b[34mSERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
LOG("SERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
resmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.resmessage(nmsg[i]);
}
return;
}
console.log("\x1b[32mSERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
LOG("SERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
errmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.errmessage(nmsg[i]);
}
return;
}
console.log("\x1b[31mSERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
LOG("SERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
locerrmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.locerrmessage(nmsg[i]);
}
return;
}
console.log("\x1b[41mSERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m");
LOG("SERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
locwarnmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.locwarnmessage(nmsg[i]);
}
return;
}
console.log("\x1b[43mSERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m");
LOG("SERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
locmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.locmessage(nmsg[i]);
}
return;
}
console.log("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg);
LOG("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
}
};
socket.on("close", function (hasError) {
if (!hasError || err.code == "ERR_SSL_HTTP_REQUEST" || err.message.indexOf("http request") != -1) serverconsole.locmessage("Client disconnected.");
else serverconsole.locmessage("Client disconnected due to error.");
});
socket.on("error", function () {});
var head = fs.existsSync("./.head") ? fs.readFileSync("./.head").toString() : (fs.existsSync("./head.html") ? fs.readFileSync("./head.html").toString() : ""); // header
var foot = fs.existsSync("./.foot") ? fs.readFileSync("./.foot").toString() : (fs.existsSync("./foot.html") ? fs.readFileSync("./foot.html").toString() : ""); // footer
var fd = "";
function responseEnd(d) {
if (d === undefined) d = fd;
res.write(head + d + foot);
res.end();
}
var serverErrorDescs = {
400: "The request you made is invalid.",
405: "Method used to access the requested file isn't allowed.",
408: "You have timed out.",
414: "URL you sent is too long.",
431: "The request you sent contains headers, that are too large.",
451: "The requested file isn't accessible for legal reasons.",
497: "You sent non-TLS request to the HTTPS server."
};
//Server error calling method
function callServerError(errorCode, extName, stack, ch) {
var errorFile = errorCode.toString() + ".html";
var errorFile2 = "." + errorCode.toString();
if (fs.existsSync(errorFile2)) errorFile = errorFile2;
if (errorCode == 404 && fs.existsSync(page404)) errorFile = page404;
if (Object.prototype.toString.call(stack) === "[object Error]") stack = generateErrorStack(stack);
if (stack === undefined) stack = generateErrorStack(new Error("Unknown error"));
if (errorCode == 500 || errorCode == 502) {
serverconsole.errmessage("There was an error while processing the request!");
serverconsole.errmessage("Stack:");
serverconsole.errmessage(stack);
}
if (stackHidden) stack = "[error stack hidden]";
if (serverErrorDescs[errorCode] === undefined) {
callServerError(501, extName, stack);
} else {
var cheaders = getCustomHeaders();
if (ch) {
var chon = Object.keys(cheaders);
var chn = Object.keys(ch);
for (var i = 0; i < chn.length; i++) {
var nhn = chn[i];
for (var j = 0; j < chon.length; j++) {
if (chon[j].toLowerCase() == chn[i].toLowerCase()) {
nhn = chon[j];
break;
}
}
if (ch[chn[i]]) cheaders[nhn] = ch[chn[i]];
}
}
cheaders["Content-Type"] = "text/html; charset=utf-8";
if (errorCode == 405 && !cheaders["Allow"]) cheaders["Allow"] = "GET, POST, HEAD, OPTIONS";
fs.readFile(errorFile, function (err, data) {
try {
if (err) throw err;
res.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders);
fd += data.toString().replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode]).replace(/{errorDesc}/g, serverErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, "&nbsp;&nbsp;")).replace(/{server}/g, "" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (extName == undefined ? "" : " " + extName)).replace(/{contact}/g, serverAdmin.replace(/\./g, "[dot]").replace(/@/g, "[at]"));
responseEnd();
} catch (err) {
2023-07-29 20:32:17 +02:00
var additionalError = 500;
if (err.code == "ENOENT") {
2023-07-29 20:32:17 +02:00
additionalError = 404;
} else if (err.code == "ENOTDIR") {
additionalError = 404; // Assume that file doesn't exist
} else if (err.code == "EACCES") {
2023-07-29 20:32:17 +02:00
additionalError = 403;
} else if (err.code == "EMFILE") {
2023-07-29 20:32:17 +02:00
additionalError = 429;
} else if (err.code == "ELOOP") {
2023-07-29 20:32:17 +02:00
additionalError = 508;
}
res.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders);
res.write(("<html><head><title>{errorMessage}</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body><h1>{errorMessage}</h1><p>{errorDesc}</p>" + ((additionalError == 404) ? "" : "<p>Additionally, a {additionalError} error occurred while loading an error page.</p>") + "<p><i>{server}</i></p></body></html>").replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode]).replace(/{errorDesc}/g, serverErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, "&nbsp;&nbsp;")).replace(/{server}/g, "" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (extName == undefined ? "" : " " + extName)).replace(/{contact}/g, serverAdmin.replace(/\./g, "[dot]").replace(/@/g, "[at]")).replace(/{additionalError}/g, additionalError.toString()));
res.end();
}
});
}
}
var reqip = socket.remoteAddress;
var reqport = socket.remotePort;
2023-08-09 21:09:23 +02:00
reqcounter++;
2023-07-29 20:44:13 +02:00
serverconsole.locmessage("Somebody connected to " + (secure && fromMain ? ((typeof sport == "number" ? "port " : "socket ") + sport) : ((typeof port == "number" ? "port " : "socket ") + port)) + "...");
2023-07-29 20:32:17 +02:00
serverconsole.reqmessage("Client " + ((!reqip || reqip == "") ? "[unknown client]" : (reqip + ((reqport && reqport !== 0) && reqport != "" ? ":" + reqport : ""))) + " sent invalid request.");
try {
if ((err.code && (err.code.indexOf("ERR_SSL_") == 0 || err.code.indexOf("ERR_TLS_") == 0)) || (!err.code && err.message.indexOf("SSL routines") != -1)) {
if (err.code == "ERR_SSL_HTTP_REQUEST" || err.message.indexOf("http request") != -1) {
serverconsole.errmessage("Client sent HTTP request to HTTPS port.");
callServerError(497);
return;
} else {
serverconsole.errmessage("An SSL error occured: " + (err.code ? err.code : err.message));
callServerError(400);
return;
}
}
2023-07-29 20:44:13 +02:00
if (err.code && err.code.indexOf("ERR_HTTP2_") == 0) {
serverconsole.errmessage("An HTTP/2 error occured: " + err.code);
callServerError(400);
return;
}
2023-07-29 20:32:17 +02:00
if (err.code && err.code == "ERR_HTTP_REQUEST_TIMEOUT") {
serverconsole.errmessage("Client timed out.");
callServerError(408);
return;
}
if (!err.rawPacket) {
serverconsole.errmessage("Connection ended prematurely.");
callServerError(400);
return;
}
var packetLines = err.rawPacket.toString().split("\r\n");
if (packetLines.length == 0) {
serverconsole.errmessage("Invalid request.");
callServerError(400);
return;
}
function checkHeaders(beginsFromFirst) {
for (var i = (beginsFromFirst ? 0 : 1); i < packetLines.length; i++) {
var header = packetLines[i];
if (header == "") return false; //Beginning of body
else if (header.indexOf(":") < 1) {
serverconsole.errmessage("Invalid header.");
callServerError(400);
return true;
} else if (header.length > 8192) {
serverconsole.errmessage("Header too large.");
callServerError(431); //Headers too large
return true;
}
}
return false;
}
var packetLine1 = packetLines[0].split(" ");
var method = "GET";
var httpVersion = "HTTP/1.1";
if (String(packetLine1[0]).indexOf(":") > 0) {
if (!checkHeaders(true)) {
2023-07-29 20:44:13 +02:00
serverconsole.errmessage("The request is invalid (it may be a part of larger invalid request).");
2023-07-29 20:32:17 +02:00
callServerError(400); //Also malformed Packet
return;
}
}
if (String(packetLine1[0]).length < 50) method = packetLine1.shift();
if (String(packetLine1[packetLine1.length - 1]).length < 50) httpVersion = packetLine1.pop();
if (packetLine1.length != 1) {
serverconsole.errmessage("The head of request is invalid.");
callServerError(400); //Malformed Packet
} else if (!httpVersion.toString().match(/^HTTP[\/]/i)) {
serverconsole.errmessage("Invalid protocol.");
callServerError(400); //bad protocol version
} else if (http.METHODS.indexOf(method) == -1) {
serverconsole.errmessage("Invalid method.");
callServerError(405); //Also malformed Packet
} else {
if (checkHeaders(false)) return;
if (packetLine1[0].length > 255) {
serverconsole.errmessage("URI too long.");
callServerError(414); //Also malformed Packet
} else {
2023-07-29 20:44:13 +02:00
serverconsole.errmessage("The request is invalid.");
2023-07-29 20:32:17 +02:00
callServerError(400); //Also malformed Packet
}
}
} catch (err) {
2023-07-29 20:32:17 +02:00
serverconsole.errmessage("There was an error while determining type of malformed request.");
callServerError(400);
}
}
function connhandler(request, socket, head) {
var reqIdInt = Math.round(Math.random() * 16777216);
var reqId = "0".repeat(6 - reqIdInt.toString(16).length) + reqIdInt.toString(16);
var serverconsole = {
climessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.climessage(nmsg[i]);
}
return;
}
console.log("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg);
LOG("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
reqmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.reqmessage(nmsg[i]);
}
return;
}
console.log("\x1b[34mSERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
LOG("SERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
resmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.resmessage(nmsg[i]);
}
return;
}
console.log("\x1b[32mSERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
LOG("SERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
errmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.errmessage(nmsg[i]);
}
return;
}
console.log("\x1b[31mSERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
LOG("SERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
locerrmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.locerrmessage(nmsg[i]);
}
return;
}
console.log("\x1b[41mSERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m");
LOG("SERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
locwarnmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.locwarnmessage(nmsg[i]);
}
return;
}
console.log("\x1b[43mSERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m");
LOG("SERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
locmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.locmessage(nmsg[i]);
}
return;
}
console.log("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg);
LOG("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
}
};
var req = request;
socket.on("close", function (hasError) {
if (!hasError) serverconsole.locmessage("Client disconnected.");
else serverconsole.locmessage("Client disconnected due to error.");
});
socket.on("error", function () {});
var reqip = socket.remoteAddress;
var reqport = socket.remotePort;
2023-08-09 21:09:23 +02:00
reqcounter++;
2023-07-29 20:44:13 +02:00
serverconsole.locmessage("Somebody connected to " + (secure ? ((typeof sport == "number" ? "port " : "socket ") + sport) : ((typeof port == "number" ? "port " : "socket ") + port)) + "...");
2023-07-29 20:32:17 +02:00
serverconsole.reqmessage("Client " + ((!reqip || reqip == "") ? "[unknown client]" : (reqip + ((reqport && reqport !== 0) && reqport != "" ? ":" + reqport : ""))) + " wants to proxy " + request.url + " through this server");
if (request.headers["user-agent"] != undefined) serverconsole.reqmessage("Client uses " + request.headers["user-agent"]);
function modExecute(mods, ffinals) {
var proxyMods = [];
for (var i = 0; i < mods.length; i++) {
if (mods[i].proxyCallback !== undefined) proxyMods.push(mods[i]);
}
var modFunction = ffinals;
for (var i = proxyMods.length - 1; i >= 0; i--) {
modFunction = proxyMods[i].callback(req, socket, head, configJSON, serverconsole, modFunction);
}
modFunction();
}
function vres(req, socket, head, serverconsole) {
return function () {
serverconsole.errmessage("SVR.JS doesn't support proxy without proxy mod.");
if (!socket.destroyed) socket.end("HTTP/1.1 501 Not Implemented\n\n");
};
}
modExecute(mods, vres(req, socket, head, serverconsole));
}
function reqhandler(request, response, fromMain) {
if (fromMain === undefined) fromMain = true;
var reqIdInt = Math.round(Math.random() * 16777216);
var reqId = "0".repeat(6 - reqIdInt.toString(16).length) + reqIdInt.toString(16);
var serverconsole = {
climessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.climessage(nmsg[i]);
}
return;
}
console.log("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg);
LOG("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
reqmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.reqmessage(nmsg[i]);
}
return;
}
console.log("\x1b[34mSERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
LOG("SERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
resmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.resmessage(nmsg[i]);
}
return;
}
console.log("\x1b[32mSERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
LOG("SERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
errmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.errmessage(nmsg[i]);
}
return;
}
console.log("\x1b[31mSERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m");
LOG("SERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
locerrmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.locerrmessage(nmsg[i]);
}
return;
}
console.log("\x1b[41mSERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m");
LOG("SERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
locwarnmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.locwarnmessage(nmsg[i]);
}
return;
}
console.log("\x1b[43mSERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m");
LOG("SERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
},
locmessage: function (msg) {
if (msg.indexOf("\n") != -1) {
var nmsg = msg.split("\n");
for (var i = 0; i < nmsg.length; i++) {
serverconsole.locmessage(nmsg[i]);
}
return;
}
console.log("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg);
LOG("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg);
return;
}
};
function getCustomHeaders() {
var ph = JSON.parse(JSON.stringify(customHeaders));
var phk = Object.keys(ph);
for (var i = 0; i < phk.length; i++) {
if (typeof ph[phk[i]] == "string") ph[phk[i]] = ph[phk[i]].replace(/\{path\}/g, request.url);
}
return ph;
}
//Make HTTP/1.x API-based scripts compatible with HTTP/2.0 API
if (configJSON.enableHTTP2 == true && request.httpVersion == "2.0") {
try {
//Set HTTP/1.x methods (to prevent process warnings)
response.writeHeadNodeApi = response.writeHead;
response.setHeaderNodeApi = response.setHeader;
response.writeHead = function (a, b, c) {
var table = c;
if (typeof (b) == "object") table = b;
if (table == undefined) table = this.tHeaders;
table = JSON.parse(JSON.stringify(table));
if (table["content-type"] != undefined && table["Content-Type"] != undefined) {
delete table["content-type"];
}
delete table["transfer-encoding"];
delete table["connection"];
delete table["keep-alive"];
delete table["upgrade"];
return response.writeHeadNodeApi(a, table);
};
response.setHeader = function (a, b) {
if (a != "transfer-encoding" && a != "connection" && a != "keep-alive" && a != "upgrade") return response.setHeaderNodeApi(a, b);
return false;
};
//Set HTTP/1.x headers
if (!request.headers.host) request.headers.host = request.headers[":authority"];
(request.headers[":path"] == undefined ? (function () {})() : request.url = request.headers[":path"]);
request.protocol = request.headers[":scheme"];
var headers = [":path" || ":method"];
for (var i = 0; i < headers.length; i++) {
if (request.headers[headers[i]] == undefined) {
var cheaders = getCustomHeaders();
cheaders["Content-Type"] = "text/html; charset=utf-8";
response.writeHead(400, "Bad Request", cheaders);
response.write("<html><head><title>400 Bad Request</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body><h1>400 Bad Request</h1><p>The request you sent is invalid. <p><i>" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (request.headers[":authority"] == undefined ? "" : " on " + request.headers[":authority"]) + "</i></p></body></html>");
response.end();
return;
}
}
} catch (err) {
2023-07-29 20:32:17 +02:00
var cheaders = getCustomHeaders();
cheaders["Content-Type"] = "text/html; charset=utf-8";
cheaders[":status"] = "500";
response.stream.respond(cheaders);
response.stream.write("<html><head><title>500 Internal Server Error</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body><h1>500 Internal Server Error</h1><p>The server had an unexpected error. Below, error stack is shown: </p><code>" + (stackHidden ? "[error stack hidden]" : generateErrorStack(err)).replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, "&nbsp;&nbsp;") + "</code><p>Please contact with developer/administrator of the website.</p><p><i>" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (request.headers[":authority"] == undefined ? "" : " on " + request.headers[":authority"]) + "</i></p></body></html>");
2023-07-29 20:32:17 +02:00
response.stream.end();
return;
}
}
if (request.headers["x-svr-js-from-main-thread"] == "true" && (!request.socket.remoteAddress || request.socket.remoteAddress == "::1" || request.socket.remoteAddress == "::ffff:127.0.0.1" || request.socket.remoteAddress == "127.0.0.1" || request.socket.remoteAddress == "localhost")) {
2023-08-09 21:09:23 +02:00
var headers = getCustomHeaders();
response.writeHead(204, "No Content", headers);
2023-07-29 20:32:17 +02:00
response.end();
return;
}
try {
request.url = encodeURI(Buffer.from(request.url, "latin1").toString("utf8")).replace(/%25/gi, "%");
} catch (err) {
2023-07-29 20:32:17 +02:00
//Request URL not modified...
}
var headWritten = false;
2023-07-29 20:44:13 +02:00
var lastStatusCode = null;
2023-07-29 20:32:17 +02:00
response.writeHeadNative = response.writeHead;
response.writeHead = function (a, b, c) {
if (!(headWritten && process.isBun && a === lastStatusCode && b === undefined && c === undefined)) {
if (headWritten) {
process.emitWarning("res.writeHead called multiple times.", {
code: "WARN_SVRJS_MULTIPLE_WRITEHEAD"
});
return response;
} else {
headWritten = true;
}
if (parseInt(a) >= 400 && parseInt(a) <= 599) {
serverconsole.errmessage("Server responded with " + a.toString() + " code.");
} else {
serverconsole.resmessage("Server responded with " + a.toString() + " code.");
}
2023-07-29 20:44:13 +02:00
if(typeof b != "string" && http.STATUS_CODES[a]) {
if(!c) c = b;
b = http.STATUS_CODES[a];
}
2023-07-29 20:32:17 +02:00
lastStatusCode = a;
}
response.writeHeadNative(a, b, c);
};
if (wwwredirect) {
var hostname = request.headers.host.split[":"];
var hostport = null;
if (hostname.length > 1 && (hostname[0] != "[" || hostname[hostname.length - 1] != "]")) hostport = hostname.pop();
hostname = hostname.join(":");
}
if (wwwredirect && hostname == domain && hostname.indexOf("www.") != 0) {
var lloc = (request.socket.encrypted ? "https" : "http") + "://" + hostname + (hostport ? ":" + hostport : "");
try {
var rheaders = getCustomHeaders();
rheaders["Location"] = lloc + (request.url.replace(/\/+/g, "/"));
response.writeHead(301, "Redirect to WWW", rheaders);
response.end();
} catch (err) {
2023-07-29 20:32:17 +02:00
var cheaders = getCustomHeaders();
cheaders["Content-Type"] = "text/html; charset=utf-8";
res.writeHead(500, "Internal Server Error", cheaders);
res.write("<html><head><title>500 Internal Server Error</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body><h1>500 Internal Server Error</h1><p>The server had an unexpected error. Below, error stack is shown: </p><code>" + (stackHidden ? "[error stack hidden]" : generateErrorStack(err)).replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, "&nbsp;&nbsp;") + "</code><p>Please contact with developer/administrator of the website.</p><p><i>" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (req.headers.host == undefined ? "" : " on " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) + "</i></p></body></html>");
2023-07-29 20:32:17 +02:00
response.end();
}
} else {
var finished = false;
response.on("finish", function () {
if (!finished) {
finished = true;
serverconsole.locmessage("Client disconnected.");
}
});
response.on("close", function () {
if (!finished) {
finished = true;
serverconsole.locmessage("Client disconnected.");
}
});
var isProxy = false;
if (request.url.indexOf("/") != 0 && request.url != "*") isProxy = true;
2023-07-29 20:44:13 +02:00
serverconsole.locmessage("Somebody connected to " + (secure && fromMain ? ((typeof sport == "number" ? "port " : "socket ") + sport) : ((typeof port == "number" ? "port " : "socket ") + port)) + "...");
2023-07-29 20:32:17 +02:00
if (request.socket == null) {
serverconsole.errmessage("Client socket is null!!!");
return;
}
var reqport = "";
var reqip = "";
var oldport = "";
var oldip = "";
if (request.headers["x-svr-js-client"] != undefined && enableIPSpoofing) {
var kl = request.headers["x-svr-js-client"].split(":");
reqport = kl.pop();
reqip = kl.join(":");
try {
oldport = request.socket.remotePort;
oldip = request.socket.remoteAddress;
request.socket.realRemotePort = reqport;
request.socket.realRemoteAddress = reqip;
request.socket.originalRemotePort = oldport;
request.socket.originalRemoteAddress = oldip;
response.socket.realRemotePort = reqport;
response.socket.realRemoteAddress = reqip;
response.socket.originalRemotePort = oldport;
response.socket.originalRemoteAddress = oldip;
} catch (err) {
2023-07-29 20:32:17 +02:00
//Address setting failed
}
} else if (request.headers["x-forwarded-for"] != undefined && enableIPSpoofing) {
reqport = null;
reqip = request.headers["x-forwarded-for"].split(",")[0].replace(/ /g, "");
if (reqip.indexOf(":") == -1) reqip = "::ffff:" + reqip;
try {
oldport = request.socket.remotePort;
oldip = request.socket.remoteAddress;
request.socket.realRemotePort = reqport;
request.socket.realRemoteAddress = reqip;
request.socket.originalRemotePort = oldport;
request.socket.originalRemoteAddress = oldip;
response.socket.realRemotePort = reqport;
response.socket.realRemoteAddress = reqip;
response.socket.originalRemotePort = oldport;
response.socket.originalRemoteAddress = oldip;
} catch (err) {
2023-07-29 20:32:17 +02:00
//Address setting failed
}
} else {
reqip = request.socket.remoteAddress;
reqport = request.socket.remotePort;
}
2023-08-09 21:09:23 +02:00
reqcounter++;
2023-07-29 20:32:17 +02:00
if (!isProxy) serverconsole.reqmessage("Client " + ((!reqip || reqip == "") ? "[unknown client]" : (reqip + ((reqport && reqport !== 0) && reqport != "" ? ":" + reqport : ""))) + " wants " + (request.method == "GET" ? "content in " : (request.method == "POST" ? "to post content in " : (request.method == "PUT" ? "to add content in " : (request.method == "DELETE" ? "to delete content in " : (request.method == "PATCH" ? "to patch content in " : "to access content using " + request.method + " method in "))))) + (request.headers.host == undefined ? "" : request.headers.host) + request.url);
else serverconsole.reqmessage("Client " + ((!reqip || reqip == "") ? "[unknown client]" : (reqip + ((reqport && reqport !== 0) && reqport != "" ? ":" + reqport : ""))) + " wants " + (request.method == "GET" ? "content in " : (request.method == "POST" ? "to post content in " : (request.method == "PUT" ? "to add content in " : (request.method == "DELETE" ? "to delete content in " : (request.method == "PATCH" ? "to patch content in " : "to access content using " + request.method + " method in "))))) + request.url);
if (request.headers["user-agent"] != undefined) serverconsole.reqmessage("Client uses " + request.headers["user-agent"]);
var acceptEncoding = request.headers["accept-encoding"];
if (!acceptEncoding) acceptEncoding = "";
var head = fs.existsSync("./.head") ? fs.readFileSync("./.head").toString() : (fs.existsSync("./head.html") ? fs.readFileSync("./head.html").toString() : ""); // header
var foot = fs.existsSync("./.foot") ? fs.readFileSync("./.foot").toString() : (fs.existsSync("./foot.html") ? fs.readFileSync("./foot.html").toString() : ""); // footer
var fd = "";
function responseEnd(d) {
if (d === undefined) d = fd;
res.write(head + d + foot);
res.end();
}
// function responseEndGzip(d) {
// if (d === undefined) d = fd;
// zlib.gzip(head + d + foot, function (err, buff) {
// if (err) {
// throw err;
// } else {
// res.write(buff);
// res.end();
// }
// });
// }
//
// function responseEndDeflate(d) {
// if (d === undefined) d = fd;
// zlib.deflateRaw(head + d + foot, function (err, buff) {
// if (err) {
// throw err;
// } else {
// res.write(buff);
// res.end();
// }
// });
// }
var req = request; // request var is req = request
var res = response; // response var is res = response
//Error descriptions
var serverErrorDescs = {
200: "The request succeeded! :)",
201: "New resource has been created.",
202: "The request has been accepted for processing, but the processing has not been completed.",
400: "The request you made is invalid.",
401: "You need to authenticate yourself in order to access the requested file.",
402: "You need to pay in order to access the requested file.",
403: "You don't have access to the requested file.",
404: "The requested file doesn't exist. If you have typed URL manually, then please check the spelling.",
405: "Method used to access the requested file isn't allowed.",
406: "The request is capable of generating only not acceptable content.",
407: "You need to authenticate yourself in order to use the proxy.",
408: "You have timed out.",
409: "The request you sent conflicts with the current state of the server.",
410: "The requested file is permanently deleted.",
411: "Content-Length property is required.",
412: "The server doesn't meet preconditions you put in the request.",
413: "The request you sent is too large.",
414: "URL you sent is too long.",
415: "The media type of request you sent isn't supported by the server.",
416: "Content-Range you sent is unsatisfiable.",
417: "Expectation in Expect property couldn't be satisfied.",
418: "The server (teapot) can't brew any coffee! ;)",
421: "The request you made isn't intended for this server.",
422: "The server couldn't process content sent by you.",
423: "The requested file is locked.",
424: "The request depends on another failed request.",
425: "The server is unwilling to risk processing a request that might be replayed.",
426: "You need to upgrade protocols you use to request a file.",
428: "The request you sent needs to be conditional, but it isn't.",
429: "You sent too much requests to the server.",
431: "The request you sent contains headers, that are too large.",
451: "The requested file isn't accessible for legal reasons.",
500: "The server had an unexpected error. Below, the error stack is shown: </p><code>{stack}</code><p>Please contact with developer/administrator at <i>{contact}</i>.",
501: "The request requires use of a function, which isn't implemented (yet) by the server.",
502: "The server had an error, while it was acting as a gateway.</p><p>Please contact with developer/administrator at <i>{contact}</i>.",
503: "Service provided by the server isn't available (yet).</p><p>Please contact with developer/administrator at <i>{contact}</i>.",
504: "The server couldn't get response in time, while it was acting as a gateway.</p><p>Please contact with developer/administrator at <i>{contact}</i>.",
505: "The server doesn't support HTTP version used in the request.",
506: "Variant header is configured to be engaged in content negotiation.</p><p>Please contact with developer/administrator at <i>{contact}</i>.",
507: "The server ran out of disk space neccessary to complete the request.",
508: "The server detected an infinite loop while processing the request.",
509: "The server has it's bandwidth limit exceeded.</p><p>Please contact with developer/administrator at <i>{contact}</i>.",
510: "The server requires an extended HTTP request. The request you made isn't an extended HTTP request.",
511: "You need to authenticate yourself in order to get network access.",
598: "The server couldn't get response in time, while it was acting as a proxy.",
599: "The server couldn't connect in time, while it was acting as a proxy."
};
//Server error calling method
// Server error calling method
function callServerError(errorCode, extName, stack, ch) {
if (typeof errorCode !== "number") {
throw new TypeError("HTTP error code parameter needs to be an integer.");
}
// Handle optional parameters
if (extName && typeof extName === "object") {
ch = stack;
stack = extName;
extName = undefined;
} else if (typeof extName !== "string" && extName !== null && extName !== undefined) {
throw new TypeError("Extension name parameter needs to be a string.");
}
if (stack && typeof stack === "object" && Object.prototype.toString.call(stack) !== "[object Error]") {
ch = stack;
stack = undefined;
} else if (typeof stack !== "object" && typeof stack !== "string" && stack) {
throw new TypeError("Error stack parameter needs to be either a string or an instance of Error object.");
}
var errorFile = errorCode.toString() + ".html";
var errorFile2 = "." + errorCode.toString();
if (fs.existsSync(errorFile2)) errorFile = errorFile2;
if (errorCode == 404 && fs.existsSync(page404)) errorFile = page404;
// Generate error stack if not provided
if (Object.prototype.toString.call(stack) === "[object Error]") stack = generateErrorStack(stack);
if (stack === undefined) stack = generateErrorStack(new Error("Unknown error"));
if (errorCode == 500 || errorCode == 502) {
serverconsole.errmessage("There was an error while processing the request!");
serverconsole.errmessage("Stack:");
serverconsole.errmessage(stack);
}
// Hide the error stack if specified
if (stackHidden) stack = "[error stack hidden]";
// Validate the error code and handle unknown codes
if (serverErrorDescs[errorCode] === undefined) {
callServerError(501, extName, stack);
} else {
var cheaders = getCustomHeaders();
// Process custom headers if provided
if (ch) {
var chon = Object.keys(cheaders);
var chn = Object.keys(ch);
for (var i = 0; i < chn.length; i++) {
var nhn = chn[i];
for (var j = 0; j < chon.length; j++) {
if (chon[j].toLowerCase() == chn[i].toLowerCase()) {
nhn = chon[j];
break;
}
}
if (ch[chn[i]]) cheaders[nhn] = ch[chn[i]];
}
}
cheaders["Content-Type"] = "text/html; charset=utf-8";
// Set default Allow header for 405 error if not provided
if (errorCode == 405 && !cheaders["Allow"]) cheaders["Allow"] = "GET, POST, HEAD, OPTIONS";
// Read the error file and replace placeholders with error information
fs.readFile(errorFile, function (err, data) {
try {
if (err) throw err;
response.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders);
fd += data.toString().replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode]).replace(/{errorDesc}/g, serverErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, "&nbsp;&nbsp;")).replace(/{path}/g, request.url.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")).replace(/{server}/g, "" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (extName == undefined ? "" : " " + extName) + ((req.headers.host == undefined || isProxy) ? "" : " on " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"))).replace(/{contact}/g, serverAdmin.replace(/\./g, "[dot]").replace(/@/g, "[at]")); // Replace placeholders in error response
responseEnd();
} catch (err) {
2023-07-29 20:32:17 +02:00
var additionalError = 500;
// Handle additional error cases
if (err.code == "ENOENT") {
2023-07-29 20:32:17 +02:00
additionalError = 404;
} else if (err.code == "ENOTDIR") {
additionalError = 404; // Assume that file doesn't exist
} else if (err.code == "EACCES") {
2023-07-29 20:32:17 +02:00
additionalError = 403;
} else if (err.code == "EMFILE") {
2023-07-29 20:32:17 +02:00
additionalError = 429;
} else if (err.code == "ELOOP") {
2023-07-29 20:32:17 +02:00
additionalError = 508;
}
response.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders);
response.write(("<html><head><title>{errorMessage}</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body><h1>{errorMessage}</h1><p>{errorDesc}</p>" + ((additionalError == 404) ? "" : "<p>Additionally, a {additionalError} error occurred while loading an error page.</p>") + "<p><i>{server}</i></p></body></html>").replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode]).replace(/{errorDesc}/g, serverErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\r\n/g, "<br/>").replace(/\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/ {2}/g, "&nbsp;&nbsp;")).replace(/{path}/g, request.url.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")).replace(/{server}/g, "" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (extName == undefined ? "" : " " + extName) + ((req.headers.host == undefined || isProxy) ? "" : " on " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"))).replace(/{contact}/g, serverAdmin.replace(/\./g, "[dot]").replace(/@/g, "[at]")).replace(/{additionalError}/g, additionalError.toString())); // Replace placeholders in error response
response.end();
}
});
}
}
2023-07-29 20:44:13 +02:00
// Function to perform HTTP redirection to a specified destination URL
function redirect(destination, isTemporary, customHeaders) {
// If customHeaders are not provided, get the default custom headers
if (customHeaders === undefined) customHeaders = getCustomHeaders();
// Set the "Location" header to the destination URL
customHeaders["Location"] = destination;
// Determine the status code for redirection based on the isTemporary flag
var statusCode = isTemporary ? 302 : 301;
// Write the response header with the appropriate status code and message
res.writeHead(statusCode, http.STATUS_CODES[statusCode], customHeaders);
// Log the redirection message
serverconsole.resmessage("Client redirected to " + destination);
// End the response
2023-07-29 20:32:17 +02:00
res.end();
2023-07-29 20:44:13 +02:00
// Return from the function
2023-07-29 20:32:17 +02:00
return;
}
2023-07-29 20:44:13 +02:00
// Function to parse incoming POST data from the request
2023-07-29 20:32:17 +02:00
function parsePostData(options, callback) {
2023-07-29 20:44:13 +02:00
// If the request method is not POST, return a 405 Method Not Allowed error
2023-07-29 20:32:17 +02:00
if (req.method != "POST") {
2023-07-29 20:44:13 +02:00
// Get the default custom headers and add "Allow" header with value "POST"
var customHeaders = getCustomHeaders();
customHeaders["Allow"] = "POST";
// Call the server error function with 405 status code and custom headers
callServerError(405, undefined, undefined, customHeaders);
2023-07-29 20:32:17 +02:00
return;
}
2023-07-29 20:44:13 +02:00
// Set formidableOptions to options, if provided; otherwise, set it to an empty object
var formidableOptions = options ? options : {};
// If no callback is provided, set the callback to options and reset formidableOptions
2023-07-29 20:32:17 +02:00
if (!callback) {
callback = options;
formidableOptions = {};
}
2023-07-29 20:44:13 +02:00
// If the formidable module had an error, call the server error function with 500 status code and error stack
2023-07-29 20:32:17 +02:00
if (formidable._errored) callServerError(500, undefined, generateErrorStack(formidable._errored));
2023-07-29 20:44:13 +02:00
// Create a new formidable form
2023-07-29 20:32:17 +02:00
var form = formidable(formidableOptions);
2023-07-29 20:44:13 +02:00
// Parse the request and process the fields and files
2023-07-29 20:32:17 +02:00
form.parse(req, function (err, fields, files) {
2023-07-29 20:44:13 +02:00
// If there was an error, call the server error function with status code determined by error
2023-07-29 20:32:17 +02:00
if (err) {
if(err.httpCode) callServerError(err.httpCode);
else callServerError(400);
return;
}
2023-07-29 20:44:13 +02:00
// Otherwise, call the provided callback function with the parsed fields and files
2023-07-29 20:32:17 +02:00
callback(fields, files);
});
}
2023-07-29 20:44:13 +02:00
// Function to parse a URL string into a URL object
function parseURL(uri) {
// Check if the URL API is available (Node.js version >= 10)
if (typeof URL !== "undefined" && url.Url) {
2023-07-29 20:32:17 +02:00
try {
2023-07-29 20:44:13 +02:00
// Create a new URL object using the provided URI and base URL
2023-07-29 20:32:17 +02:00
var uobject = new URL(uri, "http" + (req.socket.encrypted ? "s" : "") + "://" + (req.headers.host ? req.headers.host : (domain ? domain : "unknown.invalid")));
2023-07-29 20:44:13 +02:00
// Create a new URL object (similar to deprecated url.Url)
2023-07-29 20:32:17 +02:00
var nuobject = new url.Url();
2023-07-29 20:44:13 +02:00
// Set properties of the new URL object from the provided URL
2023-07-29 20:32:17 +02:00
if (uri.indexOf("/") != -1) nuobject.slashes = true;
if (uobject.protocol != "") nuobject.protocol = uobject.protocol;
if (uobject.username != "" && uobject.password != "") nuobject.auth = uobject.username + ":" + uobject.password;
if (uobject.host != "") nuobject.host = uobject.host;
if (uobject.hostname != "") nuobject.hostname = uobject.hostname;
if (uobject.port != "") nuobject.port = uobject.port;
if (uobject.pathname != "") nuobject.pathname = uobject.pathname;
if (uobject.search != "") nuobject.search = uobject.search;
if (uobject.hash != "") nuobject.hash = uobject.hash;
if (uobject.href != "") nuobject.href = uobject.href;
2023-07-29 20:44:13 +02:00
// Adjust the pathname and href properties if the URI doesn't start with "/"
2023-07-29 20:32:17 +02:00
if (uri.indexOf("/") != 0) {
if (nuobject.pathname) {
nuobject.pathname = nuobject.pathname.substr(1);
nuobject.href = nuobject.pathname + (nuobject.search ? nuobject.search : "");
}
}
2023-07-29 20:44:13 +02:00
// Set the path property as a combination of pathname and search
2023-07-29 20:32:17 +02:00
if (nuobject.pathname) {
nuobject.path = nuobject.pathname + (nuobject.search ? nuobject.search : "");
}
2023-07-29 20:44:13 +02:00
// Initialize the query object and copy URL search parameters to it
2023-07-29 20:32:17 +02:00
nuobject.query = {};
uobject.searchParams.forEach(function (value, key) {
nuobject.query[key] = value;
});
2023-07-29 20:44:13 +02:00
// Return the created URL object
2023-07-29 20:32:17 +02:00
return nuobject;
} catch (err) {
2023-07-29 20:44:13 +02:00
// If there was an error using the URL API, fall back to deprecated url.parse
2023-07-29 20:32:17 +02:00
return url.parse(uri, true);
}
} else {
2023-07-29 20:44:13 +02:00
// If the URL API is not available, fall back to deprecated url.parse
2023-07-29 20:32:17 +02:00
return url.parse(uri, true);
}
}
2023-07-29 20:44:13 +02:00
var uobject = parseURL(req.url);
2023-07-29 20:32:17 +02:00
var search = uobject.search;
var href = uobject.pathname;
var ext = path.extname(href).toLowerCase();
ext = ext.substr(1, ext.length);
var decodedHref = "";
try {
decodedHref = decodeURIComponent(href);
} catch (err) {
2023-07-29 20:32:17 +02:00
//Return 400 error
callServerError(400);
serverconsole.errmessage("Bad request!");
return;
}
if (req.headers["expect"] && req.headers["expect"] != "100-continue") {
callServerError(417);
return;
}
//MOD EXCECUTION FUNCTION
function modExecute(mods, ffinals) {
var modFunction = ffinals;
for (var i = mods.length - 1; i >= 0; i--) {
modFunction = mods[i].callback(req, res, serverconsole, responseEnd, href, ext, uobject, search, "index.html", users, page404, head, foot, fd, modFunction, configJSON, callServerError, getCustomHeaders, origHref, redirect, parsePostData);
}
modFunction();
}
var vresCalled = false;
function vres(req, res, serverconsole, responseEnd, href, ext, uobject, search, defaultpage, users, page404, head, foot, fd, callServerError, getCustomHeaders, origHref, redirect, parsePostData) {
return function () {
if (vresCalled) {
process.emitWarning("elseCallback() invoked multiple times.", {
code: "WARN_SVRJS_MULTIPLE_ELSECALLBACK"
});
return;
} else {
vresCalled = true;
}
// function responseEndGzip(d) {
// if (d === undefined) d = fd;
// zlib.gzip(head + d + foot, function (err, buff) {
// if (err) {
// throw err;
// } else {
// res.write(buff);
// res.end();
// }
// });
// }
//
// function responseEndDeflate(d) {
// if (d === undefined) d = fd;
// zlib.deflateRaw(head + d + foot, function (err, buff) {
// if (err) {
// throw err;
// } else {
// res.write(buff);
// res.end();
// }
// });
// }
function responseEnd(d) {
if (d === undefined) d = fd;
res.write(head + d + foot);
res.end();
}
if (req.socket == null) {
serverconsole.errmessage("Client socket is null!!!");
return;
}
var reqport = "";
var reqip = "";
var oldport = "";
var oldip = "";
if (req.headers["x-svr-js-client"] != undefined && enableIPSpoofing) {
var kl = req.headers["x-svr-js-client"].split(":");
reqport = kl.pop();
reqip = kl.join(":");
try {
oldport = req.socket.remotePort;
oldip = req.socket.remoteAddress;
req.socket.realRemotePort = reqport;
req.socket.realRemoteAddress = reqip;
req.socket.originalRemotePort = oldport;
req.socket.originalRemoteAddress = oldip;
res.socket.realRemotePort = reqport;
res.socket.realRemoteAddress = reqip;
res.socket.originalRemotePort = oldport;
res.socket.originalRemoteAddress = oldip;
} catch (err) {
2023-07-29 20:32:17 +02:00
//Nevermind...
}
} else if (req.headers["x-forwarded-for"] != undefined && enableIPSpoofing) {
reqport = null;
reqip = req.headers["x-forwarded-for"].split(",")[0].replace(/ /g, "");
if (reqip.indexOf(":") == -1) reqip = "::ffff:" + reqip;
try {
oldport = req.socket.remotePort;
oldip = req.socket.remoteAddress;
req.socket.realRemotePort = reqport;
req.socket.realRemoteAddress = reqip;
req.socket.originalRemotePort = oldport;
req.socket.originalRemoteAddress = oldip;
res.socket.realRemotePort = reqport;
res.socket.realRemoteAddress = reqip;
res.socket.originalRemotePort = oldport;
res.socket.originalRemoteAddress = oldip;
} catch (err) {
2023-07-29 20:32:17 +02:00
//Nevermind...
}
} else {
reqip = req.socket.remoteAddress;
reqport = req.socket.remotePort;
}
2023-07-29 20:44:13 +02:00
// Function to check the level of a path relative to the web root
function checkPathLevel(path) {
// Split the path into an array of components based on "/"
var pathComponents = path.split("/");
// Initialize counters for level up (..) and level down (.)
var levelUpCount = 0;
var levelDownCount = 0;
// Loop through the path components
for (var i = 0; i < pathComponents.length; i += 1) {
// If the component is "..", decrement the levelUpCount
if (".." === pathComponents[i]) {
levelUpCount -= 1;
}
// If the component is not "." or an empty string, increment the levelDownCount
else if ("." !== pathComponents[i] && "" !== pathComponents[i]) {
levelDownCount += 1;
}
}
// Calculate the overall level by subtracting levelUpCount from levelDownCount
var overallLevel = levelDownCount - levelUpCount;
// Return the overall level
return overallLevel;
2023-07-29 20:32:17 +02:00
}
2023-07-29 20:44:13 +02:00
2023-07-29 20:32:17 +02:00
if (isProxy) {
var eheaders = getCustomHeaders();
eheaders["Content-Type"] = "text/html; charset=utf-8";
res.writeHead(501, "Not implemented", eheaders);
res.write("<html><head><title>Proxy not implemented</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body><h1>Proxy not implemented</h1><p>SVR.JS doesn't support proxy without proxy mod. If you're administator of this server, then install this mod in order to use SVR.JS as a proxy.</p><p><i>" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + "</i></p></body></html>");
res.end();
serverconsole.errmessage("SVR.JS doesn't support proxy without proxy mod.");
return;
}
if (req.method == "OPTIONS") {
var hdss = getCustomHeaders();
hdss["Allow"] = "GET, POST, HEAD, OPTIONS";
res.writeHead(204, "No Content", hdss);
res.end();
return;
} else if (req.method != "GET" && req.method != "POST" && req.method != "HEAD") {
callServerError(405);
serverconsole.errmessage("Invaild method: " + req.method);
return;
}
if (href == "/invoke500.svr" || (os.platform() == "win32" && href.toLowerCase() == "/invoke500.svr")) {
if (version.indexOf("Nightly-") === 0 && uobject.query.crash !== undefined) throw new Error("Intentionally crashed");
try {
if (uobject.query.aprilfools === undefined) throw new Error("This page is intended to return 500 code.");
var hdhds = getCustomHeaders();
hdhds["Content-Type"] = "text/html; charset=utf-8";
if (uobject.query.activate === undefined) {
res.writeHead(599, "You may be a victim of software counterfeiting.", hdhds);
res.end("<html><head><title>Directory traversal prevention is not working.</title><style>body{background-color:#000;color:#fff}.a::after{content:\"\";clear:both;display:table}.button{text-decoration:none;color:#000;background-color:#f0f0f0;border:1px gray solid;padding:3px 20px 3px 20px}.button:hover{border:2px #00f solid}.close{text-decoration:none;color:#fff}</style></head><body><div style=\"text-align:center;font-family:sans-serif\"><div style=\"display:inline-block;background-color:gray\"><div style=\"background-color:#00f;color:#fff;text-align:left\"><a href=\"\" class=\"close\"><div style=\"float:right;background-color:red;padding:2px;margin-bottom:5px;margin-right:5px\"><small><b>&nbsp;&nbsp;X&nbsp;&nbsp;</b></small></div></a><div style=\"padding:5px\"><img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAXElEQVQ4ja2TwQoAIAhDXfT/v7wOERTYrEzo5HSvUpiRloiikiSMxHuDFMHsrCgkAdDPNYHnuKP4/wbqvl7uL0H0554mTYAxyifuSyEYExzPwa37XFO1KG6E7Do3hN8jFFmoGsUAAAAASUVORK5CYII=\"> Directory traversal prevention is not working.</div></div><div class=\"a\" style=\"background-color:#fff;margin:0 5px 0 5px;text-align:left\"><img style=\"margin:5px\" src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADZklEQVRoge2Zz0sbURDHPylqEyql3mwjBjx4EFHij4MX20O1B3/c9NhLoGAR400PIoUe4v8j1Ft/nLUXD0VED4KpVq3WgjH+nB7eCya72WT37W6k4Bceu5mZNzPf92Z2n2tERIT/GI/uOwG/eCDgjA/AY30NDyESWAYu9TU8hERgAdjQ9xv6dzgIicAykNP3OcLchRAIzAI7FtmOlgePEAh8AU4tslMtDx4BE3gL7DrodrU+WARMYAM4dtAdc9fYwSFAAm+A7So229ouOARI4IbC6h8dQSoFLS1qpFJKpvQ3wYUEkEDQISJPRQQ5PETicQRKRzyudMquI5iwIhLQDiSBvwDMzUE2a7fIZpVO2SWDCQtERPwep58A16hjgyqZcgQA4nHY3QVoAOqAM3+h8d0DC8B7CslDodbL4053qef5P2L43IEXwF6JpLERzhwWNhaDXK5Y8hz4aR4eXzuQBuzLPTHhPGN83Co50n7M4YPAZ1Ttl6ISgdFRq+Ra+/EBs4fXlIjERATbyOWQWKz0ERqNquvent1e+Zmq9WP0O8WNW4xYDIaGSmX5PHR3Q3NzuRmX2p8ZDAhMAz+o9EYdG7PLhoedrG+0v2nvqWBEYA2n1S9gZMQucyaA9rfmPRXw2gOzItJUtvato7+/tAfOz6vNadL+Q+2Br8C5K8viJ87gIESj1Waca//e4IHAPPALyLuyLu6DyuVTQF77n3efEngpoTYRiboqHxHk9hZpaFAltL7ubo7y3xZGCS2iDl/uVh8gEoGBAVU+nZ1uZ+V1nEXXcVzsQEZEnolIvevV9zfqdbxMUDuwAvwBrtyvii9c6XgrrqyrEFgCVj2nkMtBOg2JhBrptPUU6garOn4VVN6gHs8lcHGB9PXZ/6Ts61M6b/56qpZQBQIfxe1Lq3hkMvbkC2NpySuBJp2HEYEuz8mLIMmkM4GeHpOm7qpIwKEH5oEDr0ULwOammc4ZB1R6uTkQ+ATsm0Sjvd1M54x9nU95lCHwDjgxiQTA5KSZrjJOUHmVgb2qeo1qP5ynUPHoLdsDFgJjItLqi4AIcnaGzMwgiYQaMzNK5s9vq86vFJbPKq+Ab6b7XAO8xHrkLuqBJLBVy2wMsIX1s2Td3e1j4HVN0zHD75JfAXwbvV88/Kf+vvFA4L7xD6EQkx9oyJNyAAAAAElFTkSuQmCC\"><div style=\"color:#000;padding:7px 7px 5px 5px;float:right\"><big><big><span style=\"color:#5050ff\">You may be a victim of software<br>counterfeiting.</span></big></big><br><br>To use all DorianTech SVR.JS features, such as all<br>directory traversal protections; use server-side JS;<br>and recieve product support, your copy of DorianTech<br>SVR.JS must be validated as genuine.<br><br><a href=\"/invoke500.svr?aprilfools&activate\">Go online and resolve now</a><br><br></div></div><div style=\"padding:15px;border-top:1px silver solid;margin:0 5px 5px 5px;background-color:#e0e0e0;text-align:right\"><a href=\"javascript:location.reload();\" class=\"button\">Close</a></div></div></div><small>This copy of SVR.JS is not genuine.</small></body></html>");
serverconsole.resmessage("You may be a victim of software counterfeiting.");
} else {
res.writeHead(200, "OK", hdhds);
res.end("<style>body{background-color: black; color: white}</style><!DOCTYPE html>\n<!-- DON'T DELETE THE PAGE! THIS PAGE IS MEANT TO ACTIVATE SVR.JS!!! -->\n<html>\n<head>\n<title>SVR.JS Genuine Advantage</title>\n</head>\n<body>\n<h1>Activate SVR.JS</h1>\n<i>You will then be able to use all of SVR.JS features through SVR.JS Genuine Advantage!</i>\n<noscript>You need to enable JavaScript in order to activate SVR.JS.</noscript>\n<big><p id=\"activate\">Wait...</p></big>\n<script>\n setTimeout(function () {\n document.getElementById(\"activate\").innerHTML = \"Can't connect to DorianTech Activation Service! Check your internet connection.\";\n 'use doriantech activation technologies';\n //#$DAT%SURL http://cscsmaterials.ddns.net/dat/doriantech.dad\n //#$DAT%CONN __DORIANTECH__.__SVRJS__.__APRILFOOLS__.__ACTIVATE__(this,\"SVRJSSECRET32942832432\")\n //#$DAT%ATOV __DORIANTECH__.__SVRJS__.__APRILFOOLS__.__USECOPY__(this)\n //#$DAT%USEC this\n //#$DAT%IFOK __JSEXECUTE__(\"document.getElementById('activate').innerHTML = __dat__.exec('HTML');\")\n }, 2000);\n</script>\n</body>\n</html>\n<small>This copy of SVR.JS is not genuine.</small>");
}
return;
} catch (err) {
callServerError(500, undefined, generateErrorStack(err));
2023-07-29 20:32:17 +02:00
return;
}
} else if (allowStatus && (href == "/svrjsstatus.svr" || (os.platform() == "win32" && href.toLowerCase() == "/svrjsstatus.svr"))) {
function formatRelativeTime(relativeTime) {
var days = Math.floor(relativeTime / 60 / (60 * 24));
var dateDiff = new Date(relativeTime * 1000);
return days + " days, " + dateDiff.getUTCHours() + " hours, " + dateDiff.getUTCMinutes() + " minutes, " + dateDiff.getUTCSeconds() + " seconds";
}
var hdhds = getCustomHeaders();
hdhds["Content-Type"] = "text/html; charset=utf-8";
res.writeHead(200, "OK", hdhds);
res.end((head == "" ? "<html><head><title>SVR.JS status" + (request.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) + "</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body>" : head.replace(/<head>/i, "<head><title>SVR.JS status" + (request.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) + "</title>")) + "<h1>SVR.JS status" + (request.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) + "</h1>Server version: " + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + "<br/><hr/>Current time: " + new Date().toString() + "<br/>Thread start time: " + new Date(new Date() - (process.uptime() * 1000)).toString() + "<br/>Thread uptime: " + formatRelativeTime(Math.floor(process.uptime())) + "<br/>OS uptime: " + formatRelativeTime(os.uptime()) + "<br/>Total request count: " + reqcounter + "<br/>Average request rate: " + (Math.round((reqcounter / process.uptime()) * 100) / 100) + " requests/s" + (process.memoryUsage ? ("<br/>Memory usage of thread: " + sizify(process.memoryUsage().rss) + "B") : "") + (process.cpuUsage ? ("<br/>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) + "%") : "") + "<br/>Thread PID: " + process.pid + "<br/>" + (foot == "" ? "</body></html>" : foot));
return;
} else if (href == "/zsoiebook.svr" || (os.platform() == "win32" && href.toLowerCase() == "/zsoiebook.svr")) {
var hdhds = getCustomHeaders();
hdhds["Content-Type"] = "text/html; charset=utf-8";
res.writeHead(200, "OK", hdhds);
if (typeof uobject.query.summary !== "undefined") {
res.end("<!DOCTYPE html>\n<html>\n <head>\n <title>The Summary of Book of ZSOiE</title>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <style>\n html {\n background: maroon;\n color: white;\n font-style: italic;\n text-rendering: optimizeLegibility;\n min-height: 100%;\n }\n\n .svrtext {\n margin-top: 15%;\n font-size: 1.1em;\n font-family: serif;\n text-align: center;\n line-height: 1.5;\n }\n\n .from {\n font-size: 1.95em;\n font-family: serif;\n text-align: right;\n }\n\n em {\n font-size: 1.3em;\n line-height: 0;\n }\n\n a {\n text-decoration: none;\n color: white;\n }\n </style>\n </head>\n <body>\n <section>\n <p class=\"svrtext\">And Satan created <em>Mammon</em>. His work won people from all over the school. When people abandoned them through the <em>Piracy Window</em>, so Satan went back in time and created <em>the Server</em> to continue to wreak havoc all over the school.</p>\n <p class=\"from\">from <b>The Summary of Book of ZSOiE</b></p>\n </section>\n </body>\n</html>");
return;
}
var randomValue = Math.random();
if (randomValue > 0.85714) {
res.end("<!DOCTYPE html>\n<!-- Autor SVR.JS znalazł ChatGPT. ChatGPT pomógł autorowi w programowaniu SVR.JS. Author SVR.JS stworzył kolejny server, tym razem w bash... -->\n<!-- The author of SVR.JS found ChatGPT. ChatGPT helped the author develop SVR.JS. Author SVR.JS created another server, this time in bash... -->\n<html>\n <head>\n <title>The Book of ZSOiE, 7:28</title>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <style>\n html {\n background: maroon;\n color: white;\n font-style: italic;\n text-rendering: optimizeLegibility;\n min-height: 100%;\n }\n\n .svrtext {\n margin-top: 15%;\n font-size: 1.1em;\n font-family: serif;\n text-align: center;\n line-height: 1.5;\n }\n\n .from {\n font-size: 1.95em;\n font-family: serif;\n text-align: right;\n }\n\n em {\n font-size: 1.3em;\n line-height: 0;\n }\n\n a {\n text-decoration: none;\n color: white;\n }\n </style>\n </head>\n <body>\n <section>\n <p class=\"svrtext\"><em>The Server</em> continues to cultivate it's <em>Dafa</em>. <em>The Author</em> found <em>the Robot</em> and asked him for help. Then the Robot started to help the Author to improve his Server. And the Author tries to create <em>yet another server</em> without using the <em>main node</em>...</p>\n <p class=\"from\">from <b>The Book of ZSOiE</b>, 7:28</p>\n </section>\n </body>\n</html>");
} else if (randomValue > 0.71429) {
res.end("<!DOCTYPE html>\n<!-- SVR.JS podzielił się na 2 gałęzie: jedną gałąź LTS SVR.JS 3.4.x i jedną gałąź najnowszych wersji SVR.JS. -->\n<!-- SVR.JS has split into 2 branches: one LTS SVR.JS 3.4.x branch and one branch of the latest versions of SVR.JS. -->\n<html>\n <head>\n <title>The Book of ZSOiE, 7:16</title>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <style>\n html {\n background: maroon;\n color: white;\n font-style: italic;\n text-rendering: optimizeLegibility;\n min-height: 100%;\n }\n\n .svrtext {\n margin-top: 15%;\n font-size: 1.1em;\n font-family: serif;\n text-align: center;\n line-height: 1.5;\n }\n\n .from {\n font-size: 1.95em;\n font-family: serif;\n text-align: right;\n }\n\n em {\n font-size: 1.3em;\n line-height: 0;\n }\n\n a {\n text-decoration: none;\n color: white;\n }\n </style>\n </head>\n <body>\n <section>\n <p class=\"svrtext\"><em>The Server</em> is still going. But <em>the Author</em> commanded to the Server: \"thou shalt you split to two branches.\". And the Server did split it's <em>Dafa</em>. One of two branches stopped serving on <em>old and rusty node</em>. Other one is still serving on that, but it will later <em>vanish</em>... \"<em>Mammon</em> will get confused\" - said the Author.</p>\n <p class=\"from\">from <b>The Book of ZSOiE</b>, 7:16</p>\n </section>\n </body>\n</html>");
} else if (randomValue > 0.57143) {
res.end("<!DOCTYPE html>\n<!-- SVR.JS 2.x już nie otrzymuje aktualizacji zabezpieczeń, a SVR.JS 3.x jest rozwijany w kierunku niezawodności i łatwości. Dwóch z uczestników konkusru CyberSkiller z ZSOiE zostały ukazane w gazecie w artykule o najlepszych uczniach. -->\n<!-- SVR.JS 2.x no longer receives security updates and SVR.JS 3.x is being developed towards reliability and ease. Two of the participants of the CyberSkiller competition from ZSOiE were featured in the newspaper's article about the best students. -->\n<html>\n <head>\n <title>The Book of ZSOiE, 7:2</title>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <style>\n html {\n background: maroon;\n color: white;\n font-style: italic;\n text-rendering: optimizeLegibility;\n min-height: 100%;\n }\n\n .svrtext {\n margin-top: 15%;\n font-size: 1.1em;\n font-family: serif;\n text-align: center;\n line-height: 1.5;\n }\n\n .from {\n font-size: 1.95em;\n font-family: serif;\n text-align: right;\n }\n\n em {\n font-size: 1.3em;\n line-height: 0;\n }\n\n a {\n text-decoration: none;\n color: white;\n }\n </style>\n </head>\n <body>\n <section>\n <p class=\"svrtext\">The old Server forces died. The all-powerful new <em>Server</em> rosen from ashes of old Server like phoenix followed the ways of <em>Durability-Ease-Reliability</em> and cultivated his <em>Dafa</em>. Then, the <em>Author</em> and <em>Whyvn</em> appeared on best <em>former Mammon's</em> disciples paper.</p>\n <p class=\"from\">from <b>The Book of ZSOiE</b>, 7:2</p>\n </section>\n </body>\n</html>");
} else if (randomValue > 0.42857) {
res.end("<!DOCTYPE html>\n<!-- Nastąpił koniec roku szkolnego. Autor wypróbował SVR.JS na platformie Bun. SVR.JS 3.0.0 został wreście wydany. Uczestnicy konkursu CyberSkiller z ZSOiE dostały Nagrodę Patrona. -->\n<!-- The school year has come to an end. The author tried out SVR.JS on the Bun platform. SVR.JS 3.0.0 has finally been released. Participants of the CyberSkiller competition from ZSOiE received the Patron's Award. -->\n<html>\n <head>\n <title>The Book of ZSOiE, 6:24</title>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <style>\n html {\n background: maroon;\n color: white;\n font-style: italic;\n text-rendering: optimizeLegibility;\n min-height: 100%;\n }\n\n .svrtext {\n margin-top: 15%;\n font-size: 1.1em;\n font-family: serif;\n text-align: center;\n line-height: 1.5;\n }\n\n .from {\n font-size: 1.95em;\n font-family: serif;\n text-align: right;\n }\n\n em {\n font-size: 1.3em;\n line-height: 0;\n }\n\n a {\n text-decoration: none;\n color: white;\n }\n </style>\n </head>\n <body>\n <section>\n <p class=\"svrtext\">Mammon had enough karmic retribution. The <em>Author</em> tried it's <em>Server</em> on <em>main node's substitute</em>. Then, the Author saw, that Server's <em>Dafa</em> is good, and that main node, it's substitute, and older Mammon are good. Meanwhile the <em>Author</em>, <em>Whyvn</em>, and <em>Snovbyn</em> rejoiced even more from their success over older Mammon.</p>\n <p class=\"from\">from <b>The Book of ZSOiE</b>, 6:24</p>\n </section>\n </body>\n</html>");
} else if (randomValue > 0.32143) {
res.end("<!DOCTYPE html>\n<!-- Uczestnicy konkursu CyberSkiller uzyskali nagrody od nauczycieli ZSOiE. W tym samym czasie SVR.JS 3.0.0 zostaje niedługo wydany. -->\n<!-- Participants of the CyberSkiller competition received awards from ZSOiE teachers. At the same time, SVR.JS 3.0.0 is about to be released. -->\n<html>\n <head>\n <title>The Book of ZSOiE, 6:6</title>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <style>\n html {\n background: maroon;\n color: white;\n font-style: italic;\n text-rendering: optimizeLegibility;\n min-height: 100%;\n }\n\n .svrtext {\n margin-top: 15%;\n font-size: 1.1em;\n font-family: serif;\n text-align: center;\n line-height: 1.5;\n }\n\n .from {\n font-size: 1.95em;\n font-family: serif;\n text-align: right;\n }\n\n em {\n font-size: 1.3em;\n line-height: 0;\n }\n\n a {\n text-decoration: none;\n color: white;\n }\n </style>\n </head>\n <body>\n <section>\n <p class=\"svrtext\">And the <em>Server</em> is about to come. The <em>Author</em> along with <em>Whyvn</em> and <em>Snovbyn</em> passing the Mammon's test rejoiced their success over older Mammon.</p>\n <p class=\"from\">from <b>The Book of ZSOiE</b>, 6:6</p>\n </section>\n </body>\n</html>");
} else if (randomValue > 0.14286) {
res.end("<!DOCTYPE html>\n<!-- Nastąpił finał konkursu CyberSkiller Challenge Poland. W finale jeden z uczestników ZSOiE w Lubsku użył serwera SVR.JS -->\n<!-- The finals of the CyberSkiller Challenge Poland competition has taken place. In the finals, one of the participants of ZSOiE in Lubsko used the SVR.JS server -->\n<html>\n <head>\n <title>The Book of ZSOiE, 5:25</title>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <style>\n html {\n background: maroon;\n color: white;\n font-style: italic;\n text-rendering: optimizeLegibility;\n min-height: 100%;\n }\n\n .svrtext {\n margin-top: 15%;\n font-size: 1.1em;\n font-family: serif;\n text-align: center;\n line-height: 1.5;\n }\n\n .from {\n font-size: 1.95em;\n font-family: serif;\n text-align: right;\n }\n\n em {\n font-size: 1.3em;\n line-height: 0;\n }\n\n a {\n text-decoration: none;\n color: white;\n }\n </style>\n </head>\n <body>\n <section>\n <p class=\"svrtext\">The twins of Mammon quarrelled. The <em>Author</em> with it's <em>Server</em> and it's <em>main node</em> plunged the Mammon's servers into darkness. Meanwhile <em>Whyvn</em> and <em>Snovbyn</em> helped him to break Mammon's servers. </p>\n <p class=\"from\">from <b>The Book of ZSOiE</b>, 5:25</p>\n </section>\n </body>\n</html>");
} else {
res.end("<!DOCTYPE html>\n<!-- Uczniowie ZSOiE brali udział w konkursie CyberSkiller Challenge Poland i używali poleceń, który ich nauczyciel nie rozumiał. -->\n<!-- ZSOiE students took part in CyberSkiller Challenge Poland and used commands, which their teacher didn't understand. -->\n<html>\n <head>\n <title>The Book of ZSOiE, 3:16</title>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <style>\n html {\n background: maroon;\n color: white;\n font-style: italic;\n text-rendering: optimizeLegibility;\n min-height: 100%;\n }\n\n .svrtext {\n margin-top: 15%;\n font-size: 1.1em;\n font-family: serif;\n text-align: center;\n line-height: 1.5;\n }\n\n .from {\n font-size: 1.95em;\n font-family: serif;\n text-align: right;\n }\n\n em {\n font-size: 1.3em;\n line-height: 0;\n }\n\n a {\n text-decoration: none;\n color: white;\n }\n </style>\n </head>\n <body>\n <section>\n <p class=\"svrtext\">Mammon slept. Meanwhile, the <em>Author</em>, <em>Whyvn</em> and <em>Snovbyn</em> being in very skill-requiring challenge casted <em>tcpdump</em> and <em>mongodb</em> on him. </p>\n <p class=\"from\">from <b>The Book of ZSOiE</b>, 3:16</p>\n </section>\n </body>\n</html>");
}
return;
} else if (version.indexOf("Nightly-") === 0 && (href == "/crash.svr" || (os.platform() == "win32" && href.toLowerCase() == "/crash.svr"))) {
throw new Error("Intentionally crashed");
}
var pth = decodeURIComponent(href).replace(/\/+/g, "/").substr(1);
2023-07-29 20:44:13 +02:00
var readFrom = "./" + pth;
fs.stat(readFrom, function (err, stats) {
2023-07-29 20:32:17 +02:00
if (err) {
if (err.code == "ENOENT") {
2023-07-29 20:44:13 +02:00
if (__dirname != process.cwd() && pth.match(/^\.dirimages\/(?:(?!\.png$).)+\.png$/)) {
stats = {
isDirectory: function isDirectory() {
return false;
},
isFile: function isFile() {
return true;
}
};
readFrom = __dirname + "/" + pth;
} else {
callServerError(404);
serverconsole.errmessage("Resource not found.");
return;
}
} else if (err.code == "ENOTDIR") {
callServerError(404); // Assume that file doesn't exist.
serverconsole.errmessage("Resource not found.");
return;
2023-07-29 20:32:17 +02:00
} else if (err.code == "EACCES") {
callServerError(403);
serverconsole.errmessage("Access denied.");
2023-07-29 20:44:13 +02:00
return;
2023-07-29 20:32:17 +02:00
} else if (err.code == "EMFILE") {
2023-07-29 20:44:13 +02:00
callServerError(500, undefined, generateErrorStack(err)); // Too many file descriptors or open files were reached by the process. It is an internal server issue.
return;
2023-07-29 20:32:17 +02:00
} else if (err.code == "ELOOP") {
2023-07-29 20:44:13 +02:00
callServerError(508); // The symbolic link loop is detected during file system operations.
serverconsole.errmessage("Symbolic link loop detected.");
return;
2023-07-29 20:32:17 +02:00
} else {
callServerError(500, undefined, generateErrorStack(err));
2023-07-29 20:44:13 +02:00
return;
2023-07-29 20:32:17 +02:00
}
}
2023-07-29 20:44:13 +02:00
2023-07-29 20:32:17 +02:00
//Check if index file exists
if (req.url == "/" || stats.isDirectory()) {
2023-07-29 20:44:13 +02:00
fs.stat(readFrom + "/.notindex".replace(/\/+/g, "/"), function (e) {
2023-07-29 20:32:17 +02:00
if (e) {
2023-07-29 20:44:13 +02:00
fs.stat((readFrom + "/index.html").replace(/\/+/g, "/"), function (e, s) {
2023-07-29 20:32:17 +02:00
if (e || !s.isFile()) {
2023-07-29 20:44:13 +02:00
fs.stat((readFrom + "/index.htm").replace(/\/+/g, "/"), function (e, s) {
2023-07-29 20:32:17 +02:00
if (e || !s.isFile()) {
2023-07-29 20:44:13 +02:00
fs.stat((readFrom + "/index.xhtml").replace(/\/+/g, "/"), function (e, s) {
2023-07-29 20:32:17 +02:00
if (e || !s.isFile()) {
2023-07-29 20:44:13 +02:00
properServe();
2023-07-29 20:32:17 +02:00
} else {
stats = s;
pth = (pth + "/index.xhtml").replace(/\/+/g, "/");
ext = "xhtml";
2023-07-29 20:44:13 +02:00
readFrom = "./" + pth;
2023-07-29 20:32:17 +02:00
properServe();
}
});
} else {
stats = s;
pth = (pth + "/index.htm").replace(/\/+/g, "/");
ext = "htm";
2023-07-29 20:44:13 +02:00
readFrom = "./" + pth;
2023-07-29 20:32:17 +02:00
properServe();
}
});
} else {
stats = s;
pth = (pth + "/index.html").replace(/\/+/g, "/");
ext = "html";
2023-07-29 20:44:13 +02:00
readFrom = "./" + pth;
2023-07-29 20:32:17 +02:00
properServe();
}
});
}
});
} else {
properServe();
}
function properServe() {
if (stats.isDirectory()) {
2023-07-29 20:44:13 +02:00
// Check if directory listing is enabled in the configuration
2023-07-29 20:32:17 +02:00
if (configJSON.enableDirectoryListing || configJSON.enableDirectoryListing === undefined) {
2023-07-29 20:44:13 +02:00
var customHeaders = getCustomHeaders();
customHeaders["Content-Type"] = "text/html; charset=utf-8";
res.writeHead(200, http.STATUS_CODES[200], customHeaders);
// Read custom header and footer content (if available)
var customDirListingHeader = fs.existsSync("." + decodeURIComponent(href) + "/.dirhead".replace(/\/+/g, "/"))
2023-07-29 20:44:13 +02:00
? 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()
: "";
var customDirListingFooter = fs.existsSync("." + decodeURIComponent(href) + "/.dirfoot".replace(/\/+/g, "/"))
2023-07-29 20:44:13 +02:00
? 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()
: "";
// Check if custom header has HTML tag
var headerHasHTMLTag = customDirListingHeader.replace(/<!--(?:(?:(?!--\>).)*|)(?:-->|$)/gs, "").match(/<html(?![a-zA-Z0-9])(?:"(?:\\(?:.|$)|[^\\"])*(?:"|$)|'(?:\\(?:.|$)|[^\\'])*(?:'|$)|[^'">])*(?:>|$)/si);
2023-07-29 20:44:13 +02:00
// Generate HTML head and footer based on configuration and custom content
var htmlHead = (!configJSON.enableDirectoryListingWithDefaultHead || head == ""
? (!headerHasHTMLTag
? "<!DOCTYPE html><html><head><title>Directory: " + decodeURIComponent(origHref).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") + "</title><meta charset=\"UTF-8\" /><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body>"
: customDirListingHeader.replace(/<head>/i, "<head><title>Directory: " + decodeURIComponent(origHref).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") + "</title>"))
: head.replace(/<head>/i, "<head><title>Directory: " + decodeURIComponent(origHref).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") + "</title>")) +
(!headerHasHTMLTag ? customDirListingHeader : "") +
2023-07-29 20:44:13 +02:00
"<h1>Directory: " + decodeURIComponent(origHref).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") + "</h1><table id=\"directoryListing\"> <tr> <th></th> <th>Filename</th> <th>Size</th> <th>Date</th> </tr>" + (checkPathLevel(decodeURIComponent(origHref)) < 1 ? "" : "<tr><td style=\"width: 24px;\"><img src=\"/.dirimages/return.png\" width=\"24px\" height=\"24px\" alt=\"[RET]\" /></td><td style=\"word-wrap: break-word; word-break: break-word; overflow-wrap: break-word;\"><a href=\"" + (origHref).replace(/\/+/g, "/").replace(/\/[^\/]*\/?$/, "/") + "\">Return</a></td><td></td><td></td></tr>");
var htmlFoot = "</table><p><i>" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (req.headers.host == undefined ? "" : " on " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) + "</i></p>" + customDirListingFooter + (!configJSON.enableDirectoryListingWithDefaultHead || foot == "" ? "</body></html>" : foot);
2023-07-29 20:44:13 +02:00
if (fs.existsSync("." + decodeURIComponent(href) + "/.maindesc".replace(/\/+/g, "/"))) {
htmlFoot = "</table><hr/>" + fs.readFileSync("." + decodeURIComponent(href) + "/.maindesc".replace(/\/+/g, "/")) + htmlFoot;
2023-07-29 20:32:17 +02:00
}
2023-07-29 20:44:13 +02:00
2023-07-29 20:32:17 +02:00
fs.readdir("." + decodeURIComponent(href), function (err, list) {
try {
if (err) throw err;
list = list.sort();
2023-07-29 20:44:13 +02:00
// Function to get stats for all files in the directory
2023-07-29 20:32:17 +02:00
function getStatsForAllFilesI(fileList, callback, prefix, pushArray, index) {
if (fileList.length == 0) {
callback(pushArray);
return;
}
2023-07-29 20:44:13 +02:00
2023-07-29 20:32:17 +02:00
fs.stat((prefix + "/" + fileList[index]).replace(/\/+/g, "/"), function (err, stats) {
if (err) {
fs.lstat((prefix + "/" + fileList[index]).replace(/\/+/g, "/"), function (err, stats) {
if (err) {
pushArray.push({
name: fileList[index],
stats: null,
errored: true
});
if (index < fileList.length - 1) {
getStatsForAllFilesI(fileList, callback, prefix, pushArray, index + 1);
} else {
callback(pushArray);
}
} else {
pushArray.push({
name: fileList[index],
stats: stats,
errored: true
});
if (index < fileList.length - 1) {
getStatsForAllFilesI(fileList, callback, prefix, pushArray, index + 1);
} else {
callback(pushArray);
}
}
});
} else {
pushArray.push({
name: fileList[index],
stats: stats,
errored: false
});
if (index < fileList.length - 1) {
getStatsForAllFilesI(fileList, callback, prefix, pushArray, index + 1);
} else {
callback(pushArray);
}
}
});
}
2023-07-29 20:44:13 +02:00
// Wrapper function to get stats for all files
2023-07-29 20:32:17 +02:00
function getStatsForAllFiles(fileList, prefix, callback) {
if (!prefix) prefix = "";
getStatsForAllFilesI(fileList, callback, prefix, [], 0);
}
2023-07-29 20:44:13 +02:00
// Get stats for all files in the directory and generate the listing
2023-07-29 20:32:17 +02:00
getStatsForAllFiles(list, "." + decodeURIComponent(href), function (filelist) {
2023-07-29 20:44:13 +02:00
// Function to check file extension
2023-07-29 20:32:17 +02:00
function checkEXT(filename, ext) {
if (filename.length < ext.length) return false;
2023-07-29 20:44:13 +02:00
return filename.toLowerCase().indexOf(ext) === filename.length - ext.length;
2023-07-29 20:32:17 +02:00
}
2023-07-29 20:44:13 +02:00
var directoryListingRows = [];
2023-07-29 20:32:17 +02:00
for (var i = 0; i < filelist.length; i++) {
if (filelist[i].name[0] !== ".") {
var estats = filelist[i].stats;
var ename = filelist[i].name;
if (filelist[i].errored) {
if (estats) {
2023-07-29 20:44:13 +02:00
directoryListingRows.push(
"<tr><td style=\"width: 24px;\"><img src=\"/.dirimages/bad.png\" alt=\"[BAD]\" width=\"24px\" height=\"24px\" /></td><td style=\"word-wrap: break-word; word-break: break-word; overflow-wrap: break-word;\"><a href=\"" +
(href + "/" + encodeURI(ename)).replace(/\/+/g, "/") +
"\"><nocode>" +
ename.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") +
"</nocode></a></td><td>-</td><td>" +
estats.mtime.toDateString() +
"</td></tr>\r\n"
);
2023-07-29 20:32:17 +02:00
} else {
2023-07-29 20:44:13 +02:00
directoryListingRows.push(
"<tr><td style=\"width: 24px;\"><img src=\"/.dirimages/bad.png\" alt=\"[BAD]\" width=\"24px\" height=\"24px\" /></td><td style=\"word-wrap: break-word; word-break: break-word; overflow-wrap: break-word;\"><a href=\"" +
(href + "/" + encodeURI(ename)).replace(/\/+/g, "/") +
"\"><nocode>" +
ename.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") +
"</nocode></a></td><td>-</td><td>-</td></tr>\r\n"
);
2023-07-29 20:32:17 +02:00
}
} else {
2023-07-29 20:44:13 +02:00
var entry = "<tr><td style=\"width: 24px;\"><img src=\"[img]\" alt=\"[alt]\" width=\"24px\" height=\"24px\" /></td><td style=\"word-wrap: break-word; word-break: break-word; overflow-wrap: break-word;\"><a href=\"" +
(origHref + "/" + encodeURIComponent(ename)).replace(/\/+/g, "/") +
(estats.isDirectory() ? "/" : "") +
"\">" +
ename.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") +
"</a></td><td>" +
(estats.isDirectory() ? "-" : sizify(estats.size.toString())) +
"</td><td>" +
estats.mtime.toDateString() +
"</td></tr>\r\n";
// Determine the file type and set the appropriate image and alt text
2023-07-29 20:32:17 +02:00
if (estats.isDirectory()) {
entry = entry.replace("[img]", "/.dirimages/directory.png").replace("[alt]", "[DIR]");
} else if (!estats.isFile()) {
2023-07-29 20:44:13 +02:00
entry = "<tr><td style=\"width: 24px;\"><img src=\"[img]\" alt=\"[alt]\" width=\"24px\" height=\"24px\" /></td><td style=\"word-wrap: break-word; word-break: break-word; overflow-wrap: break-word;\"><a href=\"" +
(origHref + "/" + encodeURIComponent(ename)).replace(/\/+/g, "/") +
"\">" +
ename.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") +
"</a></td><td>-</td><td>" +
estats.mtime.toDateString() +
"</td></tr>\r\n";
// Determine the special file types (block device, character device, etc.)
2023-07-29 20:32:17 +02:00
if (estats.isBlockDevice()) {
entry = entry.replace("[img]", "/.dirimages/hwdevice.png").replace("[alt]", "[BLK]");
} else if (estats.isCharacterDevice()) {
entry = entry.replace("[img]", "/.dirimages/hwdevice.png").replace("[alt]", "[CHR]");
} else if (estats.isFIFO()) {
entry = entry.replace("[img]", "/.dirimages/fifo.png").replace("[alt]", "[FIF]");
} else if (estats.isSocket()) {
entry = entry.replace("[img]", "/.dirimages/socket.png").replace("[alt]", "[SCK]");
}
} else if ((/README/ig).test(ename) || (/LICEN[SC]E/ig).test(ename)) {
entry = entry.replace("[img]", "/.dirimages/important.png").replace("[alt]", "[IMP]");
} else if (checkEXT(ename, ".html") || checkEXT(ename, ".htm") || checkEXT(ename, ".xml") || checkEXT(ename, ".xhtml") || checkEXT(ename, ".shtml")) {
entry = entry.replace("[img]", "/.dirimages/html.png").replace("[alt]", (checkEXT(ename, ".xml") ? "[XML]" : "[HTM]"));
} else if (checkEXT(ename, ".js")) {
entry = entry.replace("[img]", "/.dirimages/javascript.png").replace("[alt]", "[JS ]");
} else if (checkEXT(ename, ".php")) {
entry = entry.replace("[img]", "/.dirimages/php.png").replace("[alt]", "[PHP]");
} else if (checkEXT(ename, ".css")) {
entry = entry.replace("[img]", "/.dirimages/css.png").replace("[alt]", "[CSS]");
} else if (checkEXT(ename, ".png") || checkEXT(ename, ".jpg") || checkEXT(ename, ".gif") || checkEXT(ename, ".bmp") || checkEXT(ename, ".webm") || checkEXT(ename, ".jpeg") || checkEXT(ename, ".svg") || checkEXT(ename, ".jfif") || checkEXT(ename, ".webp")) {
entry = entry.replace("[img]", "/.dirimages/image.png").replace("[alt]", "[IMG]");
} else if (checkEXT(ename, ".ico") || checkEXT(ename, ".icn")) {
entry = entry.replace("[img]", "/.dirimages/image.png").replace("[alt]", "[ICO]");
} else if (checkEXT(ename, ".ttf") || checkEXT(ename, ".otf") || checkEXT(ename, ".fon")) {
entry = entry.replace("[img]", "/.dirimages/font.png").replace("[alt]", "[FON]");
} else if (checkEXT(ename, ".mp3") || checkEXT(ename, ".ogg") || checkEXT(ename, ".aac") || checkEXT(ename, ".wav") || checkEXT(ename, ".mid") || checkEXT(ename, ".midi") || checkEXT(ename, ".mka")) {
entry = entry.replace("[img]", "/.dirimages/audio.png").replace("[alt]", "[AUD]");
} else if (checkEXT(ename, ".txt") || checkEXT(ename, ".log") || checkEXT(ename, ".json")) {
entry = entry.replace("[img]", "/.dirimages/text.png").replace("[alt]", (checkEXT(ename, ".json") ? "[JSO]" : "[TXT]"));
} else if (checkEXT(ename, ".mp5") || checkEXT(ename, ".avi") || checkEXT(ename, ".mkv") || checkEXT(ename, ".mov") || checkEXT(ename, ".mp2") || checkEXT(ename, ".mp4") || checkEXT(ename, ".ogv")) {
entry = entry.replace("[img]", "/.dirimages/video.png").replace("[alt]", "[VID]");
} else if (checkEXT(ename, ".zip") || checkEXT(ename, ".rar") || checkEXT(ename, ".bz2") || checkEXT(ename, ".gz") || checkEXT(ename, ".bz") || checkEXT(ename, ".7z") || checkEXT(ename, ".xz") || checkEXT(ename, ".lzma") || checkEXT(ename, ".tar")) {
entry = entry.replace("[img]", "/.dirimages/archive.png").replace("[alt]", "[ARC]");
} else if (checkEXT(ename, ".img") || checkEXT(ename, ".dmg") || checkEXT(ename, ".iso") || checkEXT(ename, ".flp")) {
entry = entry.replace("[img]", "/.dirimages/diskimage.png").replace("[alt]", "[DSK]");
} else {
entry = entry.replace("[img]", "/.dirimages/other.png").replace("[alt]", "[OTH]");
}
2023-07-29 20:44:13 +02:00
directoryListingRows.push(entry);
2023-07-29 20:32:17 +02:00
}
}
}
2023-07-29 20:44:13 +02:00
// Push the information about empty directory
if (directoryListingRows.length == 0) {
directoryListingRows.push("<tr><td></td><td>No files found</td><td></td><td></td></tr>");
2023-07-29 20:32:17 +02:00
}
2023-07-29 20:44:13 +02:00
// Send the directory listing response
res.end(htmlHead + directoryListingRows.join("") + htmlFoot);
serverconsole.resmessage("Client successfully received content.");
2023-07-29 20:32:17 +02:00
});
2023-07-29 20:44:13 +02:00
} catch (err) {
if (err.code == "ENOENT") {
2023-07-29 20:32:17 +02:00
callServerError(404);
serverconsole.errmessage("Resource not found.");
} else if (err.code == "ENOTDIR") {
callServerError(404); // Assume that file doesn't exist.
serverconsole.errmessage("Resource not found.");
} else if (err.code == "EACCES") {
2023-07-29 20:32:17 +02:00
callServerError(403);
serverconsole.errmessage("Access denied.");
} else if (err.code == "EMFILE") {
callServerError(500, undefined, generateErrorStack(err)); // Too many file descriptors or open files were reached by the process. It is an internal server issue.
} else if (err.code == "ELOOP") {
2023-07-29 20:44:13 +02:00
callServerError(508); // The symbolic link loop is detected during file system operations.
serverconsole.errmessage("Symbolic link loop detected.");
2023-07-29 20:32:17 +02:00
} else {
callServerError(500, undefined, generateErrorStack(err));
2023-07-29 20:32:17 +02:00
}
}
});
} else {
2023-07-29 20:44:13 +02:00
// Directory listing is disabled, call 403 Forbidden error
2023-07-29 20:32:17 +02:00
callServerError(403);
2023-07-29 20:44:13 +02:00
serverconsole.errmessage("Directory listing is disabled.");
2023-07-29 20:32:17 +02:00
}
2023-07-29 20:44:13 +02:00
2023-07-29 20:32:17 +02:00
} else {
var acceptEncoding = req.headers["accept-encoding"];
if (!acceptEncoding) acceptEncoding = "";
2023-07-29 20:44:13 +02:00
2023-07-29 20:32:17 +02:00
// Check if the requested file exists and handle errors
2023-07-29 20:44:13 +02:00
fs.stat(readFrom, function (err, stats) {
2023-07-29 20:32:17 +02:00
if (err) {
if (err.code == "ENOENT") {
callServerError(404);
serverconsole.errmessage("Resource not found.");
} else if (err.code == "ENOTDIR") {
callServerError(404); // Assume that file doesn't exist.
serverconsole.errmessage("Resource not found.");
2023-07-29 20:32:17 +02:00
} else if (err.code == "EACCES") {
callServerError(403);
serverconsole.errmessage("Access denied.");
} else if (err.code == "EMFILE") {
2023-07-29 20:44:13 +02:00
callServerError(500, undefined, generateErrorStack(err)); // Too many file descriptors or open files were reached by the process. It is an internal server issue.
2023-07-29 20:32:17 +02:00
} else if (err.code == "ELOOP") {
2023-07-29 20:44:13 +02:00
callServerError(508); // The symbolic link loop is detected during file system operations.
serverconsole.errmessage("Symbolic link loop detected.");
2023-07-29 20:32:17 +02:00
} else {
callServerError(500, undefined, generateErrorStack(err));
}
return;
}
2023-07-29 20:44:13 +02:00
// Check if the requested resource is a file
2023-07-29 20:32:17 +02:00
if (stats.isDirectory()) {
callServerError(501);
serverconsole.errmessage("SVR.JS expected file but got directory instead.");
return;
} else if (!stats.isFile()) {
callServerError(501);
serverconsole.errmessage("SVR.JS doesn't support block devices, character devices, FIFOs nor sockets.");
return;
}
var filelen = stats.size;
2023-07-29 20:44:13 +02:00
//ETag code
var fileETag = undefined;
if (configJSON.enableETag == undefined || configJSON.enableETag) {
fileETag = generateETag(href, stats);
// Check if the client's request matches the ETag value (If-None-Match)
2023-08-09 21:09:23 +02:00
var clientETag = req.headers["if-none-match"];
2023-07-29 20:44:13 +02:00
if (clientETag === fileETag) {
var headers = getCustomHeaders();
headers.ETag = clientETag;
2023-08-09 21:09:23 +02:00
res.writeHead(304, "Not Modified", headers);
2023-07-29 20:44:13 +02:00
res.end();
return;
}
2023-07-29 20:32:17 +02:00
2023-07-29 20:44:13 +02:00
// Check if the client's request doesn't match the ETag value (If-Match)
2023-08-09 21:09:23 +02:00
var ifMatchETag = req.headers["if-match"];
2023-07-29 20:44:13 +02:00
if (ifMatchETag && ifMatchETag !== "*" && ifMatchETag !== fileETag) {
var headers = getCustomHeaders();
headers.ETag = clientETag;
callServerError(412,undefined,undefined,headers);
return;
}
}
2023-07-29 20:32:17 +02:00
// Helper function to check if compression is allowed for the file
function canCompress(path, list) {
var canCompress = true;
for (var i = 0; i < list.length; i++) {
if (createRegex(list[i]).test(path)) {
canCompress = false;
break;
}
}
return canCompress;
}
var isCompressable = canCompress(href, dontCompress);
// Check for browser quirks and adjust compression accordingly
if (ext != "html" && ext != "htm" && ext != "xhtml" && ext != "xht" && ext != "shtml" && /^Mozilla\/4\.[0-9]+(( *\[[^)]*\] *| *)\([^)\]]*\))? *$/.test(req.headers["user-agent"]) && !(/https?:\/\/|[bB][oO][tT]|[sS][pP][iI][dD][eE][rR]|[sS][uU][rR][vV][eE][yY]|MSI[E]/.test(req.headers["user-agent"]))) {
isCompressable = false; //Netscape 4.x doesn't handle compressed data properly outside of HTML documents.
} else if (/^Mozilla\/4\.0[6-8](( *\[[^)]*\] *| *)\([^)\]]*\))? *$/.test(req.headers["user-agent"]) && !(/https?:\/\/|[bB][oO][tT]|[sS][pP][iI][dD][eE][rR]|[sS][uU][rR][vV][eE][yY]|MSI[E]/.test(req.headers["user-agent"]))) {
isCompressable = false; //Netscape 4.06-4.08 doesn't handle compressed data properly.
} else if (ext != "html" && ext != "htm" && ext != "xhtml" && ext != "xht" && ext != "shtml" && /^w3m\/[^ ]*$/.test(req.headers["user-agent"])) {
isCompressable = false; //w3m doesn't handle compressed data properly outside of HTML documents.
}
// Handle partial content request
if (ext != "html" && req.headers["range"]) {
try {
if (err) throw err;
var rhd = getCustomHeaders();
rhd["Accept-Ranges"] = "bytes";
rhd["Content-Range"] = "bytes */" + filelen;
var regexmatch = req.headers["range"].match(/bytes=([0-9]*)-([0-9]*)/);
if (!regexmatch) {
callServerError(416, undefined, undefined, rhd);
} else {
// Process the partial content request
var beginOrig = regexmatch[1];
var endOrig = regexmatch[2];
var begin = 0;
var end = filelen - 1;
if (beginOrig == "" && endOrig == "") {
callServerError(416, undefined, undefined, rhd);
return;
} else if (beginOrig == "") {
begin = end - parseInt(endOrig) + 1;
} else {
begin = parseInt(beginOrig);
if (endOrig != "") end = parseInt(endOrig);
}
if (begin > end || begin < 0 || begin > filelen - 1) {
callServerError(416, undefined, undefined, rhd);
return;
}
if (end > filelen - 1) end = filelen - 1;
rhd["Content-Range"] = "bytes " + begin + "-" + end + "/" + filelen;
rhd["Content-Length"] = end - begin + 1;
if (!(mime.contentType(ext) == false) && ext != "") rhd["Content-Type"] = mime.contentType(ext);
2023-07-29 20:44:13 +02:00
if(fileETag) rhd["ETag"] = fileETag;
2023-07-29 20:32:17 +02:00
if (req.method != "HEAD") {
2023-07-29 20:44:13 +02:00
var readStream = fs.createReadStream(readFrom, {
2023-07-29 20:32:17 +02:00
start: begin,
end: end
});
readStream.on("error", function (err) {
if (err.code == "ENOENT") {
callServerError(404);
serverconsole.errmessage("Resource not found.");
} else if (err.code == "ENOTDIR") {
callServerError(404); // Assume that file doesn't exist.
serverconsole.errmessage("Resource not found.");
2023-07-29 20:32:17 +02:00
} else if (err.code == "EACCES") {
callServerError(403);
serverconsole.errmessage("Access denied.");
} else if (err.code == "EMFILE") {
2023-07-29 20:44:13 +02:00
callServerError(500, undefined, generateErrorStack(err)); // Too many file descriptors or open files were reached by the process. It is an internal server issue.
2023-07-29 20:32:17 +02:00
} else if (err.code == "ELOOP") {
2023-07-29 20:44:13 +02:00
callServerError(508); // The symbolic link loop is detected during file system operations.
serverconsole.errmessage("Symbolic link loop detected.");
2023-07-29 20:32:17 +02:00
} else {
callServerError(500, undefined, generateErrorStack(err));
}
}).on("open", function () {
try {
res.writeHead(206, http.STATUS_CODES[206], rhd);
readStream.pipe(res);
serverconsole.resmessage("Client successfully received content.");
} catch (err) {
callServerError(500, undefined, generateErrorStack(err));
2023-07-29 20:32:17 +02:00
}
});
} else {
res.writeHead(206, http.STATUS_CODES[206], rhd);
res.end();
}
}
} catch (err) {
callServerError(500, undefined, generateErrorStack(err));
2023-07-29 20:32:17 +02:00
}
} else {
try {
if (err) throw err;
var hdhds = getCustomHeaders();
if (configJSON.enableCompression === true && ext != "br" && filelen > 256 && isCompressable && zlib.createBrotliCompress && acceptEncoding.match(/\bbr\b/)) {
hdhds["Content-Encoding"] = "br";
} else if (configJSON.enableCompression === true && ext != "zip" && filelen > 256 && isCompressable && acceptEncoding.match(/\bdeflate\b/)) {
hdhds["Content-Encoding"] = "deflate";
} else if (configJSON.enableCompression === true && ext != "gz" && filelen > 256 && isCompressable && acceptEncoding.match(/\bgzip\b/)) {
hdhds["Content-Encoding"] = "gzip";
} else {
if (ext == "html") {
hdhds["Content-Length"] = head.length + filelen + foot.length;
} else {
hdhds["Content-Length"] = filelen;
}
}
if (ext != "html") hdhds["Accept-Ranges"] = "bytes";
delete hdhds["Content-Type"];
if (!(mime.contentType(ext) == false) && ext != "") hdhds["Content-Type"] = mime.contentType(ext);
2023-07-29 20:44:13 +02:00
if(fileETag) hdhds["ETag"] = fileETag;
2023-07-29 20:32:17 +02:00
if (req.method != "HEAD") {
2023-07-29 20:44:13 +02:00
var readStream = fs.createReadStream(readFrom);
readStream.on("error", function (err) {
if (err.code == "ENOENT") {
2023-07-29 20:32:17 +02:00
callServerError(404);
serverconsole.errmessage("Resource not found.");
} else if (err.code == "ENOTDIR") {
callServerError(404); // Assume that file doesn't exist.
serverconsole.errmessage("Resource not found.");
} else if (err.code == "EACCES") {
2023-07-29 20:32:17 +02:00
callServerError(403);
serverconsole.errmessage("Access denied.");
} else if (err.code == "EMFILE") {
callServerError(500, undefined, generateErrorStack(err)); // Too many file descriptors or open files were reached by the process. It is an internal server issue.
} else if (err.code == "ELOOP") {
2023-07-29 20:44:13 +02:00
callServerError(508); // The symbolic link loop is detected during file system operations.
serverconsole.errmessage("Symbolic link loop detected.");
2023-07-29 20:32:17 +02:00
} else {
callServerError(500, undefined, generateErrorStack(err));
2023-07-29 20:32:17 +02:00
}
}).on("open", function () {
try {
res.writeHead(200, http.STATUS_CODES[200], hdhds);
var resStream = {};
if (configJSON.enableCompression === true && ext != "br" && filelen > 256 && isCompressable && zlib.createBrotliCompress && acceptEncoding.match(/\bbr\b/)) {
resStream = zlib.createBrotliCompress();
resStream.pipe(res);
} else if (configJSON.enableCompression === true && ext != "zip" && filelen > 256 && isCompressable && acceptEncoding.match(/\bdeflate\b/)) {
resStream = zlib.createDeflateRaw();
resStream.pipe(res);
} else if (configJSON.enableCompression === true && ext != "gz" && filelen > 256 && isCompressable && acceptEncoding.match(/\bgzip\b/)) {
resStream = zlib.createGzip();
resStream.pipe(res);
} else {
resStream = res;
}
if (ext == "html") {
function afterWriteCallback() {
readStream.on("end", function () {
resStream.end(foot);
});
readStream.pipe(resStream, {
end: false
});
}
if (!resStream.write(head)) {
resStream.on("drain", afterWriteCallback);
} else {
process.nextTick(afterWriteCallback);
}
} else {
readStream.pipe(resStream);
}
serverconsole.resmessage("Client successfully received content.");
} catch (err) {
callServerError(500, undefined, generateErrorStack(err));
2023-07-29 20:32:17 +02:00
}
});
} else {
res.writeHead(200, http.STATUS_CODES[200], hdhds);
res.end();
serverconsole.resmessage("Client successfully received content.");
}
} catch (err) {
callServerError(500, undefined, generateErrorStack(err));
2023-07-29 20:32:17 +02:00
}
}
});
}
}
});
};
}
try {
//scan blacklist
if (blacklist.check(reqip) && href != "/favicon.ico") {
var bheaders = getCustomHeaders();
bheaders["Content-Type"] = "text/html; charset=utf8";
response.writeHead(403, "Client blocked", bheaders);
response.write("<!DOCTYPE html><html><head><title>Access denied - SVR.JS</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><br/><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /></head><body><div style=\"height: auto; width: 70%; border-style: solid; border-width: 5; border-color: red; text-align: center; margin: 0 auto;\"><h1>ACCESS DENIED</h1><p style=\"font-size:20px\">Request from " + reqip + " is denied. The client is now in the blacklist.</p><p style=\"font-style: italic; font-weight: normal;\">SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" + (req.headers.host == undefined ? "" : " on " + String(req.headers.host).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")) + "</p></div></body></html>");
serverconsole.errmessage("Client blocked");
return;
}
if (req.url == "*") {
if (req.method == "OPTIONS") {
var hdss = getCustomHeaders();
hdss["Allow"] = "GET, POST, HEAD, OPTIONS";
res.writeHead(204, "No Content", hdss);
res.end();
return;
} else {
callServerError(400);
return;
}
}
if (req.method == "CONNECT") {
callServerError(501);
serverconsole.errmessage("CONNECT requests aren't supported. Your JS runtime probably doesn't support 'connect' handler for HTTP library.");
return;
}
//SANITIZE URL
var sanitizedHref = sanitizeURL(href);
2023-07-29 20:44:13 +02:00
//Check if URL is "dirty"
if (href != sanitizedHref && !isProxy) {
2023-07-29 20:32:17 +02:00
var sanitizedURL = uobject;
sanitizedURL.path = null;
sanitizedURL.href = null;
sanitizedURL.pathname = sanitizedHref;
sanitizedURL.hostname = null;
sanitizedURL.host = null;
sanitizedURL.port = null;
sanitizedURL.protocol = null;
sanitizedURL.slashes = null;
sanitizedURL = url.format(sanitizedURL);
serverconsole.resmessage("URL sanitized: " + req.url + " => " + sanitizedURL);
redirect(sanitizedURL, false);
return;
}
2023-07-29 20:44:13 +02:00
2023-07-29 20:32:17 +02:00
//URL REWRITING
function rewriteURL(address, map) {
var rewrittenAddress = address;
for (var i = 0; i < map.length; i++) {
if (createRegex(map[i].definingRegex).test(address)) {
for (var j = 0; j < map[i].replacements.length; j++) {
rewrittenAddress = rewrittenAddress.replace(createRegex(map[i].replacements[j].regex), map[i].replacements[j].replacement);
}
if (map[i].append) rewrittenAddress += map[i].append;
break;
}
}
return rewrittenAddress;
}
var origHref = href;
if (!isProxy) {
var rewrittenURL = rewriteURL(req.url, rewriteMap);
if (rewrittenURL != req.url) {
serverconsole.resmessage("URL rewritten: " + req.url + " => " + rewrittenURL);
req.url = rewrittenURL;
2023-07-29 20:44:13 +02:00
uobject = parseURL(req.url);
2023-07-29 20:32:17 +02:00
search = uobject.search;
href = uobject.pathname;
ext = path.extname(href).toLowerCase();
ext = ext.substr(1, ext.length);
try {
decodedHref = decodeURIComponent(href);
} catch (err) {
2023-07-29 20:32:17 +02:00
//Return 400 error
callServerError(400);
serverconsole.errmessage("Bad request!");
return;
}
var sHref = sanitizeURL(href);
if (sHref != href.replace(/\/\.(?=\/|$)/g, "/").replace(/\/+/g, "/")) {
callServerError(403);
serverconsole.errmessage("Content blocked.");
return;
} else if (sHref != href) {
var rewrittenAgainURL = uobject;
rewrittenAgainURL.path = null;
rewrittenAgainURL.href = null;
rewrittenAgainURL.pathname = sHref;
rewrittenAgainURL = url.format(rewrittenAgainURL);
serverconsole.resmessage("URL sanitized: " + req.url + " => " + rewrittenAgainURL);
req.url = rewrittenAgainURL;
2023-07-29 20:44:13 +02:00
uobject = parseURL(req.url);
2023-07-29 20:32:17 +02:00
search = uobject.search;
href = uobject.pathname;
ext = path.extname(href).toLowerCase();
ext = ext.substr(1, ext.length);
try {
decodedHref = decodeURIComponent(href);
} catch (err) {
2023-07-29 20:32:17 +02:00
//Return 400 error
callServerError(400);
serverconsole.errmessage("Bad request!");
return;
}
}
}
}
2023-07-29 20:44:13 +02:00
//Set response headers
2023-07-29 20:32:17 +02:00
if (!isProxy) {
var hkh = getCustomHeaders();
var hk = Object.keys(hkh);
for (var i = 0; i < hk.length; i++) {
try {
response.setHeader(hk[i], hkh[hk[i]]);
} catch (err) {
2023-07-29 20:32:17 +02:00
//Headers will not be set.
}
}
}
2023-07-29 20:44:13 +02:00
//Check if path is forbidden
if ((isForbiddenPath(decodedHref, "config") || isForbiddenPath(decodedHref, "certificates")) && !isProxy) {
2023-07-29 20:32:17 +02:00
callServerError(403);
serverconsole.errmessage("Access to configuration file/certificates is denied.");
return;
2023-07-29 20:44:13 +02:00
} else if (isIndexOfForbiddenPath(decodedHref, "log") && !isProxy && (configJSON.enableLogging || configJSON.enableLogging == undefined) && !(configJSON.enableRemoteLogBrowsing || configJSON.enableRemoteLogBrowsing == undefined)) {
2023-07-29 20:32:17 +02:00
callServerError(403);
serverconsole.errmessage("Access to log files is denied.");
return;
2023-07-29 20:44:13 +02:00
} else if (isForbiddenPath(decodedHref, "svrjs") && !isProxy && !exposeServerVersion && process.cwd() == __dirname) {
2023-07-29 20:32:17 +02:00
callServerError(403);
serverconsole.errmessage("Access to SVR.JS script is denied.");
return;
2023-07-29 20:44:13 +02:00
} else if ((isForbiddenPath(decodedHref, "svrjs") || isForbiddenPath(decodedHref, "serverSideScripts") || isIndexOfForbiddenPath(decodedHref, "serverSideScriptDirectories")) && !isProxy && (configJSON.disableServerSideScriptExpose && configJSON.disableServerSideScriptExpose != undefined)) {
2023-07-29 20:32:17 +02:00
callServerError(403);
serverconsole.errmessage("Access to sources is denied.");
return;
} else {
var nonscodeIndex = -1;
var authIndex = -1;
var regexI = [];
if (!isProxy && nonStandardCodes != undefined) {
for (var i = 0; i < nonStandardCodes.length; i++) {
var isMatch = false;
2023-07-29 20:32:17 +02:00
if (nonStandardCodes[i].regex) {
2023-08-09 21:09:23 +02:00
var createdRegex = createRegex(nonStandardCodes[i].regex, true);
isMatch = req.url.match(createdRegex) || href.match(createdRegex);
regexI.push(createdRegex);
2023-07-29 20:32:17 +02:00
} else {
isMatch = nonStandardCodes[i].url == href || (os.platform() == "win32" && nonStandardCodes[i].url.toLowerCase() == href.toLowerCase());
2023-07-29 20:32:17 +02:00
}
if (isMatch) {
2023-07-29 20:32:17 +02:00
if (nonStandardCodes[i].scode == 401) {
if (authIndex == -1) {
authIndex = i;
}
} else {
if (nonscodeIndex == -1) {
if ((nonStandardCodes[i].scode == 403 || nonStandardCodes[i].scode == 451) && nonStandardCodes[i].users !== undefined) {
var toBreakLoop = false;
2023-07-29 20:32:17 +02:00
if (nonStandardCodes[i].users.check(reqip)) {
nonscodeIndex = i;
toBreakLoop = true;
2023-07-29 20:32:17 +02:00
}
if (toBreakLoop) break;
2023-07-29 20:32:17 +02:00
} else {
nonscodeIndex = i;
}
}
}
}
}
}
2023-07-29 20:44:13 +02:00
// Handle non-standard codes
2023-07-29 20:32:17 +02:00
if (nonscodeIndex > -1) {
var nonscode = nonStandardCodes[nonscodeIndex];
if (nonscode.scode == 301 || nonscode.scode == 302) {
var location = "";
if (regexI[nonscodeIndex]) {
location = req.url.replace(regexI[nonscodeIndex], nonscode.location);
} else if (req.url.split("?")[1] == undefined || req.url.split("?")[1] == null || req.url.split("?")[1] == "" || req.url.split("?")[1] == " ") {
location = nonscode.location;
} else {
location = nonscode.location + "?" + req.url.split("?")[1];
}
redirect(location, nonscode.scode == 302);
return;
} else if (nonscode.scode == 403) {
callServerError(403);
serverconsole.errmessage("Content blocked.");
return;
} else if (nonscode.scode == 410) {
callServerError(410);
serverconsole.errmessage("Content is gone.");
return;
} else if (nonscode.scode == 418) {
callServerError(418);
serverconsole.errmessage("SVR.JS is always a teapot ;)");
return;
} else {
callServerError(nonscode.scode);
serverconsole.errmessage("Client fails recieving content.");
return;
}
}
2023-07-29 20:44:13 +02:00
// Handle HTTP authentication
2023-07-29 20:32:17 +02:00
if (authIndex > -1) {
var authcode = nonStandardCodes[authIndex];
function authorizedCallback(bruteProtection) {
var ha = getCustomHeaders();
ha["WWW-Authenticate"] = "Basic realm=\"" + (authcode.realm ? authcode.realm.replace(/(\\|")/g, "\\$1") : "SVR.JS HTTP Basic Authorization") + "\", charset=\"UTF-8\"";
var credentials = req.headers["authorization"];
if (!credentials) {
callServerError(401, undefined, undefined, ha);
serverconsole.errmessage("Content needs authorization.");
return;
}
var credentialsMatch = credentials.match(/^Basic (.+)$/);
if (!credentialsMatch) {
2023-07-29 20:32:17 +02:00
callServerError(401, undefined, undefined, ha);
serverconsole.errmessage("Malformed credentials.");
return;
}
var decodedCredentials = Buffer.from(credentialsMatch[1], "base64").toString("utf8");
var decodedCredentialsMatch = decodedCredentials.match(/^([^:]*):(.*)$/);
if (!decodedCredentialsMatch) {
2023-07-29 20:32:17 +02:00
callServerError(401, undefined, undefined, ha);
serverconsole.errmessage("Malformed credentials.");
return;
}
var username = decodedCredentialsMatch[1];
var password = decodedCredentialsMatch[2];
2023-07-29 20:32:17 +02:00
var authorized = false;
for (var i = 0; i < users.length; i++) {
var hash = sha256(password + users[i].salt);
if (users[i].name == username && users[i].pass == hash) {
authorized = true;
break;
}
}
if (!authorized) {
if (bruteProtection) {
if (process.send) {
process.send("\x12AUTHW" + reqip);
} else {
if (!bruteForceDb[reqip]) bruteForceDb[reqip] = {
invalidAttempts: 0
};
bruteForceDb[reqip].invalidAttempts++;
if (bruteForceDb[reqip].invalidAttempts >= 10) {
bruteForceDb[reqip].lastAttemptDate = new Date();
}
}
}
callServerError(401, undefined, undefined, ha);
serverconsole.errmessage("User " + username + " failed to log in.");
} else {
if (bruteProtection) {
if (process.send) {
process.send("\x12AUTHR" + reqip);
} else {
if (bruteForceDb[reqip]) bruteForceDb[reqip] = {
invalidAttempts: 0
};
}
}
modExecute(mods, vres(req, res, serverconsole, responseEnd, href, ext, uobject, search, "index.html", users, page404, head, foot, fd, callServerError, getCustomHeaders, origHref, redirect, parsePostData));
}
}
if (authcode.disableBruteProtection) {
authorizedCallback(false);
} else if (!process.send) {
if (!bruteForceDb[reqip] || !bruteForceDb[reqip].lastAttemptDate || (new Date() - 300000 >= bruteForceDb[reqip].lastAttemptDate)) {
if (bruteForceDb[reqip] && bruteForceDb[reqip].invalidAttempts >= 10) bruteForceDb[reqip] = {
invalidAttempts: 5
};
authorizedCallback(true);
} else {
callServerError(429);
serverconsole.errmessage("Brute force limit reached!");
}
} else {
var listenerEmitted = false;
function authMessageListener(message) {
if (listenerEmitted) return;
if (message == "\x14AUTHA" + reqip || message == "\x14AUTHD" + reqip) {
process.removeListener("message", authMessageListener);
listenerEmitted = true;
}
if (message == "\x14AUTHD" + reqip) {
callServerError(429);
serverconsole.errmessage("Brute force limit reached!");
} else if (message == "\x14AUTHA" + reqip) {
authorizedCallback(true);
}
}
process.on("message", authMessageListener);
process.send("\x12AUTHQ" + reqip);
}
} else {
modExecute(mods, vres(req, res, serverconsole, responseEnd, href, ext, uobject, search, "index.html", users, page404, head, foot, fd, callServerError, getCustomHeaders, origHref, redirect, parsePostData));
}
}
} catch (err) {
2023-07-29 20:32:17 +02:00
//CRASH HANDLER
if (err.message == "Intentionally crashed") throw err; //If intentionally crashed, then crash SVR.JS
callServerError(500, undefined, generateErrorStack(err)); //Else just return 500 error
2023-07-29 20:32:17 +02:00
}
}
}
//Listen port to server
server.on("error", function (err) {
if (err.code == "EADDRINUSE" || err.code == "EADDRNOTAVAIL" || err.code == "EACCES") {
attmts--;
if (cluster.isPrimary === undefined) {
2023-07-29 20:32:17 +02:00
if (err.code == "EADDRINUSE") {
2023-07-29 20:44:13 +02:00
serverconsole.locerrmessage("Address is already in use by another process.");
2023-07-29 20:32:17 +02:00
} else if (err.code == "EADDRNOTAVAIL") {
2023-07-29 20:44:13 +02:00
serverconsole.locerrmessage("Address is not available on this machine.");
2023-07-29 20:32:17 +02:00
} else if (err.code == "EACCES") {
2023-07-29 20:44:13 +02:00
serverconsole.locerrmessage("Permission denied. You may not have sufficient privileges to access the requested address.");
} else if (err.code == "EAFNOSUPPORT") {
serverconsole.locerrmessage("Address family not supported. The address family (IPv4 or IPv6) of the requested address is not supported.");
} else if (err.code == "EALREADY") {
serverconsole.locerrmessage("Operation already in progress. The server is already in the process of establishing a connection on the requested address.");
} else if (err.code == "ECONNABORTED") {
serverconsole.locerrmessage("Connection aborted. The connection to the server was terminated abruptly.");
} else if (err.code == "ECONNREFUSED") {
serverconsole.locerrmessage("Connection refused. The server refused the connection attempt.");
} else if (err.code == "ECONNRESET") {
serverconsole.locerrmessage("Connection reset by peer. The connection to the server was reset by the remote host.");
} else if (err.code == "EDESTADDRREQ") {
serverconsole.locerrmessage("Destination address required. The destination address must be specified.");
} else if (err.code == "ENETDOWN") {
serverconsole.locerrmessage("Network is down. The network interface used for the connection is not available.");
} else if (err.code == "ENETUNREACH") {
serverconsole.locerrmessage("Network is unreachable. The network destination is not reachable from this host.");
} else if (err.code == "ENOBUFS") {
serverconsole.locerrmessage("No buffer space available. Insufficient buffer space is available for the server to process the request.");
} else if (err.code == "ENOTSOCK") {
serverconsole.locerrmessage("Not a socket. The file descriptor provided is not a valid socket.");
} else if (err.code == "EPROTO") {
serverconsole.locerrmessage("Protocol error. An unspecified protocol error occurred.");
} else if (err.code == "EPROTONOSUPPORT") {
serverconsole.locerrmessage("Protocol not supported. The requested network protocol is not supported.");
} else if (err.code == "ETIMEDOUT") {
serverconsole.locerrmessage("Connection timed out. The server did not respond within the specified timeout period.");
} else {
serverconsole.locerrmessage("There was an unknown error with the server.");
2023-07-29 20:32:17 +02:00
}
serverconsole.locmessage(attmts + " attempts left.");
} else {
process.send("\x12ERRLIST" + attmts + err.code);
}
if (attmts > 0) {
server2.close();
setTimeout(start, 900);
} else {
if (cluster.isPrimary !== undefined) process.send("\x12" + err.code);
2023-07-29 20:32:17 +02:00
process.exit(errors[err.code]);
}
} else {
serverconsole.locerrmessage("There was a problem starting SVR.JS!!!");
serverconsole.locerrmessage("Stack:");
serverconsole.locerrmessage(generateErrorStack(err));
if (cluster.isPrimary !== undefined) process.send("\x12CRASH");
2023-07-29 20:32:17 +02:00
process.exit(err.code ? errors[err.code] : 1);
}
});
server.on("listening", function () {
2023-07-29 20:32:17 +02:00
listeningMessage();
});
}
var closedMaster = true;
2023-07-29 20:32:17 +02:00
function listenConnListener(msg) {
if (msg == "\x12LISTEN") {
listeningMessage();
}
}
function bruteForceListenerWrapper(worker) {
return function bruteForceListener(message) {
var ip = "";
if (message.substr(0, 6) == "\x12AUTHQ") {
ip = message.substr(6);
if (!bruteForceDb[ip] || !bruteForceDb[ip].lastAttemptDate || (new Date() - 300000 >= bruteForceDb[ip].lastAttemptDate)) {
if (bruteForceDb[ip] && bruteForceDb[ip].invalidAttempts >= 10) bruteForceDb[ip] = {
invalidAttempts: 5
};
worker.send("\x14AUTHA" + ip);
} else {
worker.send("\x14AUTHD" + ip);
}
} else if (message.substr(0, 6) == "\x12AUTHR") {
ip = message.substr(6);
if (bruteForceDb[ip]) bruteForceDb[ip] = {
invalidAttempts: 0
};
} else if (message.substr(0, 6) == "\x12AUTHW") {
ip = message.substr(6);
if (!bruteForceDb[ip]) bruteForceDb[ip] = {
invalidAttempts: 0
};
bruteForceDb[ip].invalidAttempts++;
if (bruteForceDb[ip].invalidAttempts >= 10) {
bruteForceDb[ip].lastAttemptDate = new Date();
}
}
};
}
2023-08-09 21:09:23 +02:00
var isWorkerHungUpBuff = true;
2023-07-29 20:32:17 +02:00
function msgListener(msg) {
for (var i = 0; i < Object.keys(cluster.workers).length; i++) {
if (msg == "\x12END") {
cluster.workers[Object.keys(cluster.workers)[i]].removeAllListeners("message");
cluster.workers[Object.keys(cluster.workers)[i]].on("message", bruteForceListenerWrapper(cluster.workers[Object.keys(cluster.workers)[i]]));
cluster.workers[Object.keys(cluster.workers)[i]].on("message", listenConnListener);
}
}
if (msg == "\x12CLOSE") {
closedMaster = true;
} else if (msg == "\x12LISTEN" || msg.substr(0, 4) == "\x12AUTH") {
//Do nothing!
2023-08-09 21:09:23 +02:00
} else if (msg == "\x12KILLOK") {
if(typeof isWorkerHungUpBuff != "undefined") isWorkerHungUpBuff = false;
} else if (msg == "\x12KILLTERMMSG") {
serverconsole.locmessage("Terminating unused worker process...");
2023-07-29 20:32:17 +02:00
} else if (msg == "\x12SAVEGOOD") {
serverconsole.locmessage("Configuration saved.");
} else if (msg.indexOf("\x12SAVEERR") == 0) {
serverconsole.locwarnmessage("There was a problem, while saving configuration file. Reason: " + msg.substr(8));
} else if (msg == "\x12END") {
cluster.workers[Object.keys(cluster.workers)[0]].on("message", function (msg) {
if (msg.length > 9 && msg.indexOf("\x12ERRLIST") == 0) {
var tries = parseInt(msg.substr(8, 1));
var errCode = msg.substr(9);
if (errCode == "EADDRINUSE") {
2023-07-29 20:44:13 +02:00
serverconsole.locerrmessage("Address is already in use by another process.");
2023-07-29 20:32:17 +02:00
} else if (errCode == "EADDRNOTAVAIL") {
2023-07-29 20:44:13 +02:00
serverconsole.locerrmessage("Address is not available on this machine.");
2023-07-29 20:32:17 +02:00
} else if (errCode == "EACCES") {
2023-07-29 20:44:13 +02:00
serverconsole.locerrmessage("Permission denied. You may not have sufficient privileges to access the requested address.");
} else if (errCode == "EAFNOSUPPORT") {
serverconsole.locerrmessage("Address family not supported. The address family (IPv4 or IPv6) of the requested address is not supported.");
} else if (errCode == "EALREADY") {
serverconsole.locerrmessage("Operation already in progress. The server is already in the process of establishing a connection on the requested address.");
} else if (errCode == "ECONNABORTED") {
serverconsole.locerrmessage("Connection aborted. The connection to the server was terminated abruptly.");
} else if (errCode == "ECONNREFUSED") {
serverconsole.locerrmessage("Connection refused. The server refused the connection attempt.");
} else if (errCode == "ECONNRESET") {
serverconsole.locerrmessage("Connection reset by peer. The connection to the server was reset by the remote host.");
} else if (errCode == "EDESTADDRREQ") {
serverconsole.locerrmessage("Destination address required. The destination address must be specified.");
} else if (errCode == "ENETDOWN") {
serverconsole.locerrmessage("Network is down. The network interface used for the connection is not available.");
} else if (errCode == "ENETUNREACH") {
serverconsole.locerrmessage("Network is unreachable. The network destination is not reachable from this host.");
} else if (errCode == "ENOBUFS") {
serverconsole.locerrmessage("No buffer space available. Insufficient buffer space is available for the server to process the request.");
} else if (errCode == "ENOTSOCK") {
serverconsole.locerrmessage("Not a socket. The file descriptor provided is not a valid socket.");
} else if (errCode == "EPROTO") {
serverconsole.locerrmessage("Protocol error. An unspecified protocol error occurred.");
} else if (errCode == "EPROTONOSUPPORT") {
serverconsole.locerrmessage("Protocol not supported. The requested network protocol is not supported.");
} else if (errCode == "ETIMEDOUT") {
serverconsole.locerrmessage("Connection timed out. The server did not respond within the specified timeout period.");
} else {
serverconsole.locerrmessage("There was an unknown error with the server.");
2023-07-29 20:32:17 +02:00
}
serverconsole.locmessage(tries + " attempts left.");
}
if (msg == "\x12CRASH") process.exit(1);
if (msg == "\x12EADDRINUSE" || msg == "\x12EADDRNOTAVAIL" || msg == "\x12EACCES") process.exit(errors[msg.substr(1)]);
});
} else {
serverconsole.climessage(msg);
}
}
var messageTransmitted = false;
function listeningMessage() {
if(typeof closedMaster !== "undefined") closedMaster = false;
if (!cluster.isPrimary && cluster.isPrimary !== undefined) {
2023-07-29 20:32:17 +02:00
process.send("\x12LISTEN");
return;
}
if (messageTransmitted) return;
messageTransmitted = true;
2023-07-29 20:32:17 +02:00
serverconsole.locmessage("Started server at: ");
2023-07-29 20:44:13 +02:00
if (secure) {
if (typeof sport === "number") {
serverconsole.locmessage("* https://localhost" + (sport == 443 ? "" : (":" + sport)));
} else {
serverconsole.locmessage("* " + sport); //Unix socket or Windows named pipe
}
}
if (!(secure && disableNonEncryptedServer)) {
if (typeof port === "number") {
serverconsole.locmessage("* http://localhost" + (port == 80 ? "" : (":" + port)));
} else {
serverconsole.locmessage("* " + port); //Unix socket or Windows named pipe
}
}
2023-07-29 20:32:17 +02:00
if (host != "" && host != "[offline]") {
2023-07-29 20:44:13 +02:00
if (secure && typeof sport === "number") serverconsole.locmessage("* https://" + (host.indexOf(":") > -1 ? "[" + host + "]" : host) + (sport == 443 ? "" : (":" + sport)));
if (!(secure && disableNonEncryptedServer) && typeof port === "number") serverconsole.locmessage("* http://" + (host.indexOf(":") > -1 ? "[" + host + "]" : host) + (port == 80 ? "" : (":" + port)));
2023-07-29 20:32:17 +02:00
}
ipStatusCallback(function () {
if (pubip != "") {
if (secure) serverconsole.locmessage("* https://" + (pubip.indexOf(":") > -1 ? "[" + pubip + "]" : pubip) + (spubport == 443 ? "" : (":" + spubport)));
if (!(secure && disableNonEncryptedServer)) serverconsole.locmessage("* http://" + (pubip.indexOf(":") > -1 ? "[" + pubip + "]" : pubip) + (pubport == 80 ? "" : (":" + pubport)));
}
if (domain != "") {
if (secure) serverconsole.locmessage("* https://" + domain + (spubport == 443 ? "" : (":" + spubport)));
if (!(secure && disableNonEncryptedServer)) serverconsole.locmessage("* http://" + domain + (pubport == 80 ? "" : (":" + pubport)));
}
2023-07-29 20:44:13 +02:00
serverconsole.locmessage("For CLI help, you can type \"help\"");
2023-07-29 20:32:17 +02:00
});
}
function start(init) {
init = Boolean(init);
if (cluster.isPrimary || cluster.isPrimary === undefined) {
2023-07-29 20:32:17 +02:00
if (init) {
for (i = 0; i < logo.length; i++) console.log(logo[i]); //Print logo
console.log();
console.log("Welcome to DorianTech SVR.JS server.");
2023-07-29 20:44:13 +02:00
//Print warnings
2023-07-29 20:32:17 +02:00
if (version.indexOf("Nightly-") === 0) serverconsole.locwarnmessage("This version is only for test purposes and may be unstable.");
2023-07-29 20:44:13 +02:00
if (http2.__disabled__ !== undefined) serverconsole.locwarnmessage("HTTP/2 isn't supported by your Node.JS version! You may not be able to use HTTP/2 with SVR.JS");
if (configJSON.enableHTTP2 && !secure) serverconsole.locwarnmessage("HTTP/2 without HTTPS may not work in web browsers. Web browsers only support HTTP/2 with HTTPS!");
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 (cluster.isPrimary === undefined) serverconsole.locwarnmessage("You're running SVR.JS on single thread. Reliability may suffer, as the server is stopped after crash.");
2023-07-29 20:44:13 +02:00
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.");
if (process.getuid && process.getuid() == 0) serverconsole.locwarnmessage("You're running SVR.JS as root. It's recommended to run SVR.JS as an non-root user. Running SVR.JS as root may increase the risks of OS command execution vulnerabilities.");
2023-07-29 20:32:17 +02:00
if (secure && process.versions && process.versions.openssl && process.versions.openssl.substr(0, 2) == "1.") {
if (new Date() > new Date("11 September 2023")) {
2023-07-29 20:44:13 +02:00
serverconsole.locwarnmessage("OpenSSL 1.x is no longer recieving security updates after 11th September 2023. Your HTTPS communication might be vulnerable. It is recommended to update to a newer version of Node.JS that includes OpenSSL 3.0 or higher to ensure the security of your server and data.");
2023-07-29 20:32:17 +02:00
} else {
2023-07-29 20:44:13 +02:00
serverconsole.locwarnmessage("OpenSSL 1.x will no longer recieve security updates after 11th September 2023. Your HTTPS communication might be vulnerable in future. It is recommended to update to a newer version of Node.JS that includes OpenSSL 3.0 or higher to ensure the security of your server and data.");
2023-07-29 20:32:17 +02:00
}
}
2023-07-29 20:44:13 +02:00
if (secure && configJSON.enableOCSPStapling && ocsp._errored) serverconsole.locwarnmessage("Can't load OCSP module. OCSP stapling will be disabled. OCSP stapling is a security feature that improves the performance and security of HTTPS connections by caching the certificate status response. If you require this feature, consider updating your Node.JS version or checking for any issues with the 'ocsp' module.");
if (disableMods) serverconsole.locwarnmessage("SVR.JS is running without mods and server-side JavaScript enabled. Web applications may not work as expected");
2023-07-29 20:32:17 +02:00
console.log();
2023-07-29 20:44:13 +02:00
//Print info
2023-07-29 20:32:17 +02:00
serverconsole.locmessage("Server version: " + version);
if (process.isBun) serverconsole.locmessage("Bun version: v" + process.versions.bun);
else serverconsole.locmessage("Node.JS version: " + process.version);
var CPUs = os.cpus();
if (CPUs.length > 0) serverconsole.locmessage("CPU: " + (CPUs.length > 1 ? CPUs.length + "x " : "") + CPUs[0].model);
2023-07-29 20:44:13 +02:00
//Throw errors
if (vnum < 64) {
throw new Error("SVR.JS requires Node.JS 10.0.0 and newer, but your Node.JS version isn't supported by SVR.JS.");
}
if (configJSON.enableHTTP2 && !secure && (typeof port != "number")) {
throw new Error("HTTP/2 without HTTPS, along with Unix sockets/Windows named pipes aren't supported by SVR.JS.");
2023-07-29 20:32:17 +02:00
}
}
2023-07-29 20:44:13 +02:00
//Information about starting the server
if (!(secure && disableNonEncryptedServer)) serverconsole.locmessage("Starting HTTP server at " + (typeof port == "number" ? "localhost:" : "") + port.toString() + "...");
if (secure) serverconsole.locmessage("Starting HTTPS server at " + (typeof sport == "number" ? "localhost:" : "") + sport.toString() + "...");
2023-07-29 20:32:17 +02:00
}
if (!cluster.isPrimary) {
2023-07-29 20:32:17 +02:00
if (secure) {
server.listen(sport);
if (!disableNonEncryptedServer) server2.listen(port);
} else {
server.listen(port);
}
}
2023-07-29 20:44:13 +02:00
//SVR.JS commmands
2023-07-29 20:32:17 +02:00
var commands = {
close: function () {
try {
if(server.listening || server2.listening) {
server.close();
if (secure && !disableNonEncryptedServer) {
server2.close();
}
} else {
throw new Error("Server is already closed.");
2023-07-29 20:32:17 +02:00
}
if (cluster.isPrimary === undefined) serverconsole.climessage("Server closed.");
2023-07-29 20:32:17 +02:00
else {
process.send("Server closed.");
process.send("\x12CLOSE");
}
} catch (err) {
if (cluster.isPrimary === undefined) serverconsole.climessage("Cannot close server! Reason: " + err.message);
else process.send("Cannot close server! Reason: " + err.message);
2023-07-29 20:32:17 +02:00
}
},
open: function () {
try {
if(!server.listening) {
if (secure) {
server.listen(sport);
if (!disableNonEncryptedServer) server2.listen(port);
} else {
server.listen(port); // Reopen Server
}
2023-07-29 20:32:17 +02:00
} else {
throw new Error("Server is already opened.");
2023-07-29 20:32:17 +02:00
}
if (cluster.isPrimary === undefined) serverconsole.climessage("Server opened.");
2023-07-29 20:32:17 +02:00
else {
process.send("Server opened.");
}
} catch (err) {
if (cluster.isPrimary === undefined) serverconsole.climessage("Cannot open server! Reason: " + err.message);
else process.send("Cannot open server! Reason: " + err.message);
2023-07-29 20:32:17 +02:00
}
},
help: function () {
if (cluster.isPrimary === undefined) serverconsole.climessage("Server commands:\n" + Object.keys(commands).join(" "));
2023-07-29 20:32:17 +02:00
else process.send("Server commands:\n" + Object.keys(commands).join(" "));
},
mods: function () {
if (cluster.isPrimary === undefined) serverconsole.climessage("Mods:");
2023-07-29 20:32:17 +02:00
else process.send("Mods:");
for (var i = 0; i < modInfos.length; i++) {
if (cluster.isPrimary === undefined) serverconsole.climessage((i + 1).toString() + ". " + modInfos[i].name + " " + modInfos[i].version);
2023-07-29 20:32:17 +02:00
else process.send((i + 1).toString() + ". " + modInfos[i].name + " " + modInfos[i].version);
}
if (modInfos.length == 0) {
if (cluster.isPrimary === undefined) serverconsole.climessage("No mods installed.");
2023-07-29 20:32:17 +02:00
else process.send("No mods installed.");
}
},
stop: function (retcode) {
2023-07-29 20:44:13 +02:00
reallyExiting = true;
if((!cluster.isPrimary && cluster.isPrimary !== undefined) && server.listening) {
try {
server.close(function() {
if(server2.listening) {
try {
server2.close(function() {
if (typeof retcode == "number") {
process.exit(retcode);
} else {
process.exit(0);
}
});
} catch(err) {
if (typeof retcode == "number") {
process.exit(retcode);
} else {
process.exit(0);
}
}
} else {
if (typeof retcode == "number") {
process.exit(retcode);
} else {
process.exit(0);
}
}
});
} catch(err) {
if (typeof retcode == "number") {
process.exit(retcode);
} else {
process.exit(0);
}
}
2023-07-29 20:32:17 +02:00
} else {
if (typeof retcode == "number") {
process.exit(retcode);
} else {
process.exit(0);
}
2023-07-29 20:32:17 +02:00
}
},
clear: function () {
console.clear();
},
block: function (ip) {
if (ip == undefined || JSON.stringify(ip) == "[]") {
if (cluster.isPrimary === undefined) serverconsole.climessage("Cannot block non-existent IP.");
else if (!cluster.isPrimary) process.send("Cannot block non-existent IP.");
2023-07-29 20:32:17 +02:00
} else {
for (var i = 0; i < ip.length; i++) {
if (ip[i].indexOf(":") == -1) {
ip[i] = "::ffff:" + ip[i];
}
if (!blacklist.check(ip[i])) {
blacklist.add(ip[i]);
}
}
if (cluster.isPrimary === undefined) serverconsole.climessage("IPs successfully blocked.");
else if (!cluster.isPrimary) process.send("IPs successfully blocked.");
2023-07-29 20:32:17 +02:00
}
},
unblock: function (ip) {
if (ip == undefined || JSON.stringify(ip) == "[]") {
if (cluster.isPrimary === undefined) serverconsole.climessage("Cannot unblock non-existent IP.");
else if (!cluster.isPrimary) process.send("Cannot unblock non-existent IP.");
2023-07-29 20:32:17 +02:00
} else {
for (var i = 0; i < ip.length; i++) {
if (ip[i].indexOf(":") == -1) {
ip[i] = "::ffff:" + ip[i];
}
blacklist.remove(ip[i]);
}
if (cluster.isPrimary === undefined) serverconsole.climessage("IPs successfully unblocked.");
else if (!cluster.isPrimary) process.send("IPs successfully unblocked.");
2023-07-29 20:32:17 +02:00
}
},
restart: function () {
if (cluster.isPrimary === undefined) serverconsole.climessage("This command is not supported on single-threaded SVR.JS.");
2023-07-29 20:32:17 +02:00
else process.send("This command need to be run in SVR.JS master.");
}
};
if (init) {
if (cluster.isPrimary === undefined) {
2023-07-29 20:32:17 +02:00
setInterval(function () {
try {
saveConfig();
serverconsole.locmessage("Configuration saved.");
} catch (err) {
throw new Error(err);
2023-07-29 20:32:17 +02:00
}
}, 300000);
} else if (cluster.isPrimary) {
2023-07-29 20:32:17 +02:00
setInterval(function () {
var allClusters = Object.keys(cluster.workers);
for (var i = 0; i < allClusters.length; i++) {
try {
if (cluster.workers[allClusters[i]]) {
cluster.workers[allClusters[i]].on("message", msgListener);
cluster.workers[allClusters[i]].send("\x14SAVECONF");
}
} catch (err) {
2023-07-29 20:32:17 +02:00
if (cluster.workers[allClusters[i]]) {
cluster.workers[allClusters[i]].removeAllListeners("message");
cluster.workers[allClusters[i]].on("message", bruteForceListenerWrapper(cluster.workers[allClusters[i]]));
cluster.workers[allClusters[i]].on("message", listenConnListener);
}
serverconsole.locwarnmessage("There was a problem, while saving configuration file. Reason: " + err.message);
2023-07-29 20:32:17 +02:00
}
}
}, 300000);
}
if (!cluster.isPrimary && cluster.isPrimary !== undefined) {
2023-07-29 20:32:17 +02:00
process.on("message", function (line) {
try {
if (line == "") {
//Does Nothing
process.send("\x12END");
} else if (line == "\x14SAVECONF") {
//Save configuration file
try {
saveConfig();
process.send("\x12SAVEGOOD");
} catch (err) {
process.send("\x12SAVEERR" + err.message);
2023-07-29 20:32:17 +02:00
}
process.send("\x12END");
2023-08-09 21:09:23 +02:00
} else if (line == "\x14KILLPING") {
if(!reallyExiting) {
process.send("\x12KILLOK");
process.send("\x12END");
}
// Refuse to send, when it's really exiting. Main process will treat the worker as hung up anyway...
2023-08-09 21:09:23 +02:00
} else if (line == "\x14KILLREQ") {
if(reqcounter - reqcounterKillReq < 2) {
process.send("\x12KILLTERMMSG");
process.nextTick(commands.stop);
2023-08-09 21:09:23 +02:00
} else {
reqcounterKillReq = reqcounter;
}
2023-07-29 20:32:17 +02:00
} else if (commands[line.split(" ")[0]] !== undefined && commands[line.split(" ")[0]] !== null) {
var argss = line.split(" ");
var command = argss.shift();
commands[command](argss);
process.send("\x12END");
} else {
process.send("Unrecognized command \"" + line.split(" ")[0] + "\".");
process.send("\x12END");
}
} catch (err) {
2023-07-29 20:32:17 +02:00
if (line != "") {
process.send("Can't execute command \"" + line.split(" ")[0] + "\".");
process.send("\x12END");
}
}
});
} else {
var rla = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: ""
});
rla.prompt();
rla.on("line", function (line) {
line = line.trim();
var argss = line.split(" ");
var command = argss.shift();
if (line != "") {
if (cluster.isPrimary !== undefined) {
2023-07-29 20:32:17 +02:00
var allClusters = Object.keys(cluster.workers);
if (command == "block") commands.block(argss);
if (command == "unblock") commands.unblock(argss);
if (command == "restart") {
var stopError = false;
exiting = true;
for (var i = 0; i < allClusters.length; i++) {
try {
if (cluster.workers[allClusters[i]]) {
cluster.workers[allClusters[i]].kill();
}
} catch (err) {
2023-07-29 20:32:17 +02:00
stopError = true;
}
}
if (stopError) serverconsole.climessage("Some SVR.JS workers might not be stopped.");
SVRJSInitialized = false;
closedMaster = true;
2023-07-29 20:32:17 +02:00
var cpus = os.cpus().length;
if (cpus > 16) cpus = 16;
try {
var useAvailableCores = Math.round((os.freemem()) / 50000000) - 1; //1 core deleted for safety...
if (cpus > useAvailableCores) cpus = useAvailableCores;
} catch (err) {
2023-07-29 20:32:17 +02:00
//Nevermind... Don't want SVR.JS to fail starting, because os.freemem function is not working.
}
if (cpus < 1) cpus = 1; //If SVR.JS is run on Haiku or if useAvailableCores = 0
for (var i = 0; i < cpus; i++) {
if (i == 0) {
SVRJSFork();
} else {
setTimeout((function (i) {
return function () {
SVRJSFork();
if (i >= cpus - 1) {
SVRJSInitialized = true;
exiting = false;
serverconsole.climessage("SVR.JS workers restarted.");
}
};
})(i), i * 6.6);
}
}
return;
}
if (command == "stop") {
exiting = true;
allClusters = Object.keys(cluster.workers);
}
for (var i = 0; i < allClusters.length; i++) {
try {
if (cluster.workers[allClusters[i]]) {
cluster.workers[allClusters[i]].on("message", msgListener);
cluster.workers[allClusters[i]].send(line);
}
} catch (err) {
2023-07-29 20:32:17 +02:00
if (cluster.workers[allClusters[i]]) {
cluster.workers[allClusters[i]].removeAllListeners("message");
cluster.workers[allClusters[i]].on("message", bruteForceListenerWrapper(cluster.workers[allClusters[i]]));
cluster.workers[allClusters[i]].on("message", listenConnListener);
}
serverconsole.climessage("Can't run command \"" + command + "\".");
}
}
if (command == "stop") {
setTimeout(function () {
2023-07-29 20:44:13 +02:00
reallyExiting = true;
2023-07-29 20:32:17 +02:00
process.exit(0);
}, 50);
}
} else {
2023-07-29 20:44:13 +02:00
if (command == "stop") {
reallyExiting = true;
process.exit(0);
}
2023-07-29 20:32:17 +02:00
try {
commands[command](argss);
} catch (err) {
2023-07-29 20:32:17 +02:00
serverconsole.climessage("Unrecognized command \"" + command + "\".");
}
}
}
rla.prompt();
});
}
if (cluster.isPrimary || cluster.isPrimary === undefined) {
2023-07-29 20:32:17 +02:00
//Cluster forking code
if (cluster.isPrimary !== undefined && init) {
2023-07-29 20:32:17 +02:00
var cpus = os.cpus().length;
if (cpus > 16) cpus = 16;
try {
var useAvailableCores = Math.round((os.freemem()) / 50000000) - 1; //1 core deleted for safety...
if (cpus > useAvailableCores) cpus = useAvailableCores;
} catch (err) {
2023-07-29 20:32:17 +02:00
//Nevermind... Don't want SVR.JS to fail starting, because os.freemem function is not working.
}
2023-07-29 20:44:13 +02:00
if (cpus < 1) cpus = 1; //If SVR.JS is run on Haiku (os.cpus in Haiku returns empty array) or if useAvailableCores = 0
2023-07-29 20:32:17 +02:00
for (var i = 0; i < cpus; i++) {
if (i == 0) {
SVRJSFork();
} else {
setTimeout((function (i) {
return function () {
SVRJSFork();
if (i >= cpus - 1) SVRJSInitialized = true;
};
})(i), i * 6.6);
}
}
cluster.workers[Object.keys(cluster.workers)[0]].on("message", function (msg) {
if (msg.length > 9 && msg.indexOf("\x12ERRLIST") == 0) {
var tries = parseInt(msg.substr(8, 1));
var errCode = msg.substr(9);
if (errCode == "EADDRINUSE") {
2023-07-29 20:44:13 +02:00
serverconsole.locerrmessage("Address is already in use by another process.");
2023-07-29 20:32:17 +02:00
} else if (errCode == "EADDRNOTAVAIL") {
2023-07-29 20:44:13 +02:00
serverconsole.locerrmessage("Address is not available on this machine.");
2023-07-29 20:32:17 +02:00
} else if (errCode == "EACCES") {
2023-07-29 20:44:13 +02:00
serverconsole.locerrmessage("Permission denied. You may not have sufficient privileges to access the requested address.");
} else if (errCode == "EAFNOSUPPORT") {
serverconsole.locerrmessage("Address family not supported. The address family (IPv4 or IPv6) of the requested address is not supported.");
} else if (errCode == "EALREADY") {
serverconsole.locerrmessage("Operation already in progress. The server is already in the process of establishing a connection on the requested address.");
} else if (errCode == "ECONNABORTED") {
serverconsole.locerrmessage("Connection aborted. The connection to the server was terminated abruptly.");
} else if (errCode == "ECONNREFUSED") {
serverconsole.locerrmessage("Connection refused. The server refused the connection attempt.");
} else if (errCode == "ECONNRESET") {
serverconsole.locerrmessage("Connection reset by peer. The connection to the server was reset by the remote host.");
} else if (errCode == "EDESTADDRREQ") {
serverconsole.locerrmessage("Destination address required. The destination address must be specified.");
} else if (errCode == "ENETDOWN") {
serverconsole.locerrmessage("Network is down. The network interface used for the connection is not available.");
} else if (errCode == "ENETUNREACH") {
serverconsole.locerrmessage("Network is unreachable. The network destination is not reachable from this host.");
} else if (errCode == "ENOBUFS") {
serverconsole.locerrmessage("No buffer space available. Insufficient buffer space is available for the server to process the request.");
} else if (errCode == "ENOTSOCK") {
serverconsole.locerrmessage("Not a socket. The file descriptor provided is not a valid socket.");
} else if (errCode == "EPROTO") {
serverconsole.locerrmessage("Protocol error. An unspecified protocol error occurred.");
} else if (errCode == "EPROTONOSUPPORT") {
serverconsole.locerrmessage("Protocol not supported. The requested network protocol is not supported.");
} else if (errCode == "ETIMEDOUT") {
serverconsole.locerrmessage("Connection timed out. The server did not respond within the specified timeout period.");
} else {
serverconsole.locerrmessage("There was an unknown error with the server.");
2023-07-29 20:32:17 +02:00
}
serverconsole.locmessage(tries + " attempts left.");
}
if (msg == "\x12CRASH") process.exit(1);
if (msg == "\x12EADDRINUSE" || msg == "\x12EADDRNOTAVAIL" || msg == "\x12EACCES") process.exit(errors[msg.substr(1)]);
});
2023-08-09 21:09:23 +02:00
// Hangup check and restart
2023-07-29 20:32:17 +02:00
setInterval(function () {
if (!closedMaster && !exiting) {
var chksocket = {};
if (secure && disableNonEncryptedServer) {
chksocket = https.get({
2023-07-29 20:44:13 +02:00
port: (typeof sport == "number") ? sport : undefined,
socketPath: (typeof sport == "number") ? undefined : sport,
2023-07-29 20:32:17 +02:00
headers: {
"X-SVR-JS-From-Main-Thread": "true",
"User-Agent": (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS")
},
timeout: 1620,
2023-07-29 20:32:17 +02:00
rejectUnauthorized: false
}, function (res) {
chksocket.removeAllListeners("timeout");
res.destroy();
res.on("data", function () {});
res.on("end", function () {});
crashed = false;
}).on("error", function () {
if (!exiting) {
if (!crashed) SVRJSFork();
else crashed = false;
}
}).on("timeout", function () {
if (!exiting) SVRJSFork();
crashed = true;
});
} else if ((configJSON.enableHTTP2 == undefined ? false : configJSON.enableHTTP2) && !secure) {
2023-07-29 20:44:13 +02:00
//It doesn't support through Unix sockets or Windows named pipes
2023-07-29 20:32:17 +02:00
var connection = http2.connect("http://localhost:" + port.toString());
connection.on("error", function () {
if (!exiting) {
if (!crashed) SVRJSFork();
else crashed = false;
}
});
connection.setTimeout(1620, function () {
2023-07-29 20:32:17 +02:00
if (!exiting) SVRJSFork();
crashed = true;
});
chksocket = connection.request({
":path": "/",
"x-svr-js-from-main-thread": "true",
"user-agent": (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS")
});
chksocket.on("response", function () {
connection.close();
crashed = false;
});
chksocket.on("error", function () {
if (!exiting) {
if (!crashed) SVRJSFork();
else crashed = false;
}
});
} else {
chksocket = http.get({
2023-07-29 20:44:13 +02:00
port: (typeof port == "number") ? port : undefined,
socketPath: (typeof port == "number") ? undefined : port,
2023-07-29 20:32:17 +02:00
headers: {
"X-SVR-JS-From-Main-Thread": "true",
"User-Agent": (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS")
},
timeout: 1620
2023-07-29 20:32:17 +02:00
}, function (res) {
chksocket.removeAllListeners("timeout");
res.destroy();
res.on("data", function () {});
res.on("end", function () {});
crashed = false;
}).on("error", function () {
if (!exiting) {
if (!crashed) SVRJSFork();
else crashed = false;
}
}).on("timeout", function () {
if (!exiting) SVRJSFork();
crashed = true;
});
}
}
}, 4550);
2023-08-09 21:09:23 +02:00
// Termination of unused good workers
if(cluster.isPrimary !== undefined) {
2023-08-09 21:09:23 +02:00
setTimeout(function () {
setInterval(function () {
if (!closedMaster && !exiting) {
var allClusters = Object.keys(cluster.workers);
var minClusters = 0;
minClusters = Math.ceil(cpus * 0.625);
if(minClusters < 2) minClusters = 2;
var goodWorkers = [];
function checkWorker(callback, _id) {
if(typeof _id === "undefined") _id = 0;
if(_id >= allClusters.length) {
callback();
return;
2023-08-09 21:09:23 +02:00
}
try {
if (cluster.workers[allClusters[_id]]) {
2023-08-09 21:09:23 +02:00
isWorkerHungUpBuff = true;
cluster.workers[allClusters[_id]].on("message", msgListener);
cluster.workers[allClusters[_id]].send("\x14KILLPING");
setTimeout(function() {
if (isWorkerHungUpBuff) {
checkWorker(callback, _id+1);
} else {
goodWorkers.push(allClusters[_id]);
checkWorker(callback, _id+1);
}
}, 250);
} else {
checkWorker(callback, _id+1);
2023-08-09 21:09:23 +02:00
}
} catch (err) {
if (cluster.workers[allClusters[_id]]) {
cluster.workers[allClusters[_id]].removeAllListeners("message");
cluster.workers[allClusters[_id]].on("message", bruteForceListenerWrapper(cluster.workers[allClusters[_id]]));
cluster.workers[allClusters[_id]].on("message", listenConnListener);
2023-08-09 21:09:23 +02:00
}
checkWorker(callback, _id+1);
}
2023-08-09 21:09:23 +02:00
}
checkWorker(function() {
if (goodWorkers.length > minClusters) {
var wN = Math.floor(Math.random() * goodWorkers.length);
if (wN == goodWorkers.length) return;
try {
if (cluster.workers[goodWorkers[wN]]) {
isWorkerHungUpBuff = true;
cluster.workers[goodWorkers[wN]].on("message", msgListener);
cluster.workers[goodWorkers[wN]].send("\x14KILLREQ");
}
} catch (err) {
if (cluster.workers[goodWorkers[wN]]) {
cluster.workers[goodWorkers[wN]].removeAllListeners("message");
cluster.workers[goodWorkers[wN]].on("message", bruteForceListenerWrapper(cluster.workers[goodWorkers[wN]]));
cluster.workers[goodWorkers[wN]].on("message", listenConnListener);
}
serverconsole.locwarnmessage("There was a problem, while terminating unused worker process. Reason: " + err.message);
}
}
});
}
}, 150000);
}, 2000);
2023-08-09 21:09:23 +02:00
}
2023-07-29 20:32:17 +02:00
}
}
}
}
//Save configuration file
function saveConfig() {
for (var i = 0; i < 3; i++) {
try {
var configJSONobj = {};
if (fs.existsSync(__dirname + "/config.json")) configJSONobj = JSON.parse(fs.readFileSync(__dirname + "/config.json").toString());
if (configJSONobj.users === undefined) configJSONobj.users = [];
if (secure) {
if (configJSONobj.key === undefined) configJSONobj.key = "cert/key.key";
if (configJSONobj.cert === undefined) configJSONobj.cert = "cert/cert.crt";
if (configJSONobj.sport === undefined) configJSONobj.sport = 443;
if (configJSONobj.spubport === undefined) configJSONobj.spubport = 443;
if (configJSONobj.sni === undefined) configJSONobj.sni = {};
if (configJSONobj.enableOCSPStapling === undefined) configJSONobj.enableOCSPStapling = false;
}
if (configJSONobj.port === undefined) configJSONobj.port = 80;
if (configJSONobj.pubport === undefined) configJSONobj.pubport = 80;
if (configJSONobj.domain === undefined && configJSONobj.domian !== undefined) configJSONobj.domain = configJSONobj.domian;
delete configJSONobj.domian;
if (configJSONobj.page404 === undefined) configJSONobj.page404 = "404.html";
configJSONobj.timestamp = timestamp;
configJSONobj.blacklist = blacklist.raw;
if (configJSONobj.nonStandardCodes === undefined) configJSONobj.nonStandardCodes = [];
if (configJSONobj.enableCompression === undefined) configJSONobj.enableCompression = true;
if (configJSONobj.customHeaders === undefined) configJSONobj.customHeaders = {};
if (configJSONobj.enableHTTP2 === undefined) configJSONobj.enableHTTP2 = false;
if (configJSONobj.enableLogging === undefined) configJSONobj.enableLogging = true;
if (configJSONobj.enableDirectoryListing === undefined) configJSONobj.enableDirectoryListing = true;
if (configJSONobj.enableDirectoryListingWithDefaultHead === undefined) configJSONobj.enableDirectoryListingWithDefaultHead = false;
if (configJSONobj.serverAdministratorEmail === undefined) configJSONobj.serverAdministratorEmail = "[no contact information]";
if (configJSONobj.stackHidden === undefined) configJSONobj.stackHidden = false;
if (configJSONobj.enableRemoteLogBrowsing === undefined) configJSONobj.enableRemoteLogBrowsing = true;
if (configJSONobj.exposeServerVersion === undefined) configJSONobj.exposeServerVersion = true;
if (configJSONobj.disableServerSideScriptExpose === undefined) configJSONobj.disableServerSideScriptExpose = false;
if (configJSONobj.allowStatus === undefined) configJSONobj.allowStatus = true;
if (configJSONobj.rewriteMap === undefined) configJSONobj.rewriteMap = [];
if (configJSONobj.dontCompress === undefined) configJSONobj.dontCompress = [];
if (configJSONobj.enableIPSpoofing === undefined) configJSONobj.enableIPSpoofing = false;
if (configJSONobj.secure === undefined) configJSONobj.secure = false;
if (configJSONobj.disableNonEncryptedServer === undefined) configJSONobj.disableNonEncryptedServer = false;
if (configJSONobj.disableToHTTPSRedirect === undefined) configJSONobj.disableToHTTPSRedirect = false;
2023-07-29 20:44:13 +02:00
if (configJSONobj.enableETag === undefined) configJSONobj.enableETag = true;
2023-07-29 20:32:17 +02:00
var configString = JSON.stringify(configJSONobj, null, 2);
fs.writeFileSync(__dirname + "/config.json", configString);
break;
} catch (err) {
if (i >= 2) throw err;
2023-07-29 20:32:17 +02:00
var now = Date.now();
while (Date.now() - now < 2);
}
}
}
//Process event listeners
if (cluster.isPrimary || cluster.isPrimary === undefined) {
process.on("uncaughtException", function (err) {
2023-07-29 20:32:17 +02:00
//CRASH HANDLER
serverconsole.locerrmessage("SVR.JS master process just crashed!!!");
serverconsole.locerrmessage("Stack:");
serverconsole.locerrmessage(generateErrorStack(err));
process.exit(err.errno);
2023-07-29 20:32:17 +02:00
});
process.on("unhandledRejection", function (err) {
2023-07-29 20:32:17 +02:00
//CRASH HANDLER
serverconsole.locerrmessage("SVR.JS master process just crashed!!!");
serverconsole.locerrmessage("Stack:");
serverconsole.locerrmessage(err.stack ? generateErrorStack(err) : String(err));
process.exit(err.errno);
2023-07-29 20:32:17 +02:00
});
process.on("exit", function (code) {
try {
saveConfig();
} catch (err) {
serverconsole.locwarnmessage("There was a problem, while saving configuration file. Reason: " + err.message);
2023-07-29 20:32:17 +02:00
}
try {
deleteFolderRecursive(__dirname + "/temp");
} catch (err) {
2023-07-29 20:32:17 +02:00
//Error!
}
try {
fs.mkdirSync(__dirname + "/temp");
} catch (err) {
2023-07-29 20:32:17 +02:00
//Error!
}
if (process.isBun) {
try {
fs.writeFileSync(__dirname + "/temp/serverSideScript.js", "//Placeholder server-side JavaScript to workaround Bun bug.\r\n");
} catch (err) {
2023-07-29 20:32:17 +02:00
//Error!
}
2023-07-29 20:44:13 +02:00
}
serverconsole.locmessage("Server closed with exit code: " + code);
});
process.on("warning", function (warning) {
serverconsole.locwarnmessage(warning.message);
if (generateErrorStack(warning)) {
2023-07-29 20:32:17 +02:00
serverconsole.locwarnmessage("Stack:");
serverconsole.locwarnmessage(generateErrorStack(warning));
}
});
process.on("SIGINT", function () {
2023-07-29 20:44:13 +02:00
reallyExiting = true;
if (cluster.isPrimary !== undefined) {
2023-07-29 20:32:17 +02:00
exiting = true;
var allClusters = Object.keys(cluster.workers);
for (var i = 0; i < allClusters.length; i++) {
try {
if (cluster.workers[allClusters[i]]) {
cluster.workers[allClusters[i]].send("stop");
}
} catch (err) {
2023-07-29 20:32:17 +02:00
//Worker will crash with EPIPE anyway.
}
}
}
serverconsole.locmessage("Server terminated using SIGINT");
process.exit();
});
} else {
process.on("uncaughtException", function (err) {
2023-07-29 20:32:17 +02:00
//CRASH HANDLER
serverconsole.locerrmessage("SVR.JS worker just crashed!!!");
serverconsole.locerrmessage("Stack:");
serverconsole.locerrmessage(generateErrorStack(err));
process.exit(err.errno);
2023-07-29 20:32:17 +02:00
});
process.on("unhandledRejection", function (err) {
2023-07-29 20:32:17 +02:00
//CRASH HANDLER
serverconsole.locerrmessage("SVR.JS worker just crashed!!!");
serverconsole.locerrmessage("Stack:");
serverconsole.locerrmessage(err.stack ? generateErrorStack(err) : String(err));
process.exit(err.errno);
2023-07-29 20:32:17 +02:00
});
process.on("warning", function (warning) {
serverconsole.locwarnmessage(warning.message);
if (warning.stack) {
serverconsole.locwarnmessage("Stack:");
serverconsole.locwarnmessage(generateErrorStack(warning));
}
});
}
//Call start
try {
start(true);
} catch (err) {
2023-07-29 20:32:17 +02:00
serverconsole.locerrmessage("There was a problem starting SVR.JS!!!");
serverconsole.locerrmessage("Stack:");
serverconsole.locerrmessage(generateErrorStack(err));
process.exit(err.errno);
2023-07-29 20:32:17 +02:00
}
2023-07-29 20:44:13 +02:00
//////////////////////////////////
//// THE END! ////
//// WARNING: THE CODE HAS ////
//// 5000+ LINES! ////
//////////////////////////////////