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