Loops without an increment step can suck, even on windows.
[tor.git] / contrib / tor-resolve.py
blobdd44255bc15b9eaccd6f5fb3e7f9429cbe8d24db
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):
36 version = 5
37 command = 0xF0
38 rsv = 0
39 port = 0
40 atype = 0x03
41 reqheader = struct.pack("!BBBBB",version, command, rsv, atype, len(hostname))
42 portstr = struct.pack("!H",port)
43 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,0x04)
53 expected_len = 4 + ({1:4,4:16}[atype]) + 2
54 if len(r) < expected_len:
55 return None
56 elif len(r) > expected_len:
57 raise ValueError("Overlong socks5 reply!")
58 addr = r[4:-2]
59 if atype == 0x01:
60 return "%d.%d.%d.%d"%tuple(map(ord,addr))
61 else:
62 # not really the right way to format IPv6
63 return "IPv6: %s"%(":".join([hex(ord(c)) for c in addr]))
65 def parseHostAndPort(h):
66 host, port = "localhost", 9050
67 if ":" in h:
68 i = h.index(":")
69 host = h[:i]
70 try:
71 port = int(h[i+1:])
72 except ValueError:
73 print "Bad hostname %r"%h
74 sys.exit(1)
75 elif h:
76 try:
77 port = int(h)
78 except ValueError:
79 host = h
81 return host, port
83 def resolve(hostname, sockshost, socksport, socksver=4):
84 assert socksver in (4,5)
85 if socksver == 4:
86 fmt = socks4AResolveRequest
87 parse = socks4AParseResponse
88 else:
89 fmt = socks5ResolveRequest
90 parse = socks5ParseResponse
91 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
92 s.connect((sockshost,socksport))
93 if socksver == 5:
94 s.send(socks5Hello())
95 socks5ParseHello(s.recv(2))
96 print len(fmt(hostname)), len(hostname)
97 s.send(fmt(hostname))
98 answer = s.recv(6)
99 result = parse(answer)
100 while result is None:
101 more = s.recv(1)
102 if not more:
103 return None
104 answer += more
105 result = parse(answer)
106 print "Got answer",result
107 m = s.recv(1)
108 if m:
109 print "Got extra data too: %r"%m
110 return result
112 if __name__ == '__main__':
113 if len(sys.argv) not in (2,3,4):
114 print "Syntax: resolve.py [-4|-5] hostname [sockshost:socksport]"
115 sys.exit(0)
116 socksver = 4
117 if sys.argv[1] in ("-4", "-5"):
118 socksver = int(sys.argv[1][1])
119 del sys.argv[1]
120 if len(sys.argv) == 4:
121 print "Syntax: resolve.py [-4|-5] hostname [sockshost:socksport]"
122 sys.exit(0)
123 if len(sys.argv) == 3:
124 sh,sp = parseHostAndPort(sys.argv[2])
125 else:
126 sh,sp = parseHostAndPort("")
127 resolve(sys.argv[1], sh, sp, socksver)