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;
61 @property (nonatomic) NEPolicySession * controlPolicySession;
65 @implementation AgentController
69 + (AgentController *)sharedController
71 static AgentController * gController = nil;
72 static dispatch_once_t onceToken;
74 dispatch_once(&onceToken, ^{
75 gController = [[AgentController alloc] init];
78 @synchronized (gController) {
79 if (![gController isControllerReady]) {
80 if (![gController initializeController]) {
93 [self initializeController];
99 - (BOOL)initializeController
101 const char *errorMessage = NULL;
104 /* The NE policy session for the controller */
106 if (self.policySession == nil) {
107 self.policySession = [self createPolicySession];
108 if (self.policySession == nil) {
109 errorMessage = "Failed to create a policy session";
114 /* A dictionary of all floating proxy agents
115 * Key : <entity-name> (can be an interface name or domain name)
116 * Value : agent object
119 if (self.floatingProxyAgentList == nil) {
120 self.floatingProxyAgentList = [NSMutableDictionary dictionary];
121 if (self.floatingProxyAgentList == nil) {
122 errorMessage = "Failed to create a dictionary";
127 /* A dictionary of all floating dns agents
128 * Key : <entity-name> (can be an interface name or domain name)
129 * Value : agent object
132 if (self.floatingDNSAgentList == nil) {
133 self.floatingDNSAgentList = [NSMutableDictionary dictionary];
134 if (self.floatingDNSAgentList == nil) {
135 errorMessage = "Failed to create a dictionary";
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
146 if (self.policyDB == nil) {
147 self.policyDB = [NSMutableDictionary dictionary];
148 if (self.policyDB == nil) {
149 errorMessage = "Failed to create a dictionary";
154 /* The queue to run the all processing on */
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";
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);
175 - (NEPolicySession *)createPolicySession
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
183 /* Create kernel control socket */
185 struct ctl_info kernctl_info;
186 struct sockaddr_ctl kernctl_addr;
187 const char *controlName = NECP_CONTROL_NAME;
189 if ((sock = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL)) < 0)
191 SC_log(LOG_NOTICE, "Cannot create kernel control socket (errno = %d)\n", errno);
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))
199 SC_log(LOG_NOTICE, "ioctl failed on kernel control socket (errno = %d)\n", errno);
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)))
212 SC_log(LOG_NOTICE, "connect failed on kernel control socket (errno = %d)\n", errno);
217 /* Create policy session */
218 session = [[NEPolicySession alloc] initWithSocket:sock];
219 if (session == nil) {
222 #else //!TARGET_OS_IPHONE
223 session = [[NEPolicySession alloc] init];
224 #endif //!TARGET_OS_IPHONE
229 - (BOOL)isControllerReady
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));
239 /* ========================== proxy agent helpers =========================== */
240 #pragma mark Proxy agent helper functions
242 - (NSData *)dataForProxyArray:(CFArrayRef)proxy_array_for_data
244 NSData *data = [NSPropertyListSerialization dataWithPropertyList:(__bridge id _Nonnull)(proxy_array_for_data)
245 format:NSPropertyListBinaryFormat_v1_0
252 - (NSData *)dataForProxyDictionary:(CFDictionaryRef)domain_proxy
255 CFMutableDictionaryRef domain_proxy_dict;
256 CFArrayRef domain_proxy_array;
258 if (domain_proxy == NULL) {
259 SC_log(LOG_NOTICE, "Invalid domain proxy dict");
263 domain_proxy_dict = CFDictionaryCreateMutableCopy(NULL, 0, domain_proxy);
264 CFDictionaryRemoveValue(domain_proxy_dict, kSCPropNetProxiesSupplementalMatchDomain);
266 domain_proxy_array = CFArrayCreate(NULL, (const void **)&domain_proxy_dict, 1, &kCFTypeArrayCallBacks);
267 CFRelease(domain_proxy_dict);
269 data = [self dataForProxyArray:domain_proxy_array];
270 CFRelease(domain_proxy_array);
275 - (NSData *)getProxyDataFromCurrentConfig:(CFDictionaryRef)proxies
276 domain:(NSString *)domain
280 CFArrayRef supplemental;
282 if (proxies == NULL || domain == nil) {
283 SC_log(LOG_NOTICE, "Invalid proxies/domain");
287 supplemental = CFDictionaryGetValue(proxies, kSCPropNetProxiesSupplemental);
288 count = supplemental ? CFArrayGetCount(supplemental) : 0;
290 for (idx = 0; idx < count; idx++) {
291 CFDictionaryRef domain_proxy;
292 CFStringRef match_domain;
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];
304 - (bool)getIntValue:(CFTypeRef)cf_value
305 valuePtr:(int *) int_value_ptr
308 if (cf_value && CFGetTypeID(cf_value) == CFNumberGetTypeID() && CFNumberGetValue(cf_value, kCFNumberIntType, int_value_ptr))
315 - (int)countProxyEntriesEnabled:(CFDictionaryRef)proxies
319 if (proxies == NULL) {
320 SC_log(LOG_NOTICE, "Invalid proxies");
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)) {
338 - (void)processSupplementalProxyChanges:(CFDictionaryRef)proxies
341 NSMutableArray * deleteList;
342 NSCountedSet * duplicate_domain_list;
344 NSMutableArray * new_domain_list;
345 NSMutableArray * old_domain_list;
346 CFArrayRef supplemental;
347 NSMutableArray * update_agent_list;
349 if (proxies == NULL) {
350 SC_log(LOG_INFO, "No proxy config to process");
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];
364 for (idx = 0; idx < count; idx++) {
365 CFDictionaryRef domain_proxy;
366 CFStringRef match_domain;
369 domain_proxy = CFArrayGetValueAtIndex(supplemental, idx);
370 match_domain = CFDictionaryGetValue(domain_proxy, kSCPropNetProxiesSupplementalMatchDomain);
371 if (match_domain == NULL) {
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.
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! :)
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);
390 [new_domain_list addObject:(__bridge NSString *)match_domain];
393 [self cleanConflictingAgentsFromList:old_domain_list
394 new_list:new_domain_list
395 agentDictionary:self.floatingProxyAgentList];
397 for (NSString *key in old_domain_list) {
400 domain_present = [new_domain_list containsObject:key];
401 if (domain_present == NO) {
404 agent = [self.floatingProxyAgentList objectForKey:key];
405 [self destroyFloatingAgent:agent];
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
414 for (NSString *domain in old_domain_list) {
418 agent = [self.floatingProxyAgentList objectForKey:domain];
423 /* Am I mapped to some agent? */
424 mapped_agent = [agent getAgentMapping];
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.
430 NSData * mapped_agent_data;
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];
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.
443 * All the agents which were mapped to this agent, will be deleted and
444 * re-mapped, if the data changed.
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];
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.
457 [update_agent_list addObject:agent];
460 [new_domain_list removeObject:domain];
463 for (id agent in deleteList) {
464 SC_log(LOG_INFO, "Destroying agent %@ because something changed!", [agent getAgentName]);
465 [self destroyFloatingAgent:agent];
468 for (id agent in update_agent_list) {
469 [self publishToAgent:agent];
472 for (idx = 0; idx < count; idx++) {
473 CFDictionaryRef domain_proxy;
474 CFStringRef match_domain;
476 domain_proxy = CFArrayGetValueAtIndex(supplemental, idx);
477 match_domain = CFDictionaryGetValue(domain_proxy, kSCPropNetProxiesSupplementalMatchDomain);
479 if (match_domain != NULL) {
484 found = [new_domain_list indexOfObject:(__bridge id _Nonnull)(match_domain)];
485 if (found == NSNotFound) {
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.
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 */
498 NSString *ns_domain_name_copy = [NSString stringWithFormat:@"%@" multipleEntitySuffix "%lu", match_domain, (unsigned long)domainInstance];
500 data = [self dataForProxyDictionary:domain_proxy];
502 BOOL ok = [self spawnFloatingAgent:[ProxyAgent class]
503 entity:ns_domain_name_copy
504 agentSubType:kAgentSubTypeSupplemental
505 addPolicyOfType:NEPolicyConditionTypeDomain
508 id agent = [self.floatingProxyAgentList objectForKey:ns_domain_name_copy];
509 SC_log(LOG_INFO, "Duplicate Proxy agent %@", [agent getAgentName]);;
512 data = [self dataForProxyDictionary:domain_proxy];
513 mapped_agent = [self getAgentWithSameDataAndSubType:self.floatingProxyAgentList
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
523 [self spawnFloatingAgent:[ProxyAgent class]
524 entity:(__bridge NSString *)(match_domain)
525 agentSubType:kAgentSubTypeSupplemental
526 addPolicyOfType:NEPolicyConditionTypeDomain
531 [new_domain_list removeObjectAtIndex:found];
532 [duplicate_domain_list addObject:(__bridge id _Nonnull)(match_domain)];
539 - (void)processScopedProxyChanges:(CFDictionaryRef)proxies
541 NSMutableArray * old_intf_list;
542 CFDictionaryRef scoped_proxies;
543 CFIndex scoped_proxies_count;
545 old_intf_list = [self getAgentList:self.floatingProxyAgentList
546 agentType:kAgentTypeProxy
547 agentSubType:kAgentSubTypeScoped];
549 scoped_proxies = CFDictionaryGetValue(proxies, kSCPropNetProxiesScoped);
550 scoped_proxies_count = scoped_proxies ? CFDictionaryGetCount(scoped_proxies) : 0;
552 if (scoped_proxies_count > 0) {
555 keys = malloc(scoped_proxies_count * sizeof(void *));
556 CFDictionaryGetKeysAndValues(scoped_proxies, keys, NULL);
558 for (int i = 0; i < scoped_proxies_count; i++) {
562 NSString * ns_if_name;
563 NSString * ns_if_name_with_prefix;
567 ns_if_name = (__bridge NSString *)keys[i];
568 ns_if_name_with_prefix = [NSString stringWithFormat:@"%s%@", prefixForInterfaceName, ns_if_name];
570 /* Does the proxy config have any protocols enabled? */
571 proxy_count = [self countProxyEntriesEnabled:CFDictionaryGetValue(scoped_proxies,
572 (__bridge const void *)(ns_if_name))];
574 if (proxy_count == 0) {
575 SC_log(LOG_INFO, "Proxy settings on %@ are generic. Skipping", ns_if_name);
579 idx = [old_intf_list indexOfObject:ns_if_name_with_prefix];
581 matching = SCNetworkProxiesCopyMatching(proxies, NULL, (__bridge CFStringRef)(ns_if_name));
582 if (matching != NULL) {
583 data = [self dataForProxyArray:matching];
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
597 /* We have an agent for this interface. Update it */
598 [old_intf_list removeObjectAtIndex:idx];
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];
614 [self deleteAgentList:self.floatingProxyAgentList list:old_intf_list];
617 - (void)processServiceSpecificProxyChanges:(CFDictionaryRef)proxies
619 NSMutableArray * old_service_list;
620 CFDictionaryRef service_proxies;
621 CFIndex service_proxies_count;
623 old_service_list = [self getAgentList:self.floatingProxyAgentList
624 agentType:kAgentTypeProxy
625 agentSubType:kAgentSubTypeServiceSpecific];
627 service_proxies = CFDictionaryGetValue(proxies, kSCPropNetProxiesServices);
628 service_proxies_count = service_proxies ? CFDictionaryGetCount(service_proxies) : 0;
630 if (service_proxies_count > 0) {
633 keys = malloc(service_proxies_count * sizeof(void *));
634 CFDictionaryGetKeysAndValues(service_proxies, keys, NULL);
636 for (int i = 0; i < service_proxies_count; i++) {
639 NSString * ns_service_identifier = nil;
640 NSString * ns_service_with_prefix = nil;
643 CFDictionaryRef proxyDict = NULL;
645 ns_service_identifier = (__bridge NSString *)keys[i];
646 ns_service_with_prefix = [NSString stringWithFormat:@"%s%@", prefixForInterfaceName, ns_service_identifier];
648 /* Does the proxy config have any protocols enabled? */
649 proxy_count = [self countProxyEntriesEnabled:CFDictionaryGetValue(service_proxies,
650 (__bridge const void *)(ns_service_identifier))];
652 if (proxy_count == 0) {
653 SC_log(LOG_INFO, "Proxy settings on %@ are generic. Skipping", ns_service_identifier);
657 proxyDict = CFDictionaryGetValue(service_proxies, (__bridge CFStringRef)ns_service_identifier);
658 if (proxyDict != nil) {
659 data = [self dataForProxyArray:(__bridge CFArrayRef)(@[ (__bridge NSDictionary *)proxyDict ])];
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 */
673 /* We have an agent for this service. Update it */
674 [old_service_list removeObjectAtIndex:idx];
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];
690 [self deleteAgentList:self.floatingProxyAgentList list:old_service_list];
693 - (BOOL)isGlobalProxy:(CFDictionaryRef)proxies
695 if (CFDictionaryContainsKey(proxies, kSCPropNetProxiesBypassAllowed)) {
697 * Since we did not ask to "bypass" the proxies, this key will always
698 * be present in a managed (global) proxy configuration
706 - (void)processDefaultProxyChanges:(CFDictionaryRef)proxies
708 CFArrayRef global_proxy;
709 CFIndex global_proxy_count;
710 CFMutableDictionaryRef proxies_copy;
712 proxies_copy = CFDictionaryCreateMutableCopy(NULL, 0, proxies);
713 CFDictionaryRemoveValue(proxies_copy, kSCPropNetProxiesScoped);
714 CFDictionaryRemoveValue(proxies_copy, kSCPropNetProxiesServices);
715 CFDictionaryRemoveValue(proxies_copy, kSCPropNetProxiesSupplemental);
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;
724 CFRelease(proxies_copy);
726 if (global_proxy_count > 0) {
727 BOOL spawnAgent = YES;
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];
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;
750 [self spawnFloatingAgent:[ProxyAgent class]
751 entity:@proxyAgentDefault
753 addPolicyOfType:conditionType
757 /* No default proxy config OR generic (no protocols enabled) default proxy config.
758 * Destroy the default agent if we had one
762 proxyAgent = [self.floatingProxyAgentList objectForKey:@proxyAgentDefault];
763 if (proxyAgent != nil) {
764 [self destroyFloatingAgent:proxyAgent];
768 CFRelease(global_proxy);
771 - (void)processProxyChanges
773 CFDictionaryRef proxies;
775 proxies = SCDynamicStoreCopyProxiesWithOptions(NULL, NULL);
776 if (proxies == NULL) {
777 SC_log(LOG_INFO, "No proxy information");
779 NSMutableDictionary *copy = [self.floatingProxyAgentList copy];
780 for (NSString *entity in copy) {
781 id agent = [copy objectForKey:entity];
782 [self destroyFloatingAgent:agent];
788 [self processDefaultProxyChanges:proxies];
789 [self processScopedProxyChanges:proxies];
790 [self processSupplementalProxyChanges:proxies];
791 [self processServiceSpecificProxyChanges:proxies];
796 /* ========================== DNS agent helpers =========================== */
797 #pragma mark DNS agent helper functions
799 - (void)freeResolverList:(resolver_list_t *)resolvers
801 /* This is a shallow free of resolver_list_t only.
802 * The actual resolver pointers are owned by 'dns_config'
804 if (resolvers == NULL) {
808 if (resolvers->default_resolvers != NULL) {
809 free(resolvers->default_resolvers);
811 if (resolvers->multicast_resolvers != NULL) {
812 free(resolvers->multicast_resolvers);
814 if (resolvers->private_resolvers != NULL) {
815 free(resolvers->private_resolvers);
821 - (resolver_list_t *)copyResolverList:(dns_config_t *)dns_config
823 resolver_list_t *resolvers = NULL;
825 if ((dns_config->n_resolver > 0) && (dns_config->resolver != NULL)) {
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];
834 if ([self isResolverMulticast:r]) {
835 resolvers->n_multicast_resolvers++;
838 } else if ([self isResolverPrivate:r]) {
839 resolvers->n_private_resolvers++;
843 // do not consider default resolvers with no nameservers
844 if (r->domain == NULL && r->n_nameserver > 0) {
845 resolvers->n_default_resolvers++;
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);
854 if (resolvers->n_default_resolvers > 0) {
855 resolvers->default_resolvers = calloc(resolvers->n_default_resolvers,
856 sizeof(dns_resolver_t *));
858 if (resolvers->n_multicast_resolvers > 0) {
859 resolvers->multicast_resolvers = calloc(resolvers->n_multicast_resolvers,
860 sizeof(dns_resolver_t *));
862 if (resolvers->n_private_resolvers > 0) {
863 resolvers->private_resolvers = calloc(resolvers->n_private_resolvers,
864 sizeof(dns_resolver_t *));
867 for (int i = 0; i < dns_config->n_resolver; i++) {
868 dns_resolver_t *r = dns_config->resolver[i];
870 if ([self isResolverMulticast:r] &&
871 (a < resolvers->n_multicast_resolvers)) {
872 resolvers->multicast_resolvers[a++] = r;
875 } else if ([self isResolverPrivate:r] &&
876 (b < resolvers->n_private_resolvers)) {
877 resolvers->private_resolvers[b++] = r;
881 if ((r->domain == NULL) &&
882 (r->n_nameserver > 0) &&
883 (c < resolvers->n_default_resolvers)) {
884 resolvers->default_resolvers[c++] = r;
893 * Generate a data blob for the resolver.
894 * Currently the blob only has:
896 * - sockaddr structs for each nameserver
900 - (NSData *)dataForResolver:(dns_resolver_t *)resolver
903 CFMutableDictionaryRef resolverDict = nil;
905 if (resolver == NULL) {
906 SC_log(LOG_NOTICE, "Invalid dns resolver");
910 if (resolver->n_search > 0) {
911 if (resolverDict == nil) {
912 resolverDict = CFDictionaryCreateMutable(NULL,
914 &kCFTypeDictionaryKeyCallBacks,
915 &kCFTypeDictionaryValueCallBacks);
918 CFMutableArrayRef searchDomainArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
920 /* Append search domains */
921 for (int i = 0; i < resolver->n_search; i++) {
922 CFArrayAppendValue(searchDomainArray, (__bridge CFStringRef)(@(resolver->search[i])));
925 CFDictionaryAddValue(resolverDict, CFSTR(kConfigAgentDNSSearchDomains), searchDomainArray);
926 CFRelease(searchDomainArray);
929 /* Get the count of nameservers */
930 if (resolver->n_nameserver > 0) {
931 if (resolverDict == nil) {
932 resolverDict = CFDictionaryCreateMutable(NULL,
934 &kCFTypeDictionaryKeyCallBacks,
935 &kCFTypeDictionaryValueCallBacks);
938 CFMutableArrayRef nameserverArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
940 /* Get all the nameservers */
941 for (int i = 0; i < resolver->n_nameserver; i++) {
943 _SC_sockaddr_to_string(resolver->nameserver[i], buf, sizeof(buf));
945 CFArrayAppendValue(nameserverArray, (__bridge CFStringRef)(@(buf)));
949 CFDictionaryAddValue(resolverDict, CFSTR(kConfigAgentDNSNameServers), nameserverArray);
950 CFRelease(nameserverArray);
953 if (resolverDict != nil) {
954 data = [NSPropertyListSerialization dataWithPropertyList:(__bridge id _Nonnull)(resolverDict)
955 format:NSPropertyListBinaryFormat_v1_0
959 CFRelease(resolverDict);
962 return (NSData *)data;
965 - (NSData *)getDNSDataFromCurrentConfig:(dns_config_t *)dns_config
966 domain:(NSString *)domain
968 if (dns_config == NULL || domain == nil) {
969 SC_log(LOG_NOTICE, "Invalid dns_config/domain");
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;
977 resolver = dns_config->resolver[i];
978 if (resolver->domain != NULL &&
979 ![self isResolverMulticast:resolver]) {
980 NSString * ns_domain_name;
982 ns_domain_name = @(resolver->domain);
983 if ([ns_domain_name isEqualToString:domain]) {
984 return [self dataForResolver:resolver];
995 - (BOOL)isResolverMulticast:(dns_resolver_t *)resolver
997 if (resolver->options == NULL) {
1001 if (!strstr(resolver->options, "mdns")) {
1008 - (BOOL)isResolverPrivate:(dns_resolver_t *)resolver
1010 if (resolver->options == NULL) {
1014 if (!strstr(resolver->options, "pdns")) {
1021 - (void)processSupplementalDNSResolvers:(dns_config_t *)dns_config
1023 NSMutableArray * deleteList;
1024 NSMutableArray * new_domain_list;
1025 NSCountedSet * duplicate_domain_list;
1026 NSMutableArray * old_domain_list;
1027 NSMutableArray * update_agent_list;
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];
1038 if (dns_config->resolver == NULL) {
1039 dns_config->n_resolver = 0;
1041 if (dns_config->n_resolver > 0) {
1042 for (int i = 0; i < dns_config->n_resolver; i++) {
1043 dns_resolver_t * resolver;
1045 resolver = dns_config->resolver[i];
1046 if (resolver->domain != NULL &&
1047 ![self isResolverPrivate:resolver] &&
1048 ![self isResolverMulticast:resolver]) {
1049 NSString * ns_domain_name;
1051 ns_domain_name = [NSString stringWithCString:resolver->domain encoding:NSASCIIStringEncoding];
1052 [new_domain_list addObject:ns_domain_name];
1057 [self cleanConflictingAgentsFromList:old_domain_list
1058 new_list:new_domain_list
1059 agentDictionary:self.floatingDNSAgentList];
1061 /* Sync between controller and current config */
1062 for (NSString *key in old_domain_list) {
1063 BOOL domain_present = NO;
1065 domain_present = [new_domain_list containsObject:key];
1066 if (domain_present == NO) {
1069 agent = [self.floatingDNSAgentList objectForKey:key];
1070 [self destroyFloatingAgent:agent];
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
1079 for (NSString *domain in old_domain_list) {
1083 agent = [self.floatingDNSAgentList objectForKey:domain];
1088 /* Am I mapped to some agent? */
1089 mapped_agent = [agent getAgentMapping];
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.
1095 NSData *mapped_agent_data;
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];
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.
1108 * All the agents which were mapped to this agent, will be deleted and
1109 * re-mapped, if the data changed.
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];
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.
1122 [update_agent_list addObject:agent];
1126 [new_domain_list removeObject:domain];
1129 for (id agent in deleteList) {
1130 SC_log(LOG_INFO, "Destroying agent %@ because something changed!", [agent getAgentName]);
1131 [self destroyFloatingAgent:agent];
1134 for (id agent in update_agent_list) {
1135 [self publishToAgent:agent];
1138 for (int idx = 0; idx < dns_config->n_resolver; idx++) {
1139 dns_resolver_t * resolver;
1141 resolver = dns_config->resolver[idx];
1142 if (resolver->domain != NULL &&
1143 ![self isResolverPrivate:resolver] &&
1144 ![self isResolverMulticast:resolver]) {
1148 NSString * ns_domain_name;
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 */
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.
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 */
1165 data = [self dataForResolver:resolver];
1167 NSString *ns_domain_name_copy = [NSString stringWithFormat:@"%@" multipleEntitySuffix "%lu", ns_domain_name, (unsigned long)domainInstance];
1169 BOOL ok = [self spawnFloatingAgent:[DNSAgent class]
1170 entity:ns_domain_name_copy
1171 agentSubType:kAgentSubTypeSupplemental
1172 addPolicyOfType:NEPolicyConditionTypeDomain
1175 id agent = [self.floatingDNSAgentList objectForKey:ns_domain_name_copy];
1176 SC_log(LOG_INFO, "Duplicate DNS agent %@", [agent getAgentName]);;
1179 data = [self dataForResolver:resolver];
1180 mapped_agent = [self getAgentWithSameDataAndSubType:self.floatingDNSAgentList
1182 subType:kAgentSubTypeSupplemental];
1183 if (mapped_agent != nil) {
1184 [self spawnMappedFloatingAgent:mapped_agent
1185 entity:ns_domain_name
1186 agentSubType:kAgentSubTypeSupplemental
1187 addPolicyOfType:NEPolicyConditionTypeDomain
1190 [self spawnFloatingAgent:[DNSAgent class]
1191 entity:ns_domain_name
1192 agentSubType:kAgentSubTypeSupplemental
1193 addPolicyOfType:NEPolicyConditionTypeDomain
1198 [new_domain_list removeObjectAtIndex:found];
1199 [duplicate_domain_list addObject:ns_domain_name];
1207 - (void)processDNSResolvers:(dns_config_t *)dns_config
1209 resolver_list_t *resolvers = [self copyResolverList:dns_config];
1211 /* Process Default resolvers */
1212 NSMutableArray *old_default_resolver_list = [self getAgentList:self.floatingDNSAgentList
1213 agentType:kAgentTypeDNS
1214 agentSubType:kAgentSubTypeDefault];
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];
1222 NSString * resolverName;
1224 data = [self dataForResolver:default_resolver];
1226 resolverName = @(dnsAgentDefault);
1228 resolverName = [NSString stringWithFormat:@dnsAgentDefault multipleEntitySuffix "%d", i+1 ];
1231 dnsAgent = [self.floatingDNSAgentList objectForKey:resolverName];
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! */
1239 [self destroyFloatingAgent:dnsAgent];
1243 [self spawnFloatingAgent:[DNSAgent class]
1245 agentSubType:kAgentSubTypeDefault
1246 addPolicyOfType:NEPolicyConditionTypeNone
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];
1255 /* Process Multicast resolvers */
1257 NSMutableArray *old_multicast_resolver_list = [self getAgentList:self.floatingDNSAgentList
1258 agentType:kAgentTypeDNS
1259 agentSubType:kAgentSubTypeMulticast];
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];
1265 NSString * resolverName;
1267 if (multicast_resolver == NULL) {
1271 if (multicast_resolver->domain == NULL) {
1272 /* Multicast resolvers MUST have a domain */
1276 resolverName = @(multicast_resolver->domain);
1277 if (resolverName == NULL) {
1278 /* Multicast resolvers MUST have a domain */
1282 dnsAgent = [self.floatingDNSAgentList objectForKey:resolverName];
1283 if (dnsAgent != nil) {
1284 [old_multicast_resolver_list removeObject:resolverName];
1288 [self spawnFloatingAgent:[DNSAgent class]
1290 agentSubType:kAgentSubTypeMulticast
1291 addPolicyOfType:NEPolicyConditionTypeDomain
1293 // Don't care about data for mdns resolvers. Do we?
1297 [self deleteAgentList:self.floatingDNSAgentList list:old_multicast_resolver_list];
1299 /* Process Private resolvers */
1301 NSMutableArray *old_private_resolver_list = [self getAgentList:self.floatingDNSAgentList
1302 agentType:kAgentTypeDNS
1303 agentSubType:kAgentSubTypePrivate];
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];
1309 NSString * resolverName;
1311 if (private_resolver == NULL) {
1315 if (private_resolver->domain == NULL) {
1316 /* private resolvers MUST have a domain */
1320 resolverName = @(private_resolver->domain);
1321 if (resolverName == nil) {
1322 /* Private resolvers MUST have a domain */
1326 dnsAgent = [self.floatingDNSAgentList objectForKey:resolverName];
1327 if (dnsAgent != nil) {
1328 [old_private_resolver_list removeObject:resolverName];
1332 [self spawnFloatingAgent:[DNSAgent class]
1334 agentSubType:kAgentSubTypePrivate
1335 addPolicyOfType:NEPolicyConditionTypeDomain
1337 // Don't care about data for pdns resolvers. Do we?
1341 [self deleteAgentList:self.floatingDNSAgentList list:old_private_resolver_list];
1344 [self freeResolverList:resolvers];
1347 - (void)processScopedDNSResolvers:(dns_config_t *)dns_config;
1349 NSMutableArray * old_intf_list;
1350 old_intf_list = [self getAgentList:self.floatingDNSAgentList
1351 agentType:kAgentTypeDNS
1352 agentSubType:kAgentSubTypeScoped];
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++) {
1361 NSString * ns_if_name;
1362 NSString * ns_if_name_with_prefix;
1363 dns_resolver_t * resolver;
1365 resolver = dns_config->scoped_resolver[i];
1366 if_name = if_indextoname(resolver->if_index, buf);
1368 ns_if_name = @(if_name);
1369 ns_if_name_with_prefix = [NSString stringWithFormat:@"%s%@", prefixForInterfaceName, ns_if_name];
1374 data = [self dataForResolver:resolver];
1375 idx = [old_intf_list indexOfObject:ns_if_name_with_prefix];
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
1386 /* We have an agent on this interface. Update it */
1387 [old_intf_list removeObjectAtIndex:idx];
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];
1402 [self deleteAgentList:self.floatingDNSAgentList list:old_intf_list];
1405 - (void)processServiceSpecificDNSResolvers:(dns_config_t *)dns_config;
1407 NSMutableArray * old_service_list;
1408 old_service_list = [self getAgentList:self.floatingDNSAgentList
1409 agentType:kAgentTypeDNS
1410 agentSubType:kAgentSubTypeServiceSpecific];
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++) {
1417 uint32_t service_identifier;
1418 NSString * ns_service_identifier_with_prefix;
1419 dns_resolver_t * resolver;
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];
1429 data = [self dataForResolver:resolver];
1430 idx = [old_service_list indexOfObject:ns_service_identifier_with_prefix];
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 */
1441 /* We have an agent on this interface. Update it */
1442 [old_service_list removeObjectAtIndex:idx];
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];
1457 [self deleteAgentList:self.floatingDNSAgentList list:old_service_list];
1460 #define ONION_RESOLVER_DOMAIN "onion"
1461 - (BOOL)isResolverOnion:(dns_resolver_t *)resolver
1463 if (resolver->domain != NULL &&
1464 (strcmp(resolver->domain, ONION_RESOLVER_DOMAIN) == 0)) {
1472 - (void)processOnionResolver:(dns_config_t *)dns_config
1474 static NSUInteger policy_id = 0;
1476 if (dns_config == NULL) {
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]) {
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]) {
1497 SC_log(LOG_NOTICE, "Could not add a [." ONION_RESOLVER_DOMAIN "] drop policy");
1499 SC_log(LOG_INFO, "Added a [." ONION_RESOLVER_DOMAIN "] drop policy");
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");
1515 SC_log(LOG_INFO, "Removed the [." ONION_RESOLVER_DOMAIN "] drop policy");
1521 #undef ONION_RESOLVER_DOMAIN
1524 - (void)processDNSChanges
1526 dns_config_t * dns_config;
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];
1535 [self destroyFloatingAgent:agent];
1540 [self processDNSResolvers:dns_config];
1541 [self processScopedDNSResolvers:dns_config];
1542 [self processSupplementalDNSResolvers:dns_config];
1543 [self processServiceSpecificDNSResolvers:dns_config];
1547 [self processOnionResolver:dns_config];
1548 if (dns_config != NULL) {
1549 dns_configuration_free(dns_config);
1553 #pragma mark Helper functions
1555 - (const void *)copyConfigAgentData:(NSMutableDictionary *)controllerDict
1556 uuid:(uuid_t)requested_uuid
1557 length:(uint64_t *)length
1559 if (length == NULL) {
1560 SC_log(LOG_NOTICE, "Invalid parameters for copying agent data");
1565 void *buffer = NULL;
1568 for (NSString *key in controllerDict) {
1569 id temp_agent = [controllerDict objectForKey:key];
1573 [[temp_agent getAgentUUID] getUUIDBytes:agent_uuid];
1574 if (uuid_compare(agent_uuid, requested_uuid) == 0) {
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);
1587 NSData *data = [agent getAgentData];
1588 uint64_t len = [data length];
1591 buffer = malloc((size_t)len);
1592 memcpy(buffer, [data bytes], len);
1595 return (const void *)buffer;
1598 - (const void *)copyProxyAgentData:(uuid_t)requested_uuid
1599 length:(uint64_t *)length
1601 return [self copyConfigAgentData:self.floatingProxyAgentList
1606 - (const void *)copyDNSAgentData:(uuid_t)requested_uuid
1607 length:(uint64_t *)length
1609 return [self copyConfigAgentData:self.floatingDNSAgentList
1614 - (NSData *)dataLengthSanityCheck:(id)agent
1616 NSData * data = [agent getAgentData];
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.
1624 NSMutableDictionary *data_dict = [NSMutableDictionary dictionary];
1626 NSUUID *uuid = [agent getAgentUUID];
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];
1632 NSData *new_data = [NSPropertyListSerialization dataWithPropertyList:data_dict
1633 format:NSPropertyListBinaryFormat_v1_0
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
1648 - (NSString *)sanitizeEntity:(NSString *)entity
1650 NSRange range = [entity rangeOfString:@multipleEntitySuffix];
1651 if (range.location != NSNotFound) {
1652 NSString *str = [entity substringToIndex:range.location];
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
1664 - (NSString *)sanitizeInterfaceName:(NSString *)intf
1666 NSRange range = [intf rangeOfString:@prefixForInterfaceName];
1667 if (range.location != NSNotFound) {
1668 NSString *str = [intf substringFromIndex:(range.location + strlen(prefixForInterfaceName))];
1676 * For conflicting agents, the convention is that its name & entity,
1677 * will have a suffix " #<number>". This function will return that <number>
1679 - (int)entityInstanceNumber:(NSString *)entity
1681 NSRange range = [entity rangeOfString:@multipleEntitySuffix];
1682 if (range.location != NSNotFound) {
1683 NSString *str = [entity substringFromIndex:(range.location + strlen(multipleEntitySuffix))];
1684 return str.intValue;
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
1695 - (void)cleanConflictingAgentsFromList:(NSMutableArray *)old_list
1696 new_list:(NSMutableArray *)new_list
1697 agentDictionary:(NSMutableDictionary *)agent_list
1699 NSCountedSet * duplicate_domain_list;
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];
1709 /* Destroy the conflicting domain */
1710 agent = [agent_list objectForKey:domain];
1711 [self destroyFloatingAgent:agent];
1713 SC_log(LOG_INFO, "Removing conflicting domain: %@, %@", sanitizedDomain, domain);
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);
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
1733 - (NSMutableArray *)getAgentList:(NSMutableDictionary *)all_agents
1734 agentType:(AgentType)type
1735 agentSubType:(AgentSubType)subtype
1737 NSMutableArray *list = [NSMutableArray array];
1738 NSArray *agentObjects = [all_agents allValues];
1740 for (id agent in agentObjects) {
1741 if (([agent getAgentType] == type) &&
1742 ([agent getAgentSubType] == subtype)) {
1744 [list addObject:[agent getAssociatedEntity]];
1752 * Destroy all the agents are listed in "list"
1755 - (void)deleteAgentList:(NSMutableDictionary *)all_agents
1756 list:(NSMutableArray *)list
1758 for (NSString *intf in list) {
1761 agent = [all_agents objectForKey:intf];
1762 [self destroyFloatingAgent:agent];
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.
1770 * This function tries to find that agent Y
1773 - (id)getAgentWithSameDataAndSubType:(NSMutableDictionary *)agentList
1775 subType:(AgentSubType)subtype
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) {
1785 /* Return only registered agents */
1786 if ([agent getRegistrationObject] != nil) {
1795 #pragma mark Policy installation function
1798 * Add NECP policies for an agent
1800 - (BOOL)addPolicyToFloatingAgent:(id)agent
1801 domain:(NSString *)domain
1802 agentUUIDToUse:(NSUUID *)uuid
1803 policyType:(NEPolicyConditionType)policyType
1804 useControlPolicySession:(BOOL)useControlPolicySession
1806 NEPolicyCondition * condition = nil;
1807 NEPolicySession * session;
1808 uint32_t multiple_entity_offset;
1809 NEPolicy * newPolicy;
1812 uint32_t orderForSkip;
1813 NSMutableArray * policyArray;
1814 NSUInteger policyID1;
1815 NSUInteger policyID2;
1816 NEPolicyResult * result;
1819 uint32_t typeOffset;
1821 type = [agent getAgentType];
1822 typeOffset = (type == kAgentTypeDNS) ? 0 : 5000;
1823 skipOrder = (type == kAgentTypeDNS) ? 5000 : 0;
1825 multiple_entity_offset = (uint32_t)[self entityInstanceNumber:domain];
1826 domain = [self sanitizeEntity:domain];
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;
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;
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;
1848 case NEPolicyConditionTypeNone:
1849 order = INIT_ORDER_FOR_DEFAULT_POLICY + typeOffset + multiple_entity_offset;
1850 orderForSkip = SKIP_ORDER_FOR_DEFAULT_POLICY + typeOffset;
1854 SC_log(LOG_NOTICE, "Invalid policy condition specified");
1858 result = [NEPolicyResult netAgentUUID:uuid];
1859 newPolicy = [[NEPolicy alloc] initWithOrder:order
1861 conditions: (condition ? @[condition] : nil)];
1863 if (newPolicy == nil) {
1864 SC_log(LOG_NOTICE, "Could not create a policy for agent %@", [agent getAgentName]);
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]);
1876 [self.controlPolicySession setPriority:NEPolicySessionPriorityControl];
1878 ((ConfigAgent *)agent).preferredPolicySession = self.controlPolicySession;
1880 ((ConfigAgent *)agent).preferredPolicySession = self.policySession;
1883 session = ((ConfigAgent *)agent).preferredPolicySession;
1885 policyID1 = [session addPolicy:newPolicy];
1886 if (policyID1 == 0) {
1887 SC_log(LOG_NOTICE, "Could not add a netagent policy for agent %@", [agent getAgentName]);
1891 result = [NEPolicyResult skipWithOrder:skipOrder];
1892 newPolicy = [[NEPolicy alloc] initWithOrder:orderForSkip
1894 conditions:(condition ? @[condition] : nil)];
1896 if (newPolicy == nil) {
1897 SC_log(LOG_NOTICE, "Could not create a policy for agent %@", [agent getAgentName]);
1901 policyID2 = [session addPolicy:newPolicy];
1902 if (policyID2 == 0) {
1903 SC_log(LOG_NOTICE, "Could not add a skip policy for agent %@", [agent getAgentName]);
1907 ok = [session apply];
1909 SC_log(LOG_NOTICE, "Could not apply policy for agent %@", [agent getAgentName]);
1913 policyArray = [self.policyDB objectForKey:[agent getAgentName]];
1914 if (policyArray == nil) {
1915 policyArray = [NSMutableArray array];
1918 [policyArray addObject:numberToNSNumber(policyID1)];
1919 [policyArray addObject:numberToNSNumber(policyID2)];
1920 [self.policyDB setObject:policyArray forKey:[agent getAgentName]];
1925 #pragma mark Agent manipulation functions
1930 - (BOOL)spawnFloatingAgent:(Class)agentClass
1931 entity:(NSString *)entity
1932 agentSubType:(AgentSubType)subtype
1933 addPolicyOfType:(NEPolicyConditionType)policyType
1934 publishData:(NSData *)data
1938 NSMutableDictionary * parameters;
1940 parameters =[NSMutableDictionary dictionary];
1941 [parameters setValue:entity forKey:@kEntityName];
1942 [parameters setValue:numberToNSNumber(subtype) forKey:@kAgentSubType];
1944 agent = [[agentClass alloc] initWithParameters:parameters];
1945 ok = [self registerAgent:agent];
1951 /* Since we just spawned this agent, update its data */
1952 [agent updateAgentData:data];
1953 [self publishToAgent:agent];
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;
1966 ok = [self addPolicyToFloatingAgent:agent
1968 agentUUIDToUse:[agent agentUUID]
1969 policyType:policyType
1970 useControlPolicySession:useControlPolicySession];
1973 [self unregisterAgent:agent];
1978 SC_log(LOG_INFO, "Spawning floating agent for %@", entity);
1980 if ([agent getAgentType] == kAgentTypeProxy) {
1981 [self.floatingProxyAgentList setObject:agent forKey:entity];
1983 [self.floatingDNSAgentList setObject:agent forKey:entity];
1990 * Create an agent mapped to another agent.
1992 - (BOOL)spawnMappedFloatingAgent:(id)mapped_agent
1993 entity:(NSString *)entity
1994 agentSubType:(AgentSubType)subtype
1995 addPolicyOfType:(NEPolicyConditionType)policyType
1996 updateData:(NSData *)data
1999 NSMutableDictionary * parameters;
2001 parameters = [NSMutableDictionary dictionary];
2002 [parameters setValue:entity forKey:@kEntityName];
2003 [parameters setValue:numberToNSNumber(subtype) forKey:@kAgentSubType];
2005 dummyAgent = [[[mapped_agent class] alloc] initWithParameters:parameters];
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
2012 [dummyAgent updateAgentData:data];
2015 BOOL useControlPolicySession = NO;
2016 if (subtype == kAgentSubTypeGlobal) {
2017 /* Policies for a Global scoped agents are at "control" level */
2018 useControlPolicySession = YES;
2021 BOOL ok = [self addPolicyToFloatingAgent:dummyAgent
2023 agentUUIDToUse:[mapped_agent agentUUID]
2024 policyType:policyType
2025 useControlPolicySession:useControlPolicySession];
2031 if ([mapped_agent getAgentType] == kAgentTypeProxy) {
2032 [self.floatingProxyAgentList setObject:dummyAgent forKey:entity];
2034 [self.floatingDNSAgentList setObject:dummyAgent forKey:entity];
2037 [dummyAgent setAgentMapping:mapped_agent];
2039 SC_log(LOG_INFO, "Mapped floating agent %@ to %@", [dummyAgent getAgentName], [mapped_agent getAgentName]);
2044 * Write into an agent
2046 - (BOOL)publishToAgent:(id)agent
2048 /* Before any data goes into the kernel, do a sanity check. */
2049 NSData *sanityCheckData = [self dataLengthSanityCheck:agent];
2050 NSData *tempAgentData = nil;
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!
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]);
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];
2068 SC_log(LOG_NOTICE, "Could not update config agent");
2071 SC_log(LOG_NOTICE, "Config Agent not registered. Cannot Update");
2074 if (tempAgentData != nil) {
2075 [agent updateAgentData:tempAgentData];
2084 - (BOOL)destroyFloatingAgent:(id)agent
2088 if ( agent != nil) {
2089 NSMutableArray * policyArray;
2091 policyArray = [self.policyDB objectForKey:[agent getAgentName]];
2092 if (policyArray != nil) {
2093 NEPolicySession * session = ((ConfigAgent *)agent).preferredPolicySession;
2096 for (NSNumber *policyID in policyArray) {
2099 idVal = [policyID unsignedIntegerValue];
2100 result = [session removePolicyWithID:idVal];
2102 SC_log(LOG_NOTICE, "Could not remove policy %@ for agent %@", [session policyWithID:idVal], [agent getAgentName]);
2106 result = [session apply];
2108 SC_log(LOG_NOTICE, "Could not apply removed policies for agent %@", [agent getAgentName]);
2111 [self.policyDB removeObjectForKey:[agent getAgentName]];
2114 if ([agent getAgentType] == kAgentTypeProxy) {
2115 [self.floatingProxyAgentList removeObjectForKey:[agent getAssociatedEntity]];
2117 [self.floatingDNSAgentList removeObjectForKey:[agent getAssociatedEntity]];
2120 if ([agent getRegistrationObject] != nil) {
2121 [self unregisterAgent:agent];
2124 SC_log(LOG_INFO, "X - Destroyed agent %@", [agent getAgentName]);
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];
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");
2151 - (BOOL)registerAgent:(id)agent
2155 NWNetworkAgentRegistration *registration = [[NWNetworkAgentRegistration alloc] initWithNetworkAgentClass:[agent class]];
2157 ok = [registration registerNetworkAgent:agent];
2159 SC_log(LOG_NOTICE, "Could not register config agent");
2163 [agent addAgentRegistrationObject:registration];
2170 * Unregister an agent
2172 - (BOOL)unregisterAgent:(id)agent
2176 NWNetworkAgentRegistration *regObject = [agent valueForKey:@"registrationObject"];
2177 if (regObject != nil) {
2178 ok = [regObject unregisterNetworkAgent];
2180 SC_log(LOG_NOTICE, "Could not unregister config agent");
2183 SC_log(LOG_NOTICE, "Config Agent not registered. Cannot unregister");