|
@@ -7,45 +7,58 @@ import sys
|
|
|
import time
|
|
import time
|
|
|
import itertools
|
|
import itertools
|
|
|
import os
|
|
import os
|
|
|
|
|
+import ssl
|
|
|
|
|
|
|
|
# --- CONFIGURACIÓN BASE ---
|
|
# --- CONFIGURACIÓN BASE ---
|
|
|
-LISTENING_PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 8080
|
|
|
|
|
|
|
+LISTENING_PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 443
|
|
|
SSH_HOST = '127.0.0.1'
|
|
SSH_HOST = '127.0.0.1'
|
|
|
-SSH_PORT = 22 # Compatible con Dropbear (22 o 223) y OpenSSH
|
|
|
|
|
|
|
+SSH_PORT = 22 # Asegúrate de que este es tu puerto SSH/Dropbear
|
|
|
LOG_FILE = "/root/proxy.log"
|
|
LOG_FILE = "/root/proxy.log"
|
|
|
-MAX_LOG_SIZE = 10 * 1024 * 1024 # 10MB
|
|
|
|
|
|
|
+MAX_LOG_SIZE = 10 * 1024 * 1024
|
|
|
|
|
+
|
|
|
|
|
+# --- CONFIGURACIÓN SSL/TLS ---
|
|
|
|
|
+USE_SSL = True
|
|
|
|
|
+CERT_FILE = "/root/cert.pem"
|
|
|
|
|
+KEY_FILE = "/root/key.pem"
|
|
|
|
|
|
|
|
# --- CONFIGURACIÓN DE SEGURIDAD AVANZADA ---
|
|
# --- CONFIGURACIÓN DE SEGURIDAD AVANZADA ---
|
|
|
-MAX_CONNECTIONS = 100
|
|
|
|
|
|
|
+MAX_CONNECTIONS = 150 # Ligeramente aumentado
|
|
|
CONNECTION_COOLDOWN = 0.7
|
|
CONNECTION_COOLDOWN = 0.7
|
|
|
-TIMEOUT = 60
|
|
|
|
|
BUFLEN = 16384
|
|
BUFLEN = 16384
|
|
|
-
|
|
|
|
|
-# --- FILTRADO DE ACCESO Y AUTO-BANEO ---
|
|
|
|
|
-ALLOWED_IPS = []
|
|
|
|
|
-BLOCKED_HOSTS = ['ads.doubleclick.net', 'telemetry.microsoft.com']
|
|
|
|
|
-AUTO_BAN_STRIKES = 3 # Intentos de flood antes de banear la IP en memoria
|
|
|
|
|
-BAN_TIME = 3600 # Tiempo de baneo en segundos (3600s = 1 hora)
|
|
|
|
|
-banned_ips_memory = {} # Diccionario: guarda la IP y su tiempo de expiración
|
|
|
|
|
|
|
+AUTO_BAN_STRIKES = 3
|
|
|
|
|
+BAN_TIME = 3600
|
|
|
|
|
+banned_ips_memory = {}
|
|
|
ip_strikes = {}
|
|
ip_strikes = {}
|
|
|
|
|
+ALLOWED_IPS = []
|
|
|
|
|
|
|
|
-# --- SECCIÓN DE CUSTOM HEADERS (Inyectados en la respuesta) ---
|
|
|
|
|
|
|
+# --- RESPUESTA FAKE WEB (ANTI ACTIVE PROBING) ---
|
|
|
|
|
+FAKE_WEB_RESPONSE = (
|
|
|
|
|
+ b"HTTP/1.1 200 OK\r\n"
|
|
|
|
|
+ b"Server: nginx/1.21.0\r\n"
|
|
|
|
|
+ b"Content-Type: text/html; charset=UTF-8\r\n"
|
|
|
|
|
+ b"Connection: close\r\n\r\n"
|
|
|
|
|
+ b"<!DOCTYPE html>\n<html>\n<head><title>Bienvenido</title></head>\n"
|
|
|
|
|
+ b"<body style='text-align:center; padding:50px; font-family:sans-serif;'>\n"
|
|
|
|
|
+ b"<h1>Hola</h1>\n<p>Servicio en funcionamiento.</p>\n"
|
|
|
|
|
+ b"</body>\n</html>\n"
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+# --- CUSTOM HEADERS PARA VPN ---
|
|
|
CUSTOM_HEADERS = {
|
|
CUSTOM_HEADERS = {
|
|
|
"Server": "nginx/1.21.0",
|
|
"Server": "nginx/1.21.0",
|
|
|
"X-Forwarded-For": "127.0.0.1",
|
|
"X-Forwarded-For": "127.0.0.1",
|
|
|
"Content-Type": "text/html; charset=UTF-8",
|
|
"Content-Type": "text/html; charset=UTF-8",
|
|
|
"Proxy-Connection": "keep-alive",
|
|
"Proxy-Connection": "keep-alive",
|
|
|
"Cache-Control": "no-cache",
|
|
"Cache-Control": "no-cache",
|
|
|
- "X-Proxy-Agent": "Gemini-Ultra-Robust-v3",
|
|
|
|
|
|
|
+ "X-Proxy-Agent": "Gemini-Ultra-Robust-v7-HA",
|
|
|
"X-Forwarded-For-Proxy": "True"
|
|
"X-Forwarded-For-Proxy": "True"
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-# --- MENSAJES ROTATIVOS (Status 101) ---
|
|
|
|
|
MENSAJES = [
|
|
MENSAJES = [
|
|
|
- "🚀 CONEXION ESTABLECIDA",
|
|
|
|
|
- "🛡️ SEGURIDAD ACTIVA",
|
|
|
|
|
- "🔋 OPTIMIZACION SISTEMA",
|
|
|
|
|
- "Pfsense",
|
|
|
|
|
|
|
+ "🚀 CONEXION TLS ESTABLECIDA",
|
|
|
|
|
+ "🛡️ CIFRADO MILITAR ACTIVO",
|
|
|
|
|
+ "🔋 MODO SIGILO SSL OK",
|
|
|
|
|
+ "Pfsense",
|
|
|
"OPNsense",
|
|
"OPNsense",
|
|
|
"VyOS",
|
|
"VyOS",
|
|
|
"Claro",
|
|
"Claro",
|
|
@@ -57,32 +70,23 @@ MENSAJES = [
|
|
|
"Google",
|
|
"Google",
|
|
|
"VyOS",
|
|
"VyOS",
|
|
|
"TNSR",
|
|
"TNSR",
|
|
|
- "🌐 ACCESO OK"
|
|
|
|
|
|
|
+ "🌐 BYPASS DE FIREWALL OK"
|
|
|
]
|
|
]
|
|
|
mensaje_cycle = itertools.cycle(MENSAJES)
|
|
mensaje_cycle = itertools.cycle(MENSAJES)
|
|
|
cycle_lock = threading.Lock()
|
|
cycle_lock = threading.Lock()
|
|
|
|
|
|
|
|
-# --- SISTEMA DE LOGS CON AUTO-LIMPIEZA ---
|
|
|
|
|
def log(msg, addr=None):
|
|
def log(msg, addr=None):
|
|
|
try:
|
|
try:
|
|
|
if os.path.exists(LOG_FILE) and os.path.getsize(LOG_FILE) > MAX_LOG_SIZE:
|
|
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")
|
|
|
|
|
-
|
|
|
|
|
|
|
+ with open(LOG_FILE, 'w') as f: f.write(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] LOG REINICIADO\n")
|
|
|
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
|
|
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
|
|
|
client_info = f" [{addr[0]}]" if addr else ""
|
|
client_info = f" [{addr[0]}]" if addr else ""
|
|
|
log_entry = f"[{timestamp}]{client_info} {msg}\n"
|
|
log_entry = f"[{timestamp}]{client_info} {msg}\n"
|
|
|
-
|
|
|
|
|
- with open(LOG_FILE, 'a') as f:
|
|
|
|
|
- f.write(log_entry)
|
|
|
|
|
- print(log_entry.strip())
|
|
|
|
|
- except:
|
|
|
|
|
- pass
|
|
|
|
|
|
|
+ with open(LOG_FILE, 'a') as f: f.write(log_entry)
|
|
|
|
|
+ except: pass
|
|
|
|
|
|
|
|
-# --- GESTIÓN DE ESTADO ---
|
|
|
|
|
active_connections = 0
|
|
active_connections = 0
|
|
|
conn_lock = threading.Lock()
|
|
conn_lock = threading.Lock()
|
|
|
-ip_cooldowns = {}
|
|
|
|
|
|
|
|
|
|
class ConnectionHandler(threading.Thread):
|
|
class ConnectionHandler(threading.Thread):
|
|
|
def __init__(self, client_socket, addr):
|
|
def __init__(self, client_socket, addr):
|
|
@@ -90,118 +94,90 @@ class ConnectionHandler(threading.Thread):
|
|
|
self.client = client_socket
|
|
self.client = client_socket
|
|
|
self.addr = addr
|
|
self.addr = addr
|
|
|
self.target = None
|
|
self.target = None
|
|
|
- self.tx_bytes = 0 # Bytes transmitidos (Subida)
|
|
|
|
|
- self.rx_bytes = 0 # Bytes recibidos (Bajada)
|
|
|
|
|
|
|
+ self.tx_bytes = 0
|
|
|
|
|
+ self.rx_bytes = 0
|
|
|
|
|
|
|
|
def build_http_response(self, status_msg):
|
|
def build_http_response(self, status_msg):
|
|
|
headers_str = "".join([f"{k}: {v}\r\n" for k, v in CUSTOM_HEADERS.items()])
|
|
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')
|
|
|
|
|
|
|
+ return (f"HTTP/1.1 101 {status_msg}\r\n{headers_str}Connection: Upgrade\r\nUpgrade: websocket\r\n\r\n").encode('utf-8')
|
|
|
|
|
|
|
|
def run(self):
|
|
def run(self):
|
|
|
global active_connections
|
|
global active_connections
|
|
|
client_ip = self.addr[0]
|
|
client_ip = self.addr[0]
|
|
|
|
|
|
|
|
try:
|
|
try:
|
|
|
- # 1. Filtro de Auto-Baneo temporal (1 hora)
|
|
|
|
|
if client_ip in banned_ips_memory:
|
|
if client_ip in banned_ips_memory:
|
|
|
if time.time() > banned_ips_memory[client_ip]:
|
|
if time.time() > banned_ips_memory[client_ip]:
|
|
|
- # El tiempo de castigo terminó, perdonamos la IP
|
|
|
|
|
del banned_ips_memory[client_ip]
|
|
del banned_ips_memory[client_ip]
|
|
|
- if client_ip in ip_strikes:
|
|
|
|
|
- del ip_strikes[client_ip]
|
|
|
|
|
- log(f"🔓 Baneo expirado. Acceso restaurado.", self.addr)
|
|
|
|
|
- else:
|
|
|
|
|
- return # Sigue baneado: Cierra silenciosamente
|
|
|
|
|
|
|
+ if client_ip in ip_strikes: del ip_strikes[client_ip]
|
|
|
|
|
+ else: return
|
|
|
|
|
|
|
|
- # 2. Validación de Lista Blanca
|
|
|
|
|
- if ALLOWED_IPS and client_ip not in ALLOWED_IPS:
|
|
|
|
|
- log("🚫 Conexión rechazada: IP no autorizada", self.addr)
|
|
|
|
|
- return
|
|
|
|
|
-
|
|
|
|
|
- # 3. Control de Rate-Limiting e Inteligencia de Auto-Ban
|
|
|
|
|
now = time.time()
|
|
now = time.time()
|
|
|
- if client_ip in ip_cooldowns and (now - ip_cooldowns[client_ip]) < CONNECTION_COOLDOWN:
|
|
|
|
|
- # Si viola el cooldown, sumamos un strike
|
|
|
|
|
|
|
+ if client_ip in ip_strikes and (now - ip_strikes.get('last_time', 0)) < CONNECTION_COOLDOWN:
|
|
|
ip_strikes[client_ip] = ip_strikes.get(client_ip, 0) + 1
|
|
ip_strikes[client_ip] = ip_strikes.get(client_ip, 0) + 1
|
|
|
if ip_strikes[client_ip] >= AUTO_BAN_STRIKES:
|
|
if ip_strikes[client_ip] >= AUTO_BAN_STRIKES:
|
|
|
banned_ips_memory[client_ip] = time.time() + BAN_TIME
|
|
banned_ips_memory[client_ip] = time.time() + BAN_TIME
|
|
|
- log(f"⛔ IP Baneada en memoria por {BAN_TIME} segundos (Flood/Spam)", self.addr)
|
|
|
|
|
|
|
+ log(f"⛔ IP Baneada por Flood/Escaneo: {client_ip}", self.addr)
|
|
|
return
|
|
return
|
|
|
|
|
|
|
|
- ip_cooldowns[client_ip] = now
|
|
|
|
|
- ip_strikes[client_ip] = 0 # Resetea strikes si conecta legalmente
|
|
|
|
|
|
|
+ ip_strikes['last_time'] = now
|
|
|
|
|
+ ip_strikes[client_ip] = 0
|
|
|
|
|
|
|
|
- # 4. Leer payload inicial
|
|
|
|
|
- self.client.settimeout(5)
|
|
|
|
|
|
|
+ self.client.settimeout(2.0)
|
|
|
|
|
+ payload = b""
|
|
|
try:
|
|
try:
|
|
|
payload = self.client.recv(BUFLEN)
|
|
payload = self.client.recv(BUFLEN)
|
|
|
- if not payload: return
|
|
|
|
|
-
|
|
|
|
|
- # 💡 Modo Sigilo: Si un navegador intenta acceder (ej. GET / HTTP/1.1) y NO es un inyector.
|
|
|
|
|
- # Puedes quitar el comentario de las 3 lineas de abajo para activarlo si quieres que el Inyector sea obligatorio.
|
|
|
|
|
- # if b"Upgrade: websocket" not in payload and b"HTTP/" in payload:
|
|
|
|
|
- # self.client.sendall(b"HTTP/1.1 400 Bad Request\r\nServer: nginx\r\n\r\n")
|
|
|
|
|
- # return
|
|
|
|
|
- except: return
|
|
|
|
|
-
|
|
|
|
|
- # 5. Conectar al destino (SSH/Dropbear)
|
|
|
|
|
- log(f"Intentando conectar a {SSH_HOST}:{SSH_PORT}...", self.addr)
|
|
|
|
|
- self.target = socket.create_connection((SSH_HOST, SSH_PORT), timeout=10)
|
|
|
|
|
-
|
|
|
|
|
- # 6. Elegir mensaje rotativo y responder al cliente
|
|
|
|
|
- with cycle_lock:
|
|
|
|
|
- current_status = next(mensaje_cycle)
|
|
|
|
|
-
|
|
|
|
|
- self.client.sendall(self.build_http_response(current_status))
|
|
|
|
|
- log(f"✅ Túnel activo: {current_status}", self.addr)
|
|
|
|
|
|
|
+ except socket.timeout:
|
|
|
|
|
+ pass # NetMod en silencio
|
|
|
|
|
+ except Exception:
|
|
|
|
|
+ return
|
|
|
|
|
|
|
|
- # 7. Túnel bidireccional puro
|
|
|
|
|
|
|
+ try:
|
|
|
|
|
+ self.target = socket.create_connection((SSH_HOST, SSH_PORT), timeout=10)
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ log(f"❌ Error interno destino SSH: {e}", self.addr)
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ if payload:
|
|
|
|
|
+ if payload.startswith(b"SSH-"):
|
|
|
|
|
+ self.target.sendall(payload)
|
|
|
|
|
+ elif b"HTTP/" in payload and b"Upgrade: websocket" not in payload:
|
|
|
|
|
+ log(f"🕵️ Escáner detectado. Respondiendo Fake Web.", self.addr)
|
|
|
|
|
+ self.client.sendall(FAKE_WEB_RESPONSE)
|
|
|
|
|
+ return
|
|
|
|
|
+ else:
|
|
|
|
|
+ with cycle_lock: current_status = next(mensaje_cycle)
|
|
|
|
|
+ self.client.sendall(self.build_http_response(current_status))
|
|
|
|
|
+
|
|
|
self.tunnel()
|
|
self.tunnel()
|
|
|
|
|
|
|
|
- except Exception as e:
|
|
|
|
|
- log(f"❌ Error: {e}", self.addr)
|
|
|
|
|
|
|
+ except Exception as e: pass
|
|
|
finally:
|
|
finally:
|
|
|
- with conn_lock:
|
|
|
|
|
- active_connections -= 1
|
|
|
|
|
|
|
+ with conn_lock: active_connections -= 1
|
|
|
self.cleanup()
|
|
self.cleanup()
|
|
|
|
|
|
|
|
def tunnel(self):
|
|
def tunnel(self):
|
|
|
self.client.settimeout(None)
|
|
self.client.settimeout(None)
|
|
|
self.target.settimeout(None)
|
|
self.target.settimeout(None)
|
|
|
sockets = [self.client, self.target]
|
|
sockets = [self.client, self.target]
|
|
|
-
|
|
|
|
|
while True:
|
|
while True:
|
|
|
readable, _, error = select.select(sockets, [], sockets, 300)
|
|
readable, _, error = select.select(sockets, [], sockets, 300)
|
|
|
- if error or not readable:
|
|
|
|
|
- break
|
|
|
|
|
-
|
|
|
|
|
|
|
+ if error or not readable: break
|
|
|
for s in readable:
|
|
for s in readable:
|
|
|
try:
|
|
try:
|
|
|
data = s.recv(BUFLEN)
|
|
data = s.recv(BUFLEN)
|
|
|
- if not data:
|
|
|
|
|
- return
|
|
|
|
|
-
|
|
|
|
|
|
|
+ if not data: return
|
|
|
if s is self.client:
|
|
if s is self.client:
|
|
|
self.target.sendall(data)
|
|
self.target.sendall(data)
|
|
|
- self.tx_bytes += len(data) # Sumar subida
|
|
|
|
|
|
|
+ self.tx_bytes += len(data)
|
|
|
else:
|
|
else:
|
|
|
self.client.sendall(data)
|
|
self.client.sendall(data)
|
|
|
- self.rx_bytes += len(data) # Sumar bajada
|
|
|
|
|
- except:
|
|
|
|
|
- return
|
|
|
|
|
|
|
+ self.rx_bytes += len(data)
|
|
|
|
|
+ except: return
|
|
|
|
|
|
|
|
def cleanup(self):
|
|
def cleanup(self):
|
|
|
- # Calcula el tráfico total al cerrar la conexión
|
|
|
|
|
total_mb = (self.tx_bytes + self.rx_bytes) / (1024 * 1024)
|
|
total_mb = (self.tx_bytes + self.rx_bytes) / (1024 * 1024)
|
|
|
- if total_mb > 0.01: # Solo registrar si hubo tráfico real (> 10KB)
|
|
|
|
|
- log(f"[*] Conexión finalizada. Tráfico consumido: {total_mb:.2f} MB", self.addr)
|
|
|
|
|
-
|
|
|
|
|
|
|
+ if total_mb > 0.05: log(f"[*] Tráfico finalizado: {total_mb:.2f} MB", self.addr)
|
|
|
for s in [self.client, self.target]:
|
|
for s in [self.client, self.target]:
|
|
|
if s:
|
|
if s:
|
|
|
try: s.close()
|
|
try: s.close()
|
|
@@ -209,41 +185,67 @@ class ConnectionHandler(threading.Thread):
|
|
|
|
|
|
|
|
def main():
|
|
def main():
|
|
|
global active_connections
|
|
global active_connections
|
|
|
-
|
|
|
|
|
- # Intentamos socket dual (IPv6/IPv4)
|
|
|
|
|
|
|
+ ssl_context = None
|
|
|
|
|
+ if USE_SSL:
|
|
|
|
|
+ try:
|
|
|
|
|
+ ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
|
|
|
|
+ ssl_context.load_cert_chain(certfile=CERT_FILE, keyfile=KEY_FILE)
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ print(f"Error crítico cargando certificados SSL: {e}")
|
|
|
|
|
+ sys.exit(1)
|
|
|
|
|
+
|
|
|
try:
|
|
try:
|
|
|
addr_info = socket.getaddrinfo(None, LISTENING_PORT, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE)
|
|
addr_info = socket.getaddrinfo(None, LISTENING_PORT, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE)
|
|
|
addr_info.sort(key=lambda x: x[0] == socket.AF_INET6, reverse=True)
|
|
addr_info.sort(key=lambda x: x[0] == socket.AF_INET6, reverse=True)
|
|
|
-
|
|
|
|
|
af, socktype, proto, canonname, sa = addr_info[0]
|
|
af, socktype, proto, canonname, sa = addr_info[0]
|
|
|
|
|
+
|
|
|
server = socket.socket(af, socktype, proto)
|
|
server = socket.socket(af, socktype, proto)
|
|
|
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
|
-
|
|
|
|
|
if af == socket.AF_INET6:
|
|
if af == socket.AF_INET6:
|
|
|
try: server.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
|
|
try: server.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
|
|
|
except: pass
|
|
except: pass
|
|
|
|
|
|
|
|
server.bind(sa)
|
|
server.bind(sa)
|
|
|
- server.listen(200)
|
|
|
|
|
|
|
+ server.listen(500) # Aumentado el backlog para soportar ráfagas de bots
|
|
|
|
|
|
|
|
- log(f"=====================================================")
|
|
|
|
|
- log(f"🔥 Servidor Robusto Iniciado en Puerto {LISTENING_PORT}")
|
|
|
|
|
- log(f"🎯 Destino Interno: {SSH_HOST}:{SSH_PORT}")
|
|
|
|
|
- log(f"🛡️ Logs limitados a 10MB en {LOG_FILE}")
|
|
|
|
|
- log(f"=====================================================")
|
|
|
|
|
|
|
+ print(f"=====================================================")
|
|
|
|
|
+ print(f"🔥 Servidor Robusto INMORTAL Iniciado - Puerto {LISTENING_PORT}")
|
|
|
|
|
+ print(f"🛡️ Motor SSL/TLS & Anti-Crash: ACTIVADO")
|
|
|
|
|
+ print(f"=====================================================")
|
|
|
|
|
|
|
|
|
|
+ # EL BUCLE PRINCIPAL AHORA ES BLINDADO
|
|
|
while True:
|
|
while True:
|
|
|
- client, addr = server.accept()
|
|
|
|
|
- with conn_lock:
|
|
|
|
|
- if active_connections >= MAX_CONNECTIONS:
|
|
|
|
|
- client.close()
|
|
|
|
|
- continue
|
|
|
|
|
- active_connections += 1
|
|
|
|
|
- ConnectionHandler(client, addr).start()
|
|
|
|
|
|
|
+ try:
|
|
|
|
|
+ client, addr = server.accept()
|
|
|
|
|
+
|
|
|
|
|
+ if USE_SSL:
|
|
|
|
|
+ try:
|
|
|
|
|
+ client = ssl_context.wrap_socket(client, server_side=True)
|
|
|
|
|
+ except Exception:
|
|
|
|
|
+ # Si el bot manda basura en lugar de un handshake SSL, se cierra y se ignora silenciosamente.
|
|
|
|
|
+ client.close()
|
|
|
|
|
+ continue
|
|
|
|
|
+
|
|
|
|
|
+ with conn_lock:
|
|
|
|
|
+ if active_connections >= MAX_CONNECTIONS:
|
|
|
|
|
+ client.close()
|
|
|
|
|
+ continue
|
|
|
|
|
+ active_connections += 1
|
|
|
|
|
+
|
|
|
|
|
+ ConnectionHandler(client, addr).start()
|
|
|
|
|
+
|
|
|
|
|
+ except socket.error as e:
|
|
|
|
|
+ # Si Linux se queda sin recursos por 1 segundo por un ataque DDoS,
|
|
|
|
|
+ # esperamos 50ms y volvemos a intentarlo en lugar de apagar el script.
|
|
|
|
|
+ time.sleep(0.05)
|
|
|
|
|
+ continue
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ time.sleep(1)
|
|
|
|
|
+ continue
|
|
|
|
|
|
|
|
- except Exception as e:
|
|
|
|
|
- log(f"❌ Error crítico en servidor: {e}")
|
|
|
|
|
- finally:
|
|
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ print(f"Error fatal: {e}")
|
|
|
|
|
+ finally:
|
|
|
server.close()
|
|
server.close()
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if __name__ == "__main__":
|