exitlist 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. #!/usr/bin/python
  2. # Copyright 2005-2006 Nick Mathewson
  3. # See the LICENSE file in the Tor distribution for licensing information.
  4. # Requires Python 2.2 or later.
  5. """
  6. exitlist -- Given a Tor directory on stdin, lists the Tor servers
  7. that accept connections to given addreses.
  8. example usage (Tor 0.1.0.x and earlier):
  9. python exitlist < ~/.tor/cached-directory
  10. example usage (Tor 0.1.1.10-alpha and later):
  11. cat ~/.tor/cached-routers* | python exitlist
  12. If you're using Tor 0.1.1.18-rc or later, you should look at
  13. the "FetchUselessDescriptors" config option in the man page.
  14. Note that this script won't give you a perfect list of IP addresses
  15. that might connect to you using Tor, since some Tor servers might exit
  16. from other addresses than the one they publish.
  17. """
  18. #
  19. # Change this to True if you want more verbose output. By default, we
  20. # only print the IPs of the servers that accept any the listed
  21. # addresses, one per line.
  22. #
  23. VERBOSE = False
  24. #
  25. # Change this to True if you want to reverse the output, and list the
  26. # servers that accept *none* of the listed addresses.
  27. #
  28. INVERSE = False
  29. #
  30. # Change this list to contain all of the target services you are interested
  31. # in. It must contain one entry per line, each consisting of an IPv4 address,
  32. # a colon, and a port number.
  33. #
  34. ADDRESSES_OF_INTEREST = """
  35. 192.168.0.1:80
  36. """
  37. #
  38. # YOU DO NOT NEED TO EDIT AFTER THIS POINT.
  39. #
  40. import sys
  41. import re
  42. import getopt
  43. import socket
  44. import struct
  45. assert sys.version_info >= (2,2)
  46. def maskIP(ip,mask):
  47. return "".join([chr(ord(a) & ord(b)) for a,b in zip(ip,mask)])
  48. def maskFromLong(lng):
  49. return struct.pack("!L", lng)
  50. def maskByBits(n):
  51. return maskFromLong(0xffffffffl ^ ((1L<<(32-n))-1))
  52. class Pattern:
  53. """
  54. >>> import socket
  55. >>> ip1 = socket.inet_aton("192.169.64.11")
  56. >>> ip2 = socket.inet_aton("192.168.64.11")
  57. >>> ip3 = socket.inet_aton("18.244.0.188")
  58. >>> print Pattern.parse("18.244.0.188")
  59. 18.244.0.188/255.255.255.255:1-65535
  60. >>> print Pattern.parse("18.244.0.188/16:*")
  61. 18.244.0.0/255.255.0.0:1-65535
  62. >>> print Pattern.parse("18.244.0.188/2.2.2.2:80")
  63. 2.0.0.0/2.2.2.2:80-80
  64. >>> print Pattern.parse("192.168.0.1/255.255.00.0:22-25")
  65. 192.168.0.0/255.255.0.0:22-25
  66. >>> p1 = Pattern.parse("192.168.0.1/255.255.00.0:22-25")
  67. >>> import socket
  68. >>> p1.appliesTo(ip1, 22)
  69. False
  70. >>> p1.appliesTo(ip2, 22)
  71. True
  72. >>> p1.appliesTo(ip2, 25)
  73. True
  74. >>> p1.appliesTo(ip2, 26)
  75. False
  76. """
  77. def __init__(self, ip, mask, portMin, portMax):
  78. self.ip = maskIP(ip,mask)
  79. self.mask = mask
  80. self.portMin = portMin
  81. self.portMax = portMax
  82. def __str__(self):
  83. return "%s/%s:%s-%s"%(socket.inet_ntoa(self.ip),
  84. socket.inet_ntoa(self.mask),
  85. self.portMin,
  86. self.portMax)
  87. def parse(s):
  88. if ":" in s:
  89. addrspec, portspec = s.split(":",1)
  90. else:
  91. addrspec, portspec = s, "*"
  92. if addrspec == '*':
  93. ip,mask = "\x00\x00\x00\x00","\x00\x00\x00\x00"
  94. elif '/' not in addrspec:
  95. ip = socket.inet_aton(addrspec)
  96. mask = "\xff\xff\xff\xff"
  97. else:
  98. ip,mask = addrspec.split("/",1)
  99. ip = socket.inet_aton(ip)
  100. if "." in mask:
  101. mask = socket.inet_aton(mask)
  102. else:
  103. mask = maskByBits(int(mask))
  104. if portspec == '*':
  105. portMin = 1
  106. portMax = 65535
  107. elif '-' not in portspec:
  108. portMin = portMax = int(portspec)
  109. else:
  110. portMin, portMax = map(int,portspec.split("-",1))
  111. return Pattern(ip,mask,portMin,portMax)
  112. parse = staticmethod(parse)
  113. def appliesTo(self, ip, port):
  114. return ((maskIP(ip,self.mask) == self.ip) and
  115. (self.portMin <= port <= self.portMax))
  116. class Policy:
  117. """
  118. >>> import socket
  119. >>> ip1 = socket.inet_aton("192.169.64.11")
  120. >>> ip2 = socket.inet_aton("192.168.64.11")
  121. >>> ip3 = socket.inet_aton("18.244.0.188")
  122. >>> pol = Policy.parseLines(["reject *:80","accept 18.244.0.188:*"])
  123. >>> print str(pol).strip()
  124. reject 0.0.0.0/0.0.0.0:80-80
  125. accept 18.244.0.188/255.255.255.255:1-65535
  126. >>> pol.accepts(ip1,80)
  127. False
  128. >>> pol.accepts(ip3,80)
  129. False
  130. >>> pol.accepts(ip3,81)
  131. True
  132. """
  133. def __init__(self, lst):
  134. self.lst = lst
  135. def parseLines(lines):
  136. r = []
  137. for item in lines:
  138. a,p=item.split(" ",1)
  139. if a == 'accept':
  140. a = True
  141. elif a == 'reject':
  142. a = False
  143. else:
  144. raise ValueError("Unrecognized action %r",a)
  145. p = Pattern.parse(p)
  146. r.append((p,a))
  147. return Policy(r)
  148. parseLines = staticmethod(parseLines)
  149. def __str__(self):
  150. r = []
  151. for pat, accept in self.lst:
  152. rule = accept and "accept" or "reject"
  153. r.append("%s %s\n"%(rule,pat))
  154. return "".join(r)
  155. def accepts(self, ip, port):
  156. for pattern,accept in self.lst:
  157. if pattern.appliesTo(ip,port):
  158. return accept
  159. return True
  160. class Server:
  161. def __init__(self, name, ip, policy):
  162. self.name = name
  163. self.ip = ip
  164. self.policy = policy
  165. def uniq_sort(lst):
  166. d = {}
  167. for item in lst: d[item] = 1
  168. lst = d.keys()
  169. lst.sort()
  170. return lst
  171. def run():
  172. servers = []
  173. policy = []
  174. name = ip = None
  175. for line in sys.stdin.xreadlines():
  176. if line.startswith('router '):
  177. if name:
  178. servers.append(Server(name, ip, Policy.parseLines(policy)))
  179. _, name, ip, rest = line.split(" ", 3)
  180. policy = []
  181. elif line.startswith('accept ') or line.startswith('reject '):
  182. policy.append(line.strip())
  183. if name:
  184. servers.append(Server(name, ip, Policy.parseLines(policy)))
  185. targets = []
  186. for line in ADDRESSES_OF_INTEREST.split("\n"):
  187. line = line.strip()
  188. if not line: continue
  189. p = Pattern.parse(line)
  190. targets.append((p.ip, p.portMin))
  191. accepters, rejecters = [], []
  192. for s in servers:
  193. for ip,port in targets:
  194. if s.policy.accepts(ip,port):
  195. accepters.append(s)
  196. break
  197. else:
  198. rejecters.append(s)
  199. if INVERSE:
  200. printlist = rejecters
  201. else:
  202. printlist = accepters
  203. ents = []
  204. if VERBOSE:
  205. ents = uniq_sort([ "%s\t%s"%(s.ip,s.name) for s in printlist ])
  206. else:
  207. ents = uniq_sort([ s.ip for s in printlist ])
  208. for e in ents:
  209. print e
  210. def _test():
  211. import doctest, exitparse
  212. return doctest.testmod(exitparse)
  213. #_test()
  214. run()