import socket import threading import binascii import signal import sys import time import re # ==================== НАСТРОЙКИ ==================== CONVERT_MODE = 1 # 1 - HEX, 0 - прозрачный REPLACE_ON = 0 # 0 - без замены текста # Слушаем Proxy1 на VPS LISTEN_IP = "91.221.99.183" # Внешний IP VPS LISTEN_PORT = 8080 # Исходящий интерфейс для ответов Proxy1 (обычно тот же) OUTGOING_IP = "91.221.99.183" # Выход в интернет EXTERNAL_IP = "91.221.99.183" # Тот же IP для выхода в интернет EXTERNAL_PORT = 0 # Динамический порт # Адрес первого прокси (для обратного направления - не критично, т.к. используется тот же сокет) TARGET_HOST = "91.221.99.183" TARGET_PORT = 8080 # Настройки сервера MAX_CLIENTS = 500 BUFFER_SIZE = 8192 BUFFER_LIMIT = 65536 CONNECT_TIMEOUT = 10 IDLE_TIMEOUT = 60 # === НАСТРОЙКИ ДЛЯ УДАЛЕНИЯ ТЕКСТОВОГО ЗАГОЛОВКА === # Должен совпадать с Proxy1 HTTP_PREFIX = b"GET /wiki/ HTTP/1.1\r\nHost: ru.ruwiki.ru\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36\r\nAccept: text/html\r\nAccept-Language: ru-RU,ru;q=0.9\r\nAccept-Encoding: gzip, deflate, br\r\nConnection: keep-alive\r\n\r\n" # 0 = автоматическое определение при старте HTTP_PREFIX_SIZE = 0 # ======================================================================== server_running = True active_connections = 0 connections_lock = threading.Lock() # Разделитель между префиксом и HEX-данными SEPARATOR = b"\n---HEX---\n" def safe_print(msg): try: print(msg, flush=True) except: pass safe_print("=" * 60) safe_print("ЗАПУСК ВТОРОГО ПРОКСИ-СЕРВЕРА (VPS)") safe_print("=" * 60) safe_print(f"[*] Режим: {'HEX-конвертация' if CONVERT_MODE == 1 else 'Прозрачный'}") safe_print(f"[*] Замена текста: {'ВКЛЮЧЕНА' if REPLACE_ON == 1 else 'ВЫКЛЮЧЕНA'}") # Проверка и настройка HTTP_PREFIX_SIZE if HTTP_PREFIX_SIZE == 0: HTTP_PREFIX_SIZE = len(HTTP_PREFIX) safe_print(f"[!] HTTP_PREFIX_SIZE не задан, установлен автоматически: {HTTP_PREFIX_SIZE} байт") else: if len(HTTP_PREFIX) != HTTP_PREFIX_SIZE: safe_print(f"[!] Ошибка: длина HTTP_PREFIX ({len(HTTP_PREFIX)}) не совпадает с HTTP_PREFIX_SIZE ({HTTP_PREFIX_SIZE})") sys.exit(1) safe_print(f"[*] HTTP_PREFIX длина: {HTTP_PREFIX_SIZE} байт") safe_print(f"[*] HTTP_PREFIX (первые 100 байт): {HTTP_PREFIX[:100]!r}") def HEX_TO_BINARY(data: bytes) -> bytes: """Преобразует HEX-строку в двоичные данные""" if not data: return b'' try: hex_str = data.strip() if not hex_str: return b'' if len(hex_str) % 2 != 0: safe_print(f"[!] HEX-строка нечётной длины: {hex_str[:100]}") return b'' return binascii.unhexlify(hex_str) except Exception as e: safe_print(f"[!] Ошибка HEX_TO_BINARY: {e}") return b'' def BINARY_TO_HEX(data: bytes) -> bytes: """Преобразует двоичные данные в HEX-строку с \\n""" if not data: return b"\n" return binascii.hexlify(data) + b"\n" def PASS_THROUGH(data: bytes) -> bytes: return data def REMOVETEXT2(packet: bytes) -> bytes: """ Удаляет текстовый префикс и разделитель из начала пакета. Возвращает HEX-строку или пустые байты при ошибке. """ if len(packet) < HTTP_PREFIX_SIZE + len(SEPARATOR): safe_print(f"[!] REMOVETEXT2: пакет слишком короткий ({len(packet)} байт)") return b'' received_prefix = packet[:HTTP_PREFIX_SIZE] if received_prefix != HTTP_PREFIX: safe_print(f"[!] REMOVETEXT2: префикс не совпадает") safe_print(f" Ожидалось (первые 50): {HTTP_PREFIX[:50]!r}") safe_print(f" Получено (первые 50): {received_prefix[:50]!r}") return b'' separator_pos = HTTP_PREFIX_SIZE received_separator = packet[separator_pos:separator_pos + len(SEPARATOR)] if received_separator != SEPARATOR: safe_print(f"[!] REMOVETEXT2: разделитель не совпадает на позиции {separator_pos}") safe_print(f" Ожидалось: {SEPARATOR!r}") safe_print(f" Получено: {received_separator[:50]!r}") return b'' hex_part = packet[separator_pos + len(SEPARATOR):] return hex_part def extract_host_from_request(data: bytes) -> str: try: text = data.decode('ascii', errors='ignore') match = re.search(r'CONNECT\s+([^\s:]+)', text, re.IGNORECASE) if match: return match.group(1) match = re.search(r'^(GET|POST|PUT|DELETE|HEAD|OPTIONS|PATCH)\s+https?://([^/:]+)', text, re.IGNORECASE) if match: return match.group(2) match = re.search(r'Host:\s*([^\r\n]+)', text, re.IGNORECASE) if match: return match.group(1).strip() return "unknown" except: return "unknown" if CONVERT_MODE == 1: FROM_FIRST_PROXY = HEX_TO_BINARY TO_FIRST_PROXY = BINARY_TO_HEX else: FROM_FIRST_PROXY = PASS_THROUGH TO_FIRST_PROXY = PASS_THROUGH def parse_connect_request(data: bytes) -> tuple: try: text = data.decode('ascii', errors='ignore') match = re.search(r'CONNECT\s+([^\s:]+)(?::(\d+))?\s+HTTP/\d\.\d', text, re.IGNORECASE) if match: host = match.group(1) port = int(match.group(2)) if match.group(2) else 443 return (host, port) match = re.search(r'^(GET|POST|PUT|DELETE|HEAD|OPTIONS|PATCH)\s+https?://([^/:]+)', text, re.IGNORECASE) if match: host = match.group(2) port = 80 if 'http://' in text else 443 return (host, port) return (None, None) except: return (None, None) def send_connect_response(client_sock, client_addr, success: bool): """Отправляет ответ на CONNECT запрос""" response = b"HTTP/1.1 200 Connection established\r\n\r\n" if success else b"HTTP/1.1 502 Bad Gateway\r\n\r\n" try: if CONVERT_MODE == 1: hex_response = TO_FIRST_PROXY(response) client_sock.sendall(hex_response) safe_print(f"[*] {client_addr}: Ответ 200 Connection established (HEX)") else: client_sock.sendall(response) safe_print(f"[*] {client_addr}: Ответ 200 Connection established") except Exception as e: safe_print(f"[!] {client_addr}: Ошибка отправки ответа: {e}") def connect_to_internet(host: str, port: int, client_addr: str): try: safe_print(f"[*] {client_addr}: Подключение к {host}:{port}") sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) if EXTERNAL_IP: sock.bind((EXTERNAL_IP, EXTERNAL_PORT)) safe_print(f"[*] {client_addr}: Привязан к {EXTERNAL_IP}") sock.settimeout(CONNECT_TIMEOUT) sock.connect((host, port)) sock.settimeout(None) safe_print(f"[*] {client_addr}: Подключен к {host}:{port}") return sock except Exception as e: safe_print(f"[!] {client_addr}: Ошибка подключения: {e}") raise def tunnel_to_internet(client_sock, internet_sock, client_addr, stop_event, early_data_buffer): """Поток: Proxy1 → Интернет (с удалением префикса)""" buffer = b"" if early_data_buffer: buffer += early_data_buffer while not stop_event.is_set(): try: data = client_sock.recv(BUFFER_SIZE) if not data: break buffer += data while True: sep_pos = buffer.find(SEPARATOR) if sep_pos == -1: if len(buffer) > BUFFER_LIMIT: safe_print(f"[!] {client_addr}: Буфер переполнен, сбрасываем") buffer = b"" break if sep_pos != HTTP_PREFIX_SIZE: safe_print(f"[!] {client_addr}: Разделитель найден на позиции {sep_pos}, ожидалось {HTTP_PREFIX_SIZE}") stop_event.set() return after_sep_start = sep_pos + len(SEPARATOR) line_end = buffer.find(b'\n', after_sep_start) if line_end == -1: break packet = buffer[:line_end] buffer = buffer[line_end + 1:] hex_part = REMOVETEXT2(packet) if not hex_part: safe_print(f"[!] {client_addr}: REMOVETEXT2 не смог обработать пакет") stop_event.set() return binary_data = FROM_FIRST_PROXY(hex_part) if binary_data: internet_sock.sendall(binary_data) except (BlockingIOError, socket.error): continue except Exception as e: if not stop_event.is_set(): safe_print(f"[!] {client_addr}: Ошибка tunnel_to_internet: {e}") break stop_event.set() def tunnel_to_first_proxy(internet_sock, client_sock, client_addr, stop_event): """Поток: Интернет → Proxy1 (без префикса)""" while not stop_event.is_set(): try: data = internet_sock.recv(BUFFER_SIZE) if not data: break if CONVERT_MODE == 1: hex_data = TO_FIRST_PROXY(data) client_sock.sendall(hex_data) else: client_sock.sendall(data) except (BlockingIOError, socket.error): continue except Exception as e: if not stop_event.is_set(): safe_print(f"[!] {client_addr}: Ошибка tunnel_to_first_proxy: {e}") break stop_event.set() def handle_client(client_sock, client_addr): global active_connections internet_sock = None stop_event = threading.Event() early_data_buffer = None try: initial_data = client_sock.recv(BUFFER_SIZE) if not initial_data: safe_print(f"[!] {client_addr}: Нет данных") return safe_print(f"[*] {client_addr}: Получено {len(initial_data)} байт данных") sep_pos = initial_data.find(SEPARATOR) if sep_pos == -1: safe_print(f"[!] {client_addr}: Разделитель не найден в начальных данных") send_connect_response(client_sock, client_addr, False) return if sep_pos != HTTP_PREFIX_SIZE: safe_print(f"[!] {client_addr}: Разделитель на позиции {sep_pos}, ожидалось {HTTP_PREFIX_SIZE}") send_connect_response(client_sock, client_addr, False) return if len(initial_data) < HTTP_PREFIX_SIZE + len(SEPARATOR): safe_print(f"[!] {client_addr}: Начальных данных недостаточно для удаления префикса") send_connect_response(client_sock, client_addr, False) return received_prefix = initial_data[:HTTP_PREFIX_SIZE] if received_prefix != HTTP_PREFIX: safe_print(f"[!] {client_addr}: Префикс не совпадает") send_connect_response(client_sock, client_addr, False) return received_separator = initial_data[HTTP_PREFIX_SIZE:HTTP_PREFIX_SIZE + len(SEPARATOR)] if received_separator != SEPARATOR: safe_print(f"[!] {client_addr}: Разделитель не совпадает") send_connect_response(client_sock, client_addr, False) return after_sep_start = HTTP_PREFIX_SIZE + len(SEPARATOR) line_end = initial_data.find(b'\n', after_sep_start) if line_end == -1: safe_print(f"[!] {client_addr}: Не найден конец HEX-строки в начальных данных") send_connect_response(client_sock, client_addr, False) return hex_line = initial_data[after_sep_start:line_end].strip() remaining_data = initial_data[line_end + 1:] binary_data = FROM_FIRST_PROXY(hex_line) if not binary_data: safe_print(f"[!] {client_addr}: Не удалось декодировать HEX из начальных данных") send_connect_response(client_sock, client_addr, False) return target_host, target_port = parse_connect_request(binary_data) if not target_host: safe_print(f"[!] {client_addr}: Не удалось распознать запрос") send_connect_response(client_sock, client_addr, False) return safe_print(f"[*] {client_addr}: ЗАПРОС → {target_host}:{target_port}") internet_sock = connect_to_internet(target_host, target_port, client_addr) is_connect = binary_data.decode('ascii', errors='ignore').upper().startswith('CONNECT') if is_connect: send_connect_response(client_sock, client_addr, True) if remaining_data: early_data_buffer = remaining_data else: if remaining_data: early_data_buffer = remaining_data safe_print(f"[+] {client_addr}: Соединение → {target_host}:{target_port}") with connections_lock: active_connections += 1 safe_print(f"[*] Активных: {active_connections}") t1 = threading.Thread( target=tunnel_to_internet, args=(client_sock, internet_sock, client_addr, stop_event, early_data_buffer), daemon=True ) t2 = threading.Thread( target=tunnel_to_first_proxy, args=(internet_sock, client_sock, client_addr, stop_event), daemon=True ) t1.start() t2.start() t1.join() t2.join() except Exception as e: safe_print(f"[!] {client_addr}: Ошибка: {e}") try: send_connect_response(client_sock, client_addr, False) except: pass finally: stop_event.set() try: client_sock.close() except: pass try: if internet_sock: internet_sock.close() except: pass with connections_lock: if active_connections > 0: active_connections -= 1 safe_print(f"[-] {client_addr}: Закрыто. Активных: {active_connections}") def signal_handler(signum, frame): global server_running safe_print("\n[!] Получен сигнал остановки. Завершаем работу...") server_running = False wait_time = 0 while active_connections > 0 and wait_time < 5: safe_print(f"[*] Ожидаем завершения {active_connections} соединений...") time.sleep(1) wait_time += 1 safe_print("[*] Прокси сервер остановлен") sys.exit(0) def start_proxy(): global server_running signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: server.bind((LISTEN_IP, LISTEN_PORT)) safe_print(f"[+] Привязан к {LISTEN_IP}:{LISTEN_PORT}") except Exception as e: safe_print(f"[!] Ошибка привязки: {e}") sys.exit(1) server.listen(MAX_CLIENTS) server.settimeout(1.0) safe_print("=" * 60) safe_print("ВТОРОЙ ПРОКСИ-СЕРВЕР (VPS)") safe_print("=" * 60) safe_print(f"[*] СЛУШАЕТ Proxy1 на: {LISTEN_IP}:{LISTEN_PORT}") safe_print(f"[*] Выход в Интернет через: {EXTERNAL_IP if EXTERNAL_IP else 'авто'}") safe_print(f"[*] Режим: {'HEX-конвертация' if CONVERT_MODE == 1 else 'Прозрачный'}") safe_print(f"[*] Замена текста: {'ВКЛЮЧЕНА' if REPLACE_ON == 1 else 'ВЫКЛЮЧЕНА'}") safe_print(f"[*] Максимум соединений: {MAX_CLIENTS}") safe_print("[*] Нажмите Ctrl+C для остановки") safe_print("-" * 60) safe_print("[*] Сервер запущен и ожидает подключений...") try: while server_running: try: client_sock, client_addr = server.accept() if not server_running: break safe_print(f"[*] Принято соединение от {client_addr}") client_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) threading.Thread( target=handle_client, args=(client_sock, client_addr), daemon=True ).start() except socket.timeout: continue except OSError as e: if server_running: safe_print(f"[!] Ошибка accept: {e}") break except Exception as e: safe_print(f"[!] Критическая ошибка: {e}") finally: safe_print("[*] Закрываем серверный сокет...") server.close() safe_print("[*] Сервер остановлен") if __name__ == "__main__": try: start_proxy() except KeyboardInterrupt: safe_print("\n[*] Программа прервана пользователем") except Exception as e: safe_print(f"[!] Необработанная ошибка: {e}") sys.exit(1)