[tor-commits] [ooni-probe/master] Finish reafactoring and porting daphn3.

art at torproject.org art at torproject.org
Mon Nov 12 19:14:03 UTC 2012


commit 03a8462714d0609d0548d24fb5f8762b8d12d358
Author: Arturo Filastò <art at fuffa.org>
Date:   Mon Nov 12 20:12:18 2012 +0100

    Finish reafactoring and porting daphn3.
    * XXX not fully tested
---
 nettests/core/daphn3.py          |   71 +++++++++++++++-------
 ooni/kit/daphn3.py               |  125 ++++++++++++++++++++++---------------
 oonib/config.py                  |    4 +-
 oonib/testhelpers/tcp_helpers.py |   46 ++++++++++++--
 4 files changed, 164 insertions(+), 82 deletions(-)

diff --git a/nettests/core/daphn3.py b/nettests/core/daphn3.py
index cc4803c..c277c56 100644
--- a/nettests/core/daphn3.py
+++ b/nettests/core/daphn3.py
@@ -6,18 +6,42 @@ from ooni import nettest
 from ooni.kit import daphn3
 from ooni.utils import log
 
+class Daphn3ClientProtocol(daphn3.Daphn3Protocol):
+    def nextStep(self):
+        log.debug("Moving on to next step in the state walk")
+        self.current_data_received = 0
+        if self.current_step >= (len(self.steps) - 1):
+            log.msg("Reached the end of the state machine")
+            log.msg("Censorship fingerpint bisected!")
+            step_idx, mutation_idx = self.factory.mutation
+            log.msg("step_idx: %s | mutation_id: %s" % (step_idx, mutation_idx))
+            #self.transport.loseConnection()
+            if self.report:
+                self.report['mutation_idx'] = mutation_idx
+                self.report['step_idx'] = step_idx
+            self.d.callback(None)
+            return
+        else:
+            self.current_step += 1
+        if self._current_step_role() == self.role:
+            # We need to send more data because we are again responsible for
+            # doing so.
+            self.sendPayload()
+
+
 class Daphn3ClientFactory(protocol.ClientFactory):
     protocol = daphn3.Daphn3Protocol
-    def __init__(self, steps):
-        self.steps = steps
+    mutation = [0,0]
+    steps = None
 
     def buildProtocol(self, addr):
-        p = self.protocol(steps=self.steps)
+        p = self.protocol()
+        p.steps = self.steps
         p.factory = self
         return p
 
     def startedConnecting(self, connector):
-        print "Started connecting %s" % connector
+        log.msg("Started connecting %s" % connector)
 
     def clientConnectionFailed(self, reason, connector):
         log.err("We failed connecting the the OONIB")
@@ -31,8 +55,8 @@ class Daphn3ClientFactory(protocol.ClientFactory):
 
 class daphn3Args(usage.Options):
     optParameters = [
-                     ['host', 'h', None, 'Target Hostname'],
-                     ['port', 'p', None, 'Target port number']]
+                     ['host', 'h', '127.0.0.1', 'Target Hostname'],
+                     ['port', 'p', 57003, 'Target port number']]
 
     optFlags = [['pcap', 'c', 'Specify that the input file is a pcap file'],
                 ['yaml', 'y', 'Specify that the input file is a YAML file (default)']]
@@ -68,21 +92,16 @@ class daphn3Test(nettest.NetTestCase):
         else:
             daphn3Steps = [{'client': 'testing'}, {'server': 'antani'}]
 
-        for idx, step in enumerate(daphn3Steps):
-            current_packet = step.values()[0]
-            for mutation_idx in range(len(current_packet)):
-                if step.keys()[0] == "client":
-                    mutated_step = daphn3.daphn3Mutate(daphn3Steps,
-                            idx, mutation_idx)
-                    yield mutated_step
-                else:
-                    yield daphn3Steps
-
-    def setUp(self):
-        self.factory = Daphn3ClientFactory(self.input)
-        self.factory.report = self.report
-        print "Just set the factory to %s with %s" % (self.factory, 
-                self.input)
+        #for idx, step in enumerate(daphn3Steps):
+        #    current_packet = step.values()[0]
+        #    for mutation_idx in range(len(current_packet)):
+        #        if step.keys()[0] == "client":
+        #            mutated_step = daphn3.daphn3Mutate(daphn3Steps,
+        #                    idx, mutation_idx)
+        #            yield mutated_step
+        #        else:
+        #            yield daphn3Steps
+        yield daphn3Steps
 
     def test_daphn3(self):
         host = self.localOptions['host']
@@ -95,11 +114,17 @@ class daphn3Test(nettest.NetTestCase):
 
         def success(protocol):
             log.msg("Successfully connected")
-            protocol.sendMutation()
+            protocol.sendPayload()
+            return protocol.d
 
         log.msg("Connecting to %s:%s" % (host, port))
         endpoint = endpoints.TCP4ClientEndpoint(reactor, host, port)
