[or-cvs] r11265: Implemented OceanPhobicRestriction based on partitioning the (in torflow/trunk: . TorCtl)

renner at seul.org renner at seul.org
Fri Aug 24 09:33:48 UTC 2007


Author: renner
Date: 2007-08-24 05:33:48 -0400 (Fri, 24 Aug 2007)
New Revision: 11265

Modified:
   torflow/trunk/TorCtl/GeoIPSupport.py
   torflow/trunk/TorCtl/PathSupport.py
   torflow/trunk/TorCtl/TorCtl.py
   torflow/trunk/op-addon.py
   torflow/trunk/pathrc.example
Log:

  Implemented OceanPhobicRestriction based on partitioning 
  the continents in groups, and EchelonPhobicRestrictor 
  based on entry_country and set_target().



Modified: torflow/trunk/TorCtl/GeoIPSupport.py
===================================================================
--- torflow/trunk/TorCtl/GeoIPSupport.py	2007-08-24 08:12:25 UTC (rev 11264)
+++ torflow/trunk/TorCtl/GeoIPSupport.py	2007-08-24 09:33:48 UTC (rev 11265)
@@ -11,17 +11,20 @@
 geoip = GeoIP.new(GeoIP.GEOIP_STANDARD)
 #geoip = GeoIP.open("./GeoLiteCity.dat", GeoIP.GEOIP_STANDARD)
 
-# Continent class
 class Continent:
+  """ Continent class: The group attribute is to partition the continents
+      in groups, to determine the number of ocean crossings """
   def __init__(self, continent_code):
     self.code = continent_code 
+    self.group = None
     self.countries = []
 
   def contains(self, country_code):
     return country_code in self.countries
 
-# Setup the continents
+# Set countries to continents
 africa = Continent("AF")
+africa.group = 1
 africa.countries = ["AO","BF","BI","BJ","BV","BW","CD","CF","CG","CI","CM",
    "CV","DJ","DZ","EG","EH","ER","ET","GA","GH","GM","GN","GQ","GW","HM","KE",
    "KM","LR","LS","LY","MA","MG","ML","MR","MU","MW","MZ","NA","NE","NG","RE",
@@ -29,25 +32,30 @@
    "YT","ZA","ZM","ZR","ZW"]
 
 asia = Continent("AS")
+asia.group = 1
 asia.countries = ["AP","AE","AF","AM","AZ","BD","BH","BN","BT","CC","CN","CX",
    "CY","GE","HK","ID","IL","IN","IO","IQ","IR","JO","JP","KG","KH","KP","KR",
    "KW","KZ","LA","LB","LK","MM","MN","MO","MV","MY","NP","OM","PH","PK","PS",
    "QA","RU","SA","SG","SY","TH","TJ","TM","TP","TR","TW","UZ","VN","YE"]
 
 europe = Continent("EU")
+europe.group = 1
 europe.countries = ["EU","AD","AL","AT","BA","BE","BG","BY","CH","CZ","DE",
    "DK","EE","ES","FI","FO","FR","FX","GB","GI","GR","HR","HU","IE","IS","IT",
    "LI","LT","LU","LV","MC","MD","MK","MT","NL","NO","PL","PT","RO","SE","SI",
    "SJ","SK","SM","UA","VA","YU"]
 
 oceania = Continent("OC")
+oceania.group = 2
 oceania.countries = ["AS","AU","CK","FJ","FM","GU","KI","MH","MP","NC","NF",
    "NR","NU","NZ","PF","PG","PN","PW","SB","TK","TO","TV","UM","VU","WF","WS"]
 
 north_america = Continent("NA")
+north_america.group = 0
 north_america.countries = ["CA","MX","US"]
 
 south_america = Continent("SA")
