| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- # -*- coding: utf-8 -*-
- # ==============================================================================
- # SCRIPT DE PROXY MULTIFILAMENTADO CON RESPUESTAS ROTATIVAS
- #
- # - Basado en la versión robusta y estable.
- # - Añade rotación dinámica del mensaje de estado en la respuesta HTTP 101.
- # - Utiliza un iterador cíclico para cambiar el mensaje en cada nueva conexión.
- #
- # Creado por Gemini
- # ==============================================================================
- #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 /etc/VPS-MX/protocolo/Pythonv1.py 8080
- import socket
- import threading
- import select
- import sys
- import time
- import os
- import logging
- import logging.handlers
- import itertools # 💡 Importado para la rotación de mensajes
- # ==============================================================================
- # CONFIGURACIÓN GLOBAL Y SETUP DE LOGGING
- # ==============================================================================
- IPV4_ADDR = '0.0.0.0'
- IPV6_ADDR = '::'
- if sys.argv[1:]:
- LISTENING_PORT = int(sys.argv[1])
- else:
- LISTENING_PORT = 8080
- PASS = ''
- PRIORITIZE_IPV4 = True
- CONNECTION_COOLDOWN_TIME = 10
- MAX_CONNECTIONS = 1000
- # Constantes de red
- BUFLEN = 4096 * 4
- TIMEOUT = 60
- DEFAULT_HOST = '127.0.0.1:223'
- # 💡 LISTA DE MENSAJES PARA ROTAR
- # Puedes añadir todos los que quieras aquí.
- MENSAJES_ROTATIVOS = [
- "Pfsense",
- "OPNsense",
- "VyOS",
- "TNSR"
- ]
- # Creamos un iterador infinito que rota sobre la lista anterior
- mensaje_iterator = itertools.cycle(MENSAJES_ROTATIVOS)
- # Lock para asegurar que la extracción del siguiente mensaje sea segura entre hilos
- iterator_lock = threading.Lock()
- # Configuración del log
- LOG_FILE = '/root/proxy.log'
- MAX_LOG_SIZE = 5 * 1024 * 1024
- BACKUP_COUNT = 5
- def setup_logging():
- log_format = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
- file_handler = logging.handlers.RotatingFileHandler(LOG_FILE, maxBytes=MAX_LOG_SIZE, backupCount=BACKUP_COUNT)
- file_handler.setFormatter(log_format)
- console_handler = logging.StreamHandler()
- console_handler.setFormatter(log_format)
- logger = logging.getLogger()
- logger.setLevel(logging.INFO)
- logger.addHandler(file_handler)
- logger.addHandler(console_handler)
- return logger
- logger = setup_logging()
- last_connection_times = {}
- last_connection_lock = threading.Lock()
- connection_limit_semaphore = threading.Semaphore(MAX_CONNECTIONS)
- # ==============================================================================
- # CLASE DEL SERVIDOR
- # ==============================================================================
- class Server(threading.Thread):
- def __init__(self, port):
- super().__init__()
- self.running = False
- self.port = port
- self.threads = []
- self.threads_lock = threading.Lock()
- self.ipv4_socket = None
- self.ipv6_socket = None
- def run(self):
- logger.info("\n:-------PythonProxy-------:\n")
- logger.info(f"Listening port: {self.port}")
- logger.info(f"Mensajes configurados: {len(MENSAJES_ROTATIVOS)}\n")
- logger.info(":-------------------------:\n")
- try:
- self.ipv4_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- self.ipv4_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- self.ipv4_socket.bind((IPV4_ADDR, self.port))
- self.ipv4_socket.listen(0)
- except socket.error as e:
- logger.error(f"Error IPv4: {e}")
- self.ipv4_socket = None
- try:
- self.ipv6_socket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
- self.ipv6_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- self.ipv6_socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
- self.ipv6_socket.bind((IPV6_ADDR, self.port, 0, 0))
- self.ipv6_socket.listen(0)
- except socket.error as e:
- logger.error(f"Error IPv6: {e}")
- self.ipv6_socket = None
- if not self.ipv4_socket and not self.ipv6_socket:
- return
- self.running = True
- active_sockets = [s for s in [self.ipv4_socket, self.ipv6_socket] if s]
- try:
- while self.running:
- readable, _, _ = select.select(active_sockets, [], [], 2)
- for sock in readable:
- c, addr = sock.accept()
- client_ip = addr[0]
- current_time = time.time()
- with last_connection_lock:
- last_time = last_connection_times.get(client_ip, 0)
- if current_time - last_time < CONNECTION_COOLDOWN_TIME:
- c.close()
- continue
- last_connection_times[client_ip] = current_time
- if not connection_limit_semaphore.acquire(timeout=0):
- c.close()
- continue
- c.setblocking(1)
- conn = ConnectionHandler(c, self, addr)
- conn.start()
- self.add_conn(conn)
- except Exception as e:
- logger.error(f"Error servidor: {e}")
- finally:
- self.running = False
- def add_conn(self, conn):
- with self.threads_lock:
- if self.running: self.threads.append(conn)
- def remove_conn(self, conn):
- with self.threads_lock:
- if conn in self.threads: self.threads.remove(conn)
- def close(self):
- self.running = False
- with self.threads_lock:
- for c in list(self.threads): c.close()
- # ==============================================================================
- # CLASE MANEJADORA DE CONEXIONES
- # ==============================================================================
- class ConnectionHandler(threading.Thread):
- def __init__(self, client_socket, server, addr):
- super().__init__()
- self.client = client_socket
- self.server = server
- self.addr = addr
- self.log_prefix = f"{addr[0]}:{addr[1]}"
- self.target = None
- self.client_closed = False
- self.target_closed = True
- def close(self):
- try:
- if not self.client_closed:
- self.client.close()
- except: pass
- finally: self.client_closed = True
- try:
- if not self.target_closed:
- self.target.close()
- except: pass
- finally: self.target_closed = True
-
- connection_limit_semaphore.release()
- def run(self):
- try:
- data = self.client.recv(BUFLEN)
- if not data: return
- headers = data.decode('latin-1')
- host_port = self.find_header(headers, 'X-Real-Host') or DEFAULT_HOST
- # 💡 Lógica de rotación: obtenemos el siguiente mensaje de la lista
- with iterator_lock:
- mensaje_actual = next(mensaje_iterator)
-
- # Construimos la respuesta HTTP dinámica
- response_dinamica = f"HTTP/1.1 101 {mensaje_actual}\r\nConnection: Upgrade\r\nUpgrade: websocket\r\n\r\n".encode()
- logger.info(f"[{self.log_prefix}] Usando mensaje: '{mensaje_actual}'")
-
- self.connect_target(host_port)
- self.client.sendall(response_dinamica)
- self.do_tunnel()
- except Exception as e:
- logger.error(f"Error en {self.log_prefix}: {e}")
- finally:
- self.close()
- self.server.remove_conn(self)
- def find_header(self, head, header):
- aux = head.find(header + ': ')
- if aux == -1: return ''
- head = head[aux + len(header) + 2:]
- aux = head.find('\r\n')
- return head[:aux]
- def connect_target(self, host_port):
- i = host_port.find(':')
- host = host_port[:i] if i != -1 else host_port
- port = int(host_port[i+1:]) if i != -1 else 22
-
- addr_info = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM)
- for res in addr_info:
- af, socktype, proto, _, sa = res
- try:
- self.target = socket.socket(af, socktype, proto)
- self.target.connect(sa)
- self.target_closed = False
- return
- except:
- if self.target: self.target.close()
- raise RuntimeError("No se pudo conectar al destino")
- def do_tunnel(self):
- socs = [self.client, self.target]
- while True:
- readable, _, _ = select.select(socs, [], [], TIMEOUT)
- if not readable: break
- for sock in readable:
- data = sock.recv(BUFLEN)
- if not data: return
- if sock is self.target:
- self.client.send(data)
- else:
- self.target.sendall(data)
- def main():
- server = Server(LISTENING_PORT)
- server.start()
- try:
- while True: time.sleep(2)
- except KeyboardInterrupt:
- server.close()
- if __name__ == '__main__':
- main()
|