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 CFDataRef data = NULL;
245 (void)_SCSerialize(proxy_array_for_data, &data, NULL, NULL);
246 return (__bridge_transfer NSData *)data;
249 - (NSData *)dataForProxyDictionary:(CFDictionaryRef)domain_proxy
252 CFMutableDictionaryRef domain_proxy_dict;
254 if (domain_proxy == NULL) {
255 SC_log(LOG_NOTICE, "Invalid domain proxy dict");
259 domain_proxy_dict = CFDictionaryCreateMutableCopy(NULL, 0, domain_proxy);
260 CFDictionaryRemoveValue(domain_proxy_dict, kSCPropNetProxiesSupplementalMatchDomain);
262 data = (__bridge_transfer NSData *)(SCNetworkProxiesCreateProxyAgentData(domain_proxy_dict));
263 CFRelease(domain_proxy_dict);
268 - (NSData *)getProxyDataFromCurrentConfig:(CFDictionaryRef)proxies
269 domain:(NSString *)domain
273 CFArrayRef supplemental;
275 if (proxies == NULL || domain == nil) {
276 SC_log(LOG_NOTICE, "Invalid proxies/domain");
280 supplemental = CFDictionaryGetValue(proxies, kSCPropNetProxiesSupplemental);
281 count = supplemental ? CFArrayGetCount(supplemental) : 0;
283 for (idx = 0; idx < count; idx++) {
284 CFDictionaryRef domain_proxy;
285 CFStringRef match_domain;
287 domain_proxy = CFArrayGetValueAtIndex(supplemental, idx);
288 match_domain = CFDictionaryGetValue(domain_proxy, kSCPropNetProxiesSupplementalMatchDomain);
289 if (match_domain != NULL && CFEqual(match_domain, (__bridge CFTypeRef)(domain))) {
290 return [self dataForProxyDictionary:domain_proxy];
297 - (bool)getIntValue:(CFTypeRef)cf_value
298 valuePtr:(int *) int_value_ptr
301 if (cf_value && CFGetTypeID(cf_value) == CFNumberGetTypeID() && CFNumberGetValue(cf_value, kCFNumberIntType, int_value_ptr))
308 - (int)countProxyEntriesEnabled:(CFDictionaryRef)proxies
312 if (proxies == NULL) {
313 SC_log(LOG_NOTICE, "Invalid proxies");
317 if (([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesHTTPEnable) valuePtr:&enabled] && enabled > 0) ||
318 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesHTTPSEnable) valuePtr:&enabled] && enabled > 0) ||
319 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesProxyAutoConfigEnable) valuePtr:&enabled] && enabled > 0) ||
320 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesFTPEnable) valuePtr:&enabled] && enabled > 0) ||
321 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesGopherEnable) valuePtr:&enabled] && enabled > 0) ||
322 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesRTSPEnable) valuePtr:&enabled] && enabled > 0) ||
323 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesSOCKSEnable) valuePtr:&enabled] && enabled > 0) ||
324 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesProxyAutoDiscoveryEnable) valuePtr:&enabled] && enabled > 0)) {
331 - (void)processSupplementalProxyChanges:(CFDictionaryRef)proxies
334 NSMutableArray * deleteList;
335 NSCountedSet * duplicate_domain_list;
337 NSMutableArray * new_domain_list;
338 NSMutableArray * old_domain_list;
339 CFArrayRef supplemental;
340 NSMutableArray * update_agent_list;
342 if (proxies == NULL) {
343 SC_log(LOG_INFO, "No proxy config to process");
347 old_domain_list = [self getAgentList:self.floatingProxyAgentList
348 agentType:kAgentTypeProxy
349 agentSubType:kAgentSubTypeSupplemental];
350 duplicate_domain_list = [[NSCountedSet alloc] initWithCapacity:0];
351 new_domain_list = [NSMutableArray array];
352 update_agent_list = [NSMutableArray array];
353 supplemental = CFDictionaryGetValue(proxies, kSCPropNetProxiesSupplemental);
354 count = supplemental ? CFArrayGetCount(supplemental) : 0;
355 deleteList = [NSMutableArray array];
357 for (idx = 0; idx < count; idx++) {
358 CFDictionaryRef domain_proxy;
359 CFStringRef match_domain;
362 domain_proxy = CFArrayGetValueAtIndex(supplemental, idx);
363 match_domain = CFDictionaryGetValue(domain_proxy, kSCPropNetProxiesSupplementalMatchDomain);
364 if (match_domain == NULL) {
368 /* This domain is present in current config. But if it has generic (no protocols enabled)
369 * proxy content, there is no real use of that agent. Do NOT add it to
370 * the new_domain_list.
372 * This way, if there was an agent previously for this domain,
373 * it will be destroyed AND since it is not present in the new domain list, we wont
374 * spawn a new agent too! :)
377 proxy_count = [self countProxyEntriesEnabled:domain_proxy];
378 if (proxy_count == 0) {
379 SC_log(LOG_INFO, "Proxy settings on %@ are generic. Not recognizing as new domain", match_domain);
383 [new_domain_list addObject:(__bridge NSString *)match_domain];
386 [self cleanConflictingAgentsFromList:old_domain_list
387 new_list:new_domain_list
388 agentDictionary:self.floatingProxyAgentList];
390 for (NSString *key in old_domain_list) {
393 domain_present = [new_domain_list containsObject:key];
394 if (domain_present == NO) {
397 agent = [self.floatingProxyAgentList objectForKey:key];
398 [self destroyFloatingAgent:agent];
402 /* At this point, whatever is in the controller's floating agent list,
403 * is present in the current proxy config. The current proxy config
404 * might have even more configs, not known to the controller, YET
407 for (NSString *domain in old_domain_list) {
411 agent = [self.floatingProxyAgentList objectForKey:domain];
416 /* Am I mapped to some agent? */
417 mapped_agent = [agent getAgentMapping];
419 /* OK, this agent is mapped to some other agent. We compare this agent's data
420 * to the current data of the agent to which it is mapped. If different, we destroy
421 * the agent and later map it to someone else OR spawn a new one.
423 NSData * mapped_agent_data;
425 mapped_agent_data = [self getProxyDataFromCurrentConfig:proxies domain:[mapped_agent getAssociatedEntity]];
426 if (mapped_agent_data == nil || ![[agent getAgentData] isEqual:mapped_agent_data]) {
427 /* Something changed for mapped agent */
428 [deleteList addObject:agent];
432 /* Since this agent is NOT mapped to any other agent, this agent is
433 * registered with the kernel. So instead of destroying the agent and
434 * re-registering it, just update it here.
436 * All the agents which were mapped to this agent, will be deleted and
437 * re-mapped, if the data changed.
441 agent_data = [self getProxyDataFromCurrentConfig:proxies domain:[agent getAssociatedEntity]];
442 if (![[agent getAgentData] isEqual:agent_data]) {
443 /* Something changed for agent */
444 [agent updateAgentData:agent_data];
446 /* The reason I don't publish the data to agent here is that, if there were
447 * some agents mapping to this one, they will momentarily have a policy for
448 * using this agent UUID for some domain based on this agent's previous data.
450 [update_agent_list addObject:agent];
453 [new_domain_list removeObject:domain];
456 for (id agent in deleteList) {
457 SC_log(LOG_INFO, "Destroying agent %@ because something changed!", [agent getAgentName]);
458 [self destroyFloatingAgent:agent];
461 for (id agent in update_agent_list) {
462 [self publishToAgent:agent];
465 for (idx = 0; idx < count; idx++) {
466 CFDictionaryRef domain_proxy;
467 CFStringRef match_domain;
469 domain_proxy = CFArrayGetValueAtIndex(supplemental, idx);
470 match_domain = CFDictionaryGetValue(domain_proxy, kSCPropNetProxiesSupplementalMatchDomain);
472 if (match_domain != NULL) {
477 found = [new_domain_list indexOfObject:(__bridge id _Nonnull)(match_domain)];
478 if (found == NSNotFound) {
483 * We will only process agents which are mapped AND the agent they were mapped to, changed OR
484 * agents for domains which we did not know before.
487 NSUInteger domainInstance = [duplicate_domain_list countForObject:(__bridge id _Nonnull)(match_domain)];
488 if (domainInstance > 0) {
489 /* domainInstance will be > 0, only if we have conflicting domains */
491 NSString *ns_domain_name_copy = [NSString stringWithFormat:@"%@" multipleEntitySuffix "%lu", match_domain, (unsigned long)domainInstance];
493 data = [self dataForProxyDictionary:domain_proxy];
495 BOOL ok = [self spawnFloatingAgent:[ProxyAgent class]
496 entity:ns_domain_name_copy
497 agentSubType:kAgentSubTypeSupplemental
498 addPolicyOfType:NEPolicyConditionTypeDomain
501 id agent = [self.floatingProxyAgentList objectForKey:ns_domain_name_copy];
502 SC_log(LOG_INFO, "Duplicate Proxy agent %@", [agent getAgentName]);;
505 data = [self dataForProxyDictionary:domain_proxy];
506 mapped_agent = [self getAgentWithSameDataAndSubType:self.floatingProxyAgentList
508 subType:kAgentSubTypeSupplemental];
509 if (mapped_agent != nil) {
510 [self spawnMappedFloatingAgent:mapped_agent
511 entity:(__bridge NSString *)(match_domain)
512 agentSubType:kAgentSubTypeSupplemental
513 addPolicyOfType:NEPolicyConditionTypeDomain
516 [self spawnFloatingAgent:[ProxyAgent class]
517 entity:(__bridge NSString *)(match_domain)
518 agentSubType:kAgentSubTypeSupplemental
519 addPolicyOfType:NEPolicyConditionTypeDomain
524 [new_domain_list removeObjectAtIndex:found];
525 [duplicate_domain_list addObject:(__bridge id _Nonnull)(match_domain)];
532 - (void)processScopedProxyChanges:(CFDictionaryRef)proxies
534 NSMutableArray * old_intf_list;
535 CFDictionaryRef scoped_proxies;
536 CFIndex scoped_proxies_count;
538 old_intf_list = [self getAgentList:self.floatingProxyAgentList
539 agentType:kAgentTypeProxy
540 agentSubType:kAgentSubTypeScoped];
542 scoped_proxies = CFDictionaryGetValue(proxies, kSCPropNetProxiesScoped);
543 scoped_proxies_count = scoped_proxies ? CFDictionaryGetCount(scoped_proxies) : 0;
545 if (scoped_proxies_count > 0) {
548 keys = malloc(scoped_proxies_count * sizeof(void *));
549 CFDictionaryGetKeysAndValues(scoped_proxies, keys, NULL);
551 for (int i = 0; i < scoped_proxies_count; i++) {
555 NSString * ns_if_name;
556 NSString * ns_if_name_with_prefix;
560 ns_if_name = (__bridge NSString *)keys[i];
561 ns_if_name_with_prefix = [NSString stringWithFormat:@"%s%@", prefixForInterfaceName, ns_if_name];
563 /* Does the proxy config have any protocols enabled? */
564 proxy_count = [self countProxyEntriesEnabled:CFDictionaryGetValue(scoped_proxies,
565 (__bridge const void *)(ns_if_name))];
567 if (proxy_count == 0) {
568 SC_log(LOG_INFO, "Proxy settings on %@ are generic. Skipping", ns_if_name);
572 idx = [old_intf_list indexOfObject:ns_if_name_with_prefix];
574 matching = SCNetworkProxiesCopyMatching(proxies, NULL, (__bridge CFStringRef)(ns_if_name));
575 if (matching != NULL) {
576 data = [self dataForProxyArray:matching];
580 if (idx == NSNotFound) {
581 /* We need to spawn an agent */
582 [self spawnFloatingAgent:[ProxyAgent class]
583 entity:ns_if_name_with_prefix
584 agentSubType:kAgentSubTypeScoped
585 addPolicyOfType:NEPolicyConditionTypeScopedInterface
590 /* We have an agent for this interface. Update it */
591 [old_intf_list removeObjectAtIndex:idx];
594 proxyAgent = [self.floatingProxyAgentList objectForKey:ns_if_name_with_prefix];
595 if (proxyAgent != nil) {
596 /* Do we need to update this agent? */
597 [proxyAgent updateAgentData:data];
598 if ([proxyAgent shouldUpdateAgent]) {
599 [self publishToAgent:proxyAgent];
607 [self deleteAgentList:self.floatingProxyAgentList list:old_intf_list];
610 - (void)processServiceSpecificProxyChanges:(CFDictionaryRef)proxies
612 NSMutableArray * old_service_list;
613 CFDictionaryRef service_proxies;
614 CFIndex service_proxies_count;
616 old_service_list = [self getAgentList:self.floatingProxyAgentList
617 agentType:kAgentTypeProxy
618 agentSubType:kAgentSubTypeServiceSpecific];
620 service_proxies = CFDictionaryGetValue(proxies, kSCPropNetProxiesServices);
621 service_proxies_count = service_proxies ? CFDictionaryGetCount(service_proxies) : 0;
623 if (service_proxies_count > 0) {
626 keys = malloc(service_proxies_count * sizeof(void *));
627 CFDictionaryGetKeysAndValues(service_proxies, keys, NULL);
629 for (int i = 0; i < service_proxies_count; i++) {
632 NSString * ns_service_identifier = nil;
633 NSString * ns_service_with_prefix = nil;
636 CFDictionaryRef proxyDict = NULL;
638 ns_service_identifier = (__bridge NSString *)keys[i];
639 ns_service_with_prefix = [NSString stringWithFormat:@"%s%@", prefixForInterfaceName, ns_service_identifier];
641 /* Does the proxy config have any protocols enabled? */
642 proxy_count = [self countProxyEntriesEnabled:CFDictionaryGetValue(service_proxies,
643 (__bridge const void *)(ns_service_identifier))];
645 if (proxy_count == 0) {
646 SC_log(LOG_INFO, "Proxy settings on %@ are generic. Skipping", ns_service_identifier);
650 proxyDict = CFDictionaryGetValue(service_proxies, (__bridge CFStringRef)ns_service_identifier);
651 if (proxyDict != nil) {
652 data = [self dataForProxyArray:(__bridge CFArrayRef)(@[ (__bridge NSDictionary *)proxyDict ])];
655 idx = [old_service_list indexOfObject:ns_service_with_prefix];
656 if (idx == NSNotFound) {
657 /* We need to spawn an agent */
658 [self spawnFloatingAgent:[ProxyAgent class]
659 entity:ns_service_with_prefix
660 agentSubType:kAgentSubTypeServiceSpecific
661 addPolicyOfType:(POLICY_TYPE_NO_POLICY) /* Don't install a policy */
666 /* We have an agent for this service. Update it */
667 [old_service_list removeObjectAtIndex:idx];
670 proxyAgent = [self.floatingProxyAgentList objectForKey:ns_service_with_prefix];
671 if (proxyAgent != nil) {
672 /* Do we need to update this agent? */
673 [proxyAgent updateAgentData:data];
674 if ([proxyAgent shouldUpdateAgent]) {
675 [self publishToAgent:proxyAgent];
683 [self deleteAgentList:self.floatingProxyAgentList list:old_service_list];
686 - (BOOL)isGlobalProxy:(CFDictionaryRef)proxies
688 if (CFDictionaryContainsKey(proxies, kSCPropNetProxiesBypassAllowed)) {
690 * Since we did not ask to "bypass" the proxies, this key will always
691 * be present in a managed (global) proxy configuration
699 - (void)processDefaultProxyChanges:(CFDictionaryRef)proxies
701 CFArrayRef global_proxy;
702 CFIndex global_proxy_count;
703 CFMutableDictionaryRef proxies_copy;
705 proxies_copy = CFDictionaryCreateMutableCopy(NULL, 0, proxies);
706 CFDictionaryRemoveValue(proxies_copy, kSCPropNetProxiesScoped);
707 CFDictionaryRemoveValue(proxies_copy, kSCPropNetProxiesServices);
708 CFDictionaryRemoveValue(proxies_copy, kSCPropNetProxiesSupplemental);
710 global_proxy = CFArrayCreate(NULL, (const void **)&proxies_copy, 1, &kCFTypeArrayCallBacks);
711 global_proxy_count = CFArrayGetCount(global_proxy);
712 if (global_proxy_count > 0 &&
713 [self countProxyEntriesEnabled:proxies_copy] == 0) {
714 SC_log(LOG_INFO, "Proxy settings on defaultProxy are generic. Skipping");
715 global_proxy_count = 0;
717 CFRelease(proxies_copy);
719 if (global_proxy_count > 0) {
720 BOOL spawnAgent = YES;
724 data = [self dataForProxyArray:global_proxy];
725 proxyAgent = [self.floatingProxyAgentList objectForKey:@proxyAgentDefault];
726 if (proxyAgent != nil) {
727 if (![data isEqual:[proxyAgent getAgentData]]) {
728 [self destroyFloatingAgent:proxyAgent];
735 AgentSubType subtype = kAgentSubTypeDefault;
736 NEPolicyConditionType conditionType = NEPolicyConditionTypeNone;
737 if ([self isGlobalProxy:proxies_copy]) {
738 SC_log(LOG_INFO, "Global proxy detected...");
739 conditionType = NEPolicyConditionTypeAllInterfaces;
740 subtype = kAgentSubTypeGlobal;
743 [self spawnFloatingAgent:[ProxyAgent class]
744 entity:@proxyAgentDefault
746 addPolicyOfType:conditionType
750 /* No default proxy config OR generic (no protocols enabled) default proxy config.
751 * Destroy the default agent if we had one
755 proxyAgent = [self.floatingProxyAgentList objectForKey:@proxyAgentDefault];
756 if (proxyAgent != nil) {
757 [self destroyFloatingAgent:proxyAgent];
761 CFRelease(global_proxy);
764 - (void)processProxyChanges
766 CFDictionaryRef proxies;
768 proxies = SCDynamicStoreCopyProxiesWithOptions(NULL, NULL);
769 if (proxies == NULL) {
770 SC_log(LOG_INFO, "No proxy information");
772 NSMutableDictionary *copy = [self.floatingProxyAgentList copy];
773 for (NSString *entity in copy) {
774 id agent = [copy objectForKey:entity];
775 [self destroyFloatingAgent:agent];
781 [self processDefaultProxyChanges:proxies];
782 [self processScopedProxyChanges:proxies];
783 [self processSupplementalProxyChanges:proxies];
784 [self processServiceSpecificProxyChanges:proxies];
789 /* ========================== DNS agent helpers =========================== */
790 #pragma mark DNS agent helper functions
792 - (void)freeResolverList:(resolver_list_t *)resolvers
794 /* This is a shallow free of resolver_list_t only.
795 * The actual resolver pointers are owned by 'dns_config'
797 if (resolvers == NULL) {
801 if (resolvers->default_resolvers != NULL) {
802 free(resolvers->default_resolvers);
804 if (resolvers->multicast_resolvers != NULL) {
805 free(resolvers->multicast_resolvers);
807 if (resolvers->private_resolvers != NULL) {
808 free(resolvers->private_resolvers);
814 - (resolver_list_t *)copyResolverList:(dns_config_t *)dns_config
816 resolver_list_t *resolvers = NULL;
818 if ((dns_config->n_resolver > 0) && (dns_config->resolver != NULL)) {
823 resolvers = calloc(1, sizeof(resolver_list_t));
824 for (int i = 0; i < dns_config->n_resolver; i++) {
825 dns_resolver_t *r = dns_config->resolver[i];
827 if ([self isResolverMulticast:r]) {
828 resolvers->n_multicast_resolvers++;
831 } else if ([self isResolverPrivate:r]) {
832 resolvers->n_private_resolvers++;
836 // do not consider default resolvers with no nameservers
837 if (r->domain == NULL && r->n_nameserver > 0) {
838 resolvers->n_default_resolvers++;
842 SC_log(LOG_INFO, "Resolvers: %d default, %d multicast, %d private",
843 resolvers->n_default_resolvers,
844 resolvers->n_multicast_resolvers,
845 resolvers->n_private_resolvers);
847 if (resolvers->n_default_resolvers > 0) {
848 resolvers->default_resolvers = calloc(resolvers->n_default_resolvers,
849 sizeof(dns_resolver_t *));
851 if (resolvers->n_multicast_resolvers > 0) {
852 resolvers->multicast_resolvers = calloc(resolvers->n_multicast_resolvers,
853 sizeof(dns_resolver_t *));
855 if (resolvers->n_private_resolvers > 0) {
856 resolvers->private_resolvers = calloc(resolvers->n_private_resolvers,
857 sizeof(dns_resolver_t *));
860 for (int i = 0; i < dns_config->n_resolver; i++) {
861 dns_resolver_t *r = dns_config->resolver[i];
863 if ([self isResolverMulticast:r] &&
864 (a < resolvers->n_multicast_resolvers)) {
865 resolvers->multicast_resolvers[a++] = r;
868 } else if ([self isResolverPrivate:r] &&
869 (b < resolvers->n_private_resolvers)) {
870 resolvers->private_resolvers[b++] = r;
874 if ((r->domain == NULL) &&
875 (r->n_nameserver > 0) &&
876 (c < resolvers->n_default_resolvers)) {
877 resolvers->default_resolvers[c++] = r;
886 * Generate a data blob for the resolver.
887 * Currently the blob only has:
889 * - sockaddr structs for each nameserver
893 - (NSData *)dataForResolver:(dns_resolver_t *)resolver
896 CFMutableDictionaryRef resolverDict = nil;
898 if (resolver == NULL) {
899 SC_log(LOG_NOTICE, "Invalid dns resolver");
903 if (resolver->n_search > 0) {
904 if (resolverDict == nil) {
905 resolverDict = CFDictionaryCreateMutable(NULL,
907 &kCFTypeDictionaryKeyCallBacks,
908 &kCFTypeDictionaryValueCallBacks);
911 CFMutableArrayRef searchDomainArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
913 /* Append search domains */
914 for (int i = 0; i < resolver->n_search; i++) {
915 CFArrayAppendValue(searchDomainArray, (__bridge CFStringRef)(@(resolver->search[i])));
918 CFDictionaryAddValue(resolverDict, CFSTR(kConfigAgentDNSSearchDomains), searchDomainArray);
919 CFRelease(searchDomainArray);
922 /* Get the count of nameservers */
923 if (resolver->n_nameserver > 0) {
924 if (resolverDict == nil) {
925 resolverDict = CFDictionaryCreateMutable(NULL,
927 &kCFTypeDictionaryKeyCallBacks,
928 &kCFTypeDictionaryValueCallBacks);
931 CFMutableArrayRef nameserverArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
933 /* Get all the nameservers */
934 for (int i = 0; i < resolver->n_nameserver; i++) {
936 _SC_sockaddr_to_string(resolver->nameserver[i], buf, sizeof(buf));
938 CFArrayAppendValue(nameserverArray, (__bridge CFStringRef)(@(buf)));
942 CFDictionaryAddValue(resolverDict, CFSTR(kConfigAgentDNSNameServers), nameserverArray);
943 CFRelease(nameserverArray);
946 if (resolverDict != nil) {
947 data = [NSPropertyListSerialization dataWithPropertyList:(__bridge id _Nonnull)(resolverDict)
948 format:NSPropertyListBinaryFormat_v1_0
952 CFRelease(resolverDict);
955 return (NSData *)data;
958 - (NSData *)getDNSDataFromCurrentConfig:(dns_config_t *)dns_config
959 domain:(NSString *)domain
961 if (dns_config == NULL || domain == nil) {
962 SC_log(LOG_NOTICE, "Invalid dns_config/domain");
966 if ((dns_config->n_resolver > 0) && (dns_config->resolver != NULL)) {
967 for (int i = 0; i < dns_config->n_resolver; i++) {
968 dns_resolver_t * resolver;
970 resolver = dns_config->resolver[i];
971 if (resolver->domain != NULL &&
972 ![self isResolverMulticast:resolver]) {
973 NSString * ns_domain_name;
975 ns_domain_name = @(resolver->domain);
976 if ([ns_domain_name isEqualToString:domain]) {
977 return [self dataForResolver:resolver];
988 - (BOOL)isResolverMulticast:(dns_resolver_t *)resolver
990 if (resolver->options == NULL) {
994 if (!strstr(resolver->options, "mdns")) {
1001 - (BOOL)isResolverPrivate:(dns_resolver_t *)resolver
1003 if (resolver->options == NULL) {
1007 if (!strstr(resolver->options, "pdns")) {
1014 - (void)processSupplementalDNSResolvers:(dns_config_t *)dns_config
1016 NSMutableArray * deleteList;
1017 NSMutableArray * new_domain_list;
1018 NSCountedSet * duplicate_domain_list;
1019 NSMutableArray * old_domain_list;
1020 NSMutableArray * update_agent_list;
1023 deleteList = [NSMutableArray array];
1024 duplicate_domain_list = [[NSCountedSet alloc] initWithCapacity:0];
1025 new_domain_list = [NSMutableArray array];
1026 update_agent_list = [NSMutableArray array];
1027 old_domain_list = [self getAgentList:self.floatingDNSAgentList
1028 agentType:kAgentTypeDNS
1029 agentSubType:kAgentSubTypeSupplemental];
1031 if (dns_config->resolver == NULL) {
1032 dns_config->n_resolver = 0;
1034 if (dns_config->n_resolver > 0) {
1035 for (int i = 0; i < dns_config->n_resolver; i++) {
1036 dns_resolver_t * resolver;
1038 resolver = dns_config->resolver[i];
1039 if (resolver->domain != NULL &&
1040 ![self isResolverPrivate:resolver] &&
1041 ![self isResolverMulticast:resolver]) {
1042 NSString * ns_domain_name;
1044 ns_domain_name = [NSString stringWithCString:resolver->domain encoding:NSASCIIStringEncoding];
1045 [new_domain_list addObject:ns_domain_name];
1050 [self cleanConflictingAgentsFromList:old_domain_list
1051 new_list:new_domain_list
1052 agentDictionary:self.floatingDNSAgentList];
1054 /* Sync between controller and current config */
1055 for (NSString *key in old_domain_list) {
1056 BOOL domain_present = NO;
1058 domain_present = [new_domain_list containsObject:key];
1059 if (domain_present == NO) {
1062 agent = [self.floatingDNSAgentList objectForKey:key];
1063 [self destroyFloatingAgent:agent];
1067 /* At this point, whatever is in the controller's floating agent list,
1068 is present in the current DNS config. The current DNS config
1069 might have even more configs, not known to the controller, YET
1072 for (NSString *domain in old_domain_list) {
1076 agent = [self.floatingDNSAgentList objectForKey:domain];
1081 /* Am I mapped to some agent? */
1082 mapped_agent = [agent getAgentMapping];
1084 /* OK, this agent is mapped to some other agent. We compare this agent's data
1085 * to the current data of the agent to which it is mapped. If different, we destroy
1086 * the agent and later map it to someone else OR spawn a new one.
1088 NSData *mapped_agent_data;
1090 mapped_agent_data = [self getDNSDataFromCurrentConfig:dns_config domain:[mapped_agent getAssociatedEntity]];
1091 if (mapped_agent_data == nil || ![[agent getAgentData] isEqual:mapped_agent_data]) {
1092 /* Something changed for mapped agent */
1093 [deleteList addObject:agent];
1097 /* Since this agent is NOT mapped to any other agent, this agent is
1098 * registered with the kernel. So instead of destroying the agent and
1099 * re-registering it, just update it here.
1101 * All the agents which were mapped to this agent, will be deleted and
1102 * re-mapped, if the data changed.
1106 agent_data = [self getDNSDataFromCurrentConfig:dns_config domain:[agent getAssociatedEntity]];
1107 if (![[agent getAgentData] isEqual:agent_data]) {
1108 /* Something changed for agent */
1109 [agent updateAgentData:agent_data];
1111 /* The reason I don't publish the data to agent here is that, if there were
1112 * some agents mapping to this one, they will momentarily have a policy for
1113 * using this agent UUID for some domain based on this agent's previous data.
1115 [update_agent_list addObject:agent];
1119 [new_domain_list removeObject:domain];
1122 for (id agent in deleteList) {
1123 SC_log(LOG_INFO, "Destroying agent %@ because something changed!", [agent getAgentName]);
1124 [self destroyFloatingAgent:agent];
1127 for (id agent in update_agent_list) {
1128 [self publishToAgent:agent];
1131 for (int idx = 0; idx < dns_config->n_resolver; idx++) {
1132 dns_resolver_t * resolver;
1134 resolver = dns_config->resolver[idx];
1135 if (resolver->domain != NULL &&
1136 ![self isResolverPrivate:resolver] &&
1137 ![self isResolverMulticast:resolver]) {
1141 NSString * ns_domain_name;
1143 ns_domain_name = @(resolver->domain);
1144 found = [new_domain_list indexOfObject:ns_domain_name];
1145 if (found == NSNotFound) {
1146 /* Nothing changed for this agent */
1150 /* We will only process agents which are mapped AND if the agent they were mapped to, changed OR
1151 * agents for domains which we did not know before.
1154 NSUInteger domainInstance = [duplicate_domain_list countForObject:ns_domain_name];
1155 if (domainInstance > 0) {
1156 /* domainInstance will be > 0, only if we have conflicting domains */
1158 data = [self dataForResolver:resolver];
1160 NSString *ns_domain_name_copy = [NSString stringWithFormat:@"%@" multipleEntitySuffix "%lu", ns_domain_name, (unsigned long)domainInstance];
1162 BOOL ok = [self spawnFloatingAgent:[DNSAgent class]
1163 entity:ns_domain_name_copy
1164 agentSubType:kAgentSubTypeSupplemental
1165 addPolicyOfType:NEPolicyConditionTypeDomain
1168 id agent = [self.floatingDNSAgentList objectForKey:ns_domain_name_copy];
1169 SC_log(LOG_INFO, "Duplicate DNS agent %@", [agent getAgentName]);;
1172 data = [self dataForResolver:resolver];
1173 mapped_agent = [self getAgentWithSameDataAndSubType:self.floatingDNSAgentList
1175 subType:kAgentSubTypeSupplemental];
1176 if (mapped_agent != nil) {
1177 [self spawnMappedFloatingAgent:mapped_agent
1178 entity:ns_domain_name
1179 agentSubType:kAgentSubTypeSupplemental
1180 addPolicyOfType:NEPolicyConditionTypeDomain
1183 [self spawnFloatingAgent:[DNSAgent class]
1184 entity:ns_domain_name
1185 agentSubType:kAgentSubTypeSupplemental
1186 addPolicyOfType:NEPolicyConditionTypeDomain
1191 [new_domain_list removeObjectAtIndex:found];
1192 [duplicate_domain_list addObject:ns_domain_name];
1200 - (void)processDNSResolvers:(dns_config_t *)dns_config
1202 resolver_list_t *resolvers = [self copyResolverList:dns_config];
1204 /* Process Default resolvers */
1205 NSMutableArray *old_default_resolver_list = [self getAgentList:self.floatingDNSAgentList
1206 agentType:kAgentTypeDNS
1207 agentSubType:kAgentSubTypeDefault];
1209 // For default resolvers, their name will be '_defaultDNS', '_defaultDNS #2' so on...
1210 if (resolvers->n_default_resolvers > 0 && resolvers->default_resolvers != NULL) {
1211 for (int i = 0; i < resolvers->n_default_resolvers; i++) {
1212 dns_resolver_t *default_resolver = resolvers->default_resolvers[i];
1215 NSString * resolverName;
1217 data = [self dataForResolver:default_resolver];
1219 resolverName = @(dnsAgentDefault);
1221 resolverName = [NSString stringWithFormat:@dnsAgentDefault multipleEntitySuffix "%d", i+1 ];
1224 dnsAgent = [self.floatingDNSAgentList objectForKey:resolverName];
1226 if (dnsAgent != nil) {
1227 [old_default_resolver_list removeObject:resolverName];
1228 if ([data isEqual:[dnsAgent getAgentData]]) {
1229 /* Leave this agent in place. Nothing changed! */
1232 [self destroyFloatingAgent:dnsAgent];
1236 [self spawnFloatingAgent:[DNSAgent class]
1238 agentSubType:kAgentSubTypeDefault
1239 addPolicyOfType:NEPolicyConditionTypeNone
1244 // Only agents that are NOT present in the new config, will be present in the list
1245 // and they need to be destroyed.
1246 [self deleteAgentList:self.floatingDNSAgentList list:old_default_resolver_list];
1248 /* Process Multicast resolvers */
1250 NSMutableArray *old_multicast_resolver_list = [self getAgentList:self.floatingDNSAgentList
1251 agentType:kAgentTypeDNS
1252 agentSubType:kAgentSubTypeMulticast];
1254 if (resolvers->n_multicast_resolvers > 0 && resolvers->multicast_resolvers != NULL) {
1255 for (int i = 0; i < resolvers->n_multicast_resolvers; i++) {
1256 dns_resolver_t * multicast_resolver = resolvers->multicast_resolvers[i];
1258 NSString * resolverName;
1260 if (multicast_resolver == NULL) {
1264 if (multicast_resolver->domain == NULL) {
1265 /* Multicast resolvers MUST have a domain */
1269 resolverName = @(multicast_resolver->domain);
1270 if (resolverName == NULL) {
1271 /* Multicast resolvers MUST have a domain */
1275 dnsAgent = [self.floatingDNSAgentList objectForKey:resolverName];
1276 if (dnsAgent != nil) {
1277 [old_multicast_resolver_list removeObject:resolverName];
1281 [self spawnFloatingAgent:[DNSAgent class]
1283 agentSubType:kAgentSubTypeMulticast
1284 addPolicyOfType:NEPolicyConditionTypeDomain
1286 // Don't care about data for mdns resolvers. Do we?
1290 [self deleteAgentList:self.floatingDNSAgentList list:old_multicast_resolver_list];
1292 /* Process Private resolvers */
1294 NSMutableArray *old_private_resolver_list = [self getAgentList:self.floatingDNSAgentList
1295 agentType:kAgentTypeDNS
1296 agentSubType:kAgentSubTypePrivate];
1298 if (resolvers->n_private_resolvers > 0 && resolvers->private_resolvers != NULL) {
1299 for (int i = 0; i < resolvers->n_private_resolvers; i++) {
1300 dns_resolver_t * private_resolver = resolvers->private_resolvers[i];
1302 NSString * resolverName;
1304 if (private_resolver == NULL) {
1308 if (private_resolver->domain == NULL) {
1309 /* private resolvers MUST have a domain */
1313 resolverName = @(private_resolver->domain);
1314 if (resolverName == nil) {
1315 /* Private resolvers MUST have a domain */
1319 dnsAgent = [self.floatingDNSAgentList objectForKey:resolverName];
1320 if (dnsAgent != nil) {
1321 [old_private_resolver_list removeObject:resolverName];
1325 [self spawnFloatingAgent:[DNSAgent class]
1327 agentSubType:kAgentSubTypePrivate
1328 addPolicyOfType:NEPolicyConditionTypeDomain
1330 // Don't care about data for pdns resolvers. Do we?
1334 [self deleteAgentList:self.floatingDNSAgentList list:old_private_resolver_list];
1337 [self freeResolverList:resolvers];
1340 - (void)processScopedDNSResolvers:(dns_config_t *)dns_config;
1342 NSMutableArray * old_intf_list;
1343 old_intf_list = [self getAgentList:self.floatingDNSAgentList
1344 agentType:kAgentTypeDNS
1345 agentSubType:kAgentSubTypeScoped];
1347 if ((dns_config->n_scoped_resolver > 0) && (dns_config->scoped_resolver != NULL)) {
1348 for (int i = 0; i < dns_config->n_scoped_resolver; i++) {
1354 NSString * ns_if_name;
1355 NSString * ns_if_name_with_prefix;
1356 dns_resolver_t * resolver;
1358 resolver = dns_config->scoped_resolver[i];
1359 if_name = if_indextoname(resolver->if_index, buf);
1361 ns_if_name = @(if_name);
1362 ns_if_name_with_prefix = [NSString stringWithFormat:@"%s%@", prefixForInterfaceName, ns_if_name];
1367 data = [self dataForResolver:resolver];
1368 idx = [old_intf_list indexOfObject:ns_if_name_with_prefix];
1370 if (idx == NSNotFound) {
1371 /* We need to spawn an agent */
1372 [self spawnFloatingAgent:[DNSAgent class]
1373 entity:ns_if_name_with_prefix
1374 agentSubType:kAgentSubTypeScoped
1375 addPolicyOfType:NEPolicyConditionTypeScopedInterface
1379 /* We have an agent on this interface. Update it */
1380 [old_intf_list removeObjectAtIndex:idx];
1383 /* Get the DNS agent for this interface? */
1384 dnsAgent = [self.floatingDNSAgentList objectForKey:ns_if_name_with_prefix];
1385 if (dnsAgent != nil) {
1386 /* Do we need to update this agent? */
1387 [dnsAgent updateAgentData:data];
1388 if ([dnsAgent shouldUpdateAgent]) {
1389 [self publishToAgent:dnsAgent];
1395 [self deleteAgentList:self.floatingDNSAgentList list:old_intf_list];
1398 - (void)processServiceSpecificDNSResolvers:(dns_config_t *)dns_config;
1400 NSMutableArray * old_service_list;
1401 old_service_list = [self getAgentList:self.floatingDNSAgentList
1402 agentType:kAgentTypeDNS
1403 agentSubType:kAgentSubTypeServiceSpecific];
1405 if ((dns_config->n_service_specific_resolver > 0) && (dns_config->service_specific_resolver != NULL)) {
1406 for (int i = 0; i < dns_config->n_service_specific_resolver; i++) {
1410 uint32_t service_identifier;
1411 NSString * ns_service_identifier_with_prefix;
1412 dns_resolver_t * resolver;
1414 resolver = dns_config->service_specific_resolver[i];
1415 service_identifier = resolver->service_identifier;
1416 if (service_identifier != 0) {
1417 ns_service_identifier_with_prefix = [NSString stringWithFormat:@"%s%u", prefixForInterfaceName, service_identifier];
1422 data = [self dataForResolver:resolver];
1423 idx = [old_service_list indexOfObject:ns_service_identifier_with_prefix];
1425 if (idx == NSNotFound) {
1426 /* We need to spawn an agent */
1427 [self spawnFloatingAgent:[DNSAgent class]
1428 entity:ns_service_identifier_with_prefix
1429 agentSubType:kAgentSubTypeServiceSpecific
1430 addPolicyOfType:(POLICY_TYPE_NO_POLICY) /* Don't install a policy */
1434 /* We have an agent on this interface. Update it */
1435 [old_service_list removeObjectAtIndex:idx];
1438 /* Get the DNS agent for this interface? */
1439 dnsAgent = [self.floatingDNSAgentList objectForKey:ns_service_identifier_with_prefix];
1440 if (dnsAgent != nil) {
1441 /* Do we need to update this agent? */
1442 [dnsAgent updateAgentData:data];
1443 if ([dnsAgent shouldUpdateAgent]) {
1444 [self publishToAgent:dnsAgent];
1450 [self deleteAgentList:self.floatingDNSAgentList list:old_service_list];
1453 #define ONION_RESOLVER_DOMAIN "onion"
1454 - (BOOL)isResolverOnion:(dns_resolver_t *)resolver
1456 if (resolver->domain != NULL &&
1457 (strcmp(resolver->domain, ONION_RESOLVER_DOMAIN) == 0)) {
1465 - (void)processOnionResolver:(dns_config_t *)dns_config
1467 static NSUInteger policy_id = 0;
1469 if (dns_config == NULL) {
1473 /* Run through the resolver configurations. We only care for the supplemental resolvers. */
1474 for (int32_t i = 0; i < dns_config->n_resolver; i++) {
1475 dns_resolver_t *resolver = dns_config->resolver[i];
1476 if ([self isResolverOnion:resolver]) {
1481 /* We do not have any such resolver. Add a system-wide "drop" policy for this domain */
1482 if (policy_id == 0) {
1483 NEPolicy *policy = [[NEPolicy alloc] initWithOrder:INIT_ORDER_FOR_DOMAIN_POLICY
1484 result:[NEPolicyResult drop]
1485 conditions:@[[NEPolicyCondition domain:@ONION_RESOLVER_DOMAIN]]];
1486 if (policy != nil) {
1487 policy_id = [self.policySession addPolicy:policy];
1488 if (![self.policySession apply]) {
1490 SC_log(LOG_NOTICE, "Could not add a [." ONION_RESOLVER_DOMAIN "] drop policy");
1492 SC_log(LOG_INFO, "Added a [." ONION_RESOLVER_DOMAIN "] drop policy");
1501 /* We have such a resolver in the config OR no DNS config at all. Remove the system-wide "drop" policy for this domain */
1502 if (policy_id > 0) {
1503 [self.policySession removePolicyWithID:policy_id];
1504 if (![self.policySession apply]) {
1505 SC_log(LOG_NOTICE, "Could not remove the [." ONION_RESOLVER_DOMAIN "] drop policy");
1508 SC_log(LOG_INFO, "Removed the [." ONION_RESOLVER_DOMAIN "] drop policy");
1514 #undef ONION_RESOLVER_DOMAIN
1517 - (void)processDNSChanges
1519 dns_config_t * dns_config;
1521 dns_config = dns_configuration_copy();
1522 if (dns_config == NULL) {
1523 SC_log(LOG_INFO, "No DNS configuration");
1524 NSMutableDictionary *copy = [self.floatingDNSAgentList copy];
1525 for (NSString *entity in copy) {
1526 id agent = [copy objectForKey:entity];
1528 [self destroyFloatingAgent:agent];
1533 [self processDNSResolvers:dns_config];
1534 [self processScopedDNSResolvers:dns_config];
1535 [self processSupplementalDNSResolvers:dns_config];
1536 [self processServiceSpecificDNSResolvers:dns_config];
1540 [self processOnionResolver:dns_config];
1541 if (dns_config != NULL) {
1542 dns_configuration_free(dns_config);
1546 #pragma mark Helper functions
1548 - (const void *)copyConfigAgentData:(NSMutableDictionary *)controllerDict
1549 uuid:(uuid_t)requested_uuid
1550 length:(uint64_t *)length
1552 if (length == NULL) {
1553 SC_log(LOG_NOTICE, "Invalid parameters for copying agent data");
1558 void *buffer = NULL;
1561 for (NSString *key in controllerDict) {
1562 id temp_agent = [controllerDict objectForKey:key];
1566 [[temp_agent getAgentUUID] getUUIDBytes:agent_uuid];
1567 if (uuid_compare(agent_uuid, requested_uuid) == 0) {
1574 uuid_string_t uuid_str;
1575 uuid_unparse(requested_uuid, uuid_str);
1576 SC_log(LOG_NOTICE, "Invalid config agent uuid %s specified", uuid_str);
1580 NSData *data = [agent getAgentData];
1581 uint64_t len = [data length];
1584 buffer = malloc((size_t)len);
1585 memcpy(buffer, [data bytes], len);
1588 return (const void *)buffer;
1591 - (const void *)copyProxyAgentData:(uuid_t)requested_uuid
1592 length:(uint64_t *)length
1594 return [self copyConfigAgentData:self.floatingProxyAgentList
1599 - (const void *)copyDNSAgentData:(uuid_t)requested_uuid
1600 length:(uint64_t *)length
1602 return [self copyConfigAgentData:self.floatingDNSAgentList
1607 - (NSData *)dataLengthSanityCheck:(id)agent
1609 NSData * data = [agent getAgentData];
1611 if ([data length] > CONFIG_AGENT_DATA_LIMIT) {
1612 /* We impose a limit on the config agent data as 1KB.
1613 * If we have a data blob larger than this limit, do NOT publish it into the agent.
1614 * Instead publish a key which will trigger fetching of the configuration directly
1615 * through NWI server.
1617 NSMutableDictionary *data_dict = [NSMutableDictionary dictionary];
1619 NSUUID *uuid = [agent getAgentUUID];
1621 [uuid getUUIDBytes:c_uuid];
1622 NSData *uuid_data = [[NSData alloc] initWithBytes:c_uuid length:sizeof(c_uuid)];
1623 [data_dict setValue:uuid_data forKey:@kConfigAgentOutOfBandDataUUID];
1625 NSData *new_data = [NSPropertyListSerialization dataWithPropertyList:data_dict
1626 format:NSPropertyListBinaryFormat_v1_0
1637 * For conflicting agents, the convention is that its name & entity,
1638 * will have a suffix " #<number>". This function will sanitize the
1639 * suffix and just return the entity name
1641 - (NSString *)sanitizeEntity:(NSString *)entity
1643 NSRange range = [entity rangeOfString:@multipleEntitySuffix];
1644 if (range.location != NSNotFound) {
1645 NSString *str = [entity substringToIndex:range.location];
1653 * For interface names, there is a prefix to differentiate then
1654 * from the domain name (iff there were conflicting domain names).
1655 * Returns the sanitized interface name
1657 - (NSString *)sanitizeInterfaceName:(NSString *)intf
1659 NSRange range = [intf rangeOfString:@prefixForInterfaceName];
1660 if (range.location != NSNotFound) {
1661 NSString *str = [intf substringFromIndex:(range.location + strlen(prefixForInterfaceName))];
1669 * For conflicting agents, the convention is that its name & entity,
1670 * will have a suffix " #<number>". This function will return that <number>
1672 - (int)entityInstanceNumber:(NSString *)entity
1674 NSRange range = [entity rangeOfString:@multipleEntitySuffix];
1675 if (range.location != NSNotFound) {
1676 NSString *str = [entity substringFromIndex:(range.location + strlen(multipleEntitySuffix))];
1677 return str.intValue;
1684 * In case that we have conflicting DNS/Proxy domains
1685 * This function will remove all those conflicting agents,
1686 * so that we can start afresh with the new config
1688 - (void)cleanConflictingAgentsFromList:(NSMutableArray *)old_list
1689 new_list:(NSMutableArray *)new_list
1690 agentDictionary:(NSMutableDictionary *)agent_list
1692 NSCountedSet * duplicate_domain_list;
1694 for (NSString *domain in old_list) {
1695 /* If we had conflicting domains before, remove all of them */
1696 NSString *sanitizedDomain = [self sanitizeEntity:domain];
1697 if (![sanitizedDomain isEqualToString:domain]) {
1698 /* Destroy the original domain */
1699 id agent = [agent_list objectForKey:sanitizedDomain];
1700 [self destroyFloatingAgent:agent];
1702 /* Destroy the conflicting domain */
1703 agent = [agent_list objectForKey:domain];
1704 [self destroyFloatingAgent:agent];
1706 SC_log(LOG_INFO, "Removing conflicting domain: %@, %@", sanitizedDomain, domain);
1710 duplicate_domain_list = [[NSCountedSet alloc] initWithArray:new_list];
1711 for (NSString *domain in old_list) {
1712 if ([duplicate_domain_list countForObject:domain] > 1) {
1713 id agent = [agent_list objectForKey:domain];
1714 [self destroyFloatingAgent:agent];
1715 SC_log(LOG_INFO, "Removing domain %@ as it has duplicates in the current config", domain);
1721 * Get the list of agents from a specific dictionary.
1722 * The list of agents will only consist of the ones which
1723 * match the agent type and sub-type
1726 - (NSMutableArray *)getAgentList:(NSMutableDictionary *)all_agents
1727 agentType:(AgentType)type
1728 agentSubType:(AgentSubType)subtype
1730 NSMutableArray *list = [NSMutableArray array];
1731 NSArray *agentObjects = [all_agents allValues];
1733 for (id agent in agentObjects) {
1734 if (([agent getAgentType] == type) &&
1735 ([agent getAgentSubType] == subtype)) {
1737 [list addObject:[agent getAssociatedEntity]];
1745 * Destroy all the agents are listed in "list"
1748 - (void)deleteAgentList:(NSMutableDictionary *)all_agents
1749 list:(NSMutableArray *)list
1751 for (NSString *intf in list) {
1754 agent = [all_agents objectForKey:intf];
1755 [self destroyFloatingAgent:agent];
1760 * In order to not duplicate agents with same content,
1761 * we map an agent X to agent Y, when their content is the same.
1763 * This function tries to find that agent Y
1766 - (id)getAgentWithSameDataAndSubType:(NSMutableDictionary *)agentList
1768 subType:(AgentSubType)subtype
1770 for (NSString *key in agentList) {
1771 id agent = [agentList objectForKey:key];
1772 if ([[agent getAgentData] isEqual:data]) {
1773 /* Do not map to default agents */
1774 if ([agent getAgentSubType] != subtype) {
1778 /* Return only registered agents */
1779 if ([agent getRegistrationObject] != nil) {
1788 #pragma mark Policy installation function
1791 * Add NECP policies for an agent
1793 - (BOOL)addPolicyToFloatingAgent:(id)agent
1794 domain:(NSString *)domain
1795 agentUUIDToUse:(NSUUID *)uuid
1796 policyType:(NEPolicyConditionType)policyType
1797 useControlPolicySession:(BOOL)useControlPolicySession
1799 NEPolicyCondition * condition = nil;
1800 NEPolicySession * session;
1801 uint32_t multiple_entity_offset;
1802 NEPolicy * newPolicy;
1805 uint32_t orderForSkip;
1806 NSMutableArray * policyArray;
1807 NSUInteger policyID1;
1808 NSUInteger policyID2;
1809 NEPolicyResult * result;
1812 uint32_t typeOffset;
1814 type = [agent getAgentType];
1815 typeOffset = (type == kAgentTypeDNS) ? 0 : 5000;
1816 skipOrder = (type == kAgentTypeDNS) ? 5000 : 0;
1818 multiple_entity_offset = (uint32_t)[self entityInstanceNumber:domain];
1819 domain = [self sanitizeEntity:domain];
1821 switch (policyType) {
1822 case NEPolicyConditionTypeScopedInterface:
1823 order = INIT_ORDER_FOR_SCOPED_INTERFACE_POLICY + typeOffset + multiple_entity_offset;
1824 domain = [self sanitizeInterfaceName:domain];
1825 condition = [NEPolicyCondition scopedInterface:domain];
1826 orderForSkip = SKIP_ORDER_FOR_SCOPED_INTERFACE_POLICY + typeOffset;
1829 case NEPolicyConditionTypeDomain:
1830 order = INIT_ORDER_FOR_DOMAIN_POLICY + typeOffset + multiple_entity_offset;
1831 condition = [NEPolicyCondition domain:domain];
1832 orderForSkip = SKIP_ORDER_FOR_DOMAIN_POLICY + typeOffset;
1835 case NEPolicyConditionTypeAllInterfaces:
1836 order = INIT_ORDER_FOR_DEFAULT_POLICY + typeOffset + multiple_entity_offset;
1837 condition = [NEPolicyCondition allInterfaces];
1838 orderForSkip = SKIP_ORDER_FOR_DEFAULT_POLICY + typeOffset;
1841 case NEPolicyConditionTypeNone:
1842 order = INIT_ORDER_FOR_DEFAULT_POLICY + typeOffset + multiple_entity_offset;
1843 orderForSkip = SKIP_ORDER_FOR_DEFAULT_POLICY + typeOffset;
1847 SC_log(LOG_NOTICE, "Invalid policy condition specified");
1851 result = [NEPolicyResult netAgentUUID:uuid];
1852 newPolicy = [[NEPolicy alloc] initWithOrder:order
1854 conditions: (condition ? @[condition] : nil)];
1856 if (newPolicy == nil) {
1857 SC_log(LOG_NOTICE, "Could not create a policy for agent %@", [agent getAgentName]);
1861 if (useControlPolicySession) {
1862 if (self.controlPolicySession == nil) {
1863 /* The NE policy session at "control" level for the controller */
1864 self.controlPolicySession = [self createPolicySession];
1865 if (self.controlPolicySession == nil) {
1866 SC_log(LOG_NOTICE, "Could not create a control policy session for agent %@", [agent getAgentName]);
1869 [self.controlPolicySession setPriority:NEPolicySessionPriorityControl];
1871 ((ConfigAgent *)agent).preferredPolicySession = self.controlPolicySession;
1873 ((ConfigAgent *)agent).preferredPolicySession = self.policySession;
1876 session = ((ConfigAgent *)agent).preferredPolicySession;
1878 policyID1 = [session addPolicy:newPolicy];
1879 if (policyID1 == 0) {
1880 SC_log(LOG_NOTICE, "Could not add a netagent policy for agent %@", [agent getAgentName]);
1884 result = [NEPolicyResult skipWithOrder:skipOrder];
1885 newPolicy = [[NEPolicy alloc] initWithOrder:orderForSkip
1887 conditions:(condition ? @[condition] : nil)];
1889 if (newPolicy == nil) {
1890 SC_log(LOG_NOTICE, "Could not create a policy for agent %@", [agent getAgentName]);
1894 policyID2 = [session addPolicy:newPolicy];
1895 if (policyID2 == 0) {
1896 SC_log(LOG_NOTICE, "Could not add a skip policy for agent %@", [agent getAgentName]);
1900 ok = [session apply];
1902 SC_log(LOG_NOTICE, "Could not apply policy for agent %@", [agent getAgentName]);
1906 policyArray = [self.policyDB objectForKey:[agent getAgentName]];
1907 if (policyArray == nil) {
1908 policyArray = [NSMutableArray array];
1911 [policyArray addObject:numberToNSNumber(policyID1)];
1912 [policyArray addObject:numberToNSNumber(policyID2)];
1913 [self.policyDB setObject:policyArray forKey:[agent getAgentName]];
1918 #pragma mark Agent manipulation functions
1923 - (BOOL)spawnFloatingAgent:(Class)agentClass
1924 entity:(NSString *)entity
1925 agentSubType:(AgentSubType)subtype
1926 addPolicyOfType:(NEPolicyConditionType)policyType
1927 publishData:(NSData *)data
1931 NSMutableDictionary * parameters;
1933 parameters =[NSMutableDictionary dictionary];
1934 [parameters setValue:entity forKey:@kEntityName];
1935 [parameters setValue:numberToNSNumber(subtype) forKey:@kAgentSubType];
1937 agent = [[agentClass alloc] initWithParameters:parameters];
1938 ok = [self registerAgent:agent];
1944 /* Since we just spawned this agent, update its data */
1945 [agent updateAgentData:data];
1946 [self publishToAgent:agent];
1949 /* Add a policy if there is a valid type. If POLICY_TYPE_NO_POLICY, then ignore policies.
1950 * POLICY_TYPE_NO_POLICY will be set for service-specific agents, in which case we rely on
1951 * service owners to install custom policies to point at the agents. */
1952 if (policyType >= NEPolicyResultTypeNone) {
1953 BOOL useControlPolicySession = NO;
1954 if (subtype == kAgentSubTypeGlobal) {
1955 /* Policies for a Global scoped agents are at "control" level */
1956 useControlPolicySession = YES;
1959 ok = [self addPolicyToFloatingAgent:agent
1961 agentUUIDToUse:[agent agentUUID]
1962 policyType:policyType
1963 useControlPolicySession:useControlPolicySession];
1966 [self unregisterAgent:agent];
1971 SC_log(LOG_INFO, "Spawning floating agent for %@", entity);
1973 if ([agent getAgentType] == kAgentTypeProxy) {
1974 [self.floatingProxyAgentList setObject:agent forKey:entity];
1976 [self.floatingDNSAgentList setObject:agent forKey:entity];
1983 * Create an agent mapped to another agent.
1985 - (BOOL)spawnMappedFloatingAgent:(id)mapped_agent
1986 entity:(NSString *)entity
1987 agentSubType:(AgentSubType)subtype
1988 addPolicyOfType:(NEPolicyConditionType)policyType
1989 updateData:(NSData *)data
1992 NSMutableDictionary * parameters;
1994 parameters = [NSMutableDictionary dictionary];
1995 [parameters setValue:entity forKey:@kEntityName];
1996 [parameters setValue:numberToNSNumber(subtype) forKey:@kAgentSubType];
1998 dummyAgent = [[[mapped_agent class] alloc] initWithParameters:parameters];
2001 /* Since we just spawned this agent, update its data.
2002 * We do not publish it since this agent is mapped
2003 * to an agent which already has the same data
2005 [dummyAgent updateAgentData:data];
2008 BOOL useControlPolicySession = NO;
2009 if (subtype == kAgentSubTypeGlobal) {
2010 /* Policies for a Global scoped agents are at "control" level */
2011 useControlPolicySession = YES;
2014 BOOL ok = [self addPolicyToFloatingAgent:dummyAgent
2016 agentUUIDToUse:[mapped_agent agentUUID]
2017 policyType:policyType
2018 useControlPolicySession:useControlPolicySession];
2024 if ([mapped_agent getAgentType] == kAgentTypeProxy) {
2025 [self.floatingProxyAgentList setObject:dummyAgent forKey:entity];
2027 [self.floatingDNSAgentList setObject:dummyAgent forKey:entity];
2030 [dummyAgent setAgentMapping:mapped_agent];
2032 SC_log(LOG_INFO, "Mapped floating agent %@ to %@", [dummyAgent getAgentName], [mapped_agent getAgentName]);
2037 * Write into an agent
2039 - (BOOL)publishToAgent:(id)agent
2041 /* Before any data goes into the kernel, do a sanity check. */
2042 NSData *sanityCheckData = [self dataLengthSanityCheck:agent];
2043 NSData *tempAgentData = nil;
2045 if (sanityCheckData != nil) {
2046 /* Data length is more than the limit! for updateNetworkAgent, the data blob
2047 * has to be a part of the agent object. Thus the temporary data replacement!
2049 tempAgentData = [[agent getAgentData] copy];
2050 [agent updateAgentData:sanityCheckData];
2051 SC_log(LOG_NOTICE, "Data too large for %@ (%lu bytes)!", [agent getAgentName], (unsigned long)[tempAgentData length]);
2056 NWNetworkAgentRegistration *regObject = [agent valueForKey:@"registrationObject"];
2057 if (regObject != nil) {
2058 SC_log(LOG_NOTICE, "Publishing data to agent %@ (%lu bytes)", [agent getAgentName], (unsigned long)[[agent getAgentData] length]);
2059 ok = [regObject updateNetworkAgent:agent];
2061 SC_log(LOG_NOTICE, "Could not update config agent");
2064 SC_log(LOG_NOTICE, "Config Agent not registered. Cannot Update");
2067 if (tempAgentData != nil) {
2068 [agent updateAgentData:tempAgentData];
2077 - (BOOL)destroyFloatingAgent:(id)agent
2081 if ( agent != nil) {
2082 NSMutableArray * policyArray;
2084 policyArray = [self.policyDB objectForKey:[agent getAgentName]];
2085 if (policyArray != nil) {
2086 NEPolicySession * session = ((ConfigAgent *)agent).preferredPolicySession;
2089 for (NSNumber *policyID in policyArray) {
2092 idVal = [policyID unsignedIntegerValue];
2093 result = [session removePolicyWithID:idVal];
2095 SC_log(LOG_NOTICE, "Could not remove policy %@ for agent %@", [session policyWithID:idVal], [agent getAgentName]);
2099 result = [session apply];
2101 SC_log(LOG_NOTICE, "Could not apply removed policies for agent %@", [agent getAgentName]);
2104 [self.policyDB removeObjectForKey:[agent getAgentName]];
2107 if ([agent getAgentType] == kAgentTypeProxy) {
2108 [self.floatingProxyAgentList removeObjectForKey:[agent getAssociatedEntity]];
2110 [self.floatingDNSAgentList removeObjectForKey:[agent getAssociatedEntity]];
2113 if ([agent getRegistrationObject] != nil) {
2114 [self unregisterAgent:agent];
2117 SC_log(LOG_INFO, "X - Destroyed agent %@", [agent getAgentName]);
2119 /* Check if we need to close the "control" policy session */
2120 if (self.controlPolicySession != nil) {
2121 NSMutableArray *globalProxyAgentList;
2122 NSMutableArray *globalDNSAgentList;
2123 globalProxyAgentList = [self getAgentList:self.floatingProxyAgentList agentType:kAgentTypeProxy agentSubType:kAgentSubTypeGlobal];
2124 globalDNSAgentList = [self getAgentList:self.floatingDNSAgentList agentType:kAgentTypeDNS agentSubType:kAgentSubTypeGlobal];
2126 if ([globalProxyAgentList count] == 0 &&
2127 [globalDNSAgentList count] == 0) {
2128 [self.controlPolicySession removeAllPolicies];
2129 [self.controlPolicySession apply];
2130 self.controlPolicySession = nil;
2131 SC_log(LOG_NOTICE, "Closed control policy session");
2144 - (BOOL)registerAgent:(id)agent
2148 NWNetworkAgentRegistration *registration = [[NWNetworkAgentRegistration alloc] initWithNetworkAgentClass:[agent class]];
2150 ok = [registration registerNetworkAgent:agent];
2152 SC_log(LOG_NOTICE, "Could not register config agent");
2156 [agent addAgentRegistrationObject:registration];
2163 * Unregister an agent
2165 - (BOOL)unregisterAgent:(id)agent
2169 NWNetworkAgentRegistration *regObject = [agent valueForKey:@"registrationObject"];
2170 if (regObject != nil) {
2171 ok = [regObject unregisterNetworkAgent];
2173 SC_log(LOG_NOTICE, "Could not unregister config agent");
2176 SC_log(LOG_NOTICE, "Config Agent not registered. Cannot unregister");