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()
        }