2 # Copyright 2005-2006 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.
9 example usage (Tor 0.1.0.16 and earlier):
11 python2.2 exitlist < ~/.tor/cached-directory
13 example usage (Tor 0.1.1.10-alpha and later):
15 python2.2 exitlist < ~/.tor/cached-routers*
19 # Requires Python 2.2 or later.
22 # Change this to True if you want more verbose output. By default, we
23 # only print the IPs of the servers that accept any the listed
24 # addresses, one per line.
29 # Change this to True if you want to reverse the output, and list the
30 # servers that accept *none* of the listed addresses.
35 # Change this list to contain all of the target services you are interested
36 # in. It must contain one entry per line, each consisting of an IPv4 address,
37 # a colon, and a port number.
39 ADDRESSES_OF_INTEREST
= """
45 # YOU DO NOT NEED TO EDIT AFTER THIS POINT.
54 assert sys
.version_info
>= (2,2)
58 return "".join([chr(ord(a
) & ord(b
)) for a
,b
in zip(ip
,mask
)])
60 def maskFromLong(lng
):
61 return struct
.pack("!L", lng
)
64 return maskFromLong(0xffffffffl ^
((1L<<(32-n
))-1))
69 >>> ip1 = socket.inet_aton("192.169.64.11")
70 >>> ip2 = socket.inet_aton("192.168.64.11")
71 >>> ip3 = socket.inet_aton("18.244.0.188")
73 >>> print Pattern.parse("18.244.0.188")
74 18.244.0.188/255.255.255.255:1-65535
75 >>> print Pattern.parse("18.244.0.188/16:*")
76 18.244.0.0/255.255.0.0:1-65535
77 >>> print Pattern.parse("18.244.0.188/2.2.2.2:80")
79 >>> print Pattern.parse("192.168.0.1/255.255.00.0:22-25")
80 192.168.0.0/255.255.0.0:22-25
81 >>> p1 = Pattern.parse("192.168.0.1/255.255.00.0:22-25")
83 >>> p1.appliesTo(ip1, 22)
85 >>> p1.appliesTo(ip2, 22)
87 >>> p1.appliesTo(ip2, 25)
89 >>> p1.appliesTo(ip2, 26)
92 def __init__(self
, ip
, mask
, portMin
, portMax
):
93 self
.ip
= maskIP(ip
,mask
)
95 self
.portMin
= portMin
96 self
.portMax
= portMax
99 return "%s/%s:%s-%s"%(socket
.inet_ntoa(self
.ip
),
100 socket
.inet_ntoa(self
.mask
),
106 addrspec
, portspec
= s
.split(":",1)
108 addrspec
, portspec
= s
, "*"
111 ip
,mask
= "\x00\x00\x00\x00","\x00\x00\x00\x00"
112 elif '/' not in addrspec
:
113 ip
= socket
.inet_aton(addrspec
)
114 mask
= "\xff\xff\xff\xff"
116 ip
,mask
= addrspec
.split("/",1)
117 ip
= socket
.inet_aton(ip
)
119 mask
= socket
.inet_aton(mask
)
121 mask
= maskByBits(int(mask
))
126 elif '-' not in portspec
:
127 portMin
= portMax
= int(portspec
)
129 portMin
, portMax
= map(int,portspec
.split("-",1))
131 return Pattern(ip
,mask
,portMin
,portMax
)
133 parse
= staticmethod(parse
)
135 def appliesTo(self
, ip
, port
):
136 return ((maskIP(ip
,self
.mask
) == self
.ip
) and
137 (self
.portMin
<= port
<= self
.portMax
))
142 >>> ip1 = socket.inet_aton("192.169.64.11")
143 >>> ip2 = socket.inet_aton("192.168.64.11")
144 >>> ip3 = socket.inet_aton("18.244.0.188")
146 >>> pol = Policy.parseLines(["reject *:80","accept 18.244.0.188:*"])
147 >>> print str(pol).strip()
148 reject 0.0.0.0/0.0.0.0:80-80
149 accept 18.244.0.188/255.255.255.255:1-65535
150 >>> pol.accepts(ip1,80)
152 >>> pol.accepts(ip3,80)
154 >>> pol.accepts(ip3,81)
158 def __init__(self
, lst
):
161 def parseLines(lines
):
164 a
,p
=item
.split(" ",1)
170 raise ValueError("Unrecognized action %r",a
)
175 parseLines
= staticmethod(parseLines
)
179 for pat
, accept
in self
.lst
:
180 rule
= accept
and "accept" or "reject"
181 r
.append("%s %s\n"%(rule
,pat
))
184 def accepts(self
, ip
, port
):
185 for pattern
,accept
in self
.lst
:
186 if pattern
.appliesTo(ip
,port
):
191 def __init__(self
, name
, ip
, policy
):
200 for line
in sys
.stdin
.xreadlines():
201 if line
.startswith('router '):
203 servers
.append(Server(name
, ip
, Policy
.parseLines(policy
)))
204 _
, name
, ip
, rest
= line
.split(" ", 3)
206 elif line
.startswith('accept ') or line
.startswith('reject '):
207 policy
.append(line
.strip())
210 servers
.append(Server(name
, ip
, Policy
.parseLines(policy
)))
213 for line
in ADDRESSES_OF_INTEREST
.split("\n"):
215 if not line
: continue
216 p
= Pattern
.parse(line
)
217 targets
.append((p
.ip
, p
.portMin
))
219 accepters
, rejecters
= [], []
221 for ip
,port
in targets
:
222 if s
.policy
.accepts(ip
,port
):
229 printlist
= rejecters
231 printlist
= accepters
235 print "%s\t%s"%(s
.ip
,s
.name
)
241 import doctest
, exitparse
242 return doctest
.testmod(exitparse
)