| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- # -*- 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()
|