exitlist 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  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 18.244.0.188:80 < ~/.tor/cached-directory
  10. example usage (Tor 0.1.1.10-alpha and later):
  11. cat ~/.tor/cached-routers* | python exitlist 18.244.0.188:80
  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. This default is only used if we don't learn
  33. # about any addresses from the command-line.
  34. #
  35. ADDRESSES_OF_INTEREST = """
  36. 1.2.3.4:80
  37. """
  38. #
  39. # YOU DO NOT NEED TO EDIT AFTER THIS POINT.
  40. #
  41. import sys
  42. import re
  43. import getopt
  44. import socket
  45. import struct
  46. assert sys.version_info >= (2,2)
  47. def maskIP(ip,mask):
  48. return "".join([chr(ord(a) & ord(b)) for a,b in zip(ip,mask)])
  49. def maskFromLong(lng):
  50. return struct.pack("!L", lng)
  51. def maskByBits(n):
  52. return maskFromLong(0xffffffffl ^ ((1L<<(32-n))-1))
  53. class Pattern:
  54. """
  55. >>> import socket
  56. >>> ip1 = socket.inet_aton("192.169.64.11")
  57. >>> ip2 = socket.inet_aton("192.168.64.11")
  58. >>> ip3 = socket.inet_aton("18.244.0.188")
  59. >>> print Pattern.parse("18.244.0.188")
  60. 18.244.0.188/255.255.255.255:1-65535
  61. >>> print Pattern.parse("18.244.0.188/16:*")
  62. 18.244.0.0/255.255.0.0:1-65535
  63. >>> print Pattern.parse("18.244.0.188/2.2.2.2:80")
  64. 2.0.0.0/2.2.2.2:80-80
  65. >>> print Pattern.parse("192.168.0.1/255.255.00.0:22-25")
  66. 192.168.0.0/255.255.0.0:22-25
  67. >>> p1 = Pattern.parse("192.168.0.1/255.255.00.0:22-25")
  68. >>> import socket
  69. >>> p1.appliesTo(ip1, 22)
  70. False
  71. >>> p1.appliesTo(ip2, 22)
  72. True
  73. >>> p1.appliesTo(ip2, 25)
  74. True
  75. >>> p1.appliesTo(ip2, 26)
  76. False
  77. """
  78. def __init__(self, ip, mask, portMin, portMax):
  79. self.ip = maskIP(ip,mask)
  80. self.mask = mask
  81. self.portMin = portMin
  82. self.portMax = portMax
  83. def __str__(self):
  84. return "%s/%s:%s-%s"%(socket.inet_ntoa(self.ip),
  85. socket.inet_ntoa(self.mask),
  86. self.portMin,
  87. self.portMax)
  88. def parse(s):
  89. if ":" in s:
  90. addrspec, portspec = s.split(":",1)
  91. else:
  92. addrspec, portspec = s, "*"
  93. if addrspec == '*':
  94. ip,mask = "\x00\x00\x00\x00","\x00\x00\x00\x00"
  95. elif '/' not in addrspec:
  96. ip = socket.inet_aton(addrspec)
  97. mask = "\xff\xff\xff\xff"
  98. else:
  99. ip,mask = addrspec.split("/",1)
  100. ip = socket.inet_aton(ip)
  101. if "." in mask:
  102. mask = socket.inet_aton(mask)
  103. else:
  104. mask = maskByBits(int(mask))
  105. if portspec == '*':
  106. portMin = 1
  107. portMax = 65535
  108. elif '-' not in portspec:
  109. portMin = portMax = int(portspec)
  110. else:
  111. portMin, portMax = map(int,portspec.split("-",1))
  112. return Pattern(ip,mask,portMin,portMax)
  113. parse = staticmethod(parse)
  114. def appliesTo(self, ip, port):
  115. return ((maskIP(ip,self.mask) == self.ip) and
  116. (self.portMin <= port <= self.portMax))
  117. class Policy:
  118. """
  119. >>> import socket
  120. >>> ip1 = socket.inet_aton("192.169.64.11")
  121. >>> ip2 = socket.inet_aton("192.168.64.11")
  122. >>> ip3 = socket.inet_aton("18.244.0.188")
  123. >>> pol = Policy.parseLines(["reject *:80","accept 18.244.0.188:*"])
  124. >>> print str(pol).strip()
  125. reject 0.0.0.0/0.0.0.0:80-80
  126. accept 18.244.0.188/255.255.255.255:1-65535
  127. >>> pol.accepts(ip1,80)
  128. False
  129. >>> pol.accepts(ip3,80)
  130. False
  131. >>> pol.accepts(ip3,81)
  132. True
  133. """
  134. def __init__(self, lst):
  135. self.lst = lst
  136. def parseLines(lines):
  137. r = []
  138. for item in lines:
  139. a,p=item.split(" ",1)
  140. if a == 'accept':
  141. a = True
  142. elif a == 'reject':
  143. a = False
  144. else:
  145. raise ValueError("Unrecognized action %r",a)
  146. p = Pattern.parse(p)
  147. r.append((p,a))
  148. return Policy(r)
  149. parseLines = staticmethod(parseLines)
  150. def __str__(self):
  151. r = []
  152. for pat, accept in self.lst:
  153. rule = accept and "accept" or "reject"
  154. r.append("%s %s\n"%(rule,pat))
  155. return "".join(r)
  156. def accepts(self, ip, port):
  157. for pattern,accept in self.lst:
  158. if pattern.appliesTo(ip,port):
  159. return accept
  160. return True
  161. class Server:
  162. def __init__(self, name, ip, policy):
  163. self.name = name
  164. self.ip = ip
  165. self.policy = policy
  166. def uniq_sort(lst):
  167. d = {}
  168. for item in lst: d[item] = 1
  169. lst = d.keys()
  170. lst.sort()
  171. return lst
  172. def run():
  173. global VERBOSE
  174. global INVERSE
  175. global ADDRESSES_OF_INTEREST
  176. if len(sys.argv) > 1:
  177. try:
  178. opts, pargs = getopt.getopt(sys.argv[1:], "vx")
  179. except getopt.GetoptError, e:
  180. print """
  181. usage: cat ~/.tor/cached-routers* | %s [-v] [-x] [host:port [host:port [...]]]
  182. -v verbose output
  183. -x invert results
  184. """ % sys.argv[0]
  185. sys.exit(0)
  186. for o, a in opts:
  187. if o == "-v":
  188. VERBOSE = True
  189. if o == "-x":
  190. INVERSE = True
  191. if len(pargs):
  192. ADDRESSES_OF_INTEREST = "\n".join(pargs)
  193. servers = []
  194. policy = []
  195. name = ip = None
  196. for line in sys.stdin.xreadlines():
  197. if line.startswith('router '):
  198. if name:
  199. servers.append(Server(name, ip, Policy.parseLines(policy)))
  200. _, name, ip, rest = line.split(" ", 3)
  201. policy = []
  202. elif line.startswith('accept ') or line.startswith('reject '):
  203. policy.append(line.strip())
  204. if name:
  205. servers.append(Server(name, ip, Policy.parseLines(policy)))
  206. targets = []
  207. for line in ADDRESSES_OF_INTEREST.split("\n"):
  208. line = line.strip()
  209. if not line: continue
  210. p = Pattern.parse(line)
  211. targets.append((p.ip, p.portMin))
  212. accepters, rejecters = [], []
  213. for s in servers:
  214. for ip,port in targets:
  215. if s.policy.accepts(ip,port):
  216. accepters.append(s)
  217. break
  218. else:
  219. rejecters.append(s)
  220. if INVERSE:
  221. printlist = rejecters
  222. else:
  223. printlist = accepters
  224. ents = []
  225. if VERBOSE:
  226. ents = uniq_sort([ "%s\t%s"%(s.ip,s.name) for s in printlist ])
  227. else:
  228. ents = uniq_sort([ s.ip for s in printlist ])
  229. for e in ents:
  230. print e
  231. def _test():
  232. import doctest, exitparse
  233. return doctest.testmod(exitparse)
  234. #_test()
  235. run()