[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