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