PageRenderTime 46ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/Tribler/community/tunnel/Socks5/conversion.py

https://gitlab.com/billyprice1/tribler
Python | 256 lines | 137 code | 53 blank | 66 comment | 14 complexity | b0c0752cf41dc8675fcc9440d56e8d6c MD5 | raw file
  1. import struct
  2. import socket
  3. # Some constants used in the RFC 1928 specification
  4. SOCKS_VERSION = 0x05
  5. ADDRESS_TYPE_IPV4 = 0x01
  6. ADDRESS_TYPE_DOMAIN_NAME = 0x03
  7. ADDRESS_TYPE_IPV6 = 0x04
  8. REQ_CMD_CONNECT = 0x01
  9. REQ_CMD_BIND = 0x02
  10. REQ_CMD_UDP_ASSOCIATE = 0x03
  11. REP_SUCCEEDED = 0x00
  12. REP_GENERAL_SOCKS_SERVER_FAIL = 0x01
  13. REP_CONNECTION_NOT_ALLOWED_BY_RULE_SET = 0x02
  14. REP_NETWORK_UNREACHABLE = 0x03
  15. REP_HOST_UNREACHABLE = 0x04
  16. REP_CONNECTION_REFUSED = 0x05
  17. REP_TTL_EXPIRED = 0x06
  18. REP_COMMAND_NOT_SUPPORTED = 0x07
  19. REP_ADDRESS_TYPE_NOT_SUPPORTED = 0x08
  20. class MethodRequest(object):
  21. def __init__(self, version, methods):
  22. self.version = version
  23. self.methods = methods
  24. class Request(object):
  25. def __init__(self, version, cmd, rsv, address_type, destination_address,
  26. destination_port):
  27. self.version = version
  28. self.cmd = cmd
  29. self.rsv = rsv
  30. self.address_type = address_type
  31. self.destination_host = destination_address
  32. self.destination_port = destination_port
  33. @property
  34. def destination(self):
  35. """
  36. The destination address as a tuple
  37. @rtype: (str, int)
  38. """
  39. return self.destination_host, self.destination_port
  40. class UdpRequest(object):
  41. """
  42. @param rsv: the reserved bits in the SOCKS protocol
  43. @param frag:
  44. @param address_type: whether we deal with an IPv4 or IPv6 address
  45. @param str destination_address: the destination host
  46. @param int destination_port: the destination port
  47. @param str payload: the payload
  48. """
  49. def __init__(self, rsv, frag, address_type, destination_address,
  50. destination_port, payload):
  51. self.rsv = rsv
  52. self.frag = frag
  53. self.address_type = address_type
  54. self.destination_host = destination_address
  55. self.destination_port = destination_port
  56. self.payload = payload
  57. @property
  58. def destination(self):
  59. """
  60. The destination address as a tuple
  61. @rtype: (str, int)
  62. """
  63. return self.destination_host, self.destination_port
  64. def decode_methods_request(offset, data):
  65. """
  66. Try to decodes a METHOD request
  67. @param int offset: the offset to start in the data
  68. @param str data: the serialised data to decode from
  69. @return: Tuple (offset, None) on failure, else (new_offset, MethodRequest)
  70. @rtype: (int, None|MethodRequest)
  71. """
  72. # Check if we have enough bytes
  73. if len(data) - offset < 2:
  74. return offset, None
  75. (version, number_of_methods) = struct.unpack_from("!BB", data, offset)
  76. # We only know how to handle Socks5 protocol
  77. if not version == SOCKS_VERSION:
  78. return offset, None
  79. offset += 2
  80. methods = set([])
  81. for i in range(number_of_methods):
  82. method, = struct.unpack_from("!B", data, offset)
  83. methods.add(method)
  84. offset += 1
  85. return offset, MethodRequest(version, methods)
  86. def encode_method_selection_message(version, method):
  87. """
  88. Serialise a Method Selection message
  89. @param version: the SOCKS5 version
  90. @param method: the authentication method to select
  91. @return: the serialised format
  92. @rtype: str
  93. """
  94. return struct.pack("!BB", version, method)
  95. def __encode_address(address_type, address):
  96. if address_type == ADDRESS_TYPE_IPV4:
  97. data = socket.inet_aton(address)
  98. elif address_type == ADDRESS_TYPE_IPV6:
  99. raise IPV6AddrError()
  100. elif address_type == ADDRESS_TYPE_DOMAIN_NAME:
  101. data = struct.pack("!B", len(address)) + address
  102. else:
  103. raise ValueError(
  104. "address_type must be either IPv4, IPv6 or a domain name")
  105. return data
  106. def __decode_address(address_type, offset, data):
  107. if address_type == ADDRESS_TYPE_IPV4:
  108. destination_address = socket.inet_ntoa(data[offset:offset + 4])
  109. offset += 4
  110. elif address_type == ADDRESS_TYPE_DOMAIN_NAME:
  111. domain_length, = struct.unpack_from("!B", data, offset)
  112. offset += 1
  113. destination_address = data[offset:offset + domain_length]
  114. offset += domain_length
  115. elif address_type == ADDRESS_TYPE_IPV6:
  116. raise IPV6AddrError()
  117. else:
  118. raise ValueError("Unsupported address type %r" % address_type )
  119. return offset, destination_address
  120. def decode_request(orig_offset, data):
  121. """
  122. Try to decode a SOCKS5 request
  123. @param int orig_offset: the offset to start decoding in the data
  124. @param str data: the raw data
  125. @return: tuple (new_offset, Request) or (original_offset, None) on failure
  126. @rtype: (int, Request|None)
  127. """
  128. offset = orig_offset
  129. # Check if we have enough bytes
  130. if len(data) - offset < 4:
  131. return orig_offset, None
  132. version, cmd, rsv, address_type = struct.unpack_from("!BBBB", data, offset)
  133. offset += 4
  134. assert version == SOCKS_VERSION, (version, SOCKS_VERSION)
  135. assert rsv == 0
  136. offset, destination_address = __decode_address(address_type, offset, data)
  137. # Check if we could decode address, if not bail out
  138. if not destination_address:
  139. return orig_offset, None
  140. # Check if we have enough bytes
  141. if len(data) - offset < 2:
  142. return orig_offset, None
  143. destination_port, = struct.unpack_from("!H", data, offset)
  144. offset += 2
  145. return offset, Request(version, cmd, rsv, address_type,
  146. destination_address, destination_port)
  147. def encode_reply(version, rep, rsv, address_type, bind_address, bind_port):
  148. """
  149. Encode a REPLY
  150. @param int version: SOCKS5 version
  151. @param int rep: the response
  152. @param int rsv: reserved bytes
  153. @param address_type: the address type of the bind address
  154. @param bind_address: the bind address host
  155. @param bind_port: the bind address port
  156. @return:
  157. """
  158. data = struct.pack("BBBB", version, rep, rsv, address_type)
  159. data += __encode_address(address_type, bind_address)
  160. data += struct.pack("!H", bind_port)
  161. return data
  162. def decode_udp_packet(data):
  163. """
  164. Decodes a SOCKS5 UDP packet
  165. @param str data: the raw packet data
  166. @return: An UdpRequest object containing the parsed data
  167. @rtype: UdpRequest
  168. """
  169. offset = 0
  170. (rsv, frag, address_type) = struct.unpack_from("!HBB", data, offset)
  171. offset += 4
  172. offset, destination_address = __decode_address(address_type, offset, data)
  173. destination_port, = struct.unpack_from("!H", data, offset)
  174. offset += 2
  175. payload = data[offset:]
  176. return UdpRequest(rsv, frag, address_type, destination_address,
  177. destination_port, payload)
  178. def encode_udp_packet(rsv, frag, address_type, address, port, payload):
  179. """
  180. Encodes a SOCKS5 UDP packet
  181. @param rsv: reserved bytes
  182. @param frag: fragment
  183. @param address_type: the address's type
  184. @param address: address host
  185. @param port: address port
  186. @param payload: the original UDP payload
  187. @return: serialised byte string
  188. @rtype: str
  189. """
  190. strings = [
  191. struct.pack("!HBB", rsv, frag, address_type),
  192. __encode_address(address_type, address),
  193. struct.pack("!H", port),
  194. payload
  195. ]
  196. return ''.join(strings)
  197. class IPV6AddrError(NotImplementedError):
  198. def __str__(self):
  199. return "IPV6 support not implemented"