File: GeoIPSupport.py

package info (click to toggle)
python-torctl 20130920git-2
  • links: PTS
  • area: main
  • in suites: buster, jessie, jessie-kfreebsd, stretch
  • size: 396 kB
  • ctags: 814
  • sloc: python: 5,026; makefile: 36
file content (141 lines) | stat: -rw-r--r-- 5,190 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#!/usr/bin/python
# Copyright 2007 Johannes Renner and Mike Perry. See LICENSE file.

import struct
import socket
import TorCtl
import StatsSupport

from TorUtil import plog
try:
  import GeoIP
  # GeoIP data object: choose database here
  geoip = GeoIP.new(GeoIP.GEOIP_STANDARD)
  #geoip = GeoIP.open("./GeoLiteCity.dat", GeoIP.GEOIP_STANDARD)
except:
  plog("NOTICE", "No GeoIP library. GeoIPSupport.py will not work correctly")
  # XXX: How do we bail entirely..  


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

# 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",
   "RW","SC","SD","SH","SL","SN","SO","ST","SZ","TD","TF","TG","TN","TZ","UG",
   "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",
   "SA","SR","SV","TC","TT","UY","VC","VE","VG","VI"]

# List of continents
continents = [africa, asia, europe, north_america, oceania, south_america]

def get_continent(country_code):
  """ Perform country -- continent mapping """
  for c in continents:
    if c.contains(country_code):
      return c
  plog("INFO", country_code + " is not on any continent")
  return None

def get_country(ip):
  """ Get the country via the library """
  return geoip.country_code_by_addr(ip)

def get_country_from_record(ip):
  """ Get the country code out of a GeoLiteCity record (not used) """
  record = geoip.record_by_addr(ip)
  if record != None:
    return record['country_code']

class GeoIPRouter(TorCtl.Router):
  # TODO: Its really shitty that this has to be a TorCtl.Router
  # and can't be a StatsRouter..
  """ Router class extended to GeoIP """
  def __init__(self, router):
    self.__dict__ = router.__dict__
    self.country_code = get_country(self.get_ip_dotted())
    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
   
  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=None, continent_crossings=4,
     ocean_crossings=None, entry_country=None, middle_country=None,
     exit_country=None, excludes=None):
    # 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.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