2 # Copyright 2005 Nick Mathewson
3 # See the LICENSE file in the Tor distribution for licensing information.
6 exitlist -- Given a Tor directory on stdin, lists the Tor servers
7 that accept connections to given addreses.
11 python2.2 exitlist < ~/.tor/cached-directory
14 # Requires Python 2.2 or later.
17 # Change this to True if you want more verbose output. By default, we
18 # only print the IPs of the servers that accept any the listed
19 # addresses, one per line.
24 # Change this to True if you want to reverse the output, and list the
25 # servers that accept *none* of the listed addresses.
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.
34 ADDRESSES_OF_INTEREST
= """
40 # YOU DO NOT NEED TO EDIT AFTER THIS POINT.
49 assert sys
.version_info
>= (2,2)
53 return "".join([chr(ord(a
) & ord(b
)) for a
,b
in zip(ip
,mask
)])
55 def maskFromLong(lng
):
56 return struct
.pack("!L", lng
)
59 return maskFromLong(0xffffffffl ^
((1L<<(32-n
))-1))
64 >>> ip1 = socket.inet_aton("192.169.64.11")
65 >>> ip2 = socket.inet_aton("192.168.64.11")
66 >>> ip3 = socket.inet_aton("18.244.0.188")
68 >>> print Pattern.parse("18.244.0.188")
69 18.244.0.188/255.255.255.255:1-65535
70 >>> print Pattern.parse("18.244.0.188/16:*")
71 18.244.0.0/255.255.0.0:1-65535
72 >>> print Pattern.parse("18.244.0.188/2.2.2.2:80")
74 >>> print Pattern.parse("192.168.0.1/255.255.00.0:22-25")
75 192.168.0.0/255.255.0.0:22-25
76 >>> p1 = Pattern.parse("192.168.0.1/255.255.00.0:22-25")
78 >>> p1.appliesTo(ip1, 22)
80 >>> p1.appliesTo(ip2, 22)
82 >>> p1.appliesTo(ip2, 25)
84 >>> p1.appliesTo(ip2, 26)
87 def __init__(self
, ip
, mask
, portMin
, portMax
):
88 self
.ip
= maskIP(ip
,mask
)
90 self
.portMin
= portMin
91 self
.portMax
= portMax
94 return "%s/%s:%s-%s"%(socket
.inet_ntoa(self
.ip
),
95 socket
.inet_ntoa(self
.mask
),
101 addrspec
, portspec
= s
.split(":",1)
103 addrspec
, portspec
= s
, "*"
106 ip
,mask
= "\x00\x00\x00\x00","\x00\x00\x00\x00"
107 elif '/' not in addrspec
:
108 ip
= socket
.inet_aton(addrspec
)
109 mask
= "\xff\xff\xff\xff"
111 ip
,mask
= addrspec
.split("/",1)
112 ip
= socket
.inet_aton(ip
)
114 mask
= socket
.inet_aton(mask
)
116 mask
= maskByBits(int(mask
))
121 elif '-' not in portspec
:
122 portMin
= portMax
= int(portspec
)
124 portMin
, portMax
= map(int,portspec
.split("-",1))
126 return Pattern(ip
,mask
,portMin
,portMax
)
128 parse
= staticmethod(parse
)
130 def appliesTo(self
, ip
, port
):
131 return ((maskIP(ip
,self
.mask
) == self
.ip
) and
132 (self
.portMin
<= port
<= self
.portMax
))
137 >>> ip1 = socket.inet_aton("192.169.64.11")
138 >>> ip2 = socket.inet_aton("192.168.64.11")
139 >>> ip3 = socket.inet_aton("18.244.0.188")
141 >>> pol = Policy.parseLines(["reject *:80","accept 18.244.0.188:*"])
142 >>> print str(pol).strip()
143 reject 0.0.0.0/0.0.0.0:80-80
144 accept 18.244.0.188/255.255.255.255:1-65535
145 >>> pol.accepts(ip1,80)
147 >>> pol.accepts(ip3,80)
149 >>> pol.accepts(ip3,81)
153 def __init__(self
, lst
):
156 def parseLines(lines
):
159 a
,p
=item
.split(" ",1)
165 raise ValueError("Unrecognized action %r",a
)
170 parseLines
= staticmethod(parseLines
)
174 for pat
, accept
in self
.lst
:
175 rule
= accept
and "accept" or "reject"
176 r
.append("%s %s\n"%(rule
,pat
))
179 def accepts(self
, ip
, port
):
180 for pattern
,accept
in self
.lst
:
181 if pattern
.appliesTo(ip
,port
):
186 def __init__(self
, name
, ip
, policy
):
195 for line
in sys
.stdin
.xreadlines():
196 if line
.startswith('router '):
198 servers
.append(Server(name
, ip
, Policy
.parseLines(policy
)))
199 _
, name
, ip
, rest
= line
.split(" ", 3)
201 elif line
.startswith('accept ') or line
.startswith('reject '):
202 policy
.append(line
.strip())
205 servers
.append(Server(name
, ip
, Policy
.parseLines(policy
)))
208 for line
in ADDRESSES_OF_INTEREST
.split("\n"):
210 if not line
: continue
211 p
= Pattern
.parse(line
)
212 targets
.append((p
.ip
, p
.portMin
))
214 accepters
, rejecters
= [], []
216 for ip
,port
in targets
:
217 if s
.policy
.accepts(ip
,port
):
224 printlist
= rejecters
226 printlist
= accepters
230 print "%s\t%s"%(s
.ip
,s
.name
)
236 import doctest
, exitparse
237 return doctest
.testmod(exitparse
)