Skip to content

Commit 77d87c7

Browse files
Merge pull request #48 from mysteriumnetwork/feature/check-tunnel-configuration
Implement Tunnel Configuration Check Feature
2 parents e1da550 + f7dfc8a commit 77d87c7

File tree

10 files changed

+374
-84
lines changed

10 files changed

+374
-84
lines changed

.vscode/launch.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
"name": "Test example APP (Debug Mode)",
6+
"cwd": "example",
7+
"request": "launch",
8+
"type": "dart"
9+
},
10+
{
11+
"name": "Test example APP (profile mode)",
12+
"cwd": "example",
13+
"request": "launch",
14+
"type": "dart",
15+
"flutterMode": "profile"
16+
},
17+
{
18+
"name": "Test example APP (release mode)",
19+
"cwd": "example",
20+
"request": "launch",
21+
"type": "dart",
22+
"flutterMode": "release"
23+
}
24+
]
25+
}

android/src/main/kotlin/network/mysterium/wireguard_dart/WireguardDartPlugin.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,9 @@ class WireguardDartPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
132132
call.argument<String>("tunnelName").toString(),
133133
result
134134
)
135-
135+
"checkTunnelConfiguration" -> {
136+
checkTunnelConfiguration(result)
137+
}
136138
"connect" -> connect(call.argument<String>("cfg").toString(), result)
137139
"disconnect" -> disconnect(result)
138140
"status" -> status(result)
@@ -141,6 +143,12 @@ class WireguardDartPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
141143
}
142144
}
143145