-        d = endpoint.connect(self.factory)
+        daphn3_factory = Daphn3ClientFactory()
+        #daphn3_factory.steps = self.input
+        daphn3_factory.steps = [{'client': 'client_packet'},
+                {'server': 'server_packet'}]
+        daphn3_factory.report = self.report
+        d = endpoint.connect(daphn3_factory)
         d.addErrback(failure)
         d.addCallback(success)
         return d
diff --git a/ooni/kit/daphn3.py b/ooni/kit/daphn3.py
index 1c340b4..8d655c3 100644
--- a/ooni/kit/daphn3.py
+++ b/ooni/kit/daphn3.py
@@ -76,7 +76,7 @@ def read_yaml(filename):
 class NoInputSpecified(Exception):
     pass
 
-class StateError(Exception):
+class StepError(Exception):
     pass
 
 def daphn3MutateString(string, i):
@@ -88,7 +88,7 @@ def daphn3MutateString(string, i):
         if y == i:
             mutated += chr(ord(string[i]) + 1)
         else:
-            mutated += string[i]
+            mutated += string[y]
     return mutated
 
 def daphn3Mutate(steps, step_idx, mutation_idx):
@@ -109,77 +109,100 @@ def daphn3Mutate(steps, step_idx, mutation_idx):
     return mutated_steps
 
 class Daphn3Protocol(protocol.Protocol):
-    def __init__(self, steps=None, 
-            yaml_file=None, pcap_file=None, 
-            role="client"):
-        if yaml_file:
-            self.steps = read_yaml(yaml_file)
-        elif pcap_file:
-            self.steps = read_pcap(pcap_file)
-        elif steps:
-            self.steps = steps
-        else:
-            raise NoInputSpecified
-
-        # XXX remove me
-        #self.steps = [{'client': 'antani'}, {'server': 'sblinda'}]
-        self.role = role
-        # We use this index to keep track of where we are in the state machine
-        self.current_step = 0
-
-        # 0 indicates we are waiting to receive data, while 1 indicates we are
-        # sending data
-        self.current_state = 0
-        self.current_data_received = 0
-
-    def sendMutation(self):
-        self.debug("Sending mutation")
-        current_step_role = self.steps[self.current_step].keys()[0]
-        current_step_data = self.steps[self.current_step].values()[0]
+    steps = None
+    role = "client"
+    report = None
+    # We use this index to keep track of where we are in the state machine
+    current_step = 0
+    current_data_received = 0
+
+    # We use this to keep track of the mutated steps
+    mutated_steps = None
+    d = defer.Deferred()
+
+    def _current_step_role(self):
+        return self.steps[self.current_step].keys()[0]
+
+    def _current_step_data(self):
+        step_idx, mutation_idx = self.factory.mutation
+        log.debug("Mutating %s %s" % (step_idx, mutation_idx))
+        mutated_step = daphn3Mutate(self.steps, 
+                step_idx, mutation_idx)
+        log.debug("Mutated packet into %s" % mutated_step)
+        return mutated_step[self.current_step].values()[0]
+
+    def sendPayload(self):
+        self.debug("Sending payload")
+        current_step_role = self._current_step_role()
+        current_step_data = self._current_step_data()
         if current_step_role == self.role:
             print "In a state to do shit %s" % current_step_data
             self.transport.write(current_step_data)
-            self.nextState()
+            self.nextStep()
         else:
             print "Not in a state to do anything"
 
     def connectionMade(self):
         print "Got connection"
-        self.sendMutation()
 
     def debug(self, msg):
-        print "Current step %s" % self.current_step
-        print "Current data received %s" % self.current_data_received
-        print "Current role %s" % self.role
-        print "Current steps %s" % self.steps
-        print "Current state %s" % self.current_state
-
-    def nextState(self):
-        print "Moving on to next state"
-        self.current_data_received = 0
-        self.current_step += 1
-        if self.current_step >= len(self.steps):
-            print "Going to loose this connection"
-            self.transport.loseConnection()
-            return
-        self.sendMutation()
+        log.debug("Current step %s" % self.current_step)
+        log.debug("Current data received %s" % self.current_data_received)
+        log.debug("Current role %s" % self.role)
+        log.debug("Current steps %s" % self.steps)
+        log.debug("Current step data %s" % self._current_step_data())
+
+    def nextStep(self):
+        """
+        XXX this method is overwritten individually by client and server transport.
+        There is probably a smarter way to do this and refactor the common
+        code into one place, but for the moment like this is good.
+        """
+        pass
 
     def dataReceived(self, data):
         current_step_role = self.steps[self.current_step].keys()[0]
         log.debug("Current step role %s" % current_step_role)
         if current_step_role == self.role:
             log.debug("Got a state error!")
-            raise StateError("I should not have gotten data, while I did, \
-                    perhaps there is a wrong state machine?")
+            raise StepError("I should not have gotten data, while I did, \
+                    perhaps there is something wrong with the state machine?")
 
         self.current_data_received += len(data)
         expected_data_in_this_state = len(self.steps[self.current_step].values()[0])
 
         log.debug("Current data received %s" %  self.current_data_received)
         if self.current_data_received >= expected_data_in_this_state:
