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