commit 91e5b30e4541e1849b5e5b2041c5fc4e6ee08af7
Author: Damian Johnson <atagar(a)torproject.org>
Date: Mon Jan 27 20:45:41 2014 -0800
Dropping the tor_tools module
Wow, I didn't expect to reach this point this weekend. The tor_tools module was
once a wrapper around TorCtl (providing more user friendly methods, thread
safety, etc). Stem provides all of these so a long term goal with the arm
rewrite has been to drop this module completely. Now we can!
---
arm/util/tor_tools.py | 499 -------------------------------------------------
1 file changed, 499 deletions(-)
diff --git a/arm/util/tor_tools.py b/arm/util/tor_tools.py
deleted file mode 100644
index 1cefa98..0000000
--- a/arm/util/tor_tools.py
+++ /dev/null
@@ -1,499 +0,0 @@
-"""
-Helper for working with an active tor process. This both provides a wrapper for
-accessing stem and notifications of state changes to subscribers.
-"""
-
-import threading
-
-import stem
-import stem.control
-
-from stem.util import log
-
-CONTROLLER = None # singleton Controller instance
-
-UNDEFINED = "<Undefined_ >"
-
-
-def get_conn():
- """
- Singleton constructor for a Controller. Be aware that this starts as being
- uninitialized, needing a stem Controller before it's fully functional.
- """
-
- global CONTROLLER
-
- if CONTROLLER is None:
- CONTROLLER = Controller()
-
- return CONTROLLER
-
-
-class Controller:
- """
- Stem wrapper providing convenience functions (mostly from the days of using
- TorCtl), listener functionality for tor's state, and the capability for
- controller connections to be restarted if closed.
- """
-
- def __init__(self):
- self.controller = None
- self.conn_lock = threading.RLock()
- self._consensus_lookup_cache = {} # lookup cache with network status entries
- self._descriptor_lookup_cache = {} # lookup cache with relay descriptors
-
- def init(self, controller):
- """
- Uses the given stem instance for future operations, notifying listeners
- about the change.
-
- Arguments:
- 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 controller.is_alive() and controller != self.controller:
- self.conn_lock.acquire()
-
- if self.controller:
- self.close() # shut down current connection
-
- self.controller = controller
- log.info("Stem connected to tor version %s" % self.controller.get_version())
-
- self.controller.add_event_listener(self.ns_event, stem.control.EventType.NS)
- self.controller.add_event_listener(self.new_consensus_event, stem.control.EventType.NEWCONSENSUS)
- self.controller.add_event_listener(self.new_desc_event, stem.control.EventType.NEWDESC)
-
- # reset caches for ip -> fingerprint lookups
-
- self._consensus_lookup_cache = {}
- self._descriptor_lookup_cache = {}
-
- self.conn_lock.release()
-
- def close(self):
- """
- Closes the current stem instance and notifies listeners.
- """
-
- self.conn_lock.acquire()
-
- if self.controller:
- self.controller.close()
-
- self.conn_lock.release()
-
- def get_controller(self):
- return self.controller
-
- def is_alive(self):
- """
- Returns True if this has been initialized with a working stem instance,
- False otherwise.
- """
-
- self.conn_lock.acquire()
-
- result = False
-
- if self.controller:
- if self.controller.is_alive():
- result = True
- else:
- self.close()
-
- self.conn_lock.release()
- return result
-
- def get_info(self, param, default = UNDEFINED):
- """
- Queries the control port for the given GETINFO option, providing the
- default if the response is undefined or fails for any reason (error
- response, control port closed, initiated, etc).
-
- Arguments:
- param - GETINFO option to be queried
- default - result if the query fails
- """
-
- self.conn_lock.acquire()
-
- try:
- if not self.is_alive():
- if default != UNDEFINED:
- return default
- else:
- raise stem.SocketClosed()
-
- if default != UNDEFINED:
- return self.controller.get_info(param, default)
- else:
- return self.controller.get_info(param)
- except stem.SocketClosed as exc:
- self.close()
- raise exc
- finally:
- self.conn_lock.release()
-
- def get_option(self, param, default = UNDEFINED, multiple = False):
- """
- Queries the control port for the given configuration option, providing the
- default if the response is undefined or fails for any reason. If multiple
- values exist then this arbitrarily returns the first unless the multiple
- flag is set.
-
- Arguments:
- param - configuration option to be queried
- default - result if the query fails
- multiple - provides a list with all returned values if true, otherwise
- this just provides the first result
- """
-
- self.conn_lock.acquire()
-
- try:
- if not self.is_alive():
- if default != UNDEFINED:
- return default
- else:
- raise stem.SocketClosed()
-
- if default != UNDEFINED:
- return self.controller.get_conf(param, default, multiple)
- else:
- return self.controller.get_conf(param, multiple = multiple)
- except stem.SocketClosed as exc:
- self.close()
- raise exc
- finally:
- self.conn_lock.release()
-
- def set_option(self, param, value = None):
- """
- Issues a SETCONF to set the given option/value pair. An exeptions raised
- if it fails to be set. If no value is provided then this sets the option to
- 0 or NULL.
-
- Arguments:
- param - configuration option to be set
- value - value to set the parameter to (this can be either a string or a
- list of strings)
- """
-
- self.conn_lock.acquire()
-
- try:
- if not self.is_alive():
- raise stem.SocketClosed()
-
- self.controller.set_conf(param, value)
- except stem.SocketClosed as exc:
- self.close()
- raise exc
- finally:
- self.conn_lock.release()
-
- def save_conf(self):
- """
- Calls tor's SAVECONF method.
- """
-
- self.conn_lock.acquire()
-
- if self.is_alive():
- self.controller.save_conf()
-
- self.conn_lock.release()
-
- def get_circuits(self, default = []):
- """
- This provides a list with tuples of the form:
- (circuit_id, status, purpose, (fingerprint1, fingerprint2...))
-
- Arguments:
- default - value provided back if unable to query the circuit-status
- """
-
- # TODO: We're losing caching around this. We should check to see the call
- # volume of this and probably add it to stem.
-
- results = []
-
- for entry in self.controller.get_circuits():
- fingerprints = []
-
- for fp, nickname in entry.path:
- if not fp:
- consensus_entry = self.controller.get_network_status(nickname, None)
-
- if consensus_entry:
- fp = consensus_entry.fingerprint
-
- # It shouldn't be possible for this lookup to fail, but we
- # need to fill something (callers won't expect our own client
- # paths to have unknown relays). If this turns out to be wrong
- # then log a warning.
-
- if not fp:
- log.warn("Unable to determine the fingerprint for a relay in our own circuit: %s" % nickname)
- fp = "0" * 40
-
- fingerprints.append(fp)
-
- results.append((int(entry.id), entry.status, entry.purpose, fingerprints))
-
- if results:
- return results
- else:
- return default
-
- def get_my_flags(self, default = None):
- """
- Provides the flags held by this relay.
-
- Arguments:
- default - result if the query fails or this relay isn't a part of the consensus yet
- """
-
- my_fingerprint = self.get_info("fingerprint", None)
-
- if my_fingerprint:
- my_status_entry = self.controller.get_network_status(my_fingerprint)
-
- if my_status_entry:
- return my_status_entry.flags
-
- return default
-
- def get_version(self):
- """
- Provides the version of our tor instance, this is None if we don't have a
- connection.
- """
-
- self.conn_lock.acquire()
-
- try:
- return self.controller.get_version()
- except stem.SocketClosed:
- self.close()
- return None
- except:
- return None
- finally:
- self.conn_lock.release()
-
- def get_my_user(self):
- """
- Provides the user this process is running under. If unavailable this
- provides None.
- """
-
- return self.controller.get_user(None)
-
- def get_exit_policy(self):
- """
- Provides an ExitPolicy instance for the head of this relay's exit policy
- chain. If there's no active connection then this provides None.
- """
-
- self.conn_lock.acquire()
-
- result = None
-
- if self.is_alive():
- try:
- result = self.controller.get_exit_policy(None)
- except:
- pass
-
- self.conn_lock.release()
-
- return result
-
- def get_consensus_entry(self, relay_fingerprint):
- """
- Provides the most recently available consensus information for the given
- relay. This is none if no such information exists.
-
- Arguments:
- relay_fingerprint - fingerprint of the relay
- """
-
- self.conn_lock.acquire()
-
- result = None
-
- if self.is_alive():
- if not relay_fingerprint in self._consensus_lookup_cache:
- ns_entry = self.get_info("ns/id/%s" % relay_fingerprint, None)
- self._consensus_lookup_cache[relay_fingerprint] = ns_entry
-
- result = self._consensus_lookup_cache[relay_fingerprint]
-
- self.conn_lock.release()
-
- return result
-
- def get_descriptor_entry(self, relay_fingerprint):
- """
- Provides the most recently available descriptor information for the given
- relay. Unless FetchUselessDescriptors is set this may frequently be
- unavailable. If no such descriptor is available then this returns None.
-
- Arguments:
- relay_fingerprint - fingerprint of the relay
- """
-
- self.conn_lock.acquire()
-
- result = None
-
- if self.is_alive():
- if not relay_fingerprint in self._descriptor_lookup_cache:
- desc_entry = self.get_info("desc/id/%s" % relay_fingerprint, None)
- self._descriptor_lookup_cache[relay_fingerprint] = desc_entry
-
- result = self._descriptor_lookup_cache[relay_fingerprint]
-
- self.conn_lock.release()
-
- return result
-
- def get_relay_exit_policy(self, relay_fingerprint):
- """
- Provides the ExitPolicy instance associated with the given relay. The tor
- consensus entries don't indicate if private addresses are rejected or
- address-specific policies, so this is only used as a fallback if a recent
- descriptor is unavailable. This returns None if unable to determine the
- policy.
-
- Arguments:
- relay_fingerprint - fingerprint of the relay
- """
-
- self.conn_lock.acquire()
-
- result = None
-
- if self.is_alive():
- # attempts to fetch the policy via the descriptor
- descriptor = self.controller.get_server_descriptor(relay_fingerprint, None)
-
- if descriptor:
- result = descriptor.exit_policy
-
- self.conn_lock.release()
-
- return result
-
- def add_event_listener(self, listener, *event_types):
- """
- Directs further tor controller events to callback functions of the
- listener. If a new control connection is initialized then this listener is
- reattached.
- """
-
- self.conn_lock.acquire()
-
- if self.is_alive():
- self.controller.add_event_listener(listener, *event_types)
-
- self.conn_lock.release()
-
- def remove_event_listener(self, listener):
- """
- Stops the given event listener from being notified of further events.
- """
-
- self.conn_lock.acquire()
-
- if self.is_alive():
- self.controller.remove_event_listener(listener)
-
- self.conn_lock.release()
-
- def add_status_listener(self, callback):
- """
- Directs further events related to tor's controller status to the callback
- function.
-
- Arguments:
- callback - functor that'll accept the events, expected to be of the form:
- myFunction(controller, event_type)
- """
-
- self.controller.add_status_listener(callback)
-
- def reload(self):
- """
- This resets tor (sending a RELOAD signal to the control port) causing tor's
- internal state to be reset and the torrc reloaded.
- """
-
- self.conn_lock.acquire()
-
- try:
- if self.is_alive():
- try:
- self.controller.signal(stem.Signal.RELOAD)
- except Exception as exc:
- # new torrc parameters caused an error (tor's likely shut down)
- raise IOError(str(exc))
- finally:
- self.conn_lock.release()
-
- def shutdown(self, force = False):
- """
- Sends a shutdown signal to the attached tor instance. For relays the
- actual shutdown is delayed for thirty seconds unless the force flag is
- given. This raises an IOError if a signal is sent but fails.
-
- Arguments:
- force - triggers an immediate shutdown for relays if True
- """
-
- self.conn_lock.acquire()
-
- raised_exception = None
-
- if self.is_alive():
- try:
- is_relay = self.get_option("ORPort", None) is not None
-
- if force:
- self.controller.signal(stem.Signal.HALT)
- else:
- self.controller.signal(stem.Signal.SHUTDOWN)
-
- # shuts down control connection if we aren't making a delayed shutdown
-
- if force or not is_relay:
- self.close()
- except Exception as exc:
- raised_exception = IOError(str(exc))
-
- self.conn_lock.release()
-
- if raised_exception:
- raise raised_exception
-
- def ns_event(self, event):
- self._consensus_lookup_cache = {}
-
- def new_consensus_event(self, event):
- self.conn_lock.acquire()
-
- # reconstructs consensus based mappings
-
- self._consensus_lookup_cache = {}
-
- self.conn_lock.release()
-
- def new_desc_event(self, event):
- self.conn_lock.acquire()
- self._descriptor_lookup_cache = {}
- self.conn_lock.release()