# -*- coding: utf-8 -*- # ============================================================================== # PROXY MULTIFILAMENTADO DE ALTA DISPONIBILIDAD - VERSIÓN PYTHON 3 # # CARACTERÍSTICAS PRINCIPALES: # 1. Rotación de Mensajes: Cada respuesta HTTP 101 varía cíclicamente. # 2. Soporte Dual Stack: Escucha en IPv4 (0.0.0.0) e IPv6 (::) simultáneamente. # 3. Thread-Safe: Uso de Locks para evitar colisiones entre hilos. # 4. Gestión de Logs: Rotación automática para evitar saturación de disco. # 5. Rate-Limiting: Protección básica contra ataques de inundación por IP. # ============================================================================== #screen -dmS badvpn2 /bin/badvpn-udpgw --listen-addr 127.0.0.1:7300 --max-clients 1000 --max-connections-for-client 100 #screen -dmS pydic-80 python3 /root/Pythonv1.py 8080 import socket import threading import select import sys import time import logging import logging.handlers import itertools # --- CONFIGURACIÓN DE RED --- IPV4_ADDR = '0.0.0.0' IPV6_ADDR = '::' LISTENING_PORT = int(sys.argv[1]) if sys.argv[1:] else 8080 # --- CONFIGURACIÓN DE SEGURIDAD --- MAX_CONNECTIONS = 1000 CONNECTION_COOLDOWN = 5 # Segundos entre conexiones de la misma IP TIMEOUT = 60 # Tiempo de espera para sockets BUFLEN = 16384 # 16KB de buffer para mayor velocidad # --- LISTA DE MENSAJES ROTATIVOS --- # Se enviarán en la línea de estado de la respuesta HTTP 101 MENSAJES = [ "Pfsense", "OPNsense", "VyOS", "Claro", "Windows Server", "BSD Free", "VyOS", "Altice", "Viva", "Google", "VyOS", "TNSR" ] # Inicialización del iterador cíclico y lock de seguridad mensaje_cycle = itertools.cycle(MENSAJES) cycle_lock = threading.Lock() # --- CONFIGURACIÓN DE LOGS --- LOG_FILE = 'proxy_server.log' def setup_logger(): logger = logging.getLogger("ProxyLogger") logger.setLevel(logging.INFO) formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s') # Rotación de logs: 5MB por archivo, máximo 3 backups handler = logging.handlers.RotatingFileHandler(LOG_FILE, maxBytes=5*1024*1024, backupCount=3) handler.setFormatter(formatter) # Salida a consola console = logging.StreamHandler() console.setFormatter(formatter) logger.addHandler(handler) logger.addHandler(console) return logger log = setup_logger() conn_limit = threading.Semaphore(MAX_CONNECTIONS) ip_history = {} ip_lock = threading.Lock() class ConnectionHandler(threading.Thread): def __init__(self, client_socket, addr): super().__init__(daemon=True) self.client = client_socket self.addr = addr self.target = None self.log_id = f"{addr[0]}:{addr[1]}" def finish(self): """Cierra todos los recursos de la conexión de forma segura.""" for s in [self.client, self.target]: if s: try: s.close() except: pass conn_limit.release() def run(self): try: # 1. Leer petición inicial data = self.client.recv(BUFLEN) if not data: return # 2. Determinar destino (Header X-Real-Host o Default) headers = data.decode('latin-1', errors='ignore') target_info = self.extract_header(headers, 'X-Real-Host') or "127.0.0.1:22" # 3. Obtener mensaje rotativo de forma segura with cycle_lock: msg = next(mensaje_cycle) # 4. Intentar conectar al destino if not self.connect_to_target(target_info): return # 5. Enviar respuesta con el mensaje rotado resp = (f"HTTP/1.1 101 {msg}\r\n" f"Connection: Upgrade\r\n" f"Upgrade: websocket\r\n\r\n").encode('utf-8') self.client.sendall(resp) log.info(f"[{self.log_id}] Conectado a {target_info} | Mensaje: {msg}") # 6. Iniciar túnel bidireccional self.bridge() except Exception as e: log.error(f"[{self.log_id}] Error: {e}") finally: self.finish() def extract_header(self, text, header_name): for line in text.split('\r\n'): if line.lower().startswith(header_name.lower() + ":"): return line.split(':', 1)[1].strip() return None def connect_to_target(self, target_str): try: parts = target_str.split(':') host = parts[0] port = int(parts[1]) if len(parts) > 1 else 22 # Soporte IPv4 e IPv6 automático infos = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM) for res in infos: af, socktype, proto, canonname, sa = res try: self.target = socket.socket(af, socktype, proto) self.target.settimeout(10) self.target.connect(sa) return True except: continue return False except: return False def bridge(self): """Mueve datos entre cliente y destino usando select.""" sockets = [self.client, self.target] while True: readable, _, error = select.select(sockets, [], sockets, TIMEOUT) if error or not readable: break for s in readable: other = self.target if s is self.client else self.client try: chunk = s.recv(BUFLEN) if not chunk: return other.sendall(chunk) except: return def main(): # Crear sockets de escucha listeners = [] for addr_info in [(socket.AF_INET, IPV4_ADDR), (socket.AF_INET6, IPV6_ADDR)]: try: s = socket.socket(addr_info[0], socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) if addr_info[0] == socket.AF_INET6: s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1) s.bind((addr_info[1], LISTENING_PORT)) s.listen(128) listeners.append(s) log.info(f"Escuchando en {addr_info[1]}:{LISTENING_PORT}") except Exception as e: log.warning(f"No se pudo abrir socket en {addr_info[1]}: {e}") if not listeners: log.critical("No hay sockets disponibles. Saliendo.") return log.info("Servidor Proxy iniciado. Presiona Ctrl+C para salir.") try: while True: r, _, _ = select.select(listeners, [], []) for s in r: client, addr = s.accept() # Control de frecuencia por IP ip = addr[0] with ip_lock: now = time.time() last = ip_history.get(ip, 0) if now - last < CONNECTION_COOLDOWN: client.close() continue ip_history[ip] = now # Control de límite total if not conn_limit.acquire(blocking=False): log.warning(f"Límite de conexiones alcanzado ({MAX_CONNECTIONS})") client.close() continue # Iniciar manejador ConnectionHandler(client, addr).start() except KeyboardInterrupt: log.info("Cerrando servidor...") finally: for s in listeners: s.close() if __name__ == "__main__": main()