mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-06-01 21:30:27 +00:00
265 lines
8.6 KiB
JavaScript
265 lines
8.6 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
// How to run this file:
|
|
// 1. [obtain firefox source code]
|
|
// 2. [build/obtain firefox binaries]
|
|
// 3. run `[path to]/run-mozilla.sh [path to]/xpcshell \
|
|
// [path to]/genIntolerantFallbackList.js \
|
|
// [absolute path to]/IntolerantFallbackList.inc
|
|
|
|
const { 'classes': Cc, 'interfaces': Ci, 'utils': Cu, 'results': Cr } = Components;
|
|
|
|
const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
|
|
const { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {});
|
|
const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
|
|
const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
|
|
|
|
const SEC_ERROR_UNKNOWN_ISSUER = 0x805a1ff3;
|
|
|
|
const OUTPUT = "IntolerantFallbackList.inc";
|
|
const ERROR_OUTPUT = "IntolerantFallbackList.errors";
|
|
const MAX_CONCURRENT_REQUESTS = 5;
|
|
const MAX_RETRIES = 3;
|
|
const REQUEST_TIMEOUT = 30 * 1000;
|
|
const TEST_DOMAINS = {
|
|
"fallback.test": true,
|
|
};
|
|
|
|
const FILE_HEADER = `/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// This is an automatically generated file. If you're not
|
|
// nsNSSIOLayer.cpp, you shouldn't be #including it.
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
static const char* const kIntolerantFallbackList[] =
|
|
{
|
|
`;
|
|
|
|
const FILE_FOOTER = "};\n";
|
|
|
|
let errorTable = {};
|
|
let errorWithoutFallbacks = {};
|
|
|
|
try {
|
|
|
|
if (arguments.length != 1) {
|
|
throw "Usage: genIntolerantFallbackList.js " +
|
|
"<absolute path to IntolerantFallbackList.inc> ";
|
|
}
|
|
|
|
// initialize the error message table
|
|
for (let name in Cr) {
|
|
errorTable[Cr[name]] = name;
|
|
}
|
|
|
|
// disable the current fallback list so it won't interfere with requests we make
|
|
Services.prefs.setBoolPref("security.tls.insecure_fallback_hosts.use_static_list", false);
|
|
|
|
// get the current preload list
|
|
let gIntolerantFallbackList = readCurrentList(arguments[0]);
|
|
|
|
// get the fallback status of each host without whitelist
|
|
let fallbackStatuses = [];
|
|
getFallbackStatuses(gIntolerantFallbackList, fallbackStatuses);
|
|
|
|
// reenable the current fallback list
|
|
Services.prefs.clearUserPref("security.tls.insecure_fallback_hosts.use_static_list");
|
|
|
|
// get the fallback status of each host with whitelist
|
|
for (let entry of fallbackStatuses) {
|
|
entry.errorWithoutFallback = entry.error;
|
|
delete entry.error;
|
|
entry.retries = MAX_RETRIES;
|
|
}
|
|
gIntolerantFallbackList = fallbackStatuses;
|
|
fallbackStatuses = [];
|
|
getFallbackStatuses(gIntolerantFallbackList, fallbackStatuses);
|
|
|
|
// sort the hosts alphabetically
|
|
fallbackStatuses.sort(function(a, b) {
|
|
return (a.name > b.name ? 1 : (a.name < b.name ? -1 : 0));
|
|
});
|
|
|
|
// write the results to a file (this is where we filter out hosts that we
|
|
// either could connect to without list, or couldn't connect to with list)
|
|
output(fallbackStatuses);
|
|
|
|
} catch (e) {
|
|
dump([e, e.stack].join("\n"));
|
|
throw e;
|
|
}
|
|
|
|
function readCurrentList(filename) {
|
|
let currentHosts = [];
|
|
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
|
|
file.initWithPath(filename);
|
|
let fis = Cc["@mozilla.org/network/file-input-stream;1"]
|
|
.createInstance(Ci.nsILineInputStream);
|
|
fis.init(file, -1, -1, Ci.nsIFileInputStream.CLOSE_ON_EOF);
|
|
let line = {};
|
|
let entryRegex = / "([^"]*)",(?: \/\/ (.*))?/;
|
|
while (fis.readLine(line)) {
|
|
let match = entryRegex.exec(line.value);
|
|
if (match) {
|
|
currentHosts.push({
|
|
name: match[1],
|
|
comment: match[2],
|
|
retries: MAX_RETRIES}
|
|
);
|
|
}
|
|
}
|
|
return currentHosts;
|
|
}
|
|
|
|
function getFallbackStatuses(inHosts, outStatuses) {
|
|
const expectedOutputLength = inHosts.length;
|
|
let tmpOutput = [];
|
|
function handleOneHost() {
|
|
let host = inHosts.shift();
|
|
dump("spinning off request to '" + host.name + "' (remaining retries: " +
|
|
host.retries + ")\n");
|
|
getFallbackStatus(host, tmpOutput);
|
|
}
|
|
|
|
for (let i = 0; i < MAX_CONCURRENT_REQUESTS && inHosts.length > 0; i++) {
|
|
handleOneHost();
|
|
}
|
|
while (outStatuses.length != expectedOutputLength) {
|
|
waitForAResponse(tmpOutput);
|
|
let response = tmpOutput.shift();
|
|
dump("request to '" + response.name + "' finished: " +
|
|
errorToString(response.error) + "\n");
|
|
outStatuses.push(response);
|
|
|
|
if (inHosts.length > 0) {
|
|
handleOneHost();
|
|
}
|
|
}
|
|
}
|
|
|
|
function getFallbackStatus(host, resultList) {
|
|
if (TEST_DOMAINS[host.name]) {
|
|
host.error = Cr.NS_OK;
|
|
host.retries--;
|
|
resultList.push(host);
|
|
return;
|
|
}
|
|
let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
|
.createInstance(Ci.nsIXMLHttpRequest);
|
|
let inResultList = false;
|
|
let uri = "https://" + host.name + "/";
|
|
req.open("GET", uri, true);
|
|
req.timeout = REQUEST_TIMEOUT;
|
|
req.channel.notificationCallbacks = new RedirectAndAuthStopper();
|
|
req.onreadystatechange = function(event) {
|
|
if (!inResultList && req.readyState == 4) {
|
|
inResultList = true;
|
|
// If the url is changed, a redirect happened that means
|
|
// a successfull TLS handshake. Treat it as success rather than
|
|
// using a status from the redirect target.
|
|
host.error = uri == req.responseURL ? req.channel.status : Cr.NS_OK;
|
|
host.retries--;
|
|
resultList.push(host);
|
|
}
|
|
};
|
|
|
|
try {
|
|
req.send();
|
|
}
|
|
catch (e) {
|
|
dump("ERROR: exception making request to " + host.name + ": " + e + "\n");
|
|
}
|
|
}
|
|
|
|
// RedirectAndAuthStopper prevents redirects and HTTP authentication
|
|
function RedirectAndAuthStopper() {};
|
|
|
|
RedirectAndAuthStopper.prototype = {
|
|
// nsIChannelEventSink
|
|
asyncOnChannelRedirect: function(oldChannel, newChannel, flags, callback) {
|
|
throw Cr.NS_ERROR_ENTITY_CHANGED;
|
|
},
|
|
|
|
// nsIAuthPrompt2
|
|
promptAuth: function(channel, level, authInfo) {
|
|
return false;
|
|
},
|
|
|
|
asyncPromptAuth: function(channel, callback, context, level, authInfo) {
|
|
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
|
},
|
|
|
|
getInterface: function(iid) {
|
|
return this.QueryInterface(iid);
|
|
},
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannelEventSink,
|
|
Ci.nsIAuthPrompt2])
|
|
};
|
|
|
|
// Since all events are processed on the main thread, and since event
|
|
// handlers are not preemptible, there shouldn't be any concurrency issues.
|
|
function waitForAResponse(outputList) {
|
|
// From <https://developer.mozilla.org/en/XPConnect/xpcshell/HOWTO>
|
|
let threadManager = Cc["@mozilla.org/thread-manager;1"]
|
|
.getService(Ci.nsIThreadManager);
|
|
let mainThread = threadManager.currentThread;
|
|
while (outputList.length == 0) {
|
|
mainThread.processNextEvent(true);
|
|
}
|
|
}
|
|
|
|
function errorToString(error) {
|
|
return error != undefined ? errorTable[error] || ("0x" + error.toString(16)) : error;
|
|
}
|
|
|
|
function formatComment(comment) {
|
|
return comment ? " // " + comment : "";
|
|
}
|
|
|
|
function success(error) {
|
|
return error == Cr.NS_OK || error == SEC_ERROR_UNKNOWN_ISSUER;
|
|
}
|
|
|
|
function output(sortedStatuses) {
|
|
let file = FileUtils.getFile("CurWorkD", [OUTPUT]);
|
|
let errorFile = FileUtils.getFile("CurWorkD", [ERROR_OUTPUT]);
|
|
let fos = FileUtils.openSafeFileOutputStream(file);
|
|
let eos = FileUtils.openSafeFileOutputStream(errorFile);
|
|
writeTo(FILE_HEADER, fos);
|
|
for (let status of sortedStatuses) {
|
|
|
|
if (!TEST_DOMAINS[status.name] &&
|
|
success(status.errorWithoutFallback)) {
|
|
dump("INFO: " + status.name + " does NOT require fallback\n");
|
|
writeTo(status.name + ": worked (" +
|
|
errorToString(status.errorWithoutFallback) + " / " +
|
|
errorToString(status.error) + ")" +
|
|
formatComment(status.comment) + "\n", eos);
|
|
} else {
|
|
if (!success(status.error)) {
|
|
dump("INFO: " + status.name + " is dead?\n");
|
|
writeTo(status.name + ": failed (" +
|
|
errorToString(status.errorWithoutFallback) + " / " +
|
|
errorToString(status.error) + ")" +
|
|
formatComment(status.comment) + "\n", eos);
|
|
}
|
|
dump("INFO: " + status.name + " ON the fallback list\n");
|
|
writeTo(" \"" + status.name + "\"," +
|
|
formatComment(status.comment) + "\n", fos);
|
|
}
|
|
}
|
|
writeTo(FILE_FOOTER, fos);
|
|
FileUtils.closeSafeFileOutputStream(fos);
|
|
FileUtils.closeSafeFileOutputStream(eos);
|
|
}
|
|
|
|
function writeTo(string, fos) {
|
|
fos.write(string, string.length);
|
|
}
|