tor-resolve.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. #!/usr/bin/python
  2. #$Id$
  3. import socket
  4. import struct
  5. import sys
  6. def socks4AResolveRequest(hostname):
  7. version = 4
  8. command = 0xF0
  9. port = 0
  10. addr = 0x0000001
  11. username = ""
  12. reqheader = struct.pack("!BBHL", version, command, port, addr)
  13. return "%s%s\x00%s\x00"%(reqheader,username,hostname)
  14. def socks4AParseResponse(response):
  15. RESPONSE_LEN = 8
  16. if len(response) < RESPONSE_LEN:
  17. print "return none", len(response)
  18. return None
  19. assert len(response) >= RESPONSE_LEN
  20. version,status,port = struct.unpack("!BBH",response[:4])
  21. assert version == 0
  22. assert port == 0
  23. if status == 90:
  24. return "%d.%d.%d.%d"%tuple(map(ord, response[4:]))
  25. else:
  26. return "ERROR (status %d)"%status
  27. def socks5Hello():
  28. return "\x05\x01\x00"
  29. def socks5ParseHello(response):
  30. if response != "\x05\x00":
  31. raise ValueError("Bizarre socks5 response")
  32. def socks5ResolveRequest(hostname):
  33. version = 5
  34. command = 0xF0
  35. rsv = 0
  36. port = 0
  37. atype = 0x03
  38. reqheader = struct.pack("!BBBB",version, command, rsv, atype)
  39. portstr = struct.pack("!H",port)
  40. return "%s%s\0%s"%(reqheader,hostname,port)
  41. def socks5ParseResponse(r):
  42. if len(r)<8: return None
  43. version, reply, rsv, atype = struct.unpack("!BBBB",r[:4])
  44. assert version==5
  45. assert rsv==0
  46. if reply != 0x00:
  47. return "ERROR"
  48. assert atype in (0x01,0x04)
  49. expected_len = 4 + ({1:4,4:16}[atype]) + 2
  50. if len(r) < expected_len:
  51. return None
  52. elif len(r) > expected_len:
  53. raise ValueError("Overlong socks5 reply!")
  54. addr = r[4:-2]
  55. if atype == 0x01:
  56. return "%d.%d.%d.%d"%tuple(map(ord,addr))
  57. else:
  58. # not really the right way to format IPv6
  59. return "IPv6: %s"%(":".join([hex(ord(c)) for c in addr]))
  60. def parseHostAndPort(h):
  61. host, port = "localhost", 9050
  62. if ":" in h:
  63. i = h.index(":")
  64. host = h[:i]
  65. try:
  66. port = int(h[i+1:])
  67. except ValueError:
  68. print "Bad hostname %r"%h
  69. sys.exit(1)
  70. elif h:
  71. try:
  72. port = int(h)
  73. except ValueError:
  74. host = h
  75. return host, port
  76. def resolve(hostname, sockshost, socksport, socksver=4):
  77. assert socksver in (4,5)
  78. if socksver == 4:
  79. fmt = socks4AResolveRequest
  80. parse = socks4AParseResponse
  81. else:
  82. fmt = socks5ResolveRequest
  83. parse = socks5ParseResponse
  84. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  85. s.connect((sockshost,socksport))
  86. if socksver == 5:
  87. s.send(socks5Hello())
  88. socksParseHello(s.recv(2))
  89. s.send(fmt(hostname))
  90. answer = s.recv(8)
  91. result = parse(answer)
  92. while result is None:
  93. more = s.recv(1)
  94. if not more:
  95. print "Connection closed; dying."
  96. return None
  97. answer += more
  98. result = parse(answer)
  99. print "Got answer",result
  100. m = s.recv(1)
  101. if m:
  102. print "Got extra data too! Ick."
  103. return result
  104. if __name__ == '__main__':
  105. if len(sys.argv) not in (2,3,4):
  106. print "Syntax: resolve.py [-4|-5] hostname [sockshost:socksport]"
  107. sys.exit(0)
  108. if sys.argv[1] in ("-4", "-5"):
  109. socksver = int(sys.argv[1][1])
  110. del sys.argv[1]
  111. if len(sys.argv) == 4:
  112. print "Syntax: resolve.py [-4|-5] hostname [sockshost:socksport]"
  113. sys.exit(0)
  114. if len(sys.argv) == 3:
  115. sh,sp = parseHostAndPort(sys.argv[2])
  116. else:
  117. sh,sp = parseHostAndPort("")
  118. resolve(sys.argv[1], sh, sp)