# -*- coding: utf-8 -*- import socket import threading import select import sys import time import itertools import os # --- CONFIGURACIÓN BASE --- LISTENING_PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 8080 SSH_HOST = '127.0.0.1' SSH_PORT = 22 # Compatible con Dropbear o OpenSSH local LOG_FILE = "/root/proxy.log" MAX_LOG_SIZE = 10 * 1024 * 1024 # 10MB # --- CONFIGURACIÓN DE SEGURIDAD AVANZADA --- MAX_CONNECTIONS = 1000 CONNECTION_COOLDOWN = 0.5 TIMEOUT = 60 BUFLEN = 16384 # --- FILTRADO DE ACCESO --- ALLOWED_IPS = [] BLOCKED_HOSTS = ['ads.doubleclick.net', 'telemetry.microsoft.com'] # --- SECCIÓN DE CUSTOM HEADERS (Inyectados en la respuesta 101) --- CUSTOM_HEADERS = { "Server": "nginx/1.21.0", "X-Forwarded-For": "127.0.0.1", "Content-Type": "text/html; charset=UTF-8", "Proxy-Connection": "keep-alive", "Cache-Control": "no-cache", "X-Real-IP": "127.0.0.1" } # --- MENSAJES ROTATIVOS (Status 101) --- MENSAJES = [ "🚀 CONEXION ESTABLECIDA", "🛡️ SEGURIDAD ACTIVA", "🔋 OPTIMIZACION DROPBEAR", "🌐 ACCESO NETMOD OK", "Pfsense", "OPNsense", "VyOS", "Claro", "Windows Server", "BSD Free", "VyOS", "Altice", "Viva", "Google", "VyOS", "TNSR" ] mensaje_cycle = itertools.cycle(MENSAJES) cycle_lock = threading.Lock() # --- SISTEMA DE LOGS CON AUTO-LIMPIEZA --- def log(msg, addr=None): # Verificar tamaño del log y limpiar si excede el límite if os.path.exists(LOG_FILE) and os.path.getsize(LOG_FILE) > MAX_LOG_SIZE: with open(LOG_FILE, 'w') as f: f.write(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] LOG REINICIADO (Límite 10MB alcanzado)\n") timestamp = time.strftime("%Y-%m-%d %H:%M:%S") client_info = f" [{addr[0]}]" if addr else "" log_entry = f"[{timestamp}]{client_info} {msg}\n" # Escribir en archivo y consola try: with open(LOG_FILE, 'a') as f: f.write(log_entry) print(log_entry.strip()) except Exception as e: print(f"Error escribiendo log: {e}") # --- GESTIÓN DE ESTADO --- active_connections = 0 conn_lock = threading.Lock() ip_cooldowns = {} class ConnectionHandler(threading.Thread): def __init__(self, client_socket, addr): super().__init__(daemon=True) self.client = client_socket self.addr = addr self.target = None def build_http_response(self, status_msg): headers_str = "".join([f"{k}: {v}\r\n" for k, v in CUSTOM_HEADERS.items()]) response = ( f"HTTP/1.1 101 {status_msg}\r\n" f"{headers_str}" f"Connection: Upgrade\r\n" f"Upgrade: websocket\r\n\r\n" ) return response.encode('utf-8') def run(self): global active_connections try: log("--- Nueva conexión recibida ---", self.addr) # 1. Validación de IP if ALLOWED_IPS and self.addr[0] not in ALLOWED_IPS: log("🚫 Conexión rechazada: IP no autorizada", self.addr) return # 2. Control de Rate-Limiting / Cooldown now = time.time() if self.addr[0] in ip_cooldowns and (now - ip_cooldowns[self.addr[0]]) < CONNECTION_COOLDOWN: log("⚠️ Conexión rechazada por rate-limiting", self.addr) return ip_cooldowns[self.addr[0]] = now # 3. Payload inicial self.client.settimeout(10) payload = self.client.recv(BUFLEN) if not payload: return # 4. Conexión al destino (Dropbear/SSH) with cycle_lock: current_status = next(mensaje_cycle) log(f"Intentando conectar a {SSH_HOST}:{SSH_PORT}...", self.addr) # Soporte IPv4/IPv6 para la conexión interna self.target = socket.create_connection((SSH_HOST, SSH_PORT), timeout=TIMEOUT) log("Conexión exitosa al destino SSH", self.addr) # 5. Respuesta HTTP 101 self.client.sendall(self.build_http_response(current_status)) log(f"✅ Protocolo Upgrade: {current_status}", self.addr) # 6. Túnel bidireccional self.tunnel() except Exception as e: log(f"❌ Error: {str(e)}", self.addr) finally: with conn_lock: active_connections -= 1 self.cleanup() def tunnel(self): self.client.settimeout(None) self.target.settimeout(None) sockets = [self.client, self.target] while True: readable, _, error = select.select(sockets, [], sockets, TIMEOUT) if error or not readable: break for s in readable: try: data = s.recv(BUFLEN) if not data: return dest = self.target if s is self.client else self.client dest.sendall(data) except: return def cleanup(self): for s in [self.client, self.target]: if s: try: s.close() except: pass def main(): global active_connections # Configuración de Socket Dual (IPv4 e IPv6) try: if socket.has_dualstack_ipv6(): server = socket.create_server(('::', LISTENING_PORT), family=socket.AF_INET6, dualstack_ipv6=True) else: server = socket.create_server(('0.0.0.0', LISTENING_PORT), family=socket.AF_INET) server.listen(200) log(f"🔥 Servidor Robusto Iniciado en Puerto {LISTENING_PORT} (Dual IPv4/IPv6)") log(f"🛡️ Logs: {LOG_FILE} (Autolimitado a 10MB)") while True: client, addr = server.accept() with conn_lock: if active_connections >= MAX_CONNECTIONS: log("⛔ Máximo de conexiones alcanzado.", addr) client.close() continue active_connections += 1 ConnectionHandler(client, addr).start() except Exception as e: log(f"❌ Error crítico en servidor: {e}") finally: server.close() if __name__ == "__main__": main()