tor-resolve.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. #!/usr/bin/env python
  2. # Future imports for Python 2.7, mandatory in 3.0
  3. from __future__ import division
  4. from __future__ import print_function
  5. from __future__ import unicode_literals
  6. import socket
  7. import struct
  8. import sys
  9. def socks4AResolveRequest(hostname):
  10. version = 4
  11. command = 0xF0
  12. port = 0
  13. addr = 0x0000001
  14. username = ""
  15. reqheader = struct.pack("!BBHL", version, command, port, addr)
  16. 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
  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)
  43. def socks5ParseResponse(r):
  44. if len(r)<8:
  45. return None
  46. version, reply, rsv, atype = struct.unpack("!BBBB",r[:4])
  47. assert version==5
  48. assert rsv==0
  49. if reply != 0x00:
  50. return "ERROR",reply
  51. assert atype in (0x01,0x03,0x04)
  52. if atype != 0x03:
  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]))
  64. else:
  65. hlen, = struct.unpack("!B", r[4])
  66. expected_len = 5 + hlen + 2
  67. if len(r) < expected_len:
  68. return None
  69. return r[5:-2]
  70. def socks5ResolvePTRRequest(hostname):
  71. return socks5ResolveRequest(socket.inet_aton(hostname),
  72. atype=1, command = 0xF1)
  73. def parseHostAndPort(h):
  74. host, port = "localhost", 9050
  75. if ":" in h:
  76. i = h.index(":")
  77. host = h[:i]
  78. try:
  79. port = int(h[i+1:])
  80. except ValueError:
  81. print("Bad hostname %r"%h)
  82. sys.exit(1)
  83. elif h:
  84. try:
  85. port = int(h)
  86. except ValueError:
  87. host = h
  88. return host, port
  89. def resolve(hostname, sockshost, socksport, socksver=4, reverse=0):
  90. assert socksver in (4,5)
  91. if socksver == 4:
  92. fmt = socks4AResolveRequest
  93. parse = socks4AParseResponse
  94. elif not reverse:
  95. fmt = socks5ResolveRequest
  96. parse = socks5ParseResponse
  97. else:
  98. fmt = socks5ResolvePTRRequest
  99. parse = socks5ParseResponse
  100. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  101. s.connect((sockshost,socksport))
  102. if socksver == 5:
  103. s.send(socks5Hello())
  104. socks5ParseHello(s.recv(2))
  105. s.send(fmt(hostname))
  106. answer = s.recv(6)
  107. result = parse(answer)
  108. while result is None:
  109. more = s.recv(1)
  110. if not more:
  111. return None
  112. answer += more
  113. result = parse(answer)
  114. print("Got answer",result)
  115. m = s.recv(1)
  116. if m:
  117. print("Got extra data too: %r"%m)
  118. return result
  119. if __name__ == '__main__':
  120. if len(sys.argv) not in (2,3,4):
  121. print("Syntax: resolve.py [-4|-5] hostname [sockshost:socksport]")
  122. sys.exit(0)
  123. socksver = 4
  124. reverse = 0
  125. while sys.argv[1][0] == '-':
  126. if sys.argv[1] in ("-4", "-5"):
  127. socksver = int(sys.argv[1][1])
  128. del sys.argv[1]
  129. elif sys.argv[1] == '-x':
  130. reverse = 1
  131. del sys.argv[1]
  132. elif sys.argv[1] == '--':
  133. break
  134. if len(sys.argv) >= 4:
  135. print("Syntax: resolve.py [-x] [-4|-5] hostname [sockshost:socksport]")
  136. sys.exit(0)
  137. if len(sys.argv) == 3:
  138. sh,sp = parseHostAndPort(sys.argv[2])
  139. else:
  140. sh,sp = parseHostAndPort("")
  141. if reverse and socksver == 4:
  142. socksver = 5
  143. resolve(sys.argv[1], sh, sp, socksver, reverse)