[tor-commits] [stem/master] Support for CIRC_MINOR events

atagar at torproject.org atagar at torproject.org
Mon Dec 3 17:06:02 UTC 2012


commit 3ba521dfa2f2a6f023e7b92fa24bcfb910f1fe78
Author: Damian Johnson <atagar at 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)
     



More information about the tor-commits mailing list