ТЕХНИЧЕСКОЕ ЗАДАНИЕ / ПРОМТ НА РАЗРАБОТКУ ДВУХЗВЕННОЙ HEX-КОНВЕРТИРУЮЩЕЙ ПРОКСИ-СИСТЕМЫ 1. Общая архитектура и поток данных Разработать два независимых TCP-прокси-сервера на Python, работающих в цепочке: `Браузер > Proxy1 > Proxy2 > Целевой сервер в Интернете (порт 443) > обратно` - Proxy1 принимает сырые TCP-пакеты от клиента, конвертирует их в HEX-строки с разделителем `\n` и пересылает на Proxy2 по порту `80` в виде обычного текстового потока. - Proxy2 принимает HEX-поток, декодирует его обратно в бинарный вид, парсит HTTP-запрос (`CONNECT`/`Host:`/абсолютный URL), устанавливает соединение с целевым хостом в Интернете (по умолчанию `443` для HTTPS) и организует двунаправленный туннель. Ответы от целевого сервера кодируются в HEX и возвращаются в Proxy1, который декодирует их и отдаёт браузеру. 2. Стек технологий Python | Компонент | Используемые модули | |------------------|-----------------------------------------------------------------------------------| | Proxy1 | `asyncio`, `socket`, `logging`, `binascii`, `re`, `typing`, `sys` | | Proxy2 | `socket`, `threading`, `binascii`, `signal`, `sys`, `time`, `re` | 3. Конфигурационные параметры (точные имена и значения по умолчанию) python = Общие флаги CONVERT_MODE = 1 = 1 - HEX-конвертация, 0 - прозрачный туннель REPLACE_ON = 0 = 0 - замена текста отключена (заготовка на будущее) = Proxy1 (Asyncio) LISTEN_IP = "10.10.0.1" LISTEN_PORT = 8443 SOURCE_IP = "20.20.0.1" TARGET_HOST = "20.20.0.2" TARGET_PORT = 80 MAX_CLIENTS = 500 IDLE_TIMEOUT = 60 CONNECT_TIMEOUT = 10 TOTAL_TIMEOUT = 300 BUFFER_LIMIT = 65536 LOG_LEVEL = "INFO" = Proxy2 (Threading) LISTEN_IP = "20.20.0.2" LISTEN_PORT = 80 OUTGOING_IP = "20.20.0.2" EXTERNAL_IP = "X.X.0.1" EXTERNAL_PORT = 0 TARGET_HOST = "20.20.0.1" = (резерв/обратный путь) TARGET_PORT = 8443 MAX_CLIENTS = 500 BUFFER_SIZE = 8192 BUFFER_LIMIT = 65536 CONNECT_TIMEOUT = 10 IDLE_TIMEOUT = 60 4. Алгоритмы преобразования данных - Кодирование (`CONVERTER` / `BINARY_TO_HEX`) : `binascii.hexlify(data) + b"\n"` - Декодирование (`DECONVERTER` / `HEX_TO_BINARY`) : - Проверка на пустоту, четность длины строки - Валидация символов `b'0123456789abcdefABCDEF'` - `binascii.unhexlify(hex_str)` - Логирование ошибок при некорректном HEX - Прозрачный режим (`PASS_THROUGH`) : возврат данных без изменений - Разделитель сообщений: `\n` (ASCII 0x0A) 5. Требования к реализации Proxy1 (Asyncio) = Конкурентность и управление соединениями - Использовать `asyncio.start_server` с `backlog=100` - Счетчик `active_connections` защищен `asyncio.Lock()` - Каждый клиент обрабатывается в `handle_client` > создаются две задачи: `client_to_server()` и `server_to_client()` - Ожидание завершения через `asyncio.wait(..., return_when=asyncio.FIRST_COMPLETED)`, отмена оставшихся задач - Фоновая задача `monitor_connections()` каждые 30 сек логирует статус = Сетевые настройки и функции - `connect_to_target()`: создание сокета с `SO_REUSEADDR`, `TCP_NODELAY`, привязка к `SOURCE_IP`, асинхронное подключение к `TARGET_HOST:TARGET_PORT` - `client_to_server()`: чтение до 8192 байт, таймаут `IDLE_TIMEOUT`, вызов `TO_SERVER(data)`, отправка на сервер - `server_to_client()`: буферизация входящих данных, разделение по `b'\n'`, декодирование через `TO_CLIENT(line)`, проверка `BUFFER_LIMIT` - `extract_host_from_request(data)`: парсинг через `re` (`CONNECT`, `GET/POST... https://`, `Host:`) - Инициализация логгера: `logging.basicConfig(level=..., format='%(asctime)s [%(levelname)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S')` 6. Требования к реализации Proxy2 (Threading) = Конкурентность и управление соединениями - Цикл `while server_running: server.accept()` с `socket.settimeout(1.0)` - Каждое соединение обрабатывается в отдельном `threading.Thread(target=handle_client, daemon=True)` - Счетчик `active_connections` защищен `threading.Lock()` - Управление туннелями через `threading.Event()` (`stop_event`) - Graceful shutdown: обработка `SIGINT`/`SIGTERM` через `signal.signal`, ожидание закрытия соединений до 5 сек = Сетевые настройки и функции - `connect_to_internet(host, port, client_addr)`: создание сокета, `TCP_NODELAY`, привязка к `EXTERNAL_IP:EXTERNAL_PORT`, подключение к целевому хосту - `tunnel_to_internet()`: чтение от Proxy1, декодирование HEX>BIN, отправка в Интернет, обработка буфера и `BUFFER_LIMIT` - `tunnel_to_first_proxy()`: чтение от Интернета, кодирование BIN>HEX, отправка в Proxy1 - `parse_connect_request(data)`: извлечение `host:port` из `CONNECT` или абсолютного URL - `send_connect_response(client_sock, client_addr, success)`: отправка `HTTP/1.1 200 Connection established\r\n\r\n` или `502 Bad Gateway` - `handle_client()`: получение первого пакета, декодирование, парсинг хоста, установка соединения, отправка `200`, запуск туннельных потоков, `t1.join()`, `t2.join()` - Вспомогательный вывод: обертка `safe_print(msg)` с `try/except` для избежания `UnicodeEncodeError` 7. Сетевая топология и маршрутизация | Направление | Исходный IP/Порт | Конечный IP/Порт | Режим транспорта | |--------------------------------|--------------------------------------------|----------------------------------------------------------------------------|---------------------------------------------------| | Браузер > Proxy1 | любой | `LISTEN_IP:LISTEN_PORT` (10.10.0.1:8443) | Сырой TCP | | Proxy1 > Proxy2 | `SOURCE_IP` (20.20.0.1) | `TARGET_HOST:TARGET_PORT` (20.20.0.2:80) | TCP, HEX-текст с `\n` | | Proxy2 > Интернет | `EXTERNAL_IP` (192.168.0.1) | `parsed_host:parsed_port` (обычно 443) | Сырой TCP | | Интернет > Proxy2 | целевой сервер | Proxy2 | Сырой TCP | | Proxy2 > Proxy1 | Proxy2 | `TARGET_HOST:TARGET_PORT` (20.20.0.1:8443) | TCP, HEX-текст с `\n` | | Proxy1 > Браузер | Proxy1 | клиент | Декодированный бинарный поток | 8. Требования к логированию, обработке ошибок и граничным условиям - Таймауты : `IDLE_TIMEOUT` на чтение, `CONNECT_TIMEOUT` на установку соединения - Буферизация : при превышении `BUFFER_LIMIT` буфер сбрасывается, в лог пишется предупреждение - Корректность HEX : игнорирование строк нечетной длины или содержащих недопустимые символы с логированием ошибки - Закрытие соединений : проверка `writer.is_closing()`, `try/finally` блоки, обработка `asyncio.CancelledError` и `socket.error`/`BlockingIOError` - Keep-Alive : `SO_KEEPALIVE` и `TCP_NODELAY` на клиентских сокетах Proxy1 - Логирование Proxy2 : только через `safe_print`, без внешних зависимостей 9. Ожидаемый результат Два независимых Python-скрипта: 1. `proxy1.py` – полностью асинхронный, использует `asyncio`, логирование через `logging`, строгая типизация через `typing.Tuple` 2. `proxy2.py` – многопоточный, использует `threading`, сигналы ОС, ручное управление буферами и событиями Оба скрипта должны содержать одинаковые константы `CONVERT_MODE` и `REPLACE_ON`, идентичные алгоритмы HEX-конвертации, парсеры HTTP-заголовков и механизмы ограничения ресурсов, как описано выше. Запуск осуществляется через `python proxy1.py` и `python proxy2.py`. Остановка через `Ctrl+C`. Этот промт содержит все точные имена переменных, параметры конфигурации, функции, модули Python и архитектурные решения , присутствующие в исходных листингах. Его можно использовать как эталонное ТЗ для генерации, аудита или воссоздания кода.