[tor-commits] [arm/master] Using stem's event handling
atagar at torproject.org
atagar at torproject.org
Mon Dec 17 04:25:17 UTC 2012
commit 7f295fe63f60c221bce6a9fa28ff6b713ea656c7
Author: Damian Johnson <atagar at torproject.org>
Date: Sun Dec 16 18:32:15 2012 -0800
Using stem's event handling
Replacing TorCtl event handling with stem's. This includes a few changes...
* Dropped some of the custom messaging for event types. On the down side those
messages might have been a tad more user friendly. On the up side we'll now
provide the full event message content rather than hiding things that we
don't recognize. Personally I consider this to be a net good thing. We can
add a user friendly str() for Events if we want a better rendering.
* We no longer support listener reattachment. This is because stem itself
provides reattachment but we've broken it by misusing the Controller class.
We're constructing fresh Controller instances rather than reusing the one we
made priorly - when this is fixed we'll have reattachment again.
* The interpretor panel no longer supports SETEVENTS nor event listening. It
took some pretty ugly hacks to make it work in the first place, and I'm not
very inclined to redo these hacks with stem. Besides, we'll probably be
splintering the interpretor panel into its own application for ticket #6439.
---
src/cli/graphing/graphPanel.py | 9 +-
src/cli/logPanel.py | 185 ++++++++++++++------------------------
src/util/torInterpretor.py | 17 ++--
src/util/torTools.py | 196 +++++++++-------------------------------
4 files changed, 120 insertions(+), 287 deletions(-)
diff --git a/src/cli/graphing/graphPanel.py b/src/cli/graphing/graphPanel.py
index 76221ff..d03d1bd 100644
--- a/src/cli/graphing/graphPanel.py
+++ b/src/cli/graphing/graphPanel.py
@@ -18,11 +18,12 @@ Downloaded (0.0 B/sec): Uploaded (0.0 B/sec):
import copy
import curses
-from TorCtl import TorCtl
import cli.popups
import cli.controller
+import stem.control
+
from util import enum, panel, torTools, uiTools
# time intervals at which graphs can be updated
@@ -56,7 +57,7 @@ def loadConfig(config):
"features.graph.interval": (0, len(UPDATE_INTERVALS) - 1),
"features.graph.bound": (0, 2)})
-class GraphStats(TorCtl.PostEventListener):
+class GraphStats:
"""
Module that's expected to update dynamically and provide attributes to be
graphed. Up to two graphs (a 'primary' and 'secondary') can be displayed at a
@@ -68,8 +69,6 @@ class GraphStats(TorCtl.PostEventListener):
Initializes parameters needed to present a graph.
"""
- TorCtl.PostEventListener.__init__(self)
-
# panel to be redrawn when updated (set when added to GraphPanel)
self._graphPanel = None
self.isSelected = False
@@ -96,7 +95,7 @@ class GraphStats(TorCtl.PostEventListener):
self.secondaryCounts[i] = (self.maxCol + 1) * [0]
# tracks BW events
- torTools.getConn().addEventListener(self)
+ torTools.getConn().addEventListener(self.bandwidth_event, stem.control.EventType.BW)
def clone(self, newCopy=None):
"""
diff --git a/src/cli/logPanel.py b/src/cli/logPanel.py
index ee7dee1..b866c62 100644
--- a/src/cli/logPanel.py
+++ b/src/cli/logPanel.py
@@ -11,9 +11,8 @@ import curses
import logging
import threading
-from TorCtl import TorCtl
-
import stem.util.log
+from stem.response import events
import popups
from version import VERSION
@@ -35,7 +34,7 @@ EVENT_LISTING = """ d DEBUG a ADDRMAP k DESCCHANGED s ST
j CLIENTS_SEEN q ORCONN
DINWE tor runlevel+ A All Events
12345 arm runlevel+ X No Events
- 67890 torctl runlevel+ U Unknown Events"""
+ U Unknown Events"""
RUNLEVEL_EVENT_COLOR = {log.DEBUG: "magenta", log.INFO: "blue", log.NOTICE: "green",
log.WARN: "yellow", log.ERR: "red"}
@@ -155,7 +154,7 @@ def expandEvents(eventAbbr):
def getMissingEventTypes():
"""
- Provides the event types the current torctl connection supports but arm
+ Provides the event types the current tor connection supports but arm
doesn't. This provides an empty list if no event types are missing, and None
if the GETINFO query fails.
"""
@@ -168,28 +167,6 @@ def getMissingEventTypes():
return [event for event in torEventTypes if not event in armEventTypes]
else: return None # GETINFO call failed
-def setEventListening(events):
- """
- Configures the events Tor listens for, filtering non-tor events from what we
- request from the controller. This returns a sorted list of the events we
- successfully set.
-
- Arguments:
- events - event types to attempt to set
- """
-
- events = set(events) # drops duplicates
- torEvents = events.intersection(set(TOR_EVENT_TYPES.values()))
-
- # adds events unrecognized by arm if we're listening to the 'UNKNOWN' type
- if "UNKNOWN" in events:
- torEvents.update(set(getMissingEventTypes()))
-
- setEvents = torTools.getConn().setControllerEvents(list(torEvents))
-
- # provides back the input set minus events we failed to set
- return sorted(events.difference(torTools.FAILED_EVENTS))
-
def loadLogMessages():
"""
Fetches a mapping of common log messages to their runlevels from the config.
@@ -491,95 +468,9 @@ class LogEntry():
return self._displayMessage
-class TorEventObserver(TorCtl.PostEventListener):
- """
- Listens for all types of events provided by TorCtl, providing an LogEntry
- instance to the given callback function.
- """
-
- def __init__(self, callback):
- """
- Tor event listener with the purpose of translating events to nicely
- formatted calls of a callback function.
-
- Arguments:
- callback - function accepting a LogEntry, called when an event of these
- types occur
- """
-
- TorCtl.PostEventListener.__init__(self)
- self.callback = callback
-
- def circ_status_event(self, event):
- msg = "ID: %-3s STATUS: %-10s PATH: %s" % (event.circ_id, event.status, ", ".join(event.path))
- if event.purpose: msg += " PURPOSE: %s" % event.purpose
- if event.reason: msg += " REASON: %s" % event.reason
- if event.remote_reason: msg += " REMOTE_REASON: %s" % event.remote_reason
- self._notify(event, msg, "yellow")
-
- def buildtimeout_set_event(self, event):
- self._notify(event, "SET_TYPE: %s, TOTAL_TIMES: %s, TIMEOUT_MS: %s, XM: %s, ALPHA: %s, CUTOFF_QUANTILE: %s" % (event.set_type, event.total_times, event.timeout_ms, event.xm, event.alpha, event.cutoff_quantile))
-
- def stream_status_event(self, event):
- self._notify(event, "ID: %s STATUS: %s CIRC_ID: %s TARGET: %s:%s REASON: %s REMOTE_REASON: %s SOURCE: %s SOURCE_ADDR: %s PURPOSE: %s" % (event.strm_id, event.status, event.circ_id, event.target_host, event.target_port, event.reason, event.remote_reason, event.source, event.source_addr, event.purpose))
-
- def or_conn_status_event(self, event):
- msg = "STATUS: %-10s ENDPOINT: %-20s" % (event.status, event.endpoint)
- if event.reason: msg += " REASON: %-6s" % event.reason
- if event.ncircs: msg += " NCIRCS: %i" % event.ncircs
- self._notify(event, msg)
-
- def stream_bw_event(self, event):
- self._notify(event, "ID: %s READ: %s WRITTEN: %s" % (event.strm_id, event.bytes_read, event.bytes_written))
-
- def bandwidth_event(self, event):
- self._notify(event, "READ: %i, WRITTEN: %i" % (event.read, event.written), "cyan")
-
- def msg_event(self, event):
- self._notify(event, event.msg, RUNLEVEL_EVENT_COLOR[event.level])
-
- def new_desc_event(self, event):
- idlistStr = [str(item) for item in event.idlist]
- self._notify(event, ", ".join(idlistStr))
-
- def address_mapped_event(self, event):
- whenLabel, gmtExpiryLabel = "", ""
-
- if event.when:
- whenLabel = time.strftime("%H:%M %m/%d/%Y", event.when)
-
- # TODO: torctl is getting an 'error' and 'gmt_expiry' attribute so display
- # those when they become available
- #
- #if event.gmt_expiry:
- # gmtExpiryLabel = time.strftime("%H:%M %m/%d/%Y", event.gmt_expiry)
-
- self._notify(event, "%s, %s -> %s" % (whenLabel, event.from_addr, event.to_addr))
-
- def ns_event(self, event):
- # NetworkStatus params: nickname, idhash, orhash, ip, orport (int),
- # dirport (int), flags, idhex, bandwidth, updated (datetime)
- msg = ", ".join(["%s (%s)" % (ns.idhex, ns.nickname) for ns in event.nslist])
- self._notify(event, "Listed (%i): %s" % (len(event.nslist), msg), "blue")
-
- def new_consensus_event(self, event):
- msg = ", ".join(["%s (%s)" % (ns.idhex, ns.nickname) for ns in event.nslist])
- self._notify(event, "Listed (%i): %s" % (len(event.nslist), msg), "magenta")
-
- def guard_event(self, event):
- msg = "%s (%s), STATUS: %s" % (event.idhex, event.nick, event.status)
- self._notify(event, msg, "yellow")
-
- def unknown_event(self, event):
- msg = "(%s) %s" % (event.event_name, event.event_string)
- self.callback(LogEntry(event.arrived_at, "UNKNOWN", msg, "red"))
-
- def _notify(self, event, msg, color="white"):
- self.callback(LogEntry(event.arrived_at, event.event_name, msg, color))
-
class LogPanel(panel.Panel, threading.Thread, logging.Handler):
"""
- Listens for and displays tor, arm, and torctl events. This can prepopulate
+ Listens for and displays tor, arm, and stem events. This can prepopulate
from tor's log file if it exists.
"""
@@ -626,13 +517,14 @@ class LogPanel(panel.Panel, threading.Thread, logging.Handler):
# collapses duplicate log entries if false, showing only the most recent
self.showDuplicates = self._config["features.log.showDuplicateEntries"]
+ self.loggedEvents = [] # needs to be set before we receive any events
+
# restricts the input to the set of events we can listen to, and
# configures the controller to liten to them
- loggedEvents = setEventListening(loggedEvents)
+ self.loggedEvents = self.setEventListening(loggedEvents)
self.setPauseAttr("msgLog") # tracks the message log when we're paused
self.msgLog = [] # log entries, sorted by the timestamp
- self.loggedEvents = loggedEvents # events we're listening to
self.regexFilter = None # filter for presented log events (no filtering if None)
self.lastContentHeight = 0 # height of the rendered content when last drawn
self.logFile = None # file log messages are saved to (skipped if None)
@@ -666,9 +558,8 @@ class LogPanel(panel.Panel, threading.Thread, logging.Handler):
# leaving lastContentHeight as being too low causes initialization problems
self.lastContentHeight = len(self.msgLog)
- # adds listeners for tor and torctl events
+ # adds listeners for tor and stem events
conn = torTools.getConn()
- conn.addEventListener(TorEventObserver(self.registerEvent))
conn.addStatusListener(self._resetListener)
# opens log file if we'll be saving entries
@@ -750,6 +641,33 @@ class LogPanel(panel.Panel, threading.Thread, logging.Handler):
self.showDuplicates = isVisible
+ def registerTorEvent(self, event):
+ """
+ Translates a stem.response.event.Event instance into a LogEvent, and calls
+ registerEvent().
+ """
+
+ msg, color = ' '.join(str(event).split(' ')[1:]), "white"
+
+ if isinstance(event, events.CircuitEvent):
+ color = "yellow"
+ elif isinstance(event, events.BandwidthEvent):
+ color = "cyan"
+ msg = "READ: %i, WRITTEN: %i" % (event.read, event.written)
+ elif isinstance(event, events.LogEvent):
+ color = RUNLEVEL_EVENT_COLOR[event.runlevel]
+ msg = event.message
+ elif isinstance(event, events.NetworkStatusEvent):
+ color = "blue"
+ elif isinstance(event, events.NewConsensusEvent):
+ color = "magenta"
+ elif isinstance(event, events.GuardEvent):
+ color = "yellow"
+ elif not event.type in TOR_EVENT_TYPES.values():
+ color = "red" # unknown event type
+
+ self.registerEvent(LogEntry(event.arrived_at, event.type, msg, color))
+
def registerEvent(self, event):
"""
Notes event and redraws log. If paused it's held in a temporary buffer.
@@ -801,7 +719,7 @@ class LogPanel(panel.Panel, threading.Thread, logging.Handler):
# configures the controller to listen for these tor events, and provides
# back a subset without anything we're failing to listen to
- setTypes = setEventListening(eventTypes)
+ setTypes = self.setEventListening(eventTypes)
self.loggedEvents = setTypes
self.redraw(True)
self.valsLock.release()
@@ -1217,6 +1135,35 @@ class LogPanel(panel.Panel, threading.Thread, logging.Handler):
self._cond.notifyAll()
self._cond.release()
+ def setEventListening(self, events):
+ """
+ Configures the events Tor listens for, filtering non-tor events from what we
+ request from the controller. This returns a sorted list of the events we
+ successfully set.
+
+ Arguments:
+ events - event types to attempt to set
+ """
+
+ events = set(events) # drops duplicates
+ torEvents = events.intersection(set(TOR_EVENT_TYPES.values()))
+
+ # adds events unrecognized by arm if we're listening to the 'UNKNOWN' type
+ if "UNKNOWN" in events:
+ torEvents.update(set(getMissingEventTypes()))
+
+ torConn = torTools.getConn()
+ torConn.removeEventListener(self.registerTorEvent)
+
+ for eventType in list(torEvents):
+ try:
+ torConn.addEventListener(self.registerTorEvent, eventType)
+ except stem.ProtocolError:
+ torEvents.remove(eventType)
+
+ # provides back the input set minus events we failed to set
+ return sorted(torEvents)
+
def _resetListener(self, _, eventType):
# if we're attaching to a new tor instance then clears the log and
# prepopulates it with the content belonging to this instance
@@ -1257,7 +1204,7 @@ class LogPanel(panel.Panel, threading.Thread, logging.Handler):
labelPattern = uiTools.cropStr(currentPattern, width - 18)
panelLabel = "Events (filter: %s):" % labelPattern
else:
- # does the following with all runlevel types (tor, arm, and torctl):
+ # does the following with all runlevel types (tor, arm, and stem):
# - pulls to the start of the list
# - condenses range if there's three or more in a row (ex. "ARM_INFO - WARN")
# - condense further if there's identical runlevel ranges for multiple
diff --git a/src/util/torInterpretor.py b/src/util/torInterpretor.py
index 674e5e2..5023045 100644
--- a/src/util/torInterpretor.py
+++ b/src/util/torInterpretor.py
@@ -11,12 +11,6 @@ import version
import stem
-# TODO: util should never import from the cli. This is being done as a hack to
-# simplify event listening, but we should later move the TorEventObserver into
-# the util.
-
-from cli.logPanel import TorEventObserver
-
from util import connections, enum, hostnames, torConfig, torTools, uiTools
COLOR_PROMPT = True # provides a colored interpretor prompt
@@ -445,7 +439,12 @@ class ControlInterpretor:
self.eventBuffer = [] # unread event messages
self.loggedEvents = [] # event types that we're listening for
- torTools.getConn().addEventListener(TorEventObserver(self.registerEvent))
+ # TODO: Dropping event listening since...
+ # a. it was a complete hack
+ # b. said hack isn't worth porting to stem
+ # c. I'm likely about to separate the interpretor from arm anyway
+
+ #torTools.getConn().addEventListener(TorEventObserver(self.registerEvent))
def registerEvent(self, event):
"""
@@ -913,8 +912,10 @@ class ControlInterpretor:
except Exception, exc:
outputEntry.append((str(exc), ERROR_FORMAT))
elif cmd == "SETEVENTS":
+ return ([], []) # dropping support, see the comment for the event listener above for why
+
self.loggedEvents = arg.split()
- allEvents = torTools.getConn().setControllerEvents(self.loggedEvents)
+ #allEvents = torTools.getConn().setControllerEvents(self.loggedEvents)
setEvents = set(self.loggedEvents).intersection(allEvents)
if setEvents:
diff --git a/src/util/torTools.py b/src/util/torTools.py
index 36cb70f..d261bb0 100644
--- a/src/util/torTools.py
+++ b/src/util/torTools.py
@@ -38,14 +38,6 @@ DIR_SERVERS = [("86.59.21.38", "80"), # tor26
# message logged by default when a controller can't set an event type
DEFAULT_FAILED_EVENT_MSG = "Unsupported event type: %s"
-# TODO: check version when reattaching to controller and if version changes, flush?
-# Skips attempting to set events we've failed to set before. This avoids
-# logging duplicate warnings but can be problematic if controllers belonging
-# to multiple versions of tor are attached, making this unreflective of the
-# controller's capabilites. However, this is a pretty bizarre edge case.
-DROP_FAILED_EVENTS = True
-FAILED_EVENTS = set()
-
CONTROLLER = None # singleton Controller instance
UNDEFINED = "<Undefined_ >"
@@ -493,7 +485,6 @@ class Controller(TorCtl.PostEventListener):
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.statusListeners = [] # callback functions for tor's state changes
self.controllerEvents = [] # list of successfully set controller events
self._fingerprintMappings = None # mappings of ip -> [(port, fingerprint), ...]
@@ -541,6 +532,9 @@ class Controller(TorCtl.PostEventListener):
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:
self.connLock.acquire()
@@ -550,8 +544,11 @@ class Controller(TorCtl.PostEventListener):
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)
+ self.controller.add_event_listener(self.msg_event, stem.control.EventType.NOTICE)
+ 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)
+ self.controller.add_event_listener(self.circ_status_event, stem.control.EventType.CIRC)
# registers this as our first heartbeat
self._updateHeartbeat()
@@ -570,10 +567,6 @@ class Controller(TorCtl.PostEventListener):
self._isExitingAllowed = self._exitPolicyChecker.isExitingAllowed()
self._exitPolicyLookupCache = {}
- # sets the events listened for by the new controller (incompatible events
- # are dropped with a logged warning)
- self.setControllerEvents(self.controllerEvents)
-
self._status = State.INIT
self._statusTime = time.time()
@@ -1387,19 +1380,24 @@ class Controller(TorCtl.PostEventListener):
return result
- def addEventListener(self, listener):
+ def addEventListener(self, listener, *eventTypes):
"""
Directs further tor controller events to callback functions of the
listener. If a new control connection is initialized then this listener is
reattached.
+ """
- Arguments:
- listener - TorCtl.PostEventListener instance listening for events
+ self.connLock.acquire()
+ if self.isAlive(): self.controller.add_event_listener(listener, *eventTypes)
+ self.connLock.release()
+
+ def removeEventListener(self, listener):
+ """
+ Stops the given event listener from being notified of further events.
"""
self.connLock.acquire()
- self.eventListeners.append(listener)
- if self.isAlive(): self.conn.add_event_listener(listener)
+ if self.isAlive(): self.controller.remove_event_listener(listener)
self.connLock.release()
def addStatusListener(self, callback):
@@ -1435,92 +1433,6 @@ class Controller(TorCtl.PostEventListener):
return list(self.controllerEvents)
- def setControllerEvents(self, events):
- """
- Sets the events being requested from any attached tor instance, logging
- warnings for event types that aren't supported (possibly due to version
- issues). Events in REQ_EVENTS will also be included, logging at the error
- level with an additional description in case of failure.
-
- This remembers the successfully set events and tries to request them from
- any tor instance it attaches to in the future too (again logging and
- dropping unsuccessful event types).
-
- This returns the listing of event types that were successfully set. If not
- currently attached to a tor instance then all events are assumed to be ok,
- then attempted when next attached to a control port.
-
- Arguments:
- events - listing of events to be set
- """
-
- self.connLock.acquire()
-
- returnVal = []
- if self.isAlive():
- events = set(events)
- events = events.union(set(REQ_EVENTS.keys()))
- unavailableEvents = set()
-
- # removes anything we've already failed to set
- if DROP_FAILED_EVENTS:
- unavailableEvents.update(events.intersection(FAILED_EVENTS))
- events.difference_update(FAILED_EVENTS)
-
- # initial check for event availability, using the 'events/names' GETINFO
- # option to detect invalid events
- validEvents = self.getInfo("events/names", None)
-
- if validEvents:
- validEvents = set(validEvents.split())
- unavailableEvents.update(events.difference(validEvents))
- events.intersection_update(validEvents)
-
- # attempt to set events via trial and error
- isEventsSet, isAbandoned = False, False
-
- while not isEventsSet and not isAbandoned:
- try:
- self.conn.set_events(list(events))
- isEventsSet = True
- except TorCtl.ErrorReply, exc:
- msg = str(exc)
-
- if "Unrecognized event" in msg:
- # figure out type of event we failed to listen for
- start = msg.find("event \"") + 7
- end = msg.rfind("\"")
- failedType = msg[start:end]
-
- unavailableEvents.add(failedType)
- events.discard(failedType)
- else:
- # unexpected error, abandon attempt
- isAbandoned = True
- except TorCtl.TorCtlClosed:
- self.close()
- isAbandoned = True
-
- FAILED_EVENTS.update(unavailableEvents)
- if not isAbandoned:
- # logs warnings or errors for failed events
- for eventType in unavailableEvents:
- defaultMsg = DEFAULT_FAILED_EVENT_MSG % eventType
- if eventType in REQ_EVENTS:
- log.log(log.ERR, defaultMsg + " (%s)" % REQ_EVENTS[eventType])
- else:
- log.log(log.WARN, defaultMsg)
-
- self.controllerEvents = list(events)
- returnVal = list(events)
- else:
- # attempts to set the events when next attached to a control port
- self.controllerEvents = list(events)
- returnVal = list(events)
-
- self.connLock.release()
- return returnVal
-
def reload(self, issueSighup = False):
"""
This resets tor (sending a RELOAD signal to the control port) causing tor's
@@ -1631,7 +1543,7 @@ class Controller(TorCtl.PostEventListener):
causing the torrc and internal state to be reset.
"""
- if event.level == "NOTICE" and event.msg.startswith("Received reload signal (hup)"):
+ if event.runlevel == "NOTICE" and event.message.startswith("Received reload signal (hup)"):
self.connLock.acquire()
if self.isAlive():
@@ -1652,8 +1564,8 @@ class Controller(TorCtl.PostEventListener):
myFingerprint = self.getInfo("fingerprint", None)
if myFingerprint:
- for ns in event.nslist:
- if ns.idhex == myFingerprint:
+ for desc in event.desc:
+ if desc.fingerprint == myFingerprint:
self._cachedParam["nsEntry"] = None
self._cachedParam["flags"] = None
self._cachedParam["bwMeasured"] = None
@@ -1681,7 +1593,7 @@ class Controller(TorCtl.PostEventListener):
self._consensusLookupCache = {}
if self._fingerprintMappings != None:
- self._fingerprintMappings = self._getFingerprintMappings(event.nslist)
+ self._fingerprintMappings = self._getFingerprintMappings(event.desc)
self.connLock.release()
@@ -1691,7 +1603,9 @@ class Controller(TorCtl.PostEventListener):
self.connLock.acquire()
myFingerprint = self.getInfo("fingerprint", None)
- if not myFingerprint or myFingerprint in event.idlist:
+ desc_fingerprints = [fingerprint for (fingerprint, nickname) in event.relays]
+
+ if not myFingerprint or myFingerprint in desc_fingerprints:
self._cachedParam["descEntry"] = None
self._cachedParam["bwObserved"] = None
@@ -1702,32 +1616,26 @@ class Controller(TorCtl.PostEventListener):
self._descriptorLookupCache = {}
if self._fingerprintMappings != None:
- for fingerprint in event.idlist:
+ for fingerprint in desc_fingerprints:
# gets consensus data for the new descriptor
- try: nsLookup = self.conn.get_network_status("id/%s" % fingerprint)
- except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed): continue
-
- if len(nsLookup) > 1:
- # multiple records for fingerprint (shouldn't happen)
- log.log(log.WARN, "Multiple consensus entries for fingerprint: %s" % fingerprint)
- continue
+ try: desc = self.controller.get_network_status(fingerprint)
+ except stem.ControllerError: continue
# updates fingerprintMappings with new data
- newRelay = nsLookup[0]
- if newRelay.ip in self._fingerprintMappings:
+ if desc.address in self._fingerprintMappings:
# if entry already exists with the same orport, remove it
orportMatch = None
- for entryPort, entryFingerprint in self._fingerprintMappings[newRelay.ip]:
- if entryPort == newRelay.orport:
+ for entryPort, entryFingerprint in self._fingerprintMappings[desc.address]:
+ if entryPort == desc.or_port:
orportMatch = (entryPort, entryFingerprint)
break
- if orportMatch: self._fingerprintMappings[newRelay.ip].remove(orportMatch)
+ if orportMatch: self._fingerprintMappings[desc.address].remove(orportMatch)
# add the new entry
- self._fingerprintMappings[newRelay.ip].append((newRelay.orport, newRelay.idhex))
+ self._fingerprintMappings[desc.address].append((desc.or_port, desc.fingerprint))
else:
- self._fingerprintMappings[newRelay.ip] = [(newRelay.orport, newRelay.idhex)]
+ self._fingerprintMappings[desc.address] = [(desc.or_port, desc.fingerprint)]
self.connLock.release()
@@ -1742,27 +1650,6 @@ class Controller(TorCtl.PostEventListener):
self._cachedParam["circuits"] = None
- def buildtimeout_set_event(self, event):
- self._updateHeartbeat()
-
- def stream_status_event(self, event):
- self._updateHeartbeat()
-
- def or_conn_status_event(self, event):
- self._updateHeartbeat()
-
- def stream_bw_event(self, event):
- self._updateHeartbeat()
-
- def bandwidth_event(self, event):
- self._updateHeartbeat()
-
- def address_mapped_event(self, event):
- self._updateHeartbeat()
-
- def unknown_event(self, event):
- self._updateHeartbeat()
-
def _updateHeartbeat(self):
"""
Called on any event occurance to note the time it occured.
@@ -1771,26 +1658,25 @@ class Controller(TorCtl.PostEventListener):
# alternative is to use the event's timestamp (via event.arrived_at)
self.lastHeartbeat = time.time()
- def _getFingerprintMappings(self, nsList = None):
+ def _getFingerprintMappings(self, descriptors = None):
"""
Provides IP address to (port, fingerprint) tuple mappings for all of the
currently cached relays.
Arguments:
- nsList - network status listing (fetched if not provided)
+ descriptors - router status entries (fetched if not provided)
"""
results = {}
if self.isAlive():
# fetch the current network status if not provided
- if not nsList:
- try: nsList = self.conn.get_network_status(get_iterator=True)
- except (socket.error, TorCtl.TorCtlClosed, TorCtl.ErrorReply): nsList = []
+ if not descriptors:
+ try: descriptors = self.controller.get_network_statuses()
+ except stem.ControllerError: descriptors = []
# construct mappings of ips to relay data
- for relay in nsList:
- if relay.ip in results: results[relay.ip].append((relay.orport, relay.idhex))
- else: results[relay.ip] = [(relay.orport, relay.idhex)]
+ for desc in descriptors:
+ results.setdefault(desc.address, []).append((desc.or_port, desc.fingerprint))
return results
More information about the tor-commits
mailing list