+south_america.group = 0
 south_america.countries = ["AG","AI","AN","AR","AW","BB","BM","BO","BR","BS",
    "BZ","CL","CO","CR","CU","DM","DO","EC","FK","GD","GF","GL","GP","GS","GT",
    "GY","HN","HT","JM","KN","KY","LC","MQ","MS","NI","PA","PE","PM","PR","PY",
@@ -60,7 +68,7 @@
   """ Perform country -- continent mapping """
   for c in continents:
     if c.contains(country_code):
-      return c.code
+      return c
   plog("INFO", country_code + " is not on any continent")
   return None
 
@@ -79,34 +87,46 @@
   def __init__(self, router):
     self.__dict__ = router.__dict__
     self.country_code = get_country(self.get_ip_dotted())
-    if self.country_code == None: 
+    if self.country_code != None: 
+      c = get_continent(self.country_code)
+      if c != None:
+        self.continent = c.code
+        self.cont_group = c.group
+    else: 
       plog("INFO", self.nickname + ": Country code not found")
       self.continent = None
-    else: self.continent = get_continent(self.country_code)
-
+   
   def get_ip_dotted(self):
     """ Convert long int back to dotted quad string """
     return socket.inet_ntoa(struct.pack('>I', self.ip))
 
 class GeoIPConfig:
   """ Class to configure GeoIP-based path building """		    
-  def __init__(self, unique_countries, max_crossings, entry_country, 
-     middle_country, exit_country, excludes): 
-    # TODO: Somehow ensure validity of a configuration
-    
+  def __init__(self, unique_countries, continent_crossings, ocean_crossings,
+     entry_country, middle_country, exit_country, excludes): 
+    # TODO: Somehow ensure validity of a configuration:
+    #   - continent_crossings >= ocean_crossings
+    #   - unique_countries=False --> continent_crossings!=None
+    #   - echelon? set entry_country to source and exit_country to None
+
     # Do not use a country twice in a route 
     # [True --> unique, False --> same or None --> pass] 
     self.unique_countries = unique_countries
     
     # Configure max continent crossings in one path 
     # [integer number 0-n or None --> ContinentJumper/UniqueContinent]
-    self.max_crossings = max_crossings
+    self.continent_crossings = continent_crossings
+    self.ocean_crossings = ocean_crossings
+ 
+    # Try to find an exit node in the destination country
+    # use exit_country as backup, if country cannot not be found
+    self.echelon = False
 
     # Specify countries for positions [single country code or None]
     self.entry_country = entry_country
     self.middle_country = middle_country
     self.exit_country = exit_country
-        
+
     # List of countries not to use in routes 
     # [(empty) list of country codes or None]
     self.excludes = excludes

Modified: torflow/trunk/TorCtl/PathSupport.py
===================================================================
--- torflow/trunk/TorCtl/PathSupport.py	2007-08-24 08:12:25 UTC (rev 11264)
+++ torflow/trunk/TorCtl/PathSupport.py	2007-08-24 09:33:48 UTC (rev 11265)
@@ -322,7 +322,8 @@
 
 class ContinentRestriction(PathRestriction):
   """ Do not more than n continent crossings """
-  def __init__(self, n):
+  # TODO: Add src and dest
+  def __init__(self, n, src=None, dest=None):
     self.n = n
 
   def path_is_ok(self, path):
@@ -342,7 +343,6 @@
   """ Ensure continent crossings between all hops """
   def path_is_ok(self, path):
     prev = None
-    # Compute crossings until now
     for r in path:
       # Jump over the first router
       if prev:
@@ -360,6 +360,25 @@
           return False;
     return True;
 
+class OceanPhobicRestriction(PathRestriction):
+  """ Not more than n ocean crossings """
+  # TODO: Add src and dest
+  def __init__(self, n, src=None, dest=None):
+    self.n = n
+
+  def path_is_ok(self, path):
+    crossings = 0
+    prev = None
+    # Compute ocean crossings until now
+    for r in path:
+      # Jump over the first router
+      if prev:
+        if r.cont_group != prev.cont_group:
+          crossings += 1
+      prev = r
+    if crossings > self.n: return False
+    else: return True
+
 #################### Node Generators ######################
 
 class UniformGenerator(NodeGenerator):
@@ -616,11 +635,14 @@
           # False: use the same country for all nodes in a path
           self.path_rstr.add_restriction(SingleCountryRestriction())
       
-      # Specify max number of crossings here, None means UniqueContinents
-      if self.geoip_config.max_crossings == None:
+      # Specify max number of continent crossings, None means UniqueContinents
+      if self.geoip_config.continent_crossings == None:
         self.path_rstr.add_restriction(UniqueContinentRestriction())
-      else: self.path_rstr.add_restriction(ContinentRestriction(self.geoip_config.max_crossings))
-    
+      else: self.path_rstr.add_restriction(ContinentRestriction(self.geoip_config.continent_crossings))
+      # Should even work in combination with continent crossings
+      if self.geoip_config.ocean_crossings != None:
+        self.path_rstr.add_restriction(OceanPhobicRestriction(self.geoip_config.ocean_crossings))
+
     # This is kind of hokey..
     if self.order_exits:
       if self.__ordered_exit_gen:
@@ -655,6 +677,21 @@
     self.exit_rstr.del_restriction(ExitPolicyRestriction)
     self.exit_rstr.add_restriction(ExitPolicyRestriction(ip, port))
     if self.__ordered_exit_gen: self.__ordered_exit_gen.set_port(port)
+    # Try to choose an exit node in the destination country
+    # needs an IP != 255.255.255.255
+    if self.geoip_config and self.geoip_config.echelon:
+      import GeoIPSupport
+      c = GeoIPSupport.get_country(ip)
+      if c:
+        plog("INFO", "[Echelon] IP "+ip+" is in ["+c+"]")
+        self.exit_rstr.del_restriction(CountryRestriction)
+        self.exit_rstr.add_restriction(CountryRestriction(c))
+      else: 
+        plog("INFO", "[Echelon] Could not determine destination country of IP "+ip)
+        # Try to use a backup country
+        if self.geoip_config.exit_country:
+          self.exit_rstr.del_restriction(CountryRestriction) 
+          self.exit_rstr.add_restriction(CountryRestriction(self.geoip_config.exit_country))
 
 class Circuit:
   def __init__(self):
@@ -1257,7 +1294,20 @@
              " to " + s.target_host)		   
         self.streams[s.strm_id].host = s.target_host
         self.streams[s.strm_id].port = s.target_port
