55#import < NetworkExtension/NetworkExtension.h>
66
77#import " interfaceconfig.h"
8+ #import " utils.h"
89#import " wireguardtunnel.h"
910
1011#include < atomic>
@@ -55,13 +56,6 @@ - (id)init{
5556 return self;
5657}
5758
58- + (NSError *) makeError : (NSInteger )code
59- withDescription : (NSString *)desc {
60- return [NSError errorWithDomain: [[NSBundle mainBundle ] bundleIdentifier ]
61- code: code
62- userInfo: @{NSLocalizedDescriptionKey : desc}];
63- }
64-
6559+ (nw_endpoint_t )convertEndpoint : (NWEndpoint*)old {
6660 if (old == nil ) {
6761 return nil ;
@@ -149,13 +143,11 @@ - (void)startProxyWithOptions:(NSDictionary<NSString *,id> *)options
149143 m_handledUnknown = 0 ;
150144
151145 // Parse the configuration
152- InterfaceConfig* config = [[InterfaceConfig alloc ] initFromDict: options];
153- if (!config) {
154- completionHandler ([VPNSplitTunnelProvider makeError: 1
155- withDescription: @" invalid configuration" ]);
146+ _config = [[InterfaceConfig alloc ] initFromDict: options];
147+ if (!self.config ) {
148+ completionHandler (vpnProviderError (NEProviderStopReasonConfigurationFailed));
156149 return ;
157150 }
158- _config = config;
159151
160152 self.wireguard = [WireguardTunnel new ];
161153 self.settings = [[NETransparentProxyNetworkSettings alloc ] initWithTunnelRemoteAddress: self .protocolConfiguration.serverAddress];
@@ -245,6 +237,12 @@ - (void)startProxyWithOptions:(NSDictionary<NSString *,id> *)options
245237 return ;
246238 }
247239
240+ // Register a KVO observer to switch servers upon configuration change.
241+ [weakSelf addObserver: weakSelf
242+ forKeyPath: @" protocolConfiguration"
243+ options: NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
244+ context: nil ];
245+
248246 // Success
249247 completionHandler (nil );
250248 }];
@@ -260,10 +258,91 @@ - (void)stopProxyWithReason:(NEProviderStopReason)reason
260258 NSLog (@" handled udp flows: %lld " , std::atomic_load(&m_handledUdpFlows));
261259 NSLog (@" handled unknown flows: %lld " , std::atomic_load(&m_handledUnknown));
262260
261+ [self removeObserver: self
262+ forKeyPath: @" protocolConfiguration" ];
263+
263264 [self .wireguard stopTunnelWithReason: reason
264265 completionHandler: completionHandler];
265266}
266267
268+ - (void )observeValueForKeyPath : (NSString *)keyPath
269+ ofObject : (id )object
270+ change : (NSDictionary *)change
271+ context : (void *)context {
272+ // The only thing we should be observing is the protocolConfiguration
273+ if (![keyPath isEqual: @" protocolConfiguration" ]) {
274+ return ;
275+ }
276+ NSLog (@" configuration changed" );
277+
278+ // Parse and update the configuration
279+ NETunnelProviderProtocol* proto = (NETunnelProviderProtocol*)self.protocolConfiguration ;
280+ _config = [[InterfaceConfig alloc ] initFromDict: proto.providerConfiguration];
281+ if (!self.config ) {
282+ // We can't make sense of this configuration.
283+ [self .wireguard stopTunnelWithReason: NEProviderStopReasonConfigurationFailed
284+ completionHandler: ^(){
285+ [self cancelProxyWithError: vpnProviderError (NEProviderStopReasonConfigurationFailed)];
286+ }];
287+ return ;
288+ }
289+
290+ // Update the app exclusion settings.
291+ [self .vpnDisabledApps removeAllObjects ];
292+ NSArray * apps = [proto.providerConfiguration objectForKey: @" apps" ];
293+ if (apps) {
294+ NSEnumerator * iter = [apps objectEnumerator ];
295+ while (id appId = [iter nextObject ]) {
296+ if (![appId isKindOfClass: [NSString class ]]) {
297+ continue ;
298+ }
299+ #ifdef MZ_DEBUG
300+ NSLog (@" excluding app %@ from VPN" , appId);
301+ #endif
302+ [self .vpnDisabledApps addObject: appId];
303+ }
304+ }
305+ // TODO: We probably need to update/reset connections if they changed their exclusion status.
306+ // but how?
307+
308+ // YOLO: Does this do anything....
309+ [self performSelector: @selector (fetchFlowStatesWithCompletionHandler: )
310+ withObject: ^(NSArray * result){
311+ NSLog (@" flow count: %lu " , result.count);
312+ for (NSObject * obj in result) {
313+ NSLog (@" flow state: %@ " , NSStringFromClass (obj.class ));
314+ }
315+ }];
316+
317+ // Check if the server identitiy changed. Do nothing if no changes.
318+ NETunnelProviderProtocol* old = [change objectForKey: @" old" ];
319+ if (old && [old isKindOfClass: NETunnelProviderProtocol.class ]) {
320+ NSString * oldPubKey = [old.providerConfiguration objectForKey: @" serverPublicKey" ];
321+ if (oldPubKey && [oldPubKey isKindOfClass: NSString .class ]) {
322+ if ([oldPubKey isEqual: self .config.serverPublicKey]) {
323+ return ;
324+ }
325+ }
326+ }
327+
328+ // Shutdown the old wireguard peer and start a new one.
329+ self.reasserting = TRUE ;
330+ [self .wireguard.peer stopWithReason: NEProviderStopReasonSuperceded
331+ completionHandler: ^(){
332+ // Create and start a new peer.
333+ self.wireguard .peer = [[WireguardPeer alloc ] initWithOptions: self .config
334+ andTunnel: self .wireguard];
335+ [self .wireguard.peer startWithOptions: self .config
336+ completionHandler: ^(NSError *err){
337+ if (err) {
338+ [self cancelProxyWithError: err];
339+ } else {
340+ self.reasserting = FALSE ;
341+ }
342+ }];
343+ }];
344+ }
345+
267346- (BOOL )matchAppFlow : (NEAppProxyFlow*)flow {
268347 // Without metadata - always direct the flow into the VPN.
269348 if (flow.metaData == nil ) {
@@ -337,14 +416,14 @@ - (void)handleAppMessage:(NSData *)messageData
337416 error: &error];
338417 if (error != nil ) {
339418 NSLog (@" app message error: %@ " , error.localizedDescription);
340- [VPNSplitTunnelProvider sendAppError : error completionHandler: completionHandler];
419+ [VPNSplitTunnelProvider sendAppResponse : error completionHandler: completionHandler];
341420 return ;
342421 }
343422 NSString * action = [msg decodeObjectOfClass: NSString .class forKey :@" action" ];
344423 if (!action) {
345424 NSLog (@" app message invalid action" );
346- NSError * error = [VPNSplitTunnelProvider makeError: 1 withDescription: @" invalid app message invalid " ] ;
347- [VPNSplitTunnelProvider sendAppError : error completionHandler: completionHandler];
425+ NSError * error = vpnProviderError (NEProviderStopReasonConfigurationFailed) ;
426+ [VPNSplitTunnelProvider sendAppResponse : error completionHandler: completionHandler];
348427 return ;
349428 }
350429
@@ -366,8 +445,8 @@ - (void)handleAppMessage:(NSData *)messageData
366445
367446 // Wireguard Tunnel messages
368447 if ([action isEqualToString: @" status" ]) {
369- [VPNSplitTunnelProvider sendAppObject :self .wireguard.status
370- completionHandler: completionHandler];
448+ [VPNSplitTunnelProvider sendAppResponse :self .wireguard.status
449+ completionHandler: completionHandler];
371450 return ;
372451 }
373452
@@ -385,33 +464,20 @@ - (void)handleAppMessage:(NSData *)messageData
385464 [VPNSplitTunnelProvider sendAppResponse: nil completionHandler: completionHandler];
386465}
387466
388- + (void )sendAppResponse : (NSData *) responseData
467+ + (void )sendAppResponse : (id ) obj
389468 completionHandler : (void (^)(NSData *)) completionHandler {
390469 if (!completionHandler) {
391470 return ;
392471 }
393- completionHandler (responseData);
394- }
395472
396- + (void )sendAppObject : (id ) obj
397- completionHandler : (void (^)(NSData *)) completionHandler {
398473 NSKeyedArchiver * encoder = [[NSKeyedArchiver alloc ] initRequiringSecureCoding: YES ];
399- if ([obj respondsToSelector: @selector (encodeWithCoder: )]) {
474+ if ([obj isKindOfClass: [NSError class ]]) {
475+ [encoder encodeObject: obj forKey: @" error" ];
476+ } else if ([obj respondsToSelector: @selector (encodeWithCoder: )]) {
400477 [obj encodeWithCoder: encoder];
401478 }
402479 [encoder finishEncoding ];
403480 completionHandler (encoder.encodedData );
404481}
405482
406- + (void )sendAppError : (NSError *) error
407- completionHandler : (void (^)(NSData *)) completionHandler {
408- if (!completionHandler) {
409- return ;
410- }
411- NSKeyedArchiver * encoder = [[NSKeyedArchiver alloc ] initRequiringSecureCoding: YES ];
412- [encoder encodeObject: error forKey: @" error" ];
413- [encoder finishEncoding ];
414- completionHandler (encoder.encodedData );
415- }
416-
417483@end
0 commit comments