@@ -8,6 +8,8 @@ let CTLIOCGINFO: UInt = 0xC064_4E03
8
8
class PacketTunnelProvider : NEPacketTunnelProvider , @unchecked Sendable {
9
9
private let logger = Logger ( subsystem: Bundle . main. bundleIdentifier!, category: " provider " )
10
10
private var manager : Manager ?
11
+ // a `tunnelRemoteAddress` is required, but not currently used.
12
+ private var currentSettings : NEPacketTunnelNetworkSettings = . init( tunnelRemoteAddress: " 127.0.0.1 " )
11
13
12
14
var tunnelFileDescriptor : Int32 ? {
13
15
var ctlInfo = ctl_info ( )
@@ -41,21 +43,42 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
41
43
return nil
42
44
}
43
45
44
- override func startTunnel( options _: [ String : NSObject ] ? , completionHandler: @escaping ( Error ? ) -> Void ) {
46
+ override func startTunnel(
47
+ options _: [ String : NSObject ] ? , completionHandler: @escaping ( Error ? ) -> Void
48
+ ) {
45
49
logger. debug ( " startTunnel called " )
46
50
guard manager == nil else {
47
51
logger. error ( " startTunnel called with non-nil Manager " )
48
- completionHandler ( nil )
52
+ completionHandler ( PTPError . alreadyRunning )
49
53
return
50
54
}
55
+ guard let proto = protocolConfiguration as? NETunnelProviderProtocol ,
56
+ let baseAccessURL = proto. serverAddress
57
+ else {
58
+ logger. error ( " startTunnel called with nil protocolConfiguration " )
59
+ completionHandler ( PTPError . missingConfiguration)
60
+ return
61
+ }
62
+ // HACK: We can't write to the system keychain, and the NE can't read the user keychain.
63
+ guard let token = proto. providerConfiguration ? [ " token " ] as? String else {
64
+ logger. error ( " startTunnel called with nil token " )
65
+ completionHandler ( PTPError . missingToken)
66
+ return
67
+ }
68
+ logger. debug ( " retrieved token & access URL " )
51
69
let completionHandler = CallbackWrapper ( completionHandler)
52
70
Task {
53
- // TODO: Retrieve access URL & Token via Keychain
54
71
do throws ( ManagerError) {
72
+ logger. debug ( " creating manager " )
55
73
manager = try await Manager (
56
74
with: self ,
57
- cfg: . init( apiToken: " fake-token " , serverUrl: . init( string: " https://dev.coder.com " ) !)
75
+ cfg: . init(
76
+ apiToken: token, serverUrl: . init( string: baseAccessURL) !
77
+ )
58
78
)
79
+ logger. debug ( " starting vpn " )
80
+ try await manager!. startVPN ( )
81
+ logger. info ( " vpn started " )
59
82
completionHandler ( nil )
60
83
} catch {
61
84
completionHandler ( error)
@@ -64,15 +87,26 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
64
87
}
65
88
}
66
89
67
- override func stopTunnel( with _: NEProviderStopReason , completionHandler: @escaping ( ) -> Void ) {
90
+ override func stopTunnel(
91
+ with _: NEProviderStopReason , completionHandler: @escaping ( ) -> Void
92
+ ) {
68
93
logger. debug ( " stopTunnel called " )
69
- guard manager != nil else {
94
+ guard let manager else {
70
95
logger. error ( " stopTunnel called with nil Manager " )
71
96
completionHandler ( )
72
97
return
73
98
}
74
- manager = nil
75
- completionHandler ( )
99
+
100
+ let completionHandler = CompletionWrapper ( completionHandler)
101
+ Task { [ manager] in
102
+ do throws ( ManagerError) {
103
+ try await manager. stopVPN ( )
104
+ } catch {
105
+ logger. error ( " error stopping manager: \( error. description, privacy: . public) " )
106
+ }
107
+ completionHandler ( )
108
+ }
109
+ self . manager = nil
76
110
}
77
111
78
112
override func handleAppMessage( _ messageData: Data , completionHandler: ( ( Data ? ) -> Void ) ? ) {
@@ -92,4 +126,33 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
92
126
// Add code here to wake up.
93
127
logger. debug ( " wake called " )
94
128
}
129
+
130
+ // Wrapper around `setTunnelNetworkSettings` that supports merging updates
131
+ func applyTunnelNetworkSettings( _ diff: Vpn_NetworkSettingsRequest ) async throws {
132
+ logger. debug ( " applying settings diff: \( diff. debugDescription, privacy: . public) " )
133
+
134
+ if diff. hasDnsSettings {
135
+ currentSettings. dnsSettings = convertDnsSettings ( diff. dnsSettings)
136
+ }
137
+
138
+ if diff. mtu != 0 {
139
+ currentSettings. mtu = NSNumber ( value: diff. mtu)
140
+ }
141
+
142
+ if diff. hasIpv4Settings {
143
+ currentSettings. ipv4Settings = convertIPv4Settings ( diff. ipv4Settings)
144
+ }
145
+ if diff. hasIpv6Settings {
146
+ currentSettings. ipv6Settings = convertIPv6Settings ( diff. ipv6Settings)
147
+ }
148
+
149
+ logger. info ( " applying settings: \( self . currentSettings. debugDescription, privacy: . public) " )
150
+ try await setTunnelNetworkSettings ( currentSettings)
151
+ }
152
+ }
153
+
154
+ enum PTPError : Error {
155
+ case alreadyRunning
156
+ case missingConfiguration
157
+ case missingToken
95
158
}
0 commit comments