]> git.saurik.com Git - apple/configd.git/blob - sctest/SCTestConfigAgents.m
122f258adfc55dca4f0bf43a5a5455b2baa6340a
[apple/configd.git] / sctest / SCTestConfigAgents.m
1 /*
2 * Copyright (c) 2016 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #import "SCTest.h"
25 #import "SCTestUtils.h"
26 #import <Network/Network_Private.h>
27 #import <CoreFoundation/CFXPCBridge.h>
28 #import <config_agent_info.h>
29
30 #define TEST_DOMAIN "filemaker.com"
31 #define PATH_QUIESCE_TIME 1.5 // seconds
32
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;
42 @end
43
44 @implementation SCTestConfigAgent
45
46 - (instancetype)initWithOptions:(NSDictionary *)options
47 {
48 self = [super initWithOptions:options];
49 if (self) {
50 _serviceID = @"8F66B505-EAEF-4611-BD4D-C523FD9451F0";
51 _store = SCDynamicStoreCreate(kCFAllocatorDefault,
52 CFSTR("SCTest"),
53 NULL,
54 NULL);
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];
57 }
58 return self;
59 }
60
61 - (void)dealloc
62 {
63 if (self.store != NULL) {
64 CFRelease(self.store);
65 self.store = NULL;
66 }
67 }
68
69 + (NSString *)command
70 {
71 return @"config_agent";
72 }
73
74 + (NSString *)commandDescription
75 {
76 return @"Tests the Config Agent code path";
77 }
78
79 - (void)start
80 {
81 if (self.options[kSCTestConfigAgentRemoveProxy]) {
82 [self removeFromSCDynamicStore:self.proxyKey];
83 }
84
85 if (self.options[kSCTestConfigAgentRemoveDNS]) {
86 [self removeFromSCDynamicStore:self.dnsKey];
87 }
88
89 NSDictionary *proxyConfig = [self parseProxyAgentOptions];
90 if (proxyConfig != nil) {
91 [self publishToSCDynamicStore:self.proxyKey value:proxyConfig];
92 self.testProxy = @[@[proxyConfig]];
93 }
94
95 NSDictionary *dnsConfig = [self parseDNSAgentOptions];
96 if (dnsConfig != nil) {
97 [self publishToSCDynamicStore:self.dnsKey value:dnsConfig];
98 self.testDNS = [self createDNSArray:dnsConfig];
99 }
100
101 [self cleanupAndExitWithErrorCode:0];
102 }
103
104 - (CFStringRef)copyStateKeyWithServiceID:(CFStringRef)serviceID
105 forEntity:(CFStringRef)entity
106 {
107 return SCDynamicStoreKeyCreateNetworkServiceEntity(kCFAllocatorDefault,
108 kSCDynamicStoreDomainState,
109 serviceID,
110 entity);
111 }
112
113 - (NSArray<NWEndpoint *> *)createDNSArray:(NSDictionary *)dnsConfig
114 {
115 NSArray<NSString *> *dnsServers;
116 NSMutableArray<NWEndpoint *> *dnsArray;
117
118 dnsServers = [dnsConfig objectForKey:(__bridge NSString *)kSCPropNetDNSServerAddresses];
119 if (dnsServers == nil || [dnsServers count] == 0) {
120 return nil;
121 }
122
123 dnsArray = [[NSMutableArray alloc] init];
124 for (NSString *server in dnsServers) {
125 NWEndpoint *endpoint = (NWEndpoint *)[NWAddressEndpoint endpointWithHostname:server port:@"0"];
126 [dnsArray addObject:endpoint];
127 }
128
129 return dnsArray;
130 }
131
132 - (void)publishToSCDynamicStore:(NSString *)key
133 value:(NSDictionary *)value
134 {
135
136 BOOL ok = SCDynamicStoreSetValue(self.store, (__bridge CFStringRef)key, (__bridge CFPropertyListRef _Nonnull)(value));
137 if (!ok) {
138 int error = SCError();
139 if (error == kSCStatusNoKey) {
140 return;
141 }
142 SCTestLog("Could not set in SCDynamicStore: Error: %s", SCErrorString(error));
143 return;
144 }
145 }
146
147 - (void)removeFromSCDynamicStore:(NSString *)key
148 {
149 BOOL ok = SCDynamicStoreRemoveValue(self.store, (__bridge CFStringRef)key);
150 if (!ok) {
151 int error = SCError();
152 if (error == kSCStatusNoKey) {
153 return;
154 }
155 SCTestLog("Could not remove key: %@, Error: %s", key, SCErrorString(error));
156 return;
157 }
158 }
159
160 - (NSDictionary *)parseProxyAgentOptions
161 {
162 NSMutableDictionary *proxyConfig = [[NSMutableDictionary alloc] init];
163
164 #define NS_NUMBER(x) [NSNumber numberWithInt:x]
165
166 #define SET_PROXY_CONFIG(proxyType) \
167 do { \
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"); \
173 ERR_EXIT; \
174 } \
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]; \
180 } \
181 } while(0);
182
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);
188
189 if ([proxyConfig count] > 0) {
190 NSString *matchDomain = self.options[kSCTestConfigAgentProxyMatchDomain] ? self.options[kSCTestConfigAgentProxyMatchDomain] : @TEST_DOMAIN;
191 [proxyConfig setObject:@[matchDomain] forKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains];
192 } else {
193 proxyConfig = nil;
194 }
195
196 return proxyConfig;
197 #undef SET_PROXY_CONFIG
198 }
199
200 - (NSDictionary *)parseDNSAgentOptions
201 {
202 NSMutableDictionary *dnsConfig;
203 NSString *dnsServerString;
204 NSString *dnsDomainString;
205 NSArray<NSString *> *dnsServers;
206 NSArray<NSString *> *dnsDomains;
207
208 dnsConfig = [[NSMutableDictionary alloc] init];
209 dnsServerString = self.options[kSCTestConfigAgentDNSServers];
210 if (dnsServerString == nil) {
211 return nil;
212 }
213
214 dnsDomainString = self.options[kSCTestConfigAgentDNSDomains];
215 if (dnsDomainString == nil) {
216 dnsDomainString = @TEST_DOMAIN;
217 }
218
219 dnsServers = [dnsServerString componentsSeparatedByString:@","];
220 [dnsConfig setObject:dnsServers forKey:(__bridge NSString *)kSCPropNetDNSServerAddresses];
221
222 dnsDomains = [dnsDomainString componentsSeparatedByString:@","];
223 [dnsConfig setObject:dnsDomains forKey:(__bridge NSString *)kSCPropNetDNSSupplementalMatchDomains];
224
225 return dnsConfig;
226 }
227
228 - (void)cleanupAndExitWithErrorCode:(int)error
229 {
230 CFRelease(self.store);
231 [super cleanupAndExitWithErrorCode:error];
232 }
233
234 - (BOOL)setup
235 {
236 return YES;
237 }
238
239 - (BOOL)unitTest
240 {
241 if(![self setup]) {
242 return NO;
243 }
244
245 BOOL allUnitTestsPassed = YES;
246 allUnitTestsPassed &= [self unitTestInstallProxy];
247 allUnitTestsPassed &= [self unitTestInstallProxyWithLargeConfig];
248 allUnitTestsPassed &= [self unitTestInstallProxyWithConflictingDomain];
249 allUnitTestsPassed &= [self unitTestInstallDNS];
250 allUnitTestsPassed &= [self unitTestInstallDNSWithConflictingDomain];
251
252 if(![self tearDown]) {
253 return NO;
254 }
255
256 return allUnitTestsPassed;
257 }
258
259 - (BOOL)unitTestInstallProxy
260 {
261 BOOL success = NO;
262 SCTestConfigAgent *test;
263 NSDictionary *proxyConfig;
264 NSString *hostname;
265 NSNumber *port;
266 NWHostEndpoint *hostEndpoint;
267 NWPathEvaluator *pathEvaluator;
268 NSMutableDictionary *dict;
269
270 test = [[SCTestConfigAgent alloc] initWithOptions:self.options];
271 proxyConfig = [test parseProxyAgentOptions];
272 if (proxyConfig == nil) {
273 // Use default options
274 proxyConfig = @{(__bridge NSString *)kSCPropNetProxiesHTTPEnable:@(1),
275 (__bridge NSString *)kSCPropNetProxiesHTTPPort:@(80),
276 (__bridge NSString *)kSCPropNetProxiesHTTPProxy:@"10.10.10.100",
277 (__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains:@[@TEST_DOMAIN],
278 };
279 }
280
281 hostname = [[proxyConfig objectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains] objectAtIndex:0];
282 port = [proxyConfig objectForKey:(__bridge NSString *)kSCPropNetProxiesHTTPPort];
283 hostEndpoint = [NWHostEndpoint endpointWithHostname:hostname port:port.stringValue];
284 pathEvaluator = [[NWPathEvaluator alloc] initWithEndpoint:hostEndpoint parameters:NULL];
285 [pathEvaluator addObserver:test
286 forKeyPath:@"path"
287 options:NSKeyValueObservingOptionNew
288 context:nil];
289
290 do {
291 [test publishToSCDynamicStore:test.proxyKey value:proxyConfig];
292 dict = [NSMutableDictionary dictionaryWithDictionary:proxyConfig];
293 [dict removeObjectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains];
294 test.testProxy = @[@[dict]];
295
296 // Wait for the path changes to quiesce
297 [test waitFor:PATH_QUIESCE_TIME];
298 if (![test.testProxy isEqualToArray:test.pathProxy]) {
299 SCTestLog("test proxy and applied proxy do not match. Test: %@, Applied: %@", test.testProxy, test.pathProxy);
300 break;
301 }
302
303 SCTestLog("Verified the configured proxy is the same as applied proxy");
304 [test removeFromSCDynamicStore:test.proxyKey];
305 test.testProxy = nil;
306 // Wait for the path changes to quiesce
307 [test waitFor:PATH_QUIESCE_TIME];
308 if (test.pathProxy != nil) {
309 SCTestLog("proxy applied when there is no test proxy");
310 break;
311 }
312
313 success = YES;
314 } while(0);
315
316 [pathEvaluator removeObserver:test
317 forKeyPath:@"path"];
318
319 return success;
320 }
321
322 - (BOOL)unitTestInstallDNS
323 {
324 BOOL success = NO;
325 SCTestConfigAgent *test;
326 NSDictionary *dnsConfig;
327 NSString *hostname;
328 NWHostEndpoint *hostEndpoint;
329 NWPathEvaluator *pathEvaluator;
330
331 test = [[SCTestConfigAgent alloc] initWithOptions:self.options];
332 dnsConfig = [test parseDNSAgentOptions];
333 if (dnsConfig == nil) {
334 dnsConfig = @{ (__bridge NSString *)kSCPropNetDNSServerAddresses:@[@"10.10.10.101", @"10.10.10.102", @"10.10.10.103"],
335 (__bridge NSString *)kSCPropNetDNSSupplementalMatchDomains:@[@TEST_DOMAIN],
336 };
337 }
338
339 hostname = [[dnsConfig objectForKey:(__bridge NSString *)kSCPropNetDNSSupplementalMatchDomains] objectAtIndex:0];
340 hostEndpoint = [NWHostEndpoint endpointWithHostname:hostname port:@"80"];
341 pathEvaluator = [[NWPathEvaluator alloc] initWithEndpoint:hostEndpoint parameters:NULL];
342 [pathEvaluator addObserver:test
343 forKeyPath:@"path"
344 options:NSKeyValueObservingOptionNew
345 context:nil];
346
347 do {
348 [test publishToSCDynamicStore:test.dnsKey value:dnsConfig];
349 test.testDNS = [test createDNSArray:dnsConfig];
350
351 // Wait for the path changes to quiesce
352 [test waitFor:PATH_QUIESCE_TIME];
353 if (![test.testDNS isEqualToArray:test.pathDNS]) {
354 SCTestLog("test DNS and applied DNS do not match. Test: %@, Applied: %@", test.testDNS, test.pathDNS);
355 break;
356 }
357
358 [test removeFromSCDynamicStore:test.dnsKey];
359 test.testDNS = nil;
360 [test waitFor:PATH_QUIESCE_TIME];
361
362 SCTestLog("Verified that the configured DNS is same as applied DNS for a domain");
363 success = YES;
364 } while (0);
365
366 [pathEvaluator removeObserver:test
367 forKeyPath:@"path"];
368
369 return success;
370 }
371
372 - (BOOL)unitTestInstallProxyWithLargeConfig
373 {
374 BOOL success = NO;
375 SCTestConfigAgent *test;
376 NSString *str = @"0123456789";
377 NSMutableString *largeStr;
378 NSDictionary *proxyConfig;
379 NSString *hostname;
380 NSNumber *port;
381 NWHostEndpoint *hostEndpoint;
382 NWPathEvaluator *pathEvaluator;
383 NSMutableDictionary *dict;
384
385 test = [[SCTestConfigAgent alloc] initWithOptions:self.options];
386 largeStr = [[NSMutableString alloc] init];
387 for (int i = 0; i < 200; i++) {
388 [largeStr appendString:str];
389 }
390
391 // We imitate a proxy config worth 2K bytes.
392 proxyConfig = @{(__bridge NSString *)kSCPropNetProxiesHTTPEnable:@(1),
393 (__bridge NSString *)kSCPropNetProxiesHTTPPort:@(80),
394 (__bridge NSString *)kSCPropNetProxiesHTTPProxy:@"10.10.10.100",
395 (__bridge NSString *)kSCPropNetProxiesProxyAutoConfigJavaScript:largeStr,
396 (__bridge NSString *)kSCPropNetProxiesProxyAutoConfigEnable:@(1),
397 (__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains:@[@TEST_DOMAIN],
398 };
399
400 hostname = [[proxyConfig objectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains] objectAtIndex:0];
401 port = [proxyConfig objectForKey:(__bridge NSString *)kSCPropNetProxiesHTTPPort];
402 hostEndpoint = [NWHostEndpoint endpointWithHostname:hostname port:port.stringValue];
403 pathEvaluator = [[NWPathEvaluator alloc] initWithEndpoint:hostEndpoint parameters:NULL];
404 [pathEvaluator addObserver:test
405 forKeyPath:@"path"
406 options:NSKeyValueObservingOptionNew
407 context:nil];
408
409 do {
410 [test publishToSCDynamicStore:test.proxyKey value:proxyConfig];
411 dict = [NSMutableDictionary dictionaryWithDictionary:proxyConfig];
412 [dict removeObjectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains];
413 test.testProxy = @[@[dict]];
414
415 // Wait for the path changes to quiesce
416 [test waitFor:PATH_QUIESCE_TIME];
417 if ([test.testProxy isEqualToArray:test.pathProxy]) {
418 SCTestLog("applied proxy does not contain Out of Band Agent UUID");
419 break;
420 }
421
422 // Now we verify that we are able to fetch the proxy configuration from configd
423 for (NSArray<NSDictionary *> *config in test.pathProxy) {
424 xpc_object_t xpcConfig = _CFXPCCreateXPCObjectFromCFObject((__bridge CFArrayRef)config);
425 xpc_object_t fetchedConfig = config_agent_update_proxy_information(xpcConfig);
426 if (fetchedConfig != nil) {
427 NSArray *nsConfig = (__bridge_transfer NSArray *)(_CFXPCCreateCFObjectFromXPCObject(fetchedConfig));
428 test.pathProxy = @[nsConfig];
429 break;
430 }
431 }
432
433 if (![test.testProxy isEqualToArray:test.pathProxy]) {
434 SCTestLog("Could not fetch proxy configuration from configd. Test: %@, Applied: %@", test.testProxy, test.pathProxy);
435 break;
436 }
437
438 SCTestLog("Verified that the proxy configuration is successfully fetched from configd");
439 test.testProxy = nil;
440 [test removeFromSCDynamicStore:test.proxyKey];
441
442 // Wait for the path changes to quiesce
443 [test waitFor:PATH_QUIESCE_TIME];
444 if (test.pathProxy != nil) {
445 SCTestLog("proxy applied when there is no test proxy");
446 break;
447 }
448
449 success = YES;
450 } while (0);
451
452 [pathEvaluator removeObserver:test
453 forKeyPath:@"path"];
454
455 return success;
456 }
457
458 - (BOOL)unitTestInstallDNSWithConflictingDomain
459 {
460 BOOL success = NO;
461 SCTestConfigAgent *test;
462 NSDictionary *dnsConfig;
463 NSString *hostname;
464 NWHostEndpoint *hostEndpoint;
465 NWPathEvaluator *pathEvaluator;
466
467 test = [[SCTestConfigAgent alloc] initWithOptions:self.options];
468 dnsConfig = @{ (__bridge NSString *)kSCPropNetDNSServerAddresses:@[@"10.10.10.101", @"10.10.10.102", @"10.10.10.103"],
469 (__bridge NSString *)kSCPropNetDNSSupplementalMatchDomains:@[@TEST_DOMAIN],
470 };
471
472 hostname = [[dnsConfig objectForKey:(__bridge NSString *)kSCPropNetDNSSupplementalMatchDomains] objectAtIndex:0];
473 hostEndpoint = [NWHostEndpoint endpointWithHostname:hostname port:@"80"];
474 pathEvaluator = [[NWPathEvaluator alloc] initWithEndpoint:hostEndpoint parameters:NULL];
475 [pathEvaluator addObserver:test
476 forKeyPath:@"path"
477 options:NSKeyValueObservingOptionNew
478 context:nil];
479
480 do {
481 NSDictionary *duplicateDnsConfig;
482 NSString *anotherFakeServiceID;
483 NSString *anotherDNSKey;
484 NSArray *array;
485 NSSet *testDNSSet;
486 NSSet *pathDNSSet;
487
488 [test publishToSCDynamicStore:test.dnsKey value:dnsConfig];
489 test.testDNS = [test createDNSArray:dnsConfig];
490
491 // Wait for the path changes to quiesce
492 [test waitFor:PATH_QUIESCE_TIME];
493 if (![test.testDNS isEqualToArray:test.pathDNS]) {
494 SCTestLog("test DNS and applied DNS do not match. Test: %@, Applied: %@", test.testDNS, test.pathDNS);
495 break;
496 }
497
498 // Now install the conflicting DNS configuration
499 duplicateDnsConfig = @{ (__bridge NSString *)kSCPropNetDNSServerAddresses:@[@"10.10.10.104", @"10.10.10.105", @"10.10.10.106"],
500 (__bridge NSString *)kSCPropNetDNSSupplementalMatchDomains:@[@TEST_DOMAIN],
501 };
502
503 anotherFakeServiceID = [NSUUID UUID].UUIDString;
504 anotherDNSKey = (__bridge_transfer NSString *)[self copyStateKeyWithServiceID:(__bridge CFStringRef)(anotherFakeServiceID) forEntity:kSCEntNetDNS];
505
506 [test publishToSCDynamicStore:anotherDNSKey value:duplicateDnsConfig];
507 array = [test.testDNS arrayByAddingObjectsFromArray:[test createDNSArray:duplicateDnsConfig]];
508 test.testDNS = array;
509
510 // Wait for the path changes to quiesce
511 [test waitFor:PATH_QUIESCE_TIME];
512
513 // Use NSSet for unordered comparison
514 testDNSSet = [NSSet setWithArray:test.testDNS];
515 pathDNSSet = [NSSet setWithArray:test.pathDNS];
516 success = [testDNSSet isEqualToSet:pathDNSSet];
517 [test removeFromSCDynamicStore:anotherDNSKey];
518 if (!success) {
519 SCTestLog("test DNS and applied DNS for duplicate domains do not match. Test: %@, Applied: %@", test.testDNS, test.pathDNS);
520 break;
521 }
522
523 [test removeFromSCDynamicStore:test.dnsKey];
524 test.testDNS = nil;
525 [test waitFor:PATH_QUIESCE_TIME];
526
527 SCTestLog("Verified that the configured DNS with duplicate domains is same as applied DNS for a domain");
528 success = YES;
529
530 } while (0);
531
532 [pathEvaluator removeObserver:test
533 forKeyPath:@"path"];
534
535 return success;
536 }
537
538 - (BOOL)unitTestInstallProxyWithConflictingDomain
539 {
540 BOOL success = NO;
541 SCTestConfigAgent *test;
542 NSDictionary *proxyConfig;
543 NSString *hostname;
544 NSNumber *port;
545 NWHostEndpoint *hostEndpoint;
546 NWPathEvaluator *pathEvaluator;
547
548 test = [[SCTestConfigAgent alloc] initWithOptions:self.options];
549 proxyConfig = @{(__bridge NSString *)kSCPropNetProxiesHTTPEnable:@(1),
550 (__bridge NSString *)kSCPropNetProxiesHTTPPort:@(80),
551 (__bridge NSString *)kSCPropNetProxiesHTTPProxy:@"10.10.10.100",
552 (__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains:@[@TEST_DOMAIN],
553 };
554
555 hostname = [[proxyConfig objectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains] objectAtIndex:0];
556 port = [proxyConfig objectForKey:(__bridge NSString *)kSCPropNetProxiesHTTPPort];
557 hostEndpoint = [NWHostEndpoint endpointWithHostname:hostname port:port.stringValue];
558 pathEvaluator = [[NWPathEvaluator alloc] initWithEndpoint:hostEndpoint parameters:NULL];
559 [pathEvaluator addObserver:test
560 forKeyPath:@"path"
561 options:NSKeyValueObservingOptionNew
562 context:nil];
563
564 do {
565 NSMutableDictionary *dict;
566 NSMutableDictionary *dict2;
567 NSDictionary *duplicateProxyConfig;
568 NSString *anotherFakeServiceID;
569 NSString *anotherProxyKey;
570 NSSet *testProxySet;
571 NSSet *pathProxySet;
572
573 [test publishToSCDynamicStore:test.proxyKey value:proxyConfig];
574 dict = [NSMutableDictionary dictionaryWithDictionary:proxyConfig];
575 [dict removeObjectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains];
576 test.testProxy = @[@[dict]];
577
578 // Wait for the path changes to quiesce
579 [test waitFor:PATH_QUIESCE_TIME];
580 if (![test.testProxy isEqualToArray:test.pathProxy]) {
581 SCTestLog("test proxy and applied proxy do not match. Test: %@, Applied: %@", test.testProxy, test.pathProxy);
582 break;
583 }
584
585 // Now install the conflicting Proxy configuration
586 duplicateProxyConfig = @{(__bridge NSString *)kSCPropNetProxiesHTTPSEnable:@(1),
587 (__bridge NSString *)kSCPropNetProxiesHTTPSPort:@(8080),
588 (__bridge NSString *)kSCPropNetProxiesHTTPSProxy:@"10.10.10.101",
589 (__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains:@[@TEST_DOMAIN],
590 };
591 anotherFakeServiceID = [NSUUID UUID].UUIDString;
592 anotherProxyKey = (__bridge_transfer NSString *)[self copyStateKeyWithServiceID:(__bridge CFStringRef)(anotherFakeServiceID) forEntity:kSCEntNetProxies];
593
594 [test publishToSCDynamicStore:anotherProxyKey value:duplicateProxyConfig];;
595 dict2 = [NSMutableDictionary dictionaryWithDictionary:duplicateProxyConfig];
596 [dict2 removeObjectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains];
597 test.testProxy = @[@[dict],@[dict2]];
598
599 // Wait for the path changes to quiesce
600 [test waitFor:PATH_QUIESCE_TIME];
601
602 // Use NSSet for unordered comparison
603 testProxySet = [NSSet setWithArray:test.testProxy];
604 pathProxySet = [NSSet setWithArray:test.pathProxy];
605 success = [testProxySet isEqualToSet:pathProxySet];
606 [test removeFromSCDynamicStore:anotherProxyKey];
607 if (!success) {
608 SCTestLog("test proxy and applied proxy for duplicate domains do not match. Test: %@, Applied: %@", test.testDNS, test.pathDNS);
609 break;
610 }
611
612 SCTestLog("Verified the configured proxy with Duplicate domains is the same as applied proxy");
613
614 [test removeFromSCDynamicStore:test.proxyKey];
615 test.testProxy = nil;
616
617 // Wait for the path changes to quiesce
618 [test waitFor:PATH_QUIESCE_TIME];
619
620 if (test.pathProxy != nil) {
621 SCTestLog("proxy applied when there is no test proxy");
622 break;
623 }
624
625 success = YES;
626
627 } while(0);
628
629 [pathEvaluator removeObserver:test
630 forKeyPath:@"path"];
631
632 return success;
633 }
634
635 - (BOOL)tearDown
636 {
637 [self removeFromSCDynamicStore:self.proxyKey];
638 [self removeFromSCDynamicStore:self.dnsKey];
639 return YES;
640 }
641
642 - (void)observeValueForKeyPath:(NSString *)keyPath
643 ofObject:(id)object
644 change:(NSDictionary *)change
645 context:(void *)context
646 {
647 NWPathEvaluator *pathEvaluator = (NWPathEvaluator *)object;
648 if ([keyPath isEqualToString:@"path"]) {
649 self.pathProxy = pathEvaluator.path.proxySettings;
650 self.pathDNS = pathEvaluator.path.dnsServers;
651 }
652 }
653
654 @end