very dirty hack for protoweb support

This commit is contained in:
zefie
2024-05-16 20:55:23 -04:00
parent b968278aee
commit 8cf96ad5c2
3 changed files with 136 additions and 73 deletions

View File

@@ -850,7 +850,7 @@ minisrv-no-mail-count: true`;
if (minisrv_config.config.debug_flags.show_headers) console.log(" * Incoming headers on socket ID", socket.id, (await wtvshared.decodePostData(wtvshared.filterRequestLog(wtvshared.filterSSID(request_headers))))); if (minisrv_config.config.debug_flags.show_headers) console.log(" * Incoming headers on socket ID", socket.id, (await wtvshared.decodePostData(wtvshared.filterRequestLog(wtvshared.filterSSID(request_headers)))));
socket_sessions[socket.id].request_headers = request_headers; socket_sessions[socket.id].request_headers = request_headers;
processPath(socket, urlToPath, request_headers, service_name, shared_romcache, pc_services); processPath(socket, urlToPath, request_headers, service_name, shared_romcache, pc_services);
} else if ((shortURL.indexOf('http://') >= 0 || shortURL.indexOf('https://') >= 0) && !pc_services) { } else if (shortURL.indexOf('http://') >= 0 || shortURL.indexOf('https://') >= 0 || (minisrv_config.services[service_name].use_external_proxy == true && shortURL.indexOf(service_name + "://") >= 0) && !pc_services) {
doHTTPProxy(socket, request_headers); doHTTPProxy(socket, request_headers);
} else if (shortURL.indexOf('file://') >= 0) { } else if (shortURL.indexOf('file://') >= 0) {
shortURL = shortURL.replace("file://",'').replace("romcache", "ROMCache"); shortURL = shortURL.replace("file://",'').replace("romcache", "ROMCache");
@@ -886,14 +886,96 @@ minisrv-no-mail-count: true`;
} }
} }
Array.prototype.replace = function(sub, newSub) {
splits = this.split(sub, 2);
return Array.concat(splits[0], newSub, splits[1])
}
function handleProxy(socket, request_type, request_headers, res, data) {
console.log(` * Proxy Request ${request_type.toUpperCase()} ${res.statusCode} for ${request_headers.request}`)
// an http response error is not a request error, and will come here under the 'end' event rather than an 'error' event.
switch (res.statusCode) {
case 404:
res.headers.Response = res.statusCode + " The publisher can’t find the page requested.";
break;
case 401:
case 403:
res.headers.Response = res.statusCode + " The publisher of that page has not authorized you to use it.";
break;
case 500:
res.headers.Response = res.statusCode + " The publisher of that page can’t be reached.";
break;
default:
res.headers.Response = res.statusCode + " " + res.statusMessage;
break;
}
if (res.headers['Content-yype']) {
res.headers['Content-Type'] = res.headers['Content-Type'];
delete (res.headers['Content-type'])
}
if (res.headers['content-type']) {
res.headers['Content-Type'] = res.headers['content-type'];
delete (res.headers['content-type'])
}
if (res.headers['Content-Type'].substr(0, 4) == "text" && request_type != "http" && request_type != "https") {
var data_t = data.toString().replaceAll("http://", request_type + "://").replaceAll("https://", request_type + "://");
data = [Buffer.from(data_t)]
}
// header pass-through whitelist, case insensitive comparsion to server, however, you should
// specify the header case as you intend for the client
var headers = stripHeaders(res.headers, [
'Connection',
'Server',
'Date',
'Content-Type',
'Cookie',
'Location',
'Accept-Ranges',
'Last-Modified'
]);
headers["wtv-http-proxy"] = true;
headers["wtv-trusted"] = false;
// if Connection: close header, set our internal variable to close the socket
if (headers['Connection']) {
if (headers['Connection'].toLowerCase().indexOf('close') !== -1) {
headers["wtv-connection-close"] = true;
}
}
// if a wtv-explaination is defined for an error code (except 200), define the header here to
// show the 'Explain' button on the client error ShowAlert
if (minisrv_config.services['http']['wtv-explanation']) {
if (minisrv_config.services['http']['wtv-explanation'][res.statusCode]) {
headers['wtv-explanation-url'] = minisrv_config.services['http']['wtv-explanation'][res.statusCode];
}
}
var data_hex = Buffer.concat(data).toString('hex');
if (data_hex.substring(0, 8) == "0d0a0d0a") data_hex = data_hex.substring(8);
if (data_hex.substring(0, 4) == "0a0a") data_hex = data_hex.substring(4);
sendToClient(socket, headers, Buffer.from(data_hex, 'hex'));
}
async function doHTTPProxy(socket, request_headers) { async function doHTTPProxy(socket, request_headers) {
var request_type = (request_headers.request_url.substring(0, 5) == "https") ? "https" : "http"; // detect protocol name
var idx = request_headers.request_url.indexOf('/') - 1;
var request_type = request_headers.request_url.substring(0, idx);
if (minisrv_config.config.debug_flags.show_headers) console.log(request_type.toUpperCase() + " Proxy: Client Request Headers on socket ID", socket.id, (await wtvshared.decodePostData(wtvshared.filterRequestLog(wtvshared.filterSSID(request_headers))))); if (minisrv_config.config.debug_flags.show_headers) console.log(request_type.toUpperCase() + " Proxy: Client Request Headers on socket ID", socket.id, (await wtvshared.decodePostData(wtvshared.filterRequestLog(wtvshared.filterSSID(request_headers)))));
switch (request_type) { switch (request_type) {
case "https": case "https":
var proxy_agent = https; var proxy_agent = https;
break; break;
case "http": case "http":
case "proto":
var proxy_agent = http; var proxy_agent = http;
break; break;
} }
@@ -911,7 +993,6 @@ async function doHTTPProxy(socket, request_headers) {
} }
for (var i = 0; i < 3; i++) request_url_split.shift(); for (var i = 0; i < 3; i++) request_url_split.shift();
request_data.path = "/" + request_url_split.join('/'); request_data.path = "/" + request_url_split.join('/');
if (request_data.method && request_data.host && request_data.path) { if (request_data.method && request_data.host && request_data.path) {
var options = { var options = {
@@ -949,91 +1030,61 @@ async function doHTTPProxy(socket, request_headers) {
options.port = minisrv_config.services[request_type].external_proxy_port; options.port = minisrv_config.services[request_type].external_proxy_port;
options.path = request_headers.request.split(' ')[1]; options.path = request_headers.request.split(' ')[1];
options.headers.Host = request_data.host + ":" + request_data.port; options.headers.Host = request_data.host + ":" + request_data.port;
options.headers.Connection = 'close';
options.insecureHTTPParser = true;
if (minisrv_config.services[request_type].replace_protocol) {
options.path = options.path.replace(request_type, minisrv_config.services[request_type].replace_protocol);
}
}
if (minisrv_config.services[request_type].external_proxy_is_http1) {
options.insecureHTTPParser = true;
options.headers.Connection = 'close'
} }
} }
const req = proxy_agent.request(options, function (res) { const req = proxy_agent.request(options, function (res) {
var data = []; var data = [];
var data_handled = false;
res.on('data', d => { res.on('data', d => {
data.push(d); data.push(d);
}) })
res.on('error', function (err) { res.on('error', function (err) {
console.log(" * Unhandled Proxy Request Error:", err); // hack for Protoweb ECONNRESET
if (minisrv_config.services[request_type].external_proxy_is_http1 && data.length > 0 && !data_handled) {
handleProxy(socket, request_type, request_headers, res, data);
data_handled = true
} else {
console.log(" * Unhandled Proxy Request Error:", err);
}
}); });
res.on('end', function () { res.on('end', function () {
var data_hex = Buffer.concat(data).toString('hex'); // Hack for when old http proxies behave correctly
if (!minisrv_config.services[request_type].external_proxy_is_http1 || data.length > 0 && !data_handled) {
console.log(` * Proxy Request ${request_type.toUpperCase()} ${res.statusCode} for ${request_headers.request}`) handleProxy(socket, request_type, request_headers, res, data);
// an http response error is not a request error, and will come here under the 'end' event rather than an 'error' event. data_handled = true;
switch (res.statusCode) {
case 404:
res.headers.Response = res.statusCode + " The publisher can&#146;t find the page requested.";
break;
case 401:
case 403:
res.headers.Response = res.statusCode + " The publisher of that page has not authorized you to use it.";
break;
case 500:
res.headers.Response = res.statusCode + " The publisher of that page can&#146;t be reached.";
break;
default:
res.headers.Response = res.statusCode + " " + res.statusMessage;
break;
} }
// header pass-through whitelist, case insensitive comparsion to server, however, you should
// specify the header case as you intend for the client
var headers = stripHeaders(res.headers, [
'Connection',
'Server',
'Date',
'Content-Type',
'Cookie',
'Location',
'Accept-Ranges',
'Last-Modified'
]);
headers["wtv-http-proxy"] = true;
headers["wtv-trusted"] = false;
// if Connection: close header, set our internal variable to close the socket
if (headers['Connection']) {
if (headers['Connection'].toLowerCase().indexOf('close') !== -1) {
headers["wtv-connection-close"] = true;
}
}
// if a wtv-explaination is defined for an error code (except 200), define the header here to
// show the 'Explain' button on the client error ShowAlert
if (minisrv_config.services['http']['wtv-explanation']) {
if (minisrv_config.services['http']['wtv-explanation'][res.statusCode]) {
headers['wtv-explanation-url'] = minisrv_config.services['http']['wtv-explanation'][res.statusCode];
}
}
if (data_hex.substring(0, 8) == "0d0a0d0a") data_hex = data_hex.substring(8);
if (data_hex.substring(0, 4) == "0a0a") data_hex = data_hex.substring(4);
sendToClient(socket, headers, Buffer.from(data_hex, 'hex'));
}); });
}).on('error', function (err) { }).on('error', function (err) {
// severe errors, such as unable to connect. // severe errors, such as unable to connect.
var errpage, headers, data = null; var errpage, headers, data = null;
if (err.code == "ENOTFOUND" || err.message.indexOf("HostUnreachable") > 0) { if (err.code == "ENOTFOUND" || err.message.indexOf("HostUnreachable") > 0) {
errpage = wtvshared.doErrorPage(400, `The publisher <b>${request_data.host}</b> is unknown.`); errpage = wtvshared.doErrorPage(400, `The publisher <b>${request_data.host}</b> is unknown.`);
} else {
if (minisrv_config.services[request_type].external_proxy_is_http1 && !data_handled) {
handleProxy(socket, request_type, request_headers, res, data);
data_handled = true;
} else {
console.log(" * Unhandled Proxy Request Error:", err);
errpage = wtvshared.doErrorPage(400);
headers = errpage[0];
data = errpage[1];
sendToClient(socket, headers, data);
}
} }
else {
console.log(" * Unhandled Proxy Request Error:", err); });
errpage = wtvshared.doErrorPage(400);
}
headers = errpage[0];
data = errpage[1];
sendToClient(socket, headers, data);
});;
if (request_headers.post_data) { if (request_headers.post_data) {
req.write(Buffer.from(request_headers.post_data.toString(CryptoJS.enc.Hex), 'hex'), function () { req.write(Buffer.from(request_headers.post_data.toString(CryptoJS.enc.Hex), 'hex'), function () {
req.end(); req.end();

View File

@@ -285,7 +285,7 @@
"port": 1643, "port": 1643,
"connections": 3, "connections": 3,
"enable_multi_query": true, "enable_multi_query": true,
"max_pages": 4, "max_pages": 4,
"publish_mode": "service", // "service" or "directory" "publish_mode": "service", // "service" or "directory"
"publish_dest": "pb_services", // service name, or directory path "publish_dest": "pb_services", // service name, or directory path
"modules": [ "modules": [
@@ -311,13 +311,25 @@
"external_proxy_port": 1080, // Port of proxy "external_proxy_port": 1080, // Port of proxy
"flags": "0x00000001" "flags": "0x00000001"
}, },
"pb_services": { "proto": {
"port": 1650,
"connections": 3,
"use_external_proxy": true,
"replace_protocol": "http",
"external_proxy_is_socks": false,
"external_proxy_host": "wayback.protoweb.org",
"external_proxy_port": 7851,
"external_proxy_is_http1": true,
"flags": "0x00000001"
},
"pb_services": {
// PC Services for PageBuilder // PC Services for PageBuilder
"port": 1697, "port": 1697,
"pc_services": true, // defines service as a PC service "pc_services": true, // defines service as a PC service
"hide_minisrv_version": true, // hide or show the minisrv version (eg like Apache version, can be hidden to hide known exploits for older version) "hide_minisrv_version": true, // hide or show the minisrv version (eg like Apache version, can be hidden to hide known exploits for older version)
"servicevault_dir": "http_pb", // The service vault dir for the PC Services for PageBuilder "servicevault_dir": "http_pb", // The service vault dir for the PC Services for PageBuilder
"service_vaults": ["PageBuilderVault"], // additional service vaults for this service "service_vaults": [ "PageBuilderVault" ], // additional service vaults for this service
"drop_connection_on_wrong_port": true, // If true, resets connection if the PC browser connects to a port that is not PC Services enabled "drop_connection_on_wrong_port": true, // If true, resets connection if the PC browser connects to a port that is not PC Services enabled
"show_verbose_errors": false, // extra debugging "show_verbose_errors": false, // extra debugging
"allow_https": false, // for future use with LetsEncrypt "allow_https": false, // for future use with LetsEncrypt

View File

@@ -1,12 +1,12 @@
{ {
"name": "zefie_wtvp_minisrv", "name": "zefie_wtvp_minisrv",
"version": "0.9.47", "version": "0.9.49",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "zefie_wtvp_minisrv", "name": "zefie_wtvp_minisrv",
"version": "0.9.47", "version": "0.9.49",
"license": "GPL3", "license": "GPL3",
"dependencies": { "dependencies": {
"@mafintosh/vm2": "^3.9.2", "@mafintosh/vm2": "^3.9.2",