[tbb-commits] [tor-launcher/master] Bug 20111: use Unix domain sockets for SOCKS port by default
gk at torproject.org
gk at torproject.org
Thu Oct 20 16:05:48 UTC 2016
commit 8ca52414916c3d8bc2a2974017d759901ddc1736
Author: Kathy Brade <brade at pearlcrescent.com>
Date: Thu Oct 20 09:59:20 2016 -0400
Bug 20111: use Unix domain sockets for SOCKS port by default
New preferences:
extensions.torlauncher.socks_port_use_ipc (Boolean; defaults to true)
extensions.torlauncher.socks_ipc_path (override default IPC path)
extensions.torlauncher.socks_port_flags (SocksPort flags)
Also added support for the TOR_SOCKS_IPC_PATH environment variable.
Consistently use IPC to refer to Unix domain sockets.
Renamed preferences:
extensions.torlauncher.control_port_use_socket is now
extensions.torlauncher.control_port_use_ipc
extensions.torlauncher.control_socket_path is now
extensions.torlauncher.control_ipc_path
Renamed the TOR_CONTROL_SOCKET env variable to TOR_CONTROL_IPC_PATH.
Change _strUnescape() to throw and fix hex and octal unescaping.
---
src/components/tl-process.js | 31 ++++-
src/components/tl-protocol.js | 248 ++++++++++++++++++++++++++++----------
src/defaults/preferences/prefs.js | 27 +++--
src/modules/tl-util.jsm | 30 +++--
4 files changed, 252 insertions(+), 84 deletions(-)
diff --git a/src/components/tl-process.js b/src/components/tl-process.js
index db074b4..844aba1 100644
--- a/src/components/tl-process.js
+++ b/src/components/tl-process.js
@@ -330,8 +330,9 @@ TorProcessService.prototype =
var torrcDefaultsFile =
TorLauncherUtil.getTorFile("torrc-defaults", false);
var hashedPassword = this.mProtocolSvc.TorGetPassword(true);
- var controlSocketFile = this.mProtocolSvc.TorGetControlSocketFile();
+ var controlIPCFile = this.mProtocolSvc.TorGetControlIPCFile();
var controlPort = this.mProtocolSvc.TorGetControlPort();
+ var socksPortInfo = this.mProtocolSvc.TorGetSOCKSPortInfo();
var detailsKey;
if (!exeFile)
@@ -378,10 +379,10 @@ TorProcessService.prototype =
args.push(hashedPassword);
// Include a ControlPort argument to support switching between
- // a TCP port and a Unix domain socket.
+ // a TCP port and an IPC port (e.g., a Unix domain socket).
let controlPortArg;
- if (controlSocketFile)
- controlPortArg = "unix:" + controlSocketFile.path;
+ if (controlIPCFile)
+ controlPortArg = this._ipcPortArg(controlIPCFile);
else if (controlPort)
controlPortArg = "" + controlPort;
if (controlPortArg)
@@ -390,6 +391,21 @@ TorProcessService.prototype =
args.push(controlPortArg);
}
+ // Include a SocksPort argument to support switching between
+ // a TCP port and an IPC port (e.g., a Unix domain socket).
+ if (socksPortInfo)
+ {
+ let socksPortArg = (socksPortInfo.ipcFile)
+ ? this._ipcPortArg(socksPortInfo.ipcFile)
+ : socksPortInfo.host + ':' + socksPortInfo.port;
+ let socksPortFlags = TorLauncherUtil.getCharPref(
+ "extensions.torlauncher.socks_port_flags");
+ if (socksPortFlags)
+ socksPortArg += ' ' + socksPortFlags;
+ args.push("SocksPort");
+ args.push(socksPortArg);
+ }
+
var pid = this._getpid();
if (0 != pid)
{
@@ -454,6 +470,13 @@ TorProcessService.prototype =
}
}, // _startTor()
+ // Return a ControlPort or SocksPort argument for aIPCFile (an nsIFile).
+ // The result is unix:/path or unix:"/path with spaces" with appropriate
+ // C-style escaping within the path portion.
+ _ipcPortArg: function(aIPCFile)
+ {
+ return "unix:" + this.mProtocolSvc.TorEscapeString(aIPCFile.path);
+ },
_controlTor: function()
{
diff --git a/src/components/tl-protocol.js b/src/components/tl-protocol.js
index d394b53..12f3910 100644
--- a/src/components/tl-protocol.js
+++ b/src/components/tl-protocol.js
@@ -38,10 +38,10 @@ function TorProtocolService()
.getService(Ci.nsIEnvironment);
// Determine how Tor Launcher will connect to the Tor control port.
// Environment variables get top priority followed by preferences.
- if (!isWindows && env.exists("TOR_CONTROL_SOCKET"))
+ if (!isWindows && env.exists("TOR_CONTROL_IPC_PATH"))
{
- let socketPath = env.get("TOR_CONTROL_SOCKET");
- this.mControlSocketFile = new FileUtils.File(socketPath);
+ let ipcPath = env.get("TOR_CONTROL_IPC_PATH");
+ this.mControlIPCFile = new FileUtils.File(ipcPath);
}
else
{
@@ -51,13 +51,10 @@ function TorProtocolService()
if (env.exists("TOR_CONTROL_PORT"))
this.mControlPort = parseInt(env.get("TOR_CONTROL_PORT"), 10);
- let useSocket = !isWindows && TorLauncherUtil.getBoolPref(
- "extensions.torlauncher.control_port_use_socket", true);
- if (!this.mControlHost && !this.mControlPort && useSocket)
- {
- this.mControlSocketFile = TorLauncherUtil.getTorFile("control_socket",
- false);
- }
+ let useIPC = !isWindows && TorLauncherUtil.getBoolPref(
+ "extensions.torlauncher.control_port_use_ipc", true);
+ if (!this.mControlHost && !this.mControlPort && useIPC)
+ this.mControlIPCFile = TorLauncherUtil.getTorFile("control_ipc", false);
else
{
if (!this.mControlHost)
@@ -75,7 +72,9 @@ function TorProtocolService()
// Populate mControlPassword so it is available when starting tor.
if (env.exists("TOR_CONTROL_PASSWD"))
+ {
this.mControlPassword = env.get("TOR_CONTROL_PASSWD");
+ }
else if (env.exists("TOR_CONTROL_COOKIE_AUTH_FILE"))
{
// TODO: test this code path (TOR_CONTROL_COOKIE_AUTH_FILE).
@@ -86,6 +85,117 @@ function TorProtocolService()
if (!this.mControlPassword)
this.mControlPassword = this._generateRandomPassword();
+
+ // Determine what kind of SOCKS port Tor and the browser will use.
+ // On Windows (where Unix domain sockets are not supported), TCP is
+ // always used.
+ //
+ // The following environment variables are supported and take
+ // precedence over preferences:
+ // TOR_SOCKS_IPC_PATH (file system path; ignored on Windows)
+ // TOR_SOCKS_HOST
+ // TOR_SOCKS_PORT
+ //
+ // The following preferences are consulted:
+ // network.proxy.socks
+ // network.proxy.socks_port
+ // extensions.torlauncher.socks_port_use_ipc (Boolean)
+ // extensions.torlauncher.socks_ipc_path (file system path)
+ // If extensions.torlauncher.socks_ipc_path is empty, a default
+ // path is used (<tor-data-directory>/socks.socket).
+ //
+ // When using TCP, if a value is not defined via an env variable it is
+ // taken from the corresponding browser preference if possible. The
+ // exceptions are:
+ // If network.proxy.socks contains a file: URL, a default value of
+ // "127.0.0.1" is used instead.
+ // If the network.proxy.socks_port value is 0, a default value of
+ // 9150 is used instead.
+ //
+ // Supported scenarios:
+ // 1. By default, an IPC object at a default path is used.
+ // 2. If extensions.torlauncher.socks_port_use_ipc is set to false,
+ // a TCP socket at 127.0.0.1:9150 is used, unless different values
+ // are set in network.proxy.socks and network.proxy.socks_port.
+ // 3. If the TOR_SOCKS_IPC_PATH env var is set, an IPC object at that
+ // path is used (e.g., a Unix domain socket).
+ // 4. If the TOR_SOCKS_HOST and/or TOR_SOCKS_PORT env vars are set, TCP
+ // is used. Values not set via env vars will be taken from the
+ // network.proxy.socks and network.proxy.socks_port prefs as described
+ // above.
+ // 5. If extensions.torlauncher.socks_port_use_ipc is true and
+ // extensions.torlauncher.socks_ipc_path is set, an IPC object at
+ // the specified path is used.
+ // 6. Tor Launcher is disabled. Torbutton will respect the env vars if
+ // present; if not, the values in network.proxy.socks and
+ // network.proxy.socks_port are used without modification.
+
+ let useIPC;
+ this.mSOCKSPortInfo = { ipcFile: undefined, host: undefined, port: 0 };
+ if (!isWindows && env.exists("TOR_SOCKS_IPC_PATH"))
+ {
+ let ipcPath = env.get("TOR_SOCKS_IPC_PATH");
+ this.mSOCKSPortInfo.ipcFile = new FileUtils.File(ipcPath);
+ useIPC = true;
+ }
+ else
+ {
+ // Check for TCP host and port environment variables.
+ if (env.exists("TOR_SOCKS_HOST"))
+ {
+ this.mSOCKSPortInfo.host = env.get("TOR_SOCKS_HOST");
+ useIPC = false;
+ }
+ if (env.exists("TOR_SOCKS_PORT"))
+ {
+ this.mSOCKSPortInfo.port = parseInt(env.get("TOR_SOCKS_PORT"), 10);
+ useIPC = false;
+ }
+ }
+
+ if (useIPC === undefined)
+ {
+ useIPC = !isWindows && TorLauncherUtil.getBoolPref(
+ "extensions.torlauncher.socks_port_use_ipc", true);
+ }
+
+ // Fill in missing SOCKS info from prefs.
+ if (useIPC)
+ {
+ if (!this.mSOCKSPortInfo.ipcFile)
+ {
+ this.mSOCKSPortInfo.ipcFile =
+ TorLauncherUtil.getTorFile("socks_ipc", false);
+ }
+ }
+ else
+ {
+ if (!this.mSOCKSPortInfo.host)
+ {
+ let socksAddr = TorLauncherUtil.getCharPref("network.proxy.socks",
+ "127.0.0.1");
+ let socksAddrHasHost = (socksAddr && !socksAddr.startsWith("file:"));
+ this.mSOCKSPortInfo.host = socksAddrHasHost ? socksAddr : "127.0.0.1";
+ }
+
+ if (!this.mSOCKSPortInfo.port)
+ {
+ let socksPort = TorLauncherUtil.getIntPref("network.proxy.socks_port",
+ 0);
+ this.mSOCKSPortInfo.port = (socksPort != 0) ? socksPort : 9150;
+ }
+ }
+
+ TorLauncherLogger.log(3, "SOCKS port type: " + (useIPC ? "IPC" : "TCP"));
+ if (useIPC)
+ {
+ TorLauncherLogger.log(3, "ipcFile: " + this.mSOCKSPortInfo.ipcFile.path);
+ }
+ else
+ {
+ TorLauncherLogger.log(3, "SOCKS host: " + this.mSOCKSPortInfo.host);
+ TorLauncherLogger.log(3, "SOCKS port: " + this.mSOCKSPortInfo.port);
+ }
}
catch(e)
{
@@ -164,12 +274,12 @@ TorProtocolService.prototype =
kCmdStatusOK: 250,
kCmdStatusEventNotification: 650,
- TorGetControlSocketFile: function()
+ TorGetControlIPCFile: function()
{
- if (!this.mControlSocketFile)
+ if (!this.mControlIPCFile)
return undefined;
- return this.mControlSocketFile.clone();
+ return this.mControlIPCFile.clone();
},
TorGetControlPort: function()
@@ -184,6 +294,18 @@ TorProtocolService.prototype =
return (aPleaseHash) ? this._hashPassword(pw) : pw;
},
+ TorGetSOCKSPortInfo: function()
+ {
+ return this.mSOCKSPortInfo;
+ },
+
+ // Escape non-ASCII characters for use within the Tor Control protocol.
+ // Returns a string.
+ TorEscapeString: function(aStr)
+ {
+ return this._strEscape(aStr);
+ },
+
// NOTE: Many Tor protocol functions return a reply object, which is a
// a JavaScript object that has the following fields:
// reply.statusCode -- integer, e.g., 250
@@ -373,11 +495,13 @@ TorProtocolService.prototype =
else
{
token = tokenAndVal.substring(0, idx);
- var valObj = {};
- if (!this._strUnescape(tokenAndVal.substring(idx + 1), valObj))
- continue; // skip this token/value pair.
+ try
+ {
+ val = this._strUnescape(tokenAndVal.substring(idx + 1));
+ } catch (e) {}
- val = valObj.result;
+ if (!val)
+ continue; // skip this token/value pair.
}
if ("BOOTSTRAP" == token)
@@ -548,8 +672,9 @@ TorProtocolService.prototype =
mConsoleSvc: null,
mControlPort: null,
mControlHost: null,
- mControlSocketFile: null, // An nsIFile if using a UNIX domain socket.
+ mControlIPCFile: null, // An nsIFile if using IPC for control port.
mControlPassword: null, // JS string that contains hex-encoded password.
+ mSOCKSPortInfo: null, // An object that contains ipcFile, host, port.
mControlConnection: null, // This is cached and reused.
mEventMonitorConnection: null,
mEventMonitorBuffer: null,
@@ -600,27 +725,28 @@ TorProtocolService.prototype =
let sts = Cc["@mozilla.org/network/socket-transport-service;1"]
.getService(Ci.nsISocketTransportService);
let socket;
- if (this.mControlSocketFile)
+ if (this.mControlIPCFile)
{
- let exists = this.mControlSocketFile.exists();
+ let exists = this.mControlIPCFile.exists();
if (!exists)
{
- TorLauncherLogger.log(5, "Control port socket does not exist: " +
- this.mControlSocketFile.path);
+ TorLauncherLogger.log(5, "Control port IPC object does not exist: " +
+ this.mControlIPCFile.path);
}
else
{
- let isSpecial = this.mControlSocketFile.isSpecial();
+ let isSpecial = this.mControlIPCFile.isSpecial();
if (!isSpecial)
{
- TorLauncherLogger.log(5, "Control port socket is not a socket: " +
- this.mControlSocketFile.path);
+ TorLauncherLogger.log(5,
+ "Control port IPC object is not a special file: " +
+ this.mControlIPCFile.path);
}
else
{
- TorLauncherLogger.log(2, "Opening control connection to socket " +
- this.mControlSocketFile.path);
- socket = sts.createUnixDomainTransport(this.mControlSocketFile);
+ TorLauncherLogger.log(2, "Opening control connection to " +
+ this.mControlIPCFile.path);
+ socket = sts.createUnixDomainTransport(this.mControlIPCFile);
}
}
}
@@ -855,14 +981,16 @@ TorProtocolService.prototype =
}
else
{
- var valObj = {};
- if (!this._strUnescape(line.substring(prefixLen), valObj))
+ try
{
- TorLauncherLogger.safelog(4, "Invalid string within " + aCmd +
+ let s = this._strUnescape(line.substring(prefixLen));
+ tmpArray.push(s);
+ }
+ catch (e)
+ {
+ TorLauncherLogger.safelog(4, e + " within " + aCmd +
" response: ", line);
}
- else
- tmpArray.push(valObj.result);
}
}
@@ -954,25 +1082,20 @@ TorProtocolService.prototype =
// Unescape Tor Control string aStr (removing surrounding "" and \ escapes).
// Based on Vidalia's src/common/stringutil.cpp:string_unescape().
- // Returns true if successful and sets aResultObj.result.
- _strUnescape: function(aStr, aResultObj)
+ // Returns the unescaped string. Throws upon failure.
+ // Within Torbutton, the file modules/utils.js also contains a copy of
+ // _strUnescape().
+ _strUnescape: function(aStr)
{
- if (!aResultObj)
- return false;
-
if (!aStr)
- {
- aResultObj.result = aStr;
- return true;
- }
+ return aStr;
var len = aStr.length;
if ((len < 2) || ('"' != aStr.charAt(0)) || ('"' != aStr.charAt(len - 1)))
- {
- aResultObj.result = aStr;
- return true;
- }
+ return aStr;
+ const kHexRE = /[0-9A-Fa-f]{2}/;
+ const kOctalRE = /[0-7]{3}/;
var rv = "";
var i = 1;
var lastCharIndex = len - 2;
@@ -982,7 +1105,7 @@ TorProtocolService.prototype =
if ('\\' == c)
{
if (++i > lastCharIndex)
- return false; // error: \ without next character.
+ throw new Error("missing character after \\");
c = aStr.charAt(i);
if ('n' == c)
@@ -994,24 +1117,26 @@ TorProtocolService.prototype =
else if ('x' == c)
{
if ((i + 2) > lastCharIndex)
- return false; // error: not enough hex characters.
+ throw new Error("not enough hex characters");
- var val = parseInt(aStr.substr(i, 2), 16);
- if (isNaN(val))
- return false; // error: invalid hex characters.
+ let s = aStr.substr(i + 1, 2);
+ if (!kHexRE.test(s))
+ throw new Error("invalid hex character");
+ let val = parseInt(s, 16);
rv += String.fromCharCode(val);
- i += 2;
+ i += 3;
}
else if (this._isDigit(c))
{
- if ((i + 3) > lastCharIndex)
- return false; // error: not enough octal characters.
+ let s = aStr.substr(i, 3);
+ if ((i + 2) > lastCharIndex)
+ throw new Error("not enough octal characters");
- var val = parseInt(aStr.substr(i, 3), 8);
- if (isNaN(val))
- return false; // error: invalid octal characters.
+ if (!kOctalRE.test(s))
+ throw new Error("invalid octal character");
+ let val = parseInt(s, 8);
rv += String.fromCharCode(val);
i += 3;
}
@@ -1022,7 +1147,7 @@ TorProtocolService.prototype =
}
}
else if ('"' == c)
- return false; // error: unescaped double quote in middle of string.
+ throw new Error("unescaped \" within string");
else
{
rv += c;
@@ -1031,10 +1156,7 @@ TorProtocolService.prototype =
}
// Convert from UTF-8 to Unicode. TODO: is UTF-8 always used in protocol?
- rv = decodeURIComponent(escape(rv));
-
- aResultObj.result = rv;
- return true;
+ return decodeURIComponent(escape(rv));
}, // _strUnescape()
// Returns a random 16 character password, hex-encoded.
@@ -1188,6 +1310,8 @@ TorProtocolService.prototype =
return rv;
},
+ // Within Torbutton, the file modules/utils.js also contains a copy of
+ // _isDigit().
_isDigit: function(aChar)
{
const kRE = /^\d$/;
diff --git a/src/defaults/preferences/prefs.js b/src/defaults/preferences/prefs.js
index 500fe2f..875c300 100644
--- a/src/defaults/preferences/prefs.js
+++ b/src/defaults/preferences/prefs.js
@@ -9,17 +9,30 @@ pref("extensions.torlauncher.loglevel", 4); // 1=verbose, 2=debug, 3=info, 4=no
pref("extensions.torlauncher.logmethod", 1); // 0=stdout, 1=errorconsole, 2=debuglog
pref("extensions.torlauncher.max_tor_log_entries", 1000);
-// By default, a Unix domain socket at a default location is used for
-// the Tor control port.
-// Change control_port_use_socket to false to use a TCP connection
-// instead, as defined by control_host and control_port.
-// Modify control_socket_path to override the default socket location. If a
+// By default, an IPC object (e.g., a Unix domain socket) at a default
+// location is used for the Tor control port.
+// Change control_port_use_ipc to false to use a TCP connection instead, as
+// defined by control_host and control_port.
+// Modify control_ipc_path to override the default IPC object location. If a
// relative path is used, it is handled like torrc_path (see below).
-pref("extensions.torlauncher.control_port_use_socket", true);
-pref("extensions.torlauncher.control_socket_path", "");
+pref("extensions.torlauncher.control_port_use_ipc", true);
+pref("extensions.torlauncher.control_ipc_path", "");
pref("extensions.torlauncher.control_host", "127.0.0.1");
pref("extensions.torlauncher.control_port", 9151);
+// By default, an IPC object (e.g., a Unix domain socket) at a default
+// location is used for the Tor SOCKS port.
+// Change socks_port_use_ipc to false to use a TCP connection. When a
+// TCP connection is used, the host is taken from the network.proxy.socks
+// pref and the port is taken from the network.proxy.socks_port pref.
+// Modify socks_ipc_path to override the default IPC object location. If a
+// relative path is used, it is handled like torrc_path (see below).
+// Modify socks_port_flags to use a different set of SocksPort flags (but be
+// careful).
+pref("extensions.torlauncher.socks_port_use_ipc", true);
+pref("extensions.torlauncher.socks_ipc_path", "");
+pref("extensions.torlauncher.socks_port_flags", "IPv6Traffic PreferIPv6 KeepAliveIsolateSOCKSAuth");
+
// The tor_path is relative to the application directory. On Linux and
// Windows this is the Browser/ directory that contains the firefox
// executables, and on Mac OS it is the TorBrowser.app directory.
diff --git a/src/modules/tl-util.jsm b/src/modules/tl-util.jsm
index 85b5fbe..a1256fd 100644
--- a/src/modules/tl-util.jsm
+++ b/src/modules/tl-util.jsm
@@ -388,8 +388,8 @@ let TorLauncherUtil = // Public
},
// Returns an nsIFile.
- // If aTorFileType is "control_socket", aCreate is ignored and there is
- // no requirement that the socket exists.
+ // If aTorFileType is "control_ipc" or "socks_ipc", aCreate is ignored
+ // and there is no requirement that the IPC object exists.
// For all other file types, null is returned if the file does not exist
// and it cannot be created (it will be created if aCreate is true).
getTorFile: function(aTorFileType, aCreate)
@@ -400,7 +400,9 @@ let TorLauncherUtil = // Public
let isRelativePath = true;
let isUserData = (aTorFileType != "tor") &&
(aTorFileType != "torrc-defaults");
- let isControlSocket = ("control_socket" == aTorFileType);
+ let isControlIPC = ("control_ipc" == aTorFileType);
+ let isSOCKSIPC = ("socks_ipc" == aTorFileType);
+ let isIPC = isControlIPC || isSOCKSIPC;
let prefName = "extensions.torlauncher." + aTorFileType + "_path";
let path = this.getCharPref(prefName);
if (path)
@@ -435,8 +437,10 @@ let TorLauncherUtil = // Public
path = "Tor/torrc";
else if ("tordatadir" == aTorFileType)
path = "Tor";
- else if (isControlSocket)
+ else if (isControlIPC)
path = "Tor/control.socket";
+ else if (isSOCKSIPC)
+ path = "Tor/socks.socket";
}
else // Linux and others.
{
@@ -448,8 +452,10 @@ let TorLauncherUtil = // Public
path = "Tor/torrc";
else if ("tordatadir" == aTorFileType)
path = "Tor";
- else if (isControlSocket)
+ else if (isControlIPC)
path = "Tor/control.socket";
+ else if (isSOCKSIPC)
+ path = "Tor/socks.socket";
}
}
else if (this.isWindows)
@@ -475,8 +481,10 @@ let TorLauncherUtil = // Public
path = "Data/Tor/torrc";
else if ("tordatadir" == aTorFileType)
path = "Data/Tor";
- else if (isControlSocket)
+ else if (isControlIPC)
path = "Data/Tor/control.socket";
+ else if (isSOCKSIPC)
+ path = "Data/Tor/socks.socket";
}
}
@@ -508,7 +516,7 @@ let TorLauncherUtil = // Public
f.initWithPath(path);
}
- if (!f.exists() && !isControlSocket && aCreate)
+ if (!f.exists() && !isIPC && aCreate)
{
try
{
@@ -524,10 +532,10 @@ let TorLauncherUtil = // Public
}
}
- // If the file exists or the control socket was requested, normalize
- // the path and return a file object. The control socket will be
+ // If the file exists or an IPC object was requested, normalize the path
+ // and return a file object. The control and SOCKS IPC objects will be
// created by tor.
- if (f.exists() || isControlSocket)
+ if (f.exists() || isIPC)
{
try { f.normalize(); } catch(e) {}
return f;
@@ -538,7 +546,7 @@ let TorLauncherUtil = // Public
catch(e)
{
TorLauncherLogger.safelog(4, "getTorFile " + aTorFileType +
- " failed for " + path + ": ", e);
+ " failed for " + path + ": ", e);
}
return null; // File not found or error (logged above).
More information about the tbb-commits
mailing list