Unverified Commit 580e2c01 authored by Matthew Finkel's avatar Matthew Finkel
Browse files

Bug 40004: Convert tl-protocol to async.

We resolve the issue of deadlocks within Firefox, and other unknown
bugs, during initialization by using asynchronous programming in the
controller implementation. At the same time, we take advantage of
torbutton's async 'controller' module instead of duplicating logic.
parent 47499873
......@@ -115,7 +115,7 @@ var gBridgeDBBridges = undefined; // Array of bridge lines.
var gBridgeDBRequestEventListeners = [];
function initDialogCommon()
async function initDialogCommon()
{
loadSharedXUL();
......@@ -268,10 +268,10 @@ function resizeDialogToFitContent()
}
function initDialog()
async function initDialog()
{
gIsInitialBootstrap = window.arguments[0];
initDialogCommon();
await initDialogCommon();
if (window.arguments.length > 1)
gInitialPanelID = window.arguments[1];
......@@ -297,8 +297,8 @@ function initDialog()
if (haveWizard)
{
// Add wizardfinish event handler.
document.addEventListener("wizardfinish", (aEvent) => {
if (!onWizardFinish())
document.addEventListener("wizardfinish", function (aEvent) {
if (!(onWizardFinish()))
aEvent.preventDefault();
});
......@@ -375,9 +375,9 @@ function initDialog()
}
function initLocaleDialog()
async function initLocaleDialog()
{
initDialogCommon();
await initDialogCommon();
// Add wizardfinish event handler.
document.addEventListener("wizardfinish", (aEvent) => {
......@@ -590,22 +590,22 @@ function getWizard()
}
function onWizardFirstPanelConnect()
async function onWizardFirstPanelConnect()
{
// If the user configured bridge or proxy settings, prompt before
// discarding their data.
if (isBridgeConfigured() || isProxyConfigured())
showPanel(kWizardDiscardSettingsPageID);
else
removeSettingsAndConnect()
await removeSettingsAndConnect()
}
function removeSettingsAndConnect()
async function removeSettingsAndConnect()
{
applySettings(true); // Use default settings.
await applySettings(true); // Use default settings.
if (!gTorProcessService.TorIsBootstrapDone)
readTorSettings(); // Ensure UI matches the settings that were used.
await readTorSettings(); // Ensure UI matches the settings that were used.
}
......@@ -648,10 +648,10 @@ function onBridgeTypeRadioChange()
}
function onOpenBridgeDBRequestPrompt()
async function onOpenBridgeDBRequestPrompt()
{
// Obtain the meek client path and args from the tor configuration.
let reply = gProtocolSvc.TorGetConf("ClientTransportPlugin");
let reply = await gProtocolSvc.TorGetConf("ClientTransportPlugin");
if (!gProtocolSvc.TorCommandSucceeded(reply))
return;
......@@ -908,8 +908,9 @@ var gObserver = {
showCopyLogButton(true);
if (kTorBootstrapErrorTopic == aTopic)
{
stopTorBootstrap();
showErrorMessage(aSubject.wrappedJSObject, true);
stopTorBootstrap().then((value) => {
showErrorMessage(aSubject.wrappedJSObject, true);
});
}
return;
}
......@@ -1019,7 +1020,7 @@ function updateBootstrapProgress(aStatusObj)
}
function readTorSettings()
async function readTorSettings()
{
TorLauncherLogger.log(2, "readTorSettings " +
"----------------------------------------------");
......@@ -1028,8 +1029,8 @@ function readTorSettings()
try
{
// TODO: retrieve > 1 key at one time inside initProxySettings() et al.
didSucceed = initBridgeSettings() &&
initProxySettings() && initFirewallSettings();
didSucceed = await initBridgeSettings() &&
await initProxySettings() && await initFirewallSettings();
}
catch (e) { TorLauncherLogger.safelog(4, "Error in readTorSettings: ", e); }
......@@ -1048,13 +1049,13 @@ function readTorSettings()
}
function onTorStarted()
async function onTorStarted()
{
if (readTorSettings())
if (await readTorSettings())
{
showPanel();
if (gInitialPanelID)
advanceToWizardPanel(gInitialPanelID);
await advanceToWizardPanel(gInitialPanelID);
}
}
......@@ -1087,7 +1088,7 @@ function showPanel(aPanelID)
// This function assumes that you are starting on the first page.
function advanceToWizardPanel(aPanelID)
async function advanceToWizardPanel(aPanelID)
{
var wizard = getWizard();
if (!wizard)
......@@ -1095,7 +1096,7 @@ function advanceToWizardPanel(aPanelID)
if (kWizardProgressPageID == aPanelID)
{
showProgressPanel();
await showProgressPanel();
return;
}
......@@ -1592,7 +1593,8 @@ function onWizardFinish()
return false;
}
return applySettings(false);
applySettings(false).then(result => { if (result) window.setTimeout(function() { close(); }, 0);});
return false;
}
......@@ -1616,19 +1618,20 @@ function onNetworkSettingsFinish()
return false;
}
return applySettings(false);
applySettings(false).then(result => { if (result) window.setTimeout(function() { close(); }, 0);});
return false;
}
// When the progress panel is open, cancel stops bootstrapping... unless
// we are showing an error, in which case the action is "Reconfigure".
function onProgressCancelOrReconfigure(aWizard)
async function onProgressCancelOrReconfigure(aWizard)
{
let progressContent = document.getElementById("progressContent");
if (!progressContent ||
!progressContent.hasAttribute("isShowingReconfigure"))
{
stopTorBootstrap();
await stopTorBootstrap();
}
if (aWizard)
......@@ -1737,10 +1740,10 @@ function closeHelp()
// Returns true if successful.
function initProxySettings()
async function initProxySettings()
{
let proxyType, proxyAddrPort, proxyUsername, proxyPassword;
let reply = gProtocolSvc.TorGetConfStr(kTorConfKeySocks4Proxy, null);
let reply = await gProtocolSvc.TorGetConfStr(kTorConfKeySocks4Proxy, null);
if (!gProtocolSvc.TorCommandSucceeded(reply))
return false;
......@@ -1751,7 +1754,7 @@ function initProxySettings()
}
else
{
reply = gProtocolSvc.TorGetConfStr(kTorConfKeySocks5Proxy, null);
reply = await gProtocolSvc.TorGetConfStr(kTorConfKeySocks5Proxy, null);
if (!gProtocolSvc.TorCommandSucceeded(reply))
return false;
......@@ -1759,12 +1762,12 @@ function initProxySettings()
{
proxyType = "SOCKS5";
proxyAddrPort = reply.retVal;
reply = gProtocolSvc.TorGetConfStr(kTorConfKeySocks5ProxyUsername, null);
reply = await gProtocolSvc.TorGetConfStr(kTorConfKeySocks5ProxyUsername, null);
if (!gProtocolSvc.TorCommandSucceeded(reply))
return false;
proxyUsername = reply.retVal;
reply = gProtocolSvc.TorGetConfStr(kTorConfKeySocks5ProxyPassword, null);
reply = await gProtocolSvc.TorGetConfStr(kTorConfKeySocks5ProxyPassword, null);
if (!gProtocolSvc.TorCommandSucceeded(reply))
return false;
......@@ -1772,7 +1775,7 @@ function initProxySettings()
}
else
{
reply = gProtocolSvc.TorGetConfStr(kTorConfKeyHTTPSProxy, null);
reply = await gProtocolSvc.TorGetConfStr(kTorConfKeyHTTPSProxy, null);
if (!gProtocolSvc.TorCommandSucceeded(reply))
return false;
......@@ -1780,7 +1783,7 @@ function initProxySettings()
{
proxyType = "HTTP";
proxyAddrPort = reply.retVal;
reply = gProtocolSvc.TorGetConfStr(
reply = await gProtocolSvc.TorGetConfStr(
kTorConfKeyHTTPSProxyAuthenticator, null);
if (!gProtocolSvc.TorCommandSucceeded(reply))
return false;
......@@ -1818,13 +1821,13 @@ function initProxySettings()
// Returns true if successful.
function initFirewallSettings()
async function initFirewallSettings()
{
if (getWizard())
return true; // The wizard does not directly expose firewall settings.
var allowedPorts;
var reply = gProtocolSvc.TorGetConfStr(kTorConfKeyReachableAddresses, null);
var reply = await gProtocolSvc.TorGetConfStr(kTorConfKeyReachableAddresses, null);
if (!gProtocolSvc.TorCommandSucceeded(reply))
return false;
......@@ -1854,7 +1857,7 @@ function initFirewallSettings()
// Returns true if successful.
function initBridgeSettings()
async function initBridgeSettings()
{
let typeList = TorLauncherUtil.defaultBridgeTypes;
let canUseDefaultBridges = (typeList && (typeList.length > 0));
......@@ -1871,14 +1874,14 @@ function initBridgeSettings()
{
showMenuListPlaceholderText(kDefaultBridgeTypeMenuList);
let reply = gProtocolSvc.TorGetConfBool(kTorConfKeyUseBridges, false);
let reply = await gProtocolSvc.TorGetConfBool(kTorConfKeyUseBridges, false);
if (!gProtocolSvc.TorCommandSucceeded(reply))
return false;
useBridges = reply.retVal;
// Get the list of configured bridges from tor.
let bridgeReply = gProtocolSvc.TorGetConf(kTorConfKeyBridgeList);
let bridgeReply = await gProtocolSvc.TorGetConf(kTorConfKeyBridgeList);
if (!gProtocolSvc.TorCommandSucceeded(bridgeReply))
return false;
......@@ -1953,21 +1956,21 @@ function initBridgeSettings()
// Returns true if settings were successfully applied.
function applySettings(aUseDefaults)
async function applySettings(aUseDefaults)
{
TorLauncherLogger.log(2, "applySettings ---------------------" +
"----------------------------------------------");
var didSucceed = false;
try
{
didSucceed = applyBridgeSettings(aUseDefaults) &&
applyProxySettings(aUseDefaults) &&
applyFirewallSettings(aUseDefaults);
didSucceed = await applyBridgeSettings(aUseDefaults) &&
await applyProxySettings(aUseDefaults) &&
await applyFirewallSettings(aUseDefaults);
}
catch (e) { TorLauncherLogger.safelog(4, "Error in applySettings: ", e); }
if (didSucceed)
useSettings();
await useSettings();
TorLauncherLogger.log(2, "applySettings done");
......@@ -1975,11 +1978,11 @@ function applySettings(aUseDefaults)
}
function useSettings()
async function useSettings()
{
var settings = {};
settings[kTorConfKeyDisableNetwork] = false;
let didApply = setConfAndReportErrors(settings, null);
let didApply = await setConfAndReportErrors(settings, null);
if (!didApply)
return;
......@@ -1997,7 +2000,7 @@ function useSettings()
gIsPostRestartBootstrapNeeded = false;
gProtocolSvc.TorSendCommand("SAVECONF");
await gProtocolSvc.TorSendCommand("SAVECONF");
gTorProcessService.TorClearBootstrapError();
// If bootstrapping has finished or we are not responsible for starting
......@@ -2009,11 +2012,11 @@ function useSettings()
return;
}
showProgressPanel();
await showProgressPanel();
}
function stopTorBootstrap()
async function stopTorBootstrap()
{
// Tell tor to disable use of the network; this should stop the bootstrap
// process.
......@@ -2023,7 +2026,7 @@ function stopTorBootstrap()
let settings = {};
settings["DisableNetwork"] = true;
let errObj = {};
if (!gProtocolSvc.TorSetConfWithReply(settings, errObj))
if (!await gProtocolSvc.TorSetConfWithReply(settings, errObj))
TorLauncherLogger.log(5, kErrorPrefix + errObj.details);
}
catch(e)
......@@ -2033,7 +2036,7 @@ function stopTorBootstrap()
}
function showProgressPanel()
async function showProgressPanel()
{
let progressContent = document.getElementById("progressContent");
if (progressContent)
......@@ -2072,7 +2075,7 @@ function showProgressPanel()
// Request the most recent bootstrap status info so that a
// TorBootstrapStatus notification is generated as soon as possible.
gProtocolSvc.TorRetrieveBootstrapStatus();
await gProtocolSvc.TorRetrieveBootstrapStatus();
// Also start a fail-safe timer to ensure that the progress bar is displayed
// within 2 seconds in all cases.
......@@ -2089,14 +2092,14 @@ function showProgressMeterIfNoError()
// Returns true if settings were successfully applied.
function applyProxySettings(aUseDefaults)
async function applyProxySettings(aUseDefaults)
{
let settings = aUseDefaults ? getDefaultProxySettings()
: getAndValidateProxySettings(false);
if (!settings)
return false;
return setConfAndReportErrors(settings, "configureSettings");
return await setConfAndReportErrors(settings, "configureSettings");
}
......@@ -2187,7 +2190,7 @@ function reportValidationError(aStrKey)
// Returns true if settings were successfully applied.
function applyFirewallSettings(aUseDefaults)
async function applyFirewallSettings(aUseDefaults)
{
let settings;
if (aUseDefaults)
......@@ -2200,7 +2203,7 @@ function applyFirewallSettings(aUseDefaults)
if (!settings)
return false;
return setConfAndReportErrors(settings, null);
return await setConfAndReportErrors(settings, null);
}
......@@ -2327,7 +2330,7 @@ function initDefaultBridgeTypeMenu()
// Returns true if settings were successfully applied.
function applyBridgeSettings(aUseDefaults)
async function applyBridgeSettings(aUseDefaults)
{
let settings = (aUseDefaults) ? getDefaultBridgeSettings()
: getAndValidateBridgeSettings();
......@@ -2337,7 +2340,7 @@ function applyBridgeSettings(aUseDefaults)
if (aUseDefaults)
TorLauncherUtil.setCharPref(kPrefDefaultBridgeType, "");
return setConfAndReportErrors(settings, "configureSettings");
return await setConfAndReportErrors(settings, "configureSettings");
}
......@@ -2450,10 +2453,10 @@ function parseAndValidateBridges(aStr)
// Returns true if successful.
// aShowOnErrorPanelID is only used when displaying the wizard.
function setConfAndReportErrors(aSettingsObj, aShowOnErrorPanelID)
async function setConfAndReportErrors(aSettingsObj, aShowOnErrorPanelID)
{
var errObj = {};
var didSucceed = gProtocolSvc.TorSetConfWithReply(aSettingsObj, errObj);
var didSucceed = await gProtocolSvc.TorSetConfWithReply(aSettingsObj, errObj);
if (!didSucceed)
{
if (aShowOnErrorPanelID)
......
......@@ -86,7 +86,7 @@ TorProcessService.prototype =
lockFactory: function(aDoLock) {},
// nsIObserver implementation.
observe: function(aSubject, aTopic, aParam)
observe: async function(aSubject, aTopic, aParam)
{
const kOpenNetworkSettingsTopic = "TorOpenNetworkSettings";
const kUserQuitTopic = "TorUserRequestedQuit";
......@@ -254,7 +254,7 @@ TorProcessService.prototype =
// We configure default bridges each time we start tor in case
// new default bridge preference values are available (e.g., due
// to a TBB update).
this._configureDefaultBridges();
await this._configureDefaultBridges();
}
this.mObsSvc.notifyObservers(null, "TorProcessIsReady", null);
......@@ -706,7 +706,7 @@ TorProcessService.prototype =
return this.kDefaultBridgesStatus_InUse;
},
_configureDefaultBridges: function()
_configureDefaultBridges: async function()
{
var settings = {};
var bridgeArray = TorLauncherUtil.defaultBridges;
......@@ -714,7 +714,7 @@ TorProcessService.prototype =
settings["UseBridges"] = useBridges;
settings["Bridge"] = bridgeArray;
var errObj = {};
var didSucceed = this.mProtocolSvc.TorSetConfWithReply(settings, errObj);
var didSucceed = await this.mProtocolSvc.TorSetConfWithReply(settings, errObj);
// If the network settings wizard was not opened at startup, enable the
// network so that bootstrapping will proceed with the default bridges.
......@@ -722,7 +722,7 @@ TorProcessService.prototype =
{
settings = {};
settings["DisableNetwork"] = false;
if (!this.mProtocolSvc.TorSetConfWithReply(settings,
if (!await this.mProtocolSvc.TorSetConfWithReply(settings,
(didSucceed) ? errObj : null))
{
didSucceed = false;
......
......@@ -14,12 +14,16 @@ const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
setTimeout: "resource://gre/modules/Timer.jsm",
});
XPCOMUtils.defineLazyModuleGetter(this, "TorLauncherUtil",
"resource://torlauncher/modules/tl-util.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "TorLauncherLogger",
"resource://torlauncher/modules/tl-logger.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
"resource://gre/modules/FileUtils.jsm");
let { configureControlPortModule, controller } = Cu.import("resource://torbutton/modules/tor-control-port.js", {});
function TorProtocolService()
......@@ -198,6 +202,12 @@ function TorProtocolService()
TorLauncherLogger.log(3, "SOCKS host: " + this.mSOCKSPortInfo.host);
TorLauncherLogger.log(3, "SOCKS port: " + this.mSOCKSPortInfo.port);
}
// Set the global control port info parameters.
// These values may be overwritten by torbutton when it initializes, but
// torbutton's values *should* be identical.
configureControlPortModule(this.mControlIPCFile, this.mControlHost,
this.mControlPort, this.mControlPassword);
}
catch(e)
{
......@@ -323,13 +333,13 @@ TorProtocolService.prototype =
// Perform a GETCONF command.
// If a fatal error occurs, null is returned. Otherwise, a reply object is
// returned.
TorGetConf: function(aKey)
TorGetConf: async function(aKey)
{
if (!aKey || (aKey.length < 1))
return null;
var cmd = "GETCONF";
var reply = this.TorSendCommand(cmd, aKey);
var reply = await this.TorSendCommand(cmd, aKey);
if (!this.TorCommandSucceeded(reply))
return reply;
......@@ -338,9 +348,9 @@ TorProtocolService.prototype =
// Returns a reply object. If the GETCONF command succeeded, reply.retVal
// is set (if there is no setting for aKey, it is set to aDefault).
TorGetConfStr: function(aKey, aDefault)
TorGetConfStr: async function(aKey, aDefault)
{
var reply = this.TorGetConf(aKey);
var reply = await this.TorGetConf(aKey);
if (this.TorCommandSucceeded(reply))
{
if (reply.lineArray.length > 0)
......@@ -354,9 +364,9 @@ TorProtocolService.prototype =
// Returns a reply object. If the GETCONF command succeeded, reply.retVal
// is set (if there is no setting for aKey, it is set to aDefault).
TorGetConfBool: function(aKey, aDefault)
TorGetConfBool: async function(aKey, aDefault)
{
var reply = this.TorGetConf(aKey);
var reply = await this.TorGetConf(aKey);
if (this.TorCommandSucceeded(reply))
{
if (reply.lineArray.length > 0)
......@@ -376,7 +386,7 @@ TorProtocolService.prototype =
// passed in the SETCONF command.
// If a fatal error occurs, null is returned. Otherwise, a reply object is
// returned.
TorSetConf: function(aSettingsObj)
TorSetConf: async function(aSettingsObj)
{
if (!aSettingsObj)
return null;
......@@ -420,14 +430,14 @@ TorProtocolService.prototype =
return null;
}
return this.TorSendCommand("SETCONF", cmdArgs);
return await this.TorSendCommand("SETCONF", cmdArgs);
}, // TorSetConf()
// Returns true if successful.
// Upon failure, aErrorObj.details will be set to a string.
TorSetConfWithReply: function(aSettingsObj, aErrorObj)
TorSetConfWithReply: async function(aSettingsObj, aErrorObj)
{
var reply = this.TorSetConf(aSettingsObj);
var reply = await this.TorSetConf(aSettingsObj);
var didSucceed = this.TorCommandSucceeded(reply);
if (!didSucceed)
{
......@@ -450,11 +460,11 @@ TorProtocolService.prototype =
},
// If successful, sends a "TorBootstrapStatus" notification.
TorRetrieveBootstrapStatus: function()
TorRetrieveBootstrapStatus: async function()
{
var cmd = "GETINFO";
var key = "status/bootstrap-phase";
var reply = this.TorSendCommand(cmd, key);
var reply = await this.TorSendCommand(cmd, key);
if (!this.TorCommandSucceeded(reply))
{
TorLauncherLogger.log(4, "TorRetrieveBootstrapStatus: command failed");
......@@ -548,7 +558,7 @@ TorProtocolService.prototype =
// Executes a command on the control port.
// Return a reply object or null if a fatal error occurs.
TorSendCommand: function(aCmd, aArgs)
TorSendCommand: async function(aCmd, aArgs)
{
var reply;
for (var attempt = 0; !reply && (attempt < 2); ++attempt)
......@@ -556,10 +566,10 @@ TorProtocolService.prototype =
var conn;
try
{
conn = this._getConnection();
conn = await this._getConnection();
if (conn)
{
reply = this._sendCommand(conn, aCmd, aArgs)
reply = await conn.sendCommand(aCmd + (aArgs ? " " + aArgs : ""));
if (reply)
this._returnConnection(conn); // Return for reuse.
else
......@@ -573,6 +583,22 @@ TorProtocolService.prototype =
}
}
// We failed to acquire the controller after multiple attempts.
// Try again after some time.
if (!conn) {
TorLauncherLogger.safelog(2, "TorSendCommand: Acquiring control connection failed: ", aCmd + ", " + aArgs);
let thisTorSendCommand = this.TorSendCommand.bind(this);
return await new Promise(resolve => setTimeout(function() {
TorLauncherLogger.safelog(2, "TorSendCommand: Trying again, resolving TorSendCommand: ", aCmd + ", " + aArgs);
resolve(thisTorSendCommand(aCmd, aArgs));
}, 250)
)};
if (!reply)
return reply;
reply = this.TorParseCommandResponse(reply);
return reply;
}, // TorSendCommand()
......@@ -581,6 +607,21 @@ TorProtocolService.prototype =
return !!(aReply && (this.kCmdStatusOK == aReply.statusCode));
},