+  
+  def address_mapped_event(self, event):
+    """ It is necessary to listen to ADDRMAP events to be able to 
+        perform DNS lookups using Tor """
+    output = [event.event_name, event.from_addr, event.to_addr, 
+       time.asctime(event.when)]
+    plog("DEBUG", " ".join(output))
 
+  def unknown_event(self, event):
+    # XXX: Sometimes a strange event (or parsing error) is occuring 
+    # (event_name ='OK'?)
+    plog("DEBUG", "UNKNOWN EVENT '" + event.event_name + "':" + 
+       event.event_string)
+
 ########################## Unit tests ##########################
 
 def do_unit(rst, r_list, plamb):

Modified: torflow/trunk/TorCtl/TorCtl.py
===================================================================
--- torflow/trunk/TorCtl/TorCtl.py	2007-08-24 08:12:25 UTC (rev 11264)
+++ torflow/trunk/TorCtl/TorCtl.py	2007-08-24 09:33:48 UTC (rev 11265)
@@ -32,6 +32,7 @@
           BW="BW",
           NS="NS",
           NEWDESC="NEWDESC",
+          ADDRMAP="ADDRMAP",
           DEBUG="DEBUG",
           INFO="INFO",
           NOTICE="NOTICE",
@@ -631,6 +632,13 @@
         0x0F : "TERM" }.get(sig,sig)
     self.sendAndRecv("SIGNAL %s\r\n"%sig)
 
+  def resolve(self, host):
+    """ Launch a remote hostname lookup request:
+        'host' may be a hostname or IPv4 address
+    """
+    # TODO: handle "mode=reverse"
+    self.sendAndRecv("RESOLVE %s\r\n"%host)
+
   def map_address(self, kvList):
     if not kvList:
       return
@@ -804,15 +812,15 @@
     elif evtype == "NEWDESC":
       event = NewDescEvent(evtype, body.split(" "))
     elif evtype == "ADDRMAP":
+      # TODO: Also parse errors and GMTExpiry
       m = re.match(r'(\S+)\s+(\S+)\s+(\"[^"]+\"|\w+)', body)
       if not m:
-        raise ProtocolError("BANDWIDTH event misformatted.")
+        raise ProtocolError("ADDRMAP event misformatted.")
       fromaddr, toaddr, when = m.groups()
-      if when.upper() == "NEVER":
+      if when.upper() == "NEVER":  
         when = None
       else:
-        when = time.localtime(
-          time.strptime(when[1:-1], "%Y-%m-%d %H:%M:%S"))
+        when = time.strptime(when[1:-1], "%Y-%m-%d %H:%M:%S")
       event = AddrMapEvent(evtype, fromaddr, toaddr, when)
     elif evtype == "NS":
       event = NetworkStatusEvent(evtype, parse_ns_body(data))

