]> git.saurik.com Git - apple/configd.git/blob - sctest/SCTestConfigAgents.m
configd-963.250.1.tar.gz
[apple/configd.git] / sctest / SCTestConfigAgents.m
1 /*
2 * Copyright (c) 2016, 2017 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
191 NSArray<NSString *> *domains = nil;
192 NSString *matchDomains = self.options[kSCTestConfigAgentProxyMatchDomain];
193 if (matchDomains == nil) {
194 domains = @[@TEST_DOMAIN];
195 } else {
196 domains = [matchDomains componentsSeparatedByString:@","];
197 }
198
199 [proxyConfig setObject:domains forKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains];
200 } else {
201 proxyConfig = nil;
202 }
203
204 return proxyConfig;
205 #undef SET_PROXY_CONFIG
206 }
207
208 - (NSDictionary *)parseDNSAgentOptions
209 {
210 NSMutableDictionary *dnsConfig;
211 NSString *dnsServerString;
212 NSString *dnsDomainString;
213 NSArray<NSString *> *dnsServers;
214 NSArray<NSString *> *dnsDomains;
215
216 dnsConfig = [[NSMutableDictionary alloc] init];
217 dnsServerString = self.options[kSCTestConfigAgentDNSServers];
218 if (dnsServerString == nil) {
219 return nil;
220 }
221
222 dnsDomainString = self.options[kSCTestConfigAgentDNSDomains];
223 if (dnsDomainString == nil) {
224 dnsDomainString = @TEST_DOMAIN;
225 }
226
227 dnsServers = [dnsServerString componentsSeparatedByString:@","];
228 [dnsConfig setObject:dnsServers forKey:(__bridge NSString *)kSCPropNetDNSServerAddresses];
229
230 dnsDomains = [dnsDomainString componentsSeparatedByString:@","];
231 [dnsConfig setObject:dnsDomains forKey:(__bridge NSString *)kSCPropNetDNSSupplementalMatchDomains];
232
233 return dnsConfig;
234 }
235
236 - (void)cleanupAndExitWithErrorCode:(int)error
237 {
238 CFRelease(self.store);
239 [super cleanupAndExitWithErrorCode:error];
240 }
241
242 - (BOOL)setup
243 {
244 return YES;
245 }
246
247 - (BOOL)unitTest
248 {
249 if(![self setup]) {
250 return NO;
251 }
252
253 BOOL allUnitTestsPassed = YES;
254 allUnitTestsPassed &= [self unitTestInstallProxy];
255 allUnitTestsPassed &= [self unitTestInstallProxyWithLargeConfig];
256 allUnitTestsPassed &= [self unitTestInstallProxyWithConflictingDomain];
257 allUnitTestsPassed &= [self unitTestInstallDNS];
258 allUnitTestsPassed &= [self unitTestInstallDNSWithConflictingDomain];
259
260 if(![self tearDown]) {
261 return NO;
262 }
263
264 return allUnitTestsPassed;
265 }
266
267 - (BOOL)unitTestInstallProxy
268 {
269 BOOL success = NO;
270 SCTestConfigAgent *test;
271 NSDictionary *proxyConfig;
272 NSString *hostname;
273 NSNumber *port;
274 NWHostEndpoint *hostEndpoint;
275 NWPathEvaluator *pathEvaluator;
276 NSMutableDictionary *dict;
277
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],
286 };
287 }
288
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
294 forKeyPath:@"path"
295 options:NSKeyValueObservingOptionNew
296 context:nil];
297
298 do {
299 [test publishToSCDynamicStore:test.proxyKey value:proxyConfig];
300 dict = [NSMutableDictionary dictionaryWithDictionary:proxyConfig];
301 [dict removeObjectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains];
302 test.testProxy = @[@[dict]];
303
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);
308 break;
309 }
310
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");
318 break;
319 }
320
321 success = YES;
322 } while(0);
323
324 [pathEvaluator removeObserver:test
325 forKeyPath:@"path"];
326
327 return success;
328 }
329
330 - (BOOL)unitTestInstallDNS
331 {
332 BOOL success = NO;
333 SCTestConfigAgent *test;
334 NSDictionary *dnsConfig;
335 NSString *hostname;
336 NWHostEndpoint *hostEndpoint;
337 NWPathEvaluator *pathEvaluator;
338
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],
344 };
345 }
346
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
351 forKeyPath:@"path"
352 options:NSKeyValueObservingOptionNew
353 context:nil];
354
355 do {
356 [test publishToSCDynamicStore:test.dnsKey value:dnsConfig];
357 test.testDNS = [test createDNSArray:dnsConfig];
358
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);
363 break;
364 }
365
366 [test removeFromSCDynamicStore:test.dnsKey];
367 test.testDNS = nil;
368 [test waitFor:PATH_QUIESCE_TIME];
369
370 SCTestLog("Verified that the configured DNS is same as applied DNS for a domain");
371 success = YES;
372 } while (0);
373
374 [pathEvaluator removeObserver:test
375 forKeyPath:@"path"];
376
377 return success;
378 }
379
380 - (BOOL)unitTestInstallProxyWithLargeConfig
381 {
382 BOOL success = NO;
383 SCTestConfigAgent *test;
384 NSString *str = @"0123456789";
385 NSMutableString *largeStr;
386 NSDictionary *proxyConfig;
387 NSString *hostname;
388 NSNumber *port;
389 NWHostEndpoint *hostEndpoint;
390 NWPathEvaluator *pathEvaluator;
391 NSMutableDictionary *dict;
392
393 test = [[SCTestConfigAgent alloc] initWithOptions:self.options];
394 largeStr = [[NSMutableString alloc] init];
395 for (int i = 0; i < 200; i++) {
396 [largeStr appendString:str];
397 }
398
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],
406 };
407
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
413 forKeyPath:@"path"
414 options:NSKeyValueObservingOptionNew
415 context:nil];
416
417 do {
418 [test publishToSCDynamicStore:test.proxyKey value:proxyConfig];
419 dict = [NSMutableDictionary dictionaryWithDictionary:proxyConfig];
420 [dict removeObjectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains];
421 test.testProxy = @[@[dict]];
422
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");
427 break;
428 }
429
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];
437 break;
438 }
439 }
440
441 if (![test.testProxy isEqualToArray:test.pathProxy]) {
442 SCTestLog("Could not fetch proxy configuration from configd. Test: %@, Applied: %@", test.testProxy, test.pathProxy);
443 break;
444 }
445
446 SCTestLog("Verified that the proxy configuration is successfully fetched from configd");
447 test.testProxy = nil;
448 [test removeFromSCDynamicStore:test.proxyKey];
449
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");
454 break;
455 }
456
457 success = YES;
458 } while (0);
459
460 [pathEvaluator removeObserver:test
461 forKeyPath:@"path"];
462
463 return success;
464 }
465
466 - (BOOL)unitTestInstallDNSWithConflictingDomain
467 {
468 BOOL success = NO;
469 SCTestConfigAgent *test;
470 NSDictionary *dnsConfig;
471 NSString *hostname;
472 NWHostEndpoint *hostEndpoint;
473 NWPathEvaluator *pathEvaluator;
474
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],
478 };
479
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
484 forKeyPath:@"path"
485 options:NSKeyValueObservingOptionNew
486 context:nil];
487
488 do {
489 NSDictionary *duplicateDnsConfig;
490 NSString *anotherFakeServiceID;
491 NSString *anotherDNSKey;
492 NSArray *array;
493 NSSet *testDNSSet;
494 NSSet *pathDNSSet;
495
496 [test publishToSCDynamicStore:test.dnsKey value:dnsConfig];
497 test.testDNS = [test createDNSArray:dnsConfig];
498
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);
503 break;
504 }
505
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],
509 };
510
511 anotherFakeServiceID = [NSUUID UUID].UUIDString;
512 anotherDNSKey = (__bridge_transfer NSString *)[self copyStateKeyWithServiceID:(__bridge CFStringRef)(anotherFakeServiceID) forEntity:kSCEntNetDNS];
513
514 [test publishToSCDynamicStore:anotherDNSKey value:duplicateDnsConfig];
515 array = [test.testDNS arrayByAddingObjectsFromArray:[test createDNSArray:duplicateDnsConfig]];
516 test.testDNS = array;
517
518 // Wait for the path changes to quiesce
519 [test waitFor:PATH_QUIESCE_TIME];
520
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];
526 if (!success) {
527 SCTestLog("test DNS and applied DNS for duplicate domains do not match. Test: %@, Applied: %@", test.testDNS, test.pathDNS);
528 break;
529 }
530
531 [test removeFromSCDynamicStore:test.dnsKey];
532 test.testDNS = nil;
533 [test waitFor:PATH_QUIESCE_TIME];
534
535 SCTestLog("Verified that the configured DNS with duplicate domains is same as applied DNS for a domain");
536 success = YES;
537
538 } while (0);
539
540 [pathEvaluator removeObserver:test
541 forKeyPath:@"path"];
542
543 return success;
544 }
545
546 - (BOOL)unitTestInstallProxyWithConflictingDomain
547 {
548 BOOL success = NO;
549 SCTestConfigAgent *test;
550 NSDictionary *proxyConfig;
551 NSString *hostname;
552 NSNumber *port;
553 NWHostEndpoint *hostEndpoint;
554 NWPathEvaluator *pathEvaluator;
555
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],
561 };
562
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
568 forKeyPath:@"path"
569 options:NSKeyValueObservingOptionNew
570 context:nil];
571
572 do {
573 NSMutableDictionary *dict;
574 NSMutableDictionary *dict2;
575 NSDictionary *duplicateProxyConfig;
576 NSString *anotherFakeServiceID;
577 NSString *anotherProxyKey;
578 NSSet *testProxySet;
579 NSSet *pathProxySet;
580
581 [test publishToSCDynamicStore:test.proxyKey value:proxyConfig];
582 dict = [NSMutableDictionary dictionaryWithDictionary:proxyConfig];
583 [dict removeObjectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains];
584 test.testProxy = @[@[dict]];
585
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);
590 break;
591 }
592
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],
598 };
599 anotherFakeServiceID = [NSUUID UUID].UUIDString;
600 anotherProxyKey = (__bridge_transfer NSString *)[self copyStateKeyWithServiceID:(__bridge CFStringRef)(anotherFakeServiceID) forEntity:kSCEntNetProxies];
601
602 [test publishToSCDynamicStore:anotherProxyKey value:duplicateProxyConfig];;
603 dict2 = [NSMutableDictionary dictionaryWithDictionary:duplicateProxyConfig];
604 [dict2 removeObjectForKey:(__bridge NSString *)kSCPropNetProxiesSupplementalMatchDomains];
605 test.testProxy = @[@[dict],@[dict2]];
606
607 // Wait for the path changes to quiesce
608 [test waitFor:PATH_QUIESCE_TIME];
609
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];
615 if (!success) {
616 SCTestLog("test proxy and applied proxy for duplicate domains do not match. Test: %@, Applied: %@", test.testDNS, test.pathDNS);
617 break;
618 }
619
620 SCTestLog("Verified the configured proxy with Duplicate domains is the same as applied proxy");
621
622 [test removeFromSCDynamicStore:test.proxyKey];
623 test.testProxy = nil;
624
625 // Wait for the path changes to quiesce
626 [test waitFor:PATH_QUIESCE_TIME];
627
628 if (test.pathProxy != nil) {
629 SCTestLog("proxy applied when there is no test proxy");
630 break;
631 }
632
633 success = YES;
634
635 } while(0);
636
637 [pathEvaluator removeObserver:test
638 forKeyPath:@"path"];
639
640 return success;
641 }
642
643 - (BOOL)tearDown
644 {
645 [self removeFromSCDynamicStore:self.proxyKey];
646 [self removeFromSCDynamicStore:self.dnsKey];
647 return YES;
648 }
649
650 - (void)observeValueForKeyPath:(NSString *)keyPath
651 ofObject:(id)object
652 change:(NSDictionary *)change
653 context:(void *)context
654 {
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;
661 }
662 }
663
664 @end