commit 2cea89cad359352ff2a78988e5647a728516f65f Author: Damian Johnson atagar@torproject.org Date: Sun Dec 16 19:46:49 2012 -0800
Dropping the TorCtl connection completely
Removing the TorCtl connection from arm. Barring some lingering imports we should now be able to drop the library from arm. Yay! --- src/cli/controller.py | 27 +++-------- src/cli/headerPanel.py | 27 ++-------- src/starter.py | 122 ++++++++++------------------------------------- src/util/torTools.py | 29 ++++++------ 4 files changed, 53 insertions(+), 152 deletions(-)
diff --git a/src/cli/controller.py b/src/cli/controller.py index 9c3a4eb..f01ef8a 100644 --- a/src/cli/controller.py +++ b/src/cli/controller.py @@ -543,26 +543,13 @@ class TorManager: unsuccessful. """
- torctlConn, authType, authValue = TorCtl.preauth_connect(controlPort = int(CONFIG["wizard.default"]["Control"])) - - if not torctlConn: - msg = "Unable to start tor, try running "tor -f %s" to see the error output" % self.getTorrcPath() - raise IOError(msg) - - if authType == TorCtl.AUTH_TYPE.COOKIE: - try: - authCookieSize = os.path.getsize(authValue) - if authCookieSize != 32: - raise IOError("authentication cookie '%s' is the wrong size (%i bytes instead of 32)" % (authValue, authCookieSize)) - - torctlConn.authenticate(authValue) - - controller = Controller.from_port(control_port = int(CONFIG["wizard.default"]["Control"])) - controller.authenticate() - - torTools.getConn().init(torctlConn, controller) - except Exception, exc: - raise IOError("Unable to connect to Tor: %s" % exc) + try: + controller = Controller.from_port(control_port = int(CONFIG["wizard.default"]["Control"])) + controller.authenticate() + + torTools.getConn().init(controller) + except Exception, exc: + raise IOError("Unable to connect to Tor: %s" % exc)
def shutdownDaemons(): """ diff --git a/src/cli/headerPanel.py b/src/cli/headerPanel.py index c9541c4..ce43e06 100644 --- a/src/cli/headerPanel.py +++ b/src/cli/headerPanel.py @@ -138,25 +138,23 @@ class HeaderPanel(panel.Panel, threading.Thread): if key in (ord('n'), ord('N')) and torTools.getConn().isNewnymAvailable(): self.sendNewnym() elif key in (ord('r'), ord('R')) and not self._isTorConnected: - torctlConn, controller = None, None + controller = None allowPortConnection, allowSocketConnection, _ = starter.allowConnectionTypes()
if os.path.exists(self._config["startup.interface.socket"]) and allowSocketConnection: try: - torctlConn = torTools.connect_socket(self._config["startup.interface.socket"]) - # TODO: um... what about passwords? controller = Controller.from_socket_file(self._config["startup.interface.socket"]) controller.authenticate() except (IOError, stem.SocketError), exc: - torctlConn, controller = None, None + controller = None
if not allowPortConnection: cli.popups.showMsg("Unable to reconnect (%s)" % exc, 3) elif not allowPortConnection: cli.popups.showMsg("Unable to reconnect (socket '%s' doesn't exist)" % self._config["startup.interface.socket"], 3)
- if not torctlConn and allowPortConnection: + if not controller and allowPortConnection: # TODO: This has diverged from starter.py's connection, for instance it # doesn't account for relative cookie paths or multiple authentication # methods. We can't use the starter.py's connection function directly @@ -165,19 +163,6 @@ class HeaderPanel(panel.Panel, threading.Thread):
try: ctlAddr, ctlPort = self._config["startup.interface.ipAddress"], self._config["startup.interface.port"] - tmpConn, authType, authValue = TorCtl.TorCtl.preauth_connect(ctlAddr, ctlPort) - - if authType == TorCtl.TorCtl.AUTH_TYPE.PASSWORD: - authValue = cli.popups.inputPrompt("Controller Password: ") - if not authValue: raise IOError() # cancel reconnection - elif authType == TorCtl.TorCtl.AUTH_TYPE.COOKIE: - authCookieSize = os.path.getsize(authValue) - if authCookieSize != 32: - raise IOError("authentication cookie '%s' is the wrong size (%i bytes instead of 32)" % (authValue, authCookieSize)) - - tmpConn.authenticate(authValue) - torctlConn = tmpConn - controller = Controller.from_port(ctlAddr, ctlPort)
try: @@ -185,7 +170,7 @@ class HeaderPanel(panel.Panel, threading.Thread): except stem.connection.MissingPassword: controller.authenticate(authValue) # already got the password above except Exception, exc: - torctlConn, controller = None, None + controller = None
# attempts to use the wizard port too try: @@ -196,8 +181,8 @@ class HeaderPanel(panel.Panel, threading.Thread): # displays notice for the first failed connection attempt if exc.args: cli.popups.showMsg("Unable to reconnect (%s)" % exc, 3)
- if torctlConn and controller: - torTools.getConn().init(torctlConn, controller) + if controller: + torTools.getConn().init(controller) log.log(log.NOTICE, "Reconnected to Tor's control port") cli.popups.showMsg("Tor reconnected", 1) else: isKeystrokeConsumed = False diff --git a/src/starter.py b/src/starter.py index 8b2bbb3..01a6086 100644 --- a/src/starter.py +++ b/src/starter.py @@ -29,10 +29,8 @@ import util.torInterpretor import util.torTools import util.uiTools
-import TorCtl.TorCtl -import TorCtl.TorUtil - from stem.control import Controller +import stem.connection
LOG_DUMP_PATH = os.path.expanduser("~/.arm/log") DEFAULT_CONFIG = os.path.expanduser("~/.arm/armrc") @@ -207,90 +205,27 @@ def _loadConfigurationDescriptions(pathPrefix): msg = DESC_INTERNAL_LOAD_FAILED_MSG % util.sysTools.getFileErrorMsg(exc) util.log.log(CONFIG["log.configDescriptions.internalLoadFailed"], msg)
-def _torCtlConnect(controlAddr="127.0.0.1", controlPort=9051, passphrase=None, incorrectPasswordMsg="", printError=True): +def _getController(controlAddr="127.0.0.1", controlPort=9051, passphrase=None, incorrectPasswordMsg="", printError=True): """ - Custom handler for establishing a TorCtl connection. + Custom handler for establishing a stem connection (... needs an overhaul). """
- conn, controller = None, None + controller = None try: - #conn, authType, authValue = TorCtl.TorCtl.preauth_connect(controlAddr, controlPort) - conn, authTypes, authValue = util.torTools.preauth_connect_alt(controlAddr, controlPort) - - if TorCtl.TorCtl.AUTH_TYPE.PASSWORD in authTypes: - # password authentication, promting for the password if it wasn't provided - # - # TODO: When handling multi-auth we should try to authenticate via the - # cookie first, then fall back to prompting the user for their password. - # With the stack of fixes and hacks we have here jerry-rigging that in - # without trying cookie auth twice will be a pita so leaving this alone - # for now. Stem will handle most of this transparently, letting us handle - # this much more elegantly. - - if not passphrase: - try: passphrase = getpass.getpass("Controller password: ") - except KeyboardInterrupt: return None, None - - if TorCtl.TorCtl.AUTH_TYPE.COOKIE in authTypes and authValue[0] != "/": - # Connecting to the control port will probably fail if it's using cookie - # authentication and the cookie path is relative (unfortunately this is - # the case for TBB). This is discussed in: - # https://trac.torproject.org/projects/tor/ticket/1101 - # - # This is best effort. If we can't expand the path then it's still - # attempted since we might be running in tor's pwd. - - torPid = util.torTools.getPid(controlPort) - if torPid: - try: conn._cookiePath = util.sysTools.expandRelativePath(authValue, torPid) - except IOError: pass + chroot = util.torTools.getConn().getPathPrefix() + controller = Controller.from_port(controlAddr, controlPort)
- # appends the path prefix if it's set - if TorCtl.TorCtl.AUTH_TYPE.COOKIE in authTypes: - pathPrefix = util.torTools.getConn().getPathPrefix() - - # The os.path.join function is kinda stupid. If given an absolute path - # with the second argument then it will swallow the prefix. Ie... - # os.path.join("/tmp", "/foo") => "/foo" - - if pathPrefix: - pathSuffix = conn._cookiePath - if pathSuffix.startswith("/"): pathSuffix = pathSuffix[1:] - - conn._cookiePath = os.path.join(pathPrefix, pathSuffix) - - # Abort if the file isn't 32 bytes long. This is to avoid exposing - # arbitrary file content to the port. - # - # Without this a malicious socket could, for instance, claim that - # '~/.bash_history' or '~/.ssh/id_rsa' was its authentication cookie to - # trick us into reading it for them with our current permissions. - # - # https://trac.torproject.org/projects/tor/ticket/4305 - + try: + controller.authenticate(password = passphrase, chroot_path = chroot) + except stem.connection.MissingPassword: try: - authCookieSize = os.path.getsize(conn._cookiePath) - if authCookieSize != 32: - raise IOError("authentication cookie '%s' is the wrong size (%i bytes instead of 32)" % (conn._cookiePath, authCookieSize)) - except Exception, exc: - # if the above fails then either... - # - raise an exception if cookie auth is the only method we have to - # authenticate - # - suppress the exception and try the other connection methods if we - # have alternatives - if len(authTypes) == 1: raise exc - else: conn._authTypes.remove(TorCtl.TorCtl.AUTH_TYPE.COOKIE) - - conn.authenticate(passphrase) - - # Damn well everything above this is covered by stem. :P + passphrase = getpass.getpass("Controller password: ") + controller.authenticate(password = passphrase, chroot_path = chroot) + except: + return None
- controller = Controller.from_port(controlAddr, controlPort) - controller.authenticate(password = passphrase, chroot_path = util.torTools.getConn().getPathPrefix()) - - return conn, controller + return controller except Exception, exc: - if conn: conn.close() if controller: controller.close()
# attempts to connect with the default wizard address too @@ -304,19 +239,19 @@ def _torCtlConnect(controlAddr="127.0.0.1", controlPort=9051, passphrase=None, i # connection failure. Otherwise, return the connection result.
if controlPort != wizardPort: - connResult, controller = _torCtlConnect(controlAddr, wizardPort) - if connResult != None: return connResult, controller - else: return None, None # wizard connection attempt, don't print anything + controller = _getController(controlAddr, wizardPort) + if controller != None: return controller + else: return None # wizard connection attempt, don't print anything
if passphrase and str(exc) == "Unable to authenticate: password incorrect": # provide a warning that the provided password didn't work, then try # again prompting for the user to enter it print incorrectPasswordMsg - return _torCtlConnect(controlAddr, controlPort) + return _getController(controlAddr, controlPort) elif printError: print exc
- return None, None + return None
def _dumpConfig(): """ @@ -488,21 +423,16 @@ if __name__ == '__main__': print "Unrecognized event flag: %s" % flag sys.exit()
- # temporarily disables TorCtl logging to prevent issues from going to stdout while starting - TorCtl.TorUtil.loglevel = "NONE" - # By default attempts to connect using the control socket if it exists. This # skips attempting to connect by socket or port if the user has given # arguments for connecting to the other.
- conn, controller = None, None + controller = None allowPortConnection, allowSocketConnection, allowDetachedStart = allowConnectionTypes()
socketPath = param["startup.interface.socket"] if os.path.exists(socketPath) and allowSocketConnection: try: - conn = util.torTools.connect_socket(socketPath) - # TODO: um... what about passwords? # https://trac.torproject.org/6881
@@ -514,12 +444,12 @@ if __name__ == '__main__': elif not allowPortConnection: print "Socket '%s' doesn't exist" % socketPath
- if (not conn or not controller) and allowPortConnection: - # sets up TorCtl connection, prompting for the passphrase if necessary and + if not controller and allowPortConnection: + # sets up stem connection, prompting for the passphrase if necessary and # sending problems to stdout if they arise authPassword = config.get("startup.controlPassword", CONFIG["startup.controlPassword"]) incorrectPasswordMsg = "Password found in '%s' was incorrect" % configPath - conn, controller = _torCtlConnect(controlAddr, controlPort, authPassword, incorrectPasswordMsg, not allowDetachedStart) + controller = _getController(controlAddr, controlPort, authPassword, incorrectPasswordMsg, not allowDetachedStart)
# removing references to the controller password so the memory can be freed # (unfortunately python does allow for direct access to the memory so this @@ -537,7 +467,7 @@ if __name__ == '__main__': if pwLineNum != None: del config.rawContents[i]
- if (conn is None or controller is None) and not allowDetachedStart: sys.exit(1) + if controller is None and not allowDetachedStart: sys.exit(1)
# initializing the connection may require user input (for the password) # skewing the startup time results so this isn't counted @@ -545,8 +475,8 @@ if __name__ == '__main__': controllerWrapper = util.torTools.getConn()
torUser = None - if conn and controller: - controllerWrapper.init(conn, controller) + if controller: + controllerWrapper.init(controller)
# give a notice if tor is running with root torUser = controllerWrapper.getMyUser() diff --git a/src/util/torTools.py b/src/util/torTools.py index 5ac29ea..0357b32 100644 --- a/src/util/torTools.py +++ b/src/util/torTools.py @@ -483,7 +483,6 @@ class Controller(TorCtl.PostEventListener):
def __init__(self): TorCtl.PostEventListener.__init__(self) - self.conn = None # None if uninitialized or controller's been closed self.controller = None self.connLock = threading.RLock() self.statusListeners = [] # callback functions for tor's state changes @@ -522,25 +521,22 @@ class Controller(TorCtl.PostEventListener): # cached parameters for custom getters (None if unset or possibly changed) self._cachedParam = {}
- def init(self, conn, controller): + def init(self, controller): """ Uses the given TorCtl instance for future operations, notifying listeners about the change.
Arguments: - conn - TorCtl instance to be used controller - stem based Controller instance """
# TODO: We should reuse our controller instance so event listeners will be # re-attached. This is a point of regression until we do... :(
- if conn.is_live() and controller.is_alive() and conn != self.conn: + if controller.is_alive() and controller != self.controller: self.connLock.acquire()
- if self.conn: self.close() # shut down current connection - self.conn = conn - + if self.controller: self.close() # shut down current connection self.controller = controller log.log(log.INFO, "Stem connected to tor version %s" % self.controller.get_version())
@@ -583,10 +579,7 @@ class Controller(TorCtl.PostEventListener): """
self.connLock.acquire() - if self.conn: - self.conn.close() - self.conn = None - + if self.controller: self.controller.close() self.controller = None
@@ -610,8 +603,8 @@ class Controller(TorCtl.PostEventListener): self.connLock.acquire()
result = False - if self.conn: - if self.conn.is_live() and self.controller.is_alive(): result = True + if self.controller: + if self.controller.is_alive(): result = True else: self.close()
self.connLock.release() @@ -622,7 +615,10 @@ class Controller(TorCtl.PostEventListener): Provides the time of the last registered tor message. """
- return self.controller.get_latest_heartbeat() + if self.isAlive(): + return self.controller.get_latest_heartbeat() + else: + return 0
def getInfo(self, param, default = UNDEFINED): """ @@ -912,7 +908,10 @@ class Controller(TorCtl.PostEventListener): false otherwise. """
- return self.controller.is_geoip_unavailable() + if self.isAlive(): + return self.controller.is_geoip_unavailable() + else: + return False
def getMyPid(self): """
tor-commits@lists.torproject.org