exitlist 6.1 KB

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