146+
private fun checkTunnelConfiguration(result: MethodChannel.Result) {
147+
val intent = GoBackend.VpnService.prepare(this.activity)
148+
havePermission = intent == null
149+
return result.success(havePermission)
150+
}
151+
144152
private fun generateKeyPair(result: Result) {
145153
val keyPair = KeyPair()
146154
result.success(
Lines changed: 201 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,205 @@
1+
import NetworkExtension
2+
import WireGuardKit
3+
import os
4+
15
#if os(iOS)
2-
import Flutter
3-
import UIKit
6+
import Flutter
7+
import UIKit
48
#elseif os(macOS)
5-
import Cocoa
6-
import FlutterMacOS
9+
import Cocoa
10+
import FlutterMacOS
711
#else
8-
#error("Unsupported platform")
12+
#error("Unsupported platform")
913
#endif
1014

11-
import NetworkExtension
12-
import os
13-
import WireGuardKit
14-
1515
public class WireguardDartPlugin: NSObject, FlutterPlugin {
1616
private var vpnManager: NETunnelProviderManager?
17-
1817
var vpnStatus: NEVPNStatus {
1918
vpnManager?.connection.status ?? NEVPNStatus.invalid
2019
}
2120

2221
public static func register(with registrar: FlutterPluginRegistrar) {
2322
#if os(iOS)
24-
let messenger = registrar.messenger()
23+
let messenger = registrar.messenger()
2524
#else
26-
let messenger = registrar.messenger
25+
let messenger = registrar.messenger
2726
#endif
28-
let channel = FlutterMethodChannel(name: "wireguard_dart", binaryMessenger: messenger)
27+
let channel = FlutterMethodChannel(
28+
name: "wireguard_dart", binaryMessenger: messenger)
2929

3030
let instance = WireguardDartPlugin()
3131
registrar.addMethodCallDelegate(instance, channel: channel)
3232

33-
let statusChannel = FlutterEventChannel(name: "wireguard_dart/status", binaryMessenger: messenger)
33+
let statusChannel = FlutterEventChannel(
34+
name: "wireguard_dart/status", binaryMessenger: messenger)
3435
statusChannel.setStreamHandler(ConnectionStatusObserver())
3536
}
3637

37-
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
38+
public func handle(
39+
_ call: FlutterMethodCall, result: @escaping FlutterResult
40+
) {
3841
switch call.method {
3942
case "nativeInit":
4043
result("")
4144
case "generateKeyPair":
4245
let privateKey = PrivateKey()
4346
let privateKeyResponse: [String: Any] = [
4447
"privateKey": privateKey.base64Key,
45-
"publicKey": privateKey.publicKey.base64Key
48+
"publicKey": privateKey.publicKey.base64Key,
4649
]
4750
result(privateKeyResponse)
4851
case "setupTunnel":
4952
Logger.main.debug("handle setupTunnel")
50-
guard let args = call.arguments as? [String: Any], args["bundleId"] != nil else {
51-
result(nativeFlutterError(message: "required argument: 'bundleId'"))
53+
guard let args = call.arguments as? [String: Any],
54+
args["bundleId"] != nil
55+
else {
56+
result(
57+
nativeFlutterError(message: "required argument: 'bundleId'")
58+
)
5259
return
5360
}
54-
guard let bundleId = args["bundleId"] as? String, !bundleId.isEmpty else {
55-
result(nativeFlutterError(message: "required argument: 'bundleId'"))
61+
guard let bundleId = args["bundleId"] as? String, !bundleId.isEmpty
62+
else {
63+
result(
64+
nativeFlutterError(message: "required argument: 'bundleId'")
65+
)
5666
return
5767
}
58-
guard let tunnelName = args["tunnelName"] as? String, !tunnelName.isEmpty else {
59-
result(nativeFlutterError(message: "required argument: 'tunnelName'"))
68+
guard let tunnelName = args["tunnelName"] as? String,
69+
!tunnelName.isEmpty
70+
else {
71+
result(
72+
nativeFlutterError(
73+
message: "required argument: 'tunnelName'"))
6074
return
6175
}
62-
Logger.main.debug("Tunnel bundle ID: \(bundleId), name: \(tunnelName)")
76+
Logger.main.debug(
77+
"Tunnel bundle ID: \(bundleId), name: \(tunnelName)")
6378
Task {
6479
do {
65-
vpnManager = try await setupProviderManager(bundleId: bundleId, tunnelName: tunnelName)
80+
vpnManager = try await setupProviderManager(
81+
bundleId: bundleId, tunnelName: tunnelName)
6682
Logger.main.debug("Tunnel setup OK")
6783
result("")
6884
} catch {
6985
Logger.main.error("Tunnel setup ERROR: \(error)")
70-
result(nativeFlutterError(message: "could not setup VPN tunnel: \(error)"))
86+
result(
87+
nativeFlutterError(
88+
message: "could not setup VPN tunnel: \(error)"))
7189
return
7290
}
7391
}
7492
case "connect":
7593
Logger.main.debug("handle connect")
7694
let cfg: String
7795
if let args = call.arguments as? [String: Any],
78-
let argCfg = args["cfg"] as? String {
96+
let argCfg = args["cfg"] as? String
97+
{
7998
cfg = argCfg
8099
} else {
81100
Logger.main.error("Required argument 'cfg' not provided")
82101
result(nativeFlutterError(message: "required argument: 'cfg'"))
83102
return
84103
}
85104
guard let mgr = vpnManager else {
86-
Logger.main.error("Tunnel not initialized, missing 'vpnManager'")
87-
result(nativeFlutterError(message: "tunnel not initialized, missing 'vpnManager'"))
105+
Logger.main.error(
106+
"Tunnel not initialized, missing 'vpnManager'")
107+
result(
108+
nativeFlutterError(
109+
message: "tunnel not initialized, missing 'vpnManager'")
110+
)
88111
return
89112
}
90113
Logger.main.debug("Connection configuration: \(cfg)")
91114
Task {
92115
do {
116+
if !mgr.isEnabled {
117+
mgr.isEnabled = true
118+
try await mgr.saveToPreferences()
119+
try await mgr.loadFromPreferences()
120+
121+
}
122+
93123
try mgr.connection.startVPNTunnel(options: [
94124
"cfg": cfg as NSObject
95125
])
96126
Logger.main.debug("Start VPN tunnel OK")
97127
result("")
128+
} catch let error as NEVPNError {
129+
switch error.code {
130+
case .configurationInvalid:
131+
Logger.main.error(
132+
"Start VPN tunnel ERROR: Configuration is invalid")
133+
result(
134+
nativeFlutterError(
135+
message:
136+
"could not start VPN tunnel: Configuration is invalid"
137+
))
138+
case .configurationDisabled:
139+
Logger.main.error(
140+
"Start VPN tunnel ERROR: Configuration is disabled")
141+
result(
142+
nativeFlutterError(
143+
message:
144+
"could not start VPN tunnel: Configuration is disabled"
145+
))
146+
case .connectionFailed:
147+
Logger.main.error(
148+
"Start VPN tunnel ERROR: Connection failed")
149+
result(
150+
nativeFlutterError(
151+
message:
152+
"could not start VPN tunnel: Connection failed"
153+
))
154+
case .configurationStale:
155+
Logger.main.error(
156+
"Start VPN tunnel ERROR: Configuration is stale")
157+
result(
158+
nativeFlutterError(
159+
message:
160+
"could not start VPN tunnel: Configuration is stale"
161+
))
162+
case .configurationReadWriteFailed:
163+
Logger.main.error(
164+
"Start VPN tunnel ERROR: Configuration read/write failed"
165+
)
166+
result(
167+
nativeFlutterError(
168+
message:
169+
"could not start VPN tunnel: Configuration read/write failed"
170+
))
171+
case .configurationUnknown:
172+
Logger.main.error(
173+
"Start VPN tunnel ERROR: Configuration unknown")
174+
result(
175+
nativeFlutterError(
176+
message:
177+
"could not start VPN tunnel: Configuration unknown"
178+
))
179+
@unknown default:
180+
Logger.main.error(
181+
"Start VPN tunnel ERROR: Unknown error")
182+
result(
183+
nativeFlutterError(
184+
message:
185+
"could not start VPN tunnel: Unknown error")
186+
)
187+
}
98188
} catch {
99189
Logger.main.error("Start VPN tunnel ERROR: \(error)")
100-
result(nativeFlutterError(message: "could not start VPN tunnel: \(error)"))
190+
result(
191+
nativeFlutterError(
192+
message: "could not start VPN tunnel: \(error)"))
101193
}
102194
}
103195
case "disconnect":
104196
guard let mgr = vpnManager else {
105-
Logger.main.error("Tunnel not initialized, missing 'vpnManager'")
106-
result(nativeFlutterError(message: "tunnel not initialized, missing 'vpnManager'"))
197+
Logger.main.error(
198+
"Tunnel not initialized, missing 'vpnManager'")
199+
result(
200+
nativeFlutterError(
201+
message: "tunnel not initialized, missing 'vpnManager'")
202+
)
107203
return
108204
}
109205
Task {
@@ -112,30 +208,65 @@ public class WireguardDartPlugin: NSObject, FlutterPlugin {
112208
result("")
113209
}
114210
case "status":
115-
guard vpnManager != nil else {
116-
Logger.main.error("Tunnel not initialized, missing 'vpnManager'")
117-
result(nativeFlutterError(message: "tunnel not initialized, missing 'vpnManager'"))
211+
if vpnManager != nil {
212+
Task {
213+
result(
214+
ConnectionStatus.fromNEVPNStatus(status: vpnStatus)
215+
.string())
216+
}
217+
} else {
218+
result(ConnectionStatus.unknown.string())
219+
}
220+
case "checkTunnelConfiguration":
221+
guard let args = call.arguments as? [String: Any],
222+
let bundleId = args["bundleId"] as? String, !bundleId.isEmpty
223+
else {
224+
result(
225+
nativeFlutterError(message: "required argument: 'bundleId'")
226+
)
118227
return
119228
}
120-
Task {
121-
result(ConnectionStatus.fromNEVPNStatus(status: vpnStatus).string())
229+
guard let args = call.arguments as? [String: Any],
230+
let tunnelName = args["tunnelName"] as? String,
231+
!tunnelName.isEmpty
232+
else {
233+
result(
234+
nativeFlutterError(
235+
message: "required argument: 'tunnelName'")
236+
)
237+
return
238+
}
239+
checkTunnelConfiguration(bundleId: bundleId, tunnelName: tunnelName)
240+
{ manager in
241+
if let vpnManager = manager {
242+
self.vpnManager = vpnManager
243+
Logger.main.debug("Tunnel is set up and existing")
244+
result(true)
245+
} else {
246+
Logger.main.debug("Tunnel is not set up")
247+
result(false)
248+
}
122249
}
250+
123251
default:
124252
result(FlutterMethodNotImplemented)
125253
}
126254
}
127255

128-
func setupProviderManager(bundleId: String, tunnelName: String) async throws -> NETunnelProviderManager {
256+
func setupProviderManager(bundleId: String, tunnelName: String) async throws
257+
-> NETunnelProviderManager
258+
{
129259
let mgrs = try await NETunnelProviderManager.loadAllFromPreferences()
130260
let existingMgr = mgrs.first(where: {
131-
($0.protocolConfiguration as? NETunnelProviderProtocol)?.providerBundleIdentifier == bundleId
261+
($0.protocolConfiguration as? NETunnelProviderProtocol)?
262+
.providerBundleIdentifier == bundleId
132263
})
133264
let mgr = existingMgr ?? NETunnelProviderManager()
134265

135266
mgr.localizedDescription = tunnelName
136267
let proto = NETunnelProviderProtocol()
137268
proto.providerBundleIdentifier = bundleId
138-
proto.serverAddress = "" // must be non-null
269+
proto.serverAddress = "" // must be non-null
139270
mgr.protocolConfiguration = proto
140271
mgr.isEnabled = true
141272

@@ -144,4 +275,35 @@ public class WireguardDartPlugin: NSObject, FlutterPlugin {
144275

145276
return mgr
146277
}
278+
279+
func isVpnManagerConfigured(bundleId: String, tunnelName: String)
280+
async throws -> NETunnelProviderManager?
281+
{
282+
// Load all managers from preferences
283+
let mgrs = try await NETunnelProviderManager.loadAllFromPreferences()
284+
if let existingMgr = mgrs.first(where: {
285+
($0.protocolConfiguration as? NETunnelProviderProtocol)?
286+
.providerBundleIdentifier == bundleId
287+
}) {
288+
return existingMgr
289+
}
290+
return nil
291+
}
292+
293+
func checkTunnelConfiguration(
294+
bundleId: String, tunnelName: String,
295+
result: @escaping (NETunnelProviderManager?) -> Void
296+
) {
297+
Task {
298+
do {
299+
let mgr = try await isVpnManagerConfigured(
300+
bundleId: bundleId, tunnelName: tunnelName)
301+
result(mgr)
302+
} catch {
303+
Logger.main.error(
304+
"Error checking tunnel configuration: \(error)")
305+
result(nil)
306+
}
307+
}
308+
}
147309
}

0 commit comments

Comments
 (0)