[tbb-commits] [torbutton/master] Bug 14271: Make Torbutton work with Unix Domain Socket option
gk at torproject.org
gk at torproject.org
Fri Sep 9 16:54:16 UTC 2016
commit 28a55f5bf324f43373006328c5b03793096f71c4
Author: Kathy Brade <brade at pearlcrescent.com>
Date: Fri Sep 9 11:53:23 2016 -0400
Bug 14271: Make Torbutton work with Unix Domain Socket option
Call Tor Launcher's new TorGetControlSocketFile() function to determine
if a Unix domain socket is being used for Tor control port communication
and, if it is, use it instead of a TCP connection.
---
src/chrome/content/tor-circuit-display.js | 20 +++---
src/chrome/content/torbutton.js | 101 +++++++++++++++++++-----------
src/modules/tor-control-port.js | 82 +++++++++++++-----------
3 files changed, 124 insertions(+), 79 deletions(-)
diff --git a/src/chrome/content/tor-circuit-display.js b/src/chrome/content/tor-circuit-display.js
index c99d25d..f1cf2aa 100644
--- a/src/chrome/content/tor-circuit-display.js
+++ b/src/chrome/content/tor-circuit-display.js
@@ -5,9 +5,10 @@
// call earlier functions). The file can be processed
// with docco.js to produce pretty documentation.
//
-// This script is to be embedded in torbutton.xul. It defines a single global function,
-// runTorCircuitDisplay(host, port, password), which activates the automatic Tor
-// circuit display for the current tab and any future tabs.
+// This script is to be embedded in torbutton.xul. It defines a single global
+// function, createTorCircuitDisplay(socketFile, host, port, password), which
+// activates the automatic Tor circuit display for the current tab and any
+// future tabs.
//
// See https://trac.torproject.org/8641
@@ -15,10 +16,10 @@
/* global document, gBrowser, Components */
// ### Main function
-// __createTorCircuitDisplay(host, port, password, enablePrefName)__.
+// __createTorCircuitDisplay(socketFile, host, port, password, enablePrefName)__.
// The single function that prepares tor circuit display. Connects to a tor
-// control port with the given host, port, and password, and binds to
-// a named bool pref whose value determines whether the circuit display
+// control port with the given socketFile or host plus port, and password, and
+// binds to a named bool pref whose value determines whether the circuit display
// is enabled or disabled.
let createTorCircuitDisplay = (function () {
@@ -311,11 +312,11 @@ let syncDisplayWithSelectedTab = (function() {
// ## Main function
-// __setupDisplay(host, port, password, enablePrefName)__.
+// __setupDisplay(socketFile, host, port, password, enablePrefName)__.
// Once called, the Tor circuit display will be started whenever
// the "enablePref" is set to true, and stopped when it is set to false.
// A reference to this function (called createTorCircuitDisplay) is exported as a global.
-let setupDisplay = function (host, port, password, enablePrefName) {
+let setupDisplay = function (socketFile, host, port, password, enablePrefName) {
let myController = null,
stopCollectingIsolationData = null,
stop = function() {
@@ -329,7 +330,8 @@ let setupDisplay = function (host, port, password, enablePrefName) {
},
start = function () {
if (!myController) {
- myController = controller(host, port || 9151, password, function (err) {
+ myController = controller(socketFile, host, port || 9151, password,
+ function (err) {
// An error has occurred.
logger.eclog(5, err);
logger.eclog(5, "Disabling tor display circuit because of an error.");
diff --git a/src/chrome/content/torbutton.js b/src/chrome/content/torbutton.js
index acb4b4b..bdcbc4d 100644
--- a/src/chrome/content/torbutton.js
+++ b/src/chrome/content/torbutton.js
@@ -31,9 +31,11 @@ var m_tb_window_width = window.outerWidth;
var m_tb_tbb = false;
-var m_tb_control_port = null;
-var m_tb_control_host = null;
+var m_tb_control_socket_file = null; // Set if using a UNIX domain socket.
+var m_tb_control_port = null; // Set if not using a socket.
+var m_tb_control_host = null; // Set if not using a socket.
var m_tb_control_pass = null;
+var m_tb_control_desc = null; // For logging.
var m_tb_orig_BrowserOnAboutPageLoad = null;
@@ -314,6 +316,12 @@ function torbutton_init() {
m_tb_prefs.setCharPref(k_tb_last_browser_version_pref, cur_version);
}
+ let tlps;
+ try {
+ tlps = Cc["@torproject.org/torlauncher-protocol-service;1"]
+ .getService(Ci.nsISupports).wrappedJSObject;
+ } catch(e) {}
+
// Bug 1506 P4: These vars are very important for New Identity
var environ = Components.classes["@mozilla.org/process/environment;1"]
.getService(Components.interfaces.nsIEnvironment);
@@ -329,33 +337,48 @@ function torbutton_init() {
} catch(e) {
torbutton_log(4, 'unable to read authentication cookie');
}
- } else {
+ } else try {
// Try to get password from Tor Launcher.
- try {
- let tlps = Cc["@torproject.org/torlauncher-protocol-service;1"]
- .getService(Ci.nsISupports).wrappedJSObject;
- m_tb_control_pass = tlps.TorGetPassword(false);
- } catch(e) {}
- }
+ m_tb_control_pass = tlps.TorGetPassword(false);
+ } catch(e) {}
- if (environ.exists("TOR_CONTROL_PORT")) {
- m_tb_control_port = environ.get("TOR_CONTROL_PORT");
- } else {
- try {
- const kTLControlPortPref = "extensions.torlauncher.control_port";
- m_tb_control_port = m_tb_prefs.getIntPref(kTLControlPortPref);
- } catch(e) {}
- }
+ // Try to get control port socket (an nsIFile) from Tor Launcher, since
+ // Tor Launcher knows how to handle its own preferences and how to
+ // resolve relative paths.
+ try {
+ m_tb_control_socket_file = tlps.TorGetControlSocketFile();
+ } catch(e) {}
- if (environ.exists("TOR_CONTROL_HOST")) {
- m_tb_control_host = environ.get("TOR_CONTROL_HOST");
+ if (m_tb_control_socket_file) {
+ m_tb_control_desc = m_tb_control_socket_file.path;
} else {
- try {
- const kTLControlHostPref = "extensions.torlauncher.control_host";
- m_tb_control_host = m_tb_prefs.getCharPref(kTLControlHostPref);
- } catch(e) {
- m_tb_control_host = "127.0.0.1";
- }
+ if (environ.exists("TOR_CONTROL_PORT")) {
+ m_tb_control_port = environ.get("TOR_CONTROL_PORT");
+ } else {
+ try {
+ const kTLControlPortPref = "extensions.torlauncher.control_port";
+ m_tb_control_port = m_tb_prefs.getIntPref(kTLControlPortPref);
+ } catch(e) {
+ // Since we want to disable some features when Tor Launcher is
+ // not installed (e.g., New Identity), we do not set a default
+ // port value here.
+ }
+ }
+
+ if (m_tb_control_port) {
+ m_tb_control_desc = "" + m_tb_control_port;
+ }
+
+ if (environ.exists("TOR_CONTROL_HOST")) {
+ m_tb_control_host = environ.get("TOR_CONTROL_HOST");
+ } else {
+ try {
+ const kTLControlHostPref = "extensions.torlauncher.control_host";
+ m_tb_control_host = m_tb_prefs.getCharPref(kTLControlHostPref);
+ } catch(e) {
+ m_tb_control_host = "127.0.0.1";
+ }
+ }
}
// Add event listener for about:tor page loads.
@@ -430,7 +453,8 @@ function torbutton_init() {
torbutton_notify_if_update_needed();
torbutton_update_sync_ui();
- createTorCircuitDisplay(m_tb_control_host, m_tb_control_port, m_tb_control_pass,
+ createTorCircuitDisplay(m_tb_control_socket_file, m_tb_control_host,
+ m_tb_control_port, m_tb_control_pass,
"extensions.torbutton.display_circuit");
torbutton_log(3, 'init completed');
@@ -999,9 +1023,15 @@ function torbutton_send_ctrl_cmd(command) {
m_tb_domWindowUtils.suppressEventHandling(false);
try {
- var socketTransportService = Components.classes["@mozilla.org/network/socket-transport-service;1"]
- .getService(Components.interfaces.nsISocketTransportService);
- var socket = socketTransportService.createTransport(null, 0, m_tb_control_host, m_tb_control_port, null);
+ let sts = Cc["@mozilla.org/network/socket-transport-service;1"]
+ .getService(Ci.nsISocketTransportService);
+ let socket;
+ if (m_tb_control_socket_file) {
+ socket = sts.createUnixDomainTransport(m_tb_control_socket_file);
+ } else {
+ socket = sts.createTransport(null, 0, m_tb_control_host,
+ m_tb_control_port, null);
+ }
// If we don't get a response from the control port in 2 seconds, someting is wrong..
socket.setTimeout(Ci.nsISocketTransport.TIMEOUT_READ_WRITE, 2);
@@ -1021,21 +1051,21 @@ function torbutton_send_ctrl_cmd(command) {
var bytes = torbutton_socket_readline(inputStream);
if (bytes.indexOf("250") != 0) {
- torbutton_safelog(4, "Unexpected auth response on control port "+m_tb_control_port+":", bytes);
+ torbutton_safelog(4, "Unexpected auth response on control port "+m_tb_control_desc+":", bytes);
return null;
}
outputStream.writeBytes(command, command.length);
bytes = torbutton_socket_readline(inputStream);
if(bytes.indexOf("250") != 0) {
- torbutton_safelog(4, "Unexpected command response on control port "+m_tb_control_port+":", bytes);
+ torbutton_safelog(4, "Unexpected command response on control port "+m_tb_control_desc+":", bytes);
return null;
}
// Closing these streams prevents a shutdown hang on Mac OS. See bug 10201.
inputStream.close();
outputStream.close();
- socket.close(Components.results.NS_OK);
+ socket.close(Cr.NS_OK);
return bytes.substr(4);
} catch(e) {
torbutton_log(4, "Exception on control port "+e);
@@ -1339,7 +1369,7 @@ function torbutton_do_new_identity() {
torbutton_log(3, "New Identity: Sending NEWNYM");
// We only support TBB for newnym.
- if (!m_tb_control_pass || !m_tb_control_port) {
+ if (!m_tb_control_pass || (!m_tb_control_socket_file && !m_tb_control_port)) {
var warning = torbutton_get_property_string("torbutton.popup.no_newnym");
torbutton_log(5, "Torbutton cannot safely newnym. It does not have access to the Tor Control Port.");
window.alert(warning);
@@ -1497,7 +1527,7 @@ function torbutton_do_tor_check()
const kEnvUseTransparentProxy = "TOR_TRANSPROXY";
var env = Cc["@mozilla.org/process/environment;1"]
.getService(Ci.nsIEnvironment);
- if (m_tb_control_port &&
+ if ((m_tb_control_socket_file || m_tb_control_port) &&
!env.exists(kEnvUseTransparentProxy) &&
!env.exists(kEnvSkipControlPortTest) &&
m_tb_prefs.getBoolPref("extensions.torbutton.local_tor_check")) {
@@ -2096,8 +2126,9 @@ function torbutton_check_protections()
// See https://trac.torproject.org/projects/tor/ticket/10353 for more info.
document.getElementById("torbutton-cookie-protector").hidden = m_tb_prefs.getBoolPref("extensions.torbutton.block_disk");
- if (!m_tb_control_pass || !m_tb_control_port)
+ if (!m_tb_control_pass || (!m_tb_control_socket_file && !m_tb_control_port)) {
document.getElementById("torbutton-new-identity").disabled = true;
+ }
if (!m_tb_tbb && m_tb_prefs.getBoolPref("extensions.torbutton.prompt_torbrowser")) {
torbutton_inform_about_tbb();
diff --git a/src/modules/tor-control-port.js b/src/modules/tor-control-port.js
index 63c2cd9..f23daed 100644
--- a/src/modules/tor-control-port.js
+++ b/src/modules/tor-control-port.js
@@ -7,9 +7,10 @@
//
// To import the module, use
//
-// let { controller } = Components.utils.import("path/to/controlPort.jsm");
+// let { controller } = Components.utils.import("path/to/tor-control-port.js");
//
-// See the last function defined in this file, controller(host, port, password, onError)
+// See the last function defined in this file:
+// controller(socketFile, host, port, password, onError)
// for usage of the controller function.
/* jshint esnext: true */
@@ -41,17 +42,24 @@ log("Loading tor-control-port.js\n");
// I/O utilities namespace
let io = {};
-// __io.asyncSocketStreams(host, port)__.
+// __io.asyncSocketStreams(socketFile, host, port)__.
// Creates a pair of asynchronous input and output streams for a socket at the
-// given host and port.
-io.asyncSocketStreams = function (host, port) {
- let socketTransportService = Cc["@mozilla.org/network/socket-transport-service;1"]
- .getService(Components.interfaces.nsISocketTransportService),
- UNBUFFERED = Ci.nsITransport.OPEN_UNBUFFERED,
- // Create an instance of a socket transport.
- socketTransport = socketTransportService.createTransport(null, 0, host, port, null),
- // Open unbuffered asynchronous outputStream.
- outputStream = socketTransport.openOutputStream(UNBUFFERED, 1, 1)
+// given socketFile or host and port.
+io.asyncSocketStreams = function (socketFile, host, port) {
+ let sts = Cc["@mozilla.org/network/socket-transport-service;1"]
+ .getService(Components.interfaces.nsISocketTransportService),
+ UNBUFFERED = Ci.nsITransport.OPEN_UNBUFFERED;
+
+ // Create an instance of a socket transport.
+ let socketTransport;
+ if (socketFile) {
+ socketTransport = sts.createUnixDomainTransport(socketFile);
+ } else {
+ socketTransport = sts.createTransport(null, 0, host, port, null);
+ }
+
+ // Open unbuffered asynchronous outputStream.
+ let outputStream = socketTransport.openOutputStream(UNBUFFERED, 1, 1)
.QueryInterface(Ci.nsIAsyncOutputStream),
// Open unbuffered asynchronous inputStream.
inputStream = socketTransport.openInputStream(UNBUFFERED, 1, 1)
@@ -91,14 +99,16 @@ io.pumpInputStream = function (inputStream, onInputData, onError) {
} }, null);
};
-// __io.asyncSocket(host, port, onInputData, onError)__.
-// Creates an asynchronous, text-oriented TCP socket at host:port.
+// __io.asyncSocket(socketFile, host, port, onInputData, onError)__.
+// Creates an asynchronous, text-oriented UNIX domain socket (if socketFile
+// is defined) or TCP socket at host:port.
// The onInputData callback should accept a single argument, which will be called
// repeatedly, whenever incoming text arrives. Returns a socket object with two methods:
// socket.write(text) and socket.close(). onError will be passed the error object
// whenever a write fails.
-io.asyncSocket = function (host, port, onInputData, onError) {
- let [inputStream, outputStream] = io.asyncSocketStreams(host, port),
+io.asyncSocket = function (socketFile, host, port, onInputData, onError) {
+ let [inputStream, outputStream] = io.asyncSocketStreams(socketFile, host,
+ port),
pendingWrites = [];
// Run an input stream pump to send incoming data to the onInputData callback.
io.pumpInputStream(inputStream, onInputData, onError);
@@ -243,13 +253,13 @@ io.matchRepliesToCommands = function (asyncSend, dispatcher) {
});
};
-// __io.controlSocket(host, port, password, onError)__.
-// Instantiates and returns a socket to a tor ControlPort at host:port,
-// authenticating with the given password. onError is called with an
+// __io.controlSocket(socketFile, host, port, password, onError)__.
+// Instantiates and returns a socket to a tor ControlPort at socketFile or
+// host:port, authenticating with the given password. onError is called with an
// error object as its single argument whenever an error occurs. Example:
//
// // Open the socket
-// let socket = controlSocket("127.0.0.1", 9151, "MyPassw0rd",
+// let socket = controlSocket(undefined, "127.0.0.1", 9151, "MyPassw0rd",
// function (error) { console.log(error.message || error); });
// // Send command and receive "250" reply or error message
// socket.sendCommand(commandText, replyCallback, errorCallback);
@@ -259,11 +269,11 @@ io.matchRepliesToCommands = function (asyncSend, dispatcher) {
// socket.removeNotificationCallback(callback);
// // Close the socket permanently
// socket.close();
-io.controlSocket = function (host, port, password, onError) {
+io.controlSocket = function (socketFile, host, port, password, onError) {
// Produce a callback dispatcher for Tor messages.
let mainDispatcher = io.callbackDispatcher(),
// Open the socket and convert format to Tor messages.
- socket = io.asyncSocket(host, port,
+ socket = io.asyncSocket(socketFile, host, port,
io.onDataFromOnLine(
io.onLineFromOnMessage(mainDispatcher.pushMessage)),
onError),
@@ -606,15 +616,16 @@ event.watchEvent = function (controlSocket, type, filter, onData) {
let tor = {};
// __tor.controllerCache__.
-// A map from "host:port" to controller objects. Prevents redundant instantiation
-// of control sockets.
+// A map from "unix:socketpath" or "host:port" to controller objects. Prevents
+// redundant instantiation of control sockets.
tor.controllerCache = {};
-// __tor.controller(host, port, password, onError)__.
-// Creates a tor controller at the given host and port, with the given password.
+// __tor.controller(socketFile, host, port, password, onError)__.
+// Creates a tor controller at the given socketFile or host and port, with the
+// given password.
// onError returns asynchronously whenever a connection error occurs.
-tor.controller = function (host, port, password, onError) {
- let socket = io.controlSocket(host, port, password, onError),
+tor.controller = function (socketFile, host, port, password, onError) {
+ let socket = io.controlSocket(socketFile, host, port, password, onError),
isOpen = true;
return { getInfo : key => info.getInfo(socket, key),
getConf : key => info.getConf(socket, key),
@@ -627,27 +638,28 @@ tor.controller = function (host, port, password, onError) {
// ## Export
-// __controller(host, port, password, onError)__.
+// __controller(socketFile, host, port, password, onError)__.
// Instantiates and returns a controller object connected to a tor ControlPort
-// at host:port, authenticating with the given password, if the controller doesn't yet
-// exist. Otherwise returns the existing controller to the given host:port.
+// on socketFile or at host:port, authenticating with the given password, if
+// the controller doesn't yet exist. Otherwise returns the existing controller
+// to the given socketFile or host:port.
// onError is called with an error object as its single argument whenever
// an error occurs. Example:
//
// // Get the controller
-// let c = controller("127.0.0.1", 9151, "MyPassw0rd",
+// let c = controller(undefined, "127.0.0.1", 9151, "MyPassw0rd",
// function (error) { console.log(error.message || error); });
// // Send command and receive `250` reply or error message in a promise:
// let replyPromise = c.getInfo("ip-to-country/16.16.16.16");
// // Close the controller permanently
// c.close();
-var controller = function (host, port, password, onError) {
- let dest = host + ":" + port,
+var controller = function (socketFile, host, port, password, onError) {
+ let dest = (socketFile) ? "unix:" + socketFile.path : host + ":" + port,
maybeController = tor.controllerCache[dest];
return (tor.controllerCache[dest] =
(maybeController && maybeController.isOpen()) ?
maybeController :
- tor.controller(host, port, password, onError));
+ tor.controller(socketFile, host, port, password, onError));
};
// Export the controller function for external use.
More information about the tbb-commits
mailing list