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)applyPolicies
718 if (self.controlPolicySession != nil && ![self.controlPolicySession apply]) {
719 SC_log(LOG_ERR, "Failed to apply control policies");
721 if (self.policySession != nil && ![self.policySession apply]) {
722 SC_log(LOG_ERR, "Failed to apply policies");
726 - (void)processProxyChanges
728 CFDictionaryRef proxies;
730 proxies = SCDynamicStoreCopyProxiesWithOptions(NULL, NULL);
731 if (proxies == NULL) {
732 SC_log(LOG_INFO, "No proxy information");
734 BOOL destroyedAgent = NO;
735 NSMutableDictionary *copy = [self.floatingProxyAgentList copy];
736 for (NSString *entity in copy) {
737 id agent = [copy objectForKey:entity];
738 [self destroyFloatingAgent:agent];
739 destroyedAgent = YES;
742 if (destroyedAgent) {
743 [self applyPolicies];
748 [self processDefaultProxyChanges:proxies];
749 [self processScopedProxyChanges:proxies];
750 [self processSupplementalProxyChanges:proxies];
751 [self processServiceSpecificProxyChanges:proxies];
753 [self applyPolicies];
758 /* ========================== DNS agent helpers =========================== */
759 #pragma mark DNS agent helper functions
761 - (void)freeResolverList:(resolver_list_t *)resolvers
763 /* This is a shallow free of resolver_list_t only.
764 * The actual resolver pointers are owned by 'dns_config'
766 if (resolvers == NULL) {
770 if (resolvers->default_resolvers != NULL) {
771 free(resolvers->default_resolvers);
773 if (resolvers->multicast_resolvers != NULL) {
774 free(resolvers->multicast_resolvers);
776 if (resolvers->private_resolvers != NULL) {
777 free(resolvers->private_resolvers);
783 - (resolver_list_t *)copyResolverList:(dns_config_t *)dns_config
785 resolver_list_t *resolvers = NULL;
787 if ((dns_config->n_resolver > 0) && (dns_config->resolver != NULL)) {
792 resolvers = calloc(1, sizeof(resolver_list_t));
793 for (int i = 0; i < dns_config->n_resolver; i++) {
794 dns_resolver_t *r = dns_config->resolver[i];
796 if ([self isResolverMulticast:r]) {
797 resolvers->n_multicast_resolvers++;
800 } else if ([self isResolverPrivate:r]) {
801 resolvers->n_private_resolvers++;
805 // do not consider default resolvers with no nameservers
806 if (r->domain == NULL && r->n_nameserver > 0) {
807 resolvers->n_default_resolvers++;
811 SC_log(LOG_INFO, "Resolvers: %d default, %d multicast, %d private",
812 resolvers->n_default_resolvers,
813 resolvers->n_multicast_resolvers,
814 resolvers->n_private_resolvers);
816 if (resolvers->n_default_resolvers > 0) {
817 resolvers->default_resolvers = calloc(resolvers->n_default_resolvers,
818 sizeof(dns_resolver_t *));
820 if (resolvers->n_multicast_resolvers > 0) {
821 resolvers->multicast_resolvers = calloc(resolvers->n_multicast_resolvers,
822 sizeof(dns_resolver_t *));
824 if (resolvers->n_private_resolvers > 0) {
825 resolvers->private_resolvers = calloc(resolvers->n_private_resolvers,
826 sizeof(dns_resolver_t *));
829 for (int i = 0; i < dns_config->n_resolver; i++) {
830 dns_resolver_t *r = dns_config->resolver[i];
832 if ([self isResolverMulticast:r] &&
833 (a < resolvers->n_multicast_resolvers)) {
834 resolvers->multicast_resolvers[a++] = r;
837 } else if ([self isResolverPrivate:r] &&
838 (b < resolvers->n_private_resolvers)) {
839 resolvers->private_resolvers[b++] = r;
843 if ((r->domain == NULL) &&
844 (r->n_nameserver > 0) &&
845 (c < resolvers->n_default_resolvers)) {
846 resolvers->default_resolvers[c++] = r;
855 * Generate a data blob for the resolver.
856 * Currently the blob only has:
858 * - sockaddr structs for each nameserver
862 - (NSData *)dataForResolver:(dns_resolver_t *)resolver
865 CFMutableDictionaryRef resolverDict = nil;
867 if (resolver == NULL) {
868 SC_log(LOG_NOTICE, "Invalid dns resolver");
872 if (resolver->n_search > 0) {
873 if (resolverDict == nil) {
874 resolverDict = CFDictionaryCreateMutable(NULL,
876 &kCFTypeDictionaryKeyCallBacks,
877 &kCFTypeDictionaryValueCallBacks);
880 CFMutableArrayRef searchDomainArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
882 /* Append search domains */
883 for (int i = 0; i < resolver->n_search; i++) {
884 CFArrayAppendValue(searchDomainArray, (__bridge CFStringRef)(@(resolver->search[i])));
887 CFDictionaryAddValue(resolverDict, CFSTR(kConfigAgentDNSSearchDomains), searchDomainArray);
888 CFRelease(searchDomainArray);
891 /* Get the count of nameservers */
892 if (resolver->n_nameserver > 0) {
893 if (resolverDict == nil) {
894 resolverDict = CFDictionaryCreateMutable(NULL,
896 &kCFTypeDictionaryKeyCallBacks,
897 &kCFTypeDictionaryValueCallBacks);
900 CFMutableArrayRef nameserverArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
902 /* Get all the nameservers */
903 for (int i = 0; i < resolver->n_nameserver; i++) {
905 _SC_sockaddr_to_string(resolver->nameserver[i], buf, sizeof(buf));
907 CFArrayAppendValue(nameserverArray, (__bridge CFStringRef)(@(buf)));
911 CFDictionaryAddValue(resolverDict, CFSTR(kConfigAgentDNSNameServers), nameserverArray);
912 CFRelease(nameserverArray);
915 if (resolverDict != nil) {
916 data = [NSPropertyListSerialization dataWithPropertyList:(__bridge id _Nonnull)(resolverDict)
917 format:NSPropertyListBinaryFormat_v1_0
921 CFRelease(resolverDict);
924 return (NSData *)data;
927 - (NSData *)getDNSDataFromCurrentConfig:(dns_config_t *)dns_config
928 domain:(NSString *)domain
930 if (dns_config == NULL || domain == nil) {
931 SC_log(LOG_NOTICE, "Invalid dns_config/domain");
935 if ((dns_config->n_resolver > 0) && (dns_config->resolver != NULL)) {
936 for (int i = 0; i < dns_config->n_resolver; i++) {
937 dns_resolver_t * resolver;
939 resolver = dns_config->resolver[i];
940 if (resolver->domain != NULL &&
941 ![self isResolverMulticast:resolver]) {
942 NSString * ns_domain_name;
944 ns_domain_name = @(resolver->domain);
945 if ([ns_domain_name isEqualToString:domain]) {
946 return [self dataForResolver:resolver];
957 - (BOOL)isResolverMulticast:(dns_resolver_t *)resolver
959 if (resolver->options == NULL) {
963 if (!strstr(resolver->options, "mdns")) {
970 - (BOOL)isResolverPrivate:(dns_resolver_t *)resolver
972 if (resolver->options == NULL) {
976 if (!strstr(resolver->options, "pdns")) {
983 - (void)processSupplementalDNSResolvers:(dns_config_t *)dns_config
985 NSMutableArray * deleteList;
986 NSMutableArray * new_domain_list;
987 NSCountedSet * duplicate_domain_list;
988 NSMutableArray * old_domain_list;
989 NSMutableArray * update_agent_list;
992 deleteList = [NSMutableArray array];
993 duplicate_domain_list = [[NSCountedSet alloc] initWithCapacity:0];
994 new_domain_list = [NSMutableArray array];
995 update_agent_list = [NSMutableArray array];
996 old_domain_list = [self getAgentList:self.floatingDNSAgentList
997 agentType:kAgentTypeDNS
998 agentSubType:kAgentSubTypeSupplemental];
1000 if (dns_config->resolver == NULL) {
1001 dns_config->n_resolver = 0;
1003 if (dns_config->n_resolver > 0) {
1004 for (int i = 0; i < dns_config->n_resolver; i++) {
1005 dns_resolver_t * resolver;
1007 resolver = dns_config->resolver[i];
1008 if (resolver->domain != NULL &&
1009 ![self isResolverPrivate:resolver] &&
1010 ![self isResolverMulticast:resolver]) {
1011 NSString * ns_domain_name;
1013 ns_domain_name = [NSString stringWithCString:resolver->domain encoding:NSASCIIStringEncoding];
1014 [new_domain_list addObject:ns_domain_name];
1019 [self cleanConflictingAgentsFromList:old_domain_list
1020 new_list:new_domain_list
1021 agentDictionary:self.floatingDNSAgentList];
1023 /* Sync between controller and current config */
1024 for (NSString *key in old_domain_list) {
1025 BOOL domain_present = NO;
1027 domain_present = [new_domain_list containsObject:key];
1028 if (domain_present == NO) {
1031 agent = [self.floatingDNSAgentList objectForKey:key];
1032 [self destroyFloatingAgent:agent];
1036 /* At this point, whatever is in the controller's floating agent list,
1037 is present in the current DNS config. The current DNS config
1038 might have even more configs, not known to the controller, YET
1041 for (NSString *domain in old_domain_list) {
1045 agent = [self.floatingDNSAgentList objectForKey:domain];
1050 /* Am I mapped to some agent? */
1051 mapped_agent = [agent getAgentMapping];
1053 /* OK, this agent is mapped to some other agent. We compare this agent's data
1054 * to the current data of the agent to which it is mapped. If different, we destroy
1055 * the agent and later map it to someone else OR spawn a new one.
1057 NSData *mapped_agent_data;
1059 mapped_agent_data = [self getDNSDataFromCurrentConfig:dns_config domain:[mapped_agent getAssociatedEntity]];
1060 if (mapped_agent_data == nil || ![[agent getAgentData] isEqual:mapped_agent_data]) {
1061 /* Something changed for mapped agent */
1062 [deleteList addObject:agent];
1066 /* Since this agent is NOT mapped to any other agent, this agent is
1067 * registered with the kernel. So instead of destroying the agent and
1068 * re-registering it, just update it here.
1070 * All the agents which were mapped to this agent, will be deleted and
1071 * re-mapped, if the data changed.
1075 agent_data = [self getDNSDataFromCurrentConfig:dns_config domain:[agent getAssociatedEntity]];
1076 if (![[agent getAgentData] isEqual:agent_data]) {
1077 /* Something changed for agent */
1078 [agent updateAgentData:agent_data];
1080 /* The reason I don't publish the data to agent here is that, if there were
1081 * some agents mapping to this one, they will momentarily have a policy for
1082 * using this agent UUID for some domain based on this agent's previous data.
1084 [update_agent_list addObject:agent];
1088 [new_domain_list removeObject:domain];
1091 for (id agent in deleteList) {
1092 SC_log(LOG_INFO, "Destroying agent %@ because something changed!", [agent getAgentName]);
1093 [self destroyFloatingAgent:agent];
1096 for (id agent in update_agent_list) {
1097 [self publishToAgent:agent];
1100 for (int idx = 0; idx < dns_config->n_resolver; idx++) {
1101 dns_resolver_t * resolver;
1103 resolver = dns_config->resolver[idx];
1104 if (resolver->domain != NULL &&
1105 ![self isResolverPrivate:resolver] &&
1106 ![self isResolverMulticast:resolver]) {
1110 NSString * ns_domain_name;
1112 ns_domain_name = @(resolver->domain);
1113 found = [new_domain_list indexOfObject:ns_domain_name];
1114 if (found == NSNotFound) {
1115 /* Nothing changed for this agent */
1119 /* We will only process agents which are mapped AND if the agent they were mapped to, changed OR
1120 * agents for domains which we did not know before.
1123 NSUInteger domainInstance = [duplicate_domain_list countForObject:ns_domain_name];
1124 if (domainInstance > 0) {
1125 /* domainInstance will be > 0, only if we have conflicting domains */
1127 data = [self dataForResolver:resolver];
1129 NSString *ns_domain_name_copy = [NSString stringWithFormat:@"%@" multipleEntitySuffix "%lu", ns_domain_name, (unsigned long)domainInstance];
1131 BOOL ok = [self spawnFloatingAgent:[DNSAgent class]
1132 entity:ns_domain_name_copy
1133 agentSubType:kAgentSubTypeSupplemental
1134 addPolicyOfType:NEPolicyConditionTypeDomain
1137 id agent = [self.floatingDNSAgentList objectForKey:ns_domain_name_copy];
1138 SC_log(LOG_INFO, "Duplicate DNS agent %@", [agent getAgentName]);;
1141 data = [self dataForResolver:resolver];
1142 mapped_agent = [self getAgentWithSameDataAndSubType:self.floatingDNSAgentList
1144 subType:kAgentSubTypeSupplemental];
1145 if (mapped_agent != nil) {
1146 [self spawnMappedFloatingAgent:mapped_agent
1147 entity:ns_domain_name
1148 agentSubType:kAgentSubTypeSupplemental
1149 addPolicyOfType:NEPolicyConditionTypeDomain
1152 [self spawnFloatingAgent:[DNSAgent class]
1153 entity:ns_domain_name
1154 agentSubType:kAgentSubTypeSupplemental
1155 addPolicyOfType:NEPolicyConditionTypeDomain
1160 [new_domain_list removeObjectAtIndex:found];
1161 [duplicate_domain_list addObject:ns_domain_name];
1169 - (void)processDNSResolvers:(dns_config_t *)dns_config
1171 resolver_list_t *resolvers = [self copyResolverList:dns_config];
1173 /* Process Default resolvers */
1174 NSMutableArray *old_default_resolver_list = [self getAgentList:self.floatingDNSAgentList
1175 agentType:kAgentTypeDNS
1176 agentSubType:kAgentSubTypeDefault];
1178 // For default resolvers, their name will be '_defaultDNS', '_defaultDNS #2' so on...
1179 if (resolvers->n_default_resolvers > 0 && resolvers->default_resolvers != NULL) {
1180 for (uint32_t i = 0; i < resolvers->n_default_resolvers; i++) {
1181 dns_resolver_t *default_resolver = resolvers->default_resolvers[i];
1184 NSString * resolverName;
1186 data = [self dataForResolver:default_resolver];
1188 resolverName = @(dnsAgentDefault);
1190 resolverName = [NSString stringWithFormat:@dnsAgentDefault multipleEntitySuffix "%d", i+1 ];
1193 dnsAgent = [self.floatingDNSAgentList objectForKey:resolverName];
1195 if (dnsAgent != nil) {
1196 [old_default_resolver_list removeObject:resolverName];
1197 if ([data isEqual:[dnsAgent getAgentData]]) {
1198 /* Leave this agent in place. Nothing changed! */
1201 [self destroyFloatingAgent:dnsAgent];
1205 [self spawnFloatingAgent:[DNSAgent class]
1207 agentSubType:kAgentSubTypeDefault
1208 addPolicyOfType:NEPolicyConditionTypeNone
1213 // Only agents that are NOT present in the new config, will be present in the list
1214 // and they need to be destroyed.
1215 [self deleteAgentList:self.floatingDNSAgentList list:old_default_resolver_list];
1217 /* Process Multicast resolvers */
1219 NSMutableArray *old_multicast_resolver_list = [self getAgentList:self.floatingDNSAgentList
1220 agentType:kAgentTypeDNS
1221 agentSubType:kAgentSubTypeMulticast];
1223 if (resolvers->n_multicast_resolvers > 0 && resolvers->multicast_resolvers != NULL) {
1224 for (uint32_t i = 0; i < resolvers->n_multicast_resolvers; i++) {
1225 dns_resolver_t * multicast_resolver = resolvers->multicast_resolvers[i];
1227 NSString * resolverName;
1229 if (multicast_resolver == NULL) {
1233 if (multicast_resolver->domain == NULL) {
1234 /* Multicast resolvers MUST have a domain */
1238 resolverName = @(multicast_resolver->domain);
1239 if (resolverName == NULL) {
1240 /* Multicast resolvers MUST have a domain */
1244 dnsAgent = [self.floatingDNSAgentList objectForKey:resolverName];
1245 if (dnsAgent != nil) {
1246 [old_multicast_resolver_list removeObject:resolverName];
1250 [self spawnFloatingAgent:[DNSAgent class]
1252 agentSubType:kAgentSubTypeMulticast
1253 addPolicyOfType:NEPolicyConditionTypeDomain
1255 // Don't care about data for mdns resolvers. Do we?
1259 [self deleteAgentList:self.floatingDNSAgentList list:old_multicast_resolver_list];
1261 /* Process Private resolvers */
1263 NSMutableArray *old_private_resolver_list = [self getAgentList:self.floatingDNSAgentList
1264 agentType:kAgentTypeDNS
1265 agentSubType:kAgentSubTypePrivate];
1267 if (resolvers->n_private_resolvers > 0 && resolvers->private_resolvers != NULL) {
1268 for (uint32_t i = 0; i < resolvers->n_private_resolvers; i++) {
1269 dns_resolver_t * private_resolver = resolvers->private_resolvers[i];
1271 NSString * resolverName;
1273 if (private_resolver == NULL) {
1277 if (private_resolver->domain == NULL) {
1278 /* private resolvers MUST have a domain */
1282 resolverName = @(private_resolver->domain);
1283 if (resolverName == nil) {
1284 /* Private resolvers MUST have a domain */
1288 dnsAgent = [self.floatingDNSAgentList objectForKey:resolverName];
1289 if (dnsAgent != nil) {
1290 [old_private_resolver_list removeObject:resolverName];
1294 [self spawnFloatingAgent:[DNSAgent class]
1296 agentSubType:kAgentSubTypePrivate
1297 addPolicyOfType:NEPolicyConditionTypeDomain
1299 // Don't care about data for pdns resolvers. Do we?
1303 [self deleteAgentList:self.floatingDNSAgentList list:old_private_resolver_list];
1306 [self freeResolverList:resolvers];
1309 - (void)processScopedDNSResolvers:(dns_config_t *)dns_config
1311 NSMutableArray * old_intf_list;
1312 old_intf_list = [self getAgentList:self.floatingDNSAgentList
1313 agentType:kAgentTypeDNS
1314 agentSubType:kAgentSubTypeScoped];
1316 if ((dns_config->n_scoped_resolver > 0) && (dns_config->scoped_resolver != NULL)) {
1317 for (int i = 0; i < dns_config->n_scoped_resolver; i++) {
1322 const char * if_name;
1323 NSString * ns_if_name;
1324 NSString * ns_if_name_with_prefix;
1325 dns_resolver_t * resolver;
1327 resolver = dns_config->scoped_resolver[i];
1328 if_name = my_if_indextoname(resolver->if_index, buf);
1330 ns_if_name = @(if_name);
1331 ns_if_name_with_prefix = [NSString stringWithFormat:@"%s%@", prefixForInterfaceName, ns_if_name];
1336 data = [self dataForResolver:resolver];
1337 idx = [old_intf_list indexOfObject:ns_if_name_with_prefix];
1339 if (idx == NSNotFound) {
1340 /* We need to spawn an agent */
1341 [self spawnFloatingAgent:[DNSAgent class]
1342 entity:ns_if_name_with_prefix
1343 agentSubType:kAgentSubTypeScoped
1344 addPolicyOfType:NEPolicyConditionTypeScopedInterface
1348 /* We have an agent on this interface. Update it */
1349 [old_intf_list removeObjectAtIndex:idx];
1352 /* Get the DNS agent for this interface? */
1353 dnsAgent = [self.floatingDNSAgentList objectForKey:ns_if_name_with_prefix];
1354 if (dnsAgent != nil) {
1355 /* Do we need to update this agent? */
1356 [dnsAgent updateAgentData:data];
1357 if ([dnsAgent shouldUpdateAgent]) {
1358 [self publishToAgent:dnsAgent];
1364 [self deleteAgentList:self.floatingDNSAgentList list:old_intf_list];
1367 - (void)processServiceSpecificDNSResolvers:(dns_config_t *)dns_config
1369 NSMutableArray * old_service_list;
1370 old_service_list = [self getAgentList:self.floatingDNSAgentList
1371 agentType:kAgentTypeDNS
1372 agentSubType:kAgentSubTypeServiceSpecific];
1374 if ((dns_config->n_service_specific_resolver > 0) && (dns_config->service_specific_resolver != NULL)) {
1375 for (int i = 0; i < dns_config->n_service_specific_resolver; i++) {
1379 uint32_t service_identifier;
1380 NSString * ns_service_identifier_with_prefix;
1381 dns_resolver_t * resolver;
1383 resolver = dns_config->service_specific_resolver[i];
1384 service_identifier = resolver->service_identifier;
1385 if (service_identifier != 0) {
1386 ns_service_identifier_with_prefix = [NSString stringWithFormat:@"%s%u", prefixForInterfaceName, service_identifier];
1391 data = [self dataForResolver:resolver];
1392 idx = [old_service_list indexOfObject:ns_service_identifier_with_prefix];
1394 if (idx == NSNotFound) {
1395 /* We need to spawn an agent */
1396 [self spawnFloatingAgent:[DNSAgent class]
1397 entity:ns_service_identifier_with_prefix
1398 agentSubType:kAgentSubTypeServiceSpecific
1399 addPolicyOfType:(POLICY_TYPE_NO_POLICY) /* Don't install a policy */
1403 /* We have an agent on this interface. Update it */
1404 [old_service_list removeObjectAtIndex:idx];
1407 /* Get the DNS agent for this interface? */
1408 dnsAgent = [self.floatingDNSAgentList objectForKey:ns_service_identifier_with_prefix];
1409 if (dnsAgent != nil) {
1410 /* Do we need to update this agent? */
1411 [dnsAgent updateAgentData:data];
1412 if ([dnsAgent shouldUpdateAgent]) {
1413 [self publishToAgent:dnsAgent];
1419 [self deleteAgentList:self.floatingDNSAgentList list:old_service_list];
1422 #define ONION_RESOLVER_DOMAIN "onion"
1423 - (BOOL)isResolverOnion:(dns_resolver_t *)resolver
1425 if (resolver->domain != NULL &&
1426 (strcmp(resolver->domain, ONION_RESOLVER_DOMAIN) == 0)) {
1434 - (void)processOnionResolver:(dns_config_t *)dns_config
1436 static NSUInteger policy_id = 0;
1438 if (dns_config == NULL) {
1442 /* Run through the resolver configurations. We only care for the supplemental resolvers. */
1443 for (int32_t i = 0; i < dns_config->n_resolver; i++) {
1444 dns_resolver_t *resolver = dns_config->resolver[i];
1445 if ([self isResolverOnion:resolver]) {
1450 /* We do not have any such resolver. Add a system-wide "drop" policy for this domain */
1451 if (policy_id == 0) {
1452 NEPolicy *policy = [[NEPolicy alloc] initWithOrder:INIT_ORDER_FOR_DOMAIN_POLICY
1453 result:[NEPolicyResult drop]
1454 conditions:@[[NEPolicyCondition domain:@ONION_RESOLVER_DOMAIN]]];
1455 if (policy != nil) {
1456 policy_id = [self.policySession addPolicy:policy];
1457 if (![self.policySession apply]) {
1459 SC_log(LOG_NOTICE, "Could not add a [." ONION_RESOLVER_DOMAIN "] drop policy");
1461 SC_log(LOG_INFO, "Added a [." ONION_RESOLVER_DOMAIN "] drop policy");
1470 /* We have such a resolver in the config OR no DNS config at all. Remove the system-wide "drop" policy for this domain */
1471 if (policy_id > 0) {
1472 [self.policySession removePolicyWithID:policy_id];
1473 if (![self.policySession apply]) {
1474 SC_log(LOG_NOTICE, "Could not remove the [." ONION_RESOLVER_DOMAIN "] drop policy");
1477 SC_log(LOG_INFO, "Removed the [." ONION_RESOLVER_DOMAIN "] drop policy");
1483 #undef ONION_RESOLVER_DOMAIN
1486 - (void)processDNSChanges
1488 dns_config_t * dns_config;
1490 dns_config = dns_configuration_copy();
1491 if (dns_config == NULL) {
1492 SC_log(LOG_INFO, "No DNS configuration");
1493 NSMutableDictionary *copy = [self.floatingDNSAgentList copy];
1494 for (NSString *entity in copy) {
1495 id agent = [copy objectForKey:entity];
1497 [self destroyFloatingAgent:agent];
1502 [self processDNSResolvers:dns_config];
1503 [self processScopedDNSResolvers:dns_config];
1504 [self processSupplementalDNSResolvers:dns_config];
1505 [self processServiceSpecificDNSResolvers:dns_config];
1509 [self processOnionResolver:dns_config];
1510 [self applyPolicies];
1512 if (dns_config != NULL) {
1513 dns_configuration_free(dns_config);
1517 #pragma mark Helper functions
1519 - (const void *)copyConfigAgentData:(NSMutableDictionary *)controllerDict
1520 uuid:(uuid_t)requested_uuid
1521 length:(uint64_t *)length
1523 if (length == NULL) {
1524 SC_log(LOG_NOTICE, "Invalid parameters for copying agent data");
1529 void *buffer = NULL;
1532 for (NSString *key in controllerDict) {
1533 id temp_agent = [controllerDict objectForKey:key];
1537 [[temp_agent getAgentUUID] getUUIDBytes:agent_uuid];
1538 if (uuid_compare(agent_uuid, requested_uuid) == 0) {
1545 uuid_string_t uuid_str;
1546 uuid_unparse(requested_uuid, uuid_str);
1547 SC_log(LOG_NOTICE, "Invalid config agent uuid %s specified", uuid_str);
1551 NSData *data = [agent getAgentData];
1552 uint64_t len = [data length];
1555 buffer = malloc((size_t)len);
1556 memcpy(buffer, [data bytes], len);
1559 return (const void *)buffer;
1562 - (const void *)copyProxyAgentData:(uuid_t)requested_uuid
1563 length:(uint64_t *)length
1565 return [self copyConfigAgentData:self.floatingProxyAgentList
1570 - (const void *)copyDNSAgentData:(uuid_t)requested_uuid
1571 length:(uint64_t *)length
1573 return [self copyConfigAgentData:self.floatingDNSAgentList
1578 - (NSData *)dataLengthSanityCheck:(id)agent
1580 NSData * data = [agent getAgentData];
1582 if ([data length] > CONFIG_AGENT_DATA_LIMIT) {
1583 /* We impose a limit on the config agent data as 1KB.
1584 * If we have a data blob larger than this limit, do NOT publish it into the agent.
1585 * Instead publish a key which will trigger fetching of the configuration directly
1586 * through NWI server.
1588 NSMutableDictionary *data_dict = [NSMutableDictionary dictionary];
1590 NSUUID *uuid = [agent getAgentUUID];
1592 [uuid getUUIDBytes:c_uuid];
1593 NSData *uuid_data = [[NSData alloc] initWithBytes:c_uuid length:sizeof(c_uuid)];
1594 [data_dict setValue:uuid_data forKey:@kConfigAgentOutOfBandDataUUID];
1596 NSData *new_data = [NSPropertyListSerialization dataWithPropertyList:data_dict
1597 format:NSPropertyListBinaryFormat_v1_0
1608 * For conflicting agents, the convention is that its name & entity,
1609 * will have a suffix " #<number>". This function will sanitize the
1610 * suffix and just return the entity name
1612 - (NSString *)sanitizeEntity:(NSString *)entity
1614 NSRange range = [entity rangeOfString:@multipleEntitySuffix];
1615 if (range.location != NSNotFound) {
1616 NSString *str = [entity substringToIndex:range.location];
1624 * For interface names, there is a prefix to differentiate then
1625 * from the domain name (iff there were conflicting domain names).
1626 * Returns the sanitized interface name
1628 - (NSString *)sanitizeInterfaceName:(NSString *)intf
1630 NSRange range = [intf rangeOfString:@prefixForInterfaceName];
1631 if (range.location != NSNotFound) {
1632 NSString *str = [intf substringFromIndex:(range.location + strlen(prefixForInterfaceName))];
1640 * For conflicting agents, the convention is that its name & entity,
1641 * will have a suffix " #<number>". This function will return that <number>
1643 - (int)entityInstanceNumber:(NSString *)entity
1645 NSRange range = [entity rangeOfString:@multipleEntitySuffix];
1646 if (range.location != NSNotFound) {
1647 NSString *str = [entity substringFromIndex:(range.location + strlen(multipleEntitySuffix))];
1648 return str.intValue;
1655 * In case that we have conflicting DNS/Proxy domains
1656 * This function will remove all those conflicting agents,
1657 * so that we can start afresh with the new config
1659 - (void)cleanConflictingAgentsFromList:(NSMutableArray *)old_list
1660 new_list:(NSMutableArray *)new_list
1661 agentDictionary:(NSMutableDictionary *)agent_list
1663 NSCountedSet * duplicate_domain_list;
1665 for (NSString *domain in old_list) {
1666 /* If we had conflicting domains before, remove all of them */
1667 NSString *sanitizedDomain = [self sanitizeEntity:domain];
1668 if (![sanitizedDomain isEqualToString:domain]) {
1669 /* Destroy the original domain */
1670 id agent = [agent_list objectForKey:sanitizedDomain];
1671 [self destroyFloatingAgent:agent];
1673 /* Destroy the conflicting domain */
1674 agent = [agent_list objectForKey:domain];
1675 [self destroyFloatingAgent:agent];
1677 SC_log(LOG_INFO, "Removing conflicting domain: %@, %@", sanitizedDomain, domain);
1681 duplicate_domain_list = [[NSCountedSet alloc] initWithArray:new_list];
1682 for (NSString *domain in old_list) {
1683 if ([duplicate_domain_list countForObject:domain] > 1) {
1684 id agent = [agent_list objectForKey:domain];
1685 [self destroyFloatingAgent:agent];
1686 SC_log(LOG_INFO, "Removing domain %@ as it has duplicates in the current config", domain);
1692 * Get the list of agents from a specific dictionary.
1693 * The list of agents will only consist of the ones which
1694 * match the agent type and sub-type
1697 - (NSMutableArray *)getAgentList:(NSMutableDictionary *)all_agents
1698 agentType:(AgentType)type
1699 agentSubType:(AgentSubType)subtype
1701 NSMutableArray *list = [NSMutableArray array];
1702 NSArray *agentObjects = [all_agents allValues];
1704 for (id agent in agentObjects) {
1705 if (([agent getAgentType] == type) &&
1706 ([agent getAgentSubType] == subtype)) {
1708 [list addObject:[agent getAssociatedEntity]];
1716 * Destroy all the agents are listed in "list"
1719 - (void)deleteAgentList:(NSMutableDictionary *)all_agents
1720 list:(NSMutableArray *)list
1722 for (NSString *intf in list) {
1725 agent = [all_agents objectForKey:intf];
1726 [self destroyFloatingAgent:agent];
1731 * In order to not duplicate agents with same content,
1732 * we map an agent X to agent Y, when their content is the same.
1734 * This function tries to find that agent Y
1737 - (id)getAgentWithSameDataAndSubType:(NSMutableDictionary *)agentList
1739 subType:(AgentSubType)subtype
1741 for (NSString *key in agentList) {
1742 id agent = [agentList objectForKey:key];
1743 if ([[agent getAgentData] isEqual:data]) {
1744 /* Do not map to default agents */
1745 if ([agent getAgentSubType] != subtype) {
1749 /* Return only registered agents */
1750 if ([agent getRegistrationObject] != nil) {
1759 #pragma mark Policy installation function
1762 * Add NECP policies for an agent
1764 - (BOOL)addPolicyToFloatingAgent:(id)agent
1765 domain:(NSString *)domain
1766 agentUUIDToUse:(NSUUID *)uuid
1767 policyType:(NEPolicyConditionType)policyType
1768 useControlPolicySession:(BOOL)useControlPolicySession
1770 NEPolicyCondition * condition = nil;
1771 NEPolicySession * session;
1772 uint32_t multiple_entity_offset;
1773 NEPolicy * newPolicy;
1775 uint32_t orderForSkip;
1776 NSMutableArray * policyArray;
1777 NSUInteger policyID1;
1778 NSUInteger policyID2;
1779 NEPolicyResult * result;
1782 uint32_t typeOffset;
1784 type = [agent getAgentType];
1785 typeOffset = (type == kAgentTypeDNS) ? 0 : 5000;
1786 skipOrder = (type == kAgentTypeDNS) ? 5000 : 0;
1788 multiple_entity_offset = (uint32_t)[self entityInstanceNumber:domain];
1789 domain = [self sanitizeEntity:domain];
1791 switch (policyType) {
1792 case NEPolicyConditionTypeScopedInterface:
1793 order = INIT_ORDER_FOR_SCOPED_INTERFACE_POLICY + typeOffset + multiple_entity_offset;
1794 domain = [self sanitizeInterfaceName:domain];
1795 condition = [NEPolicyCondition scopedInterface:domain];
1796 orderForSkip = SKIP_ORDER_FOR_SCOPED_INTERFACE_POLICY + typeOffset;
1799 case NEPolicyConditionTypeDomain:
1800 order = INIT_ORDER_FOR_DOMAIN_POLICY + typeOffset + multiple_entity_offset;
1801 condition = [NEPolicyCondition domain:domain];
1802 orderForSkip = SKIP_ORDER_FOR_DOMAIN_POLICY + typeOffset;
1805 case NEPolicyConditionTypeAllInterfaces:
1806 order = INIT_ORDER_FOR_DEFAULT_POLICY + typeOffset + multiple_entity_offset;
1807 condition = [NEPolicyCondition allInterfaces];
1808 orderForSkip = SKIP_ORDER_FOR_DEFAULT_POLICY + typeOffset;
1811 case NEPolicyConditionTypeNone:
1812 order = INIT_ORDER_FOR_DEFAULT_POLICY + typeOffset + multiple_entity_offset;
1813 orderForSkip = SKIP_ORDER_FOR_DEFAULT_POLICY + typeOffset;
1817 SC_log(LOG_NOTICE, "Invalid policy condition specified");
1821 result = [NEPolicyResult netAgentUUID:uuid];
1822 newPolicy = [[NEPolicy alloc] initWithOrder:order
1824 conditions: (condition ? @[condition] : nil)];
1826 if (newPolicy == nil) {
1827 SC_log(LOG_NOTICE, "Could not create a policy for agent %@", [agent getAgentName]);
1831 if (useControlPolicySession) {
1832 if (self.controlPolicySession == nil) {
1833 /* The NE policy session at "control" level for the controller */
1834 self.controlPolicySession = [self createPolicySession];
1835 if (self.controlPolicySession == nil) {
1836 SC_log(LOG_NOTICE, "Could not create a control policy session for agent %@", [agent getAgentName]);
1839 [self.controlPolicySession setPriority:NEPolicySessionPriorityControl];
1841 ((ConfigAgent *)agent).preferredPolicySession = self.controlPolicySession;
1843 ((ConfigAgent *)agent).preferredPolicySession = self.policySession;
1846 session = ((ConfigAgent *)agent).preferredPolicySession;
1848 policyID1 = [session addPolicy:newPolicy];
1849 if (policyID1 == 0) {
1850 SC_log(LOG_NOTICE, "Could not add a netagent policy for agent %@", [agent getAgentName]);
1854 result = [NEPolicyResult skipWithOrder:skipOrder];
1855 newPolicy = [[NEPolicy alloc] initWithOrder:orderForSkip
1857 conditions:(condition ? @[condition] : nil)];
1859 if (newPolicy == nil) {
1860 SC_log(LOG_NOTICE, "Could not create a policy for agent %@", [agent getAgentName]);
1864 policyID2 = [session addPolicy:newPolicy];
1865 if (policyID2 == 0) {
1866 SC_log(LOG_NOTICE, "Could not add a skip policy for agent %@", [agent getAgentName]);
1870 policyArray = [self.policyDB objectForKey:[agent getAgentName]];
1871 if (policyArray == nil) {
1872 policyArray = [NSMutableArray array];
1875 [policyArray addObject:numberToNSNumber(policyID1)];
1876 [policyArray addObject:numberToNSNumber(policyID2)];
1877 [self.policyDB setObject:policyArray forKey:[agent getAgentName]];
1882 #pragma mark Agent manipulation functions
1887 - (BOOL)spawnFloatingAgent:(Class)agentClass
1888 entity:(NSString *)entity
1889 agentSubType:(AgentSubType)subtype
1890 addPolicyOfType:(NEPolicyConditionType)policyType
1891 publishData:(NSData *)data
1895 NSMutableDictionary * parameters;
1897 parameters =[NSMutableDictionary dictionary];
1898 [parameters setValue:entity forKey:@kEntityName];
1899 [parameters setValue:numberToNSNumber(subtype) forKey:@kAgentSubType];
1901 agent = [[agentClass alloc] initWithParameters:parameters];
1902 ok = [self registerAgent:agent];
1908 /* Since we just spawned this agent, update its data */
1909 [agent updateAgentData:data];
1910 [self publishToAgent:agent];
1913 /* Add a policy if there is a valid type. If POLICY_TYPE_NO_POLICY, then ignore policies.
1914 * POLICY_TYPE_NO_POLICY will be set for service-specific agents, in which case we rely on
1915 * service owners to install custom policies to point at the agents. */
1916 if (policyType >= NEPolicyConditionTypeNone) {
1917 BOOL useControlPolicySession = NO;
1918 if (subtype == kAgentSubTypeGlobal) {
1919 /* Policies for a Global scoped agents are at "control" level */
1920 useControlPolicySession = YES;
1923 ok = [self addPolicyToFloatingAgent:agent
1925 agentUUIDToUse:[agent agentUUID]
1926 policyType:policyType
1927 useControlPolicySession:useControlPolicySession];
1930 [self unregisterAgent:agent];
1935 SC_log(LOG_INFO, "Spawning floating agent for %@", entity);
1937 if ([agent getAgentType] == kAgentTypeProxy) {
1938 [self.floatingProxyAgentList setObject:agent forKey:entity];
1940 [self.floatingDNSAgentList setObject:agent forKey:entity];
1947 * Create an agent mapped to another agent.
1949 - (BOOL)spawnMappedFloatingAgent:(id)mapped_agent
1950 entity:(NSString *)entity
1951 agentSubType:(AgentSubType)subtype
1952 addPolicyOfType:(NEPolicyConditionType)policyType
1953 updateData:(NSData *)data
1956 NSMutableDictionary * parameters;
1958 parameters = [NSMutableDictionary dictionary];
1959 [parameters setValue:entity forKey:@kEntityName];
1960 [parameters setValue:numberToNSNumber(subtype) forKey:@kAgentSubType];
1962 dummyAgent = [[[mapped_agent class] alloc] initWithParameters:parameters];
1965 /* Since we just spawned this agent, update its data.
1966 * We do not publish it since this agent is mapped
1967 * to an agent which already has the same data
1969 [dummyAgent updateAgentData:data];
1972 BOOL useControlPolicySession = NO;
1973 if (subtype == kAgentSubTypeGlobal) {
1974 /* Policies for a Global scoped agents are at "control" level */
1975 useControlPolicySession = YES;
1978 BOOL ok = [self addPolicyToFloatingAgent:dummyAgent
1980 agentUUIDToUse:[mapped_agent agentUUID]
1981 policyType:policyType
1982 useControlPolicySession:useControlPolicySession];
1988 if ([mapped_agent getAgentType] == kAgentTypeProxy) {
1989 [self.floatingProxyAgentList setObject:dummyAgent forKey:entity];
1991 [self.floatingDNSAgentList setObject:dummyAgent forKey:entity];
1994 [dummyAgent setAgentMapping:mapped_agent];
1996 SC_log(LOG_INFO, "Mapped floating agent %@ to %@", [dummyAgent getAgentName], [mapped_agent getAgentName]);
2001 * Write into an agent
2003 - (BOOL)publishToAgent:(id)agent
2005 /* Before any data goes into the kernel, do a sanity check. */
2006 NSData *sanityCheckData = [self dataLengthSanityCheck:agent];
2007 NSData *tempAgentData = nil;
2009 if (sanityCheckData != nil) {
2010 /* Data length is more than the limit! for updateNetworkAgent, the data blob
2011 * has to be a part of the agent object. Thus the temporary data replacement!
2013 tempAgentData = [[agent getAgentData] copy];
2014 [agent updateAgentData:sanityCheckData];
2015 SC_log(LOG_NOTICE, "Data too large for %@ (%lu bytes)!", [agent getAgentName], (unsigned long)[tempAgentData length]);
2020 NWNetworkAgentRegistration *regObject = [agent valueForKey:@"registrationObject"];
2021 if (regObject != nil) {
2022 SC_log(LOG_NOTICE, "Publishing data to agent %@ (%lu bytes)", [agent getAgentName], (unsigned long)[[agent getAgentData] length]);
2023 ok = [regObject updateNetworkAgent:agent];
2025 SC_log(LOG_NOTICE, "Could not update config agent");
2028 SC_log(LOG_NOTICE, "Config Agent not registered. Cannot Update");
2031 if (tempAgentData != nil) {
2032 [agent updateAgentData:tempAgentData];
2041 - (BOOL)destroyFloatingAgent:(id)agent
2045 if ( agent != nil) {
2046 NSMutableArray * policyArray;
2048 policyArray = [self.policyDB objectForKey:[agent getAgentName]];
2049 if (policyArray != nil) {
2050 NEPolicySession * session = ((ConfigAgent *)agent).preferredPolicySession;
2053 for (NSNumber *policyID in policyArray) {
2056 idVal = [policyID unsignedIntegerValue];
2057 result = [session removePolicyWithID:idVal];
2059 SC_log(LOG_NOTICE, "Could not remove policy %@ for agent %@", [session policyWithID:idVal], [agent getAgentName]);
2063 [self.policyDB removeObjectForKey:[agent getAgentName]];
2066 if ([agent getAgentType] == kAgentTypeProxy) {
2067 [self.floatingProxyAgentList removeObjectForKey:[agent getAssociatedEntity]];
2069 [self.floatingDNSAgentList removeObjectForKey:[agent getAssociatedEntity]];
2072 if ([agent getRegistrationObject] != nil) {
2073 [self unregisterAgent:agent];
2076 SC_log(LOG_INFO, "X - Destroyed agent %@", [agent getAgentName]);
2078 /* Check if we need to close the "control" policy session */
2079 if (self.controlPolicySession != nil) {
2080 NSMutableArray *globalProxyAgentList;
2081 NSMutableArray *globalDNSAgentList;
2082 globalProxyAgentList = [self getAgentList:self.floatingProxyAgentList agentType:kAgentTypeProxy agentSubType:kAgentSubTypeGlobal];
2083 globalDNSAgentList = [self getAgentList:self.floatingDNSAgentList agentType:kAgentTypeDNS agentSubType:kAgentSubTypeGlobal];
2085 if ([globalProxyAgentList count] == 0 &&
2086 [globalDNSAgentList count] == 0) {
2087 ok = [self.controlPolicySession removeAllPolicies];
2089 SC_log(LOG_ERR, "Could not remove policies for agent %@", [agent getAgentName]);
2092 self.controlPolicySession = nil;
2093 SC_log(LOG_NOTICE, "Closed control policy session");
2106 - (BOOL)registerAgent:(id)agent
2110 NWNetworkAgentRegistration *registration = [[NWNetworkAgentRegistration alloc] initWithNetworkAgentClass:[agent class]];
2112 ok = [registration registerNetworkAgent:agent];
2114 SC_log(LOG_NOTICE, "Could not register config agent");
2118 [agent addAgentRegistrationObject:registration];
2125 * Unregister an agent
2127 - (BOOL)unregisterAgent:(id)agent
2131 NWNetworkAgentRegistration *regObject = [agent valueForKey:@"registrationObject"];
2132 if (regObject != nil) {
2133 ok = [regObject unregisterNetworkAgent];
2135 SC_log(LOG_NOTICE, "Could not unregister config agent");
2138 SC_log(LOG_NOTICE, "Config Agent not registered. Cannot unregister");