exitlist 6.2 KB

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