2 * Copyright (c) 2015-2017 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>
28 #define numberToNSNumber(x) [NSNumber numberWithUnsignedInteger:x]
30 #define dnsAgentDefault "_defaultDNS"
31 #define proxyAgentDefault "_defaultProxy"
32 #define multipleEntitySuffix " #"
33 #define prefixForInterfaceName "@"
35 /* These define the starting and ending order of each policy section */
36 #define INIT_ORDER_FOR_SCOPED_INTERFACE_POLICY 100
37 #define INIT_ORDER_FOR_DOMAIN_POLICY 500
38 #define INIT_ORDER_FOR_DEFAULT_POLICY 1000
40 #define SKIP_ORDER_FOR_SCOPED_INTERFACE_POLICY 250
41 #define SKIP_ORDER_FOR_DOMAIN_POLICY 750
42 #define SKIP_ORDER_FOR_DEFAULT_POLICY 1250
44 #define POLICY_TYPE_NO_POLICY -1
45 #define CONFIG_AGENT_DATA_LIMIT MIN(NETAGENT_MAX_DATA_SIZE, 1024)
47 typedef struct resolverList {
48 dns_resolver_t **default_resolvers;
49 uint32_t n_default_resolvers;
50 dns_resolver_t **multicast_resolvers;
51 uint32_t n_multicast_resolvers;
52 dns_resolver_t **private_resolvers;
53 uint32_t n_private_resolvers;
56 @interface AgentController()
58 @property (nonatomic) NSMutableDictionary * floatingProxyAgentList;
59 @property (nonatomic) NSMutableDictionary * floatingDNSAgentList;
60 @property (nonatomic) NSMutableDictionary * policyDB;
61 @property (nonatomic) NEPolicySession * policySession;
62 @property (nonatomic) NEPolicySession * controlPolicySession;
66 @implementation AgentController
70 + (AgentController *)sharedController
72 static AgentController * gController = nil;
73 static dispatch_once_t onceToken;
75 dispatch_once(&onceToken, ^{
76 gController = [[AgentController alloc] init];
79 @synchronized (gController) {
80 if (![gController isControllerReady]) {
81 if (![gController initializeController]) {
94 [self initializeController];
100 - (BOOL)initializeController
102 const char *errorMessage = NULL;
105 /* The NE policy session for the controller */
107 if (self.policySession == nil) {
108 self.policySession = [self createPolicySession];
109 if (self.policySession == nil) {
110 errorMessage = "Failed to create a policy session";
115 /* A dictionary of all floating proxy agents
116 * Key : <entity-name> (can be an interface name or domain name)
117 * Value : agent object
120 if (self.floatingProxyAgentList == nil) {
121 self.floatingProxyAgentList = [NSMutableDictionary dictionary];
122 if (self.floatingProxyAgentList == nil) {
123 errorMessage = "Failed to create a dictionary";
128 /* A dictionary of all floating dns agents
129 * Key : <entity-name> (can be an interface name or domain name)
130 * Value : agent object
133 if (self.floatingDNSAgentList == nil) {
134 self.floatingDNSAgentList = [NSMutableDictionary dictionary];
135 if (self.floatingDNSAgentList == nil) {
136 errorMessage = "Failed to create a dictionary";
141 /* A dictionary for the maintaining the policy IDs for all installed policy.
142 * These IDs would be necessary to uninstall a policy when an agent goes away
143 * Key : agent name (which can be retrieved by [agent getAgentName])
144 * Value : An array of integers, each being a policy ID for that agent
147 if (self.policyDB == nil) {
148 self.policyDB = [NSMutableDictionary dictionary];
149 if (self.policyDB == nil) {
150 errorMessage = "Failed to create a dictionary";
155 /* The queue to run the all processing on */
157 if (self.controllerQueue == nil) {
158 self.controllerQueue = dispatch_queue_create("com.apple.SystemConfiguration.controllerQueue", NULL);
159 if (self.controllerQueue == nil) {
160 errorMessage = "Failed to create a queue";
166 if (errorMessage != NULL) {
167 /* Some error occurred. This is unlikely during controller initialization... */
168 SC_log(LOG_ERR, "Error occured while initializing AgentController: %s", errorMessage);
169 _SC_crash(errorMessage, NULL, NULL);
176 - (NEPolicySession *)createPolicySession
178 return [[NEPolicySession alloc] init];
181 - (BOOL)isControllerReady
183 /* Make sure that we have all our data structures in place */
184 return ((self.policySession != nil) &&
185 (self.floatingProxyAgentList != nil) &&
186 (self.floatingDNSAgentList != nil) &&
187 (self.policyDB != nil) &&
188 (self.controllerQueue != nil));
191 /* ========================== proxy agent helpers =========================== */
192 #pragma mark Proxy agent helper functions
194 - (NSData *)dataForProxyArray:(CFArrayRef)proxy_array_for_data
196 CFDataRef data = NULL;
197 (void)_SCSerialize(proxy_array_for_data, &data, NULL, NULL);
198 return (__bridge_transfer NSData *)data;
201 - (NSData *)dataForProxyDictionary:(CFDictionaryRef)domain_proxy
204 CFMutableDictionaryRef domain_proxy_dict;
206 if (domain_proxy == NULL) {
207 SC_log(LOG_NOTICE, "Invalid domain proxy dict");
211 domain_proxy_dict = CFDictionaryCreateMutableCopy(NULL, 0, domain_proxy);
212 CFDictionaryRemoveValue(domain_proxy_dict, kSCPropNetProxiesSupplementalMatchDomain);
214 data = (__bridge_transfer NSData *)(SCNetworkProxiesCreateProxyAgentData(domain_proxy_dict));
215 CFRelease(domain_proxy_dict);
220 - (NSData *)getProxyDataFromCurrentConfig:(CFDictionaryRef)proxies
221 domain:(NSString *)domain
225 CFArrayRef supplemental;
227 if (proxies == NULL || domain == nil) {
228 SC_log(LOG_NOTICE, "Invalid proxies/domain");
232 supplemental = CFDictionaryGetValue(proxies, kSCPropNetProxiesSupplemental);
233 count = supplemental ? CFArrayGetCount(supplemental) : 0;
235 for (idx = 0; idx < count; idx++) {
236 CFDictionaryRef domain_proxy;
237 CFStringRef match_domain;
239 domain_proxy = CFArrayGetValueAtIndex(supplemental, idx);
240 match_domain = CFDictionaryGetValue(domain_proxy, kSCPropNetProxiesSupplementalMatchDomain);
241 if (match_domain != NULL && CFEqual(match_domain, (__bridge CFTypeRef)(domain))) {
242 return [self dataForProxyDictionary:domain_proxy];
249 - (bool)getIntValue:(CFTypeRef)cf_value
250 valuePtr:(int *) int_value_ptr
253 if (cf_value && CFGetTypeID(cf_value) == CFNumberGetTypeID() && CFNumberGetValue(cf_value, kCFNumberIntType, int_value_ptr))
260 - (int)countProxyEntriesEnabled:(CFDictionaryRef)proxies
264 if (proxies == NULL) {
265 SC_log(LOG_NOTICE, "Invalid proxies");
269 if (([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesHTTPEnable) valuePtr:&enabled] && enabled > 0) ||
270 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesHTTPSEnable) valuePtr:&enabled] && enabled > 0) ||
271 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesProxyAutoConfigEnable) valuePtr:&enabled] && enabled > 0) ||
272 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesFTPEnable) valuePtr:&enabled] && enabled > 0) ||
273 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesGopherEnable) valuePtr:&enabled] && enabled > 0) ||
274 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesRTSPEnable) valuePtr:&enabled] && enabled > 0) ||
275 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesSOCKSEnable) valuePtr:&enabled] && enabled > 0) ||
276 ([self getIntValue:CFDictionaryGetValue(proxies, kSCPropNetProxiesProxyAutoDiscoveryEnable) valuePtr:&enabled] && enabled > 0)) {
283 - (void)processSupplementalProxyChanges:(CFDictionaryRef)proxies
286 NSMutableArray * deleteList;
287 NSCountedSet * duplicate_domain_list;
289 NSMutableArray * new_domain_list;
290 NSMutableArray * old_domain_list;
291 CFArrayRef supplemental;
292 NSMutableArray * update_agent_list;
294 if (proxies == NULL) {
295 SC_log(LOG_INFO, "No proxy config to process");
299 old_domain_list = [self getAgentList:self.floatingProxyAgentList
300 agentType:kAgentTypeProxy
301 agentSubType:kAgentSubTypeSupplemental];
302 duplicate_domain_list = [[NSCountedSet alloc] initWithCapacity:0];
303 new_domain_list = [NSMutableArray array];
304 update_agent_list = [NSMutableArray array];
305 supplemental = CFDictionaryGetValue(proxies, kSCPropNetProxiesSupplemental);
306 count = supplemental ? CFArrayGetCount(supplemental) : 0;
307 deleteList = [NSMutableArray array];
309 for (idx = 0; idx < count; idx++) {
310 CFDictionaryRef domain_proxy;
311 CFStringRef match_domain;
314 domain_proxy = CFArrayGetValueAtIndex(supplemental, idx);
315 match_domain = CFDictionaryGetValue(domain_proxy, kSCPropNetProxiesSupplementalMatchDomain);
316 if (match_domain == NULL) {
320 /* This domain is present in current config. But if it has generic (no protocols enabled)
321 * proxy content, there is no real use of that agent. Do NOT add it to
322 * the new_domain_list.
324 * This way, if there was an agent previously for this domain,
325 * it will be destroyed AND since it is not present in the new domain list, we wont
326 * spawn a new agent too! :)
329 proxy_count = [self countProxyEntriesEnabled:domain_proxy];
330 if (proxy_count == 0) {
331 SC_log(LOG_INFO, "Proxy settings on %@ are generic. Not recognizing as new domain", match_domain);
335 [new_domain_list addObject:(__bridge NSString *)match_domain];
338 [self cleanConflictingAgentsFromList:old_domain_list
339 new_list:new_domain_list
340 agentDictionary:self.floatingProxyAgentList];
342 for (NSString *key in old_domain_list) {
345 domain_present = [new_domain_list containsObject:key];
346 if (domain_present == NO) {
349 agent = [self.floatingProxyAgentList objectForKey:key];
350 [self destroyFloatingAgent:agent];
354 /* At this point, whatever is in the controller's floating agent list,
355 * is present in the current proxy config. The current proxy config
356 * might have even more configs, not known to the controller, YET
359 for (NSString *domain in old_domain_list) {
363 agent = [self.floatingProxyAgentList objectForKey:domain];
368 /* Am I mapped to some agent? */
369 mapped_agent = [agent getAgentMapping];
371 /* OK, this agent is mapped to some other agent. We compare this agent's data
372 * to the current data of the agent to which it is mapped. If different, we destroy
373 * the agent and later map it to someone else OR spawn a new one.
375 NSData * mapped_agent_data;
377 mapped_agent_data = [self getProxyDataFromCurrentConfig:proxies domain:[mapped_agent getAssociatedEntity]];
378 if (mapped_agent_data == nil || ![[agent getAgentData] isEqual:mapped_agent_data]) {
379 /* Something changed for mapped agent */
380 [deleteList addObject:agent];
384 /* Since this agent is NOT mapped to any other agent, this agent is
385 * registered with the kernel. So instead of destroying the agent and
386 * re-registering it, just update it here.
388 * All the agents which were mapped to this agent, will be deleted and
389 * re-mapped, if the data changed.
393 agent_data = [self getProxyDataFromCurrentConfig:proxies domain:[agent getAssociatedEntity]];
394 if (![[agent getAgentData] isEqual:agent_data]) {
395 /* Something changed for agent */
396 [agent updateAgentData:agent_data];
398 /* The reason I don't publish the data to agent here is that, if there were
399 * some agents mapping to this one, they will momentarily have a policy for
400 * using this agent UUID for some domain based on this agent's previous data.
402 [update_agent_list addObject:agent];
405 [new_domain_list removeObject:domain];
408 for (id agent in deleteList) {
409 SC_log(LOG_INFO, "Destroying agent %@ because something changed!", [agent getAgentName]);
410 [self destroyFloatingAgent:agent];
413 for (id agent in update_agent_list) {
414 [self publishToAgent:agent];
417 for (idx = 0; idx < count; idx++) {
418 CFDictionaryRef domain_proxy;
419 CFStringRef match_domain;
421 domain_proxy = CFArrayGetValueAtIndex(supplemental, idx);
422 match_domain = CFDictionaryGetValue(domain_proxy, kSCPropNetProxiesSupplementalMatchDomain);
424 if (match_domain != NULL) {
429 found = [new_domain_list indexOfObject:(__bridge id _Nonnull)(match_domain)];
430 if (found == NSNotFound) {
435 * We will only process agents which are mapped AND the agent they were mapped to, changed OR
436 * agents for domains which we did not know before.
439 NSUInteger domainInstance = [duplicate_domain_list countForObject:(__bridge id _Nonnull)(match_domain)];
440 if (domainInstance > 0) {
441 /* domainInstance will be > 0, only if we have conflicting domains */
443 NSString *ns_domain_name_copy = [NSString stringWithFormat:@"%@" multipleEntitySuffix "%lu", match_domain, (unsigned long)domainInstance];
445 data = [self dataForProxyDictionary:domain_proxy];
447 BOOL ok = [self spawnFloatingAgent:[ProxyAgent class]
448 entity:ns_domain_name_copy
449 agentSubType:kAgentSubTypeSupplemental
450 addPolicyOfType:NEPolicyConditionTypeDomain
453 id agent = [self.floatingProxyAgentList objectForKey:ns_domain_name_copy];
454 SC_log(LOG_INFO, "Duplicate Proxy agent %@", [agent getAgentName]);;
457 data = [self dataForProxyDictionary:domain_proxy];
458 mapped_agent = [self getAgentWithSameDataAndSubType:self.floatingProxyAgentList
460 subType:kAgentSubTypeSupplemental];
461 if (mapped_agent != nil) {
462 [self spawnMappedFloatingAgent:mapped_agent
463 entity:(__bridge NSString *)(match_domain)
464 agentSubType:kAgentSubTypeSupplemental
465 addPolicyOfType:NEPolicyConditionTypeDomain
468 [self spawnFloatingAgent:[ProxyAgent class]
469 entity:(__bridge NSString *)(match_domain)
470 agentSubType:kAgentSubTypeSupplemental
471 addPolicyOfType:NEPolicyConditionTypeDomain
476 [new_domain_list removeObjectAtIndex:found];
477 [duplicate_domain_list addObject:(__bridge id _Nonnull)(match_domain)];
484 - (void)processScopedProxyChanges:(CFDictionaryRef)proxies
486 NSMutableArray * old_intf_list;
487 CFDictionaryRef scoped_proxies;
488 CFIndex scoped_proxies_count;
490 old_intf_list = [self getAgentList:self.floatingProxyAgentList
491 agentType:kAgentTypeProxy
492 agentSubType:kAgentSubTypeScoped];
494 scoped_proxies = CFDictionaryGetValue(proxies, kSCPropNetProxiesScoped);
495 scoped_proxies_count = scoped_proxies ? CFDictionaryGetCount(scoped_proxies) : 0;
497 if (scoped_proxies_count > 0) {
500 keys = malloc(scoped_proxies_count * sizeof(void *));
501 CFDictionaryGetKeysAndValues(scoped_proxies, keys, NULL);
503 for (int i = 0; i < scoped_proxies_count; i++) {
507 NSString * ns_if_name;
508 NSString * ns_if_name_with_prefix;
512 ns_if_name = (__bridge NSString *)keys[i];
513 ns_if_name_with_prefix = [NSString stringWithFormat:@"%s%@", prefixForInterfaceName, ns_if_name];
515 /* Does the proxy config have any protocols enabled? */
516 proxy_count = [self countProxyEntriesEnabled:CFDictionaryGetValue(scoped_proxies,
517 (__bridge const void *)(ns_if_name))];
519 if (proxy_count == 0) {
520 SC_log(LOG_INFO, "Proxy settings on %@ are generic. Skipping", ns_if_name);
524 idx = [old_intf_list indexOfObject:ns_if_name_with_prefix];
526 matching = SCNetworkProxiesCopyMatching(proxies, NULL, (__bridge CFStringRef)(ns_if_name));
527 if (matching != NULL) {
528 data = [self dataForProxyArray:matching];
532 if (idx == NSNotFound) {
533 /* We need to spawn an agent */
534 [self spawnFloatingAgent:[ProxyAgent class]
535 entity:ns_if_name_with_prefix
536 agentSubType:kAgentSubTypeScoped
537 addPolicyOfType:NEPolicyConditionTypeScopedInterface
542 /* We have an agent for this interface. Update it */
543 [old_intf_list removeObjectAtIndex:idx];
546 proxyAgent = [self.floatingProxyAgentList objectForKey:ns_if_name_with_prefix];
547 if (proxyAgent != nil) {
548 /* Do we need to update this agent? */
549 [proxyAgent updateAgentData:data];
550 if ([proxyAgent shouldUpdateAgent]) {
551 [self publishToAgent:proxyAgent];
559 [self deleteAgentList:self.floatingProxyAgentList list:old_intf_list];
562 - (void)processServiceSpecificProxyChanges:(CFDictionaryRef)proxies
564 NSMutableArray * old_service_list;
565 CFDictionaryRef service_proxies;
566 CFIndex service_proxies_count;
568 old_service_list = [self getAgentList:self.floatingProxyAgentList
569 agentType:kAgentTypeProxy
570 agentSubType:kAgentSubTypeServiceSpecific];
572 service_proxies = CFDictionaryGetValue(proxies, kSCPropNetProxiesServices);
573 service_proxies_count = service_proxies ? CFDictionaryGetCount(service_proxies) : 0;
575 if (service_proxies_count > 0) {
578 keys = malloc(service_proxies_count * sizeof(void *));
579 CFDictionaryGetKeysAndValues(service_proxies, keys, NULL);
581 for (int i = 0; i < service_proxies_count; i++) {
584 NSString * ns_service_identifier = nil;
585 NSString * ns_service_with_prefix = nil;
588 CFDictionaryRef proxyDict = NULL;
590 ns_service_identifier = (__bridge NSString *)keys[i];
591 ns_service_with_prefix = [NSString stringWithFormat:@"%s%@", prefixForInterfaceName, ns_service_identifier];
593 /* Does the proxy config have any protocols enabled? */
594 proxy_count = [self countProxyEntriesEnabled:CFDictionaryGetValue(service_proxies,
595 (__bridge const void *)(ns_service_identifier))];
597 if (proxy_count == 0) {
598 SC_log(LOG_INFO, "Proxy settings on %@ are generic. Skipping", ns_service_identifier);
602 proxyDict = CFDictionaryGetValue(service_proxies, (__bridge CFStringRef)ns_service_identifier);
603 if (proxyDict != nil) {
604 data = [self dataForProxyArray:(__bridge CFArrayRef)(@[ (__bridge NSDictionary *)proxyDict ])];
607 idx = [old_service_list indexOfObject:ns_service_with_prefix];
608 if (idx == NSNotFound) {
609 /* We need to spawn an agent */
610 [self spawnFloatingAgent:[ProxyAgent class]
611 entity:ns_service_with_prefix
612 agentSubType:kAgentSubTypeServiceSpecific
613 addPolicyOfType:(POLICY_TYPE_NO_POLICY) /* Don't install a policy */
618 /* We have an agent for this service. Update it */
619 [old_service_list removeObjectAtIndex:idx];
622 proxyAgent = [self.floatingProxyAgentList objectForKey:ns_service_with_prefix];
623 if (proxyAgent != nil) {
624 /* Do we need to update this agent? */
625 [proxyAgent updateAgentData:data];
626 if ([proxyAgent shouldUpdateAgent]) {
627 [self publishToAgent:proxyAgent];
635 [self deleteAgentList:self.floatingProxyAgentList list:old_service_list];
638 - (BOOL)isGlobalProxy:(CFDictionaryRef)proxies
640 if (CFDictionaryContainsKey(proxies, kSCPropNetProxiesBypassAllowed)) {
642 * Since we did not ask to "bypass" the proxies, this key will always
643 * be present in a managed (global) proxy configuration
651 - (void)processDefaultProxyChanges:(CFDictionaryRef)proxies
653 CFArrayRef global_proxy;
654 CFIndex global_proxy_count;
655 CFMutableDictionaryRef proxies_copy;
657 proxies_copy = CFDictionaryCreateMutableCopy(NULL, 0, proxies);
658 CFDictionaryRemoveValue(proxies_copy, kSCPropNetProxiesScoped);
659 CFDictionaryRemoveValue(proxies_copy, kSCPropNetProxiesServices);
660 CFDictionaryRemoveValue(proxies_copy, kSCPropNetProxiesSupplemental);
662 global_proxy = CFArrayCreate(NULL, (const void **)&proxies_copy, 1, &kCFTypeArrayCallBacks);
663 global_proxy_count = CFArrayGetCount(global_proxy);
664 if (global_proxy_count > 0 &&
665 [self countProxyEntriesEnabled:proxies_copy] == 0) {
666 SC_log(LOG_INFO, "Proxy settings on defaultProxy are generic. Skipping");
667 global_proxy_count = 0;
669 CFRelease(proxies_copy);
671 if (global_proxy_count > 0) {
672 BOOL spawnAgent = YES;
676 data = [self dataForProxyArray:global_proxy];
677 proxyAgent = [self.floatingProxyAgentList objectForKey:@proxyAgentDefault];
678 if (proxyAgent != nil) {
679 if (![data isEqual:[proxyAgent getAgentData]]) {
680 [self destroyFloatingAgent:proxyAgent];
687 AgentSubType subtype = kAgentSubTypeDefault;
688 NEPolicyConditionType conditionType = NEPolicyConditionTypeNone;
689 if ([self isGlobalProxy:proxies_copy]) {
690 SC_log(LOG_INFO, "Global proxy detected...");
691 conditionType = NEPolicyConditionTypeAllInterfaces;
692 subtype = kAgentSubTypeGlobal;
695 [self spawnFloatingAgent:[ProxyAgent class]
696 entity:@proxyAgentDefault
698 addPolicyOfType:conditionType
702 /* No default proxy config OR generic (no protocols enabled) default proxy config.
703 * Destroy the default agent if we had one
707 proxyAgent = [self.floatingProxyAgentList objectForKey:@proxyAgentDefault];
708 if (proxyAgent != nil) {
709 [self destroyFloatingAgent:proxyAgent];
713 CFRelease(global_proxy);
716 - (void)processProxyChanges
718 CFDictionaryRef proxies;
720 proxies = SCDynamicStoreCopyProxiesWithOptions(NULL, NULL);
721 if (proxies == NULL) {
722 SC_log(LOG_INFO, "No proxy information");
724 NSMutableDictionary *copy = [self.floatingProxyAgentList copy];
725 for (NSString *entity in copy) {
726 id agent = [copy objectForKey:entity];
727 [self destroyFloatingAgent:agent];
733 [self processDefaultProxyChanges:proxies];
734 [self processScopedProxyChanges:proxies];
735 [self processSupplementalProxyChanges:proxies];
736 [self processServiceSpecificProxyChanges:proxies];
741 /* ========================== DNS agent helpers =========================== */
742 #pragma mark DNS agent helper functions
744 - (void)freeResolverList:(resolver_list_t *)resolvers
746 /* This is a shallow free of resolver_list_t only.
747 * The actual resolver pointers are owned by 'dns_config'
749 if (resolvers == NULL) {
753 if (resolvers->default_resolvers != NULL) {
754 free(resolvers->default_resolvers);
756 if (resolvers->multicast_resolvers != NULL) {
757 free(resolvers->multicast_resolvers);
759 if (resolvers->private_resolvers != NULL) {
760 free(resolvers->private_resolvers);
766 - (resolver_list_t *)copyResolverList:(dns_config_t *)dns_config
768 resolver_list_t *resolvers = NULL;
770 if ((dns_config->n_resolver > 0) && (dns_config->resolver != NULL)) {
775 resolvers = calloc(1, sizeof(resolver_list_t));
776 for (int i = 0; i < dns_config->n_resolver; i++) {
777 dns_resolver_t *r = dns_config->resolver[i];
779 if ([self isResolverMulticast:r]) {
780 resolvers->n_multicast_resolvers++;
783 } else if ([self isResolverPrivate:r]) {
784 resolvers->n_private_resolvers++;
788 // do not consider default resolvers with no nameservers
789 if (r->domain == NULL && r->n_nameserver > 0) {
790 resolvers->n_default_resolvers++;
794 SC_log(LOG_INFO, "Resolvers: %d default, %d multicast, %d private",
795 resolvers->n_default_resolvers,
796 resolvers->n_multicast_resolvers,
797 resolvers->n_private_resolvers);
799 if (resolvers->n_default_resolvers > 0) {
800 resolvers->default_resolvers = calloc(resolvers->n_default_resolvers,
801 sizeof(dns_resolver_t *));
803 if (resolvers->n_multicast_resolvers > 0) {
804 resolvers->multicast_resolvers = calloc(resolvers->n_multicast_resolvers,
805 sizeof(dns_resolver_t *));
807 if (resolvers->n_private_resolvers > 0) {
808 resolvers->private_resolvers = calloc(resolvers->n_private_resolvers,
809 sizeof(dns_resolver_t *));
812 for (int i = 0; i < dns_config->n_resolver; i++) {
813 dns_resolver_t *r = dns_config->resolver[i];
815 if ([self isResolverMulticast:r] &&
816 (a < resolvers->n_multicast_resolvers)) {
817 resolvers->multicast_resolvers[a++] = r;
820 } else if ([self isResolverPrivate:r] &&
821 (b < resolvers->n_private_resolvers)) {
822 resolvers->private_resolvers[b++] = r;
826 if ((r->domain == NULL) &&
827 (r->n_nameserver > 0) &&
828 (c < resolvers->n_default_resolvers)) {
829 resolvers->default_resolvers[c++] = r;
838 * Generate a data blob for the resolver.
839 * Currently the blob only has:
841 * - sockaddr structs for each nameserver
845 - (NSData *)dataForResolver:(dns_resolver_t *)resolver
848 CFMutableDictionaryRef resolverDict = nil;
850 if (resolver == NULL) {
851 SC_log(LOG_NOTICE, "Invalid dns resolver");
855 if (resolver->n_search > 0) {
856 if (resolverDict == nil) {
857 resolverDict = CFDictionaryCreateMutable(NULL,
859 &kCFTypeDictionaryKeyCallBacks,
860 &kCFTypeDictionaryValueCallBacks);
863 CFMutableArrayRef searchDomainArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
865 /* Append search domains */
866 for (int i = 0; i < resolver->n_search; i++) {
867 CFArrayAppendValue(searchDomainArray, (__bridge CFStringRef)(@(resolver->search[i])));
870 CFDictionaryAddValue(resolverDict, CFSTR(kConfigAgentDNSSearchDomains), searchDomainArray);
871 CFRelease(searchDomainArray);
874 /* Get the count of nameservers */
875 if (resolver->n_nameserver > 0) {
876 if (resolverDict == nil) {
877 resolverDict = CFDictionaryCreateMutable(NULL,
879 &kCFTypeDictionaryKeyCallBacks,
880 &kCFTypeDictionaryValueCallBacks);
883 CFMutableArrayRef nameserverArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
885 /* Get all the nameservers */
886 for (int i = 0; i < resolver->n_nameserver; i++) {
888 _SC_sockaddr_to_string(resolver->nameserver[i], buf, sizeof(buf));
890 CFArrayAppendValue(nameserverArray, (__bridge CFStringRef)(@(buf)));
894 CFDictionaryAddValue(resolverDict, CFSTR(kConfigAgentDNSNameServers), nameserverArray);
895 CFRelease(nameserverArray);
898 if (resolverDict != nil) {
899 data = [NSPropertyListSerialization dataWithPropertyList:(__bridge id _Nonnull)(resolverDict)
900 format:NSPropertyListBinaryFormat_v1_0
904 CFRelease(resolverDict);
907 return (NSData *)data;
910 - (NSData *)getDNSDataFromCurrentConfig:(dns_config_t *)dns_config
911 domain:(NSString *)domain
913 if (dns_config == NULL || domain == nil) {
914 SC_log(LOG_NOTICE, "Invalid dns_config/domain");
918 if ((dns_config->n_resolver > 0) && (dns_config->resolver != NULL)) {
919 for (int i = 0; i < dns_config->n_resolver; i++) {
920 dns_resolver_t * resolver;
922 resolver = dns_config->resolver[i];
923 if (resolver->domain != NULL &&
924 ![self isResolverMulticast:resolver]) {
925 NSString * ns_domain_name;
927 ns_domain_name = @(resolver->domain);
928 if ([ns_domain_name isEqualToString:domain]) {
929 return [self dataForResolver:resolver];
940 - (BOOL)isResolverMulticast:(dns_resolver_t *)resolver
942 if (resolver->options == NULL) {
946 if (!strstr(resolver->options, "mdns")) {
953 - (BOOL)isResolverPrivate:(dns_resolver_t *)resolver
955 if (resolver->options == NULL) {
959 if (!strstr(resolver->options, "pdns")) {
966 - (void)processSupplementalDNSResolvers:(dns_config_t *)dns_config
968 NSMutableArray * deleteList;
969 NSMutableArray * new_domain_list;
970 NSCountedSet * duplicate_domain_list;
971 NSMutableArray * old_domain_list;
972 NSMutableArray * update_agent_list;
975 deleteList = [NSMutableArray array];
976 duplicate_domain_list = [[NSCountedSet alloc] initWithCapacity:0];
977 new_domain_list = [NSMutableArray array];
978 update_agent_list = [NSMutableArray array];
979 old_domain_list = [self getAgentList:self.floatingDNSAgentList
980 agentType:kAgentTypeDNS
981 agentSubType:kAgentSubTypeSupplemental];
983 if (dns_config->resolver == NULL) {
984 dns_config->n_resolver = 0;
986 if (dns_config->n_resolver > 0) {
987 for (int i = 0; i < dns_config->n_resolver; i++) {
988 dns_resolver_t * resolver;
990 resolver = dns_config->resolver[i];
991 if (resolver->domain != NULL &&
992 ![self isResolverPrivate:resolver] &&
993 ![self isResolverMulticast:resolver]) {
994 NSString * ns_domain_name;
996 ns_domain_name = [NSString stringWithCString:resolver->domain encoding:NSASCIIStringEncoding];
997 [new_domain_list addObject:ns_domain_name];
1002 [self cleanConflictingAgentsFromList:old_domain_list
1003 new_list:new_domain_list
1004 agentDictionary:self.floatingDNSAgentList];
1006 /* Sync between controller and current config */
1007 for (NSString *key in old_domain_list) {
1008 BOOL domain_present = NO;
1010 domain_present = [new_domain_list containsObject:key];
1011 if (domain_present == NO) {
1014 agent = [self.floatingDNSAgentList objectForKey:key];
1015 [self destroyFloatingAgent:agent];
1019 /* At this point, whatever is in the controller's floating agent list,
1020 is present in the current DNS config. The current DNS config
1021 might have even more configs, not known to the controller, YET
1024 for (NSString *domain in old_domain_list) {
1028 agent = [self.floatingDNSAgentList objectForKey:domain];
1033 /* Am I mapped to some agent? */
1034 mapped_agent = [agent getAgentMapping];
1036 /* OK, this agent is mapped to some other agent. We compare this agent's data
1037 * to the current data of the agent to which it is mapped. If different, we destroy
1038 * the agent and later map it to someone else OR spawn a new one.
1040 NSData *mapped_agent_data;
1042 mapped_agent_data = [self getDNSDataFromCurrentConfig:dns_config domain:[mapped_agent getAssociatedEntity]];
1043 if (mapped_agent_data == nil || ![[agent getAgentData] isEqual:mapped_agent_data]) {
1044 /* Something changed for mapped agent */
1045 [deleteList addObject:agent];
1049 /* Since this agent is NOT mapped to any other agent, this agent is
1050 * registered with the kernel. So instead of destroying the agent and
1051 * re-registering it, just update it here.
1053 * All the agents which were mapped to this agent, will be deleted and
1054 * re-mapped, if the data changed.
1058 agent_data = [self getDNSDataFromCurrentConfig:dns_config domain:[agent getAssociatedEntity]];
1059 if (![[agent getAgentData] isEqual:agent_data]) {
1060 /* Something changed for agent */
1061 [agent updateAgentData:agent_data];
1063 /* The reason I don't publish the data to agent here is that, if there were
1064 * some agents mapping to this one, they will momentarily have a policy for
1065 * using this agent UUID for some domain based on this agent's previous data.
1067 [update_agent_list addObject:agent];
1071 [new_domain_list removeObject:domain];
1074 for (id agent in deleteList) {
1075 SC_log(LOG_INFO, "Destroying agent %@ because something changed!", [agent getAgentName]);
1076 [self destroyFloatingAgent:agent];
1079 for (id agent in update_agent_list) {
1080 [self publishToAgent:agent];
1083 for (int idx = 0; idx < dns_config->n_resolver; idx++) {
1084 dns_resolver_t * resolver;
1086 resolver = dns_config->resolver[idx];
1087 if (resolver->domain != NULL &&
1088 ![self isResolverPrivate:resolver] &&
1089 ![self isResolverMulticast:resolver]) {
1093 NSString * ns_domain_name;
1095 ns_domain_name = @(resolver->domain);
1096 found = [new_domain_list indexOfObject:ns_domain_name];
1097 if (found == NSNotFound) {
1098 /* Nothing changed for this agent */
1102 /* We will only process agents which are mapped AND if the agent they were mapped to, changed OR
1103 * agents for domains which we did not know before.
1106 NSUInteger domainInstance = [duplicate_domain_list countForObject:ns_domain_name];
1107 if (domainInstance > 0) {
1108 /* domainInstance will be > 0, only if we have conflicting domains */
1110 data = [self dataForResolver:resolver];
1112 NSString *ns_domain_name_copy = [NSString stringWithFormat:@"%@" multipleEntitySuffix "%lu", ns_domain_name, (unsigned long)domainInstance];
1114 BOOL ok = [self spawnFloatingAgent:[DNSAgent class]
1115 entity:ns_domain_name_copy
1116 agentSubType:kAgentSubTypeSupplemental
1117 addPolicyOfType:NEPolicyConditionTypeDomain
1120 id agent = [self.floatingDNSAgentList objectForKey:ns_domain_name_copy];
1121 SC_log(LOG_INFO, "Duplicate DNS agent %@", [agent getAgentName]);;
1124 data = [self dataForResolver:resolver];
1125 mapped_agent = [self getAgentWithSameDataAndSubType:self.floatingDNSAgentList
1127 subType:kAgentSubTypeSupplemental];
1128 if (mapped_agent != nil) {
1129 [self spawnMappedFloatingAgent:mapped_agent
1130 entity:ns_domain_name
1131 agentSubType:kAgentSubTypeSupplemental
1132 addPolicyOfType:NEPolicyConditionTypeDomain
1135 [self spawnFloatingAgent:[DNSAgent class]
1136 entity:ns_domain_name
1137 agentSubType:kAgentSubTypeSupplemental
1138 addPolicyOfType:NEPolicyConditionTypeDomain
1143 [new_domain_list removeObjectAtIndex:found];
1144 [duplicate_domain_list addObject:ns_domain_name];
1152 - (void)processDNSResolvers:(dns_config_t *)dns_config
1154 resolver_list_t *resolvers = [self copyResolverList:dns_config];
1156 /* Process Default resolvers */
1157 NSMutableArray *old_default_resolver_list = [self getAgentList:self.floatingDNSAgentList
1158 agentType:kAgentTypeDNS
1159 agentSubType:kAgentSubTypeDefault];
1161 // For default resolvers, their name will be '_defaultDNS', '_defaultDNS #2' so on...
1162 if (resolvers->n_default_resolvers > 0 && resolvers->default_resolvers != NULL) {
1163 for (uint32_t i = 0; i < resolvers->n_default_resolvers; i++) {
1164 dns_resolver_t *default_resolver = resolvers->default_resolvers[i];
1167 NSString * resolverName;
1169 data = [self dataForResolver:default_resolver];
1171 resolverName = @(dnsAgentDefault);
1173 resolverName = [NSString stringWithFormat:@dnsAgentDefault multipleEntitySuffix "%d", i+1 ];
1176 dnsAgent = [self.floatingDNSAgentList objectForKey:resolverName];
1178 if (dnsAgent != nil) {
1179 [old_default_resolver_list removeObject:resolverName];
1180 if ([data isEqual:[dnsAgent getAgentData]]) {
1181 /* Leave this agent in place. Nothing changed! */
1184 [self destroyFloatingAgent:dnsAgent];
1188 [self spawnFloatingAgent:[DNSAgent class]
1190 agentSubType:kAgentSubTypeDefault
1191 addPolicyOfType:NEPolicyConditionTypeNone
1196 // Only agents that are NOT present in the new config, will be present in the list
1197 // and they need to be destroyed.
1198 [self deleteAgentList:self.floatingDNSAgentList list:old_default_resolver_list];
1200 /* Process Multicast resolvers */
1202 NSMutableArray *old_multicast_resolver_list = [self getAgentList:self.floatingDNSAgentList
1203 agentType:kAgentTypeDNS
1204 agentSubType:kAgentSubTypeMulticast];
1206 if (resolvers->n_multicast_resolvers > 0 && resolvers->multicast_resolvers != NULL) {
1207 for (uint32_t i = 0; i < resolvers->n_multicast_resolvers; i++) {
1208 dns_resolver_t * multicast_resolver = resolvers->multicast_resolvers[i];
1210 NSString * resolverName;
1212 if (multicast_resolver == NULL) {
1216 if (multicast_resolver->domain == NULL) {
1217 /* Multicast resolvers MUST have a domain */
1221 resolverName = @(multicast_resolver->domain);
1222 if (resolverName == NULL) {
1223 /* Multicast resolvers MUST have a domain */
1227 dnsAgent = [self.floatingDNSAgentList objectForKey:resolverName];
1228 if (dnsAgent != nil) {
1229 [old_multicast_resolver_list removeObject:resolverName];
1233 [self spawnFloatingAgent:[DNSAgent class]
1235 agentSubType:kAgentSubTypeMulticast
1236 addPolicyOfType:NEPolicyConditionTypeDomain
1238 // Don't care about data for mdns resolvers. Do we?
1242 [self deleteAgentList:self.floatingDNSAgentList list:old_multicast_resolver_list];
1244 /* Process Private resolvers */
1246 NSMutableArray *old_private_resolver_list = [self getAgentList:self.floatingDNSAgentList
1247 agentType:kAgentTypeDNS
1248 agentSubType:kAgentSubTypePrivate];
1250 if (resolvers->n_private_resolvers > 0 && resolvers->private_resolvers != NULL) {
1251 for (uint32_t i = 0; i < resolvers->n_private_resolvers; i++) {
1252 dns_resolver_t * private_resolver = resolvers->private_resolvers[i];
1254 NSString * resolverName;
1256 if (private_resolver == NULL) {
1260 if (private_resolver->domain == NULL) {
1261 /* private resolvers MUST have a domain */
1265 resolverName = @(private_resolver->domain);
1266 if (resolverName == nil) {
1267 /* Private resolvers MUST have a domain */
1271 dnsAgent = [self.floatingDNSAgentList objectForKey:resolverName];
1272 if (dnsAgent != nil) {
1273 [old_private_resolver_list removeObject:resolverName];
1277 [self spawnFloatingAgent:[DNSAgent class]
1279 agentSubType:kAgentSubTypePrivate
1280 addPolicyOfType:NEPolicyConditionTypeDomain
1282 // Don't care about data for pdns resolvers. Do we?
1286 [self deleteAgentList:self.floatingDNSAgentList list:old_private_resolver_list];
1289 [self freeResolverList:resolvers];
1292 - (void)processScopedDNSResolvers:(dns_config_t *)dns_config
1294 NSMutableArray * old_intf_list;
1295 old_intf_list = [self getAgentList:self.floatingDNSAgentList
1296 agentType:kAgentTypeDNS
1297 agentSubType:kAgentSubTypeScoped];
1299 if ((dns_config->n_scoped_resolver > 0) && (dns_config->scoped_resolver != NULL)) {
1300 for (int i = 0; i < dns_config->n_scoped_resolver; i++) {
1305 const char * if_name;
1306 NSString * ns_if_name;
1307 NSString * ns_if_name_with_prefix;
1308 dns_resolver_t * resolver;
1310 resolver = dns_config->scoped_resolver[i];
1311 if_name = my_if_indextoname(resolver->if_index, buf);
1313 ns_if_name = @(if_name);
1314 ns_if_name_with_prefix = [NSString stringWithFormat:@"%s%@", prefixForInterfaceName, ns_if_name];
1319 data = [self dataForResolver:resolver];
1320 idx = [old_intf_list indexOfObject:ns_if_name_with_prefix];
1322 if (idx == NSNotFound) {
1323 /* We need to spawn an agent */
1324 [self spawnFloatingAgent:[DNSAgent class]
1325 entity:ns_if_name_with_prefix
1326 agentSubType:kAgentSubTypeScoped
1327 addPolicyOfType:NEPolicyConditionTypeScopedInterface
1331 /* We have an agent on this interface. Update it */
1332 [old_intf_list removeObjectAtIndex:idx];
1335 /* Get the DNS agent for this interface? */
1336 dnsAgent = [self.floatingDNSAgentList objectForKey:ns_if_name_with_prefix];
1337 if (dnsAgent != nil) {
1338 /* Do we need to update this agent? */
1339 [dnsAgent updateAgentData:data];
1340 if ([dnsAgent shouldUpdateAgent]) {
1341 [self publishToAgent:dnsAgent];
1347 [self deleteAgentList:self.floatingDNSAgentList list:old_intf_list];
1350 - (void)processServiceSpecificDNSResolvers:(dns_config_t *)dns_config
1352 NSMutableArray * old_service_list;
1353 old_service_list = [self getAgentList:self.floatingDNSAgentList
1354 agentType:kAgentTypeDNS
1355 agentSubType:kAgentSubTypeServiceSpecific];
1357 if ((dns_config->n_service_specific_resolver > 0) && (dns_config->service_specific_resolver != NULL)) {
1358 for (int i = 0; i < dns_config->n_service_specific_resolver; i++) {
1362 uint32_t service_identifier;
1363 NSString * ns_service_identifier_with_prefix;
1364 dns_resolver_t * resolver;
1366 resolver = dns_config->service_specific_resolver[i];
1367 service_identifier = resolver->service_identifier;
1368 if (service_identifier != 0) {
1369 ns_service_identifier_with_prefix = [NSString stringWithFormat:@"%s%u", prefixForInterfaceName, service_identifier];
1374 data = [self dataForResolver:resolver];
1375 idx = [old_service_list indexOfObject:ns_service_identifier_with_prefix];
1377 if (idx == NSNotFound) {
1378 /* We need to spawn an agent */
1379 [self spawnFloatingAgent:[DNSAgent class]
1380 entity:ns_service_identifier_with_prefix
1381 agentSubType:kAgentSubTypeServiceSpecific
1382 addPolicyOfType:(POLICY_TYPE_NO_POLICY) /* Don't install a policy */
1386 /* We have an agent on this interface. Update it */
1387 [old_service_list removeObjectAtIndex:idx];
1390 /* Get the DNS agent for this interface? */
1391 dnsAgent = [self.floatingDNSAgentList objectForKey:ns_service_identifier_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_service_list];
1405 #define ONION_RESOLVER_DOMAIN "onion"
1406 - (BOOL)isResolverOnion:(dns_resolver_t *)resolver
1408 if (resolver->domain != NULL &&
1409 (strcmp(resolver->domain, ONION_RESOLVER_DOMAIN) == 0)) {
1417 - (void)processOnionResolver:(dns_config_t *)dns_config
1419 static NSUInteger policy_id = 0;
1421 if (dns_config == NULL) {
1425 /* Run through the resolver configurations. We only care for the supplemental resolvers. */
1426 for (int32_t i = 0; i < dns_config->n_resolver; i++) {
1427 dns_resolver_t *resolver = dns_config->resolver[i];
1428 if ([self isResolverOnion:resolver]) {
1433 /* We do not have any such resolver. Add a system-wide "drop" policy for this domain */
1434 if (policy_id == 0) {
1435 NEPolicy *policy = [[NEPolicy alloc] initWithOrder:INIT_ORDER_FOR_DOMAIN_POLICY
1436 result:[NEPolicyResult drop]
1437 conditions:@[[NEPolicyCondition domain:@ONION_RESOLVER_DOMAIN]]];
1438 if (policy != nil) {
1439 policy_id = [self.policySession addPolicy:policy];
1440 if (![self.policySession apply]) {
1442 SC_log(LOG_NOTICE, "Could not add a [." ONION_RESOLVER_DOMAIN "] drop policy");
1444 SC_log(LOG_INFO, "Added a [." ONION_RESOLVER_DOMAIN "] drop policy");
1453 /* We have such a resolver in the config OR no DNS config at all. Remove the system-wide "drop" policy for this domain */
1454 if (policy_id > 0) {
1455 [self.policySession removePolicyWithID:policy_id];
1456 if (![self.policySession apply]) {
1457 SC_log(LOG_NOTICE, "Could not remove the [." ONION_RESOLVER_DOMAIN "] drop policy");
1460 SC_log(LOG_INFO, "Removed the [." ONION_RESOLVER_DOMAIN "] drop policy");
1466 #undef ONION_RESOLVER_DOMAIN
1469 - (void)processDNSChanges
1471 dns_config_t * dns_config;
1473 dns_config = dns_configuration_copy();
1474 if (dns_config == NULL) {
1475 SC_log(LOG_INFO, "No DNS configuration");
1476 NSMutableDictionary *copy = [self.floatingDNSAgentList copy];
1477 for (NSString *entity in copy) {
1478 id agent = [copy objectForKey:entity];
1480 [self destroyFloatingAgent:agent];
1485 [self processDNSResolvers:dns_config];
1486 [self processScopedDNSResolvers:dns_config];
1487 [self processSupplementalDNSResolvers:dns_config];
1488 [self processServiceSpecificDNSResolvers:dns_config];
1492 [self processOnionResolver:dns_config];
1493 if (dns_config != NULL) {
1494 dns_configuration_free(dns_config);
1498 #pragma mark Helper functions
1500 - (const void *)copyConfigAgentData:(NSMutableDictionary *)controllerDict
1501 uuid:(uuid_t)requested_uuid
1502 length:(uint64_t *)length
1504 if (length == NULL) {
1505 SC_log(LOG_NOTICE, "Invalid parameters for copying agent data");
1510 void *buffer = NULL;
1513 for (NSString *key in controllerDict) {
1514 id temp_agent = [controllerDict objectForKey:key];
1518 [[temp_agent getAgentUUID] getUUIDBytes:agent_uuid];
1519 if (uuid_compare(agent_uuid, requested_uuid) == 0) {
1526 uuid_string_t uuid_str;
1527 uuid_unparse(requested_uuid, uuid_str);
1528 SC_log(LOG_NOTICE, "Invalid config agent uuid %s specified", uuid_str);
1532 NSData *data = [agent getAgentData];
1533 uint64_t len = [data length];
1536 buffer = malloc((size_t)len);
1537 memcpy(buffer, [data bytes], len);
1540 return (const void *)buffer;
1543 - (const void *)copyProxyAgentData:(uuid_t)requested_uuid
1544 length:(uint64_t *)length
1546 return [self copyConfigAgentData:self.floatingProxyAgentList
1551 - (const void *)copyDNSAgentData:(uuid_t)requested_uuid
1552 length:(uint64_t *)length
1554 return [self copyConfigAgentData:self.floatingDNSAgentList
1559 - (NSData *)dataLengthSanityCheck:(id)agent
1561 NSData * data = [agent getAgentData];
1563 if ([data length] > CONFIG_AGENT_DATA_LIMIT) {
1564 /* We impose a limit on the config agent data as 1KB.
1565 * If we have a data blob larger than this limit, do NOT publish it into the agent.
1566 * Instead publish a key which will trigger fetching of the configuration directly
1567 * through NWI server.
1569 NSMutableDictionary *data_dict = [NSMutableDictionary dictionary];
1571 NSUUID *uuid = [agent getAgentUUID];
1573 [uuid getUUIDBytes:c_uuid];
1574 NSData *uuid_data = [[NSData alloc] initWithBytes:c_uuid length:sizeof(c_uuid)];
1575 [data_dict setValue:uuid_data forKey:@kConfigAgentOutOfBandDataUUID];
1577 NSData *new_data = [NSPropertyListSerialization dataWithPropertyList:data_dict
1578 format:NSPropertyListBinaryFormat_v1_0
1589 * For conflicting agents, the convention is that its name & entity,
1590 * will have a suffix " #<number>". This function will sanitize the
1591 * suffix and just return the entity name
1593 - (NSString *)sanitizeEntity:(NSString *)entity
1595 NSRange range = [entity rangeOfString:@multipleEntitySuffix];
1596 if (range.location != NSNotFound) {
1597 NSString *str = [entity substringToIndex:range.location];
1605 * For interface names, there is a prefix to differentiate then
1606 * from the domain name (iff there were conflicting domain names).
1607 * Returns the sanitized interface name
1609 - (NSString *)sanitizeInterfaceName:(NSString *)intf
1611 NSRange range = [intf rangeOfString:@prefixForInterfaceName];
1612 if (range.location != NSNotFound) {
1613 NSString *str = [intf substringFromIndex:(range.location + strlen(prefixForInterfaceName))];
1621 * For conflicting agents, the convention is that its name & entity,
1622 * will have a suffix " #<number>". This function will return that <number>
1624 - (int)entityInstanceNumber:(NSString *)entity
1626 NSRange range = [entity rangeOfString:@multipleEntitySuffix];
1627 if (range.location != NSNotFound) {
1628 NSString *str = [entity substringFromIndex:(range.location + strlen(multipleEntitySuffix))];
1629 return str.intValue;
1636 * In case that we have conflicting DNS/Proxy domains
1637 * This function will remove all those conflicting agents,
1638 * so that we can start afresh with the new config
1640 - (void)cleanConflictingAgentsFromList:(NSMutableArray *)old_list
1641 new_list:(NSMutableArray *)new_list
1642 agentDictionary:(NSMutableDictionary *)agent_list
1644 NSCountedSet * duplicate_domain_list;
1646 for (NSString *domain in old_list) {
1647 /* If we had conflicting domains before, remove all of them */
1648 NSString *sanitizedDomain = [self sanitizeEntity:domain];
1649 if (![sanitizedDomain isEqualToString:domain]) {
1650 /* Destroy the original domain */
1651 id agent = [agent_list objectForKey:sanitizedDomain];
1652 [self destroyFloatingAgent:agent];
1654 /* Destroy the conflicting domain */
1655 agent = [agent_list objectForKey:domain];
1656 [self destroyFloatingAgent:agent];
1658 SC_log(LOG_INFO, "Removing conflicting domain: %@, %@", sanitizedDomain, domain);
1662 duplicate_domain_list = [[NSCountedSet alloc] initWithArray:new_list];
1663 for (NSString *domain in old_list) {
1664 if ([duplicate_domain_list countForObject:domain] > 1) {
1665 id agent = [agent_list objectForKey:domain];
1666 [self destroyFloatingAgent:agent];
1667 SC_log(LOG_INFO, "Removing domain %@ as it has duplicates in the current config", domain);
1673 * Get the list of agents from a specific dictionary.
1674 * The list of agents will only consist of the ones which
1675 * match the agent type and sub-type
1678 - (NSMutableArray *)getAgentList:(NSMutableDictionary *)all_agents
1679 agentType:(AgentType)type
1680 agentSubType:(AgentSubType)subtype
1682 NSMutableArray *list = [NSMutableArray array];
1683 NSArray *agentObjects = [all_agents allValues];
1685 for (id agent in agentObjects) {
1686 if (([agent getAgentType] == type) &&
1687 ([agent getAgentSubType] == subtype)) {
1689 [list addObject:[agent getAssociatedEntity]];
1697 * Destroy all the agents are listed in "list"
1700 - (void)deleteAgentList:(NSMutableDictionary *)all_agents
1701 list:(NSMutableArray *)list
1703 for (NSString *intf in list) {
1706 agent = [all_agents objectForKey:intf];
1707 [self destroyFloatingAgent:agent];
1712 * In order to not duplicate agents with same content,
1713 * we map an agent X to agent Y, when their content is the same.
1715 * This function tries to find that agent Y
1718 - (id)getAgentWithSameDataAndSubType:(NSMutableDictionary *)agentList
1720 subType:(AgentSubType)subtype
1722 for (NSString *key in agentList) {
1723 id agent = [agentList objectForKey:key];
1724 if ([[agent getAgentData] isEqual:data]) {
1725 /* Do not map to default agents */
1726 if ([agent getAgentSubType] != subtype) {
1730 /* Return only registered agents */
1731 if ([agent getRegistrationObject] != nil) {
1740 #pragma mark Policy installation function
1743 * Add NECP policies for an agent
1745 - (BOOL)addPolicyToFloatingAgent:(id)agent
1746 domain:(NSString *)domain
1747 agentUUIDToUse:(NSUUID *)uuid
1748 policyType:(NEPolicyConditionType)policyType
1749 useControlPolicySession:(BOOL)useControlPolicySession
1751 NEPolicyCondition * condition = nil;
1752 NEPolicySession * session;
1753 uint32_t multiple_entity_offset;
1754 NEPolicy * newPolicy;
1757 uint32_t orderForSkip;
1758 NSMutableArray * policyArray;
1759 NSUInteger policyID1;
1760 NSUInteger policyID2;
1761 NEPolicyResult * result;
1764 uint32_t typeOffset;
1766 type = [agent getAgentType];
1767 typeOffset = (type == kAgentTypeDNS) ? 0 : 5000;
1768 skipOrder = (type == kAgentTypeDNS) ? 5000 : 0;
1770 multiple_entity_offset = (uint32_t)[self entityInstanceNumber:domain];
1771 domain = [self sanitizeEntity:domain];
1773 switch (policyType) {
1774 case NEPolicyConditionTypeScopedInterface:
1775 order = INIT_ORDER_FOR_SCOPED_INTERFACE_POLICY + typeOffset + multiple_entity_offset;
1776 domain = [self sanitizeInterfaceName:domain];
1777 condition = [NEPolicyCondition scopedInterface:domain];
1778 orderForSkip = SKIP_ORDER_FOR_SCOPED_INTERFACE_POLICY + typeOffset;
1781 case NEPolicyConditionTypeDomain:
1782 order = INIT_ORDER_FOR_DOMAIN_POLICY + typeOffset + multiple_entity_offset;
1783 condition = [NEPolicyCondition domain:domain];
1784 orderForSkip = SKIP_ORDER_FOR_DOMAIN_POLICY + typeOffset;
1787 case NEPolicyConditionTypeAllInterfaces:
1788 order = INIT_ORDER_FOR_DEFAULT_POLICY + typeOffset + multiple_entity_offset;
1789 condition = [NEPolicyCondition allInterfaces];
1790 orderForSkip = SKIP_ORDER_FOR_DEFAULT_POLICY + typeOffset;
1793 case NEPolicyConditionTypeNone:
1794 order = INIT_ORDER_FOR_DEFAULT_POLICY + typeOffset + multiple_entity_offset;
1795 orderForSkip = SKIP_ORDER_FOR_DEFAULT_POLICY + typeOffset;
1799 SC_log(LOG_NOTICE, "Invalid policy condition specified");
1803 result = [NEPolicyResult netAgentUUID:uuid];
1804 newPolicy = [[NEPolicy alloc] initWithOrder:order
1806 conditions: (condition ? @[condition] : nil)];
1808 if (newPolicy == nil) {
1809 SC_log(LOG_NOTICE, "Could not create a policy for agent %@", [agent getAgentName]);
1813 if (useControlPolicySession) {
1814 if (self.controlPolicySession == nil) {
1815 /* The NE policy session at "control" level for the controller */
1816 self.controlPolicySession = [self createPolicySession];
1817 if (self.controlPolicySession == nil) {
1818 SC_log(LOG_NOTICE, "Could not create a control policy session for agent %@", [agent getAgentName]);
1821 [self.controlPolicySession setPriority:NEPolicySessionPriorityControl];
1823 ((ConfigAgent *)agent).preferredPolicySession = self.controlPolicySession;
1825 ((ConfigAgent *)agent).preferredPolicySession = self.policySession;
1828 session = ((ConfigAgent *)agent).preferredPolicySession;
1830 policyID1 = [session addPolicy:newPolicy];
1831 if (policyID1 == 0) {
1832 SC_log(LOG_NOTICE, "Could not add a netagent policy for agent %@", [agent getAgentName]);
1836 result = [NEPolicyResult skipWithOrder:skipOrder];
1837 newPolicy = [[NEPolicy alloc] initWithOrder:orderForSkip
1839 conditions:(condition ? @[condition] : nil)];
1841 if (newPolicy == nil) {
1842 SC_log(LOG_NOTICE, "Could not create a policy for agent %@", [agent getAgentName]);
1846 policyID2 = [session addPolicy:newPolicy];
1847 if (policyID2 == 0) {
1848 SC_log(LOG_NOTICE, "Could not add a skip policy for agent %@", [agent getAgentName]);
1852 ok = [session apply];
1854 SC_log(LOG_NOTICE, "Could not apply policy for agent %@", [agent getAgentName]);
1858 policyArray = [self.policyDB objectForKey:[agent getAgentName]];
1859 if (policyArray == nil) {
1860 policyArray = [NSMutableArray array];
1863 [policyArray addObject:numberToNSNumber(policyID1)];
1864 [policyArray addObject:numberToNSNumber(policyID2)];
1865 [self.policyDB setObject:policyArray forKey:[agent getAgentName]];
1870 #pragma mark Agent manipulation functions
1875 - (BOOL)spawnFloatingAgent:(Class)agentClass
1876 entity:(NSString *)entity
1877 agentSubType:(AgentSubType)subtype
1878 addPolicyOfType:(NEPolicyConditionType)policyType
1879 publishData:(NSData *)data
1883 NSMutableDictionary * parameters;
1885 parameters =[NSMutableDictionary dictionary];
1886 [parameters setValue:entity forKey:@kEntityName];
1887 [parameters setValue:numberToNSNumber(subtype) forKey:@kAgentSubType];
1889 agent = [[agentClass alloc] initWithParameters:parameters];
1890 ok = [self registerAgent:agent];
1896 /* Since we just spawned this agent, update its data */
1897 [agent updateAgentData:data];
1898 [self publishToAgent:agent];
1901 /* Add a policy if there is a valid type. If POLICY_TYPE_NO_POLICY, then ignore policies.
1902 * POLICY_TYPE_NO_POLICY will be set for service-specific agents, in which case we rely on
1903 * service owners to install custom policies to point at the agents. */
1904 if (policyType >= NEPolicyConditionTypeNone) {
1905 BOOL useControlPolicySession = NO;
1906 if (subtype == kAgentSubTypeGlobal) {
1907 /* Policies for a Global scoped agents are at "control" level */
1908 useControlPolicySession = YES;
1911 ok = [self addPolicyToFloatingAgent:agent
1913 agentUUIDToUse:[agent agentUUID]
1914 policyType:policyType
1915 useControlPolicySession:useControlPolicySession];
1918 [self unregisterAgent:agent];
1923 SC_log(LOG_INFO, "Spawning floating agent for %@", entity);
1925 if ([agent getAgentType] == kAgentTypeProxy) {
1926 [self.floatingProxyAgentList setObject:agent forKey:entity];
1928 [self.floatingDNSAgentList setObject:agent forKey:entity];
1935 * Create an agent mapped to another agent.
1937 - (BOOL)spawnMappedFloatingAgent:(id)mapped_agent
1938 entity:(NSString *)entity
1939 agentSubType:(AgentSubType)subtype
1940 addPolicyOfType:(NEPolicyConditionType)policyType
1941 updateData:(NSData *)data
1944 NSMutableDictionary * parameters;
1946 parameters = [NSMutableDictionary dictionary];
1947 [parameters setValue:entity forKey:@kEntityName];
1948 [parameters setValue:numberToNSNumber(subtype) forKey:@kAgentSubType];
1950 dummyAgent = [[[mapped_agent class] alloc] initWithParameters:parameters];
1953 /* Since we just spawned this agent, update its data.
1954 * We do not publish it since this agent is mapped
1955 * to an agent which already has the same data
1957 [dummyAgent updateAgentData:data];
1960 BOOL useControlPolicySession = NO;
1961 if (subtype == kAgentSubTypeGlobal) {
1962 /* Policies for a Global scoped agents are at "control" level */
1963 useControlPolicySession = YES;
1966 BOOL ok = [self addPolicyToFloatingAgent:dummyAgent
1968 agentUUIDToUse:[mapped_agent agentUUID]
1969 policyType:policyType
1970 useControlPolicySession:useControlPolicySession];
1976 if ([mapped_agent getAgentType] == kAgentTypeProxy) {
1977 [self.floatingProxyAgentList setObject:dummyAgent forKey:entity];
1979 [self.floatingDNSAgentList setObject:dummyAgent forKey:entity];
1982 [dummyAgent setAgentMapping:mapped_agent];
1984 SC_log(LOG_INFO, "Mapped floating agent %@ to %@", [dummyAgent getAgentName], [mapped_agent getAgentName]);
1989 * Write into an agent
1991 - (BOOL)publishToAgent:(id)agent
1993 /* Before any data goes into the kernel, do a sanity check. */
1994 NSData *sanityCheckData = [self dataLengthSanityCheck:agent];
1995 NSData *tempAgentData = nil;
1997 if (sanityCheckData != nil) {
1998 /* Data length is more than the limit! for updateNetworkAgent, the data blob
1999 * has to be a part of the agent object. Thus the temporary data replacement!
2001 tempAgentData = [[agent getAgentData] copy];
2002 [agent updateAgentData:sanityCheckData];
2003 SC_log(LOG_NOTICE, "Data too large for %@ (%lu bytes)!", [agent getAgentName], (unsigned long)[tempAgentData length]);
2008 NWNetworkAgentRegistration *regObject = [agent valueForKey:@"registrationObject"];
2009 if (regObject != nil) {
2010 SC_log(LOG_NOTICE, "Publishing data to agent %@ (%lu bytes)", [agent getAgentName], (unsigned long)[[agent getAgentData] length]);
2011 ok = [regObject updateNetworkAgent:agent];
2013 SC_log(LOG_NOTICE, "Could not update config agent");
2016 SC_log(LOG_NOTICE, "Config Agent not registered. Cannot Update");
2019 if (tempAgentData != nil) {
2020 [agent updateAgentData:tempAgentData];
2029 - (BOOL)destroyFloatingAgent:(id)agent
2033 if ( agent != nil) {
2034 NSMutableArray * policyArray;
2036 policyArray = [self.policyDB objectForKey:[agent getAgentName]];
2037 if (policyArray != nil) {
2038 NEPolicySession * session = ((ConfigAgent *)agent).preferredPolicySession;
2041 for (NSNumber *policyID in policyArray) {
2044 idVal = [policyID unsignedIntegerValue];
2045 result = [session removePolicyWithID:idVal];
2047 SC_log(LOG_NOTICE, "Could not remove policy %@ for agent %@", [session policyWithID:idVal], [agent getAgentName]);
2051 result = [session apply];
2053 SC_log(LOG_NOTICE, "Could not apply removed policies for agent %@", [agent getAgentName]);
2056 [self.policyDB removeObjectForKey:[agent getAgentName]];
2059 if ([agent getAgentType] == kAgentTypeProxy) {
2060 [self.floatingProxyAgentList removeObjectForKey:[agent getAssociatedEntity]];
2062 [self.floatingDNSAgentList removeObjectForKey:[agent getAssociatedEntity]];
2065 if ([agent getRegistrationObject] != nil) {
2066 [self unregisterAgent:agent];
2069 SC_log(LOG_INFO, "X - Destroyed agent %@", [agent getAgentName]);
2071 /* Check if we need to close the "control" policy session */
2072 if (self.controlPolicySession != nil) {
2073 NSMutableArray *globalProxyAgentList;
2074 NSMutableArray *globalDNSAgentList;
2075 globalProxyAgentList = [self getAgentList:self.floatingProxyAgentList agentType:kAgentTypeProxy agentSubType:kAgentSubTypeGlobal];
2076 globalDNSAgentList = [self getAgentList:self.floatingDNSAgentList agentType:kAgentTypeDNS agentSubType:kAgentSubTypeGlobal];
2078 if ([globalProxyAgentList count] == 0 &&
2079 [globalDNSAgentList count] == 0) {
2080 ok = [self.controlPolicySession removeAllPolicies];
2082 SC_log(LOG_ERR, "Could not remove policies for agent %@", [agent getAgentName]);
2085 ok = [self.controlPolicySession apply];
2087 SC_log(LOG_ERR, "Could not apply policy change for agent %@", [agent getAgentName]);
2090 self.controlPolicySession = nil;
2091 SC_log(LOG_NOTICE, "Closed control policy session");
2104 - (BOOL)registerAgent:(id)agent
2108 NWNetworkAgentRegistration *registration = [[NWNetworkAgentRegistration alloc] initWithNetworkAgentClass:[agent class]];
2110 ok = [registration registerNetworkAgent:agent];
2112 SC_log(LOG_NOTICE, "Could not register config agent");
2116 [agent addAgentRegistrationObject:registration];
2123 * Unregister an agent
2125 - (BOOL)unregisterAgent:(id)agent
2129 NWNetworkAgentRegistration *regObject = [agent valueForKey:@"registrationObject"];
2130 if (regObject != nil) {
2131 ok = [regObject unregisterNetworkAgent];
2133 SC_log(LOG_NOTICE, "Could not unregister config agent");
2136 SC_log(LOG_NOTICE, "Config Agent not registered. Cannot unregister");