2 * Copyright (c) 2015-2017, 2020 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 * floatingProxyAgentList_TCPConverter;
60 @property (nonatomic) NSMutableDictionary * floatingDNSAgentList;
61 @property (nonatomic) NSMutableDictionary * policyDB;
62 @property (nonatomic) NEPolicySession * policySession;
63 @property (nonatomic) NEPolicySession * controlPolicySession;
67 @implementation AgentController
71 + (AgentController *)sharedController
73 static AgentController * gController = nil;
74 static dispatch_once_t onceToken;
76 dispatch_once(&onceToken, ^{
77 gController = [[AgentController alloc] init];
80 @synchronized (gController) {
81 if (![gController isControllerReady]) {
82 if (![gController initializeController]) {
95 [self initializeController];
101 - (BOOL)initializeController
103 const char *errorMessage = NULL;
106 /* The NE policy session for the controller */
108 if (self.policySession == nil) {
109 self.policySession = [self createPolicySession];
110 if (self.policySession == nil) {
111 errorMessage = "Failed to create a policy session";
116 /* A dictionary of all floating proxy agents
117 * Key : <entity-name> (can be an interface name or domain name)
118 * Value : agent object
121 if (self.floatingProxyAgentList == nil) {
122 self.floatingProxyAgentList = [NSMutableDictionary dictionary];
123 if (self.floatingProxyAgentList == nil) {
124 errorMessage = "Failed to create a dictionary";
129 if (self.floatingProxyAgentList_TCPConverter == nil) {
130 self.floatingProxyAgentList_TCPConverter = [NSMutableDictionary dictionary];
131 if (self.floatingProxyAgentList_TCPConverter == nil) {
132 errorMessage = "Failed to create a dictionary";
137 /* A dictionary of all floating dns agents
138 * Key : <entity-name> (can be an interface name or domain name)
139 * Value : agent object
142 if (self.floatingDNSAgentList == nil) {
143 self.floatingDNSAgentList = [NSMutableDictionary dictionary];
144 if (self.floatingDNSAgentList == nil) {
145 errorMessage = "Failed to create a dictionary";
150 /* A dictionary for the maintaining the policy IDs for all installed policy.
151 * These IDs would be necessary to uninstall a policy when an agent goes away
152 * Key : agent name (which can be retrieved by [agent getAgentName])
153 * Value : An array of integers, each being a policy ID for that agent
156 if (self.policyDB == nil) {
157 self.policyDB = [NSMutableDictionary dictionary];
158 if (self.policyDB == nil) {
159 errorMessage = "Failed to create a dictionary";
164 /* The queue to run the all processing on */
166 if (self.controllerQueue == nil) {
167 self.controllerQueue = dispatch_queue_create("com.apple.SystemConfiguration.controllerQueue", NULL);
168 if (self.controllerQueue == nil) {
169 errorMessage = "Failed to create a queue";
175 if (errorMessage != NULL) {
176 /* Some error occurred. This is unlikely during controller initialization... */
177 SC_log(LOG_ERR, "Error occured while initializing AgentController: %s", errorMessage);
178 _SC_crash(errorMessage, NULL, NULL);
185 - (NEPolicySession *)createPolicySession
187 return [[NEPolicySession alloc] init];
190 - (BOOL)isControllerReady
192 /* Make sure that we have all our data structures in place */
193 return ((self.policySession != nil) &&
194 (self.floatingProxyAgentList != nil) &&
195 (self.floatingProxyAgentList_TCPConverter != nil) &&
196 (self.floatingDNSAgentList != nil) &&
197 (self.policyDB != nil) &&
198 (self.controllerQueue != nil));
201 /* ========================== proxy agent helpers =========================== */
202 #pragma mark Proxy agent helper functions
204 - (NSData *)dataForProxyArray:(CFArrayRef)proxy_array_for_data
206 CFDataRef data = NULL;
207 (void)_SCSerialize(proxy_array_for_data, &data, NULL, NULL);
208 return (__bridge_transfer NSData *)data;
211 - (NSData *)dataForProxyDictionary:(CFDictionaryRef)domain_proxy
214 CFMutableDictionaryRef domain_proxy_dict;
216 if (domain_proxy == NULL) {
217 SC_log(LOG_NOTICE, "Invalid domain proxy dict");
221 domain_proxy_dict = CFDictionaryCreateMutableCopy(NULL, 0, domain_proxy);
222 CFDictionaryRemoveValue(domain_proxy_dict, kSCPropNetProxiesSupplementalMatchDomain);
224 data = (__bridge_transfer NSData *)(SCNetworkProxiesCreateProxyAgentData(domain_proxy_dict));
225 CFRelease(domain_proxy_dict);
230 - (NSData *)getProxyDataFromCurrentConfig:(CFDictionaryRef)proxies
231 domain:(NSString *)domain
235 CFArrayRef supplemental;
237 if (proxies == NULL || domain == nil) {
238 SC_log(LOG_NOTICE, "Invalid proxies/domain");
242 supplemental = CFDictionaryGetValue(proxies, kSCPropNetProxiesSupplemental);
243 count = supplemental ? CFArrayGetCount(supplemental) : 0;
245 for (idx = 0; idx < count; idx++) {
246 CFDictionaryRef domain_proxy;
247 CFStringRef match_domain;
249 domain_proxy = CFArrayGetValueAtIndex(supplemental, idx);
250 match_domain = CFDictionaryGetValue(domain_proxy, kSCPropNetProxiesSupplementalMatchDomain);
251 if (match_domain != NULL && CFEqual(match_domain, (__bridge CFTypeRef)(domain))) {
252 return [self dataForProxyDictionary:domain_proxy];
259 - (bool)getIntValue:(CFTypeRef)cf_value
260 valuePtr:(int *) int_value_ptr
263 if (cf_value && CFGetTypeID(cf_value) == CFNumberGetTypeID() && CFNumberGetValue(cf_value, kCFNumberIntType, int_value_ptr))
270 - (int)countProxyEntriesEnabled:(CFDictionaryRef)proxies
272 const CFStringRef enableKeys[] = {
273 kSCPropNetProxiesHTTPEnable,
274 kSCPropNetProxiesHTTPSEnable,
275 kSCPropNetProxiesProxyAutoConfigEnable,
276 kSCPropNetProxiesFTPEnable,
277 kSCPropNetProxiesGopherEnable,
278 kSCPropNetProxiesRTSPEnable,
279 kSCPropNetProxiesSOCKSEnable,
280 kSCPropNetProxiesTransportConverterEnable,
281 kSCPropNetProxiesProxyAutoDiscoveryEnable,
284 if (proxies == NULL) {
285 SC_log(LOG_NOTICE, "No proxies");
289 for (size_t i = 0; i < (sizeof(enableKeys) / sizeof(enableKeys[0])); i++) {
292 if ([self getIntValue:CFDictionaryGetValue(proxies, enableKeys[i])
293 valuePtr:&enabled] &&
302 - (void)processSupplementalProxyChanges:(CFDictionaryRef)proxies
305 NSMutableArray * deleteList;
306 NSCountedSet * duplicate_domain_list;
308 NSMutableArray * new_domain_list;
309 NSMutableArray * old_domain_list;
310 CFArrayRef supplemental;
311 NSMutableArray * update_agent_list;
313 if (proxies == NULL) {
314 SC_log(LOG_INFO, "No proxy config to process");
318 old_domain_list = [self getAgentList:self.floatingProxyAgentList
319 agentType:kAgentTypeProxy
320 agentSubType:kAgentSubTypeSupplemental];
321 duplicate_domain_list = [[NSCountedSet alloc] initWithCapacity:0];
322 new_domain_list = [NSMutableArray array];
323 update_agent_list = [NSMutableArray array];
324 supplemental = CFDictionaryGetValue(proxies, kSCPropNetProxiesSupplemental);
325 count = supplemental ? CFArrayGetCount(supplemental) : 0;
326 deleteList = [NSMutableArray array];
328 for (idx = 0; idx < count; idx++) {
329 CFDictionaryRef domain_proxy;
330 CFStringRef match_domain;
333 domain_proxy = CFArrayGetValueAtIndex(supplemental, idx);
334 match_domain = CFDictionaryGetValue(domain_proxy, kSCPropNetProxiesSupplementalMatchDomain);
335 if (match_domain == NULL) {
339 /* This domain is present in current config. But if it has generic (no protocols enabled)
340 * proxy content, there is no real use of that agent. Do NOT add it to
341 * the new_domain_list.
343 * This way, if there was an agent previously for this domain,
344 * it will be destroyed AND since it is not present in the new domain list, we wont
345 * spawn a new agent too! :)
348 proxy_count = [self countProxyEntriesEnabled:domain_proxy];
349 if (proxy_count == 0) {
350 SC_log(LOG_INFO, "Proxy settings on %@ are generic. Not recognizing as new domain", match_domain);
354 [new_domain_list addObject:(__bridge NSString *)match_domain];
357 [self cleanConflictingAgentsFromList:old_domain_list
358 new_list:new_domain_list
359 agentDictionary:self.floatingProxyAgentList];
361 for (NSString *key in old_domain_list) {
364 domain_present = [new_domain_list containsObject:key];
365 if (domain_present == NO) {
368 agent = [self.floatingProxyAgentList objectForKey:key];
369 [self destroyFloatingAgent:agent];
373 /* At this point, whatever is in the controller's floating agent list,
374 * is present in the current proxy config. The current proxy config
375 * might have even more configs, not known to the controller, YET
378 for (NSString *domain in old_domain_list) {
382 agent = [self.floatingProxyAgentList objectForKey:domain];
387 /* Am I mapped to some agent? */
388 mapped_agent = [agent getAgentMapping];
390 /* OK, this agent is mapped to some other agent. We compare this agent's data
391 * to the current data of the agent to which it is mapped. If different, we destroy
392 * the agent and later map it to someone else OR spawn a new one.
394 NSData * mapped_agent_data;
396 mapped_agent_data = [self getProxyDataFromCurrentConfig:proxies domain:[mapped_agent getAssociatedEntity]];
397 if (mapped_agent_data == nil || ![[agent getAgentData] isEqual:mapped_agent_data]) {
398 /* Something changed for mapped agent */
399 [deleteList addObject:agent];
403 /* Since this agent is NOT mapped to any other agent, this agent is
404 * registered with the kernel. So instead of destroying the agent and
405 * re-registering it, just update it here.
407 * All the agents which were mapped to this agent, will be deleted and
408 * re-mapped, if the data changed.
412 agent_data = [self getProxyDataFromCurrentConfig:proxies domain:[agent getAssociatedEntity]];
413 if (![[agent getAgentData] isEqual:agent_data]) {
414 /* Something changed for agent */
415 [agent updateAgentData:agent_data];
417 /* The reason I don't publish the data to agent here is that, if there were
418 * some agents mapping to this one, they will momentarily have a policy for
419 * using this agent UUID for some domain based on this agent's previous data.
421 [update_agent_list addObject:agent];
424 [new_domain_list removeObject:domain];
427 for (id agent in deleteList) {
428 SC_log(LOG_INFO, "Destroying agent %@ because something changed!", [agent getAgentName]);
429 [self destroyFloatingAgent:agent];
432 for (id agent in update_agent_list) {
433 [self publishToAgent:agent];
436 for (idx = 0; idx < count; idx++) {
437 CFDictionaryRef domain_proxy;
438 CFStringRef match_domain;
440 domain_proxy = CFArrayGetValueAtIndex(supplemental, idx);
441 match_domain = CFDictionaryGetValue(domain_proxy, kSCPropNetProxiesSupplementalMatchDomain);
443 if (match_domain != NULL) {
448 found = [new_domain_list indexOfObject:(__bridge id _Nonnull)(match_domain)];
449 if (found == NSNotFound) {
454 * We will only process agents which are mapped AND the agent they were mapped to, changed OR
455 * agents for domains which we did not know before.
458 NSUInteger domainInstance = [duplicate_domain_list countForObject:(__bridge id _Nonnull)(match_domain)];
459 if (domainInstance > 0) {
460 /* domainInstance will be > 0, only if we have conflicting domains */
462 NSString *ns_domain_name_copy = [NSString stringWithFormat:@"%@" multipleEntitySuffix "%lu", match_domain, (unsigned long)domainInstance];
464 data = [self dataForProxyDictionary:domain_proxy];
466 BOOL ok = [self spawnFloatingAgent:[ProxyAgent class]
467 entity:ns_domain_name_copy
468 agentSubType:kAgentSubTypeSupplemental
469 addPolicyOfType:NEPolicyConditionTypeDomain
472 id agent = [self.floatingProxyAgentList objectForKey:ns_domain_name_copy];
473 SC_log(LOG_INFO, "Duplicate Proxy agent %@", [agent getAgentName]);;
476 data = [self dataForProxyDictionary:domain_proxy];
477 mapped_agent = [self getAgentWithSameDataAndSubType:self.floatingProxyAgentList
479 subType:kAgentSubTypeSupplemental];
480 if (mapped_agent != nil) {
481 [self spawnMappedFloatingAgent:mapped_agent
482 entity:(__bridge NSString *)(match_domain)
483 agentSubType:kAgentSubTypeSupplemental
484 addPolicyOfType:NEPolicyConditionTypeDomain
487 [self spawnFloatingAgent:[ProxyAgent class]
488 entity:(__bridge NSString *)(match_domain)
489 agentSubType:kAgentSubTypeSupplemental
490 addPolicyOfType:NEPolicyConditionTypeDomain
495 [new_domain_list removeObjectAtIndex:found];
496 [duplicate_domain_list addObject:(__bridge id _Nonnull)(match_domain)];
503 - (void)processScopedProxyChanges:(CFDictionaryRef)proxies
505 NSMutableArray * old_intf_list;
506 CFDictionaryRef scoped_proxies;
507 CFIndex scoped_proxies_count;
509 old_intf_list = [self getAgentList:self.floatingProxyAgentList
510 agentType:kAgentTypeProxy
511 agentSubType:kAgentSubTypeScoped];
513 scoped_proxies = CFDictionaryGetValue(proxies, kSCPropNetProxiesScoped);
514 scoped_proxies_count = scoped_proxies ? CFDictionaryGetCount(scoped_proxies) : 0;
516 if (scoped_proxies_count > 0) {
519 keys = malloc(scoped_proxies_count * sizeof(void *));
520 CFDictionaryGetKeysAndValues(scoped_proxies, keys, NULL);
522 for (int i = 0; i < scoped_proxies_count; i++) {
526 NSString * ns_if_name;
527 NSString * ns_if_name_with_prefix;
531 ns_if_name = (__bridge NSString *)keys[i];
532 ns_if_name_with_prefix = [NSString stringWithFormat:@"%s%@", prefixForInterfaceName, ns_if_name];
534 /* Does the proxy config have any protocols enabled? */
535 proxy_count = [self countProxyEntriesEnabled:CFDictionaryGetValue(scoped_proxies,
536 (__bridge const void *)(ns_if_name))];
538 if (proxy_count == 0) {
539 SC_log(LOG_INFO, "Proxy settings on %@ are generic. Skipping", ns_if_name);
543 idx = [old_intf_list indexOfObject:ns_if_name_with_prefix];
545 matching = SCNetworkProxiesCopyMatching(proxies, NULL, (__bridge CFStringRef)(ns_if_name));
546 if (matching != NULL) {
547 data = [self dataForProxyArray:matching];
551 if (idx == NSNotFound) {
552 /* We need to spawn an agent */
553 [self spawnFloatingAgent:[ProxyAgent class]
554 entity:ns_if_name_with_prefix
555 agentSubType:kAgentSubTypeScoped
556 addPolicyOfType:NEPolicyConditionTypeScopedInterface
561 /* We have an agent for this interface. Update it */
562 [old_intf_list removeObjectAtIndex:idx];
565 proxyAgent = [self.floatingProxyAgentList objectForKey:ns_if_name_with_prefix];
566 if (proxyAgent != nil) {
567 /* Do we need to update this agent? */
568 [proxyAgent updateAgentData:data];
569 if ([proxyAgent shouldUpdateAgent]) {
570 [self publishToAgent:proxyAgent];
578 [self deleteAgentList:self.floatingProxyAgentList list:old_intf_list];
581 - (void)processServiceSpecificProxyChanges:(CFDictionaryRef)proxies
583 NSMutableArray * old_service_list;
584 CFDictionaryRef service_proxies;
585 CFIndex service_proxies_count;
587 old_service_list = [self getAgentList:self.floatingProxyAgentList
588 agentType:kAgentTypeProxy
589 agentSubType:kAgentSubTypeServiceSpecific];
591 service_proxies = CFDictionaryGetValue(proxies, kSCPropNetProxiesServices);
592 service_proxies_count = service_proxies ? CFDictionaryGetCount(service_proxies) : 0;
594 if (service_proxies_count > 0) {
597 keys = malloc(service_proxies_count * sizeof(void *));
598 CFDictionaryGetKeysAndValues(service_proxies, keys, NULL);
600 for (int i = 0; i < service_proxies_count; i++) {
603 NSString * ns_service_identifier = nil;
604 NSString * ns_service_with_prefix = nil;
607 CFDictionaryRef proxyDict = NULL;
609 ns_service_identifier = (__bridge NSString *)keys[i];
610 ns_service_with_prefix = [NSString stringWithFormat:@"%s%@", prefixForInterfaceName, ns_service_identifier];
612 /* Does the proxy config have any protocols enabled? */
613 proxy_count = [self countProxyEntriesEnabled:CFDictionaryGetValue(service_proxies,
614 (__bridge const void *)(ns_service_identifier))];
616 if (proxy_count == 0) {
617 SC_log(LOG_INFO, "Proxy settings on %@ are generic. Skipping", ns_service_identifier);
621 proxyDict = CFDictionaryGetValue(service_proxies, (__bridge CFStringRef)ns_service_identifier);
622 if (proxyDict != nil) {
623 data = [self dataForProxyArray:(__bridge CFArrayRef)(@[ (__bridge NSDictionary *)proxyDict ])];
626 idx = [old_service_list indexOfObject:ns_service_with_prefix];
627 if (idx == NSNotFound) {
628 /* We need to spawn an agent */
629 [self spawnFloatingAgent:[ProxyAgent class]
630 entity:ns_service_with_prefix
631 agentSubType:kAgentSubTypeServiceSpecific
632 addPolicyOfType:(POLICY_TYPE_NO_POLICY) /* Don't install a policy */
637 /* We have an agent for this service. Update it */
638 [old_service_list removeObjectAtIndex:idx];
641 proxyAgent = [self.floatingProxyAgentList objectForKey:ns_service_with_prefix];
642 if (proxyAgent != nil) {
643 /* Do we need to update this agent? */
644 [proxyAgent updateAgentData:data];
645 if ([proxyAgent shouldUpdateAgent]) {
646 [self publishToAgent:proxyAgent];
654 [self deleteAgentList:self.floatingProxyAgentList list:old_service_list];
657 - (BOOL)isGlobalProxy:(CFDictionaryRef)proxies
659 if (CFDictionaryContainsKey(proxies, kSCPropNetProxiesBypassAllowed)) {
661 * Since we did not ask to "bypass" the proxies, this key will always
662 * be present in a managed (global) proxy configuration
670 - (BOOL)isTCPConverterProxyEnabled:(CFDictionaryRef)proxies
673 CFNumberRef num = NULL;
675 if (CFDictionaryGetValueIfPresent(proxies,
676 kSCPropNetProxiesTransportConverterEnable,
677 (const void **)&num) &&
679 CFNumberGetValue(num, kCFNumberIntType, &enabled) &&
687 #define ALLOW_AGGREGATE "net.inet.mptcp.allow_aggregate"
690 updateTransportConverterProxyEnabled(BOOL enabled)
693 int val = enabled ? 1 : 0;
695 ret = sysctlbyname(ALLOW_AGGREGATE, NULL, 0, &val, sizeof(val));
697 SC_log(LOG_NOTICE, "Transport Converter Proxy: sysctl " ALLOW_AGGREGATE "=%d", val);
699 if (errno != ENOENT) {
700 SC_log(LOG_ERR, "sysctlbyname(" ALLOW_AGGREGATE ") failed: %s", strerror(errno));
707 - (void)processDefaultProxyChanges:(CFDictionaryRef)proxies
709 CFArrayRef global_proxy;
710 CFIndex global_proxy_count;
711 CFMutableDictionaryRef proxies_copy;
713 proxies_copy = CFDictionaryCreateMutableCopy(NULL, 0, proxies);
714 CFDictionaryRemoveValue(proxies_copy, kSCPropNetProxiesScoped);
715 CFDictionaryRemoveValue(proxies_copy, kSCPropNetProxiesServices);
716 CFDictionaryRemoveValue(proxies_copy, kSCPropNetProxiesSupplemental);
718 global_proxy = CFArrayCreate(NULL, (const void **)&proxies_copy, 1, &kCFTypeArrayCallBacks);
719 global_proxy_count = CFArrayGetCount(global_proxy);
720 if (global_proxy_count > 0 &&
721 [self countProxyEntriesEnabled:proxies_copy] == 0) {
722 SC_log(LOG_INFO, "Proxy settings on defaultProxy are generic. Skipping");
723 global_proxy_count = 0;
725 CFRelease(proxies_copy);
727 if (global_proxy_count > 0) {
728 BOOL spawnAgent = YES;
732 data = [self dataForProxyArray:global_proxy];
733 proxyAgent = [self.floatingProxyAgentList objectForKey:@proxyAgentDefault];
734 if (proxyAgent != nil) {
735 if (![data isEqual:[proxyAgent getAgentData]]) {
736 [self destroyFloatingAgent:proxyAgent];
745 AgentSubType subtype = kAgentSubTypeDefault;
746 NEPolicyConditionType conditionType = NEPolicyConditionTypeNone;
747 if ([self isGlobalProxy:proxies_copy]) {
748 SC_log(LOG_INFO, "Global proxy detected...");
749 conditionType = NEPolicyConditionTypeAllInterfaces;
750 subtype = kAgentSubTypeGlobal;
753 ok = [self spawnFloatingAgent:[ProxyAgent class]
754 entity:@proxyAgentDefault
756 addPolicyOfType:conditionType
759 (subtype == kAgentSubTypeGlobal) &&
760 [self isTCPConverterProxyEnabled:proxies_copy]) {
761 proxyAgent = [self.floatingProxyAgentList objectForKey:@proxyAgentDefault];
762 if ((proxyAgent != nil) &&
763 [data isEqual:[proxyAgent getAgentData]]) {
764 [self.floatingProxyAgentList_TCPConverter setObject:proxyAgent
765 forKey:@proxyAgentDefault];
766 updateTransportConverterProxyEnabled(TRUE); // enable "net.inet.mptcp.allow_aggregate"
771 /* No default proxy config OR generic (no protocols enabled) default proxy config.
772 * Destroy the default agent if we had one
776 proxyAgent = [self.floatingProxyAgentList objectForKey:@proxyAgentDefault];
777 if (proxyAgent != nil) {
778 [self destroyFloatingAgent:proxyAgent];
782 CFRelease(global_proxy);
785 - (void)applyPolicies
787 if (self.controlPolicySession != nil && ![self.controlPolicySession apply]) {
788 SC_log(LOG_ERR, "Failed to apply control policies");
790 if (self.policySession != nil && ![self.policySession apply]) {
791 SC_log(LOG_ERR, "Failed to apply policies");
795 - (void)processProxyChanges
797 CFDictionaryRef proxies;
799 proxies = SCDynamicStoreCopyProxiesWithOptions(NULL, NULL);
800 if (proxies == NULL) {
801 SC_log(LOG_INFO, "No proxy information");
803 BOOL destroyedAgent = NO;
804 NSMutableDictionary *copy = [self.floatingProxyAgentList copy];
805 for (NSString *entity in copy) {
806 id agent = [copy objectForKey:entity];
807 [self destroyFloatingAgent:agent];
808 destroyedAgent = YES;
811 if (destroyedAgent) {
812 [self applyPolicies];
817 [self processDefaultProxyChanges:proxies];
818 [self processScopedProxyChanges:proxies];
819 [self processSupplementalProxyChanges:proxies];
820 [self processServiceSpecificProxyChanges:proxies];
822 [self applyPolicies];
827 /* ========================== DNS agent helpers =========================== */
828 #pragma mark DNS agent helper functions
830 - (void)freeResolverList:(resolver_list_t *)resolvers
832 /* This is a shallow free of resolver_list_t only.
833 * The actual resolver pointers are owned by 'dns_config'
835 if (resolvers == NULL) {
839 if (resolvers->default_resolvers != NULL) {
840 free(resolvers->default_resolvers);
842 if (resolvers->multicast_resolvers != NULL) {
843 free(resolvers->multicast_resolvers);
845 if (resolvers->private_resolvers != NULL) {
846 free(resolvers->private_resolvers);
852 - (resolver_list_t *)copyResolverList:(dns_config_t *)dns_config
854 resolver_list_t *resolvers = NULL;
856 if ((dns_config->n_resolver > 0) && (dns_config->resolver != NULL)) {
861 resolvers = calloc(1, sizeof(resolver_list_t));
862 for (int i = 0; i < dns_config->n_resolver; i++) {
863 dns_resolver_t *r = dns_config->resolver[i];
865 if ([self isResolverMulticast:r]) {
866 resolvers->n_multicast_resolvers++;
869 } else if ([self isResolverPrivate:r]) {
870 resolvers->n_private_resolvers++;
874 // do not consider default resolvers with no nameservers
875 if (r->domain == NULL && r->n_nameserver > 0) {
876 resolvers->n_default_resolvers++;
880 SC_log(LOG_INFO, "Resolvers: %d default, %d multicast, %d private",
881 resolvers->n_default_resolvers,
882 resolvers->n_multicast_resolvers,
883 resolvers->n_private_resolvers);
885 if (resolvers->n_default_resolvers > 0) {
886 resolvers->default_resolvers = calloc(resolvers->n_default_resolvers,
887 sizeof(dns_resolver_t *));
889 if (resolvers->n_multicast_resolvers > 0) {
890 resolvers->multicast_resolvers = calloc(resolvers->n_multicast_resolvers,
891 sizeof(dns_resolver_t *));
893 if (resolvers->n_private_resolvers > 0) {
894 resolvers->private_resolvers = calloc(resolvers->n_private_resolvers,
895 sizeof(dns_resolver_t *));
898 for (int i = 0; i < dns_config->n_resolver; i++) {
899 dns_resolver_t *r = dns_config->resolver[i];
901 if ([self isResolverMulticast:r] &&
902 (a < resolvers->n_multicast_resolvers)) {
903 resolvers->multicast_resolvers[a++] = r;
906 } else if ([self isResolverPrivate:r] &&
907 (b < resolvers->n_private_resolvers)) {
908 resolvers->private_resolvers[b++] = r;
912 if ((r->domain == NULL) &&
913 (r->n_nameserver > 0) &&
914 (c < resolvers->n_default_resolvers)) {
915 resolvers->default_resolvers[c++] = r;
924 * Generate a data blob for the resolver.
925 * Currently the blob only has:
927 * - sockaddr structs for each nameserver
931 - (NSData *)dataForResolver:(dns_resolver_t *)resolver
934 CFMutableDictionaryRef resolverDict = nil;
936 if (resolver == NULL) {
937 SC_log(LOG_NOTICE, "Invalid dns resolver");
941 if (resolver->n_search > 0) {
942 if (resolverDict == nil) {
943 resolverDict = CFDictionaryCreateMutable(NULL,
945 &kCFTypeDictionaryKeyCallBacks,
946 &kCFTypeDictionaryValueCallBacks);
949 CFMutableArrayRef searchDomainArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
951 /* Append search domains */
952 for (int i = 0; i < resolver->n_search; i++) {
953 CFArrayAppendValue(searchDomainArray, (__bridge CFStringRef)(@(resolver->search[i])));
956 CFDictionaryAddValue(resolverDict, CFSTR(kConfigAgentDNSSearchDomains), searchDomainArray);
957 CFRelease(searchDomainArray);
960 /* Get the count of nameservers */
961 if (resolver->n_nameserver > 0) {
962 if (resolverDict == nil) {
963 resolverDict = CFDictionaryCreateMutable(NULL,
965 &kCFTypeDictionaryKeyCallBacks,
966 &kCFTypeDictionaryValueCallBacks);
969 CFMutableArrayRef nameserverArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
971 /* Get all the nameservers */
972 for (int i = 0; i < resolver->n_nameserver; i++) {
974 _SC_sockaddr_to_string(resolver->nameserver[i], buf, sizeof(buf));
976 CFArrayAppendValue(nameserverArray, (__bridge CFStringRef)(@(buf)));
980 CFDictionaryAddValue(resolverDict, CFSTR(kConfigAgentDNSNameServers), nameserverArray);
981 CFRelease(nameserverArray);
984 if (resolverDict != nil) {
985 data = [NSPropertyListSerialization dataWithPropertyList:(__bridge id _Nonnull)(resolverDict)
986 format:NSPropertyListBinaryFormat_v1_0
990 CFRelease(resolverDict);
993 return (NSData *)data;
996 - (NSData *)getDNSDataFromCurrentConfig:(dns_config_t *)dns_config
997 domain:(NSString *)domain
999 if (dns_config == NULL || domain == nil) {
1000 SC_log(LOG_NOTICE, "Invalid dns_config/domain");
1004 if ((dns_config->n_resolver > 0) && (dns_config->resolver != NULL)) {
1005 for (int i = 0; i < dns_config->n_resolver; i++) {
1006 dns_resolver_t * resolver;
1008 resolver = dns_config->resolver[i];
1009 if (resolver->domain != NULL &&
1010 ![self isResolverMulticast:resolver]) {
1011 NSString * ns_domain_name;
1013 ns_domain_name = @(resolver->domain);
1014 if ([ns_domain_name isEqualToString:domain]) {
1015 return [self dataForResolver:resolver];
1026 - (BOOL)isResolverMulticast:(dns_resolver_t *)resolver
1028 if (resolver->options == NULL) {
1032 if (!strstr(resolver->options, "mdns")) {
1039 - (BOOL)isResolverPrivate:(dns_resolver_t *)resolver
1041 if (resolver->options == NULL) {
1045 if (!strstr(resolver->options, "pdns")) {
1052 - (void)processSupplementalDNSResolvers:(dns_config_t *)dns_config
1054 NSMutableArray * deleteList;
1055 NSMutableArray * new_domain_list;
1056 NSCountedSet * duplicate_domain_list;
1057 NSMutableArray * old_domain_list;
1058 NSMutableArray * update_agent_list;
1061 deleteList = [NSMutableArray array];
1062 duplicate_domain_list = [[NSCountedSet alloc] initWithCapacity:0];
1063 new_domain_list = [NSMutableArray array];
1064 update_agent_list = [NSMutableArray array];
1065 old_domain_list = [self getAgentList:self.floatingDNSAgentList
1066 agentType:kAgentTypeDNS
1067 agentSubType:kAgentSubTypeSupplemental];
1069 if (dns_config->resolver == NULL) {
1070 dns_config->n_resolver = 0;
1072 if (dns_config->n_resolver > 0) {
1073 for (int i = 0; i < dns_config->n_resolver; i++) {
1074 dns_resolver_t * resolver;
1076 resolver = dns_config->resolver[i];
1077 if (resolver->domain != NULL &&
1078 ![self isResolverPrivate:resolver] &&
1079 ![self isResolverMulticast:resolver]) {
1080 NSString * ns_domain_name;
1082 ns_domain_name = [NSString stringWithCString:resolver->domain encoding:NSASCIIStringEncoding];
1083 [new_domain_list addObject:ns_domain_name];
1088 [self cleanConflictingAgentsFromList:old_domain_list
1089 new_list:new_domain_list
1090 agentDictionary:self.floatingDNSAgentList];
1092 /* Sync between controller and current config */
1093 for (NSString *key in old_domain_list) {
1094 BOOL domain_present = NO;
1096 domain_present = [new_domain_list containsObject:key];
1097 if (domain_present == NO) {
1100 agent = [self.floatingDNSAgentList objectForKey:key];
1101 [self destroyFloatingAgent:agent];
1105 /* At this point, whatever is in the controller's floating agent list,
1106 is present in the current DNS config. The current DNS config
1107 might have even more configs, not known to the controller, YET
1110 for (NSString *domain in old_domain_list) {
1114 agent = [self.floatingDNSAgentList objectForKey:domain];
1119 /* Am I mapped to some agent? */
1120 mapped_agent = [agent getAgentMapping];
1122 /* OK, this agent is mapped to some other agent. We compare this agent's data
1123 * to the current data of the agent to which it is mapped. If different, we destroy
1124 * the agent and later map it to someone else OR spawn a new one.
1126 NSData *mapped_agent_data;
1128 mapped_agent_data = [self getDNSDataFromCurrentConfig:dns_config domain:[mapped_agent getAssociatedEntity]];
1129 if (mapped_agent_data == nil || ![[agent getAgentData] isEqual:mapped_agent_data]) {
1130 /* Something changed for mapped agent */
1131 [deleteList addObject:agent];
1135 /* Since this agent is NOT mapped to any other agent, this agent is
1136 * registered with the kernel. So instead of destroying the agent and
1137 * re-registering it, just update it here.
1139 * All the agents which were mapped to this agent, will be deleted and
1140 * re-mapped, if the data changed.
1144 agent_data = [self getDNSDataFromCurrentConfig:dns_config domain:[agent getAssociatedEntity]];
1145 if (![[agent getAgentData] isEqual:agent_data]) {
1146 /* Something changed for agent */
1147 [agent updateAgentData:agent_data];
1149 /* The reason I don't publish the data to agent here is that, if there were
1150 * some agents mapping to this one, they will momentarily have a policy for
1151 * using this agent UUID for some domain based on this agent's previous data.
1153 [update_agent_list addObject:agent];
1157 [new_domain_list removeObject:domain];
1160 for (id agent in deleteList) {
1161 SC_log(LOG_INFO, "Destroying agent %@ because something changed!", [agent getAgentName]);
1162 [self destroyFloatingAgent:agent];
1165 for (id agent in update_agent_list) {
1166 [self publishToAgent:agent];
1169 for (int idx = 0; idx < dns_config->n_resolver; idx++) {
1170 dns_resolver_t * resolver;
1172 resolver = dns_config->resolver[idx];
1173 if (resolver->domain != NULL &&
1174 ![self isResolverPrivate:resolver] &&
1175 ![self isResolverMulticast:resolver]) {
1179 NSString * ns_domain_name;
1181 ns_domain_name = @(resolver->domain);
1182 found = [new_domain_list indexOfObject:ns_domain_name];
1183 if (found == NSNotFound) {
1184 /* Nothing changed for this agent */
1188 /* We will only process agents which are mapped AND if the agent they were mapped to, changed OR
1189 * agents for domains which we did not know before.
1192 NSUInteger domainInstance = [duplicate_domain_list countForObject:ns_domain_name];
1193 if (domainInstance > 0) {
1194 /* domainInstance will be > 0, only if we have conflicting domains */
1196 data = [self dataForResolver:resolver];
1198 NSString *ns_domain_name_copy = [NSString stringWithFormat:@"%@" multipleEntitySuffix "%lu", ns_domain_name, (unsigned long)domainInstance];
1200 BOOL ok = [self spawnFloatingAgent:[DNSAgent class]
1201 entity:ns_domain_name_copy
1202 agentSubType:kAgentSubTypeSupplemental
1203 addPolicyOfType:NEPolicyConditionTypeDomain
1206 id agent = [self.floatingDNSAgentList objectForKey:ns_domain_name_copy];
1207 SC_log(LOG_INFO, "Duplicate DNS agent %@", [agent getAgentName]);;
1210 data = [self dataForResolver:resolver];
1211 mapped_agent = [self getAgentWithSameDataAndSubType:self.floatingDNSAgentList
1213 subType:kAgentSubTypeSupplemental];
1214 if (mapped_agent != nil) {
1215 [self spawnMappedFloatingAgent:mapped_agent
1216 entity:ns_domain_name
1217 agentSubType:kAgentSubTypeSupplemental
1218 addPolicyOfType:NEPolicyConditionTypeDomain
1221 [self spawnFloatingAgent:[DNSAgent class]
1222 entity:ns_domain_name
1223 agentSubType:kAgentSubTypeSupplemental
1224 addPolicyOfType:NEPolicyConditionTypeDomain
1229 [new_domain_list removeObjectAtIndex:found];
1230 [duplicate_domain_list addObject:ns_domain_name];
1238 - (void)processDNSResolvers:(dns_config_t *)dns_config
1240 resolver_list_t *resolvers = [self copyResolverList:dns_config];
1242 /* Process Default resolvers */
1243 NSMutableArray *old_default_resolver_list = [self getAgentList:self.floatingDNSAgentList
1244 agentType:kAgentTypeDNS
1245 agentSubType:kAgentSubTypeDefault];
1247 // For default resolvers, their name will be '_defaultDNS', '_defaultDNS #2' so on...
1248 if (resolvers->n_default_resolvers > 0 && resolvers->default_resolvers != NULL) {
1249 for (uint32_t i = 0; i < resolvers->n_default_resolvers; i++) {
1250 dns_resolver_t *default_resolver = resolvers->default_resolvers[i];
1253 NSString * resolverName;
1255 data = [self dataForResolver:default_resolver];
1257 resolverName = @(dnsAgentDefault);
1259 resolverName = [NSString stringWithFormat:@dnsAgentDefault multipleEntitySuffix "%d", i+1 ];
1262 dnsAgent = [self.floatingDNSAgentList objectForKey:resolverName];
1264 if (dnsAgent != nil) {
1265 [old_default_resolver_list removeObject:resolverName];
1266 if ([data isEqual:[dnsAgent getAgentData]]) {
1267 /* Leave this agent in place. Nothing changed! */
1270 [self destroyFloatingAgent:dnsAgent];
1274 [self spawnFloatingAgent:[DNSAgent class]
1276 agentSubType:kAgentSubTypeDefault
1277 addPolicyOfType:NEPolicyConditionTypeNone
1282 // Only agents that are NOT present in the new config, will be present in the list
1283 // and they need to be destroyed.
1284 [self deleteAgentList:self.floatingDNSAgentList list:old_default_resolver_list];
1286 /* Process Multicast resolvers */
1288 NSMutableArray *old_multicast_resolver_list = [self getAgentList:self.floatingDNSAgentList
1289 agentType:kAgentTypeDNS
1290 agentSubType:kAgentSubTypeMulticast];
1292 if (resolvers->n_multicast_resolvers > 0 && resolvers->multicast_resolvers != NULL) {
1293 for (uint32_t i = 0; i < resolvers->n_multicast_resolvers; i++) {
1294 dns_resolver_t * multicast_resolver = resolvers->multicast_resolvers[i];
1296 NSString * resolverName;
1298 if (multicast_resolver == NULL) {
1302 if (multicast_resolver->domain == NULL) {
1303 /* Multicast resolvers MUST have a domain */
1307 resolverName = @(multicast_resolver->domain);
1308 if (resolverName == NULL) {
1309 /* Multicast resolvers MUST have a domain */
1313 dnsAgent = [self.floatingDNSAgentList objectForKey:resolverName];
1314 if (dnsAgent != nil) {
1315 [old_multicast_resolver_list removeObject:resolverName];
1319 [self spawnFloatingAgent:[DNSAgent class]
1321 agentSubType:kAgentSubTypeMulticast
1322 addPolicyOfType:NEPolicyConditionTypeDomain
1324 // Don't care about data for mdns resolvers. Do we?
1328 [self deleteAgentList:self.floatingDNSAgentList list:old_multicast_resolver_list];
1330 /* Process Private resolvers */
1332 NSMutableArray *old_private_resolver_list = [self getAgentList:self.floatingDNSAgentList
1333 agentType:kAgentTypeDNS
1334 agentSubType:kAgentSubTypePrivate];
1336 if (resolvers->n_private_resolvers > 0 && resolvers->private_resolvers != NULL) {
1337 for (uint32_t i = 0; i < resolvers->n_private_resolvers; i++) {
1338 dns_resolver_t * private_resolver = resolvers->private_resolvers[i];
1340 NSString * resolverName;
1342 if (private_resolver == NULL) {
1346 if (private_resolver->domain == NULL) {
1347 /* private resolvers MUST have a domain */
1351 resolverName = @(private_resolver->domain);
1352 if (resolverName == nil) {
1353 /* Private resolvers MUST have a domain */
1357 dnsAgent = [self.floatingDNSAgentList objectForKey:resolverName];
1358 if (dnsAgent != nil) {
1359 [old_private_resolver_list removeObject:resolverName];
1363 [self spawnFloatingAgent:[DNSAgent class]
1365 agentSubType:kAgentSubTypePrivate
1366 addPolicyOfType:NEPolicyConditionTypeDomain
1368 // Don't care about data for pdns resolvers. Do we?
1372 [self deleteAgentList:self.floatingDNSAgentList list:old_private_resolver_list];
1375 [self freeResolverList:resolvers];
1378 - (void)processScopedDNSResolvers:(dns_config_t *)dns_config
1380 NSMutableArray * old_intf_list;
1381 old_intf_list = [self getAgentList:self.floatingDNSAgentList
1382 agentType:kAgentTypeDNS
1383 agentSubType:kAgentSubTypeScoped];
1385 if ((dns_config->n_scoped_resolver > 0) && (dns_config->scoped_resolver != NULL)) {
1386 for (int i = 0; i < dns_config->n_scoped_resolver; i++) {
1391 const char * if_name;
1392 NSString * ns_if_name;
1393 NSString * ns_if_name_with_prefix;
1394 dns_resolver_t * resolver;
1396 resolver = dns_config->scoped_resolver[i];
1397 if_name = my_if_indextoname(resolver->if_index, buf);
1399 ns_if_name = @(if_name);
1400 ns_if_name_with_prefix = [NSString stringWithFormat:@"%s%@", prefixForInterfaceName, ns_if_name];
1405 data = [self dataForResolver:resolver];
1406 idx = [old_intf_list indexOfObject:ns_if_name_with_prefix];
1408 if (idx == NSNotFound) {
1409 /* We need to spawn an agent */
1410 [self spawnFloatingAgent:[DNSAgent class]
1411 entity:ns_if_name_with_prefix
1412 agentSubType:kAgentSubTypeScoped
1413 addPolicyOfType:NEPolicyConditionTypeScopedInterface
1417 /* We have an agent on this interface. Update it */
1418 [old_intf_list removeObjectAtIndex:idx];
1421 /* Get the DNS agent for this interface? */
1422 dnsAgent = [self.floatingDNSAgentList objectForKey:ns_if_name_with_prefix];
1423 if (dnsAgent != nil) {
1424 /* Do we need to update this agent? */
1425 [dnsAgent updateAgentData:data];
1426 if ([dnsAgent shouldUpdateAgent]) {
1427 [self publishToAgent:dnsAgent];
1433 [self deleteAgentList:self.floatingDNSAgentList list:old_intf_list];
1436 - (void)processServiceSpecificDNSResolvers:(dns_config_t *)dns_config
1438 NSMutableArray * old_service_list;
1439 old_service_list = [self getAgentList:self.floatingDNSAgentList
1440 agentType:kAgentTypeDNS
1441 agentSubType:kAgentSubTypeServiceSpecific];
1443 if ((dns_config->n_service_specific_resolver > 0) && (dns_config->service_specific_resolver != NULL)) {
1444 for (int i = 0; i < dns_config->n_service_specific_resolver; i++) {
1448 uint32_t service_identifier;
1449 NSString * ns_service_identifier_with_prefix;
1450 dns_resolver_t * resolver;
1452 resolver = dns_config->service_specific_resolver[i];
1453 service_identifier = resolver->service_identifier;
1454 if (service_identifier != 0) {
1455 ns_service_identifier_with_prefix = [NSString stringWithFormat:@"%s%u", prefixForInterfaceName, service_identifier];
1460 data = [self dataForResolver:resolver];
1461 idx = [old_service_list indexOfObject:ns_service_identifier_with_prefix];
1463 if (idx == NSNotFound) {
1464 /* We need to spawn an agent */
1465 [self spawnFloatingAgent:[DNSAgent class]
1466 entity:ns_service_identifier_with_prefix
1467 agentSubType:kAgentSubTypeServiceSpecific
1468 addPolicyOfType:(POLICY_TYPE_NO_POLICY) /* Don't install a policy */
1472 /* We have an agent on this interface. Update it */
1473 [old_service_list removeObjectAtIndex:idx];
1476 /* Get the DNS agent for this interface? */
1477 dnsAgent = [self.floatingDNSAgentList objectForKey:ns_service_identifier_with_prefix];
1478 if (dnsAgent != nil) {
1479 /* Do we need to update this agent? */
1480 [dnsAgent updateAgentData:data];
1481 if ([dnsAgent shouldUpdateAgent]) {
1482 [self publishToAgent:dnsAgent];
1488 [self deleteAgentList:self.floatingDNSAgentList list:old_service_list];
1491 #define ONION_RESOLVER_DOMAIN "onion"
1492 - (BOOL)isResolverOnion:(dns_resolver_t *)resolver
1494 if (resolver->domain != NULL &&
1495 (strcmp(resolver->domain, ONION_RESOLVER_DOMAIN) == 0)) {
1503 - (void)processOnionResolver:(dns_config_t *)dns_config
1505 static NSUInteger policy_id = 0;
1507 if (dns_config == NULL) {
1511 /* Run through the resolver configurations. We only care for the supplemental resolvers. */
1512 for (int32_t i = 0; i < dns_config->n_resolver; i++) {
1513 dns_resolver_t *resolver = dns_config->resolver[i];
1514 if ([self isResolverOnion:resolver]) {
1519 /* We do not have any such resolver. Add a system-wide "drop" policy for this domain */
1520 if (policy_id == 0) {
1521 NEPolicy *policy = [[NEPolicy alloc] initWithOrder:INIT_ORDER_FOR_DOMAIN_POLICY
1522 result:[NEPolicyResult drop]
1523 conditions:@[[NEPolicyCondition domain:@ONION_RESOLVER_DOMAIN]]];
1524 if (policy != nil) {
1525 policy_id = [self.policySession addPolicy:policy];
1526 if (![self.policySession apply]) {
1528 SC_log(LOG_NOTICE, "Could not add a [." ONION_RESOLVER_DOMAIN "] drop policy");
1530 SC_log(LOG_INFO, "Added a [." ONION_RESOLVER_DOMAIN "] drop policy");
1539 /* We have such a resolver in the config OR no DNS config at all. Remove the system-wide "drop" policy for this domain */
1540 if (policy_id > 0) {
1541 [self.policySession removePolicyWithID:policy_id];
1542 if (![self.policySession apply]) {
1543 SC_log(LOG_NOTICE, "Could not remove the [." ONION_RESOLVER_DOMAIN "] drop policy");
1546 SC_log(LOG_INFO, "Removed the [." ONION_RESOLVER_DOMAIN "] drop policy");
1552 #undef ONION_RESOLVER_DOMAIN
1555 - (void)processDNSChanges
1557 dns_config_t * dns_config;
1559 dns_config = dns_configuration_copy();
1560 if (dns_config == NULL) {
1561 SC_log(LOG_INFO, "No DNS configuration");
1562 NSMutableDictionary *copy = [self.floatingDNSAgentList copy];
1563 for (NSString *entity in copy) {
1564 id agent = [copy objectForKey:entity];
1566 [self destroyFloatingAgent:agent];
1571 [self processDNSResolvers:dns_config];
1572 [self processScopedDNSResolvers:dns_config];
1573 [self processSupplementalDNSResolvers:dns_config];
1574 [self processServiceSpecificDNSResolvers:dns_config];
1578 [self processOnionResolver:dns_config];
1579 [self applyPolicies];
1581 if (dns_config != NULL) {
1582 dns_configuration_free(dns_config);
1586 #pragma mark Helper functions
1588 - (const void *)copyConfigAgentData:(NSMutableDictionary *)controllerDict
1589 uuid:(uuid_t)requested_uuid
1590 length:(uint64_t *)length
1592 if (length == NULL) {
1593 SC_log(LOG_NOTICE, "Invalid parameters for copying agent data");
1598 void *buffer = NULL;
1601 for (NSString *key in controllerDict) {
1602 id temp_agent = [controllerDict objectForKey:key];
1606 [[temp_agent getAgentUUID] getUUIDBytes:agent_uuid];
1607 if (uuid_compare(agent_uuid, requested_uuid) == 0) {
1614 uuid_string_t uuid_str;
1615 uuid_unparse(requested_uuid, uuid_str);
1616 SC_log(LOG_NOTICE, "Invalid config agent uuid %s specified", uuid_str);
1620 NSData *data = [agent getAgentData];
1621 uint64_t len = [data length];
1624 buffer = malloc((size_t)len);
1625 memcpy(buffer, [data bytes], len);
1628 return (const void *)buffer;
1631 - (const void *)copyProxyAgentData:(uuid_t)requested_uuid
1632 length:(uint64_t *)length
1634 return [self copyConfigAgentData:self.floatingProxyAgentList
1639 - (const void *)copyDNSAgentData:(uuid_t)requested_uuid
1640 length:(uint64_t *)length
1642 return [self copyConfigAgentData:self.floatingDNSAgentList
1647 - (NSData *)dataLengthSanityCheck:(id)agent
1649 NSData * data = [agent getAgentData];
1651 if ([data length] > CONFIG_AGENT_DATA_LIMIT) {
1652 /* We impose a limit on the config agent data as 1KB.
1653 * If we have a data blob larger than this limit, do NOT publish it into the agent.
1654 * Instead publish a key which will trigger fetching of the configuration directly
1655 * through NWI server.
1657 NSMutableDictionary *data_dict = [NSMutableDictionary dictionary];
1659 NSUUID *uuid = [agent getAgentUUID];
1661 [uuid getUUIDBytes:c_uuid];
1662 NSData *uuid_data = [[NSData alloc] initWithBytes:c_uuid length:sizeof(c_uuid)];
1663 [data_dict setValue:uuid_data forKey:@kConfigAgentOutOfBandDataUUID];
1665 NSData *new_data = [NSPropertyListSerialization dataWithPropertyList:data_dict
1666 format:NSPropertyListBinaryFormat_v1_0
1677 * For conflicting agents, the convention is that its name & entity,
1678 * will have a suffix " #<number>". This function will sanitize the
1679 * suffix and just return the entity name
1681 - (NSString *)sanitizeEntity:(NSString *)entity
1683 NSRange range = [entity rangeOfString:@multipleEntitySuffix];
1684 if (range.location != NSNotFound) {
1685 NSString *str = [entity substringToIndex:range.location];
1693 * For interface names, there is a prefix to differentiate then
1694 * from the domain name (iff there were conflicting domain names).
1695 * Returns the sanitized interface name
1697 - (NSString *)sanitizeInterfaceName:(NSString *)intf
1699 NSRange range = [intf rangeOfString:@prefixForInterfaceName];
1700 if (range.location != NSNotFound) {
1701 NSString *str = [intf substringFromIndex:(range.location + strlen(prefixForInterfaceName))];
1709 * For conflicting agents, the convention is that its name & entity,
1710 * will have a suffix " #<number>". This function will return that <number>
1712 - (int)entityInstanceNumber:(NSString *)entity
1714 NSRange range = [entity rangeOfString:@multipleEntitySuffix];
1715 if (range.location != NSNotFound) {
1716 NSString *str = [entity substringFromIndex:(range.location + strlen(multipleEntitySuffix))];
1717 return str.intValue;
1724 * In case that we have conflicting DNS/Proxy domains
1725 * This function will remove all those conflicting agents,
1726 * so that we can start afresh with the new config
1728 - (void)cleanConflictingAgentsFromList:(NSMutableArray *)old_list
1729 new_list:(NSMutableArray *)new_list
1730 agentDictionary:(NSMutableDictionary *)agent_list
1732 NSCountedSet * duplicate_domain_list;
1734 for (NSString *domain in old_list) {
1735 /* If we had conflicting domains before, remove all of them */
1736 NSString *sanitizedDomain = [self sanitizeEntity:domain];
1737 if (![sanitizedDomain isEqualToString:domain]) {
1738 /* Destroy the original domain */
1739 id agent = [agent_list objectForKey:sanitizedDomain];
1740 [self destroyFloatingAgent:agent];
1742 /* Destroy the conflicting domain */
1743 agent = [agent_list objectForKey:domain];
1744 [self destroyFloatingAgent:agent];
1746 SC_log(LOG_INFO, "Removing conflicting domain: %@, %@", sanitizedDomain, domain);
1750 duplicate_domain_list = [[NSCountedSet alloc] initWithArray:new_list];
1751 for (NSString *domain in old_list) {
1752 if ([duplicate_domain_list countForObject:domain] > 1) {
1753 id agent = [agent_list objectForKey:domain];
1754 [self destroyFloatingAgent:agent];
1755 SC_log(LOG_INFO, "Removing domain %@ as it has duplicates in the current config", domain);
1761 * Get the list of agents from a specific dictionary.
1762 * The list of agents will only consist of the ones which
1763 * match the agent type and sub-type
1766 - (NSMutableArray *)getAgentList:(NSMutableDictionary *)all_agents
1767 agentType:(AgentType)type
1768 agentSubType:(AgentSubType)subtype
1770 NSMutableArray *list = [NSMutableArray array];
1771 NSArray *agentObjects = [all_agents allValues];
1773 for (id agent in agentObjects) {
1774 if (([agent getAgentType] == type) &&
1775 ([agent getAgentSubType] == subtype)) {
1777 [list addObject:[agent getAssociatedEntity]];
1785 * Destroy all the agents are listed in "list"
1788 - (void)deleteAgentList:(NSMutableDictionary *)all_agents
1789 list:(NSMutableArray *)list
1791 for (NSString *intf in list) {
1794 agent = [all_agents objectForKey:intf];
1795 [self destroyFloatingAgent:agent];
1800 * In order to not duplicate agents with same content,
1801 * we map an agent X to agent Y, when their content is the same.
1803 * This function tries to find that agent Y
1806 - (id)getAgentWithSameDataAndSubType:(NSMutableDictionary *)agentList
1808 subType:(AgentSubType)subtype
1810 for (NSString *key in agentList) {
1811 id agent = [agentList objectForKey:key];
1812 if ([[agent getAgentData] isEqual:data]) {
1813 /* Do not map to default agents */
1814 if ([agent getAgentSubType] != subtype) {
1818 /* Return only registered agents */
1819 if ([agent getRegistrationObject] != nil) {
1828 #pragma mark Policy installation function
1831 * Add NECP policies for an agent
1833 - (BOOL)addPolicyToFloatingAgent:(id)agent
1834 domain:(NSString *)domain
1835 agentUUIDToUse:(NSUUID *)uuid
1836 policyType:(NEPolicyConditionType)policyType
1837 useControlPolicySession:(BOOL)useControlPolicySession
1839 NEPolicyCondition * condition = nil;
1840 NEPolicySession * session;
1841 uint32_t multiple_entity_offset;
1842 NEPolicy * newPolicy;
1844 uint32_t orderForSkip;
1845 NSMutableArray * policyArray;
1846 NSUInteger policyID1;
1847 NSUInteger policyID2;
1848 NEPolicyResult * result;
1851 uint32_t typeOffset;
1853 type = [agent getAgentType];
1854 typeOffset = (type == kAgentTypeDNS) ? 0 : 5000;
1855 skipOrder = (type == kAgentTypeDNS) ? 5000 : 0;
1857 multiple_entity_offset = (uint32_t)[self entityInstanceNumber:domain];
1858 domain = [self sanitizeEntity:domain];
1860 switch (policyType) {
1861 case NEPolicyConditionTypeScopedInterface:
1862 order = INIT_ORDER_FOR_SCOPED_INTERFACE_POLICY + typeOffset + multiple_entity_offset;
1863 domain = [self sanitizeInterfaceName:domain];
1864 condition = [NEPolicyCondition scopedInterface:domain];
1865 orderForSkip = SKIP_ORDER_FOR_SCOPED_INTERFACE_POLICY + typeOffset;
1868 case NEPolicyConditionTypeDomain:
1869 order = INIT_ORDER_FOR_DOMAIN_POLICY + typeOffset + multiple_entity_offset;
1870 condition = [NEPolicyCondition domain:domain];
1871 orderForSkip = SKIP_ORDER_FOR_DOMAIN_POLICY + typeOffset;
1874 case NEPolicyConditionTypeAllInterfaces:
1875 order = INIT_ORDER_FOR_DEFAULT_POLICY + typeOffset + multiple_entity_offset;
1876 condition = [NEPolicyCondition allInterfaces];
1877 orderForSkip = SKIP_ORDER_FOR_DEFAULT_POLICY + typeOffset;
1880 case NEPolicyConditionTypeNone:
1881 order = INIT_ORDER_FOR_DEFAULT_POLICY + typeOffset + multiple_entity_offset;
1882 orderForSkip = SKIP_ORDER_FOR_DEFAULT_POLICY + typeOffset;
1886 SC_log(LOG_NOTICE, "Invalid policy condition specified");
1890 result = [NEPolicyResult netAgentUUID:uuid];
1891 newPolicy = [[NEPolicy alloc] initWithOrder:order
1893 conditions: (condition ? @[condition] : nil)];
1895 if (newPolicy == nil) {
1896 SC_log(LOG_NOTICE, "Could not create a policy for agent %@", [agent getAgentName]);
1900 if (useControlPolicySession) {
1901 if (self.controlPolicySession == nil) {
1902 /* The NE policy session at "control" level for the controller */
1903 self.controlPolicySession = [self createPolicySession];
1904 if (self.controlPolicySession == nil) {
1905 SC_log(LOG_NOTICE, "Could not create a control policy session for agent %@", [agent getAgentName]);
1908 [self.controlPolicySession setPriority:NEPolicySessionPriorityControl];
1910 ((ConfigAgent *)agent).preferredPolicySession = self.controlPolicySession;
1912 ((ConfigAgent *)agent).preferredPolicySession = self.policySession;
1915 session = ((ConfigAgent *)agent).preferredPolicySession;
1917 policyID1 = [session addPolicy:newPolicy];
1918 if (policyID1 == 0) {
1919 SC_log(LOG_NOTICE, "Could not add a netagent policy for agent %@", [agent getAgentName]);
1923 result = [NEPolicyResult skipWithOrder:skipOrder];
1924 newPolicy = [[NEPolicy alloc] initWithOrder:orderForSkip
1926 conditions:(condition ? @[condition] : nil)];
1928 if (newPolicy == nil) {
1929 SC_log(LOG_NOTICE, "Could not create a policy for agent %@", [agent getAgentName]);
1933 policyID2 = [session addPolicy:newPolicy];
1934 if (policyID2 == 0) {
1935 SC_log(LOG_NOTICE, "Could not add a skip policy for agent %@", [agent getAgentName]);
1939 policyArray = [self.policyDB objectForKey:[agent getAgentName]];
1940 if (policyArray == nil) {
1941 policyArray = [NSMutableArray array];
1944 [policyArray addObject:numberToNSNumber(policyID1)];
1945 [policyArray addObject:numberToNSNumber(policyID2)];
1946 [self.policyDB setObject:policyArray forKey:[agent getAgentName]];
1951 #pragma mark Agent manipulation functions
1956 - (BOOL)spawnFloatingAgent:(Class)agentClass
1957 entity:(NSString *)entity
1958 agentSubType:(AgentSubType)subtype
1959 addPolicyOfType:(NEPolicyConditionType)policyType
1960 publishData:(NSData *)data
1964 NSMutableDictionary * parameters;
1966 parameters =[NSMutableDictionary dictionary];
1967 [parameters setValue:entity forKey:@kEntityName];
1968 [parameters setValue:numberToNSNumber(subtype) forKey:@kAgentSubType];
1970 agent = [[agentClass alloc] initWithParameters:parameters];
1971 ok = [self registerAgent:agent];
1977 /* Since we just spawned this agent, update its data */
1978 [agent updateAgentData:data];
1979 [self publishToAgent:agent];
1982 /* Add a policy if there is a valid type. If POLICY_TYPE_NO_POLICY, then ignore policies.
1983 * POLICY_TYPE_NO_POLICY will be set for service-specific agents, in which case we rely on
1984 * service owners to install custom policies to point at the agents. */
1985 if (policyType >= NEPolicyConditionTypeNone) {
1986 BOOL useControlPolicySession = NO;
1987 if (subtype == kAgentSubTypeGlobal) {
1988 /* Policies for a Global scoped agents are at "control" level */
1989 useControlPolicySession = YES;
1992 ok = [self addPolicyToFloatingAgent:agent
1994 agentUUIDToUse:[agent agentUUID]
1995 policyType:policyType
1996 useControlPolicySession:useControlPolicySession];
1999 [self unregisterAgent:agent];
2004 SC_log(LOG_INFO, "Spawning floating agent for %@", entity);
2006 if ([agent getAgentType] == kAgentTypeProxy) {
2007 [self.floatingProxyAgentList setObject:agent forKey:entity];
2009 [self.floatingDNSAgentList setObject:agent forKey:entity];
2016 * Create an agent mapped to another agent.
2018 - (BOOL)spawnMappedFloatingAgent:(id)mapped_agent
2019 entity:(NSString *)entity
2020 agentSubType:(AgentSubType)subtype
2021 addPolicyOfType:(NEPolicyConditionType)policyType
2022 updateData:(NSData *)data
2025 NSMutableDictionary * parameters;
2027 parameters = [NSMutableDictionary dictionary];
2028 [parameters setValue:entity forKey:@kEntityName];
2029 [parameters setValue:numberToNSNumber(subtype) forKey:@kAgentSubType];
2031 dummyAgent = [[[mapped_agent class] alloc] initWithParameters:parameters];
2034 /* Since we just spawned this agent, update its data.
2035 * We do not publish it since this agent is mapped
2036 * to an agent which already has the same data
2038 [dummyAgent updateAgentData:data];
2041 BOOL useControlPolicySession = NO;
2042 if (subtype == kAgentSubTypeGlobal) {
2043 /* Policies for a Global scoped agents are at "control" level */
2044 useControlPolicySession = YES;
2047 BOOL ok = [self addPolicyToFloatingAgent:dummyAgent
2049 agentUUIDToUse:[mapped_agent agentUUID]
2050 policyType:policyType
2051 useControlPolicySession:useControlPolicySession];
2057 if ([mapped_agent getAgentType] == kAgentTypeProxy) {
2058 [self.floatingProxyAgentList setObject:dummyAgent forKey:entity];
2060 [self.floatingDNSAgentList setObject:dummyAgent forKey:entity];
2063 [dummyAgent setAgentMapping:mapped_agent];
2065 SC_log(LOG_INFO, "Mapped floating agent %@ to %@", [dummyAgent getAgentName], [mapped_agent getAgentName]);
2070 * Write into an agent
2072 - (BOOL)publishToAgent:(id)agent
2074 /* Before any data goes into the kernel, do a sanity check. */
2075 NSData *sanityCheckData = [self dataLengthSanityCheck:agent];
2076 NSData *tempAgentData = nil;
2078 if (sanityCheckData != nil) {
2079 /* Data length is more than the limit! for updateNetworkAgent, the data blob
2080 * has to be a part of the agent object. Thus the temporary data replacement!
2082 tempAgentData = [[agent getAgentData] copy];
2083 [agent updateAgentData:sanityCheckData];
2084 SC_log(LOG_NOTICE, "Data too large for %@ (%lu bytes)!", [agent getAgentName], (unsigned long)[tempAgentData length]);
2089 NWNetworkAgentRegistration *regObject = [agent valueForKey:@"registrationObject"];
2090 if (regObject != nil) {
2091 SC_log(LOG_NOTICE, "Publishing data to agent %@ (%lu bytes)", [agent getAgentName], (unsigned long)[[agent getAgentData] length]);
2092 ok = [regObject updateNetworkAgent:agent];
2094 SC_log(LOG_NOTICE, "Could not update config agent");
2097 SC_log(LOG_NOTICE, "Config Agent not registered. Cannot Update");
2100 if (tempAgentData != nil) {
2101 [agent updateAgentData:tempAgentData];
2110 - (BOOL)destroyFloatingAgent:(id)agent
2114 if ( agent != nil) {
2115 NSString * entity = [agent getAssociatedEntity];
2116 NSMutableArray * policyArray;
2118 policyArray = [self.policyDB objectForKey:[agent getAgentName]];
2119 if (policyArray != nil) {
2120 NEPolicySession * session = ((ConfigAgent *)agent).preferredPolicySession;
2123 for (NSNumber *policyID in policyArray) {
2126 idVal = [policyID unsignedIntegerValue];
2127 result = [session removePolicyWithID:idVal];
2129 SC_log(LOG_NOTICE, "Could not remove policy %@ for agent %@", [session policyWithID:idVal], [agent getAgentName]);
2133 [self.policyDB removeObjectForKey:[agent getAgentName]];
2136 SC_log(LOG_INFO, "Destroying floating agent for %@", entity);
2138 if ([agent getAgentType] == kAgentTypeProxy) {
2139 [self.floatingProxyAgentList removeObjectForKey:entity];
2141 [self.floatingProxyAgentList_TCPConverter removeObjectForKey:entity];
2142 if ([self.floatingProxyAgentList_TCPConverter count] == 0) {
2143 updateTransportConverterProxyEnabled(FALSE); // disable "net.inet.mptcp.allow_aggregate"
2146 [self.floatingDNSAgentList removeObjectForKey:[agent getAssociatedEntity]];
2149 if ([agent getRegistrationObject] != nil) {
2150 [self unregisterAgent:agent];
2153 /* Check if we need to close the "control" policy session */
2154 if (self.controlPolicySession != nil) {
2155 NSMutableArray *globalProxyAgentList;
2156 NSMutableArray *globalDNSAgentList;
2157 globalProxyAgentList = [self getAgentList:self.floatingProxyAgentList agentType:kAgentTypeProxy agentSubType:kAgentSubTypeGlobal];
2158 globalDNSAgentList = [self getAgentList:self.floatingDNSAgentList agentType:kAgentTypeDNS agentSubType:kAgentSubTypeGlobal];
2160 if ([globalProxyAgentList count] == 0 &&
2161 [globalDNSAgentList count] == 0) {
2162 ok = [self.controlPolicySession removeAllPolicies];
2164 SC_log(LOG_ERR, "Could not remove policies for agent %@", [agent getAgentName]);
2167 self.controlPolicySession = nil;
2168 SC_log(LOG_NOTICE, "Closed control policy session");
2182 - (BOOL)registerAgent:(id)agent
2186 NWNetworkAgentRegistration *registration = [[NWNetworkAgentRegistration alloc] initWithNetworkAgentClass:[agent class]];
2188 ok = [registration registerNetworkAgent:agent];
2190 SC_log(LOG_NOTICE, "Could not register config agent");
2194 [agent addAgentRegistrationObject:registration];
2201 * Unregister an agent
2203 - (BOOL)unregisterAgent:(id)agent
2207 NWNetworkAgentRegistration *regObject = [agent valueForKey:@"registrationObject"];
2208 if (regObject != nil) {
2209 ok = [regObject unregisterNetworkAgent];
2211 SC_log(LOG_NOTICE, "Could not unregister config agent");
2214 SC_log(LOG_NOTICE, "Config Agent not registered. Cannot unregister");