python.py 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. #!/usr/bin/env python2.7
  2. # -*- coding: utf-8 -*-
  3. import socket, threading, thread, select, signal, sys, time, getopt
  4. # ==============================================================================
  5. # SCRIPT DE PROXY MULTIFILAMENTADO CON SOPORTE PARA IPV4 E IPV6
  6. # - Solucionado el error "Address already in use" con la opción IPV6_V6ONLY.
  7. # - Modificado por Gemini.
  8. # ==============================================================================
  9. # Listen
  10. # Se usan direcciones específicas para evitar errores de getaddrinfo.
  11. IPV4_ADDR = '0.0.0.0'
  12. IPV6_ADDR = '::'
  13. if sys.argv[1:]:
  14. LISTENING_PORT = sys.argv[1]
  15. else:
  16. LISTENING_PORT = 80
  17. #Pass
  18. PASS = ''
  19. # CONST
  20. BUFLEN = 4096 * 4
  21. TIMEOUT = 60
  22. DEFAULT_HOST = '127.0.0.1:22'
  23. RESPONSE = 'HTTP/1.1 101 Switching Protocols <strong>By: VPS-MX</strong>\r\n\r\n'
  24. class Server(threading.Thread):
  25. def __init__(self, host, port):
  26. threading.Thread.__init__(self)
  27. self.running = False
  28. self.host = host
  29. self.port = port
  30. self.threads = []
  31. self.threadsLock = threading.Lock()
  32. self.logLock = threading.Lock()
  33. self.soc = None
  34. self.soc_v6 = None
  35. def run(self):
  36. self.printLog("\n:-------PythonProxy-------:\n")
  37. self.printLog("Listening addr: " + IPV4_ADDR + " and " + IPV6_ADDR)
  38. self.printLog("Listening port: " + str(self.port) + "\n")
  39. self.printLog(":-------------------------:\n")
  40. # Intentar enlazar a IPv4
  41. try:
  42. self.soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  43. self.soc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  44. self.soc.settimeout(2)
  45. self.soc.bind((IPV4_ADDR, int(self.port)))
  46. self.soc.listen(0)
  47. self.printLog("Esperando conexiones IPv4 en %s:%s" % (IPV4_ADDR, self.port))
  48. except socket.error as e:
  49. self.printLog("No se pudo enlazar a IPv4 (%s)" % e)
  50. if self.soc:
  51. self.soc.close()
  52. self.soc = None
  53. # Intentar enlazar a IPv6
  54. try:
  55. self.soc_v6 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
  56. self.soc_v6.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  57. # 💡 SOLUCIÓN: Establecer la opción IPV6_V6ONLY para evitar conflictos con IPv4.
  58. # Esto fuerza al socket IPv6 a solo aceptar conexiones IPv6.
  59. self.soc_v6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
  60. self.soc_v6.settimeout(2)
  61. self.soc_v6.bind((IPV6_ADDR, int(self.port), 0, 0))
  62. self.soc_v6.listen(0)
  63. self.printLog("Esperando conexiones IPv6 en %s:%s" % (IPV6_ADDR, self.port))
  64. except socket.error as e:
  65. self.printLog("No se pudo enlazar a IPv6 (%s)" % e)
  66. if self.soc_v6:
  67. self.soc_v6.close()
  68. self.soc_v6 = None
  69. if not self.soc and not self.soc_v6:
  70. self.printLog("No se pudo iniciar el servidor. Saliendo.")
  71. return
  72. self.running = True
  73. active_sockets = []
  74. if self.soc:
  75. active_sockets.append(self.soc)
  76. if self.soc_v6:
  77. active_sockets.append(self.soc_v6)
  78. try:
  79. while self.running:
  80. try:
  81. readable, _, _ = select.select(active_sockets, [], [], 2)
  82. for sock in readable:
  83. c, addr = sock.accept()
  84. c.setblocking(1)
  85. conn = ConnectionHandler(c, self, addr)
  86. conn.start()
  87. self.addConn(conn)
  88. except socket.timeout:
  89. continue
  90. except socket.error as e:
  91. if self.running:
  92. self.printLog("Error al aceptar conexión: %s" % e)
  93. continue
  94. finally:
  95. self.running = False
  96. if self.soc:
  97. self.soc.close()
  98. if self.soc_v6:
  99. self.soc_v6.close()
  100. def printLog(self, log):
  101. self.logLock.acquire()
  102. print log
  103. self.logLock.release()
  104. def addConn(self, conn):
  105. try:
  106. self.threadsLock.acquire()
  107. if self.running:
  108. self.threads.append(conn)
  109. finally:
  110. self.threadsLock.release()
  111. def removeConn(self, conn):
  112. try:
  113. self.threadsLock.acquire()
  114. self.threads.remove(conn)
  115. finally:
  116. self.threadsLock.release()
  117. def close(self):
  118. try:
  119. self.running = False
  120. self.threadsLock.acquire()
  121. threads = list(self.threads)
  122. for c in threads:
  123. c.close()
  124. finally:
  125. self.threadsLock.release()
  126. class ConnectionHandler(threading.Thread):
  127. def __init__(self, socClient, server, addr):
  128. threading.Thread.__init__(self)
  129. self.clientClosed = False
  130. self.targetClosed = True
  131. self.client = socClient
  132. self.client_buffer = ''
  133. self.server = server
  134. self.log = 'Connection: ' + str(addr)
  135. def close(self):
  136. try:
  137. if not self.clientClosed:
  138. self.client.shutdown(socket.SHUT_RDWR)
  139. self.client.close()
  140. except:
  141. pass
  142. finally:
  143. self.clientClosed = True
  144. try:
  145. if not self.targetClosed:
  146. self.target.shutdown(socket.SHUT_RDWR)
  147. self.target.close()
  148. except:
  149. pass
  150. finally:
  151. self.targetClosed = True
  152. def run(self):
  153. try:
  154. self.client_buffer = self.client.recv(BUFLEN)
  155. hostPort = self.findHeader(self.client_buffer, 'X-Real-Host')
  156. if hostPort == '':
  157. hostPort = DEFAULT_HOST
  158. split = self.findHeader(self.client_buffer, 'X-Split')
  159. if split != '':
  160. self.client.recv(BUFLEN)
  161. if hostPort != '':
  162. passwd = self.findHeader(self.client_buffer, 'X-Pass')
  163. if len(PASS) != 0 and passwd == PASS:
  164. self.method_CONNECT(hostPort)
  165. elif len(PASS) != 0 and passwd != PASS:
  166. self.client.send('HTTP/1.1 400 WrongPass!\r\n\r\n')
  167. elif hostPort.startswith('127.0.0.1') or hostPort.startswith('localhost'):
  168. self.method_CONNECT(hostPort)
  169. else:
  170. self.client.send('HTTP/1.1 403 Forbidden!\r\n\r\n')
  171. else:
  172. self.server.printLog('- No X-Real-Host!')
  173. self.client.send('HTTP/1.1 400 NoXRealHost!\r\n\r\n')
  174. except Exception as e:
  175. self.log += ' - error: ' + str(e)
  176. self.server.printLog(self.log)
  177. pass
  178. finally:
  179. self.close()
  180. self.server.removeConn(self)
  181. def findHeader(self, head, header):
  182. aux = head.find(header + ': ')
  183. if aux == -1:
  184. return ''
  185. aux = head.find(':', aux)
  186. head = head[aux+2:]
  187. aux = head.find('\r\n')
  188. if aux == -1:
  189. return ''
  190. return head[:aux];
  191. def connect_target(self, host):
  192. i = host.find(':')
  193. if i != -1:
  194. port = int(host[i+1:])
  195. host = host[:i]
  196. else:
  197. if self.method=='CONNECT':
  198. port = 22
  199. else:
  200. port = int(sys.argv[1])
  201. (soc_family, soc_type, proto, _, address) = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM)[0]
  202. self.target = socket.socket(soc_family, soc_type, proto)
  203. self.targetClosed = False
  204. self.target.connect(address)
  205. def method_CONNECT(self, path):
  206. self.log += ' - CONNECT ' + path
  207. self.connect_target(path)
  208. self.client.sendall(RESPONSE)
  209. self.client_buffer = ''
  210. self.server.printLog(self.log)
  211. self.doCONNECT()
  212. def doCONNECT(self):
  213. socs = [self.client, self.target]
  214. count = 0
  215. error = False
  216. while True:
  217. count += 1
  218. (recv, _, err) = select.select(socs, [], socs, 3)
  219. if err:
  220. error = True
  221. if recv:
  222. for in_ in recv:
  223. try:
  224. data = in_.recv(BUFLEN)
  225. if data:
  226. if in_ is self.target:
  227. self.client.send(data)
  228. else:
  229. while data:
  230. byte = self.target.send(data)
  231. data = data[byte:]
  232. count = 0
  233. else:
  234. break
  235. except:
  236. error = True
  237. break
  238. if count == TIMEOUT:
  239. error = True
  240. if error:
  241. break
  242. def print_usage():
  243. print 'Usage: proxy.py -p <port>'
  244. print ' proxy.py -b <bindAddr> -p <port>'
  245. print ' proxy.py -b 0.0.0.0 -p 80'
  246. def parse_args(argv):
  247. global IPV4_ADDR
  248. global IPV6_ADDR
  249. global LISTENING_PORT
  250. try:
  251. opts, args = getopt.getopt(argv,"hb:p:",["bind=","port="])
  252. except getopt.GetoptError:
  253. print_usage()
  254. sys.exit(2)
  255. for opt, arg in opts:
  256. if opt == '-h':
  257. print_usage()
  258. sys.exit()
  259. elif opt in ("-b", "--bind"):
  260. pass
  261. elif opt in ("-p", "--port"):
  262. LISTENING_PORT = int(arg)
  263. def main(port=LISTENING_PORT):
  264. server = Server(None, port)
  265. server.start()
  266. while True:
  267. try:
  268. time.sleep(2)
  269. except KeyboardInterrupt:
  270. print 'Stopping...'
  271. server.close()
  272. break
  273. if __name__ == '__main__':
  274. main()