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