| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- # -*- 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 (22 o 223) y OpenSSH
- 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 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
- ip_strikes = {}
- # --- 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"
- }
- # --- MENSAJES ROTATIVOS (Status 101) ---
- MENSAJES = [
- "🚀 CONEXION ESTABLECIDA",
- "🛡️ SEGURIDAD ACTIVA",
- "🔋 OPTIMIZACION SISTEMA",
- "🌐 ACCESO NETMOD OK"
- ]
- mensaje_cycle = itertools.cycle(MENSAJES)
- cycle_lock = threading.Lock()
- # --- SISTEMA DE LOGS CON AUTO-LIMPIEZA ---
- def log(msg, addr=None):
- try:
- 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"
-
- with open(LOG_FILE, 'a') as f:
- f.write(log_entry)
- print(log_entry.strip())
- except:
- pass
- # --- 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
- self.tx_bytes = 0 # Bytes transmitidos (Subida)
- self.rx_bytes = 0 # Bytes recibidos (Bajada)
- 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
- client_ip = self.addr[0]
-
- try:
- # 1. Filtro de Auto-Baneo temporal (1 hora)
- if client_ip in banned_ips_memory:
- if time.time() > banned_ips_memory[client_ip]:
- # El tiempo de castigo terminó, perdonamos la 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
-
- # 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()
- if client_ip in ip_cooldowns and (now - ip_cooldowns[client_ip]) < CONNECTION_COOLDOWN:
- # Si viola el cooldown, sumamos un strike
- ip_strikes[client_ip] = ip_strikes.get(client_ip, 0) + 1
- if ip_strikes[client_ip] >= AUTO_BAN_STRIKES:
- banned_ips_memory[client_ip] = time.time() + BAN_TIME
- log(f"⛔ IP Baneada en memoria por {BAN_TIME} segundos (Flood/Spam)", self.addr)
- return
-
- ip_cooldowns[client_ip] = now
- ip_strikes[client_ip] = 0 # Resetea strikes si conecta legalmente
- # 4. Leer payload inicial de Netmod (Absorción)
- self.client.settimeout(5)
- try:
- payload = self.client.recv(BUFLEN)
- if not payload: return
-
- # 💡 Modo Sigilo: Si un navegador intenta acceder (ej. GET / HTTP/1.1) y NO es Netmod.
- # Puedes quitar el comentario de las 3 líneas de abajo para activarlo si quieres que Netmod 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)
- # 7. Túnel bidireccional puro
- self.tunnel()
- except Exception as e:
- log(f"❌ Error: {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, 300)
- if error or not readable:
- break
-
- for s in readable:
- try:
- data = s.recv(BUFLEN)
- if not data:
- return
-
- if s is self.client:
- self.target.sendall(data)
- self.tx_bytes += len(data) # Sumar subida
- else:
- self.client.sendall(data)
- self.rx_bytes += len(data) # Sumar bajada
- except:
- return
- def cleanup(self):
- # Calcula el tráfico total al cerrar la conexión
- 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)
-
- for s in [self.client, self.target]:
- if s:
- try: s.close()
- except: pass
- def main():
- global active_connections
-
- # Intentamos socket dual (IPv6/IPv4)
- try:
- 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)
-
- af, socktype, proto, canonname, sa = addr_info[0]
- server = socket.socket(af, socktype, proto)
- server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-
- if af == socket.AF_INET6:
- try: server.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
- except: pass
- server.bind(sa)
- server.listen(200)
-
- 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"=====================================================")
- while True:
- client, addr = server.accept()
- with conn_lock:
- if active_connections >= MAX_CONNECTIONS:
- 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()
|