| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 | #!/usr/bin/python# Copyright 2005-2006 Nick Mathewson# See the LICENSE file in the Tor distribution for licensing information.# Requires Python 2.2 or later.""" exitlist -- Given a Tor directory on stdin, lists the Tor servers that accept connections to given addreses. example usage (Tor 0.2.0.7-alpha and earlier):    cat ~/.tor/cached-routers* | python exitlist 18.244.0.188:80 example usage (Tor 0.2.0.8-alpha and later):    cat ~/.tor/cached-descriptors* | python exitlist 18.244.0.188:80 You should look at the "FetchUselessDescriptors" config option in the man page. For 0.2.0.13-alpha and later, also look at the "FetchDirInfoEarly" config option. Note that this script won't give you a perfect list of IP addresses that might connect to you using Tor, since some Tor servers might exit from other addresses than the one they publish. See https://check.torproject.org/ for an alternative (more accurate!) approach."""## Change this to True if you want more verbose output.  By default, we# only print the IPs of the servers that accept any the listed# addresses, one per line.#VERBOSE = False## Change this to True if you want to reverse the output, and list the# servers that accept *none* of the listed addresses.#INVERSE = False## Change this list to contain all of the target services you are interested# in.  It must contain one entry per line, each consisting of an IPv4 address,# a colon, and a port number. This default is only used if we don't learn# about any addresses from the command-line.#ADDRESSES_OF_INTEREST = """    1.2.3.4:80"""## YOU DO NOT NEED TO EDIT AFTER THIS POINT.#import sysimport reimport getoptimport socketimport structimport timeassert sys.version_info >= (2,2)def maskIP(ip,mask):    return "".join([chr(ord(a) & ord(b)) for a,b in zip(ip,mask)])def maskFromLong(lng):    return struct.pack("!L", lng)def maskByBits(n):    return maskFromLong(0xffffffffl ^ ((1L<<(32-n))-1))class Pattern:    """    >>> import socket    >>> ip1 = socket.inet_aton("192.169.64.11")    >>> ip2 = socket.inet_aton("192.168.64.11")    >>> ip3 = socket.inet_aton("18.244.0.188")    >>> print Pattern.parse("18.244.0.188")    18.244.0.188/255.255.255.255:1-65535    >>> print Pattern.parse("18.244.0.188/16:*")    18.244.0.0/255.255.0.0:1-65535    >>> print Pattern.parse("18.244.0.188/2.2.2.2:80")    2.0.0.0/2.2.2.2:80-80    >>> print Pattern.parse("192.168.0.1/255.255.00.0:22-25")    192.168.0.0/255.255.0.0:22-25    >>> p1 = Pattern.parse("192.168.0.1/255.255.00.0:22-25")    >>> import socket    >>> p1.appliesTo(ip1, 22)    False    >>> p1.appliesTo(ip2, 22)    True    >>> p1.appliesTo(ip2, 25)    True    >>> p1.appliesTo(ip2, 26)    False    """    def __init__(self, ip, mask, portMin, portMax):        self.ip = maskIP(ip,mask)        self.mask = mask        self.portMin = portMin        self.portMax = portMax    def __str__(self):        return "%s/%s:%s-%s"%(socket.inet_ntoa(self.ip),                              socket.inet_ntoa(self.mask),                              self.portMin,                              self.portMax)    def parse(s):        if ":" in s:            addrspec, portspec = s.split(":",1)        else:            addrspec, portspec = s, "*"        if addrspec == '*':            ip,mask = "\x00\x00\x00\x00","\x00\x00\x00\x00"        elif '/' not in addrspec:            ip = socket.inet_aton(addrspec)            mask = "\xff\xff\xff\xff"        else:            ip,mask = addrspec.split("/",1)            ip = socket.inet_aton(ip)            if "." in mask:                mask = socket.inet_aton(mask)            else:                mask = maskByBits(int(mask))        if portspec == '*':            portMin = 1            portMax = 65535        elif '-' not in portspec:            portMin = portMax = int(portspec)        else:            portMin, portMax = map(int,portspec.split("-",1))        return Pattern(ip,mask,portMin,portMax)    parse = staticmethod(parse)    def appliesTo(self, ip, port):        return ((maskIP(ip,self.mask) == self.ip) and                (self.portMin <= port <= self.portMax))class Policy:    """    >>> import socket    >>> ip1 = socket.inet_aton("192.169.64.11")    >>> ip2 = socket.inet_aton("192.168.64.11")    >>> ip3 = socket.inet_aton("18.244.0.188")    >>> pol = Policy.parseLines(["reject *:80","accept 18.244.0.188:*"])    >>> print str(pol).strip()    reject 0.0.0.0/0.0.0.0:80-80    accept 18.244.0.188/255.255.255.255:1-65535    >>> pol.accepts(ip1,80)    False    >>> pol.accepts(ip3,80)    False    >>> pol.accepts(ip3,81)    True    """    def __init__(self, lst):        self.lst = lst    def parseLines(lines):        r = []        for item in lines:            a,p=item.split(" ",1)            if a == 'accept':                a = True            elif a == 'reject':                a = False            else:                raise ValueError("Unrecognized action %r",a)            p = Pattern.parse(p)            r.append((p,a))        return Policy(r)    parseLines = staticmethod(parseLines)    def __str__(self):        r = []        for pat, accept in self.lst:            rule = accept and "accept" or "reject"            r.append("%s %s\n"%(rule,pat))        return "".join(r)    def accepts(self, ip, port):        for pattern,accept in self.lst:            if pattern.appliesTo(ip,port):                return accept        return Trueclass Server:    def __init__(self, name, ip, policy, published, fingerprint):        self.name = name        self.ip = ip        self.policy = policy        self.published = published        self.fingerprint = fingerprintdef uniq_sort(lst):    d = {}    for item in lst: d[item] = 1    lst = d.keys()    lst.sort()    return lstdef run():    global VERBOSE    global INVERSE    global ADDRESSES_OF_INTEREST    if len(sys.argv) > 1:        try:            opts, pargs = getopt.getopt(sys.argv[1:], "vx")        except getopt.GetoptError, e:            print """usage: cat ~/.tor/cached-routers* | %s [-v] [-x] [host:port [host:port [...]]]    -v  verbose output    -x  invert results""" % sys.argv[0]            sys.exit(0)        for o, a in opts:            if o == "-v":                VERBOSE = True            if o == "-x":                INVERSE = True        if len(pargs):            ADDRESSES_OF_INTEREST = "\n".join(pargs)    servers = []    policy = []    name = ip = None    published = 0    fp = ""    for line in sys.stdin.xreadlines():        if line.startswith('router '):            if name:                servers.append(Server(name, ip, Policy.parseLines(policy),                                      published, fp))            _, name, ip, rest = line.split(" ", 3)            policy = []            published = 0            fp = ""        elif line.startswith('fingerprint') or \                 line.startswith('opt fingerprint'):            elts = line.strip().split()            if elts[0] == 'opt': del elts[0]            assert elts[0] == 'fingerprint'            del elts[0]            fp = "".join(elts)        elif line.startswith('accept ') or line.startswith('reject '):            policy.append(line.strip())        elif line.startswith('published '):            date = time.strptime(line[len('published '):].strip(),                                 "%Y-%m-%d %H:%M:%S")            published = time.mktime(date)    if name:        servers.append(Server(name, ip, Policy.parseLines(policy), published,                              fp))    targets = []    for line in ADDRESSES_OF_INTEREST.split("\n"):        line = line.strip()        if not line: continue        p = Pattern.parse(line)        targets.append((p.ip, p.portMin))    # remove all but the latest server of each IP/Nickname pair.    latest = {}    for s in servers:        if (not latest.has_key((s.fingerprint))            or s.published > latest[(s.fingerprint)]):            latest[s.fingerprint] = s    servers = latest.values()    accepters, rejecters = {}, {}    for s in servers:        for ip,port in targets:            if s.policy.accepts(ip,port):                accepters[s.ip] = s                break        else:            rejecters[s.ip] = s    # If any server at IP foo accepts, the IP does not reject.    for k in accepters.keys():        if rejecters.has_key(k):            del rejecters[k]    if INVERSE:        printlist = rejecters.values()    else:        printlist = accepters.values()    ents = []    if VERBOSE:        ents = uniq_sort([ "%s\t%s"%(s.ip,s.name) for s in printlist ])    else:        ents = uniq_sort([ s.ip for s in printlist ])    for e in ents:        print edef _test():    import doctest, exitparse    return doctest.testmod(exitparse)#_test()run()
 |