tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

tor-resolve.py (4516B)


      1 #!/usr/bin/env python
      2 
      3 # Future imports for Python 2.7, mandatory in 3.0
      4 from __future__ import division
      5 from __future__ import print_function
      6 from __future__ import unicode_literals
      7 
      8 import socket
      9 import struct
     10 import sys
     11 
     12 def socks4AResolveRequest(hostname):
     13    version = 4
     14    command = 0xF0
     15    port = 0
     16    addr = 0x0000001
     17    username = ""
     18    reqheader = struct.pack("!BBHL", version, command, port, addr)
     19    return "%s%s\x00%s\x00"%(reqheader,username,hostname)
     20 
     21 def socks4AParseResponse(response):
     22    RESPONSE_LEN = 8
     23    if len(response) < RESPONSE_LEN:
     24        return None
     25    assert len(response) >= RESPONSE_LEN
     26    version,status,port = struct.unpack("!BBH",response[:4])
     27    assert version == 0
     28    assert port == 0
     29    if status == 90:
     30        return "%d.%d.%d.%d"%tuple(map(ord, response[4:]))
     31    else:
     32        return "ERROR (status %d)"%status
     33 
     34 def socks5Hello():
     35    return "\x05\x01\x00"
     36 def socks5ParseHello(response):
     37    if response != "\x05\x00":
     38        raise ValueError("Bizarre socks5 response")
     39 def socks5ResolveRequest(hostname, atype=0x03, command=0xF0):
     40    version = 5
     41    rsv = 0
     42    port = 0
     43    reqheader = struct.pack("!BBBB",version, command, rsv, atype)
     44    if atype == 0x03:
     45        reqheader += struct.pack("!B", len(hostname))
     46    portstr = struct.pack("!H",port)
     47    return "%s%s%s"%(reqheader,hostname,portstr)
     48 
     49 def socks5ParseResponse(r):
     50    if len(r)<8:
     51        return None
     52    version, reply, rsv, atype = struct.unpack("!BBBB",r[:4])
     53    assert version==5
     54    assert rsv==0
     55    if reply != 0x00:
     56        return "ERROR",reply
     57    assert atype in (0x01,0x03,0x04)
     58    if atype != 0x03:
     59        expected_len = 4 + ({1:4,4:16}[atype]) + 2
     60        if len(r) < expected_len:
     61            return None
     62        elif len(r) > expected_len:
     63            raise ValueError("Overlong socks5 reply!")
     64        addr = r[4:-2]
     65        if atype == 0x01:
     66            return "%d.%d.%d.%d"%tuple(map(ord,addr))
     67        else:
     68            # not really the right way to format IPv6
     69            return "IPv6: %s"%(":".join([hex(ord(c)) for c in addr]))
     70    else:
     71        hlen, = struct.unpack("!B", r[4])
     72        expected_len = 5 + hlen + 2
     73        if len(r) < expected_len:
     74            return None
     75        return r[5:-2]
     76 
     77 def socks5ResolvePTRRequest(hostname):
     78    return socks5ResolveRequest(socket.inet_aton(hostname),
     79                                atype=1, command = 0xF1)
     80 
     81 
     82 def parseHostAndPort(h):
     83    host, port = "localhost", 9050
     84    if ":" in h:
     85        i = h.index(":")
     86        host = h[:i]
     87        try:
     88            port = int(h[i+1:])
     89        except ValueError:
     90            print("Bad hostname %r"%h)
     91            sys.exit(1)
     92    elif h:
     93        try:
     94            port = int(h)
     95        except ValueError:
     96            host = h
     97 
     98    return host, port
     99 
    100 def resolve(hostname, sockshost, socksport, socksver=4, reverse=0):
    101    assert socksver in (4,5)
    102    if socksver == 4:
    103        fmt = socks4AResolveRequest
    104        parse = socks4AParseResponse
    105    elif not reverse:
    106        fmt = socks5ResolveRequest
    107        parse = socks5ParseResponse
    108    else:
    109        fmt = socks5ResolvePTRRequest
    110        parse = socks5ParseResponse
    111 
    112    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    113    s.connect((sockshost,socksport))
    114    if socksver == 5:
    115        s.send(socks5Hello())
    116        socks5ParseHello(s.recv(2))
    117    s.send(fmt(hostname))
    118    answer = s.recv(6)
    119    result = parse(answer)
    120    while result is None:
    121        more = s.recv(1)
    122        if not more:
    123            return None
    124        answer += more
    125        result = parse(answer)
    126    print("Got answer",result)
    127    m = s.recv(1)
    128    if m:
    129        print("Got extra data too: %r"%m)
    130    return result
    131 
    132 if __name__ == '__main__':
    133    if len(sys.argv) not in (2,3,4):
    134        print("Syntax: resolve.py [-4|-5] hostname [sockshost:socksport]")
    135        sys.exit(0)
    136    socksver = 4
    137    reverse = 0
    138    while sys.argv[1][0] == '-':
    139        if sys.argv[1] in ("-4", "-5"):
    140            socksver = int(sys.argv[1][1])
    141            del sys.argv[1]
    142        elif sys.argv[1] == '-x':
    143            reverse = 1
    144            del sys.argv[1]
    145        elif sys.argv[1] == '--':
    146            break
    147 
    148    if len(sys.argv) >= 4:
    149        print("Syntax: resolve.py [-x] [-4|-5] hostname [sockshost:socksport]")
    150        sys.exit(0)
    151    if len(sys.argv) == 3:
    152        sh,sp = parseHostAndPort(sys.argv[2])
    153    else:
    154        sh,sp = parseHostAndPort("")
    155 
    156    if reverse and socksver == 4:
    157        socksver = 5
    158    resolve(sys.argv[1], sh, sp, socksver, reverse)