[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[or-cvs] r9363: Reorg and cleanup. Also fixed a bug in circuit builder stori (torflow/trunk)



Author: mikeperry
Date: 2007-01-16 03:19:52 -0500 (Tue, 16 Jan 2007)
New Revision: 9363

Added:
   torflow/trunk/LICENSE
   torflow/trunk/TorUtil.py
Modified:
   torflow/trunk/TorCtl.py
   torflow/trunk/metatroller.py
Log:
Reorg and cleanup. Also fixed a bug in circuit builder storing exits
improperly.



Added: torflow/trunk/LICENSE
===================================================================
--- torflow/trunk/LICENSE	2007-01-16 02:35:33 UTC (rev 9362)
+++ torflow/trunk/LICENSE	2007-01-16 08:19:52 UTC (rev 9363)
@@ -0,0 +1,32 @@
+===============================================================================
+The TorFlow code is distributed under this license:
+
+Copyright (c) 2007, Nick Mathewson, Roger Dingledine, Mike Perry
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+    * Neither the names of the copyright owners nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Modified: torflow/trunk/TorCtl.py
===================================================================
--- torflow/trunk/TorCtl.py	2007-01-16 02:35:33 UTC (rev 9362)
+++ torflow/trunk/TorCtl.py	2007-01-16 08:19:52 UTC (rev 9363)
@@ -1,10 +1,10 @@
 #!/usr/bin/python
 # TorCtl.py -- Python module to interface with Tor Control interface.
-# Copyright 2005 Nick Mathewson -- See LICENSE for licensing information.
-#$Id: TorCtl.py 6882 2005-11-19 19:42:31Z nickm $
+# Copyright 2005 Nick Mathewson
+# Copyright 2007 Mike Perry. See LICENSE file.
 
 """
-TorCtl -- Library to control Tor processes.  See TorCtlDemo.py for example use.
+TorCtl -- Library to control Tor processes.
 """
 
 import os
@@ -18,139 +18,10 @@
 import socket
 import binascii
 import types
+from TorUtil import *
 
-# XXX: Make a TorUtil.py
-class _Enum:
-    # Helper: define an ordered dense name-to-number 1-1 mapping.
-    def __init__(self, start, names):
-        self.nameOf = {}
-        idx = start
-        for name in names:
-            setattr(self,name,idx)
-            self.nameOf[idx] = name
-            idx += 1
-
-class _Enum2:
-    # Helper: define an ordered sparse name-to-number 1-1 mapping.
-    def __init__(self, **args):
-        self.__dict__.update(args)
-        self.nameOf = {}
-        for k,v in args.items():
-            self.nameOf[v] = k
-
-def _quote(s):
-    return re.sub(r'([\r\n\\\"])', r'\\\1', s)
-
-def _escape_dots(s, translate_nl=1):
-    if translate_nl:
-        lines = re.split(r"\r?\n", s)
-    else:
-        lines = s.split("\r\n")
-    if lines and not lines[-1]:
-        del lines[-1]
-    for i in xrange(len(lines)):
-        if lines[i].startswith("."):
-            lines[i] = "."+lines[i]
-    lines.append(".\r\n")
-    return "\r\n".join(lines)
-
-def _unescape_dots(s, translate_nl=1):
-    lines = s.split("\r\n")
-
-    for i in xrange(len(lines)):
-        if lines[i].startswith("."):
-            lines[i] = lines[i][1:]
-
-    if lines and lines[-1]:
-        lines.append("")
-
-    if translate_nl:
-        return "\n".join(lines)
-    else:
-        return "\r\n".join(lines)
-
-class _BufSock:
-    def __init__(self, s):
-        self._s = s
-        self._buf = []
-
-    def readline(self):
-        if self._buf:
-            idx = self._buf[0].find('\n')
-            if idx >= 0:
-                result = self._buf[0][:idx+1]
-                self._buf[0] = self._buf[0][idx+1:]
-                return result
-
-        while 1:
-            s = self._s.recv(128)
-            if not s:
-                raise TorCtlClosed()
-            idx = s.find('\n')
-            if idx >= 0:
-                self._buf.append(s[:idx+1])
-                result = "".join(self._buf)
-                rest = s[idx+1:]
-                if rest:
-                    self._buf = [ rest ]
-                else:
-                    del self._buf[:]
-                return result
-            else:
-                self._buf.append(s)
-
-    def write(self, s):
-        self._s.send(s)
-
-    def close(self):
-        self._s.close()
-
-def secret_to_key(secret, s2k_specifier):
-    """Used to generate a hashed password string. DOCDOC."""
-    c = ord(s2k_specifier[8])
-    EXPBIAS = 6
-    count = (16+(c&15)) << ((c>>4) + EXPBIAS)
-
-    d = sha.new()
-    tmp = s2k_specifier[:8]+secret
-    slen = len(tmp)
-    while count:
-        if count > slen:
-            d.update(tmp)
-            count -= slen
-        else:
-            d.update(tmp[:count])
-            count = 0
-    return d.digest()
-
-def urandom_rng(n):
-    """Try to read some entropy from the platform entropy source."""
-    f = open('/dev/urandom', 'rb')
-    try:
-        return f.read(n)
-    finally:
-        f.close()
-
-def s2k_gen(secret, rng=None):
-    """DOCDOC"""
-    if rng is None:
-        if hasattr(os, "urandom"):
-            rng = os.urandom
-        else:
-            rng = urandom_rng
-    spec = "%s%s"%(rng(8), chr(96))
-    return "16:%s"%(
-        binascii.b2a_hex(spec + secret_to_key(secret, spec)))
-
-def s2k_check(secret, k):
-    """DOCDOC"""
-    assert k[:3] == "16:"
-
-    k =  binascii.a2b_hex(k[3:])
-    return secret_to_key(secret, k[:9]) == k[9:]
-
 # Types of "EVENT" message.
-EVENT_TYPE = _Enum2(
+EVENT_TYPE = Enum2(
                     CIRC="CIRC",
                     STREAM="STREAM",
                     ORCONN="ORCONN",
@@ -163,13 +34,6 @@
                     WARN="WARN",
                     ERR="ERR")
 
-loglevel = "DEBUG"
-loglevels = {"DEBUG" : 0, "INFO" : 1, "NOTICE" : 2, "WARN" : 3, "ERROR" : 4}
-
-def plog(level, msg): # XXX: Timestamps
-    if(loglevels[level] >= loglevels[loglevel]):
-        print level + ": " + msg
-
 class TorCtlError(Exception):
     "Generic error raised by TorControl code."
     pass
@@ -197,7 +61,7 @@
             self.ip = 0
             self.netmask = 0
         else:
-            if ip_mask.find("/") == -1:
+            if not "/" in ip_mask:
                 self.netmask = 0xFFFFFFFF
                 ip = ip_mask
             else:
@@ -241,7 +105,6 @@
         for line in self.exitpolicy:
             ret = line.check(ip, port)
             if ret != -1:
-                if ret: plog("DEBUG", "Match: "+str(ret)+" for "+self.name)
                 return ret
         plog("NOTICE", "No matching exit line for "+self.name)
         return 0
@@ -253,9 +116,12 @@
         self.path = [] # routers
         self.exit = 0
 
-
-class _ConnectionBase:
-    def __init__(self):
+class Connection:
+    """A Connection represents a connection to the Tor process."""
+    def __init__(self, sock):
+        """Create a Connection to communicate with the Tor process over the
+           socket 'sock'.
+        """
         self._s = None
         self._handler = None
         self._handleFn = None
@@ -267,12 +133,9 @@
         self._closeHandler = None
         self._eventThread = None
         self._eventQueue = Queue.Queue()
+        self._s = BufSock(sock)
+        self._debugFile = None
 
-    def set_event_handler(self, handler):
-        """Cause future events from the Tor process to be sent to 'handler'.
-        """
-        raise NotImplemented
-
     def set_close_handler(self, handler):
         """Call 'handler' when the Tor process has closed its connection or
            given us an exception.  If we close normally, no arguments are
@@ -293,10 +156,6 @@
         finally:
             self._sendLock.release()
 
-#    def _read_reply(self):
-#        """DOCDOC"""
-#        raise NotImplementd
-
     def launch_thread(self, daemon=1):
         """Launch a background thread to handle messages from the Tor process."""
         assert self._thread is None
@@ -416,16 +275,6 @@
         return reply
 
 
-class Connection(_ConnectionBase):
-    """A Connection represents a connection to the Tor process."""
-    def __init__(self, sock):
-        """Create a Connection to communicate with the Tor process over the
-           socket 'sock'.
-        """
-        _ConnectionBase.__init__(self)
-        self._s = _BufSock(sock)
-        self._debugFile = None
-
     def debug(self, f):
         """DOCDOC"""
         self._debugFile = f
@@ -464,7 +313,7 @@
                     if line in (".\r\n", ".\n"):
                         break
                     more.append(line)
-                lines.append((code, s, _unescape_dots("".join(more))))
+                lines.append((code, s, unescape_dots("".join(more))))
         isEvent = (lines and lines[0][0][0] == '6')
         return (isEvent, lines)
 
@@ -535,7 +384,7 @@
         """
         if not kvlist:
             return
-        msg = " ".join(["%s=%s"%(k,_quote(v)) for k,v in kvlist])
+        msg = " ".join(["%s=%s"%(k,quote(v)) for k,v in kvlist])
         self._sendAndRecv("SETCONF %s\r\n"%msg)
 
     def reset_options(self, keylist):
@@ -609,7 +458,7 @@
            Recognized event names are listed in section 3.3 of the control-spec
         """
         if extended:
-            print ("SETEVENTS EXTENDED %s\r\n" % " ".join(events))
+            plog ("DEBUG", "SETEVENTS EXTENDED %s\r\n" % " ".join(events))
             self._sendAndRecv("SETEVENTS EXTENDED %s\r\n" % " ".join(events))
         else:
             self._sendAndRecv("SETEVENTS %s\r\n" % " ".join(events))
@@ -662,17 +511,17 @@
         circ = Circuit()
         if pathlen == 1:
             circ.exit = exit_chooser(circ.path)
-            circ.path = [circ.exit]
+            circ.path = [circ.exit.idhex]
             circ.cid = self.extend_circuit(0, circ.path)
         else:
-            circ.path.append(entry_chooser(circ.path))
+            circ.path.append(entry_chooser(circ.path).idhex)
             for i in xrange(1, pathlen-1):
-                circ.path.append(middle_chooser(circ.path))
-            circ.path.append(exit_chooser(circ.path))
+                circ.path.append(middle_chooser(circ.path).idhex)
+            circ.exit = exit_chooser(circ.path)
+            circ.path.append(circ.exit.idhex)
             circ.cid = self.extend_circuit(0, circ.path)
         circ.created_at = datetime.datetime.now()
         return circ
- 
 
     def redirect_stream(self, streamid, newaddr, newport=""):
         """DOCDOC"""
@@ -696,7 +545,7 @@
                           %(circid, reason, "".join(flags)))
 
     def post_descriptor(self, desc):
-        self._sendAndRecv("+POSTDESCRIPTOR\r\n%s"%_escape_dots(desc))
+        self._sendAndRecv("+POSTDESCRIPTOR\r\n%s"%escape_dots(desc))
 
 def parse_ns_body(data):
     "Parse the body of an NS event or command."
@@ -756,7 +605,7 @@
             ident,status,path,reason,remote = m.groups()
             ident = int(ident)
             if path:
-                if path.find("REASON=") != -1:
+                if "REASON=" in path:
                     remote = reason
                     reason = path
                     path=[]
@@ -777,11 +626,17 @@
             if remote: remote = remote[15:]
             args = ident, status, circ, target_host, int(target_port), reason, remote
         elif evtype == "ORCONN":
-            m = re.match(r"(\S+)\s+(\S+)", body)
+            m = re.match(r"(\S+)\s+(\S+)(\s\S+)?(\s\S+)?", body)
             if not m:
                 raise ProtocolError("ORCONN event misformatted.")
-            target, status = m.groups()
-            args = status, target
+            target, status, reason, ncircs = m.groups()
+            if reason and not ncircs:
+                if "NCIRCS=" in reason:
+                    ncircs = reason
+                    reason = None
+            if ncircs: ncircs = int(ncircs[7:])
+            if reason: reason = reason[8:]
+            args = status, target, reason, ncircs
         elif evtype == "BW":
             m = re.match(r"(\d+)\s+(\d+)", body)
             if not m:
@@ -831,7 +686,7 @@
         """
         raise NotImplemented
 
-    def or_conn_status(self, eventtype, status, target):
+    def or_conn_status(self, eventtype, status, target, reason, ncircs):
         """Called when an OR connection's status changes if listening to
            ORCONNSTATUS events. 'status' is a member of OR_CONN_STATUS; target
            is the OR in question.
@@ -889,6 +744,13 @@
 
     def new_desc(self, eventtype, identities):
         print " ".join((eventtype, " ".join(identities)))
+   
+    def or_conn_status(self, eventtype, status, target, reason, ncircs):
+        if reason: reason = "REASON="+reason
+        else: reason = ""
+        if ncircs: ncircs = "NCIRCS="+str(ncircs)
+        else: ncircs = ""
+        print " ".join((eventtype, target, status, reason, ncircs))
 
 def parseHostAndPort(h):
     """Given a string of the form 'address:port' or 'address' or
@@ -948,8 +810,10 @@
     #set_option(s,"bandwidthburstbytes 100000")
     #set_option(s,"runasdaemon 1")
     #set_events(s,[EVENT_TYPE.WARN])
+#    c.set_events([EVENT_TYPE.ORCONN], True)
     c.set_events([EVENT_TYPE.STREAM, EVENT_TYPE.CIRC,
-                  EVENT_TYPE.NS, EVENT_TYPE.NEWDESC], True)
+                  EVENT_TYPE.NS, EVENT_TYPE.NEWDESC,
+                  EVENT_TYPE.ORCONN], True)
 
     th.join()
     return

