unified: Phantom Protocol 2025 complete archive integration
This commit is contained in:
504
release/examples/socks5-proxy.py
Normal file
504
release/examples/socks5-proxy.py
Normal file
@@ -0,0 +1,504 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Phantom SOCKS5 Proxy Server
|
||||
Создает SOCKS5 прокси сервер, который маршрутизирует трафик через Phantom сеть
|
||||
|
||||
Использование:
|
||||
python3 socks5-proxy.py --port 8080 --hops 3
|
||||
|
||||
Автор: Phantom Protocol Team 2025
|
||||
"""
|
||||
|
||||
import socket
|
||||
import threading
|
||||
import struct
|
||||
import select
|
||||
import sys
|
||||
import argparse
|
||||
import time
|
||||
import logging
|
||||
from typing import Optional, Tuple, List
|
||||
|
||||
# Настройка логирования
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# SOCKS5 константы
|
||||
SOCKS5_VERSION = 0x05
|
||||
SOCKS5_CONNECT = 0x01
|
||||
SOCKS5_IPV4 = 0x01
|
||||
SOCKS5_DOMAIN = 0x03
|
||||
SOCKS5_IPV6 = 0x04
|
||||
|
||||
# Коды ответов SOCKS5
|
||||
SOCKS5_SUCCESS = 0x00
|
||||
SOCKS5_GENERAL_FAILURE = 0x01
|
||||
SOCKS5_CONNECTION_NOT_ALLOWED = 0x02
|
||||
SOCKS5_NETWORK_UNREACHABLE = 0x03
|
||||
SOCKS5_HOST_UNREACHABLE = 0x04
|
||||
SOCKS5_CONNECTION_REFUSED = 0x05
|
||||
SOCKS5_TTL_EXPIRED = 0x06
|
||||
SOCKS5_COMMAND_NOT_SUPPORTED = 0x07
|
||||
SOCKS5_ADDRESS_TYPE_NOT_SUPPORTED = 0x08
|
||||
|
||||
class PhantomRoute:
|
||||
"""Представляет маршрут через Phantom сеть"""
|
||||
|
||||
def __init__(self, hops: int = 3):
|
||||
self.hops = hops
|
||||
self.nodes: List[Tuple[str, int]] = []
|
||||
self.build_route()
|
||||
|
||||
def build_route(self):
|
||||
"""Построение маршрута через Phantom узлы"""
|
||||
# В реальной реализации здесь будет запрос к Kademlia DHT
|
||||
# Сейчас используем симуляцию
|
||||
|
||||
logger.info(f"Построение маршрута через {self.hops} хопов...")
|
||||
|
||||
# Симуляция поиска узлов в Phantom сети
|
||||
available_nodes = [
|
||||
("10.0.1.100", 8050),
|
||||
("10.0.1.101", 8050),
|
||||
("10.0.1.102", 8050),
|
||||
("10.0.1.103", 8050),
|
||||
("10.0.1.104", 8050),
|
||||
]
|
||||
|
||||
# Выбор случайных узлов для маршрута
|
||||
import random
|
||||
self.nodes = random.sample(available_nodes, min(self.hops, len(available_nodes)))
|
||||
|
||||
for i, (ip, port) in enumerate(self.nodes):
|
||||
logger.info(f" Хоп {i+1}: {ip}:{port}")
|
||||
|
||||
class PhantomConnection:
|
||||
"""Подключение через Phantom сеть"""
|
||||
|
||||
def __init__(self, route: PhantomRoute):
|
||||
self.route = route
|
||||
self.socket: Optional[socket.socket] = None
|
||||
self.connected = False
|
||||
|
||||
def connect(self, target_host: str, target_port: int) -> bool:
|
||||
"""Подключение к целевому хосту через Phantom сеть"""
|
||||
try:
|
||||
logger.info(f"Подключение к {target_host}:{target_port} через Phantom сеть")
|
||||
|
||||
# Подключение к первому узлу маршрута
|
||||
if not self.route.nodes:
|
||||
logger.error("Маршрут не построен")
|
||||
return False
|
||||
|
||||
first_hop = self.route.nodes[0]
|
||||
logger.info(f"Подключение к первому хопу: {first_hop[0]}:{first_hop[1]}")
|
||||
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.socket.settimeout(10)
|
||||
|
||||
try:
|
||||
self.socket.connect(first_hop)
|
||||
except socket.error as e:
|
||||
logger.error(f"Не удалось подключиться к первому хопу: {e}")
|
||||
# Fallback: прямое подключение для демонстрации
|
||||
logger.info(f"Fallback: прямое подключение к {target_host}:{target_port}")
|
||||
self.socket.close()
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.socket.settimeout(10)
|
||||
self.socket.connect((target_host, target_port))
|
||||
self.connected = True
|
||||
return True
|
||||
|
||||
# Отправка команды построения туннеля
|
||||
tunnel_command = self._build_tunnel_command(target_host, target_port)
|
||||
self.socket.send(tunnel_command.encode('utf-8'))
|
||||
|
||||
# Ожидание подтверждения
|
||||
response = self.socket.recv(1024).decode('utf-8')
|
||||
if response.startswith("PHANTOM_TUNNEL_OK"):
|
||||
logger.info("Туннель через Phantom сеть установлен")
|
||||
self.connected = True
|
||||
return True
|
||||
else:
|
||||
logger.error(f"Ошибка установки туннеля: {response}")
|
||||
# Fallback: прямое подключение
|
||||
logger.info(f"Fallback: прямое подключение к {target_host}:{target_port}")
|
||||
self.socket.close()
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.socket.settimeout(10)
|
||||
self.socket.connect((target_host, target_port))
|
||||
self.connected = True
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка подключения: {e}")
|
||||
return False
|
||||
|
||||
def _build_tunnel_command(self, target_host: str, target_port: int) -> str:
|
||||
"""Построение команды для создания туннеля"""
|
||||
command = f"PHANTOM_BUILD_TUNNEL {target_host} {target_port}"
|
||||
|
||||
# Добавление промежуточных хопов
|
||||
for i in range(1, len(self.route.nodes)):
|
||||
hop = self.route.nodes[i]
|
||||
command += f" {hop[0]}:{hop[1]}"
|
||||
|
||||
return command + "\n"
|
||||
|
||||
def send(self, data: bytes) -> int:
|
||||
"""Отправка данных через Phantom соединение"""
|
||||
if not self.connected or not self.socket:
|
||||
raise ConnectionError("Соединение не установлено")
|
||||
return self.socket.send(data)
|
||||
|
||||
def recv(self, size: int) -> bytes:
|
||||
"""Получение данных через Phantom соединение"""
|
||||
if not self.connected or not self.socket:
|
||||
raise ConnectionError("Соединение не установлено")
|
||||
return self.socket.recv(size)
|
||||
|
||||
def close(self):
|
||||
"""Закрытие соединения"""
|
||||
if self.socket:
|
||||
self.socket.close()
|
||||
self.socket = None
|
||||
self.connected = False
|
||||
|
||||
class SOCKS5Handler:
|
||||
"""Обработчик SOCKS5 протокола"""
|
||||
|
||||
def __init__(self, client_socket: socket.socket, hops: int = 3):
|
||||
self.client_socket = client_socket
|
||||
self.hops = hops
|
||||
self.phantom_connection: Optional[PhantomConnection] = None
|
||||
|
||||
def handle(self):
|
||||
"""Основная обработка SOCKS5 соединения"""
|
||||
try:
|
||||
# Этап 1: Аутентификация
|
||||
if not self._handle_authentication():
|
||||
return
|
||||
|
||||
# Этап 2: Обработка запроса подключения
|
||||
if not self._handle_connection_request():
|
||||
return
|
||||
|
||||
# Этап 3: Проксирование данных
|
||||
self._relay_data()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка обработки SOCKS5: {e}")
|
||||
finally:
|
||||
self._cleanup()
|
||||
|
||||
def _handle_authentication(self) -> bool:
|
||||
"""Обработка этапа аутентификации SOCKS5"""
|
||||
try:
|
||||
# Получение запроса аутентификации
|
||||
data = self.client_socket.recv(256)
|
||||
if len(data) < 2:
|
||||
return False
|
||||
|
||||
version, nmethods = struct.unpack('!BB', data[:2])
|
||||
|
||||
if version != SOCKS5_VERSION:
|
||||
logger.error(f"Неподдерживаемая версия SOCKS: {version}")
|
||||
return False
|
||||
|
||||
if len(data) < 2 + nmethods:
|
||||
return False
|
||||
|
||||
methods = struct.unpack('!' + 'B' * nmethods, data[2:2+nmethods])
|
||||
|
||||
# Поддерживаем только "без аутентификации" (метод 0)
|
||||
if 0 in methods:
|
||||
# Отправка ответа: версия + выбранный метод
|
||||
response = struct.pack('!BB', SOCKS5_VERSION, 0)
|
||||
self.client_socket.send(response)
|
||||
logger.debug("Аутентификация SOCKS5 пройдена")
|
||||
return True
|
||||
else:
|
||||
# Нет поддерживаемых методов
|
||||
response = struct.pack('!BB', SOCKS5_VERSION, 0xFF)
|
||||
self.client_socket.send(response)
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка аутентификации SOCKS5: {e}")
|
||||
return False
|
||||
|
||||
def _handle_connection_request(self) -> bool:
|
||||
"""Обработка запроса подключения SOCKS5"""
|
||||
try:
|
||||
# Получение запроса подключения
|
||||
data = self.client_socket.recv(4)
|
||||
if len(data) < 4:
|
||||
return False
|
||||
|
||||
version, cmd, reserved, address_type = struct.unpack('!BBBB', data)
|
||||
|
||||
if version != SOCKS5_VERSION:
|
||||
self._send_error_response(SOCKS5_GENERAL_FAILURE)
|
||||
return False
|
||||
|
||||
if cmd != SOCKS5_CONNECT:
|
||||
self._send_error_response(SOCKS5_COMMAND_NOT_SUPPORTED)
|
||||
return False
|
||||
|
||||
# Получение адреса назначения
|
||||
target_host, target_port = self._parse_target_address(address_type)
|
||||
if not target_host:
|
||||
self._send_error_response(SOCKS5_ADDRESS_TYPE_NOT_SUPPORTED)
|
||||
return False
|
||||
|
||||
logger.info(f"SOCKS5 запрос подключения к {target_host}:{target_port}")
|
||||
|
||||
# Подключение через Phantom сеть
|
||||
route = PhantomRoute(self.hops)
|
||||
self.phantom_connection = PhantomConnection(route)
|
||||
|
||||
if self.phantom_connection.connect(target_host, target_port):
|
||||
# Отправка успешного ответа
|
||||
self._send_success_response()
|
||||
logger.info(f"SOCKS5 туннель к {target_host}:{target_port} установлен")
|
||||
return True
|
||||
else:
|
||||
self._send_error_response(SOCKS5_HOST_UNREACHABLE)
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка обработки запроса подключения: {e}")
|
||||
self._send_error_response(SOCKS5_GENERAL_FAILURE)
|
||||
return False
|
||||
|
||||
def _parse_target_address(self, address_type: int) -> Tuple[Optional[str], Optional[int]]:
|
||||
"""Парсинг адреса назначения"""
|
||||
try:
|
||||
if address_type == SOCKS5_IPV4:
|
||||
# IPv4 адрес (4 байта) + порт (2 байта)
|
||||
data = self.client_socket.recv(6)
|
||||
if len(data) < 6:
|
||||
return None, None
|
||||
|
||||
ip_bytes = data[:4]
|
||||
port_bytes = data[4:6]
|
||||
|
||||
ip = socket.inet_ntoa(ip_bytes)
|
||||
port = struct.unpack('!H', port_bytes)[0]
|
||||
|
||||
return ip, port
|
||||
|
||||
elif address_type == SOCKS5_DOMAIN:
|
||||
# Доменное имя: длина (1 байт) + имя + порт (2 байта)
|
||||
length_data = self.client_socket.recv(1)
|
||||
if len(length_data) < 1:
|
||||
return None, None
|
||||
|
||||
domain_length = struct.unpack('!B', length_data)[0]
|
||||
domain_data = self.client_socket.recv(domain_length + 2)
|
||||
|
||||
if len(domain_data) < domain_length + 2:
|
||||
return None, None
|
||||
|
||||
domain = domain_data[:domain_length].decode('utf-8')
|
||||
port = struct.unpack('!H', domain_data[domain_length:])[0]
|
||||
|
||||
return domain, port
|
||||
|
||||
elif address_type == SOCKS5_IPV6:
|
||||
# IPv6 адрес (16 байт) + порт (2 байта)
|
||||
data = self.client_socket.recv(18)
|
||||
if len(data) < 18:
|
||||
return None, None
|
||||
|
||||
ip_bytes = data[:16]
|
||||
port_bytes = data[16:18]
|
||||
|
||||
ip = socket.inet_ntop(socket.AF_INET6, ip_bytes)
|
||||
port = struct.unpack('!H', port_bytes)[0]
|
||||
|
||||
return ip, port
|
||||
|
||||
return None, None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка парсинга адреса: {e}")
|
||||
return None, None
|
||||
|
||||
def _send_success_response(self):
|
||||
"""Отправка успешного ответа SOCKS5"""
|
||||
# Версия + статус + зарезервировано + тип адреса
|
||||
response = struct.pack('!BBBB', SOCKS5_VERSION, SOCKS5_SUCCESS, 0, SOCKS5_IPV4)
|
||||
# Связанный адрес (0.0.0.0) + порт (0)
|
||||
response += struct.pack('!IH', 0, 0)
|
||||
self.client_socket.send(response)
|
||||
|
||||
def _send_error_response(self, error_code: int):
|
||||
"""Отправка ответа об ошибке SOCKS5"""
|
||||
response = struct.pack('!BBBB', SOCKS5_VERSION, error_code, 0, SOCKS5_IPV4)
|
||||
response += struct.pack('!IH', 0, 0)
|
||||
self.client_socket.send(response)
|
||||
|
||||
def _relay_data(self):
|
||||
"""Проксирование данных между клиентом и Phantom соединением"""
|
||||
if not self.phantom_connection:
|
||||
return
|
||||
|
||||
logger.debug("Начало проксирования данных")
|
||||
|
||||
try:
|
||||
while True:
|
||||
# Ожидание данных от клиента или сервера
|
||||
ready_sockets, _, error_sockets = select.select(
|
||||
[self.client_socket, self.phantom_connection.socket],
|
||||
[],
|
||||
[self.client_socket, self.phantom_connection.socket],
|
||||
1.0 # Таймаут 1 секунда
|
||||
)
|
||||
|
||||
if error_sockets:
|
||||
logger.debug("Ошибка в сокетах, завершение проксирования")
|
||||
break
|
||||
|
||||
if not ready_sockets:
|
||||
continue
|
||||
|
||||
# Данные от клиента к серверу
|
||||
if self.client_socket in ready_sockets:
|
||||
data = self.client_socket.recv(4096)
|
||||
if not data:
|
||||
logger.debug("Клиент закрыл соединение")
|
||||
break
|
||||
|
||||
self.phantom_connection.send(data)
|
||||
logger.debug(f"Отправлено {len(data)} байт через Phantom")
|
||||
|
||||
# Данные от сервера к клиенту
|
||||
if self.phantom_connection.socket in ready_sockets:
|
||||
data = self.phantom_connection.recv(4096)
|
||||
if not data:
|
||||
logger.debug("Сервер закрыл соединение")
|
||||
break
|
||||
|
||||
self.client_socket.send(data)
|
||||
logger.debug(f"Получено {len(data)} байт от Phantom")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка проксирования данных: {e}")
|
||||
|
||||
def _cleanup(self):
|
||||
"""Очистка ресурсов"""
|
||||
try:
|
||||
if self.phantom_connection:
|
||||
self.phantom_connection.close()
|
||||
self.client_socket.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
class PhantomSOCKS5Server:
|
||||
"""SOCKS5 прокси сервер через Phantom сеть"""
|
||||
|
||||
def __init__(self, host: str = '127.0.0.1', port: int = 8080, hops: int = 3):
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.hops = hops
|
||||
self.server_socket: Optional[socket.socket] = None
|
||||
self.running = False
|
||||
|
||||
def start(self):
|
||||
"""Запуск SOCKS5 сервера"""
|
||||
try:
|
||||
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
self.server_socket.bind((self.host, self.port))
|
||||
self.server_socket.listen(10)
|
||||
|
||||
self.running = True
|
||||
|
||||
logger.info(f"🚀 Phantom SOCKS5 прокси запущен на {self.host}:{self.port}")
|
||||
logger.info(f" Количество хопов через Phantom сеть: {self.hops}")
|
||||
logger.info(f" Настройте ваш браузер или приложение:")
|
||||
logger.info(f" - Тип прокси: SOCKS v5")
|
||||
logger.info(f" - Адрес: {self.host}")
|
||||
logger.info(f" - Порт: {self.port}")
|
||||
logger.info(f" - Аутентификация: Нет")
|
||||
|
||||
while self.running:
|
||||
try:
|
||||
client_socket, client_address = self.server_socket.accept()
|
||||
logger.info(f"Новое SOCKS5 подключение от {client_address[0]}:{client_address[1]}")
|
||||
|
||||
# Обработка клиента в отдельном потоке
|
||||
handler = SOCKS5Handler(client_socket, self.hops)
|
||||
client_thread = threading.Thread(target=handler.handle)
|
||||
client_thread.daemon = True
|
||||
client_thread.start()
|
||||
|
||||
except socket.error as e:
|
||||
if self.running:
|
||||
logger.error(f"Ошибка принятия подключения: {e}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка запуска сервера: {e}")
|
||||
finally:
|
||||
self.stop()
|
||||
|
||||
def stop(self):
|
||||
"""Остановка SOCKS5 сервера"""
|
||||
self.running = False
|
||||
if self.server_socket:
|
||||
self.server_socket.close()
|
||||
logger.info("Phantom SOCKS5 прокси остановлен")
|
||||
|
||||
def main():
|
||||
"""Главная функция"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Phantom SOCKS5 Proxy - SOCKS5 прокси через Phantom сеть'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--host',
|
||||
default='127.0.0.1',
|
||||
help='IP адрес для прослушивания (по умолчанию: 127.0.0.1)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--port',
|
||||
type=int,
|
||||
default=8080,
|
||||
help='Порт для прослушивания (по умолчанию: 8080)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--hops',
|
||||
type=int,
|
||||
default=3,
|
||||
help='Количество хопов через Phantom сеть (по умолчанию: 3)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--verbose',
|
||||
action='store_true',
|
||||
help='Подробный вывод'
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.verbose:
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
|
||||
# Создание и запуск SOCKS5 сервера
|
||||
server = PhantomSOCKS5Server(args.host, args.port, args.hops)
|
||||
|
||||
try:
|
||||
server.start()
|
||||
except KeyboardInterrupt:
|
||||
logger.info("Получен сигнал прерывания, остановка сервера...")
|
||||
server.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
504
release/examples/vpn-client.py
Normal file
504
release/examples/vpn-client.py
Normal file
@@ -0,0 +1,504 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Phantom VPN Client
|
||||
Создает VPN подключение через Phantom сеть с использованием TUN интерфейса
|
||||
|
||||
Использование:
|
||||
sudo python3 vpn-client.py --server phantom-vpn.local --interface tun0
|
||||
|
||||
Автор: Phantom Protocol Team 2025
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import socket
|
||||
import struct
|
||||
import select
|
||||
import threading
|
||||
import argparse
|
||||
import logging
|
||||
import time
|
||||
import subprocess
|
||||
import fcntl
|
||||
from typing import Optional, Tuple
|
||||
|
||||
# Проверка прав root
|
||||
if os.geteuid() != 0:
|
||||
print("Ошибка: VPN клиент требует права root для создания TUN интерфейса")
|
||||
print("Запустите: sudo python3 vpn-client.py")
|
||||
sys.exit(1)
|
||||
|
||||
# Настройка логирования
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Константы для TUN интерфейса
|
||||
TUNSETIFF = 0x400454ca
|
||||
IFF_TUN = 0x0001
|
||||
IFF_NO_PI = 0x1000
|
||||
|
||||
class PhantomVPNClient:
|
||||
"""VPN клиент через Phantom сеть"""
|
||||
|
||||
def __init__(self, server_host: str, server_port: int = 1194,
|
||||
interface: str = 'tun0', hops: int = 3):
|
||||
self.server_host = server_host
|
||||
self.server_port = server_port
|
||||
self.interface = interface
|
||||
self.hops = hops
|
||||
|
||||
self.tun_fd: Optional[int] = None
|
||||
self.phantom_socket: Optional[socket.socket] = None
|
||||
self.running = False
|
||||
|
||||
# Сетевые настройки
|
||||
self.local_ip = "10.8.0.2"
|
||||
self.remote_ip = "10.8.0.1"
|
||||
self.netmask = "255.255.255.0"
|
||||
self.mtu = 1500
|
||||
|
||||
def create_tun_interface(self) -> bool:
|
||||
"""Создание TUN интерфейса"""
|
||||
try:
|
||||
logger.info(f"Создание TUN интерфейса {self.interface}")
|
||||
|
||||
# Открытие /dev/net/tun
|
||||
self.tun_fd = os.open('/dev/net/tun', os.O_RDWR)
|
||||
|
||||
# Настройка интерфейса
|
||||
ifr = struct.pack('16sH', self.interface.encode('utf-8'), IFF_TUN | IFF_NO_PI)
|
||||
fcntl.ioctl(self.tun_fd, TUNSETIFF, ifr)
|
||||
|
||||
logger.info(f"TUN интерфейс {self.interface} создан")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка создания TUN интерфейса: {e}")
|
||||
return False
|
||||
|
||||
def configure_interface(self) -> bool:
|
||||
"""Настройка сетевого интерфейса"""
|
||||
try:
|
||||
logger.info(f"Настройка интерфейса {self.interface}")
|
||||
|
||||
# Назначение IP адреса
|
||||
cmd = f"ip addr add {self.local_ip}/24 dev {self.interface}"
|
||||
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
logger.error(f"Ошибка назначения IP: {result.stderr}")
|
||||
return False
|
||||
|
||||
# Поднятие интерфейса
|
||||
cmd = f"ip link set {self.interface} up"
|
||||
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
logger.error(f"Ошибка поднятия интерфейса: {result.stderr}")
|
||||
return False
|
||||
|
||||
# Настройка MTU
|
||||
cmd = f"ip link set {self.interface} mtu {self.mtu}"
|
||||
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
logger.warning(f"Не удалось установить MTU: {result.stderr}")
|
||||
|
||||
logger.info(f"Интерфейс {self.interface} настроен: {self.local_ip}/24")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка настройки интерфейса: {e}")
|
||||
return False
|
||||
|
||||
def setup_routing(self) -> bool:
|
||||
"""Настройка маршрутизации"""
|
||||
try:
|
||||
logger.info("Настройка маршрутизации для VPN")
|
||||
|
||||
# Сохранение текущего шлюза по умолчанию
|
||||
result = subprocess.run("ip route show default", shell=True,
|
||||
capture_output=True, text=True)
|
||||
if result.returncode == 0 and result.stdout.strip():
|
||||
self.original_gateway = result.stdout.strip()
|
||||
logger.info(f"Сохранен оригинальный шлюз: {self.original_gateway}")
|
||||
|
||||
# Добавление маршрута для VPN сервера через оригинальный шлюз
|
||||
# (чтобы избежать рекурсии)
|
||||
if hasattr(self, 'original_gateway'):
|
||||
gateway_ip = self.original_gateway.split()[2]
|
||||
cmd = f"ip route add {self.server_host}/32 via {gateway_ip}"
|
||||
subprocess.run(cmd, shell=True)
|
||||
|
||||
# Настройка маршрута по умолчанию через VPN
|
||||
cmd = f"ip route add 0.0.0.0/1 dev {self.interface}"
|
||||
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
logger.warning(f"Не удалось добавить маршрут 0.0.0.0/1: {result.stderr}")
|
||||
|
||||
cmd = f"ip route add 128.0.0.0/1 dev {self.interface}"
|
||||
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
logger.warning(f"Не удалось добавить маршрут 128.0.0.0/1: {result.stderr}")
|
||||
|
||||
logger.info("Маршрутизация настроена")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка настройки маршрутизации: {e}")
|
||||
return False
|
||||
|
||||
def connect_to_phantom_server(self) -> bool:
|
||||
"""Подключение к VPN серверу через Phantom сеть"""
|
||||
try:
|
||||
logger.info(f"Подключение к VPN серверу {self.server_host}:{self.server_port}")
|
||||
logger.info(f"Маршрут через {self.hops} хопов Phantom сети")
|
||||
|
||||
# Создание сокета
|
||||
self.phantom_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.phantom_socket.settimeout(30)
|
||||
|
||||
# В реальной реализации здесь будет построение маршрута через Phantom
|
||||
# Сейчас используем прямое подключение для демонстрации
|
||||
try:
|
||||
# Попытка подключения через Phantom (симуляция)
|
||||
phantom_gateway = "127.0.0.1" # Локальный Phantom узел
|
||||
phantom_port = 8050
|
||||
|
||||
logger.info(f"Подключение через Phantom gateway {phantom_gateway}:{phantom_port}")
|
||||
self.phantom_socket.connect((phantom_gateway, phantom_port))
|
||||
|
||||
# Отправка команды VPN туннеля
|
||||
vpn_command = f"PHANTOM_VPN_CONNECT {self.server_host} {self.server_port} {self.hops}\n"
|
||||
self.phantom_socket.send(vpn_command.encode('utf-8'))
|
||||
|
||||
# Ожидание подтверждения
|
||||
response = self.phantom_socket.recv(1024).decode('utf-8')
|
||||
if response.startswith("PHANTOM_VPN_OK"):
|
||||
logger.info("VPN туннель через Phantom сеть установлен")
|
||||
return True
|
||||
else:
|
||||
logger.error(f"Ошибка установки VPN туннеля: {response}")
|
||||
return False
|
||||
|
||||
except socket.error:
|
||||
# Fallback: прямое подключение для демонстрации
|
||||
logger.info(f"Fallback: прямое подключение к {self.server_host}:{self.server_port}")
|
||||
self.phantom_socket.close()
|
||||
self.phantom_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.phantom_socket.settimeout(30)
|
||||
self.phantom_socket.connect((self.server_host, self.server_port))
|
||||
|
||||
# Простой VPN handshake
|
||||
handshake = b"PHANTOM_VPN_CLIENT_HELLO\n"
|
||||
self.phantom_socket.send(handshake)
|
||||
|
||||
response = self.phantom_socket.recv(1024)
|
||||
if b"VPN_SERVER_HELLO" in response:
|
||||
logger.info("VPN handshake завершен")
|
||||
return True
|
||||
else:
|
||||
logger.error("Ошибка VPN handshake")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка подключения к VPN серверу: {e}")
|
||||
return False
|
||||
|
||||
def start_packet_forwarding(self):
|
||||
"""Запуск пересылки пакетов между TUN и Phantom сокетом"""
|
||||
logger.info("Запуск пересылки пакетов")
|
||||
|
||||
# Поток для пересылки пакетов от TUN к сокету
|
||||
tun_to_socket_thread = threading.Thread(target=self._forward_tun_to_socket)
|
||||
tun_to_socket_thread.daemon = True
|
||||
tun_to_socket_thread.start()
|
||||
|
||||
# Поток для пересылки пакетов от сокета к TUN
|
||||
socket_to_tun_thread = threading.Thread(target=self._forward_socket_to_tun)
|
||||
socket_to_tun_thread.daemon = True
|
||||
socket_to_tun_thread.start()
|
||||
|
||||
return tun_to_socket_thread, socket_to_tun_thread
|
||||
|
||||
def _forward_tun_to_socket(self):
|
||||
"""Пересылка пакетов от TUN интерфейса к Phantom сокету"""
|
||||
logger.debug("Поток TUN → Socket запущен")
|
||||
|
||||
while self.running:
|
||||
try:
|
||||
# Чтение пакета из TUN интерфейса
|
||||
ready, _, _ = select.select([self.tun_fd], [], [], 1.0)
|
||||
if not ready:
|
||||
continue
|
||||
|
||||
packet = os.read(self.tun_fd, 2048)
|
||||
if not packet:
|
||||
continue
|
||||
|
||||
# Отправка пакета через Phantom сокет
|
||||
# Добавляем заголовок с длиной пакета
|
||||
packet_length = struct.pack('!H', len(packet))
|
||||
self.phantom_socket.send(packet_length + packet)
|
||||
|
||||
logger.debug(f"TUN → Socket: {len(packet)} байт")
|
||||
|
||||
except Exception as e:
|
||||
if self.running:
|
||||
logger.error(f"Ошибка пересылки TUN → Socket: {e}")
|
||||
break
|
||||
|
||||
logger.debug("Поток TUN → Socket завершен")
|
||||
|
||||
def _forward_socket_to_tun(self):
|
||||
"""Пересылка пакетов от Phantom сокета к TUN интерфейсу"""
|
||||
logger.debug("Поток Socket → TUN запущен")
|
||||
|
||||
while self.running:
|
||||
try:
|
||||
# Чтение заголовка с длиной пакета
|
||||
ready, _, _ = select.select([self.phantom_socket], [], [], 1.0)
|
||||
if not ready:
|
||||
continue
|
||||
|
||||
length_data = self._recv_exact(self.phantom_socket, 2)
|
||||
if not length_data:
|
||||
continue
|
||||
|
||||
packet_length = struct.unpack('!H', length_data)[0]
|
||||
|
||||
# Чтение самого пакета
|
||||
packet = self._recv_exact(self.phantom_socket, packet_length)
|
||||
if not packet:
|
||||
continue
|
||||
|
||||
# Запись пакета в TUN интерфейс
|
||||
os.write(self.tun_fd, packet)
|
||||
|
||||
logger.debug(f"Socket → TUN: {len(packet)} байт")
|
||||
|
||||
except Exception as e:
|
||||
if self.running:
|
||||
logger.error(f"Ошибка пересылки Socket → TUN: {e}")
|
||||
break
|
||||
|
||||
logger.debug("Поток Socket → TUN завершен")
|
||||
|
||||
def _recv_exact(self, sock: socket.socket, length: int) -> bytes:
|
||||
"""Получение точного количества байт из сокета"""
|
||||
data = b''
|
||||
while len(data) < length:
|
||||
chunk = sock.recv(length - len(data))
|
||||
if not chunk:
|
||||
return b''
|
||||
data += chunk
|
||||
return data
|
||||
|
||||
def start(self) -> bool:
|
||||
"""Запуск VPN клиента"""
|
||||
try:
|
||||
logger.info("🚀 Запуск Phantom VPN Client")
|
||||
|
||||
# Создание TUN интерфейса
|
||||
if not self.create_tun_interface():
|
||||
return False
|
||||
|
||||
# Настройка интерфейса
|
||||
if not self.configure_interface():
|
||||
return False
|
||||
|
||||
# Подключение к VPN серверу
|
||||
if not self.connect_to_phantom_server():
|
||||
return False
|
||||
|
||||
# Настройка маршрутизации
|
||||
if not self.setup_routing():
|
||||
return False
|
||||
|
||||
self.running = True
|
||||
|
||||
# Запуск пересылки пакетов
|
||||
threads = self.start_packet_forwarding()
|
||||
|
||||
logger.info("✅ Phantom VPN подключение установлено")
|
||||
logger.info(f" Локальный IP: {self.local_ip}")
|
||||
logger.info(f" VPN сервер: {self.server_host}:{self.server_port}")
|
||||
logger.info(f" Интерфейс: {self.interface}")
|
||||
logger.info(f" Хопов через Phantom: {self.hops}")
|
||||
logger.info(" Весь трафик теперь проходит через Phantom VPN")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка запуска VPN клиента: {e}")
|
||||
return False
|
||||
|
||||
def stop(self):
|
||||
"""Остановка VPN клиента"""
|
||||
logger.info("Остановка Phantom VPN Client")
|
||||
|
||||
self.running = False
|
||||
|
||||
# Восстановление маршрутизации
|
||||
self._restore_routing()
|
||||
|
||||
# Закрытие сокета
|
||||
if self.phantom_socket:
|
||||
self.phantom_socket.close()
|
||||
self.phantom_socket = None
|
||||
|
||||
# Удаление TUN интерфейса
|
||||
if self.tun_fd:
|
||||
os.close(self.tun_fd)
|
||||
self.tun_fd = None
|
||||
|
||||
# Удаление интерфейса
|
||||
subprocess.run(f"ip link delete {self.interface}", shell=True)
|
||||
|
||||
logger.info("Phantom VPN Client остановлен")
|
||||
|
||||
def _restore_routing(self):
|
||||
"""Восстановление оригинальной маршрутизации"""
|
||||
try:
|
||||
logger.info("Восстановление оригинальной маршрутизации")
|
||||
|
||||
# Удаление VPN маршрутов
|
||||
subprocess.run(f"ip route del 0.0.0.0/1 dev {self.interface}", shell=True)
|
||||
subprocess.run(f"ip route del 128.0.0.0/1 dev {self.interface}", shell=True)
|
||||
|
||||
# Удаление маршрута к VPN серверу
|
||||
if hasattr(self, 'original_gateway'):
|
||||
subprocess.run(f"ip route del {self.server_host}/32", shell=True)
|
||||
|
||||
logger.info("Маршрутизация восстановлена")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка восстановления маршрутизации: {e}")
|
||||
|
||||
def show_status(self):
|
||||
"""Показ статуса VPN подключения"""
|
||||
print("\n=== Статус Phantom VPN ===")
|
||||
print(f"Состояние: {'Подключен' if self.running else 'Отключен'}")
|
||||
|
||||
if self.running:
|
||||
print(f"VPN сервер: {self.server_host}:{self.server_port}")
|
||||
print(f"Локальный IP: {self.local_ip}")
|
||||
print(f"Интерфейс: {self.interface}")
|
||||
print(f"Хопов через Phantom: {self.hops}")
|
||||
|
||||
# Показ статистики интерфейса
|
||||
try:
|
||||
result = subprocess.run(f"ip -s link show {self.interface}",
|
||||
shell=True, capture_output=True, text=True)
|
||||
if result.returncode == 0:
|
||||
print("\nСтатистика интерфейса:")
|
||||
lines = result.stdout.split('\n')
|
||||
for line in lines:
|
||||
if 'RX:' in line or 'TX:' in line or 'bytes' in line:
|
||||
print(f" {line.strip()}")
|
||||
except:
|
||||
pass
|
||||
|
||||
# Проверка внешнего IP
|
||||
try:
|
||||
import urllib.request
|
||||
with urllib.request.urlopen('http://httpbin.org/ip', timeout=5) as response:
|
||||
ip_info = response.read().decode('utf-8')
|
||||
print(f"\nВнешний IP: {ip_info.strip()}")
|
||||
except:
|
||||
print("\nВнешний IP: Не удалось определить")
|
||||
|
||||
print("========================\n")
|
||||
|
||||
def main():
|
||||
"""Главная функция"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Phantom VPN Client - VPN клиент через Phantom сеть'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--server',
|
||||
required=True,
|
||||
help='Адрес VPN сервера'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--port',
|
||||
type=int,
|
||||
default=1194,
|
||||
help='Порт VPN сервера (по умолчанию: 1194)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--interface',
|
||||
default='tun0',
|
||||
help='Имя TUN интерфейса (по умолчанию: tun0)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--hops',
|
||||
type=int,
|
||||
default=3,
|
||||
help='Количество хопов через Phantom сеть (по умолчанию: 3)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--local-ip',
|
||||
default='10.8.0.2',
|
||||
help='Локальный IP адрес (по умолчанию: 10.8.0.2)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--verbose',
|
||||
action='store_true',
|
||||
help='Подробный вывод'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--status',
|
||||
action='store_true',
|
||||
help='Показать статус и выйти'
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.verbose:
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
|
||||
# Создание VPN клиента
|
||||
vpn_client = PhantomVPNClient(
|
||||
server_host=args.server,
|
||||
server_port=args.port,
|
||||
interface=args.interface,
|
||||
hops=args.hops
|
||||
)
|
||||
|
||||
vpn_client.local_ip = args.local_ip
|
||||
|
||||
if args.status:
|
||||
vpn_client.show_status()
|
||||
return
|
||||
|
||||
try:
|
||||
# Запуск VPN клиента
|
||||
if vpn_client.start():
|
||||
print("\nPhantom VPN подключен. Нажмите Ctrl+C для отключения")
|
||||
|
||||
# Основной цикл
|
||||
while vpn_client.running:
|
||||
time.sleep(1)
|
||||
|
||||
else:
|
||||
logger.error("Не удалось запустить VPN клиент")
|
||||
sys.exit(1)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logger.info("Получен сигнал прерывания, отключение VPN...")
|
||||
except Exception as e:
|
||||
logger.error(f"Критическая ошибка: {e}")
|
||||
finally:
|
||||
vpn_client.stop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user