-            self.nextState()
+            self.nextStep()
+
+    def nextMutation(self):
+        log.debug("Moving onto next mutation")
+        # [step_idx, mutation_idx]
+        c_step_idx, c_mutation_idx = self.factory.mutation
+        log.debug("[%s]: c_step_idx: %s | c_mutation_idx: %s" % (self.role,
+            c_step_idx, c_mutation_idx))
+
+        if c_step_idx >= (len(self.steps) - 1):
+            log.err("No censorship fingerprint bisected.")
+            log.err("Givinig up.")
+            self.transport.loseConnection()
+            return
+
+        # This means we have mutated all bytes in the step
+        # we should proceed to mutating the next step.
+        log.debug("steps: %s | %s" % (self.steps, self.steps[c_step_idx]))
+        if c_mutation_idx >= (len(self.steps[c_step_idx].values()[0]) - 1):
+            log.debug("Finished mutating step")
+            # increase step
+            self.factory.mutation[0] += 1
+            # reset mutation idx
+            self.factory.mutation[1] = 0
+        else:
+            log.debug("Mutating next byte in step")
+            # increase mutation index
+            self.factory.mutation[1] += 1
 
     def connectionLost(self, reason):
-        self.debug("Lost the connection")
-        print reason
+        self.debug("--- Lost the connection ---")
+        self.nextMutation()
 
diff --git a/oonib/config.py b/oonib/config.py
index cf2e362..1a70b85 100644
--- a/oonib/config.py
+++ b/oonib/config.py
@@ -27,8 +27,8 @@ helpers.tcp_echo = Storage()
 helpers.tcp_echo.port = 57002
 
 helpers.daphn3 = Storage()
-helpers.daphn3.yaml_file = "/path/to/data/oonib/daphn3.yaml"
-helpers.daphn3.pcap_file = "/path/to/data/server.pcap"
+#helpers.daphn3.yaml_file = "/path/to/data/oonib/daphn3.yaml"
+#helpers.daphn3.pcap_file = "/path/to/data/server.pcap"
 helpers.daphn3.port = 57003
 
 helpers.dns = Storage()
diff --git a/oonib/testhelpers/tcp_helpers.py b/oonib/testhelpers/tcp_helpers.py
index 4287a59..4d32ae0 100644
--- a/oonib/testhelpers/tcp_helpers.py
+++ b/oonib/testhelpers/tcp_helpers.py
@@ -3,7 +3,7 @@ from twisted.internet.protocol import Protocol, Factory, ServerFactory
 from twisted.internet.error import ConnectionDone
 
 from oonib import config
-
+from ooni.utils import log
 from ooni.kit.daphn3 import Daphn3Protocol
 from ooni.kit.daphn3 import read_pcap, read_yaml
 
@@ -17,8 +17,37 @@ class TCPEchoHelper(Factory):
     """
     protocol = TCPEchoProtocol
 
-daphn3Steps = [{'client': '\x00\x00\x00'}, 
-        {'server': '\x00\x00\x00'}]
+if config.helpers.daphn3.yaml_file:
+    daphn3Steps = read_pcap(config.helpers.daphn3.yaml_file)
+
+elif config.helpers.daphn3.pcap_file:
+    daphn3Steps = read_yaml(config.helpers.daphn3.pcap_file)
+
+else:
+    daphn3Steps = [{'client': 'client_packet'}, 
+        {'server': 'server_packet'}]
+
+class Daphn3ServerProtocol(Daphn3Protocol):
+    def nextStep(self):
+        log.debug("Moving on to next step in the state walk")
+        self.current_data_received = 0
+        # Python why?
+        if self.current_step >= (len(self.steps) - 1):
+            log.msg("Reached the end of the state machine")
+            log.msg("Censorship fingerpint bisected!")
+            step_idx, mutation_idx = self.factory.mutation
+            log.msg("step_idx: %s | mutation_id: %s" % (step_idx, mutation_idx))
+            #self.transport.loseConnection()
+            if self.report:
+                self.report['mutation_idx'] = mutation_idx
+                self.report['step_idx'] = step_idx
+            return
+        else:
+            self.current_step += 1
+        if self._current_step_role() == self.role:
+            # We need to send more data because we are again responsible for
+            # doing so.
+            self.sendPayload()
 
 class Daphn3Server(ServerFactory):
     """
@@ -29,10 +58,15 @@ class Daphn3Server(ServerFactory):
     two different clients are sharing the same IP, but hopefully the
     probability of such thing is not that likely.
     """
-    protocol = Daphn3Protocol
+    protocol = Daphn3ServerProtocol
+    # step_idx, mutation_idx
+    mutation = [0, 0]
     def buildProtocol(self, addr):
-        p = self.protocol(steps=daphn3Steps, 
-                role="server")
+        p = self.protocol()
+        p.steps = daphn3Steps
+        p.role = "server"
         p.factory = self
         return p
 
+
+





More information about the tor-commits mailing list