Added: torflow/trunk/TorUtil.py
===================================================================
--- torflow/trunk/TorUtil.py	2007-01-16 02:35:33 UTC (rev 9362)
+++ torflow/trunk/TorUtil.py	2007-01-16 08:19:52 UTC (rev 9363)
@@ -0,0 +1,163 @@
+#!/usr/bin/python
+# TorCtl.py -- Python module to interface with Tor Control interface.
+# Copyright 2007 Mike Perry -- See LICENSE for licensing information.
+# Portions Copyright 2005 Nick Matthewson
+
+"""
+TorUtil -- Support functions for TorCtl.py and metatroller
+"""
+
+import os
+import re
+import struct
+import sys
+import threading
+import Queue
+import datetime
+import traceback
+import socket
+import binascii
+import types
+
+__all__ = ["Enum", "Enum2", "quote", "escape_dots", "unescape_dots",
+            "BufSock", "secret_to_key", "urandom_rng", "s2k_gen", "s2k_check",
+            "plog"]
+
+class Enum:
+    # Helper: define an ordered dense name-to-number 1-1 mapping.
+    def __init__(self, start, names):
+        self.nameOf = {}
+        idx = start
+        for name in names:
+            setattr(self,name,idx)
+            self.nameOf[idx] = name
+            idx += 1
+
+class Enum2:
+    # Helper: define an ordered sparse name-to-number 1-1 mapping.
+    def __init__(self, **args):
+        self.__dict__.update(args)
+        self.nameOf = {}
+        for k,v in args.items():
+            self.nameOf[v] = k
+
+def quote(s):
+    return re.sub(r'([\r\n\\\"])', r'\\\1', s)
+
+def escape_dots(s, translate_nl=1):
+    if translate_nl:
+        lines = re.split(r"\r?\n", s)
+    else:
+        lines = s.split("\r\n")
+    if lines and not lines[-1]:
+        del lines[-1]
+    for i in xrange(len(lines)):
+        if lines[i].startswith("."):
+            lines[i] = "."+lines[i]
+    lines.append(".\r\n")
+    return "\r\n".join(lines)
+
+def unescape_dots(s, translate_nl=1):
+    lines = s.split("\r\n")
+
+    for i in xrange(len(lines)):
+        if lines[i].startswith("."):
+            lines[i] = lines[i][1:]
+
+    if lines and lines[-1]:
+        lines.append("")
+
+    if translate_nl:
+        return "\n".join(lines)
+    else:
+        return "\r\n".join(lines)
+
+class BufSock:
+    def __init__(self, s):
+        self._s = s
+        self._buf = []
+
+    def readline(self):
+        if self._buf:
+            idx = self._buf[0].find('\n')
+            if idx >= 0:
+                result = self._buf[0][:idx+1]
+                self._buf[0] = self._buf[0][idx+1:]
+                return result
+
+        while 1:
+            s = self._s.recv(128)
+            if not s:
+                raise TorCtlClosed()
+            idx = s.find('\n')
+            if idx >= 0:
+                self._buf.append(s[:idx+1])
+                result = "".join(self._buf)
+                rest = s[idx+1:]
+                if rest:
+                    self._buf = [ rest ]
+                else:
+                    del self._buf[:]
+                return result
+            else:
+                self._buf.append(s)
+
+    def write(self, s):
+        self._s.send(s)
+
+    def close(self):
+        self._s.close()
+
+def secret_to_key(secret, s2k_specifier):
+    """Used to generate a hashed password string. DOCDOC."""
+    c = ord(s2k_specifier[8])
+    EXPBIAS = 6
+    count = (16+(c&15)) << ((c>>4) + EXPBIAS)
+
+    d = sha.new()
+    tmp = s2k_specifier[:8]+secret
+    slen = len(tmp)
+    while count:
+        if count > slen:
+            d.update(tmp)
+            count -= slen
+        else:
+            d.update(tmp[:count])
+            count = 0
+    return d.digest()
+
+def urandom_rng(n):
+    """Try to read some entropy from the platform entropy source."""
+    f = open('/dev/urandom', 'rb')
+    try:
+        return f.read(n)
+    finally:
+        f.close()
+
+def s2k_gen(secret, rng=None):
+    """DOCDOC"""
+    if rng is None:
+        if hasattr(os, "urandom"):
+            rng = os.urandom
+        else:
+            rng = urandom_rng
+    spec = "%s%s"%(rng(8), chr(96))
+    return "16:%s"%(
+        binascii.b2a_hex(spec + secret_to_key(secret, spec)))
+
+def s2k_check(secret, k):
+    """DOCDOC"""
+    assert k[:3] == "16:"
+
+    k =  binascii.a2b_hex(k[3:])
+    return secret_to_key(secret, k[:9]) == k[9:]
+
+
+## XXX: Make this a class?
+loglevel = "DEBUG"
+loglevels = {"DEBUG" : 0, "INFO" : 1, "NOTICE" : 2, "WARN" : 3, "ERROR" : 4}
+
+def plog(level, msg): # XXX: Timestamps
+    if(loglevels[level] >= loglevels[loglevel]):
+        print level + ": " + msg
+

