2 * Copyright (c) 2016, 2017 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 #import "SCTestUtils.h"
26 #import <Network/Network_Private.h>
27 #import <CoreFoundation/CFXPCBridge.h>
28 #import <config_agent_info.h>
30 #define TEST_DOMAIN "filemaker.com"
31 #define PATH_QUIESCE_TIME 1.5 // seconds
33 @interface SCTestConfigAgent : SCTest
34 @property NSString *serviceID;
35 @property NSString *proxyKey;
36 @property NSString *dnsKey;
37 @property NSArray<NSArray<NSDictionary *> *> *testProxy;
38 @property NSArray<NWEndpoint *> *testDNS;
39 @property SCDynamicStoreRef store;
40 @property (copy) NSArray<NSArray<NSDictionary *> *> *pathProxy;
41 @property (copy) NSArray<NWEndpoint *> *pathDNS;
44 @implementation SCTestConfigAgent
46 - (instancetype)initWithOptions:(NSDictionary *)options
48 self = [super initWithOptions:options];
50 _serviceID = @"8F66B505-EAEF-4611-BD4D-C523FD9451F0";
51 _store = SCDynamicStoreCreate(kCFAllocatorDefault,
55 _proxyKey = (__bridge_transfer NSString *)[self copyStateKeyWithServiceID:(__bridge CFStringRef)(self.serviceID) forEntity:kSCEntNetProxies];
56 _dnsKey = (__bridge_transfer NSString *)[self copyStateKeyWithServiceID:(__bridge CFStringRef)(self.serviceID) forEntity:kSCEntNetDNS];
63 if (self.store != NULL) {
64 CFRelease(self.store);
71 return @"config_agent";
74 + (NSString *)commandDescription
76 return @"Tests the Config Agent code path";
81 if (self.options[kSCTestConfigAgentRemoveProxy]) {
82 [self removeFromSCDynamicStore:self.proxyKey];
85 if (self.options[kSCTestConfigAgentRemoveDNS]) {
86 [self removeFromSCDynamicStore:self.dnsKey];
89 NSDictionary *proxyConfig = [self parseProxyAgentOptions];
90 if (proxyConfig != nil) {
91 [self publishToSCDynamicStore:self.proxyKey value:proxyConfig];
92 self.testProxy = @[@[proxyConfig]];
95 NSDictionary *dnsConfig = [self parseDNSAgentOptions];
96 if (dnsConfig != nil) {
97 [self publishToSCDynamicStore:self.dnsKey value:dnsConfig];
98 self.testDNS = [self createDNSArray:dnsConfig];
101 [self cleanupAndExitWithErrorCode:0];
104 - (CFStringRef)copyStateKeyWithServiceID:(CFStringRef)serviceID
105 forEntity:(CFStringRef)entity
107 return SCDynamicStoreKeyCreateNetworkServiceEntity(kCFAllocatorDefault,
108 kSCDynamicStoreDomainState,
113 - (NSArray<NWEndpoint *> *)createDNSArray:(NSDictionary *)dnsConfig
115 NSArray<NSString *> *dnsServers;
116 NSMutableArray<NWEndpoint *> *dnsArray;
118 dnsServers = [dnsConfig objectForKey:(__bridge NSString *)kSCPropNetDNSServerAddresses];
119 if (dnsServers == nil || [dnsServers count] == 0) {
123 dnsArray = [[NSMutableArray alloc] init];
124 for (NSString *server in dnsServers) {
125 NWEndpoint *endpoint = (NWEndpoint *)[NWAddressEndpoint endpointWithHostname:server port:@"0"];
126 [dnsArray addObject:endpoint];
132 - (void)publishToSCDynamicStore:(NSString *)key
133 value:(NSDictionary *)value
136 BOOL ok = SCDynamicStoreSetValue(self.store, (__bridge CFStringRef)key, (__bridge CFPropertyListRef _Nonnull)(value));
138 int error = SCError();
139 if (error == kSCStatusNoKey) {
142 SCTestLog("Could not set in SCDynamicStore: Error: %s", SCErrorString(error));
147 - (void)removeFromSCDynamicStore:(NSString *)key
149 BOOL ok = SCDynamicStoreRemoveValue(self.store, (__bridge CFStringRef)key);
151 int error = SCError();
152 if (error == kSCStatusNoKey) {
155 SCTestLog("Could not remove key: %@, Error: %s", key, SCErrorString(error));
160 - (NSDictionary *)parseProxyAgentOptions
162 NSMutableDictionary *proxyConfig = [[NSMutableDictionary alloc] init];
164 #define NS_NUMBER(x) [NSNumber numberWithInt:x]
166 #define SET_PROXY_CONFIG(proxyType) \
168 if (self.options[kSCTestConfigAgent ## proxyType ## Proxy] != nil) { \
169 NSString *serverAndPortString = self.options[kSCTestConfigAgent ## proxyType ## Proxy]; \
170 NSArray<NSString *> *serverAndPortArray = [serverAndPortString componentsSeparatedByString:@":"]; \
171 if ([serverAndPortArray count] != 2) { \
172 SCTestLog("server address or port missing"); \
175 NSString *server = [serverAndPortArray objectAtIndex:0]; \
176 NSString *port = [serverAndPortArray objectAtIndex:1]; \
177 [proxyConfig setObject:server forKey:(__bridge NSString *)kSCPropNetProxies ## proxyType ## Proxy]; \
178 [proxyConfig setObject:NS_NUMBER(port.intValue) forKey:(__bridge NSString *)kSCPropNetProxies ## proxyType ## Port]; \
179 [proxyConfig setObject:NS_NUMBER(1) forKey:(__bridge NSString *)kSCPropNetProxies ## proxyType ## Enable]; \
183 SET_PROXY_CONFIG(HTTP);
184 SET_PROXY_CONFIG(HTTPS);
185 SET_PROXY_CONFIG(FTP);
186 SET_PROXY_CONFIG(Gopher);
187 SET_PROXY_CONFIG(SOCKS);
189 if ([proxyConfig count] > 0) {
191 NSArray<NSString *> *domains = nil;
192 NSString *matchDomains = self.options[kSCTestConfigAgentProxyMatchDomain];
193 if (matchDomains == nil) {
194 domains = @[@TEST_DOMAIN];
196 domains = [matchDomains componentsSeparatedByString:@","];
199 [proxyConfig setObject:domains forKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains];
205 #undef SET_PROXY_CONFIG
208 - (NSDictionary *)parseDNSAgentOptions
210 NSMutableDictionary *dnsConfig;
211 NSString *dnsServerString;
212 NSString *dnsDomainString;
213 NSArray<NSString *> *dnsServers;
214 NSArray<NSString *> *dnsDomains;
216 dnsConfig = [[NSMutableDictionary alloc] init];
217 dnsServerString = self.options[kSCTestConfigAgentDNSServers];
218 if (dnsServerString == nil) {
222 dnsDomainString = self.options[kSCTestConfigAgentDNSDomains];
223 if (dnsDomainString == nil) {
224 dnsDomainString = @TEST_DOMAIN;
227 dnsServers = [dnsServerString componentsSeparatedByString:@","];
228 [dnsConfig setObject:dnsServers forKey:(__bridge NSString *)kSCPropNetDNSServerAddresses];
230 dnsDomains = [dnsDomainString componentsSeparatedByString:@","];
231 [dnsConfig setObject:dnsDomains forKey:(__bridge NSString *)kSCPropNetDNSSupplementalMatchDomains];
236 - (void)cleanupAndExitWithErrorCode:(int)error
238 CFRelease(self.store);
239 [super cleanupAndExitWithErrorCode:error];
253 BOOL allUnitTestsPassed = YES;
254 allUnitTestsPassed &= [self unitTestInstallProxy];
255 allUnitTestsPassed &= [self unitTestInstallProxyWithLargeConfig];
256 allUnitTestsPassed &= [self unitTestInstallProxyWithConflictingDomain];
257 allUnitTestsPassed &= [self unitTestInstallDNS];
258 allUnitTestsPassed &= [self unitTestInstallDNSWithConflictingDomain];
260 if(![self tearDown]) {
264 return allUnitTestsPassed;
267 - (BOOL)unitTestInstallProxy
270 SCTestConfigAgent *test;
271 NSDictionary *proxyConfig;
274 NWHostEndpoint *hostEndpoint;
275 NWPathEvaluator *pathEvaluator;
276 NSMutableDictionary *dict;
278 test = [[SCTestConfigAgent alloc] initWithOptions:self.options];
279 proxyConfig = [test parseProxyAgentOptions];
280 if (proxyConfig == nil) {
281 // Use default options
282 proxyConfig = @{(__bridge NSString *)kSCPropNetProxiesHTTPEnable:@(1),
283 (__bridge NSString *)kSCPropNetProxiesHTTPPort:@(80),
284 (__bridge NSString *)kSCPropNetProxiesHTTPProxy:@"10.10.10.100",
285 (__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains:@[@TEST_DOMAIN],
289 hostname = [[proxyConfig objectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains] objectAtIndex:0];
290 port = [proxyConfig objectForKey:(__bridge NSString *)kSCPropNetProxiesHTTPPort];
291 hostEndpoint = [NWHostEndpoint endpointWithHostname:hostname port:port.stringValue];
292 pathEvaluator = [[NWPathEvaluator alloc] initWithEndpoint:hostEndpoint parameters:NULL];
293 [pathEvaluator addObserver:test
295 options:NSKeyValueObservingOptionNew
299 [test publishToSCDynamicStore:test.proxyKey value:proxyConfig];
300 dict = [NSMutableDictionary dictionaryWithDictionary:proxyConfig];
301 [dict removeObjectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains];
302 test.testProxy = @[@[dict]];
304 // Wait for the path changes to quiesce
305 [test waitFor:PATH_QUIESCE_TIME];
306 if (![test.testProxy isEqualToArray:test.pathProxy]) {
307 SCTestLog("test proxy and applied proxy do not match. Test: %@, Applied: %@", test.testProxy, test.pathProxy);
311 SCTestLog("Verified the configured proxy is the same as applied proxy");
312 [test removeFromSCDynamicStore:test.proxyKey];
313 test.testProxy = nil;
314 // Wait for the path changes to quiesce
315 [test waitFor:PATH_QUIESCE_TIME];
316 if (test.pathProxy != nil) {
317 SCTestLog("proxy applied when there is no test proxy");
324 [pathEvaluator removeObserver:test
330 - (BOOL)unitTestInstallDNS
333 SCTestConfigAgent *test;
334 NSDictionary *dnsConfig;
336 NWHostEndpoint *hostEndpoint;
337 NWPathEvaluator *pathEvaluator;
339 test = [[SCTestConfigAgent alloc] initWithOptions:self.options];
340 dnsConfig = [test parseDNSAgentOptions];
341 if (dnsConfig == nil) {
342 dnsConfig = @{ (__bridge NSString *)kSCPropNetDNSServerAddresses:@[@"10.10.10.101", @"10.10.10.102", @"10.10.10.103"],
343 (__bridge NSString *)kSCPropNetDNSSupplementalMatchDomains:@[@TEST_DOMAIN],
347 hostname = [[dnsConfig objectForKey:(__bridge NSString *)kSCPropNetDNSSupplementalMatchDomains] objectAtIndex:0];
348 hostEndpoint = [NWHostEndpoint endpointWithHostname:hostname port:@"80"];
349 pathEvaluator = [[NWPathEvaluator alloc] initWithEndpoint:hostEndpoint parameters:NULL];
350 [pathEvaluator addObserver:test
352 options:NSKeyValueObservingOptionNew
356 [test publishToSCDynamicStore:test.dnsKey value:dnsConfig];
357 test.testDNS = [test createDNSArray:dnsConfig];
359 // Wait for the path changes to quiesce
360 [test waitFor:PATH_QUIESCE_TIME];
361 if (![test.testDNS isEqualToArray:test.pathDNS]) {
362 SCTestLog("test DNS and applied DNS do not match. Test: %@, Applied: %@", test.testDNS, test.pathDNS);
366 [test removeFromSCDynamicStore:test.dnsKey];
368 [test waitFor:PATH_QUIESCE_TIME];
370 SCTestLog("Verified that the configured DNS is same as applied DNS for a domain");
374 [pathEvaluator removeObserver:test
380 - (BOOL)unitTestInstallProxyWithLargeConfig
383 SCTestConfigAgent *test;
384 NSString *str = @"0123456789";
385 NSMutableString *largeStr;
386 NSDictionary *proxyConfig;
389 NWHostEndpoint *hostEndpoint;
390 NWPathEvaluator *pathEvaluator;
391 NSMutableDictionary *dict;
393 test = [[SCTestConfigAgent alloc] initWithOptions:self.options];
394 largeStr = [[NSMutableString alloc] init];
395 for (int i = 0; i < 200; i++) {
396 [largeStr appendString:str];
399 // We imitate a proxy config worth 2K bytes.
400 proxyConfig = @{(__bridge NSString *)kSCPropNetProxiesHTTPEnable:@(1),
401 (__bridge NSString *)kSCPropNetProxiesHTTPPort:@(80),
402 (__bridge NSString *)kSCPropNetProxiesHTTPProxy:@"10.10.10.100",
403 (__bridge NSString *)kSCPropNetProxiesProxyAutoConfigJavaScript:largeStr,
404 (__bridge NSString *)kSCPropNetProxiesProxyAutoConfigEnable:@(1),
405 (__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains:@[@TEST_DOMAIN],
408 hostname = [[proxyConfig objectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains] objectAtIndex:0];
409 port = [proxyConfig objectForKey:(__bridge NSString *)kSCPropNetProxiesHTTPPort];
410 hostEndpoint = [NWHostEndpoint endpointWithHostname:hostname port:port.stringValue];
411 pathEvaluator = [[NWPathEvaluator alloc] initWithEndpoint:hostEndpoint parameters:NULL];
412 [pathEvaluator addObserver:test
414 options:NSKeyValueObservingOptionNew
418 [test publishToSCDynamicStore:test.proxyKey value:proxyConfig];
419 dict = [NSMutableDictionary dictionaryWithDictionary:proxyConfig];
420 [dict removeObjectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains];
421 test.testProxy = @[@[dict]];
423 // Wait for the path changes to quiesce
424 [test waitFor:PATH_QUIESCE_TIME];
425 if ([test.testProxy isEqualToArray:test.pathProxy]) {
426 SCTestLog("applied proxy does not contain Out of Band Agent UUID");
430 // Now we verify that we are able to fetch the proxy configuration from configd
431 for (NSArray<NSDictionary *> *config in test.pathProxy) {
432 xpc_object_t xpcConfig = _CFXPCCreateXPCObjectFromCFObject((__bridge CFArrayRef)config);
433 xpc_object_t fetchedConfig = config_agent_update_proxy_information(xpcConfig);
434 if (fetchedConfig != nil) {
435 NSArray *nsConfig = (__bridge_transfer NSArray *)(_CFXPCCreateCFObjectFromXPCObject(fetchedConfig));
436 test.pathProxy = @[nsConfig];
441 if (![test.testProxy isEqualToArray:test.pathProxy]) {
442 SCTestLog("Could not fetch proxy configuration from configd. Test: %@, Applied: %@", test.testProxy, test.pathProxy);
446 SCTestLog("Verified that the proxy configuration is successfully fetched from configd");
447 test.testProxy = nil;
448 [test removeFromSCDynamicStore:test.proxyKey];
450 // Wait for the path changes to quiesce
451 [test waitFor:PATH_QUIESCE_TIME];
452 if (test.pathProxy != nil) {
453 SCTestLog("proxy applied when there is no test proxy");
460 [pathEvaluator removeObserver:test
466 - (BOOL)unitTestInstallDNSWithConflictingDomain
469 SCTestConfigAgent *test;
470 NSDictionary *dnsConfig;
472 NWHostEndpoint *hostEndpoint;
473 NWPathEvaluator *pathEvaluator;
475 test = [[SCTestConfigAgent alloc] initWithOptions:self.options];
476 dnsConfig = @{ (__bridge NSString *)kSCPropNetDNSServerAddresses:@[@"10.10.10.101", @"10.10.10.102", @"10.10.10.103"],
477 (__bridge NSString *)kSCPropNetDNSSupplementalMatchDomains:@[@TEST_DOMAIN],
480 hostname = [[dnsConfig objectForKey:(__bridge NSString *)kSCPropNetDNSSupplementalMatchDomains] objectAtIndex:0];
481 hostEndpoint = [NWHostEndpoint endpointWithHostname:hostname port:@"80"];
482 pathEvaluator = [[NWPathEvaluator alloc] initWithEndpoint:hostEndpoint parameters:NULL];
483 [pathEvaluator addObserver:test
485 options:NSKeyValueObservingOptionNew
489 NSDictionary *duplicateDnsConfig;
490 NSString *anotherFakeServiceID;
491 NSString *anotherDNSKey;
496 [test publishToSCDynamicStore:test.dnsKey value:dnsConfig];
497 test.testDNS = [test createDNSArray:dnsConfig];
499 // Wait for the path changes to quiesce
500 [test waitFor:PATH_QUIESCE_TIME];
501 if (![test.testDNS isEqualToArray:test.pathDNS]) {
502 SCTestLog("test DNS and applied DNS do not match. Test: %@, Applied: %@", test.testDNS, test.pathDNS);
506 // Now install the conflicting DNS configuration
507 duplicateDnsConfig = @{ (__bridge NSString *)kSCPropNetDNSServerAddresses:@[@"10.10.10.104", @"10.10.10.105", @"10.10.10.106"],
508 (__bridge NSString *)kSCPropNetDNSSupplementalMatchDomains:@[@TEST_DOMAIN],
511 anotherFakeServiceID = [NSUUID UUID].UUIDString;
512 anotherDNSKey = (__bridge_transfer NSString *)[self copyStateKeyWithServiceID:(__bridge CFStringRef)(anotherFakeServiceID) forEntity:kSCEntNetDNS];
514 [test publishToSCDynamicStore:anotherDNSKey value:duplicateDnsConfig];
515 array = [test.testDNS arrayByAddingObjectsFromArray:[test createDNSArray:duplicateDnsConfig]];
516 test.testDNS = array;
518 // Wait for the path changes to quiesce
519 [test waitFor:PATH_QUIESCE_TIME];
521 // Use NSSet for unordered comparison
522 testDNSSet = [NSSet setWithArray:test.testDNS];
523 pathDNSSet = [NSSet setWithArray:test.pathDNS];
524 success = [testDNSSet isEqualToSet:pathDNSSet];
525 [test removeFromSCDynamicStore:anotherDNSKey];
527 SCTestLog("test DNS and applied DNS for duplicate domains do not match. Test: %@, Applied: %@", test.testDNS, test.pathDNS);
531 [test removeFromSCDynamicStore:test.dnsKey];
533 [test waitFor:PATH_QUIESCE_TIME];
535 SCTestLog("Verified that the configured DNS with duplicate domains is same as applied DNS for a domain");
540 [pathEvaluator removeObserver:test
546 - (BOOL)unitTestInstallProxyWithConflictingDomain
549 SCTestConfigAgent *test;
550 NSDictionary *proxyConfig;
553 NWHostEndpoint *hostEndpoint;
554 NWPathEvaluator *pathEvaluator;
556 test = [[SCTestConfigAgent alloc] initWithOptions:self.options];
557 proxyConfig = @{(__bridge NSString *)kSCPropNetProxiesHTTPEnable:@(1),
558 (__bridge NSString *)kSCPropNetProxiesHTTPPort:@(80),
559 (__bridge NSString *)kSCPropNetProxiesHTTPProxy:@"10.10.10.100",
560 (__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains:@[@TEST_DOMAIN],
563 hostname = [[proxyConfig objectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains] objectAtIndex:0];
564 port = [proxyConfig objectForKey:(__bridge NSString *)kSCPropNetProxiesHTTPPort];
565 hostEndpoint = [NWHostEndpoint endpointWithHostname:hostname port:port.stringValue];
566 pathEvaluator = [[NWPathEvaluator alloc] initWithEndpoint:hostEndpoint parameters:NULL];
567 [pathEvaluator addObserver:test
569 options:NSKeyValueObservingOptionNew
573 NSMutableDictionary *dict;
574 NSMutableDictionary *dict2;
575 NSDictionary *duplicateProxyConfig;
576 NSString *anotherFakeServiceID;
577 NSString *anotherProxyKey;
581 [test publishToSCDynamicStore:test.proxyKey value:proxyConfig];
582 dict = [NSMutableDictionary dictionaryWithDictionary:proxyConfig];
583 [dict removeObjectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains];
584 test.testProxy = @[@[dict]];
586 // Wait for the path changes to quiesce
587 [test waitFor:PATH_QUIESCE_TIME];
588 if (![test.testProxy isEqualToArray:test.pathProxy]) {
589 SCTestLog("test proxy and applied proxy do not match. Test: %@, Applied: %@", test.testProxy, test.pathProxy);
593 // Now install the conflicting Proxy configuration
594 duplicateProxyConfig = @{(__bridge NSString *)kSCPropNetProxiesHTTPSEnable:@(1),
595 (__bridge NSString *)kSCPropNetProxiesHTTPSPort:@(8080),
596 (__bridge NSString *)kSCPropNetProxiesHTTPSProxy:@"10.10.10.101",
597 (__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains:@[@TEST_DOMAIN],
599 anotherFakeServiceID = [NSUUID UUID].UUIDString;
600 anotherProxyKey = (__bridge_transfer NSString *)[self copyStateKeyWithServiceID:(__bridge CFStringRef)(anotherFakeServiceID) forEntity:kSCEntNetProxies];
602 [test publishToSCDynamicStore:anotherProxyKey value:duplicateProxyConfig];;
603 dict2 = [NSMutableDictionary dictionaryWithDictionary:duplicateProxyConfig];
604 [dict2 removeObjectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains];
605 test.testProxy = @[@[dict],@[dict2]];
607 // Wait for the path changes to quiesce
608 [test waitFor:PATH_QUIESCE_TIME];
610 // Use NSSet for unordered comparison
611 testProxySet = [NSSet setWithArray:test.testProxy];
612 pathProxySet = [NSSet setWithArray:test.pathProxy];
613 success = [testProxySet isEqualToSet:pathProxySet];
614 [test removeFromSCDynamicStore:anotherProxyKey];
616 SCTestLog("test proxy and applied proxy for duplicate domains do not match. Test: %@, Applied: %@", test.testDNS, test.pathDNS);
620 SCTestLog("Verified the configured proxy with Duplicate domains is the same as applied proxy");
622 [test removeFromSCDynamicStore:test.proxyKey];
623 test.testProxy = nil;
625 // Wait for the path changes to quiesce
626 [test waitFor:PATH_QUIESCE_TIME];
628 if (test.pathProxy != nil) {
629 SCTestLog("proxy applied when there is no test proxy");
637 [pathEvaluator removeObserver:test
645 [self removeFromSCDynamicStore:self.proxyKey];
646 [self removeFromSCDynamicStore:self.dnsKey];
650 - (void)observeValueForKeyPath:(NSString *)keyPath
652 change:(NSDictionary *)change
653 context:(void *)context
655 #pragma unused(change)
656 #pragma unused(context)
657 NWPathEvaluator *pathEvaluator = (NWPathEvaluator *)object;
658 if ([keyPath isEqualToString:@"path"]) {
659 self.pathProxy = pathEvaluator.path.proxySettings;
660 self.pathDNS = pathEvaluator.path.dnsServers;