Modified: torflow/trunk/op-addon.py
===================================================================
--- torflow/trunk/op-addon.py	2007-08-24 08:12:25 UTC (rev 11264)
+++ torflow/trunk/op-addon.py	2007-08-24 09:33:48 UTC (rev 11265)
@@ -102,14 +102,18 @@
   if config.getboolean(GEOIP, "use_geoip"):
     # Optional options
     unique_countries = None
-    max_crossings = None
+    continent_crossings = None
+    ocean_crossings = None
     if config.has_option(GEOIP, "unique_countries"):
       unique_countries = config.getboolean(GEOIP, "unique_countries")
-    if config.has_option(GEOIP, "max_crossings"):
-      max_crossings = config.getint(GEOIP, "max_crossings")
+    if config.has_option(GEOIP, "continent_crossings"):
+      continent_crossings = config.getint(GEOIP, "continent_crossings")
+    if config.has_option(GEOIP,"ocean_crossings"):
+      ocean_crossings = config.getint(GEOIP, "ocean_crossings")
     path_config = GeoIPSupport.GeoIPConfig(
        unique_countries,
-       max_crossings,
+       continent_crossings,
+       ocean_crossings,
        entry_country = config.get(GEOIP, "entry_country"),
        middle_country = config.get(GEOIP, "middle_country"),
        exit_country = config.get(GEOIP, "exit_country"),
@@ -219,7 +223,7 @@
     s += " s, avg=" + str(self.mean) + " s" 
     s += ", dev=" + str(self.dev) + " s (min=" + str(self.min)
     s += " s, max=" + str(self.max) + " s)\n"
-    s += "Failures during circuit-buildups: " + str(self.failures_buildup) + "\n"
+    s += "Failures during circuit buildups: " + str(self.failures_buildup) + "\n"
     s += "Failures on established circuits: " + str(self.failures_established)
     return s
 
@@ -658,6 +662,13 @@
     """ Separate pings from regular streams directly """
     if not (s.target_host == ping_dummy_host and 
        s.target_port == ping_dummy_port):
+
+      # TODO: Handle echelon here?
+      # - perform DNS request (or use REMAP?)
+      # - determine destination country
+      # - check if there is already a circuit with exit node
+      #   in destination country
+      
       # This is no ping, call the other method
       return PathSupport.StreamHandler.stream_status_event(self, s)
     
@@ -924,11 +935,6 @@
     if len(routers) == len(keys):
       return routers
 
-  def unknown_event(self, event):
-    # XXX: Sometimes a strange event is occuring
-    plog("DEBUG", "UNKNOWN EVENT '" + event.event_name + "':" + 
-       event.event_string)
-
 ## Pinger #####################################################################
 
 class Pinger(threading.Thread):
@@ -992,6 +998,7 @@
   """ Set events and options """
   conn.set_events([TorCtl.EVENT_TYPE.STREAM,
       TorCtl.EVENT_TYPE.CIRC,
+      TorCtl.EVENT_TYPE.ADDRMAP,
       TorCtl.EVENT_TYPE.NS,	  
       TorCtl.EVENT_TYPE.NEWDESC], True)
   # Set options: We attach streams now & build circuits

Modified: torflow/trunk/pathrc.example
===================================================================
--- torflow/trunk/pathrc.example	2007-08-24 08:12:25 UTC (rev 11264)
+++ torflow/trunk/pathrc.example	2007-08-24 09:33:48 UTC (rev 11265)
@@ -43,21 +43,29 @@
 use_geoip = yes
 
 # yes|no for unique|distinct countries,
-# comment out means don't care
-unique_countries = yes
+# ! comment to don't care
+#unique_countries = yes
 
+# If echelon is set, OP-Addon will try to find an 
+# exit in the destination country of the current 
+# request (exit_country may be used as backup)
+# yes|no
+# TODO: echelon = yes
+
 # Set country codes for single positions
 #entry_country = DE
 #middle_country = RU
 #exit_country = US
 
-# Maximum number of continent-crossings 
-# in generated paths: 0-n or comment out
-# to enforce distinct continents
-max_crossings = 1
+# Maximum number of continent-crossings: 0-n
+# ! comment out to enforce distinct continents
+# ! set >= pathlen to not care about
+continent_crossings = 2
+# Maximum number of ocean crossings: 0-n
+# ! comment out to don't care
+ocean_crossings = 0
 
-# TODO: excludes = ["FR"]
-# TODO: echelon = yes|no
+# TODO: excludes = [".."]
 
 [TESTING]
 



More information about the tor-commits mailing list