@@ -8,6 +8,8 @@ let CTLIOCGINFO: UInt = 0xC064_4E03
88class PacketTunnelProvider : NEPacketTunnelProvider , @unchecked Sendable {
99 private let logger = Logger ( subsystem: Bundle . main. bundleIdentifier!, category: " provider " )
1010 private var manager : Manager ?
11+ // a `tunnelRemoteAddress` is required, but not currently used.
12+ private var currentSettings : NEPacketTunnelNetworkSettings = . init( tunnelRemoteAddress: " 127.0.0.1 " )
1113
1214 var tunnelFileDescriptor : Int32 ? {
1315 var ctlInfo = ctl_info ( )
@@ -41,21 +43,42 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
4143 return nil
4244 }
4345
44- override func startTunnel( options _: [ String : NSObject ] ? , completionHandler: @escaping ( Error ? ) -> Void ) {
46+ override func startTunnel(
47+ options _: [ String : NSObject ] ? , completionHandler: @escaping ( Error ? ) -> Void
48+ ) {
4549 logger. debug ( " startTunnel called " )
4650 guard manager == nil else {
4751 logger. error ( " startTunnel called with non-nil Manager " )
48- completionHandler ( nil )
52+ completionHandler ( PTPError . alreadyRunning )
4953 return
5054 }
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 " )
5169 let completionHandler = CallbackWrapper ( completionHandler)
5270 Task {
53- // TODO: Retrieve access URL & Token via Keychain
5471 do throws ( ManagerError) {
72+ logger. debug ( " creating manager " )
5573 manager = try await Manager (
5674 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+ )
5878 )
79+ logger. debug ( " starting vpn " )
80+ try await manager!. startVPN ( )
81+ logger. info ( " vpn started " )
5982 completionHandler ( nil )
6083 } catch {
6184 completionHandler ( error)
@@ -64,15 +87,26 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
6487 }
6588 }
6689
67- override func stopTunnel( with _: NEProviderStopReason , completionHandler: @escaping ( ) -> Void ) {
90+ override func stopTunnel(
91+ with _: NEProviderStopReason , completionHandler: @escaping ( ) -> Void
92+ ) {
6893 logger. debug ( " stopTunnel called " )
69- guard manager != nil else {
94+ guard let manager else {
7095 logger. error ( " stopTunnel called with nil Manager " )
7196 completionHandler ( )
7297 return
7398 }
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
76110 }
77111
78112 override func handleAppMessage( _ messageData: Data , completionHandler: ( ( Data ? ) -> Void ) ? ) {
@@ -92,4 +126,33 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
92126 // Add code here to wake up.
93127 logger. debug ( " wake called " )
94128 }
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
95158}
0 commit comments