1+ import NetworkExtension
2+ import Foundation
3+
4+ class VPNManager {
5+ private var vpnManager : NETunnelProviderManager ?
6+ private let profileName = " Controller "
7+ private var isInitialized = false
8+ private var initializationCompletion : ( ( Error ? ) -> Void ) ?
9+ static var sharedInstance : VPNManager ?
10+
11+ init ( ) {
12+ print ( " VPNManager: Initializing... " )
13+ loadVPNManager { error in
14+ if let error = error {
15+ print ( " VPNManager: Initialization failed with error: \( error. localizedDescription) " )
16+ } else {
17+ print ( " VPNManager: Initialization completed successfully " )
18+ }
19+ self . isInitialized = true
20+ self . initializationCompletion ? ( error)
21+ self . initializationCompletion = nil
22+ }
23+ }
24+
25+ private func loadVPNManager( completion: @escaping ( Error ? ) -> Void ) {
26+ print ( " VPNManager: Loading existing VPN managers... " )
27+ NETunnelProviderManager . loadAllFromPreferences { managers, error in
28+ if let error = error {
29+ print ( " VPNManager: Error loading VPN managers: \( error. localizedDescription) " )
30+ self . vpnManager = NETunnelProviderManager ( )
31+ self . vpnManager? . localizedDescription = self . profileName
32+ print ( " VPNManager: Created new VPN profile due to error: \( self . profileName) " )
33+ completion ( nil )
34+ return
35+ }
36+
37+ print ( " VPNManager: Loaded \( managers? . count ?? 0 ) VPN managers " )
38+ if let existingManager = managers? . first ( where: { $0. localizedDescription == self . profileName } ) {
39+ self . vpnManager = existingManager
40+ print ( " VPNManager: Found existing VPN profile: \( self . profileName) " )
41+ } else {
42+ self . vpnManager = NETunnelProviderManager ( )
43+ self . vpnManager? . localizedDescription = self . profileName
44+ print ( " VPNManager: Created new VPN profile: \( self . profileName) " )
45+ }
46+ print ( " VPNManager: vpnManager is \( self . vpnManager != nil ? " set " : " nil " ) " )
47+ completion ( nil )
48+ }
49+
50+ // Таймаут для loadAllFromPreferences
51+ DispatchQueue . global ( ) . asyncAfter ( deadline: . now( ) + 5 ) {
52+ if !self . isInitialized {
53+ print ( " VPNManager: loadAllFromPreferences timed out " )
54+ self . vpnManager = NETunnelProviderManager ( )
55+ self . vpnManager? . localizedDescription = self . profileName
56+ print ( " VPNManager: Created new VPN profile due to timeout: \( self . profileName) " )
57+ self . isInitialized = true
58+ completion ( nil )
59+ }
60+ }
61+ }
62+
63+ private func waitForInitialization( completion: @escaping ( Error ? ) -> Void ) {
64+ if isInitialized {
65+ print ( " VPNManager: Already initialized " )
66+ completion ( nil )
67+ return
68+ }
69+ print ( " VPNManager: Waiting for initialization... " )
70+ initializationCompletion = completion
71+ }
72+
73+ func setupVPNConfiguration( tunAddr: String , tunMask: String , tunDns: String , socks5Proxy: String , completion: @escaping ( Error ? ) -> Void ) {
74+ print ( " VPNManager: Setting up VPN configuration with tunAddr= \( tunAddr) , tunMask= \( tunMask) , tunDns= \( tunDns) , socks5Proxy= \( socks5Proxy) " )
75+ waitForInitialization { error in
76+ if let error = error {
77+ completion ( error)
78+ return
79+ }
80+ guard let vpnManager = self . vpnManager else {
81+ print ( " VPNManager: VPN Manager not initialized " )
82+ completion ( NSError ( domain: " VPNError " , code: - 1 , userInfo: [ NSLocalizedDescriptionKey: " VPN Manager not initialized " ] ) )
83+ return
84+ }
85+
86+ vpnManager. loadFromPreferences { error in
87+ if let error = error {
88+ print ( " VPNManager: Load preferences error: \( error. localizedDescription) " )
89+ completion ( error)
90+ return
91+ }
92+
93+ let tunnelProtocol = NETunnelProviderProtocol ( )
94+ tunnelProtocol. providerBundleIdentifier = " click.vpnclient.VPNclientTunnel "
95+ tunnelProtocol. serverAddress = socks5Proxy
96+ tunnelProtocol. providerConfiguration = [
97+ " tunAddr " : tunAddr,
98+ " tunMask " : tunMask,
99+ " tunDns " : tunDns,
100+ " socks5Proxy " : socks5Proxy
101+ ]
102+
103+ vpnManager. protocolConfiguration = tunnelProtocol
104+ vpnManager. isEnabled = true
105+ vpnManager. isOnDemandEnabled = false
106+
107+ print ( " VPNManager: Saving VPN configuration... " )
108+ vpnManager. saveToPreferences { error in
109+ if let error = error {
110+ print ( " VPNManager: Save preferences error: \( error. localizedDescription) " )
111+ completion ( error)
112+ } else {
113+ print ( " VPNManager: VPN configuration saved successfully " )
114+ completion ( nil )
115+ }
116+ }
117+ }
118+ }
119+ }
120+
121+ func startVPN( completion: @escaping ( Error ? ) -> Void ) {
122+ print ( " VPNManager: Starting VPN... " )
123+ waitForInitialization { error in
124+ if let error = error {
125+ completion ( error)
126+ return
127+ }
128+ guard let vpnManager = self . vpnManager else {
129+ print ( " VPNManager: VPN Manager not initialized " )
130+ completion ( NSError ( domain: " VPNError " , code: - 1 , userInfo: [ NSLocalizedDescriptionKey: " VPN Manager not initialized " ] ) )
131+ return
132+ }
133+ vpnManager. loadFromPreferences { error in
134+ if let error = error {
135+ print ( " VPNManager: Load preferences error before start: \( error. localizedDescription) " )
136+ completion ( error)
137+ return
138+ }
139+ do {
140+ try vpnManager. connection. startVPNTunnel ( )
141+ print ( " VPNManager: VPN tunnel started successfully " )
142+ completion ( nil )
143+ } catch {
144+ print ( " VPNManager: Start VPN error: \( error. localizedDescription) " )
145+ completion ( error)
146+ }
147+ }
148+ }
149+ }
150+
151+ func stopVPN( completion: @escaping ( ) -> Void ) {
152+ print ( " VPNManager: Stopping VPN... " )
153+ waitForInitialization { _ in
154+ self . vpnManager? . connection. stopVPNTunnel ( )
155+ completion ( )
156+ }
157+ }
158+
159+ var vpnStatus : NEVPNStatus {
160+ let status = vpnManager? . connection. status ?? . invalid
161+ print ( " VPNManager: Current VPN status: \( status. description) " )
162+ return status
163+ }
164+
165+ static func cleanup( ) {
166+ sharedInstance = nil
167+ }
168+ }
169+
170+ extension NEVPNStatus {
171+ var description : String {
172+ switch self {
173+ case . disconnected: return " Disconnected "
174+ case . connecting: return " Connecting... "
175+ case . connected: return " Connected "
176+ case . disconnecting: return " Disconnecting... "
177+ default : return " Not Added Profile "
178+ }
179+ }
180+ }
0 commit comments