2024-08-24 08:22:49 +02:00
|
|
|
const fs = require("fs");
|
|
|
|
const createRegex = require("../utils/createRegex.js");
|
|
|
|
const ipMatch = require("../utils/ipMatch.js");
|
|
|
|
const sanitizeURL = require("../utils/urlSanitizer.js");
|
2024-08-24 17:21:09 +02:00
|
|
|
const matchHostname = require("../utils/matchHostname.js");
|
2024-08-24 08:22:49 +02:00
|
|
|
|
|
|
|
module.exports = (req, res, logFacilities, config, next) => {
|
|
|
|
try {
|
|
|
|
decodeURIComponent(req.parsedURL.pathname);
|
|
|
|
} catch (err) {
|
|
|
|
res.error(400);
|
|
|
|
}
|
|
|
|
|
2024-08-24 20:40:28 +02:00
|
|
|
req.originalParsedURL = req.parsedURL;
|
|
|
|
|
2024-08-24 08:22:49 +02:00
|
|
|
// Handle URL rewriting
|
|
|
|
const rewriteURL = (address, map, callback, _fileState, _mapBegIndex) => {
|
|
|
|
let rewrittenURL = address;
|
|
|
|
let doCallback = true;
|
|
|
|
if (!req.isProxy) {
|
|
|
|
for (let i = _mapBegIndex ? _mapBegIndex : 0; i < map.length; i++) {
|
|
|
|
let mapEntry = map[i];
|
|
|
|
if (
|
2024-08-25 09:14:38 +02:00
|
|
|
req.parsedURL.pathname != "/" &&
|
2024-08-24 08:22:49 +02:00
|
|
|
(mapEntry.isNotDirectory || mapEntry.isNotFile) &&
|
|
|
|
!_fileState
|
|
|
|
) {
|
|
|
|
fs.stat(
|
|
|
|
"." + decodeURIComponent(req.parsedUrl.pathname),
|
|
|
|
(err, stats) => {
|
|
|
|
var _fileState = 3;
|
|
|
|
if (err) {
|
|
|
|
_fileState = 3;
|
|
|
|
} else if (stats.isDirectory()) {
|
|
|
|
_fileState = 2;
|
|
|
|
} else if (stats.isFile()) {
|
|
|
|
_fileState = 1;
|
|
|
|
} else {
|
|
|
|
_fileState = 3;
|
|
|
|
}
|
|
|
|
rewriteURL(address, map, callback, _fileState, i);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
doCallback = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
let tempRewrittenURL = rewrittenURL;
|
|
|
|
if (!mapEntry.allowDoubleSlashes) {
|
|
|
|
address = address.replace(/\/+/g, "/");
|
|
|
|
tempRewrittenURL = address;
|
|
|
|
}
|
|
|
|
if (
|
2024-08-24 17:21:09 +02:00
|
|
|
matchHostname(mapEntry.host, req.headers.host) &&
|
2024-08-24 08:22:49 +02:00
|
|
|
ipMatch(
|
|
|
|
mapEntry.ip,
|
|
|
|
req.socket ? req.socket.localAddress : undefined,
|
|
|
|
) &&
|
|
|
|
address.match(createRegex(mapEntry.definingRegex)) &&
|
|
|
|
!(mapEntry.isNotDirectory && _fileState == 2) &&
|
|
|
|
!(mapEntry.isNotFile && _fileState == 1)
|
|
|
|
) {
|
|
|
|
rewrittenURL = tempRewrittenURL;
|
|
|
|
try {
|
|
|
|
mapEntry.replacements.forEach(function (replacement) {
|
|
|
|
rewrittenURL = rewrittenURL.replace(
|
|
|
|
createRegex(replacement.regex),
|
|
|
|
replacement.replacement,
|
|
|
|
);
|
|
|
|
});
|
|
|
|
if (mapEntry.append) rewrittenURL += mapEntry.append;
|
|
|
|
} catch (err) {
|
|
|
|
doCallback = false;
|
|
|
|
callback(err, null);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (doCallback) callback(null, rewrittenURL);
|
|
|
|
};
|
|
|
|
|
|
|
|
// Rewrite URLs
|
|
|
|
rewriteURL(req.url, config.rewriteMap, function (err, rewrittenURL) {
|
|
|
|
if (err) {
|
|
|
|
res.error(500, err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (rewrittenURL != req.url) {
|
|
|
|
logFacilities.resmessage(
|
|
|
|
"URL rewritten: " + req.url + " => " + rewrittenURL,
|
|
|
|
);
|
|
|
|
req.url = rewrittenURL;
|
|
|
|
try {
|
|
|
|
req.parsedURL = new URL(
|
|
|
|
req.url,
|
|
|
|
"http" +
|
|
|
|
(req.socket.encrypted ? "s" : "") +
|
|
|
|
"://" +
|
|
|
|
(req.headers.host
|
|
|
|
? req.headers.host
|
|
|
|
: config.domain
|
|
|
|
? config.domain
|
|
|
|
: "unknown.invalid"),
|
|
|
|
);
|
|
|
|
} catch (err) {
|
|
|
|
res.error(400, err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-08-24 08:24:39 +02:00
|
|
|
const sHref = sanitizeURL(
|
2024-08-24 08:22:49 +02:00
|
|
|
req.parsedURL.pathname,
|
|
|
|
config.allowDoubleSlashes,
|
|
|
|
);
|
2024-08-24 08:24:39 +02:00
|
|
|
const preparedReqUrl2 =
|
2024-08-24 08:22:49 +02:00
|
|
|
req.parsedURL.pathname + req.parsedURL.search + req.parsedURL.hash;
|
|
|
|
|
|
|
|
if (
|
|
|
|
req.url != preparedReqUrl2 ||
|
|
|
|
sHref !=
|
|
|
|
req.parsedURL.pathname
|
|
|
|
.replace(/\/\.(?=\/|$)/g, "/")
|
|
|
|
.replace(/\/+/g, "/")
|
|
|
|
) {
|
|
|
|
res.error(403);
|
|
|
|
logFacilities.errmessage("Content blocked.");
|
|
|
|
return;
|
|
|
|
} else if (sHref != req.parsedURL.pathname) {
|
2024-08-24 20:45:17 +02:00
|
|
|
var rewrittenAgainURL =
|
|
|
|
sHref + req.parsedURL.search + req.parsedURL.hash;
|
2024-08-24 08:22:49 +02:00
|
|
|
logFacilities.resmessage(
|
|
|
|
"URL sanitized: " + req.url + " => " + rewrittenAgainURL,
|
|
|
|
);
|
|
|
|
req.url = rewrittenAgainURL;
|
|
|
|
try {
|
|
|
|
req.parsedURL = new URL(
|
|
|
|
req.url,
|
|
|
|
"http" +
|
|
|
|
(req.socket.encrypted ? "s" : "") +
|
|
|
|
"://" +
|
|
|
|
(req.headers.host
|
|
|
|
? req.headers.host
|
|
|
|
: config.domain
|
|
|
|
? config.domain
|
|
|
|
: "unknown.invalid"),
|
|
|
|
);
|
|
|
|
} catch (err) {
|
|
|
|
res.error(400, err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
next();
|
|
|
|
});
|
|
|
|
};
|