2 * Copyright (c) 2015, 2016 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #import "controller.h"
25 #import <SystemConfiguration/SCPrivate.h>
27 #define numberToNSNumber(x) [NSNumber numberWithUnsignedInteger:x]
29 #define dnsAgentDefault "_defaultDNS"
30 #define proxyAgentDefault "_defaultProxy"
31 #define multipleEntitySuffix " #"
32 #define prefixForInterfaceName "@"
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
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
43 #define POLICY_TYPE_NO_POLICY -1
44 #define CONFIG_AGENT_DATA_LIMIT MIN(NETAGENT_MAX_DATA_SIZE, 1024)
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;
55 @interface AgentController()
57 @property (nonatomic) NSMutableDictionary * floatingProxyAgentList;
58 @property (nonatomic) NSMutableDictionary * floatingDNSAgentList;
59 @property (nonatomic) NSMutableDictionary * policyDB;
60 @property (nonatomic) NEPolicySession * policySession;
64 @implementation AgentController
68 + (AgentController *)sharedController
70 static AgentController * gController = nil;
71 static dispatch_once_t onceToken;
73 dispatch_once(&onceToken, ^{
74 gController = [[AgentController alloc] init];
77 @synchronized (gController) {
78 if (![gController isControllerReady]) {
79 if (![gController initializeController]) {
92 [self initializeController];
98 - (BOOL)initializeController
100 const char *errorMessage = NULL;
103 /* The NE policy session for the controller */
105 if (self.policySession == nil) {
106 self.policySession = [self createPolicySession];
107 if (self.policySession == nil) {
108 errorMessage = "Failed to create a policy session";
113 /* A dictionary of all floating proxy agents
114 * Key : <entity-name> (can be an interface name or domain name)
115 * Value : agent object
118 if (self.floatingProxyAgentList == nil) {
119 self.floatingProxyAgentList = [NSMutableDictionary dictionary];
120 if (self.floatingProxyAgentList == nil) {
121 errorMessage = "Failed to create a dictionary";
126 /* A dictionary of all floating dns agents
127 * Key : <entity-name> (can be an interface name or domain name)
128 * Value : agent object
131 if (self.floatingDNSAgentList == nil) {
132 self.floatingDNSAgentList = [NSMutableDictionary dictionary];
133 if (self.floatingDNSAgentList == nil) {
134 errorMessage = "Failed to create a dictionary";
139 /* A dictionary for the maintaining the policy IDs for all installed policy.
140 * These IDs would be necessary to uninstall a policy when an agent goes away
141 * Key : agent name (which can be retrieved by [agent getAgentName])
142 * Value : An array of integers, each being a policy ID for that agent
145 if (self.policyDB == nil) {
146 self.policyDB = [NSMutableDictionary dictionary];
147 if (self.policyDB == nil) {
148 errorMessage = "Failed to create a dictionary";
153 /* The queue to run the all processing on */
155 if (self.controllerQueue == nil) {
156 self.controllerQueue = dispatch_queue_create("com.apple.SystemConfiguration.controllerQueue", NULL);
157 if (self.controllerQueue == nil) {
158 errorMessage = "Failed to create a queue";
164 if (errorMessage != NULL) {
165 /* Some error occurred. This is unlikely during controller initialization... */
166 SC_log(LOG_ERR, "Error occured while initializing AgentController: %s", errorMessage);
167 _SC_crash(errorMessage, NULL, NULL);
174 - (NEPolicySession *)createPolicySession
176 NEPolicySession *session = nil;
177 #if !TARGET_OS_IPHONE
178 /* On OS X, since we cannot have entitlements, we open a kernel control
179 * socket and use it to create a policy session
182 /* Create kernel control socket */
184 struct ctl_info kernctl_info;
185 struct sockaddr_ctl kernctl_addr;
186 const char *controlName = NECP_CONTROL_NAME;
188 if ((sock = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL)) < 0)
190 SC_log(LOG_NOTICE, "Cannot create kernel control socket (errno = %d)\n", errno);
194 bzero(&kernctl_info, sizeof(kernctl_info));
195 strlcpy(kernctl_info.ctl_name, controlName, sizeof(kernctl_info.ctl_name));
196 if (ioctl(sock, CTLIOCGINFO, &kernctl_info))
198 SC_log(LOG_NOTICE, "ioctl failed on kernel control socket (errno = %d)\n", errno);
203 bzero(&kernctl_addr, sizeof(kernctl_addr));
204 kernctl_addr.sc_len = sizeof(kernctl_addr);
205 kernctl_addr.sc_family = AF_SYSTEM;
206 kernctl_addr.ss_sysaddr = AF_SYS_CONTROL;
207 kernctl_addr.sc_id = kernctl_info.ctl_id;
208 kernctl_addr.sc_unit = 0;
209 if (connect(sock, (struct sockaddr *)&kernctl_addr, sizeof(kernctl_addr)))
211 SC_log(LOG_NOTICE, "connect failed on kernel control socket (errno = %d)\n", errno);
216 /* Create policy session */
217 session = [[NEPolicySession alloc] initWithSocket:sock];
218 if (session == nil) {
221 #else //!TARGET_OS_IPHONE
222 session = [[NEPolicySession alloc] init];
223 #endif //!TARGET_OS_IPHONE
228 - (BOOL)isControllerReady
230 /* Make sure that we have all our data structures in place */
231 return ((self.policySession != nil) &&
232 (self.floatingProxyAgentList != nil) &&
233 (self.floatingDNSAgentList != nil) &&
234 (self.policyDB != nil) &&
235 (self.controllerQueue != nil));
238 /* ========================== proxy agent helpers =========================== */
239 #pragma mark Proxy agent helper functions
241 - (NSData *)dataForProxyArray:(CFArrayRef)proxy_array_for_data
243 NSData *data = [NSPropertyListSerialization dataWithPropertyList:(__bridge id _Nonnull)(proxy_array_for_data)
244 format:NSPropertyListBinaryFormat_v1_0
251 - (NSData *)dataForProxyDictionary:(CFDictionaryRef)domain_proxy
254 CFMutableDictionaryRef domain_proxy_dict;
255 CFArrayRef domain_proxy_array;
257 if (domain_proxy == NULL) {
258 SC_log(LOG_NOTICE, "Invalid domain proxy dict");
262 domain_proxy_dict = CFDictionaryCreateMutableCopy(NULL, 0, domain_proxy);
263 CFDictionaryRemoveValue(domain_proxy_dict, kSCPropNetProxiesSupplementalMatchDomain);
265 domain_proxy_array = CFArrayCreate(NULL, (const void **)&domain_proxy_dict, 1, &kCFTypeArrayCallBacks);
266 CFRelease(domain_proxy_dict);
268 data = [self dataForProxyArray:domain_proxy_array];
269 CFRelease(domain_proxy_array);
274 - (NSData *)getProxyDataFromCurrentConfig:(CFDictionaryRef)proxies
275 domain:(NSString *)domain
279 CFArrayRef supplemental;
281 if (proxies == NULL || domain == nil) {
282 SC_log(LOG_NOTICE, "Invalid proxies/domain");
286 supplemental = CFDictionaryGetValue(proxies, kSCPropNetProxiesSupplemental);
287 count = supplemental ? CFArrayGetCount(supplemental) : 0;
289 for (idx = 0; idx < count; idx++) {
290 CFDictionaryRef domain_proxy;
291 CFStringRef match_domain;
293 domain_proxy = CFArrayGetValueAtIndex(supplemental, idx);
294 match_domain = CFDictionaryGetValue(domain_proxy, kSCPropNetProxiesSupplementalMatchDomain);
295 if (match_domain != NULL && CFEqual(match_domain, (__bridge CFTypeRef)(domain))) {
296 return [self dataForProxyDictionary:domain_proxy];
303 - (bool)getIntValue:(CFTypeRef)cf_value
304 valuePtr:(int *) int_value_ptr
307 if (cf_value && CFGetTypeID(cf_value) == CFNumberGetTypeID() && CFNumberGetValue(cf_value, kCFNumberIntType, int_value_ptr))
314 - (int)countProxyEntriesEnabled:(CFDictionaryRef)proxies
318 if (proxies == NULL) {
319 SC_log(LOG_NOTICE, "Invalid proxies");
323 if (([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesHTTPEnable) valuePtr:&enabled] && enabled > 0) ||
324 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesHTTPSEnable) valuePtr:&enabled] && enabled > 0) ||
325 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesProxyAutoConfigEnable) valuePtr:&enabled] && enabled > 0) ||
326 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesFTPEnable) valuePtr:&enabled] && enabled > 0) ||
327 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesGopherEnable) valuePtr:&enabled] && enabled > 0) ||
328 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesRTSPEnable) valuePtr:&enabled] && enabled > 0) ||
329 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesSOCKSEnable) valuePtr:&enabled] && enabled > 0) ||
330 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesProxyAutoDiscoveryEnable) valuePtr:&enabled] && enabled > 0)) {
337 - (void)processSupplementalProxyChanges:(CFDictionaryRef)proxies
340 NSMutableArray * deleteList;
341 NSCountedSet * duplicate_domain_list;
343 NSMutableArray * new_domain_list;
344 NSMutableArray * old_domain_list;
345 CFArrayRef supplemental;
346 NSMutableArray * update_agent_list;
348 if (proxies == NULL) {
349 SC_log(LOG_INFO, "No proxy config to process");
353 old_domain_list = [self getAgentList:self.floatingProxyAgentList
354 agentType:kAgentTypeProxy
355 agentSubType:kAgentSubTypeSupplemental];
356 duplicate_domain_list = [[NSCountedSet alloc] initWithCapacity:0];
357 new_domain_list = [NSMutableArray array];
358 update_agent_list = [NSMutableArray array];
359 supplemental = CFDictionaryGetValue(proxies, kSCPropNetProxiesSupplemental);
360 count = supplemental ? CFArrayGetCount(supplemental) : 0;
361 deleteList = [NSMutableArray array];
363 for (idx = 0; idx < count; idx++) {
364 CFDictionaryRef domain_proxy;
365 CFStringRef match_domain;
368 domain_proxy = CFArrayGetValueAtIndex(supplemental, idx);
369 match_domain = CFDictionaryGetValue(domain_proxy, kSCPropNetProxiesSupplementalMatchDomain);
370 if (match_domain == NULL) {
374 /* This domain is present in current config. But if it has generic (no protocols enabled)
375 * proxy content, there is no real use of that agent. Do NOT add it to
376 * the new_domain_list.
378 * This way, if there was an agent previously for this domain,
379 * it will be destroyed AND since it is not present in the new domain list, we wont
380 * spawn a new agent too! :)
383 proxy_count = [self countProxyEntriesEnabled:domain_proxy];
384 if (proxy_count == 0) {
385 SC_log(LOG_INFO, "Proxy settings on %@ are generic. Not recognizing as new domain", match_domain);
389 [new_domain_list addObject:(__bridge NSString *)match_domain];
392 [self cleanConflictingAgentsFromList:old_domain_list
393 new_list:new_domain_list
394 agentDictionary:self.floatingProxyAgentList];
396 for (NSString *key in old_domain_list) {
399 domain_present = [new_domain_list containsObject:key];
400 if (domain_present == NO) {
403 agent = [self.floatingProxyAgentList objectForKey:key];
404 [self destroyFloatingAgent:agent];
408 /* At this point, whatever is in the controller's floating agent list,
409 * is present in the current proxy config. The current proxy config
410 * might have even more configs, not known to the controller, YET
413 for (NSString *domain in old_domain_list) {
417 agent = [self.floatingProxyAgentList objectForKey:domain];
422 /* Am I mapped to some agent? */
423 mapped_agent = [agent getAgentMapping];
425 /* OK, this agent is mapped to some other agent. We compare this agent's data
426 * to the current data of the agent to which it is mapped. If different, we destroy
427 * the agent and later map it to someone else OR spawn a new one.
429 NSData * mapped_agent_data;
431 mapped_agent_data = [self getProxyDataFromCurrentConfig:proxies domain:[mapped_agent getAssociatedEntity]];
432 if (mapped_agent_data == nil || ![[agent getAgentData] isEqual:mapped_agent_data]) {
433 /* Something changed for mapped agent */
434 [deleteList addObject:agent];
438 /* Since this agent is NOT mapped to any other agent, this agent is
439 * registered with the kernel. So instead of destroying the agent and
440 * re-registering it, just update it here.
442 * All the agents which were mapped to this agent, will be deleted and
443 * re-mapped, if the data changed.
447 agent_data = [self getProxyDataFromCurrentConfig:proxies domain:[agent getAssociatedEntity]];
448 if (![[agent getAgentData] isEqual:agent_data]) {
449 /* Something changed for agent */
450 [agent updateAgentData:agent_data];
452 /* The reason I don't publish the data to agent here is that, if there were
453 * some agents mapping to this one, they will momentarily have a policy for
454 * using this agent UUID for some domain based on this agent's previous data.
456 [update_agent_list addObject:agent];
459 [new_domain_list removeObject:domain];
462 for (id agent in deleteList) {
463 SC_log(LOG_INFO, "Destroying agent %@ because something changed!", [agent getAgentName]);
464 [self destroyFloatingAgent:agent];
467 for (id agent in update_agent_list) {
468 [self publishToAgent:agent];
471 for (idx = 0; idx < count; idx++) {
472 CFDictionaryRef domain_proxy;
473 CFStringRef match_domain;
475 domain_proxy = CFArrayGetValueAtIndex(supplemental, idx);
476 match_domain = CFDictionaryGetValue(domain_proxy, kSCPropNetProxiesSupplementalMatchDomain);
478 if (match_domain != NULL) {
483 found = [new_domain_list indexOfObject:(__bridge id _Nonnull)(match_domain)];
484 if (found == NSNotFound) {
489 * We will only process agents which are mapped AND the agent they were mapped to, changed OR
490 * agents for domains which we did not know before.
493 NSUInteger domainInstance = [duplicate_domain_list countForObject:(__bridge id _Nonnull)(match_domain)];
494 if (domainInstance > 0) {
495 /* domainInstance will be > 0, only if we have conflicting domains */
497 NSString *ns_domain_name_copy = [NSString stringWithFormat:@"%@" multipleEntitySuffix "%lu", match_domain, (unsigned long)domainInstance];
499 data = [self dataForProxyDictionary:domain_proxy];
501 BOOL ok = [self spawnFloatingAgent:[ProxyAgent class]
502 entity:ns_domain_name_copy
503 agentSubType:kAgentSubTypeSupplemental
504 addPolicyOfType:NEPolicyConditionTypeDomain
507 id agent = [self.floatingProxyAgentList objectForKey:ns_domain_name_copy];
508 SC_log(LOG_INFO, "Duplicate Proxy agent %@", [agent getAgentName]);;
511 data = [self dataForProxyDictionary:domain_proxy];
512 mapped_agent = [self getAgentWithSameDataAndSubType:self.floatingProxyAgentList
514 subType:kAgentSubTypeSupplemental];
515 if (mapped_agent != nil) {
516 [self spawnMappedFloatingAgent:mapped_agent
517 entity:(__bridge NSString *)(match_domain)
518 agentSubType:kAgentSubTypeSupplemental
519 addPolicyOfType:NEPolicyConditionTypeDomain
522 [self spawnFloatingAgent:[ProxyAgent class]
523 entity:(__bridge NSString *)(match_domain)
524 agentSubType:kAgentSubTypeSupplemental
525 addPolicyOfType:NEPolicyConditionTypeDomain
530 [new_domain_list removeObjectAtIndex:found];
531 [duplicate_domain_list addObject:(__bridge id _Nonnull)(match_domain)];
538 - (void)processScopedProxyChanges:(CFDictionaryRef)proxies
540 NSMutableArray * old_intf_list;
541 CFDictionaryRef scoped_proxies;
542 CFIndex scoped_proxies_count;
544 old_intf_list = [self getAgentList:self.floatingProxyAgentList
545 agentType:kAgentTypeProxy
546 agentSubType:kAgentSubTypeScoped];
548 scoped_proxies = CFDictionaryGetValue(proxies, kSCPropNetProxiesScoped);
549 scoped_proxies_count = scoped_proxies ? CFDictionaryGetCount(scoped_proxies) : 0;
551 if (scoped_proxies_count > 0) {
554 keys = malloc(scoped_proxies_count * sizeof(void *));
555 CFDictionaryGetKeysAndValues(scoped_proxies, keys, NULL);
557 for (int i = 0; i < scoped_proxies_count; i++) {
561 NSString * ns_if_name;
562 NSString * ns_if_name_with_prefix;
566 ns_if_name = (__bridge NSString *)keys[i];
567 ns_if_name_with_prefix = [NSString stringWithFormat:@"%s%@", prefixForInterfaceName, ns_if_name];
569 /* Does the proxy config have any protocols enabled? */
570 proxy_count = [self countProxyEntriesEnabled:CFDictionaryGetValue(scoped_proxies,
571 (__bridge const void *)(ns_if_name))];
573 if (proxy_count == 0) {
574 SC_log(LOG_INFO, "Proxy settings on %@ are generic. Skipping", ns_if_name);
578 idx = [old_intf_list indexOfObject:ns_if_name_with_prefix];
580 matching = SCNetworkProxiesCopyMatching(proxies, NULL, (__bridge CFStringRef)(ns_if_name));
581 if (matching != NULL) {
582 data = [self dataForProxyArray:matching];
586 if (idx == NSNotFound) {
587 /* We need to spawn an agent */
588 [self spawnFloatingAgent:[ProxyAgent class]
589 entity:ns_if_name_with_prefix
590 agentSubType:kAgentSubTypeScoped
591 addPolicyOfType:NEPolicyConditionTypeScopedInterface
596 /* We have an agent for this interface. Update it */
597 [old_intf_list removeObjectAtIndex:idx];
600 proxyAgent = [self.floatingProxyAgentList objectForKey:ns_if_name_with_prefix];
601 if (proxyAgent != nil) {
602 /* Do we need to update this agent? */
603 [proxyAgent updateAgentData:data];
604 if ([proxyAgent shouldUpdateAgent]) {
605 [self publishToAgent:proxyAgent];
613 [self deleteAgentList:self.floatingProxyAgentList list:old_intf_list];
616 - (void)processServiceSpecificProxyChanges:(CFDictionaryRef)proxies
618 NSMutableArray * old_service_list;
619 CFDictionaryRef service_proxies;
620 CFIndex service_proxies_count;
622 old_service_list = [self getAgentList:self.floatingProxyAgentList
623 agentType:kAgentTypeProxy
624 agentSubType:kAgentSubTypeServiceSpecific];
626 service_proxies = CFDictionaryGetValue(proxies, kSCPropNetProxiesServices);
627 service_proxies_count = service_proxies ? CFDictionaryGetCount(service_proxies) : 0;
629 if (service_proxies_count > 0) {
632 keys = malloc(service_proxies_count * sizeof(void *));
633 CFDictionaryGetKeysAndValues(service_proxies, keys, NULL);
635 for (int i = 0; i < service_proxies_count; i++) {
638 NSString * ns_service_identifier = nil;
639 NSString * ns_service_with_prefix = nil;
642 CFDictionaryRef proxyDict = NULL;
644 ns_service_identifier = (__bridge NSString *)keys[i];
645 ns_service_with_prefix = [NSString stringWithFormat:@"%s%@", prefixForInterfaceName, ns_service_identifier];
647 /* Does the proxy config have any protocols enabled? */
648 proxy_count = [self countProxyEntriesEnabled:CFDictionaryGetValue(service_proxies,
649 (__bridge const void *)(ns_service_identifier))];
651 if (proxy_count == 0) {
652 SC_log(LOG_INFO, "Proxy settings on %@ are generic. Skipping", ns_service_identifier);
656 proxyDict = CFDictionaryGetValue(service_proxies, (__bridge CFStringRef)ns_service_identifier);
657 if (proxyDict != nil) {
658 data = [self dataForProxyArray:(__bridge CFArrayRef)(@[ (__bridge NSDictionary *)proxyDict ])];
661 idx = [old_service_list indexOfObject:ns_service_with_prefix];
662 if (idx == NSNotFound) {
663 /* We need to spawn an agent */
664 [self spawnFloatingAgent:[ProxyAgent class]
665 entity:ns_service_with_prefix
666 agentSubType:kAgentSubTypeServiceSpecific
667 addPolicyOfType:(POLICY_TYPE_NO_POLICY) /* Don't install a policy */
672 /* We have an agent for this service. Update it */
673 [old_service_list removeObjectAtIndex:idx];
676 proxyAgent = [self.floatingProxyAgentList objectForKey:ns_service_with_prefix];
677 if (proxyAgent != nil) {
678 /* Do we need to update this agent? */
679 [proxyAgent updateAgentData:data];
680 if ([proxyAgent shouldUpdateAgent]) {
681 [self publishToAgent:proxyAgent];
689 [self deleteAgentList:self.floatingProxyAgentList list:old_service_list];
692 - (void)processDefaultProxyChanges:(CFDictionaryRef)proxies
694 CFArrayRef global_proxy;
695 CFIndex global_proxy_count;
696 CFMutableDictionaryRef proxies_copy;
698 proxies_copy = CFDictionaryCreateMutableCopy(NULL, 0, proxies);
699 CFDictionaryRemoveValue(proxies_copy, kSCPropNetProxiesScoped);
700 CFDictionaryRemoveValue(proxies_copy, kSCPropNetProxiesServices);
701 CFDictionaryRemoveValue(proxies_copy, kSCPropNetProxiesSupplemental);
703 global_proxy = CFArrayCreate(NULL, (const void **)&proxies_copy, 1, &kCFTypeArrayCallBacks);
704 global_proxy_count = CFArrayGetCount(global_proxy);
705 if (global_proxy_count > 0 &&
706 [self countProxyEntriesEnabled:proxies_copy] == 0) {
707 SC_log(LOG_INFO, "Proxy settings on defaultProxy are generic. Skipping");
708 global_proxy_count = 0;
710 CFRelease(proxies_copy);
712 if (global_proxy_count > 0) {
716 data = [self dataForProxyArray:global_proxy];
717 proxyAgent = [self.floatingProxyAgentList objectForKey:@proxyAgentDefault];
718 if (proxyAgent == nil) {
719 [self spawnFloatingAgent:[ProxyAgent class]
720 entity:@proxyAgentDefault
721 agentSubType:kAgentSubTypeDefault
722 addPolicyOfType:NEPolicyConditionTypeNone
725 [proxyAgent updateAgentData:data];
726 if ([proxyAgent shouldUpdateAgent]) {
727 [self publishToAgent:proxyAgent];
731 /* No default proxy config OR generic (no protocols enabled) default proxy config.
732 * Destroy the default agent if we had one
736 proxyAgent = [self.floatingProxyAgentList objectForKey:@proxyAgentDefault];
737 if (proxyAgent != nil) {
738 [self destroyFloatingAgent:proxyAgent];
742 CFRelease(global_proxy);
745 - (void)processProxyChanges
747 CFDictionaryRef proxies;
749 proxies = SCDynamicStoreCopyProxiesWithOptions(NULL, NULL);
750 if (proxies == NULL) {
751 SC_log(LOG_INFO, "No proxy information");
753 NSMutableDictionary *copy = [self.floatingProxyAgentList copy];
754 for (NSString *entity in copy) {
755 id agent = [copy objectForKey:entity];
756 [self destroyFloatingAgent:agent];
762 [self processDefaultProxyChanges:proxies];
763 [self processScopedProxyChanges:proxies];
764 [self processSupplementalProxyChanges:proxies];
765 [self processServiceSpecificProxyChanges:proxies];
770 /* ========================== DNS agent helpers =========================== */
771 #pragma mark DNS agent helper functions
773 - (void)freeResolverList:(resolver_list_t *)resolvers
775 /* This is a shallow free of resolver_list_t only.
776 * The actual resolver pointers are owned by 'dns_config'
778 if (resolvers == NULL) {
782 if (resolvers->default_resolvers != NULL) {
783 free(resolvers->default_resolvers);
785 if (resolvers->multicast_resolvers != NULL) {
786 free(resolvers->multicast_resolvers);
788 if (resolvers->private_resolvers != NULL) {
789 free(resolvers->private_resolvers);
795 - (resolver_list_t *)copyResolverList:(dns_config_t *)dns_config
797 resolver_list_t *resolvers = NULL;
799 if ((dns_config->n_resolver > 0) && (dns_config->resolver != NULL)) {
804 resolvers = calloc(1, sizeof(resolver_list_t));
805 for (int i = 0; i < dns_config->n_resolver; i++) {
806 dns_resolver_t *r = dns_config->resolver[i];
808 if ([self isResolverMulticast:r]) {
809 resolvers->n_multicast_resolvers++;
812 } else if ([self isResolverPrivate:r]) {
813 resolvers->n_private_resolvers++;
817 // do not consider default resolvers with no nameservers
818 if (r->domain == NULL && r->n_nameserver > 0) {
819 resolvers->n_default_resolvers++;
823 SC_log(LOG_INFO, "Resolvers: %d default, %d multicast, %d private",
824 resolvers->n_default_resolvers,
825 resolvers->n_multicast_resolvers,
826 resolvers->n_private_resolvers);
828 if (resolvers->n_default_resolvers > 0) {
829 resolvers->default_resolvers = calloc(resolvers->n_default_resolvers,
830 sizeof(dns_resolver_t *));
832 if (resolvers->n_multicast_resolvers > 0) {
833 resolvers->multicast_resolvers = calloc(resolvers->n_multicast_resolvers,
834 sizeof(dns_resolver_t *));
836 if (resolvers->n_private_resolvers > 0) {
837 resolvers->private_resolvers = calloc(resolvers->n_private_resolvers,
838 sizeof(dns_resolver_t *));
841 for (int i = 0; i < dns_config->n_resolver; i++) {
842 dns_resolver_t *r = dns_config->resolver[i];
844 if ([self isResolverMulticast:r] &&
845 (a < resolvers->n_multicast_resolvers)) {
846 resolvers->multicast_resolvers[a++] = r;
849 } else if ([self isResolverPrivate:r] &&
850 (b < resolvers->n_private_resolvers)) {
851 resolvers->private_resolvers[b++] = r;
855 if ((r->domain == NULL) &&
856 (r->n_nameserver > 0) &&
857 (c < resolvers->n_default_resolvers)) {
858 resolvers->default_resolvers[c++] = r;
867 * Generate a data blob for the resolver.
868 * Currently the blob only has:
870 * - sockaddr structs for each nameserver
874 - (NSData *)dataForResolver:(dns_resolver_t *)resolver
877 CFMutableDictionaryRef resolverDict = nil;
879 if (resolver == NULL) {
880 SC_log(LOG_NOTICE, "Invalid dns resolver");
884 if (resolver->n_search > 0) {
885 if (resolverDict == nil) {
886 resolverDict = CFDictionaryCreateMutable(NULL,
888 &kCFTypeDictionaryKeyCallBacks,
889 &kCFTypeDictionaryValueCallBacks);
892 CFMutableArrayRef searchDomainArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
894 /* Append search domains */
895 for (int i = 0; i < resolver->n_search; i++) {
896 CFArrayAppendValue(searchDomainArray, (__bridge CFStringRef)(@(resolver->search[i])));
899 CFDictionaryAddValue(resolverDict, CFSTR(kConfigAgentDNSSearchDomains), searchDomainArray);
900 CFRelease(searchDomainArray);
903 /* Get the count of nameservers */
904 if (resolver->n_nameserver > 0) {
905 if (resolverDict == nil) {
906 resolverDict = CFDictionaryCreateMutable(NULL,
908 &kCFTypeDictionaryKeyCallBacks,
909 &kCFTypeDictionaryValueCallBacks);
912 CFMutableArrayRef nameserverArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
914 /* Get all the nameservers */
915 for (int i = 0; i < resolver->n_nameserver; i++) {
917 _SC_sockaddr_to_string(resolver->nameserver[i], buf, sizeof(buf));
919 CFArrayAppendValue(nameserverArray, (__bridge CFStringRef)(@(buf)));
923 CFDictionaryAddValue(resolverDict, CFSTR(kConfigAgentDNSNameServers), nameserverArray);
924 CFRelease(nameserverArray);
927 if (resolverDict != nil) {
928 data = [NSPropertyListSerialization dataWithPropertyList:(__bridge id _Nonnull)(resolverDict)
929 format:NSPropertyListBinaryFormat_v1_0
933 CFRelease(resolverDict);
936 return (NSData *)data;
939 - (NSData *)getDNSDataFromCurrentConfig:(dns_config_t *)dns_config
940 domain:(NSString *)domain
942 if (dns_config == NULL || domain == nil) {
943 SC_log(LOG_NOTICE, "Invalid dns_config/domain");
947 if ((dns_config->n_resolver > 0) && (dns_config->resolver != NULL)) {
948 for (int i = 0; i < dns_config->n_resolver; i++) {
949 dns_resolver_t * resolver;
951 resolver = dns_config->resolver[i];
952 if (resolver->domain != NULL &&
953 ![self isResolverMulticast:resolver]) {
954 NSString * ns_domain_name;
956 ns_domain_name = @(resolver->domain);
957 if ([ns_domain_name isEqualToString:domain]) {
958 return [self dataForResolver:resolver];
969 - (BOOL)isResolverMulticast:(dns_resolver_t *)resolver
971 if (resolver->options == NULL) {
975 if (!strstr(resolver->options, "mdns")) {
982 - (BOOL)isResolverPrivate:(dns_resolver_t *)resolver
984 if (resolver->options == NULL) {
988 if (!strstr(resolver->options, "pdns")) {
995 - (void)processSupplementalDNSResolvers:(dns_config_t *)dns_config
997 NSMutableArray * deleteList;
998 NSMutableArray * new_domain_list;
999 NSCountedSet * duplicate_domain_list;
1000 NSMutableArray * old_domain_list;
1001 NSMutableArray * update_agent_list;
1004 deleteList = [NSMutableArray array];
1005 duplicate_domain_list = [[NSCountedSet alloc] initWithCapacity:0];
1006 new_domain_list = [NSMutableArray array];
1007 update_agent_list = [NSMutableArray array];
1008 old_domain_list = [self getAgentList:self.floatingDNSAgentList
1009 agentType:kAgentTypeDNS
1010 agentSubType:kAgentSubTypeSupplemental];
1012 if (dns_config->resolver == NULL) {
1013 dns_config->n_resolver = 0;
1015 if (dns_config->n_resolver > 0) {
1016 for (int i = 0; i < dns_config->n_resolver; i++) {
1017 dns_resolver_t * resolver;
1019 resolver = dns_config->resolver[i];
1020 if (resolver->domain != NULL &&
1021 ![self isResolverPrivate:resolver] &&
1022 ![self isResolverMulticast:resolver]) {
1023 NSString * ns_domain_name;
1025 ns_domain_name = [NSString stringWithCString:resolver->domain encoding:NSASCIIStringEncoding];
1026 [new_domain_list addObject:ns_domain_name];
1031 [self cleanConflictingAgentsFromList:old_domain_list
1032 new_list:new_domain_list
1033 agentDictionary:self.floatingDNSAgentList];
1035 /* Sync between controller and current config */
1036 for (NSString *key in old_domain_list) {
1037 BOOL domain_present = NO;
1039 domain_present = [new_domain_list containsObject:key];
1040 if (domain_present == NO) {
1043 agent = [self.floatingDNSAgentList objectForKey:key];
1044 [self destroyFloatingAgent:agent];
1048 /* At this point, whatever is in the controller's floating agent list,
1049 is present in the current DNS config. The current DNS config
1050 might have even more configs, not known to the controller, YET
1053 for (NSString *domain in old_domain_list) {
1057 agent = [self.floatingDNSAgentList objectForKey:domain];
1062 /* Am I mapped to some agent? */
1063 mapped_agent = [agent getAgentMapping];
1065 /* OK, this agent is mapped to some other agent. We compare this agent's data
1066 * to the current data of the agent to which it is mapped. If different, we destroy
1067 * the agent and later map it to someone else OR spawn a new one.
1069 NSData *mapped_agent_data;
1071 mapped_agent_data = [self getDNSDataFromCurrentConfig:dns_config domain:[mapped_agent getAssociatedEntity]];
1072 if (mapped_agent_data == nil || ![[agent getAgentData] isEqual:mapped_agent_data]) {
1073 /* Something changed for mapped agent */
1074 [deleteList addObject:agent];
1078 /* Since this agent is NOT mapped to any other agent, this agent is
1079 * registered with the kernel. So instead of destroying the agent and
1080 * re-registering it, just update it here.
1082 * All the agents which were mapped to this agent, will be deleted and
1083 * re-mapped, if the data changed.
1087 agent_data = [self getDNSDataFromCurrentConfig:dns_config domain:[agent getAssociatedEntity]];
1088 if (![[agent getAgentData] isEqual:agent_data]) {
1089 /* Something changed for agent */
1090 [agent updateAgentData:agent_data];
1092 /* The reason I don't publish the data to agent here is that, if there were
1093 * some agents mapping to this one, they will momentarily have a policy for
1094 * using this agent UUID for some domain based on this agent's previous data.
1096 [update_agent_list addObject:agent];
1100 [new_domain_list removeObject:domain];
1103 for (id agent in deleteList) {
1104 SC_log(LOG_INFO, "Destroying agent %@ because something changed!", [agent getAgentName]);
1105 [self destroyFloatingAgent:agent];
1108 for (id agent in update_agent_list) {
1109 [self publishToAgent:agent];
1112 for (int idx = 0; idx < dns_config->n_resolver; idx++) {
1113 dns_resolver_t * resolver;
1115 resolver = dns_config->resolver[idx];
1116 if (resolver->domain != NULL &&
1117 ![self isResolverPrivate:resolver] &&
1118 ![self isResolverMulticast:resolver]) {
1122 NSString * ns_domain_name;
1124 ns_domain_name = @(resolver->domain);
1125 found = [new_domain_list indexOfObject:ns_domain_name];
1126 if (found == NSNotFound) {
1127 /* Nothing changed for this agent */
1131 /* We will only process agents which are mapped AND if the agent they were mapped to, changed OR
1132 * agents for domains which we did not know before.
1135 NSUInteger domainInstance = [duplicate_domain_list countForObject:ns_domain_name];
1136 if (domainInstance > 0) {
1137 /* domainInstance will be > 0, only if we have conflicting domains */
1139 data = [self dataForResolver:resolver];
1141 NSString *ns_domain_name_copy = [NSString stringWithFormat:@"%@" multipleEntitySuffix "%lu", ns_domain_name, (unsigned long)domainInstance];
1143 BOOL ok = [self spawnFloatingAgent:[DNSAgent class]
1144 entity:ns_domain_name_copy
1145 agentSubType:kAgentSubTypeSupplemental
1146 addPolicyOfType:NEPolicyConditionTypeDomain
1149 id agent = [self.floatingDNSAgentList objectForKey:ns_domain_name_copy];
1150 SC_log(LOG_INFO, "Duplicate DNS agent %@", [agent getAgentName]);;
1153 data = [self dataForResolver:resolver];
1154 mapped_agent = [self getAgentWithSameDataAndSubType:self.floatingDNSAgentList
1156 subType:kAgentSubTypeSupplemental];
1157 if (mapped_agent != nil) {
1158 [self spawnMappedFloatingAgent:mapped_agent
1159 entity:ns_domain_name
1160 agentSubType:kAgentSubTypeSupplemental
1161 addPolicyOfType:NEPolicyConditionTypeDomain
1164 [self spawnFloatingAgent:[DNSAgent class]
1165 entity:ns_domain_name
1166 agentSubType:kAgentSubTypeSupplemental
1167 addPolicyOfType:NEPolicyConditionTypeDomain
1172 [new_domain_list removeObjectAtIndex:found];
1173 [duplicate_domain_list addObject:ns_domain_name];
1181 - (void)processDNSResolvers:(dns_config_t *)dns_config
1183 resolver_list_t *resolvers = [self copyResolverList:dns_config];
1185 /* Process Default resolvers */
1186 NSMutableArray *old_default_resolver_list = [self getAgentList:self.floatingDNSAgentList
1187 agentType:kAgentTypeDNS
1188 agentSubType:kAgentSubTypeDefault];
1190 // For default resolvers, their name will be '_defaultDNS', '_defaultDNS #2' so on...
1191 if (resolvers->n_default_resolvers > 0 && resolvers->default_resolvers != NULL) {
1192 for (int i = 0; i < resolvers->n_default_resolvers; i++) {
1193 dns_resolver_t *default_resolver = resolvers->default_resolvers[i];
1196 NSString * resolverName;
1198 data = [self dataForResolver:default_resolver];
1200 resolverName = @(dnsAgentDefault);
1202 resolverName = [NSString stringWithFormat:@dnsAgentDefault multipleEntitySuffix "%d", i+1 ];
1205 dnsAgent = [self.floatingDNSAgentList objectForKey:resolverName];
1207 if (dnsAgent != nil) {
1208 [old_default_resolver_list removeObject:resolverName];
1209 if ([data isEqual:[dnsAgent getAgentData]]) {
1210 /* Leave this agent in place. Nothing changed! */
1213 [self destroyFloatingAgent:dnsAgent];
1217 [self spawnFloatingAgent:[DNSAgent class]
1219 agentSubType:kAgentSubTypeDefault
1220 addPolicyOfType:NEPolicyConditionTypeNone
1225 // Only agents that are NOT present in the new config, will be present in the list
1226 // and they need to be destroyed.
1227 [self deleteAgentList:self.floatingDNSAgentList list:old_default_resolver_list];
1229 /* Process Multicast resolvers */
1231 NSMutableArray *old_multicast_resolver_list = [self getAgentList:self.floatingDNSAgentList
1232 agentType:kAgentTypeDNS
1233 agentSubType:kAgentSubTypeMulticast];
1235 if (resolvers->n_multicast_resolvers > 0 && resolvers->multicast_resolvers != NULL) {
1236 for (int i = 0; i < resolvers->n_multicast_resolvers; i++) {
1237 dns_resolver_t * multicast_resolver = resolvers->multicast_resolvers[i];
1239 NSString * resolverName;
1241 if (multicast_resolver == NULL) {
1245 if (multicast_resolver->domain == NULL) {
1246 /* Multicast resolvers MUST have a domain */
1250 resolverName = @(multicast_resolver->domain);
1251 if (resolverName == NULL) {
1252 /* Multicast resolvers MUST have a domain */
1256 dnsAgent = [self.floatingDNSAgentList objectForKey:resolverName];
1257 if (dnsAgent != nil) {
1258 [old_multicast_resolver_list removeObject:resolverName];
1262 [self spawnFloatingAgent:[DNSAgent class]
1264 agentSubType:kAgentSubTypeMulticast
1265 addPolicyOfType:NEPolicyConditionTypeDomain
1267 // Don't care about data for mdns resolvers. Do we?
1271 [self deleteAgentList:self.floatingDNSAgentList list:old_multicast_resolver_list];
1273 /* Process Private resolvers */
1275 NSMutableArray *old_private_resolver_list = [self getAgentList:self.floatingDNSAgentList
1276 agentType:kAgentTypeDNS
1277 agentSubType:kAgentSubTypePrivate];
1279 if (resolvers->n_private_resolvers > 0 && resolvers->private_resolvers != NULL) {
1280 for (int i = 0; i < resolvers->n_private_resolvers; i++) {
1281 dns_resolver_t * private_resolver = resolvers->private_resolvers[i];
1283 NSString * resolverName;
1285 if (private_resolver == NULL) {
1289 if (private_resolver->domain == NULL) {
1290 /* private resolvers MUST have a domain */
1294 resolverName = @(private_resolver->domain);
1295 if (resolverName == nil) {
1296 /* Private resolvers MUST have a domain */
1300 dnsAgent = [self.floatingDNSAgentList objectForKey:resolverName];
1301 if (dnsAgent != nil) {
1302 [old_private_resolver_list removeObject:resolverName];
1306 [self spawnFloatingAgent:[DNSAgent class]
1308 agentSubType:kAgentSubTypePrivate
1309 addPolicyOfType:NEPolicyConditionTypeDomain
1311 // Don't care about data for pdns resolvers. Do we?
1315 [self deleteAgentList:self.floatingDNSAgentList list:old_private_resolver_list];
1318 [self freeResolverList:resolvers];
1321 - (void)processScopedDNSResolvers:(dns_config_t *)dns_config;
1323 NSMutableArray * old_intf_list;
1324 old_intf_list = [self getAgentList:self.floatingDNSAgentList
1325 agentType:kAgentTypeDNS
1326 agentSubType:kAgentSubTypeScoped];
1328 if ((dns_config->n_scoped_resolver > 0) && (dns_config->scoped_resolver != NULL)) {
1329 for (int i = 0; i < dns_config->n_scoped_resolver; i++) {
1335 NSString * ns_if_name;
1336 NSString * ns_if_name_with_prefix;
1337 dns_resolver_t * resolver;
1339 resolver = dns_config->scoped_resolver[i];
1340 if_name = if_indextoname(resolver->if_index, buf);
1342 ns_if_name = @(if_name);
1343 ns_if_name_with_prefix = [NSString stringWithFormat:@"%s%@", prefixForInterfaceName, ns_if_name];
1348 data = [self dataForResolver:resolver];
1349 idx = [old_intf_list indexOfObject:ns_if_name_with_prefix];
1351 if (idx == NSNotFound) {
1352 /* We need to spawn an agent */
1353 [self spawnFloatingAgent:[DNSAgent class]
1354 entity:ns_if_name_with_prefix
1355 agentSubType:kAgentSubTypeScoped
1356 addPolicyOfType:NEPolicyConditionTypeScopedInterface
1360 /* We have an agent on this interface. Update it */
1361 [old_intf_list removeObjectAtIndex:idx];
1364 /* Get the DNS agent for this interface? */
1365 dnsAgent = [self.floatingDNSAgentList objectForKey:ns_if_name_with_prefix];
1366 if (dnsAgent != nil) {
1367 /* Do we need to update this agent? */
1368 [dnsAgent updateAgentData:data];
1369 if ([dnsAgent shouldUpdateAgent]) {
1370 [self publishToAgent:dnsAgent];
1376 [self deleteAgentList:self.floatingDNSAgentList list:old_intf_list];
1379 - (void)processServiceSpecificDNSResolvers:(dns_config_t *)dns_config;
1381 NSMutableArray * old_service_list;
1382 old_service_list = [self getAgentList:self.floatingDNSAgentList
1383 agentType:kAgentTypeDNS
1384 agentSubType:kAgentSubTypeServiceSpecific];
1386 if ((dns_config->n_service_specific_resolver > 0) && (dns_config->service_specific_resolver != NULL)) {
1387 for (int i = 0; i < dns_config->n_service_specific_resolver; i++) {
1391 uint32_t service_identifier;
1392 NSString * ns_service_identifier_with_prefix;
1393 dns_resolver_t * resolver;
1395 resolver = dns_config->service_specific_resolver[i];
1396 service_identifier = resolver->service_identifier;
1397 if (service_identifier != 0) {
1398 ns_service_identifier_with_prefix = [NSString stringWithFormat:@"%s%u", prefixForInterfaceName, service_identifier];
1403 data = [self dataForResolver:resolver];
1404 idx = [old_service_list indexOfObject:ns_service_identifier_with_prefix];
1406 if (idx == NSNotFound) {
1407 /* We need to spawn an agent */
1408 [self spawnFloatingAgent:[DNSAgent class]
1409 entity:ns_service_identifier_with_prefix
1410 agentSubType:kAgentSubTypeServiceSpecific
1411 addPolicyOfType:(POLICY_TYPE_NO_POLICY) /* Don't install a policy */
1415 /* We have an agent on this interface. Update it */
1416 [old_service_list removeObjectAtIndex:idx];
1419 /* Get the DNS agent for this interface? */
1420 dnsAgent = [self.floatingDNSAgentList objectForKey:ns_service_identifier_with_prefix];
1421 if (dnsAgent != nil) {
1422 /* Do we need to update this agent? */
1423 [dnsAgent updateAgentData:data];
1424 if ([dnsAgent shouldUpdateAgent]) {
1425 [self publishToAgent:dnsAgent];
1431 [self deleteAgentList:self.floatingDNSAgentList list:old_service_list];
1434 #define ONION_RESOLVER_DOMAIN "onion"
1435 - (BOOL)isResolverOnion:(dns_resolver_t *)resolver
1437 if (resolver->domain != NULL &&
1438 (strcmp(resolver->domain, ONION_RESOLVER_DOMAIN) == 0)) {
1446 - (void)processOnionResolver:(dns_config_t *)dns_config
1448 static NSUInteger policy_id = 0;
1450 if (dns_config == NULL) {
1454 /* Run through the resolver configurations. We only care for the supplemental resolvers. */
1455 for (int32_t i = 0; i < dns_config->n_resolver; i++) {
1456 dns_resolver_t *resolver = dns_config->resolver[i];
1457 if ([self isResolverOnion:resolver]) {
1462 /* We do not have any such resolver. Add a system-wide "drop" policy for this domain */
1463 if (policy_id == 0) {
1464 NEPolicy *policy = [[NEPolicy alloc] initWithOrder:INIT_ORDER_FOR_DOMAIN_POLICY
1465 result:[NEPolicyResult drop]
1466 conditions:@[[NEPolicyCondition domain:@ONION_RESOLVER_DOMAIN]]];
1467 if (policy != nil) {
1468 policy_id = [self.policySession addPolicy:policy];
1469 if (![self.policySession apply]) {
1471 SC_log(LOG_NOTICE, "Could not add a [." ONION_RESOLVER_DOMAIN "] drop policy");
1473 SC_log(LOG_INFO, "Added a [." ONION_RESOLVER_DOMAIN "] drop policy");
1482 /* We have such a resolver in the config OR no DNS config at all. Remove the system-wide "drop" policy for this domain */
1483 if (policy_id > 0) {
1484 [self.policySession removePolicyWithID:policy_id];
1485 if (![self.policySession apply]) {
1486 SC_log(LOG_NOTICE, "Could not remove the [." ONION_RESOLVER_DOMAIN "] drop policy");
1489 SC_log(LOG_INFO, "Removed the [." ONION_RESOLVER_DOMAIN "] drop policy");
1495 #undef ONION_RESOLVER_DOMAIN
1498 - (void)processDNSChanges
1500 dns_config_t * dns_config;
1502 dns_config = dns_configuration_copy();
1503 if (dns_config == NULL) {
1504 SC_log(LOG_INFO, "No DNS configuration");
1505 NSMutableDictionary *copy = [self.floatingDNSAgentList copy];
1506 for (NSString *entity in copy) {
1507 id agent = [copy objectForKey:entity];
1509 [self destroyFloatingAgent:agent];
1514 [self processDNSResolvers:dns_config];
1515 [self processScopedDNSResolvers:dns_config];
1516 [self processSupplementalDNSResolvers:dns_config];
1517 [self processServiceSpecificDNSResolvers:dns_config];
1521 [self processOnionResolver:dns_config];
1522 if (dns_config != NULL) {
1523 dns_configuration_free(dns_config);
1527 #pragma mark Helper functions
1529 - (const void *)copyConfigAgentData:(NSMutableDictionary *)controllerDict
1530 uuid:(uuid_t)requested_uuid
1531 length:(uint64_t *)length
1533 if (length == NULL) {
1534 SC_log(LOG_NOTICE, "Invalid parameters for copying agent data");
1539 void *buffer = NULL;
1542 for (NSString *key in controllerDict) {
1543 id temp_agent = [controllerDict objectForKey:key];
1547 [[temp_agent getAgentUUID] getUUIDBytes:agent_uuid];
1548 if (uuid_compare(agent_uuid, requested_uuid) == 0) {
1555 uuid_string_t uuid_str;
1556 uuid_unparse(requested_uuid, uuid_str);
1557 SC_log(LOG_NOTICE, "Invalid config agent uuid %s specified", uuid_str);
1561 NSData *data = [agent getAgentData];
1562 uint64_t len = [data length];
1565 buffer = malloc((size_t)len);
1566 memcpy(buffer, [data bytes], len);
1569 return (const void *)buffer;
1572 - (const void *)copyProxyAgentData:(uuid_t)requested_uuid
1573 length:(uint64_t *)length
1575 return [self copyConfigAgentData:self.floatingProxyAgentList
1580 - (const void *)copyDNSAgentData:(uuid_t)requested_uuid
1581 length:(uint64_t *)length
1583 return [self copyConfigAgentData:self.floatingDNSAgentList
1588 - (NSData *)dataLengthSanityCheck:(id)agent
1590 NSData * data = [agent getAgentData];
1592 if ([data length] > CONFIG_AGENT_DATA_LIMIT) {
1593 /* We impose a limit on the config agent data as 1KB.
1594 * If we have a data blob larger than this limit, do NOT publish it into the agent.
1595 * Instead publish a key which will trigger fetching of the configuration directly
1596 * through NWI server.
1598 NSMutableDictionary *data_dict = [NSMutableDictionary dictionary];
1600 NSUUID *uuid = [agent getAgentUUID];
1602 [uuid getUUIDBytes:c_uuid];
1603 NSData *uuid_data = [[NSData alloc] initWithBytes:c_uuid length:sizeof(c_uuid)];
1604 [data_dict setValue:uuid_data forKey:@kConfigAgentOutOfBandDataUUID];
1606 NSData *new_data = [NSPropertyListSerialization dataWithPropertyList:data_dict
1607 format:NSPropertyListBinaryFormat_v1_0
1618 * For conflicting agents, the convention is that its name & entity,
1619 * will have a suffix " #<number>". This function will sanitize the
1620 * suffix and just return the entity name
1622 - (NSString *)sanitizeEntity:(NSString *)entity
1624 NSRange range = [entity rangeOfString:@multipleEntitySuffix];
1625 if (range.location != NSNotFound) {
1626 NSString *str = [entity substringToIndex:range.location];
1634 * For interface names, there is a prefix to differentiate then
1635 * from the domain name (iff there were conflicting domain names).
1636 * Returns the sanitized interface name
1638 - (NSString *)sanitizeInterfaceName:(NSString *)intf
1640 NSRange range = [intf rangeOfString:@prefixForInterfaceName];
1641 if (range.location != NSNotFound) {
1642 NSString *str = [intf substringFromIndex:(range.location + strlen(prefixForInterfaceName))];
1650 * For conflicting agents, the convention is that its name & entity,
1651 * will have a suffix " #<number>". This function will return that <number>
1653 - (int)entityInstanceNumber:(NSString *)entity
1655 NSRange range = [entity rangeOfString:@multipleEntitySuffix];
1656 if (range.location != NSNotFound) {
1657 NSString *str = [entity substringFromIndex:(range.location + strlen(multipleEntitySuffix))];
1658 return str.intValue;
1665 * In case that we have conflicting DNS/Proxy domains
1666 * This function will remove all those conflicting agents,
1667 * so that we can start afresh with the new config
1669 - (void)cleanConflictingAgentsFromList:(NSMutableArray *)old_list
1670 new_list:(NSMutableArray *)new_list
1671 agentDictionary:(NSMutableDictionary *)agent_list
1673 NSCountedSet * duplicate_domain_list;
1675 for (NSString *domain in old_list) {
1676 /* If we had conflicting domains before, remove all of them */
1677 NSString *sanitizedDomain = [self sanitizeEntity:domain];
1678 if (![sanitizedDomain isEqualToString:domain]) {
1679 /* Destroy the original domain */
1680 id agent = [agent_list objectForKey:sanitizedDomain];
1681 [self destroyFloatingAgent:agent];
1683 /* Destroy the conflicting domain */
1684 agent = [agent_list objectForKey:domain];
1685 [self destroyFloatingAgent:agent];
1687 SC_log(LOG_INFO, "Removing conflicting domain: %@, %@", sanitizedDomain, domain);
1691 duplicate_domain_list = [[NSCountedSet alloc] initWithArray:new_list];
1692 for (NSString *domain in old_list) {
1693 if ([duplicate_domain_list countForObject:domain] > 1) {
1694 id agent = [agent_list objectForKey:domain];
1695 [self destroyFloatingAgent:agent];
1696 SC_log(LOG_INFO, "Removing domain %@ as it has duplicates in the current config", domain);
1702 * Get the list of agents from a specific dictionary.
1703 * The list of agents will only consist of the ones which
1704 * match the agent type and sub-type
1707 - (NSMutableArray *)getAgentList:(NSMutableDictionary *)all_agents
1708 agentType:(AgentType)type
1709 agentSubType:(AgentSubType)subtype
1711 NSMutableArray *list = [NSMutableArray array];
1712 NSArray *agentObjects = [all_agents allValues];
1714 for (id agent in agentObjects) {
1715 if (([agent getAgentType] == type) &&
1716 ([agent getAgentSubType] == subtype)) {
1718 [list addObject:[agent getAssociatedEntity]];
1726 * Destroy all the agents are listed in "list"
1729 - (void)deleteAgentList:(NSMutableDictionary *)all_agents
1730 list:(NSMutableArray *)list
1732 for (NSString *intf in list) {
1735 agent = [all_agents objectForKey:intf];
1736 [self destroyFloatingAgent:agent];
1741 * In order to not duplicate agents with same content,
1742 * we map an agent X to agent Y, when their content is the same.
1744 * This function tries to find that agent Y
1747 - (id)getAgentWithSameDataAndSubType:(NSMutableDictionary *)agentList
1749 subType:(AgentSubType)subtype
1751 for (NSString *key in agentList) {
1752 id agent = [agentList objectForKey:key];
1753 if ([[agent getAgentData] isEqual:data]) {
1754 /* Do not map to default agents */
1755 if ([agent getAgentSubType] != subtype) {
1759 /* Return only registered agents */
1760 if ([agent getRegistrationObject] != nil) {
1769 #pragma mark Policy installation function
1772 * Add NECP policies for an agent
1774 - (BOOL)addPolicyToFloatingAgent:(id)agent
1775 domain:(NSString *)domain
1776 agentUUIDToUse:(NSUUID *)uuid
1777 policyType:(NEPolicyConditionType)policyType
1779 NEPolicyCondition * condition = nil;
1780 uint32_t multiple_entity_offset;
1781 NEPolicy * newPolicy;
1784 uint32_t orderForSkip;
1785 NSMutableArray * policyArray;
1786 NSUInteger policyID1;
1787 NSUInteger policyID2;
1788 NEPolicyResult * result;
1791 uint32_t typeOffset;
1793 type = [agent getAgentType];
1794 typeOffset = (type == kAgentTypeDNS) ? 0 : 5000;
1795 skipOrder = (type == kAgentTypeDNS) ? 5000 : 0;
1797 multiple_entity_offset = (uint32_t)[self entityInstanceNumber:domain];
1798 domain = [self sanitizeEntity:domain];
1800 switch (policyType) {
1801 case NEPolicyConditionTypeScopedInterface:
1802 order = INIT_ORDER_FOR_SCOPED_INTERFACE_POLICY + typeOffset + multiple_entity_offset;
1803 domain = [self sanitizeInterfaceName:domain];
1804 condition = [NEPolicyCondition scopedInterface:domain];
1805 orderForSkip = SKIP_ORDER_FOR_SCOPED_INTERFACE_POLICY + typeOffset;
1808 case NEPolicyConditionTypeDomain:
1809 order = INIT_ORDER_FOR_DOMAIN_POLICY + typeOffset + multiple_entity_offset;
1810 condition = [NEPolicyCondition domain:domain];
1811 orderForSkip = SKIP_ORDER_FOR_DOMAIN_POLICY + typeOffset;
1814 case NEPolicyConditionTypeNone:
1815 order = INIT_ORDER_FOR_DEFAULT_POLICY + typeOffset + multiple_entity_offset;
1816 orderForSkip = SKIP_ORDER_FOR_DEFAULT_POLICY + typeOffset;
1820 SC_log(LOG_NOTICE, "Invalid policy condition specified");
1824 result = [NEPolicyResult netAgentUUID:uuid];
1825 newPolicy = [[NEPolicy alloc] initWithOrder:order
1827 conditions: (condition ? @[condition] : nil)];
1829 if (newPolicy == nil) {
1830 SC_log(LOG_NOTICE, "Could not create a policy for agent %@", [agent getAgentName]);
1834 policyID1 = [self.policySession addPolicy:newPolicy];
1835 if (policyID1 == 0) {
1836 SC_log(LOG_NOTICE, "Could not add a netagent policy for agent %@", [agent getAgentName]);
1840 result = [NEPolicyResult skipWithOrder:skipOrder];
1841 newPolicy = [[NEPolicy alloc] initWithOrder:orderForSkip
1843 conditions:(condition ? @[condition] : nil)];
1845 if (newPolicy == nil) {
1846 SC_log(LOG_NOTICE, "Could not create a policy for agent %@", [agent getAgentName]);
1850 policyID2 = [self.policySession addPolicy:newPolicy];
1851 if (policyID2 == 0) {
1852 SC_log(LOG_NOTICE, "Could not add a skip policy for agent %@", [agent getAgentName]);
1856 ok = [self.policySession apply];
1858 SC_log(LOG_NOTICE, "Could not apply policy for agent %@", [agent getAgentName]);
1862 policyArray = [self.policyDB objectForKey:[agent getAgentName]];
1863 if (policyArray == nil) {
1864 policyArray = [NSMutableArray array];
1867 [policyArray addObject:numberToNSNumber(policyID1)];
1868 [policyArray addObject:numberToNSNumber(policyID2)];
1869 [self.policyDB setObject:policyArray forKey:[agent getAgentName]];
1874 #pragma mark Agent manipulation functions
1879 - (BOOL)spawnFloatingAgent:(Class)agentClass
1880 entity:(NSString *)entity
1881 agentSubType:(AgentSubType)subtype
1882 addPolicyOfType:(NEPolicyConditionType)policyType
1883 publishData:(NSData *)data
1887 NSMutableDictionary * parameters;
1889 parameters =[NSMutableDictionary dictionary];
1890 [parameters setValue:entity forKey:@kEntityName];
1891 [parameters setValue:numberToNSNumber(subtype) forKey:@kAgentSubType];
1893 agent = [[agentClass alloc] initWithParameters:parameters];
1894 ok = [self registerAgent:agent];
1900 /* Since we just spawned this agent, update its data */
1901 [agent updateAgentData:data];
1902 [self publishToAgent:agent];
1905 /* Add a policy if there is a valid type. If POLICY_TYPE_NO_POLICY, then ignore policies.
1906 * POLICY_TYPE_NO_POLICY will be set for service-specific agents, in which case we rely on
1907 * service owners to install custom policies to point at the agents. */
1908 if (policyType >= NEPolicyResultTypeNone) {
1909 ok = [self addPolicyToFloatingAgent:agent
1911 agentUUIDToUse:[agent agentUUID]
1912 policyType:policyType];
1915 [self unregisterAgent:agent];
1920 SC_log(LOG_INFO, "Spawning floating agent for %@", entity);
1922 if ([agent getAgentType] == kAgentTypeProxy) {
1923 [self.floatingProxyAgentList setObject:agent forKey:entity];
1925 [self.floatingDNSAgentList setObject:agent forKey:entity];
1932 * Create an agent mapped to another agent.
1934 - (BOOL)spawnMappedFloatingAgent:(id)mapped_agent
1935 entity:(NSString *)entity
1936 agentSubType:(AgentSubType)subtype
1937 addPolicyOfType:(NEPolicyConditionType)policyType
1938 updateData:(NSData *)data
1941 NSMutableDictionary * parameters;
1943 parameters = [NSMutableDictionary dictionary];
1944 [parameters setValue:entity forKey:@kEntityName];
1945 [parameters setValue:numberToNSNumber(subtype) forKey:@kAgentSubType];
1947 dummyAgent = [[[mapped_agent class] alloc] initWithParameters:parameters];
1950 /* Since we just spawned this agent, update its data.
1951 * We do not publish it since this agent is mapped
1952 * to an agent which already has the same data
1954 [dummyAgent updateAgentData:data];
1957 BOOL ok = [self addPolicyToFloatingAgent:dummyAgent
1959 agentUUIDToUse:[mapped_agent agentUUID]
1960 policyType:policyType];
1966 if ([mapped_agent getAgentType] == kAgentTypeProxy) {
1967 [self.floatingProxyAgentList setObject:dummyAgent forKey:entity];
1969 [self.floatingDNSAgentList setObject:dummyAgent forKey:entity];
1972 [dummyAgent setAgentMapping:mapped_agent];
1974 SC_log(LOG_INFO, "Mapped floating agent %@ to %@", [dummyAgent getAgentName], [mapped_agent getAgentName]);
1979 * Write into an agent
1981 - (BOOL)publishToAgent:(id)agent
1983 /* Before any data goes into the kernel, do a sanity check. */
1984 NSData *sanityCheckData = [self dataLengthSanityCheck:agent];
1985 NSData *tempAgentData = nil;
1987 if (sanityCheckData != nil) {
1988 /* Data length is more than the limit! for updateNetworkAgent, the data blob
1989 * has to be a part of the agent object. Thus the temporary data replacement!
1991 tempAgentData = [[agent getAgentData] copy];
1992 [agent updateAgentData:sanityCheckData];
1993 SC_log(LOG_NOTICE, "Data too large for %@ (%lu bytes)!", [agent getAgentName], (unsigned long)[tempAgentData length]);
1998 NWNetworkAgentRegistration *regObject = [agent valueForKey:@"registrationObject"];
1999 if (regObject != nil) {
2000 SC_log(LOG_NOTICE, "Publishing data to agent %@ (%lu bytes)", [agent getAgentName], (unsigned long)[[agent getAgentData] length]);
2001 ok = [regObject updateNetworkAgent:agent];
2003 SC_log(LOG_NOTICE, "Could not update config agent");
2006 SC_log(LOG_NOTICE, "Config Agent not registered. Cannot Update");
2009 if (tempAgentData != nil) {
2010 [agent updateAgentData:tempAgentData];
2019 - (BOOL)destroyFloatingAgent:(id)agent
2023 if ( agent != nil) {
2024 NSMutableArray * policyArray;
2026 policyArray = [self.policyDB objectForKey:[agent getAgentName]];
2027 if (policyArray != nil) {
2030 for (NSNumber *policyID in policyArray) {
2033 idVal = [policyID unsignedIntegerValue];
2034 result = [self.policySession removePolicyWithID:idVal];
2036 SC_log(LOG_NOTICE, "Could not remove policy %@ for agent %@", [self.policySession policyWithID:idVal], [agent getAgentName]);
2040 result = [self.policySession apply];
2042 SC_log(LOG_NOTICE, "Could not apply removed policies for agent %@", [agent getAgentName]);
2045 [self.policyDB removeObjectForKey:[agent getAgentName]];
2048 if ([agent getAgentType] == kAgentTypeProxy) {
2049 [self.floatingProxyAgentList removeObjectForKey:[agent getAssociatedEntity]];
2051 [self.floatingDNSAgentList removeObjectForKey:[agent getAssociatedEntity]];
2054 if ([agent getRegistrationObject] != nil) {
2055 [self unregisterAgent:agent];
2058 SC_log(LOG_INFO, "X - Destroyed agent %@", [agent getAgentName]);
2068 - (BOOL)registerAgent:(id)agent
2072 NWNetworkAgentRegistration *registration = [[NWNetworkAgentRegistration alloc] initWithNetworkAgentClass:[agent class]];
2074 ok = [registration registerNetworkAgent:agent];
2076 SC_log(LOG_NOTICE, "Could not register config agent");
2080 [agent addAgentRegistrationObject:registration];
2087 * Unregister an agent
2089 - (BOOL)unregisterAgent:(id)agent
2093 NWNetworkAgentRegistration *regObject = [agent valueForKey:@"registrationObject"];
2094 if (regObject != nil) {
2095 ok = [regObject unregisterNetworkAgent];
2097 SC_log(LOG_NOTICE, "Could not unregister config agent");
2100 SC_log(LOG_NOTICE, "Config Agent not registered. Cannot unregister");