[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [arm/master] Mirroring TorCtl connection with stem
commit f05b54f10a1c212cd4da4d31934506156cfe4cb2
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Tue Dec 11 08:43:26 2012 -0800
Mirroring TorCtl connection with stem
Arm uses a wrapper for its controller usage. This abstraction is highly
convenient for our migration since it means that our wrapper can connect to tor
with *both* TorCtl and stem, letting us migrate calls one at a time while
keeping arm functional.
Changing our wrapper's init() function to require both connection types, and
revising everywhere we use it. This breaks test.py and likely breaks the gui,
though I'll probably simply drop both of them before the next release. The gui
was an experiment, and can be resurrected if someone becomes interested in
developing it.
---
src/cli/controller.py | 7 +++++-
src/cli/headerPanel.py | 30 ++++++++++++++++++++++++----
src/starter.py | 50 ++++++++++++++++++++++++++++++++---------------
src/util/torTools.py | 23 ++++++++++++---------
4 files changed, 78 insertions(+), 32 deletions(-)
diff --git a/src/cli/controller.py b/src/cli/controller.py
index c1be694..c72375b 100644
--- a/src/cli/controller.py
+++ b/src/cli/controller.py
@@ -23,6 +23,7 @@ import cli.graphing.resourceStats
import cli.connections.connPanel
from TorCtl import TorCtl
+from stem.control import Controller
from util import connections, conf, enum, hostnames, log, panel, sysTools, torConfig, torTools
@@ -555,7 +556,11 @@ class TorManager:
raise IOError("authentication cookie '%s' is the wrong size (%i bytes instead of 32)" % (authValue, authCookieSize))
torctlConn.authenticate(authValue)
- torTools.getConn().init(torctlConn)
+
+ 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)
diff --git a/src/cli/headerPanel.py b/src/cli/headerPanel.py
index bf8dbdb..50222f1 100644
--- a/src/cli/headerPanel.py
+++ b/src/cli/headerPanel.py
@@ -20,6 +20,10 @@ import curses
import threading
import TorCtl.TorCtl
+import stem
+import stem.connection
+
+from stem.control import Controller
import starter
import cli.popups
@@ -134,12 +138,19 @@ 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 = None
+ torctlConn, controller = None, 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"])
- except IOError, exc:
+ 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
+
if not allowPortConnection:
cli.popups.showMsg("Unable to reconnect (%s)" % exc, 3)
elif not allowPortConnection:
@@ -166,7 +177,16 @@ class HeaderPanel(panel.Panel, threading.Thread):
tmpConn.authenticate(authValue)
torctlConn = tmpConn
+
+ controller = Controller.from_port(ctlAddr, ctlPort)
+
+ try:
+ controller.authenticate()
+ except stem.connection.MissingPassword:
+ controller.authenticate(authValue) # already got the password above
except Exception, exc:
+ torctlConn, controller = None, None
+
# attempts to use the wizard port too
try:
cli.controller.getController().getTorManager().connectManagedInstance()
@@ -176,8 +196,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:
- torTools.getConn().init(torctlConn)
+ if torctlConn and controller:
+ torTools.getConn().init(torctlConn, 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 40c23f7..7c51aae 100644
--- a/src/starter.py
+++ b/src/starter.py
@@ -28,9 +28,12 @@ import util.torConfig
import util.torInterpretor
import util.torTools
import util.uiTools
+
import TorCtl.TorCtl
import TorCtl.TorUtil
+from stem.control import Controller
+
LOG_DUMP_PATH = os.path.expanduser("~/.arm/log")
DEFAULT_CONFIG = os.path.expanduser("~/.arm/armrc")
CONFIG = {"startup.controlPassword": None,
@@ -210,7 +213,7 @@ def _torCtlConnect(controlAddr="127.0.0.1", controlPort=9051, passphrase=None, i
Custom handler for establishing a TorCtl connection.
"""
- conn = None
+ conn, controller = None, None
try:
#conn, authType, authValue = TorCtl.TorCtl.preauth_connect(controlAddr, controlPort)
conn, authTypes, authValue = util.torTools.preauth_connect_alt(controlAddr, controlPort)
@@ -227,7 +230,7 @@ def _torCtlConnect(controlAddr="127.0.0.1", controlPort=9051, passphrase=None, i
if not passphrase:
try: passphrase = getpass.getpass("Controller password: ")
- except KeyboardInterrupt: return None
+ 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
@@ -280,9 +283,16 @@ def _torCtlConnect(controlAddr="127.0.0.1", controlPort=9051, passphrase=None, i
else: conn._authTypes.remove(TorCtl.TorCtl.AUTH_TYPE.COOKIE)
conn.authenticate(passphrase)
- return conn
+
+ # Damn well everything above this is covered by stem. :P
+
+ controller = Controller.from_port(controlAddr, controlPort)
+ controller.authenticate(password = passphrase, chroot_path = util.torTools.getConn().getPathPrefix())
+
+ return conn, controller
except Exception, exc:
if conn: conn.close()
+ if controller: controller.close()
# attempts to connect with the default wizard address too
wizardPort = CONFIG["wizard.default"].get("Control")
@@ -295,9 +305,9 @@ def _torCtlConnect(controlAddr="127.0.0.1", controlPort=9051, passphrase=None, i
# connection failure. Otherwise, return the connection result.
if controlPort != wizardPort:
- connResult = _torCtlConnect(controlAddr, wizardPort)
- if connResult != None: return connResult
- else: return None # wizard connection attempt, don't print anything
+ connResult, controller = _torCtlConnect(controlAddr, wizardPort)
+ if connResult != None: return connResult, controller
+ else: return None, 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
@@ -306,7 +316,8 @@ def _torCtlConnect(controlAddr="127.0.0.1", controlPort=9051, passphrase=None, i
return _torCtlConnect(controlAddr, controlPort)
elif printError:
print exc
- return None
+
+ return None, None
def _dumpConfig():
"""
@@ -487,24 +498,31 @@ if __name__ == '__main__':
# skips attempting to connect by socket or port if the user has given
# arguments for connecting to the other.
- conn = None
+ conn, controller = None, None
allowPortConnection, allowSocketConnection, allowDetachedStart = allowConnectionTypes()
socketPath = param["startup.interface.socket"]
if os.path.exists(socketPath) and allowSocketConnection:
- try: conn = util.torTools.connect_socket(socketPath)
+ try:
+ conn = util.torTools.connect_socket(socketPath)
+
+ # TODO: um... what about passwords?
+ # https://trac.torproject.org/6881
+
+ controller = Controller.from_socket_file(socketPath)
+ controller.authenticate()
except IOError, exc:
if not allowPortConnection:
print "Unable to use socket '%s': %s" % (socketPath, exc)
elif not allowPortConnection:
print "Socket '%s' doesn't exist" % socketPath
- if not conn and allowPortConnection:
+ if (not conn or not controller) and allowPortConnection:
# sets up TorCtl 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 = _torCtlConnect(controlAddr, controlPort, authPassword, incorrectPasswordMsg, not allowDetachedStart)
+ conn, controller = _torCtlConnect(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
@@ -522,19 +540,19 @@ if __name__ == '__main__':
if pwLineNum != None:
del config.rawContents[i]
- if conn == None and not allowDetachedStart: sys.exit(1)
+ if (conn is None or 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
initTime = time.time() - startTime
- controller = util.torTools.getConn()
+ controllerWrapper = util.torTools.getConn()
torUser = None
- if conn:
- controller.init(conn)
+ if conn and controller:
+ controllerWrapper.init(conn, controller)
# give a notice if tor is running with root
- torUser = controller.getMyUser()
+ torUser = controllerWrapper.getMyUser()
if torUser == "root":
util.log.log(util.log.NOTICE, TOR_ROOT_NOTICE)
diff --git a/src/util/torTools.py b/src/util/torTools.py
index a4d49c7..7d742e4 100644
--- a/src/util/torTools.py
+++ b/src/util/torTools.py
@@ -579,6 +579,7 @@ 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.eventListeners = [] # instances listening for tor controller events
self.torctlListeners = [] # callback functions for TorCtl events
@@ -631,26 +632,25 @@ class Controller(TorCtl.PostEventListener):
# tracks the number of sequential geoip lookup failures
self.geoipFailureCount = 0
- def init(self, conn=None):
+ def init(self, conn, controller):
"""
Uses the given TorCtl instance for future operations, notifying listeners
about the change.
Arguments:
- conn - TorCtl instance to be used, if None then a new instance is fetched
- via the connect function
+ conn - TorCtl instance to be used
+ controller - stem based Controller instance
"""
- if conn == None:
- conn = TorCtl.connect()
-
- if conn == None: raise ValueError("Unable to initialize TorCtl instance.")
-
- if conn.is_live() and conn != self.conn:
+ if conn.is_live() and controller.is_alive() and conn != self.conn:
self.connLock.acquire()
if self.conn: self.close() # shut down current connection
self.conn = conn
+
+ self.controller = controller
+ log.log(log.INFO, "Stem connected to tor version %s" % self.controller.get_version())
+
self.conn.add_event_listener(self)
for listener in self.eventListeners: self.conn.add_event_listener(listener)
@@ -698,6 +698,9 @@ class Controller(TorCtl.PostEventListener):
self.conn.close()
self.conn = None
+ self.controller.close()
+ self.controller = None
+
self._status = State.CLOSED
self._statusTime = time.time()
@@ -719,7 +722,7 @@ class Controller(TorCtl.PostEventListener):
result = False
if self.conn:
- if self.conn.is_live(): result = True
+ if self.conn.is_live() and self.controller.is_alive(): result = True
else: self.close()
self.connLock.release()
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits