[tor-commits] [ooni-probe/master] Implement censorship detection for fingerprint

art at torproject.org art at torproject.org
Wed Jul 11 13:56:42 UTC 2012


commit d249c7ff695b0efdb4dd23a0a4bc6756024f7f19
Author: Arturo Filastò <hellais at torproject.org>
Date:   Wed Jul 11 12:37:23 2012 +0200

    Implement censorship detection for fingerprint
    * Move part of b0wser to a ooni protocol
---
 ooni/protocols/b0wser.py |  104 ++++++++++++++++++++++++++++++++++++++++
 oonib/b0wser.py          |  118 ++++++++++++----------------------------------
 2 files changed, 134 insertions(+), 88 deletions(-)

diff --git a/ooni/protocols/b0wser.py b/ooni/protocols/b0wser.py
new file mode 100644
index 0000000..ae6b002
--- /dev/null
+++ b/ooni/protocols/b0wser.py
@@ -0,0 +1,104 @@
+from ooni.utils import log
+
+class Mutator:
+    idx = 0
+    step = 0
+
+    waiting = False
+    waiting_step = 0
+
+    def __init__(self, steps):
+        """
+        @param steps: array of dicts containing as keys data and wait. Data is
+                      the content of the ith packet to be sent and wait is how
+                      much we should wait before mutating the packet of the
+                      next step.
+        """
+        self.steps = steps
+
+    def _mutate(self, data, idx):
+        """
+        Mutate the idx bytes by increasing it's value by one
+
+        @param data: the data to be mutated.
+
+        @param idx: what byte should be mutated.
+        """
+        print "idx: %s, data: %s" % (idx, data)
+        ret = data[:idx]
+        ret += chr(ord(data[idx]) + 1)
+        ret += data[idx+1:]
+        return ret
+
+    def state(self):
+        """
+        Return the current mutation state. As in what bytes are being mutated.
+
+        Returns a dict containg the packet index and the step number.
+        """
+        return {'idx': self.idx, 'step': self.step}
+
+    def next_mutation(self):
+        """
+        Increases by one the mutation state.
+
+        ex. (* is the mutation state, i.e. the byte to be mutated)
+        before [___*] [____]
+               step1   step2
+        after  [____] [*___]
+
+        Should be called every time you need to proceed onto the next mutation.
+        It changes the internal state of the mutator to that of the next
+        mutatation.
+
+        returns True if another mutation is available.
+        returns False if all the possible mutations have been done.
+        """
+        if self.step > len(self.steps):
+            self.waiting = True
+            return False
+
+        self.idx += 1
+        current_idx = self.idx
+        current_step = self.step
+        current_data = self.steps[current_step]['data']
+        data_to_receive = self.steps[current_step]['wait']
+
+        if self.waiting and self.waiting_step == data_to_receive:
+            log.debug("I am no longer waiting.")
+            self.waiting = False
+            self.waiting_step = 0
+            self.idx = 0
+
+        elif self.waiting:
+            log.debug("Waiting some more.")
+            self.waiting_step += 1
+
+        elif current_idx >= len(current_data):
+            log.debug("Entering waiting mode.")
+            self.step += 1
+            self.idx = 0
+            self.waiting = True
+        log.debug("current index %s" % current_idx)
+        log.debug("current data %s" % len(current_data))
+        return True
+
+    def get_mutation(self, step):
+        """
+        Returns the current packet to be sent to the wire.
+        If no mutation is necessary it will return the plain data.
+        Should be called when you are interested in obtaining the data to be
+        sent for the selected state.
+
+        @param step: the current step you want the mutation for
+
+        returns the mutated packet for the specified step.
+        """
+        if step != self.step or self.waiting:
+            log.debug("I am not going to do anything :)")
+            return self.steps[step]['data']
+
+        data = self.steps[step]['data']
+        print "Mutating %s with idx %s" % (data, self.idx)
+        return self._mutate(data, self.idx)
+
diff --git a/oonib/b0wser.py b/oonib/b0wser.py
index 186c9a4..6da1997 100644
--- a/oonib/b0wser.py
+++ b/oonib/b0wser.py
@@ -1,100 +1,19 @@
 from twisted.internet import protocol
+from twisted.internet.error import ConnectionDone
 
-class Mutator:
-    idx = 0
-    step = 0
-
-    waiting = False
-    waiting_step = 0
-
-    def __init__(self, steps):
-        self.steps = steps
-
-    def _mutate(self, data, idx):
-        """
-        Mutate the idx bytes by increasing it's value by one
-
-        @param data: the data to be mutated.
-
-        @param idx: what byte should be mutated.
-        """
-        print "idx: %s, data: %s" % (idx, data)
-        ret = data[:idx]
-        ret += chr(ord(data[idx]) + 1)
-        ret += data[idx+1:]
-        return ret
-
-    def next_mutation(self):
-        """
-        Increases by one the mutation state.
-
-        ex. (* is the mutation state, i.e. the byte to be mutated)
-        before [___*] [____]
-               step1   step2
-        after  [____] [*___]
-
-        Should be called every time you need to proceed onto the next mutation.
-        It changes the internal state of the mutator to that of the next
-        mutatation.
-
-        returns True if another mutation is available.
-        returns False if all the possible mutations have been done.
-        """
-        if self.step > len(self.steps):
-            self.waiting = True
-            return False
-
-        self.idx += 1
-        current_idx = self.idx
-        current_step = self.step
-        current_data = self.steps[current_step]['data']
-        data_to_receive = self.steps[current_step]['wait']
-
-        if self.waiting and self.waiting_step == data_to_receive:
-            print "I am no longer waiting..."
-            self.waiting = False
-            self.waiting_step = 0
-            self.idx = 0
-
-        elif self.waiting:
-            print "Waiting some more..."
-            self.waiting_step += 1
-
-        elif current_idx >= len(current_data):
-            print "Entering waiting mode..."
-            self.step += 1
-            self.idx = 0
-            self.waiting = True
-        print "current index %s current data %s" % (current_idx, len(current_data))
-        return True
-
-    def get_mutation(self, state):
-        """
-        Returns the current packet to be sent to the wire.
-        If no mutation is necessary it will return the plain data.
-        Should be called when you are interested in obtaining the data to be
-        sent for the selected state.
-
-        @param step: the current step you want the mutation for
-
-        returns the mutated packet for the specified step.
-        """
-        if step != self.step or self.waiting:
-            print "I am not going to do anything :)"
-            return self.steps[step]['data']
-
-        data = self.steps[step]['data']
-        print "Mutating %s with idx %s" % (data, self.idx)
-        return self._mutate(data, self.idx)
+from ooni.plugoo import reports
+from ooni.protocols.b0wser import Mutator
 
 class B0wserProtocol(protocol.Protocol):
     steps = [{'data': "STEP1", 'wait': 4},
              {'data': "STEP2", 'wait': 4},
              {'data': "STEP3", 'wait': 4}]
-
     mutator = None
+
     state = 0
+    total_states = len(steps) - 1
     received_data = 0
+    report = reports.Report('b0wser', 'b0wser.yamlooni')
 
     def next_state(self):
         data = self.mutator.get_mutation(self.state)
@@ -111,6 +30,28 @@ class B0wserProtocol(protocol.Protocol):
             print "Moving to next state %s" % self.state
             self.next_state()
 
+    def censorship_detected(self, report):
+        print "The connection was closed because of %s" % report['reason']
+        print "I may have matched the censorship fingerprint"
+        print "State %s, Mutator %s" % (report['proto_state'],
+                                        report['mutator_state'])
+        self.report(report)
+
+
+    def connectionLost(self, reason):
+        report = {'reason': reason, 'proto_state': self.state,
+                'mutator_state': self.mutator.state(), 'trigger': None}
+
+        if self.state < self.total_states:
+            report['trigger'] = 'did not finish state walk'
+            self.censorship_detected(report)
+
+        if reason.check(ConnectionDone):
+            print "Connection closed cleanly"
+        else:
+            report['trigger'] = 'unclean connection closure'
+            self.censorship_detected(report)
+
 class B0wserServer(protocol.ServerFactory):
     protocol = B0wserProtocol
     mutations = {}
@@ -122,7 +63,8 @@ class B0wserServer(protocol.ServerFactory):
             self.mutations[addr.host] = Mutator(p.steps)
         else:
             print "Moving on to next mutation"
-            self.mutations[addr.host].next_mutation()
+            if not self.mutations[addr.host].next_mutation():
+                self.mutations.pop(addr.host)
         p.mutator = self.mutations[addr.host]
         return p
 





More information about the tor-commits mailing list