main.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. try:
  2. import gevent
  3. import gevent.monkey
  4. gevent.monkey.patch_all(dns=gevent.version_info[0] >= 1)
  5. except ImportError:
  6. print("Warning: gevent not found, fallback to traditional implementation.")
  7. import socket
  8. import socketserver
  9. import struct
  10. import select
  11. from sys import argv
  12. import chnroutes_data
  13. DEFAULT_LISTEN_HOST = "127.0.0.1"
  14. DEFAULT_LISTEN_PORT = 8964
  15. DEFAULT_UPSTREAM_HOST = "127.0.0.1"
  16. DEFAULT_UPSTREAM_PORT = 1984
  17. SOCKS5_VER = 5
  18. SOCKS5_METHOD = 0
  19. SOCKS5_CMD_CONNECT = 1
  20. SOCKS5_CMD_BIND = 2
  21. SOCKS5_CMD_UDP_ASSOCIATE = 3
  22. SOCKS5_ATYP_IPV4 = 1
  23. SOCKS5_ATYP_DOMAINNAME = 3
  24. SOCKS5_ATYP_IPV6 = 4
  25. SOCKS5_REP_SUCCESS = 0
  26. SOCKS5_RESERVED = 0
  27. _dns = {}
  28. def dns(addr):
  29. ip = _dns.get(addr, "")
  30. if not ip:
  31. ip = socket.getaddrinfo(addr, None)[0][4][0]
  32. _dns[addr] = ip
  33. return ip
  34. class UpstreamProxyServer():
  35. def __init__(self, addr, port):
  36. self._sock = socket.create_connection((addr, port))
  37. def connect(self, target):
  38. hello = bytes((SOCKS5_VER, 1, SOCKS5_METHOD))
  39. self._sock.sendall(hello)
  40. ver, method = self._sock.recv(2)
  41. connect = bytes((SOCKS5_VER, SOCKS5_CMD_CONNECT, SOCKS5_RESERVED,
  42. target.atyp)) + target.raw_addr + target.raw_port
  43. self._sock.sendall(connect)
  44. reply = self._sock.recv(4 + 4 + 2)
  45. assert reply[1] == 0
  46. def recv(self, buf):
  47. return self._sock.recv(buf)
  48. def send(self, buf):
  49. return self._sock.send(buf)
  50. def close(self):
  51. return self._sock.close()
  52. def fileno(self):
  53. return self._sock.fileno()
  54. class DirectConnectProxyServer(UpstreamProxyServer):
  55. def __init__(self, addr, port):
  56. pass
  57. def connect(self, target):
  58. self._sock = socket.create_connection((target.addr, target.port))
  59. class Target():
  60. def __init__(self, atyp, addr):
  61. self.atyp = atyp
  62. if self.atyp == SOCKS5_ATYP_IPV4:
  63. self.length = 4
  64. self.raw_addr = addr[:self.length]
  65. self.addr = socket.inet_ntoa(self.raw_addr)
  66. self.raw_port = addr[self.length:self.length + 2]
  67. elif self.atyp == SOCKS5_ATYP_IPV6:
  68. self.length = 16
  69. self.raw_addr = addr[:self.length]
  70. self.addr = socket.inet_ntop(socket.AF_INET6, self.raw_addr)
  71. self.raw_port = addr[self.length:self.length + 2]
  72. elif self.atyp == SOCKS5_ATYP_DOMAINNAME:
  73. self.length = addr[0]
  74. self.raw_addr = addr[0:1 + self.length]
  75. self.addr = self.raw_addr[1:].decode("UTF-8")
  76. self.raw_port = addr[1 + self.length:1 + self.length + 2]
  77. self.port = struct.unpack("!H", self.raw_port)[0]
  78. class ThreadingTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
  79. allow_reuse_address = True
  80. class MySocksServer(socketserver.StreamRequestHandler):
  81. @staticmethod
  82. def _is_private(target):
  83. if target.atyp == SOCKS5_ATYP_DOMAINNAME:
  84. try:
  85. ip = dns(target.addr)
  86. except socket.gaierror:
  87. return False
  88. else:
  89. ip = target.addr
  90. f = struct.unpack('!I', socket.inet_pton(socket.AF_INET, ip))[0]
  91. private = (
  92. [2130706432, 4278190080], # 127.0.0.0, 255.0.0.0 http://tools.ietf.org/html/rfc3330
  93. [3232235520, 4294901760], # 192.168.0.0, 255.255.0.0 http://tools.ietf.org/html/rfc1918
  94. [2886729728, 4293918720], # 172.16.0.0, 255.240.0.0 http://tools.ietf.org/html/rfc1918
  95. [167772160, 4278190080], # 10.0.0.0, 255.0.0.0 http://tools.ietf.org/html/rfc1918
  96. )
  97. for net in private:
  98. if (f & net[1] == net[0]):
  99. return True
  100. return False
  101. @staticmethod
  102. def _in_prc(target):
  103. def in_subnet(ip, subnet, netmask):
  104. return (ip & netmask) == (subnet & netmask)
  105. def str_ip_to_num(ip):
  106. return struct.unpack('!I', socket.inet_aton(ip))[0]
  107. if target.atyp == SOCKS5_ATYP_DOMAINNAME:
  108. try:
  109. ip = str_ip_to_num(dns(target.addr))
  110. except socket.gaierror:
  111. return False
  112. else:
  113. ip = struct.unpack("!I", target.raw_addr)[0]
  114. for i in chnroutes_data.CHINESE_SUBNETS:
  115. subnet = str_ip_to_num(i[0])
  116. netmask = str_ip_to_num(i[1])
  117. if in_subnet(ip, subnet, netmask):
  118. return True
  119. return False
  120. @staticmethod
  121. def _sendall(sock, data):
  122. sent_bytes = 0
  123. while sent_bytes < len(data):
  124. sent = sock.send(data[sent_bytes:])
  125. if sent < 0:
  126. return sent
  127. sent_bytes += sent
  128. return sent_bytes
  129. def handle_proxy_tcp(self, target):
  130. connect_msg = "Connect to %s:%d %s"
  131. connect_fmt = [target.addr, target.port]
  132. if self._in_prc(target) or self._is_private(target):
  133. proxy_server = DirectConnectProxyServer
  134. connect_fmt.append("(PRC)")
  135. else:
  136. proxy_server = UpstreamProxyServer
  137. connect_fmt.append("(Not in PRC)")
  138. print(connect_msg % tuple(connect_fmt))
  139. proxy = proxy_server(DEFAULT_UPSTREAM_HOST, DEFAULT_UPSTREAM_PORT)
  140. proxy.connect(target)
  141. fdset = (self.request, proxy)
  142. try:
  143. while True:
  144. r, w, e = select.select(fdset, (), ())
  145. if self.request in r:
  146. data = self.request.recv(4096)
  147. if len(data) <= 0:
  148. break
  149. sent = self._sendall(proxy, data)
  150. if sent < len(data):
  151. raise IOError("Failed to sent all data.")
  152. if proxy in r:
  153. data = proxy.recv(4096)
  154. if len(data) <= 0:
  155. break
  156. sent = self._sendall(self.request, data)
  157. if sent < len(data):
  158. raise IOError("Failed to sent all data.")
  159. except Exception as e:
  160. print(e)
  161. finally:
  162. self.request.close()
  163. proxy.close()
  164. pass
  165. def handle(self):
  166. ver, nmethods, methods = self.request.recv(3)
  167. if ver != SOCKS5_VER:
  168. self.request.close()
  169. return
  170. self.request.sendall(bytes((SOCKS5_VER, SOCKS5_METHOD)))
  171. ver, cmd, rsv, atyp = self.request.recv(4)
  172. if ver != SOCKS5_VER:
  173. self.request.close()
  174. return
  175. if cmd == SOCKS5_CMD_CONNECT:
  176. pass
  177. elif cmd == SOCKS5_CMD_BIND:
  178. self.request.close()
  179. return
  180. elif cmd == SOCKS5_CMD_UDP_ASSOCIATE:
  181. self.request.close()
  182. return
  183. if atyp not in (SOCKS5_ATYP_IPV4, SOCKS5_ATYP_IPV6, SOCKS5_ATYP_DOMAINNAME):
  184. self.request.close()
  185. return
  186. dst_addr = self.request.recv(256) # 1 (domain length) + 253 (max domain length) + 2 (port)
  187. target = Target(atyp, dst_addr)
  188. reply = bytes((SOCKS5_VER, SOCKS5_REP_SUCCESS, SOCKS5_RESERVED, SOCKS5_ATYP_IPV4))
  189. reply += socket.inet_aton('0.0.0.0') + struct.pack("!H", 0)
  190. self.request.sendall(reply)
  191. self.handle_proxy_tcp(target)
  192. if __name__ == "__main__":
  193. host = DEFAULT_LISTEN_HOST
  194. port = DEFAULT_LISTEN_PORT
  195. if len(argv) == 1:
  196. pass
  197. elif len(argv) == 2:
  198. port = int(argv[1])
  199. elif len(argv) == 3:
  200. host = argv[1]
  201. port = int(argv[2])
  202. server = ThreadingTCPServer((host, port), MySocksServer)
  203. server.serve_forever()