#!/usr/bin/env python2.7 # -*- coding: utf-8 -*- import socket, threading, thread, select, signal, sys, time, getopt # ============================================================================== # SCRIPT DE PROXY MULTIFILAMENTADO CON SOPORTE PARA IPV4 E IPV6 # - Solucionado el error "Address already in use" con la opción IPV6_V6ONLY. # - Modificado por Gemini. # ============================================================================== # Listen # Se usan direcciones específicas para evitar errores de getaddrinfo. IPV4_ADDR = '0.0.0.0' IPV6_ADDR = '::' if sys.argv[1:]: LISTENING_PORT = sys.argv[1] else: LISTENING_PORT = 80 #Pass PASS = '' # CONST BUFLEN = 4096 * 4 TIMEOUT = 60 DEFAULT_HOST = '127.0.0.1:22' RESPONSE = 'HTTP/1.1 101 Switching Protocols By: VPS-MX\r\n\r\n' class Server(threading.Thread): def __init__(self, host, port): threading.Thread.__init__(self) self.running = False self.host = host self.port = port self.threads = [] self.threadsLock = threading.Lock() self.logLock = threading.Lock() self.soc = None self.soc_v6 = None def run(self): self.printLog("\n:-------PythonProxy-------:\n") self.printLog("Listening addr: " + IPV4_ADDR + " and " + IPV6_ADDR) self.printLog("Listening port: " + str(self.port) + "\n") self.printLog(":-------------------------:\n") # Intentar enlazar a IPv4 try: self.soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.soc.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.soc.settimeout(2) self.soc.bind((IPV4_ADDR, int(self.port))) self.soc.listen(0) self.printLog("Esperando conexiones IPv4 en %s:%s" % (IPV4_ADDR, self.port)) except socket.error as e: self.printLog("No se pudo enlazar a IPv4 (%s)" % e) if self.soc: self.soc.close() self.soc = None # Intentar enlazar a IPv6 try: self.soc_v6 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) self.soc_v6.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 💡 SOLUCIÓN: Establecer la opción IPV6_V6ONLY para evitar conflictos con IPv4. # Esto fuerza al socket IPv6 a solo aceptar conexiones IPv6. self.soc_v6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1) self.soc_v6.settimeout(2) self.soc_v6.bind((IPV6_ADDR, int(self.port), 0, 0)) self.soc_v6.listen(0) self.printLog("Esperando conexiones IPv6 en %s:%s" % (IPV6_ADDR, self.port)) except socket.error as e: self.printLog("No se pudo enlazar a IPv6 (%s)" % e) if self.soc_v6: self.soc_v6.close() self.soc_v6 = None if not self.soc and not self.soc_v6: self.printLog("No se pudo iniciar el servidor. Saliendo.") return self.running = True active_sockets = [] if self.soc: active_sockets.append(self.soc) if self.soc_v6: active_sockets.append(self.soc_v6) try: while self.running: try: readable, _, _ = select.select(active_sockets, [], [], 2) for sock in readable: c, addr = sock.accept() c.setblocking(1) conn = ConnectionHandler(c, self, addr) conn.start() self.addConn(conn) except socket.timeout: continue except socket.error as e: if self.running: self.printLog("Error al aceptar conexión: %s" % e) continue finally: self.running = False if self.soc: self.soc.close() if self.soc_v6: self.soc_v6.close() def printLog(self, log): self.logLock.acquire() print log self.logLock.release() def addConn(self, conn): try: self.threadsLock.acquire() if self.running: self.threads.append(conn) finally: self.threadsLock.release() def removeConn(self, conn): try: self.threadsLock.acquire() self.threads.remove(conn) finally: self.threadsLock.release() def close(self): try: self.running = False self.threadsLock.acquire() threads = list(self.threads) for c in threads: c.close() finally: self.threadsLock.release() class ConnectionHandler(threading.Thread): def __init__(self, socClient, server, addr): threading.Thread.__init__(self) self.clientClosed = False self.targetClosed = True self.client = socClient self.client_buffer = '' self.server = server self.log = 'Connection: ' + str(addr) def close(self): try: if not self.clientClosed: self.client.shutdown(socket.SHUT_RDWR) self.client.close() except: pass finally: self.clientClosed = True try: if not self.targetClosed: self.target.shutdown(socket.SHUT_RDWR) self.target.close() except: pass finally: self.targetClosed = True def run(self): try: self.client_buffer = self.client.recv(BUFLEN) hostPort = self.findHeader(self.client_buffer, 'X-Real-Host') if hostPort == '': hostPort = DEFAULT_HOST split = self.findHeader(self.client_buffer, 'X-Split') if split != '': self.client.recv(BUFLEN) if hostPort != '': passwd = self.findHeader(self.client_buffer, 'X-Pass') if len(PASS) != 0 and passwd == PASS: self.method_CONNECT(hostPort) elif len(PASS) != 0 and passwd != PASS: self.client.send('HTTP/1.1 400 WrongPass!\r\n\r\n') elif hostPort.startswith('127.0.0.1') or hostPort.startswith('localhost'): self.method_CONNECT(hostPort) else: self.client.send('HTTP/1.1 403 Forbidden!\r\n\r\n') else: self.server.printLog('- No X-Real-Host!') self.client.send('HTTP/1.1 400 NoXRealHost!\r\n\r\n') except Exception as e: self.log += ' - error: ' + str(e) self.server.printLog(self.log) pass finally: self.close() self.server.removeConn(self) def findHeader(self, head, header): aux = head.find(header + ': ') if aux == -1: return '' aux = head.find(':', aux) head = head[aux+2:] aux = head.find('\r\n') if aux == -1: return '' return head[:aux]; def connect_target(self, host): i = host.find(':') if i != -1: port = int(host[i+1:]) host = host[:i] else: if self.method=='CONNECT': port = 22 else: port = int(sys.argv[1]) (soc_family, soc_type, proto, _, address) = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM)[0] self.target = socket.socket(soc_family, soc_type, proto) self.targetClosed = False self.target.connect(address) def method_CONNECT(self, path): self.log += ' - CONNECT ' + path self.connect_target(path) self.client.sendall(RESPONSE) self.client_buffer = '' self.server.printLog(self.log) self.doCONNECT() def doCONNECT(self): socs = [self.client, self.target] count = 0 error = False while True: count += 1 (recv, _, err) = select.select(socs, [], socs, 3) if err: error = True if recv: for in_ in recv: try: data = in_.recv(BUFLEN) if data: if in_ is self.target: self.client.send(data) else: while data: byte = self.target.send(data) data = data[byte:] count = 0 else: break except: error = True break if count == TIMEOUT: error = True if error: break def print_usage(): print 'Usage: proxy.py -p ' print ' proxy.py -b -p ' print ' proxy.py -b 0.0.0.0 -p 80' def parse_args(argv): global IPV4_ADDR global IPV6_ADDR global LISTENING_PORT try: opts, args = getopt.getopt(argv,"hb:p:",["bind=","port="]) except getopt.GetoptError: print_usage() sys.exit(2) for opt, arg in opts: if opt == '-h': print_usage() sys.exit() elif opt in ("-b", "--bind"): pass elif opt in ("-p", "--port"): LISTENING_PORT = int(arg) def main(port=LISTENING_PORT): server = Server(None, port) server.start() while True: try: time.sleep(2) except KeyboardInterrupt: print 'Stopping...' server.close() break if __name__ == '__main__': main()