commit 6ed5a561c2b42b48ca3edca0905926979194e3a6 Author: Damian Johnson <ata...@torproject.org> Date: Sun Jul 12 13:42:49 2015 -0700
Move FingerprintTracker to tracker util When picking apart our old tor_tools I plopped the FingerprintTracker into the conn_entry because that is its sole caller. However, now that I've cleaned it up it's time to find a more permanent home. As the name suggests it's similar to the other trackers, so moving it there. Also renaming it to ConsensusTracker because it's used to provide performant lookups of any consensus information we need (not just fingerprints). --- nyx/connections/conn_entry.py | 115 ++------------------------------------ nyx/connections/conn_panel.py | 2 +- nyx/util/tracker.py | 123 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 127 insertions(+), 113 deletions(-) diff --git a/nyx/connections/conn_entry.py b/nyx/connections/conn_entry.py index 5cb4274..317da22 100644 --- a/nyx/connections/conn_entry.py +++ b/nyx/connections/conn_entry.py @@ -6,13 +6,12 @@ Connection panel entries related to actual connections to or from the system import time import curses +import nyx.util.tracker import nyx.util.ui_tools from nyx.util import tor_controller from nyx.connections import entries -import stem.control - from stem.util import conf, connection, enum, str_tools # Connection Categories: @@ -58,17 +57,6 @@ CONFIG = conf.config_dict('nyx', { 'features.connection.showColumn.expandedIp': True, }) -FINGERPRINT_TRACKER = None - - -def get_fingerprint_tracker(): - global FINGERPRINT_TRACKER - - if FINGERPRINT_TRACKER is None: - FINGERPRINT_TRACKER = FingerprintTracker() - - return FINGERPRINT_TRACKER - class Endpoint: """ @@ -125,13 +113,13 @@ class Endpoint: if self.fingerprint_overwrite: return self.fingerprint_overwrite - my_fingerprint = get_fingerprint_tracker().get_relay_fingerprint(self.address) + my_fingerprint = nyx.util.tracker.get_consensus_tracker().get_relay_fingerprint(self.address) # If there were multiple matches and our port is likely the ORPort then # try again with that to narrow the results. if not my_fingerprint and not self.is_not_or_port: - my_fingerprint = get_fingerprint_tracker().get_relay_fingerprint(self.address, int(self.port)) + my_fingerprint = nyx.util.tracker.get_consensus_tracker().get_relay_fingerprint(self.address, int(self.port)) if my_fingerprint: return my_fingerprint @@ -147,7 +135,7 @@ class Endpoint: my_fingerprint = self.get_fingerprint() if my_fingerprint != 'UNKNOWN': - my_nickname = get_fingerprint_tracker().get_relay_nickname(my_fingerprint) + my_nickname = nyx.util.tracker.get_consensus_tracker().get_relay_nickname(my_fingerprint) if my_nickname: return my_nickname @@ -397,7 +385,7 @@ class ConnectionLine(entries.ConnectionPanelLine): controller = tor_controller() if controller.is_user_traffic_allowed().inbound: - all_matches = get_fingerprint_tracker().get_all_relay_fingerprints(self.foreign.get_address()) + all_matches = nyx.util.tracker.get_consensus_tracker().get_all_relay_fingerprints(self.foreign.get_address()) return all_matches == [] elif my_type == Category.EXIT: # DNS connections exiting us aren't private (since they're hitting our @@ -795,7 +783,7 @@ class ConnectionLine(entries.ConnectionPanelLine): if contact: lines[6] = 'contact: %s' % contact else: - all_matches = get_fingerprint_tracker().get_all_relay_fingerprints(self.foreign.get_address()) + all_matches = nyx.util.tracker.get_consensus_tracker().get_all_relay_fingerprints(self.foreign.get_address()) if all_matches: # multiple matches @@ -889,94 +877,3 @@ class ConnectionLine(entries.ConnectionPanelLine): destination_address += ' (%s)' % ', '.join(extra_info) return destination_address[:max_length] - - -class FingerprintTracker: - def __init__(self): - self._fingerprint_cache = {} # {address => [(port, fingerprint), ..]} for relays - self._nickname_cache = {} # fingerprint => nickname lookup cache - - tor_controller().add_event_listener(self._new_consensus_event, stem.control.EventType.NEWCONSENSUS) - - def _new_consensus_event(self, event): - self._nickname_cache = {} - self.update(event.desc) - - def update(self, router_status_entries): - """ - Updates our cache with the given router status entries. - - :param list router_status_entries: router status entries to populate our cache with - """ - - new_fingerprint_cache = {} - - for desc in router_status_entries: - new_fingerprint_cache.setdefault(desc.address, []).append((desc.or_port, desc.fingerprint)) - - self._fingerprint_cache = new_fingerprint_cache - - def get_relay_fingerprint(self, address, port = None): - """ - Provides the relay running at a given location. If there's multiple relays - and no port is provided to disambiguate then this returns **None**. - - :param str address: address to be checked - :param int port: optional ORPort to match against - - :returns: **str** with the fingerprint of the relay running there - """ - - controller = tor_controller() - - if address == controller.get_info('address', None): - if not port or port in controller.get_ports(stem.control.Listener.OR, []): - return controller.get_info('fingerprint', None) - - matches = self._fingerprint_cache.get(address, []) - - if len(matches) == 1: - match_port, match_fingerprint = matches[0] - return match_fingerprint if (not port or port == match_port) else None - elif len(matches) > 1 and port: - # there's multiple matches and we have a port to disambiguate with - - for match_port, match_fingerprint in matches: - if port == match_port: - return match_fingerprint - - return None - - def get_all_relay_fingerprints(self, address): - """ - Provides a [(port, fingerprint)...] tuple of all relays on a given address. - - :param str address: address to be checked - - :returns: **list** of port/fingerprint tuples running on it - """ - - return self._fingerprint_cache.get(address, []) - - def get_relay_nickname(self, fingerprint): - """ - Provides the nickname associated with the given relay. - - :param str fingerprint: relay to look up - - :reutrns: **str** with the nickname ("Unnamed" if unset), and **None** if - no such relay exists - """ - - controller = tor_controller() - - if fingerprint not in self._nickname_cache: - if fingerprint == controller.get_info('fingerprint', None): - self._nickname_cache[fingerprint] = controller.get_conf('Nickname', 'Unnamed') - else: - ns_entry = controller.get_network_status(fingerprint, None) - - if ns_entry: - self._nickname_cache[fingerprint] = ns_entry.nickname if ns_entry.nickname else 'Unnamed' - - return self._nickname_cache.get(fingerprint) diff --git a/nyx/connections/conn_panel.py b/nyx/connections/conn_panel.py index 83b9d3a..7536a8d 100644 --- a/nyx/connections/conn_panel.py +++ b/nyx/connections/conn_panel.py @@ -332,7 +332,7 @@ class ConnectionPanel(panel.Panel, threading.Thread): # iteration to hide the lag. if last_ran == -1: - conn_entry.get_fingerprint_tracker().update(tor_controller().get_network_statuses([])) + nyx.util.tracker.get_consensus_tracker().update(tor_controller().get_network_statuses([])) last_ran = time.time() diff --git a/nyx/util/tracker.py b/nyx/util/tracker.py index 1b51a05..f721c6f 100644 --- a/nyx/util/tracker.py +++ b/nyx/util/tracker.py @@ -6,6 +6,7 @@ Background tasks for gathering information about the tor process. get_connection_tracker - provides a ConnectionTracker for our tor process get_resource_tracker - provides a ResourceTracker for our tor process get_port_usage_tracker - provides a PortUsageTracker for our system + get_consensus_tracker - provides a ConsensusTracker for our tor process stop_trackers - halts any active trackers @@ -27,6 +28,12 @@ Background tasks for gathering information about the tor process. |- set_paused - pauses or continues work +- stop - stops further work by the daemon + ConsensusTracker - performant lookups for consensus related information + |- update - updates the consensus information we're based on + |- get_relay_nickname - provides the nickname for a given relay + |- get_relay_fingerprint - provides the relay running at a location + +- get_all_relay_fingerprints - provides all relays running at a location + .. data:: Resources Resource usage information retrieved about the tor process. @@ -43,7 +50,8 @@ import collections import time import threading -from stem.control import State +import stem.control + from stem.util import conf, connection, proc, str_tools, system from nyx.util import log, tor_controller @@ -57,6 +65,7 @@ CONFIG = conf.config_dict('nyx', { CONNECTION_TRACKER = None RESOURCE_TRACKER = None PORT_USAGE_TRACKER = None +CONSENSUS_TRACKER = None Resources = collections.namedtuple('Resources', [ 'cpu_sample', @@ -107,6 +116,19 @@ def get_port_usage_tracker(): return PORT_USAGE_TRACKER +def get_consensus_tracker(): + """ + Singleton for tracking the connections established by tor. + """ + + global CONSENSUS_TRACKER + + if CONSENSUS_TRACKER is None: + CONSENSUS_TRACKER = ConsensusTracker() + + return CONSENSUS_TRACKER + + def stop_trackers(): """ Halts active trackers, providing back the thread shutting them down. @@ -305,7 +327,7 @@ class Daemon(threading.Thread): controller = tor_controller() controller.add_status_listener(self._tor_status_listener) - self._tor_status_listener(controller, State.INIT, None) + self._tor_status_listener(controller, stem.control.State.INIT, None) def run(self): while not self._halt: @@ -393,7 +415,7 @@ class Daemon(threading.Thread): def _tor_status_listener(self, controller, event_type, _): with self._process_lock: - if not self._halt and event_type in (State.INIT, State.RESET): + if not self._halt and event_type in (stem.control.State.INIT, stem.control.State.RESET): tor_pid = controller.get_pid(None) tor_cmd = system.name_by_pid(tor_pid) if tor_pid else None @@ -664,3 +686,98 @@ class PortUsageTracker(Daemon): log.debug('tracker.unable_to_get_port_usages', exc = exc) return False + + +class ConsensusTracker(object): + """ + Provides performant lookups of consensus information. + """ + + def __init__(self): + self._fingerprint_cache = {} # {address => [(port, fingerprint), ..]} for relays + self._nickname_cache = {} # fingerprint => nickname lookup cache + + tor_controller().add_event_listener(self._new_consensus_event, stem.control.EventType.NEWCONSENSUS) + + def _new_consensus_event(self, event): + self._nickname_cache = {} + self.update(event.desc) + + def update(self, router_status_entries): + """ + Updates our cache with the given router status entries. + + :param list router_status_entries: router status entries to populate our cache with + """ + + new_fingerprint_cache = {} + + for desc in router_status_entries: + new_fingerprint_cache.setdefault(desc.address, []).append((desc.or_port, desc.fingerprint)) + + self._fingerprint_cache = new_fingerprint_cache + + def get_relay_nickname(self, fingerprint): + """ + Provides the nickname associated with the given relay. + + :param str fingerprint: relay to look up + + :reutrns: **str** with the nickname ("Unnamed" if unset), and **None** if + no such relay exists + """ + + controller = tor_controller() + + if fingerprint not in self._nickname_cache: + if fingerprint == controller.get_info('fingerprint', None): + self._nickname_cache[fingerprint] = controller.get_conf('Nickname', 'Unnamed') + else: + ns_entry = controller.get_network_status(fingerprint, None) + + if ns_entry: + self._nickname_cache[fingerprint] = ns_entry.nickname if ns_entry.nickname else 'Unnamed' + + return self._nickname_cache.get(fingerprint) + + def get_relay_fingerprint(self, address, port = None): + """ + Provides the relay running at a given location. If there's multiple relays + and no port is provided to disambiguate then this returns **None**. + + :param str address: address to be checked + :param int port: optional ORPort to match against + + :returns: **str** with the fingerprint of the relay running there + """ + + controller = tor_controller() + + if address == controller.get_info('address', None): + if not port or port in controller.get_ports(stem.control.Listener.OR, []): + return controller.get_info('fingerprint', None) + + matches = self._fingerprint_cache.get(address, []) + + if len(matches) == 1: + match_port, match_fingerprint = matches[0] + return match_fingerprint if (not port or port == match_port) else None + elif len(matches) > 1 and port: + # there's multiple matches and we have a port to disambiguate with + + for match_port, match_fingerprint in matches: + if port == match_port: + return match_fingerprint + + return None + + def get_all_relay_fingerprints(self, address): + """ + Provides a [(port, fingerprint)...] tuple of all relays on a given address. + + :param str address: address to be checked + + :returns: **list** of port/fingerprint tuples running on it + """ + + return self._fingerprint_cache.get(address, []) _______________________________________________ tor-commits mailing list tor-commits@lists.torproject.org https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits