commit 3ba521dfa2f2a6f023e7b92fa24bcfb910f1fe78 Author: Damian Johnson atagar@torproject.org Date: Mon Dec 3 09:00:49 2012 -0800
Support for CIRC_MINOR events
Implementation and testing for CIRC_MINOR events. Snagged test data by listening for events while bootstrapping. --- stem/__init__.py | 20 +++++++++++ stem/control.py | 1 + stem/response/events.py | 77 ++++++++++++++++++++++++++++++++++++++++++ test/unit/response/events.py | 25 +++++++++++++ 4 files changed, 123 insertions(+), 0 deletions(-)
diff --git a/stem/__init__.py b/stem/__init__.py index d7ec4f8..cddf033 100644 --- a/stem/__init__.py +++ b/stem/__init__.py @@ -87,6 +87,7 @@ Library for working with the tor process. **HS_SERVICE_REND** server side hidden service rendezvous circuit **TESTING** testing to see if we're reachable, so we can be used as a relay **CONTROLLER** circuit that was built by a controller + **MEASURE_TIMEOUT** unknown (https://trac.torproject.org/7626) ==================== ===========
.. data:: CircClosureReason (enum) @@ -114,6 +115,18 @@ Library for working with the tor process. **MEASUREMENT_EXPIRED** same as **TIMEOUT** except that it was left open for measurement purposes ========================= ===========
+.. data:: CircEvent (enum) + + Type of change reflected in a circuit by a CIRC_MINOR event. Tor may provide + event types not in this enum. + + ===================== =========== + CircEvent Description + ===================== =========== + **PURPOSE_CHANGED** circuit purpose or hidden service state has changed + **CANNIBALIZED** circuit connections are being reused for a different circuit + ===================== =========== + .. data:: HiddenServiceState (enum)
State that a hidden service circuit can have. These were introduced in tor @@ -355,6 +368,7 @@ __all__ = [ "CircBuildFlag", "CircPurpose", "CircClosureReason", + "CircEvent", "HiddenServiceState", "StreamStatus", "StreamClosureReason", @@ -467,6 +481,7 @@ CircPurpose = stem.util.enum.UppercaseEnum( "HS_SERVICE_REND", "TESTING", "CONTROLLER", + "MEASURE_TIMEOUT", )
CircClosureReason = stem.util.enum.UppercaseEnum( @@ -487,6 +502,11 @@ CircClosureReason = stem.util.enum.UppercaseEnum( "MEASUREMENT_EXPIRED", )
+CircEvent = stem.util.enum.UppercaseEnum( + "PURPOSE_CHANGED", + "CANNIBALIZED", +) + HiddenServiceState = stem.util.enum.UppercaseEnum( "HSCI_CONNECTING", "HSCI_INTRO_SENT", diff --git a/stem/control.py b/stem/control.py index 43229b5..c20db12 100644 --- a/stem/control.py +++ b/stem/control.py @@ -83,6 +83,7 @@ providing its own for interacting at a higher level. **BUILDTIMEOUT_SET** :class:`stem.response.events.BuildTimeoutSetEvent` **BW** :class:`stem.response.events.BandwidthEvent` **CIRC** :class:`stem.response.events.CircuitEvent` + **CIRC_MINOR** :class:`stem.response.events.CircMinorEvent` **CLIENTS_SEEN** :class:`stem.response.events.ClientsSeenEvent` **CONF_CHANGED** :class:`stem.response.events.ConfChangedEvent` **DEBUG** :class:`stem.response.events.LogEvent` diff --git a/stem/response/events.py b/stem/response/events.py index bf74fce..1763913 100644 --- a/stem/response/events.py +++ b/stem/response/events.py @@ -367,6 +367,82 @@ class CircuitEvent(Event): log_id = "event.circ.unknown_remote_reason.%s" % self.remote_reason log.log_once(log_id, log.INFO, unrecognized_msg % ('remote reason', self.remote_reason))
+class CircMinorEvent(Event): + """ + Event providing information about minor changes in our circuits. This was + first added in tor version 0.2.3.11. + + :var str id: circuit identifier + :var stem.CircEvent event: type of change in the circuit + :var tuple path: relays involved in the circuit, these are + **(fingerprint, nickname)** tuples + :var tuple build_flags: :data:`~stem.CircBuildFlag` attributes + governing how the circuit is built + :var stem.CircPurpose purpose: purpose that the circuit is intended for + :var stem.HiddenServiceState hs_state: status if this is a hidden service circuit + :var str rend_query: circuit's rendezvous-point if this is hidden service related + :var datetime created: time when the circuit was created or cannibalized + :var stem.CircPurpose old_purpose: prior purpose for the circuit + :var stem.HiddenServiceState old_hs_state: prior status as a hidden service circuit + """ + + _POSITIONAL_ARGS = ("id", "event", "path") + _KEYWORD_ARGS = { + "BUILD_FLAGS": "build_flags", + "PURPOSE": "purpose", + "HS_STATE": "hs_state", + "REND_QUERY": "rend_query", + "TIME_CREATED": "created", + "OLD_PURPOSE": "old_purpose", + "OLD_HS_STATE": "old_hs_state", + } + + def _parse(self): + self.path = tuple(stem.control._parse_circ_path(self.path)) + + if self.build_flags != None: + self.build_flags = tuple(self.build_flags.split(',')) + + if self.created != None: + try: + self.created = str_tools.parse_iso_timestamp(self.created) + except ValueError, exc: + raise stem.ProtocolError("Unable to parse create date (%s): %s" % (exc, self)) + + if self.id != None and not tor_tools.is_valid_circuit_id(self.id): + raise stem.ProtocolError("Circuit IDs must be one to sixteen alphanumeric characters, got '%s': %s" % (self.id, self)) + + # log if we have an unrecognized event, status, build flag, purpose, or + # hidden service state + + unrecognized_msg = UNRECOGNIZED_ATTR_MSG % ("CIRC_MINOR", self) + + if not self.event in stem.CircEvent: + log_id = "event.circ_minor.unknown_event.%s" % self.event + log.log_once(log_id, log.INFO, unrecognized_msg % ('event', self.event)) + + if self.build_flags: + for flag in self.build_flags: + if not flag in stem.CircBuildFlag: + log_id = "event.circ_minor.unknown_build_flag.%s" % flag + log.log_once(log_id, log.INFO, unrecognized_msg % ('build flag', flag)) + + if self.purpose and (not self.purpose in stem.CircPurpose): + log_id = "event.circ_minor.unknown_purpose.%s" % self.purpose + log.log_once(log_id, log.INFO, unrecognized_msg % ('purpose', self.purpose)) + + if self.hs_state and (not self.hs_state in stem.HiddenServiceState): + log_id = "event.circ_minor.unknown_hs_state.%s" % self.hs_state + log.log_once(log_id, log.INFO, unrecognized_msg % ('hidden service state', self.hs_state)) + + if self.old_purpose and (not self.old_purpose in stem.CircPurpose): + log_id = "event.circ_minor.unknown_purpose.%s" % self.old_purpose + log.log_once(log_id, log.INFO, unrecognized_msg % ('purpose', self.old_purpose)) + + if self.old_hs_state and (not self.old_hs_state in stem.HiddenServiceState): + log_id = "event.circ_minor.unknown_hs_state.%s" % self.old_hs_state + log.log_once(log_id, log.INFO, unrecognized_msg % ('hidden service state', self.hs_state)) + class ClientsSeenEvent(Event): """ Periodic event on bridge relays that provides a summary of our users. @@ -784,6 +860,7 @@ EVENT_TYPE_TO_CLASS = { "BUILDTIMEOUT_SET": BuildTimeoutSetEvent, "BW": BandwidthEvent, "CIRC": CircuitEvent, + "CIRC_MINOR": CircMinorEvent, "CLIENTS_SEEN": ClientsSeenEvent, "CONF_CHANGED": ConfChangedEvent, "DEBUG": LogEvent, diff --git a/test/unit/response/events.py b/test/unit/response/events.py index f6a4b85..a977627 100644 --- a/test/unit/response/events.py +++ b/test/unit/response/events.py @@ -53,6 +53,15 @@ $E57A476CD4DFBD99B4EE52A100A58610AD6E80B9,hamburgerphone" CIRC_BUILT_OLD = "650 CIRC 1 BUILT \ $E57A476CD4DFBD99B4EE52A100A58610AD6E80B9,hamburgerphone,PrivacyRepublic14"
+# CIRC_MINOR event from tor 0.2.3.16. + +CIRC_MINOR_EVENT = "650 CIRC_MINOR 7 PURPOSE_CHANGED \ +$67B2BDA4264D8A189D9270E28B1D30A262838243~europa1 \ +BUILD_FLAGS=IS_INTERNAL,NEED_CAPACITY \ +PURPOSE=MEASURE_TIMEOUT \ +TIME_CREATED=2012-12-03T16:45:33.409602 \ +OLD_PURPOSE=TESTING" + # CLIENTS_SEEN example from the spec
CLIENTS_SEEN_EVENT = '650 CLIENTS_SEEN \ @@ -406,6 +415,22 @@ class TestEvents(unittest.TestCase): self.assertEqual({'us': 16, 'de': 8, 'uk': 8}, event.locales) self.assertEqual({'v4': 16, 'v6': 40}, event.ip_versions)
+ def test_circ_minor_event(self): + event = _get_event(CIRC_MINOR_EVENT) + + self.assertTrue(isinstance(event, stem.response.events.CircMinorEvent)) + self.assertEqual(CIRC_MINOR_EVENT.lstrip("650 "), str(event)) + self.assertEqual("7", event.id) + self.assertEqual(CircEvent.PURPOSE_CHANGED, event.event) + self.assertEqual((("67B2BDA4264D8A189D9270E28B1D30A262838243", "europa1"),), event.path) + self.assertEqual((CircBuildFlag.IS_INTERNAL, CircBuildFlag.NEED_CAPACITY), event.build_flags) + self.assertEqual(CircPurpose.MEASURE_TIMEOUT, event.purpose) + self.assertEqual(None, event.hs_state) + self.assertEqual(None, event.rend_query) + self.assertEqual(datetime.datetime(2012, 12, 3, 16, 45, 33, 409602), event.created) + self.assertEqual(CircPurpose.TESTING, event.old_purpose) + self.assertEqual(None, event.old_hs_state) + def test_conf_changed(self): event = _get_event(CONF_CHANGED_EVENT)
tor-commits@lists.torproject.org