Spaces:
Runtime error
Runtime error
File size: 6,847 Bytes
6a5b8d8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
"""
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()
}
|