exitlist 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  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. """
  15. #
  16. # Change this to True if you want more verbose output. By default, we
  17. # only print the IPs of the servers that accept any the listed
  18. # addresses, one per line.
  19. #
  20. VERBOSE = False
  21. #
  22. # Change this to True if you want to reverse the output, and list the
  23. # servers that accept *none* of the listed addresses.
  24. #
  25. INVERSE = False
  26. #
  27. # Change this list to contain all of the target services you are interested
  28. # in. It must contain one entry per line, each consisting of an IPv4 address,
  29. # a colon, and a port number.
  30. #
  31. ADDRESSES_OF_INTEREST = """
  32. 192.168.0.1:80
  33. """
  34. #
  35. # YOU DO NOT NEED TO EDIT AFTER THIS POINT.
  36. #
  37. import sys
  38. import re
  39. import getopt
  40. import socket
  41. import struct
  42. assert sys.version_info >= (2,2)
  43. def maskIP(ip,mask):
  44. return "".join([chr(ord(a) & ord(b)) for a,b in zip(ip,mask)])
  45. def maskFromLong(lng):
  46. return struct.pack("!L", lng)
  47. def maskByBits(n):
  48. return maskFromLong(0xffffffffl ^ ((1L<<(32-n))-1))
  49. class Pattern:
  50. """
  51. >>> import socket
  52. >>> ip1 = socket.inet_aton("192.169.64.11")
  53. >>> ip2 = socket.inet_aton("192.168.64.11")
  54. >>> ip3 = socket.inet_aton("18.244.0.188")
  55. >>> print Pattern.parse("18.244.0.188")
  56. 18.244.0.188/255.255.255.255:1-65535
  57. >>> print Pattern.parse("18.244.0.188/16:*")
  58. 18.244.0.0/255.255.0.0:1-65535
  59. >>> print Pattern.parse("18.244.0.188/2.2.2.2:80")
  60. 2.0.0.0/2.2.2.2:80-80
  61. >>> print Pattern.parse("192.168.0.1/255.255.00.0:22-25")
  62. 192.168.0.0/255.255.0.0:22-25
  63. >>> p1 = Pattern.parse("192.168.0.1/255.255.00.0:22-25")
  64. >>> import socket
  65. >>> p1.appliesTo(ip1, 22)
  66. False
  67. >>> p1.appliesTo(ip2, 22)
  68. True
  69. >>> p1.appliesTo(ip2, 25)
  70. True
  71. >>> p1.appliesTo(ip2, 26)
  72. False
  73. """
  74. def __init__(self, ip, mask, portMin, portMax):
  75. self.ip = maskIP(ip,mask)
  76. self.mask = mask
  77. self.portMin = portMin
  78. self.portMax = portMax
  79. def __str__(self):
  80. return "%s/%s:%s-%s"%(socket.inet_ntoa(self.ip),
  81. socket.inet_ntoa(self.mask),
  82. self.portMin,
  83. self.portMax)
  84. def parse(s):
  85. if ":" in s:
  86. addrspec, portspec = s.split(":",1)
  87. else:
  88. addrspec, portspec = s, "*"
  89. if addrspec == '*':
  90. ip,mask = "\x00\x00\x00\x00","\x00\x00\x00\x00"
  91. elif '/' not in addrspec:
  92. ip = socket.inet_aton(addrspec)
  93. mask = "\xff\xff\xff\xff"
  94. else:
  95. ip,mask = addrspec.split("/",1)
  96. ip = socket.inet_aton(ip)
  97. if "." in mask:
  98. mask = socket.inet_aton(mask)
  99. else:
  100. mask = maskByBits(int(mask))
  101. if portspec == '*':
  102. portMin = 1
  103. portMax = 65535
  104. elif '-' not in portspec:
  105. portMin = portMax = int(portspec)
  106. else:
  107. portMin, portMax = map(int,portspec.split("-",1))
  108. return Pattern(ip,mask,portMin,portMax)
  109. parse = staticmethod(parse)
  110. def appliesTo(self, ip, port):
  111. return ((maskIP(ip,self.mask) == self.ip) and
  112. (self.portMin <= port <= self.portMax))
  113. class Policy:
  114. """
  115. >>> import socket
  116. >>> ip1 = socket.inet_aton("192.169.64.11")
  117. >>> ip2 = socket.inet_aton("192.168.64.11")
  118. >>> ip3 = socket.inet_aton("18.244.0.188")
  119. >>> pol = Policy.parseLines(["reject *:80","accept 18.244.0.188:*"])
  120. >>> print str(pol).strip()
  121. reject 0.0.0.0/0.0.0.0:80-80
  122. accept 18.244.0.188/255.255.255.255:1-65535
  123. >>> pol.accepts(ip1,80)
  124. False
  125. >>> pol.accepts(ip3,80)
  126. False
  127. >>> pol.accepts(ip3,81)
  128. True
  129. """
  130. def __init__(self, lst):
  131. self.lst = lst
  132. def parseLines(lines):
  133. r = []
  134. for item in lines:
  135. a,p=item.split(" ",1)
  136. if a == 'accept':
  137. a = True
  138. elif a == 'reject':
  139. a = False
  140. else:
  141. raise ValueError("Unrecognized action %r",a)
  142. p = Pattern.parse(p)
  143. r.append((p,a))
  144. return Policy(r)
  145. parseLines = staticmethod(parseLines)
  146. def __str__(self):
  147. r = []
  148. for pat, accept in self.lst:
  149. rule = accept and "accept" or "reject"
  150. r.append("%s %s\n"%(rule,pat))
  151. return "".join(r)
  152. def accepts(self, ip, port):
  153. for pattern,accept in self.lst:
  154. if pattern.appliesTo(ip,port):
  155. return accept
  156. return True
  157. class Server:
  158. def __init__(self, name, ip, policy):
  159. self.name = name
  160. self.ip = ip
  161. self.policy = policy
  162. def run():
  163. servers = []
  164. policy = []
  165. name = ip = None
  166. for line in sys.stdin.xreadlines():
  167. if line.startswith('router '):
  168. if name:
  169. servers.append(Server(name, ip, Policy.parseLines(policy)))
  170. _, name, ip, rest = line.split(" ", 3)
  171. policy = []
  172. elif line.startswith('accept ') or line.startswith('reject '):
  173. policy.append(line.strip())
  174. if name:
  175. servers.append(Server(name, ip, Policy.parseLines(policy)))
  176. targets = []
  177. for line in ADDRESSES_OF_INTEREST.split("\n"):
  178. line = line.strip()
  179. if not line: continue
  180. p = Pattern.parse(line)
  181. targets.append((p.ip, p.portMin))
  182. accepters, rejecters = [], []
  183. for s in servers:
  184. for ip,port in targets:
  185. if s.policy.accepts(ip,port):
  186. accepters.append(s)
  187. break
  188. else:
  189. rejecters.append(s)
  190. if INVERSE:
  191. printlist = rejecters
  192. else:
  193. printlist = accepters
  194. if VERBOSE:
  195. for s in printlist:
  196. print "%s\t%s"%(s.ip,s.name)
  197. else:
  198. for s in printlist:
  199. print s.ip
  200. def _test():
  201. import doctest, exitparse
  202. return doctest.testmod(exitparse)
  203. #_test()
  204. run()