""" Enhanced Outline VPN Server Implementation Core server component with offline access and internet sharing support """ import asyncio import logging import json import os import psutil from typing import Dict, Optional, List, Tuple from .shadowsocks_protocol import ShadowsocksProtocol from .nat_engine import NATEngine from .traffic_router import TrafficRouter from .outline_config import OutlineManager, OutlineConfig from .ikev2_server import IKEv2Server as IPsecManager from .session_tracker import SessionTracker from .port_manager import PortManager logger = logging.getLogger(__name__) class OutlineServer: def __init__(self, config: Dict): self.config = config self.outline_manager = OutlineManager() self.ipsec_manager = IPsecManager(config["server"]["host"], logger) # Initialize authentication from .vpn_auth import VPNAuthManager from .database_init import init_db # Initialize database if not init_db(): raise RuntimeError("Failed to initialize database") self.auth_manager = VPNAuthManager() # Initialize port manager self.port_manager = PortManager() # Initialize components self.nat_engine = NATEngine(logger) self.traffic_router = TrafficRouter({ "vpn_host": "0.0.0.0", # Listen on all interfaces "vpn_port": config["server"]["port"], "virtual_network": config["server"]["virtual_network"] }, logger) # Initialize session tracker for offline support self.session_tracker = SessionTracker() self.sessions = {} self.is_running = False self.bound_ports: List[Tuple[int, str]] = [] # List of (port, service) tuples async def setup_internet_sharing(self): """Configure internet sharing for VPN clients""" try: interface = self.config["server"].get("interface", "eth0") await self.nat_engine.setup_nat(interface) logger.info(f"Internet sharing enabled on interface {interface}") except Exception as e: logger.error(f"Failed to setup internet sharing: {e}") raise async def start(self): """Start the VPN server""" if self.is_running: logger.warning("Outline VPN server is already running") return try: # Clean up any existing connections first await self.cleanup_existing_connections() # Setup network await self.setup_internet_sharing() # Start components in order with proper error handling try: await self.nat_engine.start() except Exception as e: logger.error(f"Failed to start NAT engine: {e}") raise try: await self.traffic_router.start() except Exception as e: await self.nat_engine.stop() logger.error(f"Failed to start traffic router: {e}") raise try: await self.ipsec_manager.start() except Exception as e: await self.nat_engine.stop() await self.traffic_router.stop() logger.error(f"Failed to start IKEv2 service: {e}") raise self.is_running = True logger.info(f"Outline VPN server started successfully on port {self.config['server']['port']}") except Exception as e: logger.error(f"Failed to start server: {e}") await self.stop() # Ensure cleanup on failure raise async def cleanup_existing_connections(self): """Clean up any existing connections before starting""" import psutil current_pid = os.getpid() # Find and clean up any existing VPN processes for proc in psutil.process_iter(['pid', 'name', 'connections']): try: if proc.pid != current_pid: for conn in proc.connections(): if conn.laddr.port == self.config['server']['port']: logger.warning(f"Found existing process using port {self.config['server']['port']}, terminating...") proc.terminate() proc.wait(timeout=5) except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.TimeoutExpired): continue async def stop(self): """Stop the VPN server""" self.is_running = False await self.traffic_router.stop() await self.nat_engine.stop() await self.ipsec_manager.stop() # Close all active sessions for session in self.sessions.values(): await session.close() self.sessions.clear() logger.info("VPN server stopped") async def _handle_client(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter): """Handle new client connections""" peer = writer.get_extra_info('peername') logger.info(f"New connection from {peer}") try: # First packet contains access key first_packet = await reader.read(64) access_key = self._extract_access_key(first_packet) # Validate access key user = self.outline_manager.get_user_by_key(access_key) if not user: logger.warning(f"Invalid access key from {peer}") writer.close() return # Create protocol handler protocol = ShadowsocksProtocol(access_key) self.sessions[user.user_id] = protocol # Handle the connection await protocol.handle_connection(reader, writer) except Exception as e: logger.error(f"Error handling client {peer}: {e}") finally: if not writer.is_closing(): writer.close() await writer.wait_closed() def _extract_access_key(self, packet: bytes) -> str: """Extract access key from initial packet""" return packet[:32].hex() def get_stats(self) -> Dict: """Get server statistics""" return { "is_running": self.is_running, "active_sessions": len(self.sessions), "traffic_stats": self.traffic_router.get_stats(), "nat_stats": self.nat_engine.get_stats() }