Modified: torflow/trunk/metatroller.py
===================================================================
--- torflow/trunk/metatroller.py	2007-01-16 02:35:33 UTC (rev 9362)
+++ torflow/trunk/metatroller.py	2007-01-16 08:19:52 UTC (rev 9363)
@@ -1,12 +1,11 @@
 #!/usr/bin/python
+# Metatroller. 
 
 """
 Metatroller - Tor Meta controller
 """
 
-# Metatroller.
 
-
 import TorCtl
 import atexit
 import sys
@@ -15,7 +14,7 @@
 import traceback
 import re
 import random
-from TorCtl import plog
+from TorUtil import *
 
 routers = {} # indexed by idhex
 name_to_key = {}
@@ -67,25 +66,23 @@
     r = random.choice(sorted_g)
     while r.idhex in path:
         r = random.choice(sorted_g)
-    return r.idhex
+    return r
 
 def choose_middle_uniform(path):
     r = random.choice(sorted_r)
     while r.idhex in path:
         r = random.choice(sorted_r)
-    return r.idhex
+    return r
 
 def choose_exit_uniform(path, target_ip, target_port):
     allowed = []
-    print target_ip, target_port
     for r in sorted_r:
         if r.will_exit_to(target_ip, target_port):
-            plog("DEBUG", r.name + " will exit")
             allowed.append(r)
     r = random.choice(allowed)
     while r.idhex in path:
         r = random.choice(allowed)
-    return r.idhex
+    return r
  
 def read_routers(c, nslist):
     bad_key = 0
@@ -108,7 +105,6 @@
                      +ns.idhex+" has no descriptor")
             pass
         except:
-            #print sys.exc_info()[1]
             traceback.print_exception(*sys.exc_info())
             continue
     sorted_r.sort(cmp, lambda sr: sr.bw)
@@ -121,8 +117,6 @@
                 total_g_bw += r.bw
                 sorted_g.append(r)
 
-    plog("DEBUG", str(bad_key) + " bad keys")
-
 # Make eventhandler
 class SnakeHandler(TorCtl.EventHandler):
     def __init__(self, c):