]> git.saurik.com Git - apple/configd.git/blob - Plugins/IPMonitor/controller.m
configd-963.tar.gz
[apple/configd.git] / Plugins / IPMonitor / controller.m
1 /*
2 * Copyright (c) 2015-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 "controller.h"
25 #import <SystemConfiguration/SCPrivate.h>
26 #import "ip_plugin.h"
27
28 #define numberToNSNumber(x) [NSNumber numberWithUnsignedInteger:x]
29
30 #define dnsAgentDefault "_defaultDNS"
31 #define proxyAgentDefault "_defaultProxy"
32 #define multipleEntitySuffix " #"
33 #define prefixForInterfaceName "@"
34
35 /* These define the starting and ending order of each policy section */
36 #define INIT_ORDER_FOR_SCOPED_INTERFACE_POLICY 100
37 #define INIT_ORDER_FOR_DOMAIN_POLICY 500
38 #define INIT_ORDER_FOR_DEFAULT_POLICY 1000
39
40 #define SKIP_ORDER_FOR_SCOPED_INTERFACE_POLICY 250
41 #define SKIP_ORDER_FOR_DOMAIN_POLICY 750
42 #define SKIP_ORDER_FOR_DEFAULT_POLICY 1250
43
44 #define POLICY_TYPE_NO_POLICY -1
45 #define CONFIG_AGENT_DATA_LIMIT MIN(NETAGENT_MAX_DATA_SIZE, 1024)
46
47 typedef struct resolverList {
48 dns_resolver_t **default_resolvers;
49 uint32_t n_default_resolvers;
50 dns_resolver_t **multicast_resolvers;
51 uint32_t n_multicast_resolvers;
52 dns_resolver_t **private_resolvers;
53 uint32_t n_private_resolvers;
54 } resolver_list_t;
55
56 @interface AgentController()
57
58 @property (nonatomic) NSMutableDictionary * floatingProxyAgentList;
59 @property (nonatomic) NSMutableDictionary * floatingDNSAgentList;
60 @property (nonatomic) NSMutableDictionary * policyDB;
61 @property (nonatomic) NEPolicySession * policySession;
62 @property (nonatomic) NEPolicySession * controlPolicySession;
63
64 @end
65
66 @implementation AgentController
67
68 #pragma mark Init
69
70 + (AgentController *)sharedController
71 {
72 static AgentController * gController = nil;
73 static dispatch_once_t onceToken;
74
75 dispatch_once(&onceToken, ^{
76 gController = [[AgentController alloc] init];
77 });
78
79 @synchronized (gController) {
80 if (![gController isControllerReady]) {
81 if (![gController initializeController]) {
82 return nil;
83 }
84 }
85 }
86
87 return gController;
88 }
89
90 - (instancetype)init
91 {
92 self = [super init];
93 if (self) {
94 [self initializeController];
95 }
96
97 return self;
98 }
99
100 - (BOOL)initializeController
101 {
102 const char *errorMessage = NULL;
103
104 do {
105 /* The NE policy session for the controller */
106
107 if (self.policySession == nil) {
108 self.policySession = [self createPolicySession];
109 if (self.policySession == nil) {
110 errorMessage = "Failed to create a policy session";
111 break;
112 }
113 }
114
115 /* A dictionary of all floating proxy agents
116 * Key : <entity-name> (can be an interface name or domain name)
117 * Value : agent object
118 */
119
120 if (self.floatingProxyAgentList == nil) {
121 self.floatingProxyAgentList = [NSMutableDictionary dictionary];
122 if (self.floatingProxyAgentList == nil) {
123 errorMessage = "Failed to create a dictionary";
124 break;
125 }
126 }
127
128 /* A dictionary of all floating dns agents
129 * Key : <entity-name> (can be an interface name or domain name)
130 * Value : agent object
131 */
132
133 if (self.floatingDNSAgentList == nil) {
134 self.floatingDNSAgentList = [NSMutableDictionary dictionary];
135 if (self.floatingDNSAgentList == nil) {
136 errorMessage = "Failed to create a dictionary";
137 break;
138 }
139 }
140
141 /* A dictionary for the maintaining the policy IDs for all installed policy.
142 * These IDs would be necessary to uninstall a policy when an agent goes away
143 * Key : agent name (which can be retrieved by [agent getAgentName])
144 * Value : An array of integers, each being a policy ID for that agent
145 */
146
147 if (self.policyDB == nil) {
148 self.policyDB = [NSMutableDictionary dictionary];
149 if (self.policyDB == nil) {
150 errorMessage = "Failed to create a dictionary";
151 break;
152 }
153 }
154
155 /* The queue to run the all processing on */
156
157 if (self.controllerQueue == nil) {
158 self.controllerQueue = dispatch_queue_create("com.apple.SystemConfiguration.controllerQueue", NULL);
159 if (self.controllerQueue == nil) {
160 errorMessage = "Failed to create a queue";
161 break;
162 }
163 }
164 } while (0);
165
166 if (errorMessage != NULL) {
167 /* Some error occurred. This is unlikely during controller initialization... */
168 SC_log(LOG_ERR, "Error occured while initializing AgentController: %s", errorMessage);
169 _SC_crash(errorMessage, NULL, NULL);
170 return NO;
171 }
172
173 return YES;
174 }
175
176 - (NEPolicySession *)createPolicySession
177 {
178 return [[NEPolicySession alloc] init];
179 }
180
181 - (BOOL)isControllerReady
182 {
183 /* Make sure that we have all our data structures in place */
184 return ((self.policySession != nil) &&
185 (self.floatingProxyAgentList != nil) &&
186 (self.floatingDNSAgentList != nil) &&
187 (self.policyDB != nil) &&
188 (self.controllerQueue != nil));
189 }
190
191 /* ========================== proxy agent helpers =========================== */
192 #pragma mark Proxy agent helper functions
193
194 - (NSData *)dataForProxyArray:(CFArrayRef)proxy_array_for_data
195 {
196 CFDataRef data = NULL;
197 (void)_SCSerialize(proxy_array_for_data, &data, NULL, NULL);
198 return (__bridge_transfer NSData *)data;
199 }
200
201 - (NSData *)dataForProxyDictionary:(CFDictionaryRef)domain_proxy
202 {
203 NSData * data = nil;
204 CFMutableDictionaryRef domain_proxy_dict;
205
206 if (domain_proxy == NULL) {
207 SC_log(LOG_NOTICE, "Invalid domain proxy dict");
208 return nil;
209 }
210
211 domain_proxy_dict = CFDictionaryCreateMutableCopy(NULL, 0, domain_proxy);
212 CFDictionaryRemoveValue(domain_proxy_dict, kSCPropNetProxiesSupplementalMatchDomain);
213
214 data = (__bridge_transfer NSData *)(SCNetworkProxiesCreateProxyAgentData(domain_proxy_dict));
215 CFRelease(domain_proxy_dict);
216
217 return data;
218 }
219
220 - (NSData *)getProxyDataFromCurrentConfig:(CFDictionaryRef)proxies
221 domain:(NSString *)domain
222 {
223 CFIndex count;
224 CFIndex idx;
225 CFArrayRef supplemental;
226
227 if (proxies == NULL || domain == nil) {
228 SC_log(LOG_NOTICE, "Invalid proxies/domain");
229 return nil;
230 }
231
232 supplemental = CFDictionaryGetValue(proxies, kSCPropNetProxiesSupplemental);
233 count = supplemental ? CFArrayGetCount(supplemental) : 0;
234
235 for (idx = 0; idx < count; idx++) {
236 CFDictionaryRef domain_proxy;
237 CFStringRef match_domain;
238
239 domain_proxy = CFArrayGetValueAtIndex(supplemental, idx);
240 match_domain = CFDictionaryGetValue(domain_proxy, kSCPropNetProxiesSupplementalMatchDomain);
241 if (match_domain != NULL && CFEqual(match_domain, (__bridge CFTypeRef)(domain))) {
242 return [self dataForProxyDictionary:domain_proxy];
243 }
244 }
245
246 return nil;
247 }
248
249 - (bool)getIntValue:(CFTypeRef)cf_value
250 valuePtr:(int *) int_value_ptr
251 {
252 bool valid = false;
253 if (cf_value && CFGetTypeID(cf_value) == CFNumberGetTypeID() && CFNumberGetValue(cf_value, kCFNumberIntType, int_value_ptr))
254 {
255 valid = true;
256 }
257 return valid;
258 }
259
260 - (int)countProxyEntriesEnabled:(CFDictionaryRef)proxies
261 {
262 int enabled = 0;
263
264 if (proxies == NULL) {
265 SC_log(LOG_NOTICE, "Invalid proxies");
266 return 0;
267 }
268
269 if (([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesHTTPEnable) valuePtr:&enabled] && enabled > 0) ||
270 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesHTTPSEnable) valuePtr:&enabled] && enabled > 0) ||
271 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesProxyAutoConfigEnable) valuePtr:&enabled] && enabled > 0) ||
272 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesFTPEnable) valuePtr:&enabled] && enabled > 0) ||
273 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesGopherEnable) valuePtr:&enabled] && enabled > 0) ||
274 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesRTSPEnable) valuePtr:&enabled] && enabled > 0) ||
275 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesSOCKSEnable) valuePtr:&enabled] && enabled > 0) ||
276 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesProxyAutoDiscoveryEnable) valuePtr:&enabled] && enabled > 0)) {
277 return enabled;
278 }
279
280 return 0;
281 }
282
283 - (void)processSupplementalProxyChanges:(CFDictionaryRef)proxies
284 {
285 CFIndex count;
286 NSMutableArray * deleteList;
287 NSCountedSet * duplicate_domain_list;
288 CFIndex idx;
289 NSMutableArray * new_domain_list;
290 NSMutableArray * old_domain_list;
291 CFArrayRef supplemental;
292 NSMutableArray * update_agent_list;
293
294 if (proxies == NULL) {
295 SC_log(LOG_INFO, "No proxy config to process");
296 return;
297 }
298
299 old_domain_list = [self getAgentList:self.floatingProxyAgentList
300 agentType:kAgentTypeProxy
301 agentSubType:kAgentSubTypeSupplemental];
302 duplicate_domain_list = [[NSCountedSet alloc] initWithCapacity:0];
303 new_domain_list = [NSMutableArray array];
304 update_agent_list = [NSMutableArray array];
305 supplemental = CFDictionaryGetValue(proxies, kSCPropNetProxiesSupplemental);
306 count = supplemental ? CFArrayGetCount(supplemental) : 0;
307 deleteList = [NSMutableArray array];
308
309 for (idx = 0; idx < count; idx++) {
310 CFDictionaryRef domain_proxy;
311 CFStringRef match_domain;
312 int proxy_count;
313
314 domain_proxy = CFArrayGetValueAtIndex(supplemental, idx);
315 match_domain = CFDictionaryGetValue(domain_proxy, kSCPropNetProxiesSupplementalMatchDomain);
316 if (match_domain == NULL) {
317 continue;
318 }
319
320 /* This domain is present in current config. But if it has generic (no protocols enabled)
321 * proxy content, there is no real use of that agent. Do NOT add it to
322 * the new_domain_list.
323 *
324 * This way, if there was an agent previously for this domain,
325 * it will be destroyed AND since it is not present in the new domain list, we wont
326 * spawn a new agent too! :)
327 */
328
329 proxy_count = [self countProxyEntriesEnabled:domain_proxy];
330 if (proxy_count == 0) {
331 SC_log(LOG_INFO, "Proxy settings on %@ are generic. Not recognizing as new domain", match_domain);
332 continue;
333 }
334
335 [new_domain_list addObject:(__bridge NSString *)match_domain];
336 }
337
338 [self cleanConflictingAgentsFromList:old_domain_list
339 new_list:new_domain_list
340 agentDictionary:self.floatingProxyAgentList];
341
342 for (NSString *key in old_domain_list) {
343 BOOL domain_present;
344
345 domain_present = [new_domain_list containsObject:key];
346 if (domain_present == NO) {
347 id agent;
348
349 agent = [self.floatingProxyAgentList objectForKey:key];
350 [self destroyFloatingAgent:agent];
351 }
352 }
353
354 /* At this point, whatever is in the controller's floating agent list,
355 * is present in the current proxy config. The current proxy config
356 * might have even more configs, not known to the controller, YET
357 */
358
359 for (NSString *domain in old_domain_list) {
360 id agent;
361 id mapped_agent;
362
363 agent = [self.floatingProxyAgentList objectForKey:domain];
364 if (agent == nil) {
365 continue;
366 }
367
368 /* Am I mapped to some agent? */
369 mapped_agent = [agent getAgentMapping];
370 if (mapped_agent) {
371 /* OK, this agent is mapped to some other agent. We compare this agent's data
372 * to the current data of the agent to which it is mapped. If different, we destroy
373 * the agent and later map it to someone else OR spawn a new one.
374 */
375 NSData * mapped_agent_data;
376
377 mapped_agent_data = [self getProxyDataFromCurrentConfig:proxies domain:[mapped_agent getAssociatedEntity]];
378 if (mapped_agent_data == nil || ![[agent getAgentData] isEqual:mapped_agent_data]) {
379 /* Something changed for mapped agent */
380 [deleteList addObject:agent];
381 continue;
382 }
383 } else {
384 /* Since this agent is NOT mapped to any other agent, this agent is
385 * registered with the kernel. So instead of destroying the agent and
386 * re-registering it, just update it here.
387 *
388 * All the agents which were mapped to this agent, will be deleted and
389 * re-mapped, if the data changed.
390 */
391 NSData * agent_data;
392
393 agent_data = [self getProxyDataFromCurrentConfig:proxies domain:[agent getAssociatedEntity]];
394 if (![[agent getAgentData] isEqual:agent_data]) {
395 /* Something changed for agent */
396 [agent updateAgentData:agent_data];
397
398 /* The reason I don't publish the data to agent here is that, if there were
399 * some agents mapping to this one, they will momentarily have a policy for
400 * using this agent UUID for some domain based on this agent's previous data.
401 */
402 [update_agent_list addObject:agent];
403 }
404 }
405 [new_domain_list removeObject:domain];
406 }
407
408 for (id agent in deleteList) {
409 SC_log(LOG_INFO, "Destroying agent %@ because something changed!", [agent getAgentName]);
410 [self destroyFloatingAgent:agent];
411 }
412
413 for (id agent in update_agent_list) {
414 [self publishToAgent:agent];
415 }
416
417 for (idx = 0; idx < count; idx++) {
418 CFDictionaryRef domain_proxy;
419 CFStringRef match_domain;
420
421 domain_proxy = CFArrayGetValueAtIndex(supplemental, idx);
422 match_domain = CFDictionaryGetValue(domain_proxy, kSCPropNetProxiesSupplementalMatchDomain);
423
424 if (match_domain != NULL) {
425 NSData * data;
426 NSUInteger found;
427 id mapped_agent;
428
429 found = [new_domain_list indexOfObject:(__bridge id _Nonnull)(match_domain)];
430 if (found == NSNotFound) {
431 continue;
432 }
433
434 /*
435 * We will only process agents which are mapped AND the agent they were mapped to, changed OR
436 * agents for domains which we did not know before.
437 */
438
439 NSUInteger domainInstance = [duplicate_domain_list countForObject:(__bridge id _Nonnull)(match_domain)];
440 if (domainInstance > 0) {
441 /* domainInstance will be > 0, only if we have conflicting domains */
442 domainInstance++;
443 NSString *ns_domain_name_copy = [NSString stringWithFormat:@"%@" multipleEntitySuffix "%lu", match_domain, (unsigned long)domainInstance];
444
445 data = [self dataForProxyDictionary:domain_proxy];
446
447 BOOL ok = [self spawnFloatingAgent:[ProxyAgent class]
448 entity:ns_domain_name_copy
449 agentSubType:kAgentSubTypeSupplemental
450 addPolicyOfType:NEPolicyConditionTypeDomain
451 publishData:data];
452 if (ok) {
453 id agent = [self.floatingProxyAgentList objectForKey:ns_domain_name_copy];
454 SC_log(LOG_INFO, "Duplicate Proxy agent %@", [agent getAgentName]);;
455 }
456 } else {
457 data = [self dataForProxyDictionary:domain_proxy];
458 mapped_agent = [self getAgentWithSameDataAndSubType:self.floatingProxyAgentList
459 data:data
460 subType:kAgentSubTypeSupplemental];
461 if (mapped_agent != nil) {
462 [self spawnMappedFloatingAgent:mapped_agent
463 entity:(__bridge NSString *)(match_domain)
464 agentSubType:kAgentSubTypeSupplemental
465 addPolicyOfType:NEPolicyConditionTypeDomain
466 updateData:data];
467 } else {
468 [self spawnFloatingAgent:[ProxyAgent class]
469 entity:(__bridge NSString *)(match_domain)
470 agentSubType:kAgentSubTypeSupplemental
471 addPolicyOfType:NEPolicyConditionTypeDomain
472 publishData:data];
473 }
474 }
475
476 [new_domain_list removeObjectAtIndex:found];
477 [duplicate_domain_list addObject:(__bridge id _Nonnull)(match_domain)];
478 }
479 }
480
481 return;
482 }
483
484 - (void)processScopedProxyChanges:(CFDictionaryRef)proxies
485 {
486 NSMutableArray * old_intf_list;
487 CFDictionaryRef scoped_proxies;
488 CFIndex scoped_proxies_count;
489
490 old_intf_list = [self getAgentList:self.floatingProxyAgentList
491 agentType:kAgentTypeProxy
492 agentSubType:kAgentSubTypeScoped];
493
494 scoped_proxies = CFDictionaryGetValue(proxies, kSCPropNetProxiesScoped);
495 scoped_proxies_count = scoped_proxies ? CFDictionaryGetCount(scoped_proxies) : 0;
496
497 if (scoped_proxies_count > 0) {
498 const void **keys;
499
500 keys = malloc(scoped_proxies_count * sizeof(void *));
501 CFDictionaryGetKeysAndValues(scoped_proxies, keys, NULL);
502
503 for (int i = 0; i < scoped_proxies_count; i++) {
504 NSData * data = nil;
505 NSUInteger idx;
506 CFArrayRef matching;
507 NSString * ns_if_name;
508 NSString * ns_if_name_with_prefix;
509 int proxy_count = 0;
510 id proxyAgent;
511
512 ns_if_name = (__bridge NSString *)keys[i];
513 ns_if_name_with_prefix = [NSString stringWithFormat:@"%s%@", prefixForInterfaceName, ns_if_name];
514
515 /* Does the proxy config have any protocols enabled? */
516 proxy_count = [self countProxyEntriesEnabled:CFDictionaryGetValue(scoped_proxies,
517 (__bridge const void *)(ns_if_name))];
518
519 if (proxy_count == 0) {
520 SC_log(LOG_INFO, "Proxy settings on %@ are generic. Skipping", ns_if_name);
521 continue;
522 }
523
524 idx = [old_intf_list indexOfObject:ns_if_name_with_prefix];
525
526 matching = SCNetworkProxiesCopyMatching(proxies, NULL, (__bridge CFStringRef)(ns_if_name));
527 if (matching != NULL) {
528 data = [self dataForProxyArray:matching];
529 CFRelease(matching);
530 }
531
532 if (idx == NSNotFound) {
533 /* We need to spawn an agent */
534 [self spawnFloatingAgent:[ProxyAgent class]
535 entity:ns_if_name_with_prefix
536 agentSubType:kAgentSubTypeScoped
537 addPolicyOfType:NEPolicyConditionTypeScopedInterface
538 publishData:data];
539
540 continue;
541 } else {
542 /* We have an agent for this interface. Update it */
543 [old_intf_list removeObjectAtIndex:idx];
544 }
545
546 proxyAgent = [self.floatingProxyAgentList objectForKey:ns_if_name_with_prefix];
547 if (proxyAgent != nil) {
548 /* Do we need to update this agent? */
549 [proxyAgent updateAgentData:data];
550 if ([proxyAgent shouldUpdateAgent]) {
551 [self publishToAgent:proxyAgent];
552 }
553 }
554 }
555
556 free(keys);
557 }
558
559 [self deleteAgentList:self.floatingProxyAgentList list:old_intf_list];
560 }
561
562 - (void)processServiceSpecificProxyChanges:(CFDictionaryRef)proxies
563 {
564 NSMutableArray * old_service_list;
565 CFDictionaryRef service_proxies;
566 CFIndex service_proxies_count;
567
568 old_service_list = [self getAgentList:self.floatingProxyAgentList
569 agentType:kAgentTypeProxy
570 agentSubType:kAgentSubTypeServiceSpecific];
571
572 service_proxies = CFDictionaryGetValue(proxies, kSCPropNetProxiesServices);
573 service_proxies_count = service_proxies ? CFDictionaryGetCount(service_proxies) : 0;
574
575 if (service_proxies_count > 0) {
576 const void **keys;
577
578 keys = malloc(service_proxies_count * sizeof(void *));
579 CFDictionaryGetKeysAndValues(service_proxies, keys, NULL);
580
581 for (int i = 0; i < service_proxies_count; i++) {
582 NSData * data = nil;
583 NSUInteger idx;
584 NSString * ns_service_identifier = nil;
585 NSString * ns_service_with_prefix = nil;
586 int proxy_count = 0;
587 id proxyAgent;
588 CFDictionaryRef proxyDict = NULL;
589
590 ns_service_identifier = (__bridge NSString *)keys[i];
591 ns_service_with_prefix = [NSString stringWithFormat:@"%s%@", prefixForInterfaceName, ns_service_identifier];
592
593 /* Does the proxy config have any protocols enabled? */
594 proxy_count = [self countProxyEntriesEnabled:CFDictionaryGetValue(service_proxies,
595 (__bridge const void *)(ns_service_identifier))];
596
597 if (proxy_count == 0) {
598 SC_log(LOG_INFO, "Proxy settings on %@ are generic. Skipping", ns_service_identifier);
599 continue;
600 }
601
602 proxyDict = CFDictionaryGetValue(service_proxies, (__bridge CFStringRef)ns_service_identifier);
603 if (proxyDict != nil) {
604 data = [self dataForProxyArray:(__bridge CFArrayRef)(@[ (__bridge NSDictionary *)proxyDict ])];
605 }
606
607 idx = [old_service_list indexOfObject:ns_service_with_prefix];
608 if (idx == NSNotFound) {
609 /* We need to spawn an agent */
610 [self spawnFloatingAgent:[ProxyAgent class]
611 entity:ns_service_with_prefix
612 agentSubType:kAgentSubTypeServiceSpecific
613 addPolicyOfType:(POLICY_TYPE_NO_POLICY) /* Don't install a policy */
614 publishData:data];
615
616 continue;
617 } else {
618 /* We have an agent for this service. Update it */
619 [old_service_list removeObjectAtIndex:idx];
620 }
621
622 proxyAgent = [self.floatingProxyAgentList objectForKey:ns_service_with_prefix];
623 if (proxyAgent != nil) {
624 /* Do we need to update this agent? */
625 [proxyAgent updateAgentData:data];
626 if ([proxyAgent shouldUpdateAgent]) {
627 [self publishToAgent:proxyAgent];
628 }
629 }
630 }
631
632 free(keys);
633 }
634
635 [self deleteAgentList:self.floatingProxyAgentList list:old_service_list];
636 }
637
638 - (BOOL)isGlobalProxy:(CFDictionaryRef)proxies
639 {
640 if (CFDictionaryContainsKey(proxies, kSCPropNetProxiesBypassAllowed)) {
641 /*
642 * Since we did not ask to "bypass" the proxies, this key will always
643 * be present in a managed (global) proxy configuration
644 */
645 return YES;
646 }
647
648 return NO;
649 }
650
651 - (void)processDefaultProxyChanges:(CFDictionaryRef)proxies
652 {
653 CFArrayRef global_proxy;
654 CFIndex global_proxy_count;
655 CFMutableDictionaryRef proxies_copy;
656
657 proxies_copy = CFDictionaryCreateMutableCopy(NULL, 0, proxies);
658 CFDictionaryRemoveValue(proxies_copy, kSCPropNetProxiesScoped);
659 CFDictionaryRemoveValue(proxies_copy, kSCPropNetProxiesServices);
660 CFDictionaryRemoveValue(proxies_copy, kSCPropNetProxiesSupplemental);
661
662 global_proxy = CFArrayCreate(NULL, (const void **)&proxies_copy, 1, &kCFTypeArrayCallBacks);
663 global_proxy_count = CFArrayGetCount(global_proxy);
664 if (global_proxy_count > 0 &&
665 [self countProxyEntriesEnabled:proxies_copy] == 0) {
666 SC_log(LOG_INFO, "Proxy settings on defaultProxy are generic. Skipping");
667 global_proxy_count = 0;
668 }
669 CFRelease(proxies_copy);
670
671 if (global_proxy_count > 0) {
672 BOOL spawnAgent = YES;
673 id proxyAgent;
674 NSData * data;
675
676 data = [self dataForProxyArray:global_proxy];
677 proxyAgent = [self.floatingProxyAgentList objectForKey:@proxyAgentDefault];
678 if (proxyAgent != nil) {
679 if (![data isEqual:[proxyAgent getAgentData]]) {
680 [self destroyFloatingAgent:proxyAgent];
681 } else {
682 spawnAgent = NO;
683 }
684 }
685
686 if (spawnAgent) {
687 AgentSubType subtype = kAgentSubTypeDefault;
688 NEPolicyConditionType conditionType = NEPolicyConditionTypeNone;
689 if ([self isGlobalProxy:proxies_copy]) {
690 SC_log(LOG_INFO, "Global proxy detected...");
691 conditionType = NEPolicyConditionTypeAllInterfaces;
692 subtype = kAgentSubTypeGlobal;
693 }
694
695 [self spawnFloatingAgent:[ProxyAgent class]
696 entity:@proxyAgentDefault
697 agentSubType:subtype
698 addPolicyOfType:conditionType
699 publishData:data];
700 }
701 } else {
702 /* No default proxy config OR generic (no protocols enabled) default proxy config.
703 * Destroy the default agent if we had one
704 */
705 id proxyAgent;
706
707 proxyAgent = [self.floatingProxyAgentList objectForKey:@proxyAgentDefault];
708 if (proxyAgent != nil) {
709 [self destroyFloatingAgent:proxyAgent];
710 }
711 }
712
713 CFRelease(global_proxy);
714 }
715
716 - (void)processProxyChanges
717 {
718 CFDictionaryRef proxies;
719
720 proxies = SCDynamicStoreCopyProxiesWithOptions(NULL, NULL);
721 if (proxies == NULL) {
722 SC_log(LOG_INFO, "No proxy information");
723
724 NSMutableDictionary *copy = [self.floatingProxyAgentList copy];
725 for (NSString *entity in copy) {
726 id agent = [copy objectForKey:entity];
727 [self destroyFloatingAgent:agent];
728 }
729
730 return;
731 }
732
733 [self processDefaultProxyChanges:proxies];
734 [self processScopedProxyChanges:proxies];
735 [self processSupplementalProxyChanges:proxies];
736 [self processServiceSpecificProxyChanges:proxies];
737
738 CFRelease(proxies);
739 }
740
741 /* ========================== DNS agent helpers =========================== */
742 #pragma mark DNS agent helper functions
743
744 - (void)freeResolverList:(resolver_list_t *)resolvers
745 {
746 /* This is a shallow free of resolver_list_t only.
747 * The actual resolver pointers are owned by 'dns_config'
748 */
749 if (resolvers == NULL) {
750 return;
751 }
752
753 if (resolvers->default_resolvers != NULL) {
754 free(resolvers->default_resolvers);
755 }
756 if (resolvers->multicast_resolvers != NULL) {
757 free(resolvers->multicast_resolvers);
758 }
759 if (resolvers->private_resolvers != NULL) {
760 free(resolvers->private_resolvers);
761 }
762
763 free(resolvers);
764 }
765
766 - (resolver_list_t *)copyResolverList:(dns_config_t *)dns_config
767 {
768 resolver_list_t *resolvers = NULL;
769
770 if ((dns_config->n_resolver > 0) && (dns_config->resolver != NULL)) {
771 uint32_t a = 0;
772 uint32_t b = 0;
773 uint32_t c = 0;
774
775 resolvers = calloc(1, sizeof(resolver_list_t));
776 for (int i = 0; i < dns_config->n_resolver; i++) {
777 dns_resolver_t *r = dns_config->resolver[i];
778
779 if ([self isResolverMulticast:r]) {
780 resolvers->n_multicast_resolvers++;
781 continue;
782
783 } else if ([self isResolverPrivate:r]) {
784 resolvers->n_private_resolvers++;
785 continue;
786 }
787
788 // do not consider default resolvers with no nameservers
789 if (r->domain == NULL && r->n_nameserver > 0) {
790 resolvers->n_default_resolvers++;
791 }
792 }
793
794 SC_log(LOG_INFO, "Resolvers: %d default, %d multicast, %d private",
795 resolvers->n_default_resolvers,
796 resolvers->n_multicast_resolvers,
797 resolvers->n_private_resolvers);
798
799 if (resolvers->n_default_resolvers > 0) {
800 resolvers->default_resolvers = calloc(resolvers->n_default_resolvers,
801 sizeof(dns_resolver_t *));
802 }
803 if (resolvers->n_multicast_resolvers > 0) {
804 resolvers->multicast_resolvers = calloc(resolvers->n_multicast_resolvers,
805 sizeof(dns_resolver_t *));
806 }
807 if (resolvers->n_private_resolvers > 0) {
808 resolvers->private_resolvers = calloc(resolvers->n_private_resolvers,
809 sizeof(dns_resolver_t *));
810 }
811
812 for (int i = 0; i < dns_config->n_resolver; i++) {
813 dns_resolver_t *r = dns_config->resolver[i];
814
815 if ([self isResolverMulticast:r] &&
816 (a < resolvers->n_multicast_resolvers)) {
817 resolvers->multicast_resolvers[a++] = r;
818 continue;
819
820 } else if ([self isResolverPrivate:r] &&
821 (b < resolvers->n_private_resolvers)) {
822 resolvers->private_resolvers[b++] = r;
823 continue;
824 }
825
826 if ((r->domain == NULL) &&
827 (r->n_nameserver > 0) &&
828 (c < resolvers->n_default_resolvers)) {
829 resolvers->default_resolvers[c++] = r;
830 }
831 }
832 }
833
834 return resolvers;
835 }
836
837 /*
838 * Generate a data blob for the resolver.
839 * Currently the blob only has:
840 * - nameserver count
841 * - sockaddr structs for each nameserver
842 * - ifindex
843 */
844
845 - (NSData *)dataForResolver:(dns_resolver_t *)resolver
846 {
847 NSData * data = nil;
848 CFMutableDictionaryRef resolverDict = nil;
849
850 if (resolver == NULL) {
851 SC_log(LOG_NOTICE, "Invalid dns resolver");
852 return nil;
853 }
854
855 if (resolver->n_search > 0) {
856 if (resolverDict == nil) {
857 resolverDict = CFDictionaryCreateMutable(NULL,
858 0,
859 &kCFTypeDictionaryKeyCallBacks,
860 &kCFTypeDictionaryValueCallBacks);
861 }
862
863 CFMutableArrayRef searchDomainArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
864
865 /* Append search domains */
866 for (int i = 0; i < resolver->n_search; i++) {
867 CFArrayAppendValue(searchDomainArray, (__bridge CFStringRef)(@(resolver->search[i])));
868 }
869
870 CFDictionaryAddValue(resolverDict, CFSTR(kConfigAgentDNSSearchDomains), searchDomainArray);
871 CFRelease(searchDomainArray);
872 }
873
874 /* Get the count of nameservers */
875 if (resolver->n_nameserver > 0) {
876 if (resolverDict == nil) {
877 resolverDict = CFDictionaryCreateMutable(NULL,
878 0,
879 &kCFTypeDictionaryKeyCallBacks,
880 &kCFTypeDictionaryValueCallBacks);
881 }
882
883 CFMutableArrayRef nameserverArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
884
885 /* Get all the nameservers */
886 for (int i = 0; i < resolver->n_nameserver; i++) {
887 char buf[128] = {0};
888 _SC_sockaddr_to_string(resolver->nameserver[i], buf, sizeof(buf));
889 if (*buf != '\0') {
890 CFArrayAppendValue(nameserverArray, (__bridge CFStringRef)(@(buf)));
891 }
892 }
893
894 CFDictionaryAddValue(resolverDict, CFSTR(kConfigAgentDNSNameServers), nameserverArray);
895 CFRelease(nameserverArray);
896 }
897
898 if (resolverDict != nil) {
899 data = [NSPropertyListSerialization dataWithPropertyList:(__bridge id _Nonnull)(resolverDict)
900 format:NSPropertyListBinaryFormat_v1_0
901 options:0
902 error:nil];
903
904 CFRelease(resolverDict);
905 }
906
907 return (NSData *)data;
908 }
909
910 - (NSData *)getDNSDataFromCurrentConfig:(dns_config_t *)dns_config
911 domain:(NSString *)domain
912 {
913 if (dns_config == NULL || domain == nil) {
914 SC_log(LOG_NOTICE, "Invalid dns_config/domain");
915 return nil;
916 }
917
918 if ((dns_config->n_resolver > 0) && (dns_config->resolver != NULL)) {
919 for (int i = 0; i < dns_config->n_resolver; i++) {
920 dns_resolver_t * resolver;
921
922 resolver = dns_config->resolver[i];
923 if (resolver->domain != NULL &&
924 ![self isResolverMulticast:resolver]) {
925 NSString * ns_domain_name;
926
927 ns_domain_name = @(resolver->domain);
928 if ([ns_domain_name isEqualToString:domain]) {
929 return [self dataForResolver:resolver];
930 } else {
931 continue;
932 }
933 }
934 }
935 }
936
937 return nil;
938 }
939
940 - (BOOL)isResolverMulticast:(dns_resolver_t *)resolver
941 {
942 if (resolver->options == NULL) {
943 return NO;
944 }
945
946 if (!strstr(resolver->options, "mdns")) {
947 return NO;
948 }
949
950 return YES;
951 }
952
953 - (BOOL)isResolverPrivate:(dns_resolver_t *)resolver
954 {
955 if (resolver->options == NULL) {
956 return NO;
957 }
958
959 if (!strstr(resolver->options, "pdns")) {
960 return NO;
961 }
962
963 return YES;
964 }
965
966 - (void)processSupplementalDNSResolvers:(dns_config_t *)dns_config
967 {
968 NSMutableArray * deleteList;
969 NSMutableArray * new_domain_list;
970 NSCountedSet * duplicate_domain_list;
971 NSMutableArray * old_domain_list;
972 NSMutableArray * update_agent_list;
973
974
975 deleteList = [NSMutableArray array];
976 duplicate_domain_list = [[NSCountedSet alloc] initWithCapacity:0];
977 new_domain_list = [NSMutableArray array];
978 update_agent_list = [NSMutableArray array];
979 old_domain_list = [self getAgentList:self.floatingDNSAgentList
980 agentType:kAgentTypeDNS
981 agentSubType:kAgentSubTypeSupplemental];
982
983 if (dns_config->resolver == NULL) {
984 dns_config->n_resolver = 0;
985 }
986 if (dns_config->n_resolver > 0) {
987 for (int i = 0; i < dns_config->n_resolver; i++) {
988 dns_resolver_t * resolver;
989
990 resolver = dns_config->resolver[i];
991 if (resolver->domain != NULL &&
992 ![self isResolverPrivate:resolver] &&
993 ![self isResolverMulticast:resolver]) {
994 NSString * ns_domain_name;
995
996 ns_domain_name = [NSString stringWithCString:resolver->domain encoding:NSASCIIStringEncoding];
997 [new_domain_list addObject:ns_domain_name];
998 }
999 }
1000 }
1001
1002 [self cleanConflictingAgentsFromList:old_domain_list
1003 new_list:new_domain_list
1004 agentDictionary:self.floatingDNSAgentList];
1005
1006 /* Sync between controller and current config */
1007 for (NSString *key in old_domain_list) {
1008 BOOL domain_present = NO;
1009
1010 domain_present = [new_domain_list containsObject:key];
1011 if (domain_present == NO) {
1012 id agent;
1013
1014 agent = [self.floatingDNSAgentList objectForKey:key];
1015 [self destroyFloatingAgent:agent];
1016 }
1017 }
1018
1019 /* At this point, whatever is in the controller's floating agent list,
1020 is present in the current DNS config. The current DNS config
1021 might have even more configs, not known to the controller, YET
1022 */
1023
1024 for (NSString *domain in old_domain_list) {
1025 id agent;
1026 id mapped_agent;
1027
1028 agent = [self.floatingDNSAgentList objectForKey:domain];
1029 if (agent == nil) {
1030 continue;
1031 }
1032
1033 /* Am I mapped to some agent? */
1034 mapped_agent = [agent getAgentMapping];
1035 if (mapped_agent) {
1036 /* OK, this agent is mapped to some other agent. We compare this agent's data
1037 * to the current data of the agent to which it is mapped. If different, we destroy
1038 * the agent and later map it to someone else OR spawn a new one.
1039 */
1040 NSData *mapped_agent_data;
1041
1042 mapped_agent_data = [self getDNSDataFromCurrentConfig:dns_config domain:[mapped_agent getAssociatedEntity]];
1043 if (mapped_agent_data == nil || ![[agent getAgentData] isEqual:mapped_agent_data]) {
1044 /* Something changed for mapped agent */
1045 [deleteList addObject:agent];
1046 continue;
1047 }
1048 } else {
1049 /* Since this agent is NOT mapped to any other agent, this agent is
1050 * registered with the kernel. So instead of destroying the agent and
1051 * re-registering it, just update it here.
1052 *
1053 * All the agents which were mapped to this agent, will be deleted and
1054 * re-mapped, if the data changed.
1055 */
1056 NSData *agent_data;
1057
1058 agent_data = [self getDNSDataFromCurrentConfig:dns_config domain:[agent getAssociatedEntity]];
1059 if (![[agent getAgentData] isEqual:agent_data]) {
1060 /* Something changed for agent */
1061 [agent updateAgentData:agent_data];
1062
1063 /* The reason I don't publish the data to agent here is that, if there were
1064 * some agents mapping to this one, they will momentarily have a policy for
1065 * using this agent UUID for some domain based on this agent's previous data.
1066 */
1067 [update_agent_list addObject:agent];
1068
1069 }
1070 }
1071 [new_domain_list removeObject:domain];
1072 }
1073
1074 for (id agent in deleteList) {
1075 SC_log(LOG_INFO, "Destroying agent %@ because something changed!", [agent getAgentName]);
1076 [self destroyFloatingAgent:agent];
1077 }
1078
1079 for (id agent in update_agent_list) {
1080 [self publishToAgent:agent];
1081 }
1082
1083 for (int idx = 0; idx < dns_config->n_resolver; idx++) {
1084 dns_resolver_t * resolver;
1085
1086 resolver = dns_config->resolver[idx];
1087 if (resolver->domain != NULL &&
1088 ![self isResolverPrivate:resolver] &&
1089 ![self isResolverMulticast:resolver]) {
1090 NSData * data;
1091 NSUInteger found;
1092 id mapped_agent;
1093 NSString * ns_domain_name;
1094
1095 ns_domain_name = @(resolver->domain);
1096 found = [new_domain_list indexOfObject:ns_domain_name];
1097 if (found == NSNotFound) {
1098 /* Nothing changed for this agent */
1099 continue;
1100 }
1101
1102 /* We will only process agents which are mapped AND if the agent they were mapped to, changed OR
1103 * agents for domains which we did not know before.
1104 */
1105
1106 NSUInteger domainInstance = [duplicate_domain_list countForObject:ns_domain_name];
1107 if (domainInstance > 0) {
1108 /* domainInstance will be > 0, only if we have conflicting domains */
1109 domainInstance++;
1110 data = [self dataForResolver:resolver];
1111
1112 NSString *ns_domain_name_copy = [NSString stringWithFormat:@"%@" multipleEntitySuffix "%lu", ns_domain_name, (unsigned long)domainInstance];
1113
1114 BOOL ok = [self spawnFloatingAgent:[DNSAgent class]
1115 entity:ns_domain_name_copy
1116 agentSubType:kAgentSubTypeSupplemental
1117 addPolicyOfType:NEPolicyConditionTypeDomain
1118 publishData:data];
1119 if (ok) {
1120 id agent = [self.floatingDNSAgentList objectForKey:ns_domain_name_copy];
1121 SC_log(LOG_INFO, "Duplicate DNS agent %@", [agent getAgentName]);;
1122 }
1123 } else {
1124 data = [self dataForResolver:resolver];
1125 mapped_agent = [self getAgentWithSameDataAndSubType:self.floatingDNSAgentList
1126 data:data
1127 subType:kAgentSubTypeSupplemental];
1128 if (mapped_agent != nil) {
1129 [self spawnMappedFloatingAgent:mapped_agent
1130 entity:ns_domain_name
1131 agentSubType:kAgentSubTypeSupplemental
1132 addPolicyOfType:NEPolicyConditionTypeDomain
1133 updateData:data];
1134 } else {
1135 [self spawnFloatingAgent:[DNSAgent class]
1136 entity:ns_domain_name
1137 agentSubType:kAgentSubTypeSupplemental
1138 addPolicyOfType:NEPolicyConditionTypeDomain
1139 publishData:data];
1140 }
1141 }
1142
1143 [new_domain_list removeObjectAtIndex:found];
1144 [duplicate_domain_list addObject:ns_domain_name];
1145 }
1146 }
1147
1148 return;
1149
1150 }
1151
1152 - (void)processDNSResolvers:(dns_config_t *)dns_config
1153 {
1154 resolver_list_t *resolvers = [self copyResolverList:dns_config];
1155 if (resolvers) {
1156 /* Process Default resolvers */
1157 NSMutableArray *old_default_resolver_list = [self getAgentList:self.floatingDNSAgentList
1158 agentType:kAgentTypeDNS
1159 agentSubType:kAgentSubTypeDefault];
1160
1161 // For default resolvers, their name will be '_defaultDNS', '_defaultDNS #2' so on...
1162 if (resolvers->n_default_resolvers > 0 && resolvers->default_resolvers != NULL) {
1163 for (uint32_t i = 0; i < resolvers->n_default_resolvers; i++) {
1164 dns_resolver_t *default_resolver = resolvers->default_resolvers[i];
1165 NSData * data;
1166 id dnsAgent;
1167 NSString * resolverName;
1168
1169 data = [self dataForResolver:default_resolver];
1170 if (i == 0) {
1171 resolverName = @(dnsAgentDefault);
1172 } else {
1173 resolverName = [NSString stringWithFormat:@dnsAgentDefault multipleEntitySuffix "%d", i+1 ];
1174 }
1175
1176 dnsAgent = [self.floatingDNSAgentList objectForKey:resolverName];
1177
1178 if (dnsAgent != nil) {
1179 [old_default_resolver_list removeObject:resolverName];
1180 if ([data isEqual:[dnsAgent getAgentData]]) {
1181 /* Leave this agent in place. Nothing changed! */
1182 continue;
1183 } else {
1184 [self destroyFloatingAgent:dnsAgent];
1185 }
1186 }
1187
1188 [self spawnFloatingAgent:[DNSAgent class]
1189 entity:resolverName
1190 agentSubType:kAgentSubTypeDefault
1191 addPolicyOfType:NEPolicyConditionTypeNone
1192 publishData:data];
1193 }
1194 }
1195
1196 // Only agents that are NOT present in the new config, will be present in the list
1197 // and they need to be destroyed.
1198 [self deleteAgentList:self.floatingDNSAgentList list:old_default_resolver_list];
1199
1200 /* Process Multicast resolvers */
1201
1202 NSMutableArray *old_multicast_resolver_list = [self getAgentList:self.floatingDNSAgentList
1203 agentType:kAgentTypeDNS
1204 agentSubType:kAgentSubTypeMulticast];
1205
1206 if (resolvers->n_multicast_resolvers > 0 && resolvers->multicast_resolvers != NULL) {
1207 for (uint32_t i = 0; i < resolvers->n_multicast_resolvers; i++) {
1208 dns_resolver_t * multicast_resolver = resolvers->multicast_resolvers[i];
1209 id dnsAgent;
1210 NSString * resolverName;
1211
1212 if (multicast_resolver == NULL) {
1213 continue;
1214 }
1215
1216 if (multicast_resolver->domain == NULL) {
1217 /* Multicast resolvers MUST have a domain */
1218 continue;
1219 }
1220
1221 resolverName = @(multicast_resolver->domain);
1222 if (resolverName == NULL) {
1223 /* Multicast resolvers MUST have a domain */
1224 continue;
1225 }
1226
1227 dnsAgent = [self.floatingDNSAgentList objectForKey:resolverName];
1228 if (dnsAgent != nil) {
1229 [old_multicast_resolver_list removeObject:resolverName];
1230 continue;
1231 }
1232
1233 [self spawnFloatingAgent:[DNSAgent class]
1234 entity:resolverName
1235 agentSubType:kAgentSubTypeMulticast
1236 addPolicyOfType:NEPolicyConditionTypeDomain
1237 publishData:nil];
1238 // Don't care about data for mdns resolvers. Do we?
1239 }
1240 }
1241
1242 [self deleteAgentList:self.floatingDNSAgentList list:old_multicast_resolver_list];
1243
1244 /* Process Private resolvers */
1245
1246 NSMutableArray *old_private_resolver_list = [self getAgentList:self.floatingDNSAgentList
1247 agentType:kAgentTypeDNS
1248 agentSubType:kAgentSubTypePrivate];
1249
1250 if (resolvers->n_private_resolvers > 0 && resolvers->private_resolvers != NULL) {
1251 for (uint32_t i = 0; i < resolvers->n_private_resolvers; i++) {
1252 dns_resolver_t * private_resolver = resolvers->private_resolvers[i];
1253 id dnsAgent;
1254 NSString * resolverName;
1255
1256 if (private_resolver == NULL) {
1257 continue;
1258 }
1259
1260 if (private_resolver->domain == NULL) {
1261 /* private resolvers MUST have a domain */
1262 continue;
1263 }
1264
1265 resolverName = @(private_resolver->domain);
1266 if (resolverName == nil) {
1267 /* Private resolvers MUST have a domain */
1268 continue;
1269 }
1270
1271 dnsAgent = [self.floatingDNSAgentList objectForKey:resolverName];
1272 if (dnsAgent != nil) {
1273 [old_private_resolver_list removeObject:resolverName];
1274 continue;
1275 }
1276
1277 [self spawnFloatingAgent:[DNSAgent class]
1278 entity:resolverName
1279 agentSubType:kAgentSubTypePrivate
1280 addPolicyOfType:NEPolicyConditionTypeDomain
1281 publishData:nil];
1282 // Don't care about data for pdns resolvers. Do we?
1283 }
1284 }
1285
1286 [self deleteAgentList:self.floatingDNSAgentList list:old_private_resolver_list];
1287 }
1288
1289 [self freeResolverList:resolvers];
1290 }
1291
1292 - (void)processScopedDNSResolvers:(dns_config_t *)dns_config
1293 {
1294 NSMutableArray * old_intf_list;
1295 old_intf_list = [self getAgentList:self.floatingDNSAgentList
1296 agentType:kAgentTypeDNS
1297 agentSubType:kAgentSubTypeScoped];
1298
1299 if ((dns_config->n_scoped_resolver > 0) && (dns_config->scoped_resolver != NULL)) {
1300 for (int i = 0; i < dns_config->n_scoped_resolver; i++) {
1301 char buf[IFNAMSIZ];
1302 NSData * data;
1303 id dnsAgent;
1304 NSUInteger idx;
1305 const char * if_name;
1306 NSString * ns_if_name;
1307 NSString * ns_if_name_with_prefix;
1308 dns_resolver_t * resolver;
1309
1310 resolver = dns_config->scoped_resolver[i];
1311 if_name = my_if_indextoname(resolver->if_index, buf);
1312 if (if_name) {
1313 ns_if_name = @(if_name);
1314 ns_if_name_with_prefix = [NSString stringWithFormat:@"%s%@", prefixForInterfaceName, ns_if_name];
1315 } else {
1316 continue;
1317 }
1318
1319 data = [self dataForResolver:resolver];
1320 idx = [old_intf_list indexOfObject:ns_if_name_with_prefix];
1321
1322 if (idx == NSNotFound) {
1323 /* We need to spawn an agent */
1324 [self spawnFloatingAgent:[DNSAgent class]
1325 entity:ns_if_name_with_prefix
1326 agentSubType:kAgentSubTypeScoped
1327 addPolicyOfType:NEPolicyConditionTypeScopedInterface
1328 publishData:data];
1329 continue;
1330 } else {
1331 /* We have an agent on this interface. Update it */
1332 [old_intf_list removeObjectAtIndex:idx];
1333 }
1334
1335 /* Get the DNS agent for this interface? */
1336 dnsAgent = [self.floatingDNSAgentList objectForKey:ns_if_name_with_prefix];
1337 if (dnsAgent != nil) {
1338 /* Do we need to update this agent? */
1339 [dnsAgent updateAgentData:data];
1340 if ([dnsAgent shouldUpdateAgent]) {
1341 [self publishToAgent:dnsAgent];
1342 }
1343 }
1344 }
1345 }
1346
1347 [self deleteAgentList:self.floatingDNSAgentList list:old_intf_list];
1348 }
1349
1350 - (void)processServiceSpecificDNSResolvers:(dns_config_t *)dns_config
1351 {
1352 NSMutableArray * old_service_list;
1353 old_service_list = [self getAgentList:self.floatingDNSAgentList
1354 agentType:kAgentTypeDNS
1355 agentSubType:kAgentSubTypeServiceSpecific];
1356
1357 if ((dns_config->n_service_specific_resolver > 0) && (dns_config->service_specific_resolver != NULL)) {
1358 for (int i = 0; i < dns_config->n_service_specific_resolver; i++) {
1359 NSData * data;
1360 id dnsAgent;
1361 NSUInteger idx;
1362 uint32_t service_identifier;
1363 NSString * ns_service_identifier_with_prefix;
1364 dns_resolver_t * resolver;
1365
1366 resolver = dns_config->service_specific_resolver[i];
1367 service_identifier = resolver->service_identifier;
1368 if (service_identifier != 0) {
1369 ns_service_identifier_with_prefix = [NSString stringWithFormat:@"%s%u", prefixForInterfaceName, service_identifier];
1370 } else {
1371 continue;
1372 }
1373
1374 data = [self dataForResolver:resolver];
1375 idx = [old_service_list indexOfObject:ns_service_identifier_with_prefix];
1376
1377 if (idx == NSNotFound) {
1378 /* We need to spawn an agent */
1379 [self spawnFloatingAgent:[DNSAgent class]
1380 entity:ns_service_identifier_with_prefix
1381 agentSubType:kAgentSubTypeServiceSpecific
1382 addPolicyOfType:(POLICY_TYPE_NO_POLICY) /* Don't install a policy */
1383 publishData:data];
1384 continue;
1385 } else {
1386 /* We have an agent on this interface. Update it */
1387 [old_service_list removeObjectAtIndex:idx];
1388 }
1389
1390 /* Get the DNS agent for this interface? */
1391 dnsAgent = [self.floatingDNSAgentList objectForKey:ns_service_identifier_with_prefix];
1392 if (dnsAgent != nil) {
1393 /* Do we need to update this agent? */
1394 [dnsAgent updateAgentData:data];
1395 if ([dnsAgent shouldUpdateAgent]) {
1396 [self publishToAgent:dnsAgent];
1397 }
1398 }
1399 }
1400 }
1401
1402 [self deleteAgentList:self.floatingDNSAgentList list:old_service_list];
1403 }
1404
1405 #define ONION_RESOLVER_DOMAIN "onion"
1406 - (BOOL)isResolverOnion:(dns_resolver_t *)resolver
1407 {
1408 if (resolver->domain != NULL &&
1409 (strcmp(resolver->domain, ONION_RESOLVER_DOMAIN) == 0)) {
1410 return YES;
1411 }
1412
1413 return NO;
1414 }
1415
1416
1417 - (void)processOnionResolver:(dns_config_t *)dns_config
1418 {
1419 static NSUInteger policy_id = 0;
1420
1421 if (dns_config == NULL) {
1422 goto remove_policy;
1423 }
1424
1425 /* Run through the resolver configurations. We only care for the supplemental resolvers. */
1426 for (int32_t i = 0; i < dns_config->n_resolver; i++) {
1427 dns_resolver_t *resolver = dns_config->resolver[i];
1428 if ([self isResolverOnion:resolver]) {
1429 goto remove_policy;
1430 }
1431 }
1432
1433 /* We do not have any such resolver. Add a system-wide "drop" policy for this domain */
1434 if (policy_id == 0) {
1435 NEPolicy *policy = [[NEPolicy alloc] initWithOrder:INIT_ORDER_FOR_DOMAIN_POLICY
1436 result:[NEPolicyResult drop]
1437 conditions:@[[NEPolicyCondition domain:@ONION_RESOLVER_DOMAIN]]];
1438 if (policy != nil) {
1439 policy_id = [self.policySession addPolicy:policy];
1440 if (![self.policySession apply]) {
1441 policy_id = 0;
1442 SC_log(LOG_NOTICE, "Could not add a [." ONION_RESOLVER_DOMAIN "] drop policy");
1443 } else {
1444 SC_log(LOG_INFO, "Added a [." ONION_RESOLVER_DOMAIN "] drop policy");
1445 }
1446 }
1447 }
1448
1449 return;
1450
1451 remove_policy:
1452
1453 /* We have such a resolver in the config OR no DNS config at all. Remove the system-wide "drop" policy for this domain */
1454 if (policy_id > 0) {
1455 [self.policySession removePolicyWithID:policy_id];
1456 if (![self.policySession apply]) {
1457 SC_log(LOG_NOTICE, "Could not remove the [." ONION_RESOLVER_DOMAIN "] drop policy");
1458 } else {
1459 policy_id = 0;
1460 SC_log(LOG_INFO, "Removed the [." ONION_RESOLVER_DOMAIN "] drop policy");
1461 }
1462 }
1463
1464 return;
1465 }
1466 #undef ONION_RESOLVER_DOMAIN
1467
1468
1469 - (void)processDNSChanges
1470 {
1471 dns_config_t * dns_config;
1472
1473 dns_config = dns_configuration_copy();
1474 if (dns_config == NULL) {
1475 SC_log(LOG_INFO, "No DNS configuration");
1476 NSMutableDictionary *copy = [self.floatingDNSAgentList copy];
1477 for (NSString *entity in copy) {
1478 id agent = [copy objectForKey:entity];
1479
1480 [self destroyFloatingAgent:agent];
1481 }
1482 goto done;
1483 }
1484
1485 [self processDNSResolvers:dns_config];
1486 [self processScopedDNSResolvers:dns_config];
1487 [self processSupplementalDNSResolvers:dns_config];
1488 [self processServiceSpecificDNSResolvers:dns_config];
1489
1490 done:
1491
1492 [self processOnionResolver:dns_config];
1493 if (dns_config != NULL) {
1494 dns_configuration_free(dns_config);
1495 }
1496 }
1497
1498 #pragma mark Helper functions
1499
1500 - (const void *)copyConfigAgentData:(NSMutableDictionary *)controllerDict
1501 uuid:(uuid_t)requested_uuid
1502 length:(uint64_t *)length
1503 {
1504 if (length == NULL) {
1505 SC_log(LOG_NOTICE, "Invalid parameters for copying agent data");
1506 return NULL;
1507 }
1508
1509 id agent = nil;
1510 void *buffer = NULL;
1511 *length = 0;
1512
1513 for (NSString *key in controllerDict) {
1514 id temp_agent = [controllerDict objectForKey:key];
1515
1516 uuid_t agent_uuid;
1517
1518 [[temp_agent getAgentUUID] getUUIDBytes:agent_uuid];
1519 if (uuid_compare(agent_uuid, requested_uuid) == 0) {
1520 agent = temp_agent;
1521 break;
1522 }
1523 }
1524
1525 if (agent == nil) {
1526 uuid_string_t uuid_str;
1527 uuid_unparse(requested_uuid, uuid_str);
1528 SC_log(LOG_NOTICE, "Invalid config agent uuid %s specified", uuid_str);
1529 return NULL;
1530 }
1531
1532 NSData *data = [agent getAgentData];
1533 uint64_t len = [data length];
1534 if (len > 0) {
1535 *length = len;
1536 buffer = malloc((size_t)len);
1537 memcpy(buffer, [data bytes], len);
1538 }
1539
1540 return (const void *)buffer;
1541 }
1542
1543 - (const void *)copyProxyAgentData:(uuid_t)requested_uuid
1544 length:(uint64_t *)length
1545 {
1546 return [self copyConfigAgentData:self.floatingProxyAgentList
1547 uuid:requested_uuid
1548 length:length];
1549 }
1550
1551 - (const void *)copyDNSAgentData:(uuid_t)requested_uuid
1552 length:(uint64_t *)length
1553 {
1554 return [self copyConfigAgentData:self.floatingDNSAgentList
1555 uuid:requested_uuid
1556 length:length];
1557 }
1558
1559 - (NSData *)dataLengthSanityCheck:(id)agent
1560 {
1561 NSData * data = [agent getAgentData];
1562
1563 if ([data length] > CONFIG_AGENT_DATA_LIMIT) {
1564 /* We impose a limit on the config agent data as 1KB.
1565 * If we have a data blob larger than this limit, do NOT publish it into the agent.
1566 * Instead publish a key which will trigger fetching of the configuration directly
1567 * through NWI server.
1568 */
1569 NSMutableDictionary *data_dict = [NSMutableDictionary dictionary];
1570
1571 NSUUID *uuid = [agent getAgentUUID];
1572 uuid_t c_uuid;
1573 [uuid getUUIDBytes:c_uuid];
1574 NSData *uuid_data = [[NSData alloc] initWithBytes:c_uuid length:sizeof(c_uuid)];
1575 [data_dict setValue:uuid_data forKey:@kConfigAgentOutOfBandDataUUID];
1576
1577 NSData *new_data = [NSPropertyListSerialization dataWithPropertyList:data_dict
1578 format:NSPropertyListBinaryFormat_v1_0
1579 options:0
1580 error:nil];
1581
1582 return new_data;
1583 }
1584
1585 return nil;
1586 }
1587
1588 /*
1589 * For conflicting agents, the convention is that its name & entity,
1590 * will have a suffix " #<number>". This function will sanitize the
1591 * suffix and just return the entity name
1592 */
1593 - (NSString *)sanitizeEntity:(NSString *)entity
1594 {
1595 NSRange range = [entity rangeOfString:@multipleEntitySuffix];
1596 if (range.location != NSNotFound) {
1597 NSString *str = [entity substringToIndex:range.location];
1598 return str;
1599 }
1600
1601 return entity;
1602 }
1603
1604 /*
1605 * For interface names, there is a prefix to differentiate then
1606 * from the domain name (iff there were conflicting domain names).
1607 * Returns the sanitized interface name
1608 */
1609 - (NSString *)sanitizeInterfaceName:(NSString *)intf
1610 {
1611 NSRange range = [intf rangeOfString:@prefixForInterfaceName];
1612 if (range.location != NSNotFound) {
1613 NSString *str = [intf substringFromIndex:(range.location + strlen(prefixForInterfaceName))];
1614 return str;
1615 }
1616
1617 return intf;
1618 }
1619
1620 /*
1621 * For conflicting agents, the convention is that its name & entity,
1622 * will have a suffix " #<number>". This function will return that <number>
1623 */
1624 - (int)entityInstanceNumber:(NSString *)entity
1625 {
1626 NSRange range = [entity rangeOfString:@multipleEntitySuffix];
1627 if (range.location != NSNotFound) {
1628 NSString *str = [entity substringFromIndex:(range.location + strlen(multipleEntitySuffix))];
1629 return str.intValue;
1630 }
1631
1632 return 0;
1633 }
1634
1635 /*
1636 * In case that we have conflicting DNS/Proxy domains
1637 * This function will remove all those conflicting agents,
1638 * so that we can start afresh with the new config
1639 */
1640 - (void)cleanConflictingAgentsFromList:(NSMutableArray *)old_list
1641 new_list:(NSMutableArray *)new_list
1642 agentDictionary:(NSMutableDictionary *)agent_list
1643 {
1644 NSCountedSet * duplicate_domain_list;
1645
1646 for (NSString *domain in old_list) {
1647 /* If we had conflicting domains before, remove all of them */
1648 NSString *sanitizedDomain = [self sanitizeEntity:domain];
1649 if (![sanitizedDomain isEqualToString:domain]) {
1650 /* Destroy the original domain */
1651 id agent = [agent_list objectForKey:sanitizedDomain];
1652 [self destroyFloatingAgent:agent];
1653
1654 /* Destroy the conflicting domain */
1655 agent = [agent_list objectForKey:domain];
1656 [self destroyFloatingAgent:agent];
1657
1658 SC_log(LOG_INFO, "Removing conflicting domain: %@, %@", sanitizedDomain, domain);
1659 }
1660 }
1661
1662 duplicate_domain_list = [[NSCountedSet alloc] initWithArray:new_list];
1663 for (NSString *domain in old_list) {
1664 if ([duplicate_domain_list countForObject:domain] > 1) {
1665 id agent = [agent_list objectForKey:domain];
1666 [self destroyFloatingAgent:agent];
1667 SC_log(LOG_INFO, "Removing domain %@ as it has duplicates in the current config", domain);
1668 }
1669 }
1670 }
1671
1672 /*
1673 * Get the list of agents from a specific dictionary.
1674 * The list of agents will only consist of the ones which
1675 * match the agent type and sub-type
1676 */
1677
1678 - (NSMutableArray *)getAgentList:(NSMutableDictionary *)all_agents
1679 agentType:(AgentType)type
1680 agentSubType:(AgentSubType)subtype
1681 {
1682 NSMutableArray *list = [NSMutableArray array];
1683 NSArray *agentObjects = [all_agents allValues];
1684
1685 for (id agent in agentObjects) {
1686 if (([agent getAgentType] == type) &&
1687 ([agent getAgentSubType] == subtype)) {
1688
1689 [list addObject:[agent getAssociatedEntity]];
1690 }
1691 }
1692
1693 return list;
1694 }
1695
1696 /*
1697 * Destroy all the agents are listed in "list"
1698 */
1699
1700 - (void)deleteAgentList:(NSMutableDictionary *)all_agents
1701 list:(NSMutableArray *)list
1702 {
1703 for (NSString *intf in list) {
1704 id agent;
1705
1706 agent = [all_agents objectForKey:intf];
1707 [self destroyFloatingAgent:agent];
1708 }
1709 }
1710
1711 /*
1712 * In order to not duplicate agents with same content,
1713 * we map an agent X to agent Y, when their content is the same.
1714 *
1715 * This function tries to find that agent Y
1716 */
1717
1718 - (id)getAgentWithSameDataAndSubType:(NSMutableDictionary *)agentList
1719 data:(NSData *)data
1720 subType:(AgentSubType)subtype
1721 {
1722 for (NSString *key in agentList) {
1723 id agent = [agentList objectForKey:key];
1724 if ([[agent getAgentData] isEqual:data]) {
1725 /* Do not map to default agents */
1726 if ([agent getAgentSubType] != subtype) {
1727 continue;
1728 }
1729
1730 /* Return only registered agents */
1731 if ([agent getRegistrationObject] != nil) {
1732 return agent;
1733 }
1734 }
1735 }
1736
1737 return nil;
1738 }
1739
1740 #pragma mark Policy installation function
1741
1742 /*
1743 * Add NECP policies for an agent
1744 */
1745 - (BOOL)addPolicyToFloatingAgent:(id)agent
1746 domain:(NSString *)domain
1747 agentUUIDToUse:(NSUUID *)uuid
1748 policyType:(NEPolicyConditionType)policyType
1749 useControlPolicySession:(BOOL)useControlPolicySession
1750 {
1751 NEPolicyCondition * condition = nil;
1752 NEPolicySession * session;
1753 uint32_t multiple_entity_offset;
1754 NEPolicy * newPolicy;
1755 BOOL ok;
1756 uint32_t order;
1757 uint32_t orderForSkip;
1758 NSMutableArray * policyArray;
1759 NSUInteger policyID1;
1760 NSUInteger policyID2;
1761 NEPolicyResult * result;
1762 uint32_t skipOrder;
1763 AgentType type;
1764 uint32_t typeOffset;
1765
1766 type = [agent getAgentType];
1767 typeOffset = (type == kAgentTypeDNS) ? 0 : 5000;
1768 skipOrder = (type == kAgentTypeDNS) ? 5000 : 0;
1769
1770 multiple_entity_offset = (uint32_t)[self entityInstanceNumber:domain];
1771 domain = [self sanitizeEntity:domain];
1772
1773 switch (policyType) {
1774 case NEPolicyConditionTypeScopedInterface:
1775 order = INIT_ORDER_FOR_SCOPED_INTERFACE_POLICY + typeOffset + multiple_entity_offset;
1776 domain = [self sanitizeInterfaceName:domain];
1777 condition = [NEPolicyCondition scopedInterface:domain];
1778 orderForSkip = SKIP_ORDER_FOR_SCOPED_INTERFACE_POLICY + typeOffset;
1779 break;
1780
1781 case NEPolicyConditionTypeDomain:
1782 order = INIT_ORDER_FOR_DOMAIN_POLICY + typeOffset + multiple_entity_offset;
1783 condition = [NEPolicyCondition domain:domain];
1784 orderForSkip = SKIP_ORDER_FOR_DOMAIN_POLICY + typeOffset;
1785 break;
1786
1787 case NEPolicyConditionTypeAllInterfaces:
1788 order = INIT_ORDER_FOR_DEFAULT_POLICY + typeOffset + multiple_entity_offset;
1789 condition = [NEPolicyCondition allInterfaces];
1790 orderForSkip = SKIP_ORDER_FOR_DEFAULT_POLICY + typeOffset;
1791 break;
1792
1793 case NEPolicyConditionTypeNone:
1794 order = INIT_ORDER_FOR_DEFAULT_POLICY + typeOffset + multiple_entity_offset;
1795 orderForSkip = SKIP_ORDER_FOR_DEFAULT_POLICY + typeOffset;
1796 break;
1797
1798 default:
1799 SC_log(LOG_NOTICE, "Invalid policy condition specified");
1800 return NO;
1801 }
1802
1803 result = [NEPolicyResult netAgentUUID:uuid];
1804 newPolicy = [[NEPolicy alloc] initWithOrder:order
1805 result:result
1806 conditions: (condition ? @[condition] : nil)];
1807
1808 if (newPolicy == nil) {
1809 SC_log(LOG_NOTICE, "Could not create a policy for agent %@", [agent getAgentName]);
1810 return NO;
1811 }
1812
1813 if (useControlPolicySession) {
1814 if (self.controlPolicySession == nil) {
1815 /* The NE policy session at "control" level for the controller */
1816 self.controlPolicySession = [self createPolicySession];
1817 if (self.controlPolicySession == nil) {
1818 SC_log(LOG_NOTICE, "Could not create a control policy session for agent %@", [agent getAgentName]);
1819 return NO;
1820 }
1821 [self.controlPolicySession setPriority:NEPolicySessionPriorityControl];
1822 }
1823 ((ConfigAgent *)agent).preferredPolicySession = self.controlPolicySession;
1824 } else {
1825 ((ConfigAgent *)agent).preferredPolicySession = self.policySession;
1826 }
1827
1828 session = ((ConfigAgent *)agent).preferredPolicySession;
1829
1830 policyID1 = [session addPolicy:newPolicy];
1831 if (policyID1 == 0) {
1832 SC_log(LOG_NOTICE, "Could not add a netagent policy for agent %@", [agent getAgentName]);
1833 return NO;
1834 }
1835
1836 result = [NEPolicyResult skipWithOrder:skipOrder];
1837 newPolicy = [[NEPolicy alloc] initWithOrder:orderForSkip
1838 result:result
1839 conditions:(condition ? @[condition] : nil)];
1840
1841 if (newPolicy == nil) {
1842 SC_log(LOG_NOTICE, "Could not create a policy for agent %@", [agent getAgentName]);
1843 return NO;
1844 }
1845
1846 policyID2 = [session addPolicy:newPolicy];
1847 if (policyID2 == 0) {
1848 SC_log(LOG_NOTICE, "Could not add a skip policy for agent %@", [agent getAgentName]);
1849 return NO;
1850 }
1851
1852 ok = [session apply];
1853 if (!ok) {
1854 SC_log(LOG_NOTICE, "Could not apply policy for agent %@", [agent getAgentName]);
1855 return NO;
1856 }
1857
1858 policyArray = [self.policyDB objectForKey:[agent getAgentName]];
1859 if (policyArray == nil) {
1860 policyArray = [NSMutableArray array];
1861 }
1862
1863 [policyArray addObject:numberToNSNumber(policyID1)];
1864 [policyArray addObject:numberToNSNumber(policyID2)];
1865 [self.policyDB setObject:policyArray forKey:[agent getAgentName]];
1866
1867 return ok;
1868 }
1869
1870 #pragma mark Agent manipulation functions
1871
1872 /*
1873 * Create an agent
1874 */
1875 - (BOOL)spawnFloatingAgent:(Class)agentClass
1876 entity:(NSString *)entity
1877 agentSubType:(AgentSubType)subtype
1878 addPolicyOfType:(NEPolicyConditionType)policyType
1879 publishData:(NSData *)data
1880 {
1881 id agent;
1882 BOOL ok;
1883 NSMutableDictionary * parameters;
1884
1885 parameters =[NSMutableDictionary dictionary];
1886 [parameters setValue:entity forKey:@kEntityName];
1887 [parameters setValue:numberToNSNumber(subtype) forKey:@kAgentSubType];
1888
1889 agent = [[agentClass alloc] initWithParameters:parameters];
1890 ok = [self registerAgent:agent];
1891 if (!ok) {
1892 return NO;
1893 }
1894
1895 if (data) {
1896 /* Since we just spawned this agent, update its data */
1897 [agent updateAgentData:data];
1898 [self publishToAgent:agent];
1899 }
1900
1901 /* Add a policy if there is a valid type. If POLICY_TYPE_NO_POLICY, then ignore policies.
1902 * POLICY_TYPE_NO_POLICY will be set for service-specific agents, in which case we rely on
1903 * service owners to install custom policies to point at the agents. */
1904 if (policyType >= NEPolicyConditionTypeNone) {
1905 BOOL useControlPolicySession = NO;
1906 if (subtype == kAgentSubTypeGlobal) {
1907 /* Policies for a Global scoped agents are at "control" level */
1908 useControlPolicySession = YES;
1909 }
1910
1911 ok = [self addPolicyToFloatingAgent:agent
1912 domain:entity
1913 agentUUIDToUse:[agent agentUUID]
1914 policyType:policyType
1915 useControlPolicySession:useControlPolicySession];
1916
1917 if (!ok) {
1918 [self unregisterAgent:agent];
1919 return NO;
1920 }
1921 }
1922
1923 SC_log(LOG_INFO, "Spawning floating agent for %@", entity);
1924
1925 if ([agent getAgentType] == kAgentTypeProxy) {
1926 [self.floatingProxyAgentList setObject:agent forKey:entity];
1927 } else {
1928 [self.floatingDNSAgentList setObject:agent forKey:entity];
1929 }
1930
1931 return ok;
1932 }
1933
1934 /*
1935 * Create an agent mapped to another agent.
1936 */
1937 - (BOOL)spawnMappedFloatingAgent:(id)mapped_agent
1938 entity:(NSString *)entity
1939 agentSubType:(AgentSubType)subtype
1940 addPolicyOfType:(NEPolicyConditionType)policyType
1941 updateData:(NSData *)data
1942 {
1943 id dummyAgent;
1944 NSMutableDictionary * parameters;
1945
1946 parameters = [NSMutableDictionary dictionary];
1947 [parameters setValue:entity forKey:@kEntityName];
1948 [parameters setValue:numberToNSNumber(subtype) forKey:@kAgentSubType];
1949
1950 dummyAgent = [[[mapped_agent class] alloc] initWithParameters:parameters];
1951
1952 if (data) {
1953 /* Since we just spawned this agent, update its data.
1954 * We do not publish it since this agent is mapped
1955 * to an agent which already has the same data
1956 */
1957 [dummyAgent updateAgentData:data];
1958 }
1959
1960 BOOL useControlPolicySession = NO;
1961 if (subtype == kAgentSubTypeGlobal) {
1962 /* Policies for a Global scoped agents are at "control" level */
1963 useControlPolicySession = YES;
1964 }
1965
1966 BOOL ok = [self addPolicyToFloatingAgent:dummyAgent
1967 domain:entity
1968 agentUUIDToUse:[mapped_agent agentUUID]
1969 policyType:policyType
1970 useControlPolicySession:useControlPolicySession];
1971
1972 if (!ok) {
1973 return NO;
1974 }
1975
1976 if ([mapped_agent getAgentType] == kAgentTypeProxy) {
1977 [self.floatingProxyAgentList setObject:dummyAgent forKey:entity];
1978 } else {
1979 [self.floatingDNSAgentList setObject:dummyAgent forKey:entity];
1980 }
1981
1982 [dummyAgent setAgentMapping:mapped_agent];
1983
1984 SC_log(LOG_INFO, "Mapped floating agent %@ to %@", [dummyAgent getAgentName], [mapped_agent getAgentName]);
1985 return YES;
1986 }
1987
1988 /*
1989 * Write into an agent
1990 */
1991 - (BOOL)publishToAgent:(id)agent
1992 {
1993 /* Before any data goes into the kernel, do a sanity check. */
1994 NSData *sanityCheckData = [self dataLengthSanityCheck:agent];
1995 NSData *tempAgentData = nil;
1996
1997 if (sanityCheckData != nil) {
1998 /* Data length is more than the limit! for updateNetworkAgent, the data blob
1999 * has to be a part of the agent object. Thus the temporary data replacement!
2000 */
2001 tempAgentData = [[agent getAgentData] copy];
2002 [agent updateAgentData:sanityCheckData];
2003 SC_log(LOG_NOTICE, "Data too large for %@ (%lu bytes)!", [agent getAgentName], (unsigned long)[tempAgentData length]);
2004 }
2005
2006 BOOL ok = NO;
2007
2008 NWNetworkAgentRegistration *regObject = [agent valueForKey:@"registrationObject"];
2009 if (regObject != nil) {
2010 SC_log(LOG_NOTICE, "Publishing data to agent %@ (%lu bytes)", [agent getAgentName], (unsigned long)[[agent getAgentData] length]);
2011 ok = [regObject updateNetworkAgent:agent];
2012 if (!ok) {
2013 SC_log(LOG_NOTICE, "Could not update config agent");
2014 }
2015 } else {
2016 SC_log(LOG_NOTICE, "Config Agent not registered. Cannot Update");
2017 }
2018
2019 if (tempAgentData != nil) {
2020 [agent updateAgentData:tempAgentData];
2021 }
2022
2023 return ok;
2024 }
2025
2026 /*
2027 * Destroy an agent
2028 */
2029 - (BOOL)destroyFloatingAgent:(id)agent
2030 {
2031 BOOL ok = NO;
2032
2033 if ( agent != nil) {
2034 NSMutableArray * policyArray;
2035
2036 policyArray = [self.policyDB objectForKey:[agent getAgentName]];
2037 if (policyArray != nil) {
2038 NEPolicySession * session = ((ConfigAgent *)agent).preferredPolicySession;
2039 BOOL result = NO;
2040
2041 for (NSNumber *policyID in policyArray) {
2042 NSUInteger idVal;
2043
2044 idVal = [policyID unsignedIntegerValue];
2045 result = [session removePolicyWithID:idVal];
2046 if (result == NO) {
2047 SC_log(LOG_NOTICE, "Could not remove policy %@ for agent %@", [session policyWithID:idVal], [agent getAgentName]);
2048 }
2049 }
2050
2051 result = [session apply];
2052 if (result == NO) {
2053 SC_log(LOG_NOTICE, "Could not apply removed policies for agent %@", [agent getAgentName]);
2054 }
2055
2056 [self.policyDB removeObjectForKey:[agent getAgentName]];
2057 }
2058
2059 if ([agent getAgentType] == kAgentTypeProxy) {
2060 [self.floatingProxyAgentList removeObjectForKey:[agent getAssociatedEntity]];
2061 } else {
2062 [self.floatingDNSAgentList removeObjectForKey:[agent getAssociatedEntity]];
2063 }
2064
2065 if ([agent getRegistrationObject] != nil) {
2066 [self unregisterAgent:agent];
2067 }
2068
2069 SC_log(LOG_INFO, "X - Destroyed agent %@", [agent getAgentName]);
2070
2071 /* Check if we need to close the "control" policy session */
2072 if (self.controlPolicySession != nil) {
2073 NSMutableArray *globalProxyAgentList;
2074 NSMutableArray *globalDNSAgentList;
2075 globalProxyAgentList = [self getAgentList:self.floatingProxyAgentList agentType:kAgentTypeProxy agentSubType:kAgentSubTypeGlobal];
2076 globalDNSAgentList = [self getAgentList:self.floatingDNSAgentList agentType:kAgentTypeDNS agentSubType:kAgentSubTypeGlobal];
2077
2078 if ([globalProxyAgentList count] == 0 &&
2079 [globalDNSAgentList count] == 0) {
2080 ok = [self.controlPolicySession removeAllPolicies];
2081 if (!ok) {
2082 SC_log(LOG_ERR, "Could not remove policies for agent %@", [agent getAgentName]);
2083 }
2084
2085 ok = [self.controlPolicySession apply];
2086 if (!ok) {
2087 SC_log(LOG_ERR, "Could not apply policy change for agent %@", [agent getAgentName]);
2088 }
2089
2090 self.controlPolicySession = nil;
2091 SC_log(LOG_NOTICE, "Closed control policy session");
2092 }
2093 }
2094
2095 ok = YES;
2096 }
2097
2098 return ok;
2099 }
2100
2101 /*
2102 * Register an agent
2103 */
2104 - (BOOL)registerAgent:(id)agent
2105 {
2106 BOOL ok = NO;
2107
2108 NWNetworkAgentRegistration *registration = [[NWNetworkAgentRegistration alloc] initWithNetworkAgentClass:[agent class]];
2109
2110 ok = [registration registerNetworkAgent:agent];
2111 if (!ok) {
2112 SC_log(LOG_NOTICE, "Could not register config agent");
2113 goto done;
2114 }
2115
2116 [agent addAgentRegistrationObject:registration];
2117
2118 done:
2119 return ok;
2120 }
2121
2122 /*
2123 * Unregister an agent
2124 */
2125 - (BOOL)unregisterAgent:(id)agent
2126 {
2127 BOOL ok = false;
2128
2129 NWNetworkAgentRegistration *regObject = [agent valueForKey:@"registrationObject"];
2130 if (regObject != nil) {
2131 ok = [regObject unregisterNetworkAgent];
2132 if (!ok) {
2133 SC_log(LOG_NOTICE, "Could not unregister config agent");
2134 }
2135 } else {
2136 SC_log(LOG_NOTICE, "Config Agent not registered. Cannot unregister");
2137 }
2138
2139 return ok;
2140 }
2141
2142
2143 @end