Skip to content

Commit 71fe09a

Browse files
committed
added base structure to creating vpn profile
1 parent 1aab1d3 commit 71fe09a

10 files changed

Lines changed: 833 additions & 102 deletions

File tree

ios/Runner.xcodeproj/project.pbxproj

Lines changed: 239 additions & 7 deletions
Large diffs are not rendered by default.

ios/Runner/AppDelegate.swift

Lines changed: 64 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,75 @@
11
import Flutter
22
import UIKit
3-
///VPN
4-
///import Foundation
53
import NetworkExtension
64

7-
class VpnConfigurator {
8-
static func setupTunnel() {
9-
let manager = NEVPNManager.shared()
10-
manager.loadFromPreferences { error in
11-
if let error = error {
12-
print("Failed to load VPN preferences: \(error)")
13-
return
14-
}
5+
@UIApplicationMain
6+
@objc class AppDelegate: FlutterAppDelegate {
7+
override func application(
8+
_ application: UIApplication,
9+
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
10+
) -> Bool {
11+
GeneratedPluginRegistrant.register(with: self)
1512

16-
let proto = NETunnelProviderProtocol()
17-
proto.providerBundleIdentifier = "com.vpnclient.VPNclientTunnel"
18-
proto.serverAddress = "VPNclient"
13+
let controller = window?.rootViewController as! FlutterViewController
14+
let vpnChannel = FlutterMethodChannel(
15+
name: "com.vpnclient/vpn_control",
16+
binaryMessenger: controller.binaryMessenger
17+
)
1918

20-
manager.protocolConfiguration = proto
21-
manager.localizedDescription = "VPNclient"
22-
manager.isEnabled = true
19+
vpnChannel.setMethodCallHandler { [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) in
20+
guard let self = self else { return }
21+
switch call.method {
22+
case "setupVPN":
23+
guard let args = call.arguments as? [String: String],
24+
let tunAddr = args["tunAddr"],
25+
let tunMask = args["tunMask"],
26+
let tunDns = args["tunDns"],
27+
let socks5Proxy = args["socks5Proxy"] else {
28+
result(FlutterError(code: "INVALID_ARGS", message: "Invalid arguments", details: nil))
29+
return
30+
}
31+
print("AppDelegate: Setting up VPN with tunAddr=\(tunAddr), socks5Proxy=\(socks5Proxy)")
32+
let vpnManager = VPNManager.sharedInstance ?? VPNManager()
33+
vpnManager.setupVPNConfiguration(tunAddr: tunAddr, tunMask: tunMask, tunDns: tunDns, socks5Proxy: socks5Proxy) { error in
34+
if let error = error {
35+
print("AppDelegate: Setup VPN failed: \(error.localizedDescription)")
36+
result(FlutterError(code: "SETUP_FAILED", message: error.localizedDescription, details: nil))
37+
} else {
38+
print("AppDelegate: Setup VPN succeeded")
39+
result(nil)
40+
}
41+
}
2342

24-
manager.saveToPreferences { error in
25-
if let error = error {
26-
print("Failed to save VPN configuration: \(error)")
27-
} else {
28-
print("VPN configuration saved successfully.")
43+
case "startVPN":
44+
let vpnManager = VPNManager.sharedInstance ?? VPNManager()
45+
vpnManager.startVPN { error in
46+
if let error = error {
47+
print("AppDelegate: Start VPN failed: \(error.localizedDescription)")
48+
result(FlutterError(code: "START_FAILED", message: error.localizedDescription, details: nil))
49+
} else {
50+
print("AppDelegate: Start VPN succeeded")
51+
result(nil)
52+
}
2953
}
30-
}
31-
}
32-
}
33-
}
34-
///VPN
3554

55+
case "stopVPN":
56+
let vpnManager = VPNManager.sharedInstance ?? VPNManager()
57+
vpnManager.stopVPN {
58+
print("AppDelegate: Stop VPN succeeded")
59+
result(nil)
60+
}
3661

62+
case "getVPNStatus":
63+
let vpnManager = VPNManager.sharedInstance ?? VPNManager()
64+
let status = vpnManager.vpnStatus.description
65+
print("AppDelegate: VPN status: \(status)")
66+
result(status)
3767

38-
@main
39-
@objc class AppDelegate: FlutterAppDelegate {
40-
override func application(
41-
_ application: UIApplication,
42-
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
43-
) -> Bool {
44-
GeneratedPluginRegistrant.register(with: self)
45-
///vpn
46-
VpnConfigurator.setupTunnel()
47-
///vpn
48-
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
49-
}
50-
}
68+
default:
69+
result(FlutterMethodNotImplemented)
70+
}
71+
}
72+
73+
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
74+
}
75+
}

ios/Runner/Runner.entitlements

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>com.apple.developer.networking.networkextension</key>
6+
<array>
7+
<string>packet-tunnel-provider</string>
8+
</array>
9+
</dict>
10+
</plist>
Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
33
<plist version="1.0">
4-
<dict>
5-
<key>com.apple.developer.networking.networkextension</key>
6-
<array>
7-
<string>dns-settings</string>
8-
<string>packet-tunnel-provider</string>
9-
<string>dns-proxy</string>
10-
<string>app-proxy-provider</string>
11-
<string>content-filter-provider</string>
12-
</array>
13-
<key>com.apple.developer.networking.vpn.api</key>
14-
<array>
15-
<string>allow-vpn</string>
16-
</array>
17-
</dict>
4+
<dict/>
185
</plist>

ios/Runner/VPNManager.swift

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
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+
}

ios/VPNclientTunnel/Info.plist

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>NSExtension</key>
6+
<dict>
7+
<key>NSExtensionPointIdentifier</key>
8+
<string>com.apple.networkextension.packet-tunnel</string>
9+
<key>NSExtensionPrincipalClass</key>
10+
<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
11+
</dict>
12+
</dict>
13+
</plist>

0 commit comments

Comments
 (0)