@@ -9,6 +9,8 @@ let CTLIOCGINFO: UInt = 0xC064_4E03
9
9
class PacketTunnelProvider : NEPacketTunnelProvider , @unchecked Sendable {
10
10
private let logger = Logger ( subsystem: Bundle . main. bundleIdentifier!, category: " provider " )
11
11
private var manager : Manager ?
12
+ // a `tunnelRemoteAddress` is required, but not currently used.
13
+ private var currentSettings : NEPacketTunnelNetworkSettings = . init( tunnelRemoteAddress: " 127.0.0.1 " )
12
14
13
15
var tunnelFileDescriptor : Int32 ? {
14
16
var ctlInfo = ctl_info ( )
@@ -48,25 +50,37 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
48
50
logger. info ( " startTunnel called " )
49
51
guard manager == nil else {
50
52
logger. error ( " startTunnel called with non-nil Manager " )
51
- completionHandler ( nil )
53
+ completionHandler ( PTPError . alreadyRunning )
52
54
return
53
55
}
56
+ guard let proto = protocolConfiguration as? NETunnelProviderProtocol ,
57
+ let baseAccessURL = proto. serverAddress
58
+ else {
59
+ logger. error ( " startTunnel called with nil protocolConfiguration " )
60
+ completionHandler ( PTPError . missingConfiguration)
61
+ return
62
+ }
63
+ // HACK: We can't write to the system keychain, and the NE can't read the user keychain.
64
+ guard let token = proto. providerConfiguration ? [ " token " ] as? String else {
65
+ logger. error ( " startTunnel called with nil token " )
66
+ completionHandler ( PTPError . missingToken)
67
+ return
68
+ }
69
+ logger. debug ( " retrieved token & access URL " )
54
70
let completionHandler = CallbackWrapper ( completionHandler)
55
71
Task {
56
- // TODO: Retrieve access URL & Token via Keychain
57
72
do throws ( ManagerError) {
58
- logger. info ( " creating manager " )
73
+ logger. debug ( " creating manager " )
59
74
manager = try await Manager (
60
75
with: self ,
61
76
cfg: . init(
62
- apiToken: " qGg1rDGWzL-a814TWDGcTDOs4AX7laDEI " ,
63
- serverUrl: . init( string: " https://dev.coder.com " ) !
77
+ apiToken: token, serverUrl: . init( string: baseAccessURL) !
64
78
)
65
79
)
66
80
globalXPCListenerDelegate. vpnXPCInterface. setManager ( manager)
67
- logger. debug ( " calling manager.startVPN " )
68
- // try await manager!.startVPN()
69
- logger. debug ( " vpn started " )
81
+ logger. debug ( " starting vpn " )
82
+ try await manager!. startVPN ( )
83
+ logger. info ( " vpn started " )
70
84
if let conn = globalXPCListenerDelegate. getActiveConnection ( ) {
71
85
conn. onStart ( )
72
86
} else {
@@ -75,6 +89,11 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
75
89
completionHandler ( nil )
76
90
} catch {
77
91
logger. error ( " error starting manager: \( error. description, privacy: . public) " )
92
+ if let conn = globalXPCListenerDelegate. getActiveConnection ( ) {
93
+ conn. onError ( error as NSError )
94
+ } else {
95
+ logger. info ( " no active connection " )
96
+ }
78
97
completionHandler ( error as NSError )
79
98
}
80
99
}
@@ -84,30 +103,28 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
84
103
with _: NEProviderStopReason , completionHandler: @escaping ( ) -> Void
85
104
) {
86
105
logger. debug ( " stopTunnel called " )
87
- guard manager != nil else {
106
+ guard let manager else {
88
107
logger. error ( " stopTunnel called with nil Manager " )
89
108
completionHandler ( )
90
109
return
91
110
}
92
111
93
- if let conn = globalXPCListenerDelegate. getActiveConnection ( ) {
94
- conn. onStop ( )
95
- } else {
96
- logger. info ( " no active connection " )
97
- }
98
-
99
- let managerCopy = manager
100
- Task {
112
+ let completionHandler = CompletionWrapper ( completionHandler)
113
+ Task { [ manager] in
101
114
do throws ( ManagerError) {
102
- try await managerCopy ? . stopVPN ( )
115
+ try await manager . stopVPN ( )
103
116
} catch {
104
117
logger. error ( " error stopping manager: \( error. description, privacy: . public) " )
105
118
}
119
+ if let conn = globalXPCListenerDelegate. getActiveConnection ( ) {
120
+ conn. onStop ( )
121
+ } else {
122
+ logger. info ( " no active connection " )
123
+ }
124
+ globalXPCListenerDelegate. vpnXPCInterface. setManager ( nil )
125
+ completionHandler ( )
106
126
}
107
-
108
- manager = nil
109
- globalXPCListenerDelegate. vpnXPCInterface. setManager ( nil )
110
- completionHandler ( )
127
+ self . manager = nil
111
128
}
112
129
113
130
override func handleAppMessage( _ messageData: Data , completionHandler: ( ( Data ? ) -> Void ) ? ) {
@@ -127,4 +144,33 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
127
144
// Add code here to wake up.
128
145
logger. debug ( " wake called " )
129
146
}
147
+
148
+ // Wrapper around `setTunnelNetworkSettings` that supports merging updates
149
+ func applyTunnelNetworkSettings( _ diff: Vpn_NetworkSettingsRequest ) async throws {
150
+ logger. debug ( " applying settings diff: \( diff. debugDescription, privacy: . public) " )
151
+
152
+ if diff. hasDnsSettings {
153
+ currentSettings. dnsSettings = convertDnsSettings ( diff. dnsSettings)
154
+ }
155
+
156
+ if diff. mtu != 0 {
157
+ currentSettings. mtu = NSNumber ( value: diff. mtu)
158
+ }
159
+
160
+ if diff. hasIpv4Settings {
161
+ currentSettings. ipv4Settings = convertIPv4Settings ( diff. ipv4Settings)
162
+ }
163
+ if diff. hasIpv6Settings {
164
+ currentSettings. ipv6Settings = convertIPv6Settings ( diff. ipv6Settings)
165
+ }
166
+
167
+ logger. info ( " applying settings: \( self . currentSettings. debugDescription, privacy: . public) " )
168
+ try await setTunnelNetworkSettings ( currentSettings)
169
+ }
170
+ }
171
+
172
+ enum PTPError : Error {
173
+ case alreadyRunning
174
+ case missingConfiguration
175
+ case missingToken
130
176
}
0 commit comments