/*
- * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2004-2018 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
- *
+ *
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
- *
+ *
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
- *
+ *
* @APPLE_LICENSE_HEADER_END@
*/
* - initial revision
*/
+#include <TargetConditionals.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <resolv.h>
+#if !TARGET_OS_IPHONE
+#include <notify.h>
+extern uint32_t notify_monitor_file(int token, const char *name, int flags);
+#endif // !TARGET_OS_IPHONE
+#include <CommonCrypto/CommonDigest.h>
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCPrivate.h>
#include <SystemConfiguration/SCValidation.h>
+#include "ip_plugin.h"
+
+#include "dns-configuration.h"
#include <dnsinfo.h>
-#include <dnsinfo_create.h>
+#include "dnsinfo_create.h"
+#include "dnsinfo_internal.h"
+#include "dnsinfo_logging.h"
+#include "dnsinfo_private.h"
+#include "dnsinfo_server.h"
+
+#include <network_information.h>
+
+#include <dns_sd.h>
+#include <dns_sd_private.h>
+
+#define DNS_CONFIGURATION_FLAGS_KEY CFSTR("__FLAGS__")
+#define DNS_CONFIGURATION_IF_INDEX_KEY CFSTR("__IF_INDEX__")
+#define DNS_CONFIGURATION_ORDER_KEY CFSTR("__ORDER__")
+
+/* multicast DNS resolver configurations */
+static CFNumberRef S_mdns_timeout = NULL;
+
+/* private DNS resolver configurations */
+static CFNumberRef S_pdns_timeout = NULL;
+
+
+#pragma mark -
+#pragma mark DNS resolver flags
+
+
+static __inline__ boolean_t
+dns_resolver_flags_all_queries(uint32_t query_flags)
+{
+ return ((query_flags & DNS_RESOLVER_FLAGS_REQUEST_ALL_RECORDS) == DNS_RESOLVER_FLAGS_REQUEST_ALL_RECORDS);
+}
+
+
+
+
+static uint32_t
+dns_resolver_flags_service(CFDictionaryRef service, uint32_t resolver_flags)
+{
+
+ // check if the service has v4 configured
+ if (((resolver_flags & DNS_RESOLVER_FLAGS_REQUEST_A_RECORDS) == 0) &&
+ service_contains_protocol(service, AF_INET)) {
+ resolver_flags |= DNS_RESOLVER_FLAGS_REQUEST_A_RECORDS;
+ }
+
+ // check if the service has v6 configured
+ if (((resolver_flags & DNS_RESOLVER_FLAGS_REQUEST_AAAA_RECORDS) == 0) &&
+ service_contains_protocol(service, AF_INET6)) {
+ resolver_flags |= DNS_RESOLVER_FLAGS_REQUEST_AAAA_RECORDS;
+ }
+
+ return resolver_flags;
+}
+
+
+static void
+add_dns_resolver_flags(const void *key, const void *value, void *context)
+{
+#pragma unused(key)
+ CFDictionaryRef service = (CFDictionaryRef)value;
+// CFStringRef serviceID = (CFStringRef)key;
+ uint32_t *resolver_flags = (uint32_t *)context;
+
+ if (service_is_scoped_only(service)) {
+ return;
+ }
+
+ // update resovler flags based on configured (and available) protocols
+ *resolver_flags = dns_resolver_flags_service(service, *resolver_flags);
+ return;
+}
+
+
+#pragma mark -
+#pragma mark DNS resolver configuration
+
+
+static void
+add_resolver(CFMutableArrayRef resolvers, CFMutableDictionaryRef resolver)
+{
+ CFIndex i;
+ CFStringRef interface;
+ CFIndex n_resolvers;
+ CFNumberRef order;
+ uint32_t order_val = 0;
+
+ order = CFDictionaryGetValue(resolver, kSCPropNetDNSSearchOrder);
+ if (!isA_CFNumber(order) ||
+ !CFNumberGetValue(order, kCFNumberSInt32Type, &order_val)) {
+ order = NULL;
+ order_val = 0;
+ }
+
+ n_resolvers = CFArrayGetCount(resolvers);
+ for (i = 0; i < n_resolvers; i++) {
+ CFDictionaryRef match_resolver;
+
+ match_resolver = CFArrayGetValueAtIndex(resolvers, i);
+ if (CFEqual(resolver, match_resolver)) {
+ // a real duplicate
+ return;
+ }
+
+ if (order != NULL) {
+ CFMutableDictionaryRef compare;
+ Boolean match;
+
+ compare = CFDictionaryCreateMutableCopy(NULL, 0, match_resolver);
+ CFDictionarySetValue(compare, kSCPropNetDNSSearchOrder, order);
+ match = CFEqual(resolver, compare);
+ CFRelease(compare);
+ if (match) {
+ CFNumberRef match_order;
+ uint32_t match_order_val = 0;
+
+ // if only the search order's are different
+ match_order = CFDictionaryGetValue(match_resolver, kSCPropNetDNSSearchOrder);
+ if (!isA_CFNumber(match_order) ||
+ !CFNumberGetValue(match_order, kCFNumberSInt32Type, &match_order_val)) {
+ match_order_val = 0;
+ }
+
+ if (order_val < match_order_val ) {
+ // if we should prefer this match resolver, else just skip it
+ CFArraySetValueAtIndex(resolvers, i, resolver);
+ }
+
+ return;
+ }
+ }
+ }
+ order = CFNumberCreate(NULL, kCFNumberCFIndexType, &n_resolvers);
+ CFDictionarySetValue(resolver, DNS_CONFIGURATION_ORDER_KEY, order);
+ CFRelease(order);
-/* pre-defined (supplemental) resolver configurations */
-static CFArrayRef S_predefined = NULL;
+ interface = CFDictionaryGetValue(resolver, kSCPropInterfaceName);
+ if ((interface != NULL) && !CFEqual(interface, CFSTR("*"))) {
+ uint32_t flags;
+ unsigned int if_index = 0;
+ char if_name[IF_NAMESIZE];
+ CFNumberRef num;
+ CFBooleanRef val;
+
+ if (_SC_cfstring_to_cstring(interface,
+ if_name,
+ sizeof(if_name),
+ kCFStringEncodingASCII) != NULL) {
+ if_index = my_if_nametoindex(if_name);
+ }
+
+ if ((if_index != 0) &&
+ (
+ // check if this is a "scoped" configuration
+ (CFDictionaryGetValueIfPresent(resolver, DNS_CONFIGURATION_FLAGS_KEY, (const void **)&num) &&
+ isA_CFNumber(num) &&
+ CFNumberGetValue(num, kCFNumberSInt32Type, &flags) &&
+ (flags & DNS_RESOLVER_FLAGS_SCOPED) != 0)
+ ||
+ // check if we should scope all queries with this configuration
+ (CFDictionaryGetValueIfPresent(resolver, DNS_CONFIGURATION_SCOPED_QUERY_KEY, (const void **)&val) &&
+ isA_CFBoolean(val) &&
+ CFBooleanGetValue(val))
+ )
+ ) {
+ // if interface index available and it should be used
+ num = CFNumberCreate(NULL, kCFNumberIntType, &if_index);
+ CFDictionarySetValue(resolver, DNS_CONFIGURATION_IF_INDEX_KEY, num);
+ CFRelease(num);
+ }
+ }
+
+ CFArrayAppendValue(resolvers, resolver);
+ return;
+}
+
+
+#define DNS_CONFIGURATION_CONFIGURATION_ID CFSTR("__CONFIGURATION_ID__")
+
+
+static void
+add_resolver_signature(CFMutableDictionaryRef resolver, const char *rType, CFStringRef cID, CFIndex rIndex)
+{
+ CFStringRef str;
+
+ str = CFStringCreateWithFormat(NULL, NULL,
+ CFSTR("%s:%s%@ %ld"),
+ rType,
+ (cID != NULL) ? " " : "",
+ (cID != NULL) ? cID : CFSTR(""),
+ rIndex);
+ CFDictionarySetValue(resolver, DNS_CONFIGURATION_CONFIGURATION_ID, str);
+ CFRelease(str);
+
+ return;
+}
static void
-add_supplemental(CFMutableArrayRef supplemental, CFDictionaryRef dns, uint32_t defaultOrder)
+add_supplemental(CFMutableArrayRef resolvers,
+ CFStringRef serviceID,
+ CFDictionaryRef dns,
+ uint32_t defaultOrder,
+ Boolean scoped)
{
CFArrayRef domains;
CFIndex i;
CFIndex n_domains;
CFArrayRef orders;
-
+ CFArrayRef servers;
domains = CFDictionaryGetValue(dns, kSCPropNetDNSSupplementalMatchDomains);
n_domains = isA_CFArray(domains) ? CFArrayGetCount(domains) : 0;
if (n_domains == 0) {
+ // if no supplemental match domains
return;
}
orders = CFDictionaryGetValue(dns, kSCPropNetDNSSupplementalMatchOrders);
if (orders != NULL) {
if (!isA_CFArray(orders) || (n_domains != CFArrayGetCount(orders))) {
+ // if supplemental match orders... but too many/not enough
return;
}
}
+ servers = CFDictionaryGetValue(dns, kSCPropNetDNSServerAddresses);
+ if (!isA_CFArray(servers) || (CFArrayGetCount(servers) == 0)) {
+ // if no DNS server addresses
+ return;
+ }
+
/*
* yes, this is a "supplemental" resolver configuration, expand
- * the match domains and add each to the supplemental list.
+ * the match domains and add each to the resolvers list.
*/
for (i = 0; i < n_domains; i++) {
- CFIndex j;
CFStringRef match_domain;
CFNumberRef match_order;
- uint32_t match_order_val = 0;
CFMutableDictionaryRef match_resolver;
- CFIndex n_supplemental;
match_domain = CFArrayGetValueAtIndex(domains, i);
if (!isA_CFString(match_domain)) {
continue;
}
- match_order = (orders != NULL) ? CFArrayGetValueAtIndex(orders, i) : NULL;
-
match_resolver = CFDictionaryCreateMutableCopy(NULL, 0, dns);
- CFDictionaryRemoveValue(match_resolver, kSCPropNetDNSSupplementalMatchDomains);
- CFDictionaryRemoveValue(match_resolver, kSCPropNetDNSSupplementalMatchOrders);
- CFDictionaryRemoveValue(match_resolver, kSCPropNetDNSSearchDomains);
- CFDictionarySetValue(match_resolver, kSCPropNetDNSDomainName, match_domain);
+
+ // set supplemental resolver "domain"
+ if (CFStringGetLength(match_domain) > 0) {
+ CFDictionarySetValue(match_resolver, kSCPropNetDNSDomainName, match_domain);
+ } else {
+ CFDictionaryRemoveValue(match_resolver, kSCPropNetDNSDomainName);
+ }
+
+ // set supplemental resolver "search_order"
+ match_order = (orders != NULL) ? CFArrayGetValueAtIndex(orders, i) : NULL;
if (isA_CFNumber(match_order)) {
CFDictionarySetValue(match_resolver, kSCPropNetDNSSearchOrder, match_order);
} else if (!CFDictionaryContainsKey(match_resolver, kSCPropNetDNSSearchOrder)) {
defaultOrder++; // if multiple domains, maintain ordering
}
- match_order = CFDictionaryGetValue(match_resolver, kSCPropNetDNSSearchOrder);
- if (!isA_CFNumber(match_order) ||
- !CFNumberGetValue(match_order, kCFNumberIntType, &match_order_val)) {
- match_order = NULL;
- match_order_val = 0;
- }
-
- n_supplemental = CFArrayGetCount(supplemental);
- for (j = 0; j < n_supplemental; j++) {
- CFMutableDictionaryRef compare;
- Boolean match;
- CFDictionaryRef supplemental_resolver;
-
- supplemental_resolver = CFArrayGetValueAtIndex(supplemental, j);
- if (CFEqual(match_resolver, supplemental_resolver)) {
- // a real duplicate
- CFRelease(match_resolver);
- match_resolver = NULL;
- break;
- }
-
- compare = CFDictionaryCreateMutableCopy(NULL, 0, supplemental_resolver);
- if (match_order != NULL) {
- CFDictionarySetValue(compare, kSCPropNetDNSSearchOrder, match_order);
- }
- match = CFEqual(match_resolver, compare);
- CFRelease(compare);
-
- if (match) {
- CFNumberRef supplemental_order;
- uint32_t supplemental_order_val = 0;
-
- // if only the search order's are different
- supplemental_order = CFDictionaryGetValue(supplemental_resolver, kSCPropNetDNSSearchOrder);
- if (!isA_CFNumber(supplemental_order) ||
- !CFNumberGetValue(supplemental_order, kCFNumberIntType, &supplemental_order_val)) {
- supplemental_order_val = 0;
- }
-
- if (match_order_val < supplemental_order_val ) {
- // if we should prefer this match resolver, else just skip it
- CFArraySetValueAtIndex(supplemental, j, match_resolver);
- }
-
- CFRelease(match_resolver);
- match_resolver = NULL;
- break;
- }
- }
-
- if (match_resolver != NULL) {
- CFArrayAppendValue(supplemental, match_resolver);
- CFRelease(match_resolver);
- }
+ // remove keys we don't want in a supplemental resolver
+ CFDictionaryRemoveValue(match_resolver, kSCPropNetDNSSupplementalMatchDomains);
+ CFDictionaryRemoveValue(match_resolver, kSCPropNetDNSSupplementalMatchOrders);
+ CFDictionaryRemoveValue(match_resolver, kSCPropNetDNSSearchDomains);
+ CFDictionaryRemoveValue(match_resolver, kSCPropNetDNSSortList);
+
+ add_resolver_signature(match_resolver,
+ scoped ? "Supplemental/Scoped" : "Supplemental",
+ serviceID,
+ i);
+ add_resolver(resolvers, match_resolver);
+ CFRelease(match_resolver);
}
return;
}
+#define N_QUICK 32
+
+
static void
-add_predefined_resolvers(CFMutableArrayRef supplemental)
+merge_configuration_flags(CFMutableDictionaryRef newDNS, uint32_t mergeFlags)
{
- CFIndex i;
- CFIndex n;
+ uint32_t flags;
+ CFNumberRef num;
- if (S_predefined == NULL) {
- return;
+ if (!CFDictionaryGetValueIfPresent(newDNS, DNS_CONFIGURATION_FLAGS_KEY, (const void **)&num) ||
+ !isA_CFNumber(num) ||
+ !CFNumberGetValue(num, kCFNumberSInt32Type, &flags)) {
+ flags = 0;
}
- n = CFArrayGetCount(S_predefined);
- for (i = 0; i < n; i++) {
- uint32_t defaultOrder;
- CFDictionaryRef dns;
-
- dns = CFArrayGetValueAtIndex(S_predefined, i);
- if (!isA_CFDictionary(dns)) {
- continue;
- }
+ flags |= mergeFlags;
- defaultOrder = DEFAULT_SEARCH_ORDER +
- (DEFAULT_SEARCH_ORDER / 2) +
- ((DEFAULT_SEARCH_ORDER / 1000) * i);
- add_supplemental(supplemental, dns, defaultOrder);
- }
+ num = CFNumberCreate(NULL, kCFNumberSInt32Type, &flags);
+ CFDictionarySetValue(newDNS, DNS_CONFIGURATION_FLAGS_KEY, num);
+ CFRelease(num);
return;
}
-#define N_QUICK 32
-
-
static void
-add_supplemental_resolvers(CFMutableArrayRef supplemental, CFDictionaryRef services, CFArrayRef service_order)
+add_supplemental_resolvers(CFMutableArrayRef resolvers,
+ CFDictionaryRef services,
+ CFArrayRef service_order,
+ CFStringRef scoped_interface,
+ CFDictionaryRef scoped_service)
{
const void * keys_q[N_QUICK];
const void ** keys = keys_q;
CFDictionaryGetKeysAndValues(services, keys, vals);
for (i = 0; i < n_services; i++) {
- uint32_t defaultOrder;
- CFDictionaryRef dns;
- CFDictionaryRef service = (CFDictionaryRef)vals[i];
+ uint32_t defaultOrder;
+ CFDictionaryRef dns;
+ uint32_t dns_resolver_flags;
+ CFStringRef interface;
+ CFMutableDictionaryRef newDNS = NULL;
+ uint32_t newFlags;
+ CFDictionaryRef service = (CFDictionaryRef)vals[i];
+ CFStringRef serviceID = (CFStringRef)keys[i];
+ Boolean trusted = FALSE; // trusted config w/interface
if (!isA_CFDictionary(service)) {
continue;
}
dns = CFDictionaryGetValue(service, kSCEntNetDNS);
- if (!isA_CFDictionary(dns)) {
+ dns = isA_CFDictionary(dns);
+ if (dns == NULL) {
continue;
}
- defaultOrder = DEFAULT_SEARCH_ORDER -
- (DEFAULT_SEARCH_ORDER / 2) +
- ((DEFAULT_SEARCH_ORDER / 1000) * i);
+ interface = CFDictionaryGetValue(dns, kSCPropInterfaceName);
+
+ if (scoped_interface != NULL) {
+ //
+ // we only want to add split/supplemental configurations
+ // for queries scoped to an interface if they are NOT
+ // associated with a "real" service
+ //
+ if (CFDictionaryContainsKey(service, kSCEntNetIPv4) ||
+ CFDictionaryContainsKey(service, kSCEntNetIPv6)) {
+ continue;
+ }
+
+ //
+ // in addition, we don't want to add split/supplemental
+ // configurations for queries scoped to an interface if
+ // the configuration does not apply to all interfaces and
+ // the configuration is explicitly NOT for this interface
+ //
+ if (!_SC_CFEqual(interface, CFSTR("*")) &&
+ !_SC_CFEqual(interface, scoped_interface)) {
+ continue;
+ }
+
+ //
+ // lastly, check if A/AAAA queries should be issued (based
+ // on the IP[v6] addresses). If we would not be issuing a
+ // query then don't bother adding the configuration.
+ //
+ dns_resolver_flags = dns_resolver_flags_service(scoped_service, 0);
+ if (dns_resolver_flags == 0) {
+ continue;
+ }
+ }
+
+ defaultOrder = DEFAULT_SEARCH_ORDER
+ - (DEFAULT_SEARCH_ORDER / 2)
+ + ((DEFAULT_SEARCH_ORDER / 1000) * (uint32_t)i);
if ((n_order > 0) &&
!CFArrayContainsValue(service_order, CFRangeMake(0, n_order), keys[i])) {
// push out services not specified in service order
defaultOrder += (DEFAULT_SEARCH_ORDER / 1000) * n_services;
}
- add_supplemental(supplemental, dns, defaultOrder);
+ /*
+ * Ensure that we have the correct InterfaceName in the DNS configuration
+ *
+ * scoped_interface [supplemental] interface Trusted config DNS interface
+ * ================ ======================== ============== =================
+ * NULL NULL No NULL (No change)
+ * NULL en0 No NULL
+ * NULL * No NULL
+ * NULL NULL Yes NULL (No change)
+ * NULL en0 Yes en0 (trusted config w/interface)
+ * NULL * Yes NULL
+ * en0 NULL N/A en0 (scoped interface)
+ * en0 en0 N/A en0 (scoped interface)
+ * en0 * N/A en0 (scoped interface)
+ */
+ if ((scoped_interface == NULL) && (interface == NULL)) {
+ newDNS = (CFMutableDictionaryRef)CFRetain(dns);
+ } else {
+ CFBooleanRef val;
+
+ newDNS = CFDictionaryCreateMutableCopy(NULL, 0, dns);
+ if (scoped_interface != NULL) {
+ CFDictionarySetValue(newDNS, kSCPropInterfaceName, scoped_interface);
+ } else if ((interface != NULL) &&
+ CFDictionaryGetValueIfPresent(dns, DNS_CONFIGURATION_SCOPED_QUERY_KEY, (const void **)&val) &&
+ isA_CFBoolean(val) &&
+ CFBooleanGetValue(val)) {
+ // leave the [trusted configuration] InterfaceName in place
+ trusted = TRUE;
+ } else {
+ CFDictionaryRemoveValue(newDNS, kSCPropInterfaceName);
+ }
+ }
+
+ // set "supplemental" flag
+ newFlags = DNS_RESOLVER_FLAGS_SUPPLEMENTAL;
+
+ if (scoped_interface != NULL) {
+ // set "scoped" configuration flag
+ newFlags |= DNS_RESOLVER_FLAGS_SCOPED;
+
+ // add "Request A/AAAA query" flag(s)
+ newFlags |= dns_resolver_flags;
+ } else if (trusted) {
+ // use the DNS query flags from the supplemental match service
+ newFlags |= dns_resolver_flags_service(service, 0);
+ }
+
+ merge_configuration_flags(newDNS, newFlags);
+
+ // add [scoped] resolver entry
+ add_supplemental(resolvers, serviceID, newDNS, defaultOrder, (scoped_interface != NULL));
+ CFRelease(newDNS);
}
if (keys != keys_q) {
}
-static CFComparisonResult
-compareBySearchOrder(const void *val1, const void *val2, void *context)
+static void
+add_multicast_resolvers(CFMutableArrayRef resolvers, CFArrayRef multicastResolvers)
{
- CFDictionaryRef dns1 = (CFDictionaryRef)val1;
- CFDictionaryRef dns2 = (CFDictionaryRef)val2;
- CFNumberRef num;
- uint32_t order1 = DEFAULT_SEARCH_ORDER;
- uint32_t order2 = DEFAULT_SEARCH_ORDER;
+ CFIndex i;
+ CFIndex n;
- num = CFDictionaryGetValue(dns1, kSCPropNetDNSSearchOrder);
- if (!isA_CFNumber(num) ||
- !CFNumberGetValue(num, kCFNumberIntType, &order1)) {
- order1 = DEFAULT_SEARCH_ORDER;
- }
+ n = isA_CFArray(multicastResolvers) ? CFArrayGetCount(multicastResolvers) : 0;
+ for (i = 0; i < n; i++) {
+ uint32_t defaultOrder;
+ CFStringRef domain;
+ CFNumberRef num;
+ CFMutableDictionaryRef resolver;
+
+ domain = CFArrayGetValueAtIndex(multicastResolvers, i);
+ domain = _SC_trimDomain(domain);
+ if (domain == NULL) {
+ continue;
+ }
- num = CFDictionaryGetValue(dns2, kSCPropNetDNSSearchOrder);
- if (!isA_CFNumber(num) ||
- !CFNumberGetValue(num, kCFNumberIntType, &order2)) {
- order2 = DEFAULT_SEARCH_ORDER;
+ defaultOrder = DEFAULT_SEARCH_ORDER
+ + (DEFAULT_SEARCH_ORDER / 2)
+ + ((DEFAULT_SEARCH_ORDER / 1000) * (uint32_t)i);
+
+ resolver = CFDictionaryCreateMutable(NULL,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ CFDictionarySetValue(resolver, kSCPropNetDNSDomainName, domain);
+ CFDictionarySetValue(resolver, kSCPropNetDNSOptions, CFSTR("mdns"));
+ num = CFNumberCreate(NULL, kCFNumberIntType, &defaultOrder);
+ CFDictionarySetValue(resolver, kSCPropNetDNSSearchOrder, num);
+ CFRelease(num);
+ if (S_mdns_timeout != NULL) {
+ CFDictionarySetValue(resolver, kSCPropNetDNSServerTimeout, S_mdns_timeout);
+ }
+ add_resolver_signature(resolver, "Multicast DNS", NULL, i);
+ add_resolver(resolvers, resolver);
+ CFRelease(resolver);
+ CFRelease(domain);
}
- if (order1 == order2) {
- return kCFCompareEqualTo;
+ return;
+}
+
+
+static void
+add_private_resolvers(CFMutableArrayRef resolvers, CFArrayRef privateResolvers)
+{
+ CFIndex i;
+ CFIndex n;
+
+ n = isA_CFArray(privateResolvers) ? CFArrayGetCount(privateResolvers) : 0;
+ for (i = 0; i < n; i++) {
+ uint32_t defaultOrder;
+ CFStringRef domain;
+ CFNumberRef num;
+ CFMutableDictionaryRef resolver;
+
+ domain = CFArrayGetValueAtIndex(privateResolvers, i);
+ domain = _SC_trimDomain(domain);
+ if (domain == NULL) {
+ continue;
+ }
+
+ defaultOrder = DEFAULT_SEARCH_ORDER
+ - (DEFAULT_SEARCH_ORDER / 4)
+ + ((DEFAULT_SEARCH_ORDER / 1000) * (uint32_t)i);
+
+ resolver = CFDictionaryCreateMutable(NULL,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ CFDictionarySetValue(resolver, kSCPropNetDNSDomainName, domain);
+ CFDictionarySetValue(resolver, kSCPropNetDNSOptions, CFSTR("pdns"));
+ num = CFNumberCreate(NULL, kCFNumberIntType, &defaultOrder);
+ CFDictionarySetValue(resolver, kSCPropNetDNSSearchOrder, num);
+ CFRelease(num);
+ if (S_pdns_timeout != NULL) {
+ CFDictionarySetValue(resolver, kSCPropNetDNSServerTimeout, S_pdns_timeout);
+ }
+ add_resolver_signature(resolver, "Private DNS", NULL, i);
+ add_resolver(resolvers, resolver);
+ CFRelease(resolver);
+ CFRelease(domain);
}
- return (order1 < order2) ? kCFCompareLessThan : kCFCompareGreaterThan;
+ return;
}
-static CFStringRef
-trimDomain(CFStringRef domain)
+static CFComparisonResult
+compareBySearchOrder(const void *val1, const void *val2, void *context)
{
- CFIndex length;
- CFRange range;
- Boolean trimmed = FALSE;
+#pragma unused(context)
+ CFDictionaryRef dns1 = (CFDictionaryRef)val1;
+ CFDictionaryRef dns2 = (CFDictionaryRef)val2;
+ CFNumberRef num1;
+ CFNumberRef num2;
+ uint32_t order1 = DEFAULT_SEARCH_ORDER;
+ uint32_t order2 = DEFAULT_SEARCH_ORDER;
- if (!isA_CFString(domain)) {
- return NULL;
+ num1 = CFDictionaryGetValue(dns1, kSCPropNetDNSSearchOrder);
+ if (!isA_CFNumber(num1) ||
+ !CFNumberGetValue(num1, kCFNumberSInt32Type, &order1)) {
+ order1 = DEFAULT_SEARCH_ORDER;
}
- // remove trailing dots
- length = CFStringGetLength(domain);
- while (CFStringFindWithOptions(domain,
- CFSTR("."),
- CFRangeMake(0, length),
- kCFCompareAnchored|kCFCompareBackwards,
- &range)) {
- trimmed = TRUE;
- length = range.location;
+ num2 = CFDictionaryGetValue(dns2, kSCPropNetDNSSearchOrder);
+ if (!isA_CFNumber(num2) ||
+ !CFNumberGetValue(num2, kCFNumberSInt32Type, &order2)) {
+ order2 = DEFAULT_SEARCH_ORDER;
}
- if (length == 0) {
- return NULL;
- }
+ if (order1 == order2) {
+ // if same "SearchOrder", retain original orderring for configurations
+ if (CFDictionaryGetValueIfPresent(dns1, DNS_CONFIGURATION_ORDER_KEY, (const void **)&num1) &&
+ CFDictionaryGetValueIfPresent(dns2, DNS_CONFIGURATION_ORDER_KEY, (const void **)&num2) &&
+ isA_CFNumber(num1) &&
+ isA_CFNumber(num2) &&
+ CFNumberGetValue(num1, kCFNumberSInt32Type, &order1) &&
+ CFNumberGetValue(num2, kCFNumberSInt32Type, &order2)) {
+ if (order1 == order2) {
+ return kCFCompareEqualTo;
+ } else {
+ return (order1 < order2) ? kCFCompareLessThan : kCFCompareGreaterThan;
+ }
+ }
- if (trimmed) {
- domain = CFStringCreateWithSubstring(NULL, domain, CFRangeMake(0, length));
- } else {
- CFRetain(domain);
+ return kCFCompareEqualTo;
}
- return domain;
+ return (order1 < order2) ? kCFCompareLessThan : kCFCompareGreaterThan;
}
-static void
-update_search_domains(CFMutableDictionaryRef *defaultDomain, CFArrayRef supplemental)
+static CF_RETURNS_RETAINED CFArrayRef
+extract_search_domains(CFMutableDictionaryRef defaultDomain, CFArrayRef supplemental)
{
CFStringRef defaultDomainName = NULL;
uint32_t defaultOrder = DEFAULT_SEARCH_ORDER;
CFArrayRef defaultSearchDomains = NULL;
CFIndex defaultSearchIndex = 0;
- CFIndex i;
CFMutableArrayRef mySearchDomains;
- CFMutableArrayRef mySupplemental = (CFMutableArrayRef)supplemental;
+ CFMutableArrayRef mySupplemental = NULL;
CFIndex n_supplemental;
- Boolean searchDomainAdded = FALSE;
+ CFStringRef trimmedDomainName;
- n_supplemental = CFArrayGetCount(supplemental);
- if (n_supplemental == 0) {
- // if no supplemental domains
- return;
- }
+ mySearchDomains = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
- if (*defaultDomain != NULL) {
+ if (defaultDomain != NULL) {
CFNumberRef num;
- num = CFDictionaryGetValue(*defaultDomain, kSCPropNetDNSSearchOrder);
+ num = CFDictionaryGetValue(defaultDomain, kSCPropNetDNSSearchOrder);
if (!isA_CFNumber(num) ||
- !CFNumberGetValue(num, kCFNumberIntType, &defaultOrder)) {
+ !CFNumberGetValue(num, kCFNumberSInt32Type, &defaultOrder)) {
defaultOrder = DEFAULT_SEARCH_ORDER;
}
- defaultDomainName = CFDictionaryGetValue(*defaultDomain, kSCPropNetDNSDomainName);
- defaultSearchDomains = CFDictionaryGetValue(*defaultDomain, kSCPropNetDNSSearchDomains);
+ defaultDomainName = CFDictionaryGetValue(defaultDomain, kSCPropNetDNSDomainName);
+ defaultSearchDomains = CFDictionaryGetValue(defaultDomain, kSCPropNetDNSSearchDomains);
}
- mySearchDomains = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
-
+ // validate the provided "search" domains or move/expand/promote the "domain" name
if (isA_CFArray(defaultSearchDomains)) {
CFIndex n_search;
n_search = CFArrayGetCount(defaultSearchDomains);
- for (i = 0; i < n_search; i++) {
+ for (int i = 0; i < n_search; i++) {
CFStringRef search;
search = CFArrayGetValueAtIndex(defaultSearchDomains, i);
- search = trimDomain(search);
+ search = _SC_trimDomain(search);
if (search != NULL) {
CFArrayAppendValue(mySearchDomains, search);
CFRelease(search);
}
}
} else {
- defaultDomainName = trimDomain(defaultDomainName);
- if (defaultDomainName != NULL) {
- char *domain;
- int domain_parts = 1;
- char *dp;
-
- domain = _SC_cfstring_to_cstring(defaultDomainName,
+ trimmedDomainName = _SC_trimDomain(defaultDomainName);
+#ifdef PERFORM_DOMAIN_EXPANSION
+ /*
+ * With BIND 4.8.3 (and earlier) resolvers, the default search list included
+ * the default domain and each of its parent domains with two or more labels.
+ */
+ if ((trimmedDomainName != NULL) &&
+ CFStringHasSuffix(defaultDomainName, CFSTR("."))) {
+ // if "domain" name is fully qualified
+ CFArrayAppendValue(mySearchDomains, trimmedDomainName);
+ CFRelease(trimmedDomainName);
+ } else if (trimmedDomainName != NULL) {
+ char *domain;
+ int domain_parts = 1;
+ char *dp;
+ const int ndots = 1;
+
+ domain = _SC_cfstring_to_cstring(trimmedDomainName,
NULL,
0,
kCFStringEncodingUTF8);
- CFRelease(defaultDomainName);
+ CFRelease(trimmedDomainName);
// count domain parts
for (dp = domain; *dp != '\0'; dp++) {
}
}
+ // move "domain" to "search" list (and expand as needed)
dp = domain;
- for (i = LOCALDOMAINPARTS; i <= domain_parts; i++) {
+ do {
CFStringRef search;
-
- search = CFStringCreateWithCString(NULL,
- dp,
- kCFStringEncodingUTF8);
- CFArrayAppendValue(mySearchDomains, search);
- CFRelease(search);
+ CFStringRef str;
+
+ str = CFStringCreateWithCString(NULL,
+ dp,
+ kCFStringEncodingUTF8);
+ search = _SC_trimDomain(str);
+ CFRelease(str);
+ if (search != NULL) {
+ CFArrayAppendValue(mySearchDomains, search);
+ CFRelease(search);
+ }
dp = strchr(dp, '.') + 1;
- }
-
+ } while (domain_parts-- > 2);
CFAllocatorDeallocate(NULL, domain);
}
+#else // PERFORM_DOMAIN_EXPANSION
+ /*
+ * With BIND 4.9.3 (and later) resolvers, the default search list included
+ * just the default domain.
+ */
+ if (trimmedDomainName != NULL) {
+ CFArrayAppendValue(mySearchDomains, trimmedDomainName);
+ CFRelease(trimmedDomainName);
+ }
+#endif // PERFORM_DOMAIN_EXPANSION
}
+ // add any supplemental "domain" names to the search list
+ n_supplemental = (supplemental != NULL) ? CFArrayGetCount(supplemental) : 0;
if (n_supplemental > 1) {
mySupplemental = CFArrayCreateMutableCopy(NULL, 0, supplemental);
CFArraySortValues(mySupplemental,
CFRangeMake(0, n_supplemental),
compareBySearchOrder,
NULL);
+ supplemental = mySupplemental;
}
-
- for (i = 0; i < n_supplemental; i++) {
+ for (int i = 0; i < n_supplemental; i++) {
CFDictionaryRef dns;
CFIndex domainIndex;
+ int noSearch;
CFNumberRef num;
+ CFStringRef options;
CFStringRef supplementalDomain;
uint32_t supplementalOrder;
- dns = CFArrayGetValueAtIndex(mySupplemental, i);
+ dns = CFArrayGetValueAtIndex(supplemental, i);
+
+ options = CFDictionaryGetValue(dns, kSCPropNetDNSOptions);
+ if (isA_CFString(options)) {
+ CFRange range;
+
+ if (CFEqual(options, CFSTR("pdns"))) {
+ // don't add private resolver domains to the search list
+ continue;
+ }
+
+ range = CFStringFind(options, CFSTR("interface="), 0);
+ if (range.location != kCFNotFound) {
+ // don't add scoped resolver domains to the search list
+ continue;
+ }
+ }
supplementalDomain = CFDictionaryGetValue(dns, kSCPropNetDNSDomainName);
- supplementalDomain = trimDomain(supplementalDomain);
+ supplementalDomain = _SC_trimDomain(supplementalDomain);
if (supplementalDomain == NULL) {
continue;
}
+ num = CFDictionaryGetValue(dns, kSCPropNetDNSSupplementalMatchDomainsNoSearch);
+ if (isA_CFNumber(num) &&
+ CFNumberGetValue(num, kCFNumberIntType, &noSearch) &&
+ (noSearch != 0)) {
+ CFRelease(supplementalDomain);
+ continue;
+ }
+
if (CFStringHasSuffix(supplementalDomain, CFSTR(".in-addr.arpa")) ||
CFStringHasSuffix(supplementalDomain, CFSTR(".ip6.arpa" ))) {
CFRelease(supplementalDomain);
num = CFDictionaryGetValue(dns, kSCPropNetDNSSearchOrder);
if (!isA_CFNumber(num) ||
- !CFNumberGetValue(num, kCFNumberIntType, &supplementalOrder)) {
+ !CFNumberGetValue(num, kCFNumberSInt32Type, &supplementalOrder)) {
supplementalOrder = DEFAULT_SEARCH_ORDER;
}
defaultSearchIndex,
supplementalDomain);
defaultSearchIndex++;
- searchDomainAdded = TRUE;
} else {
if (domainIndex == kCFNotFound) {
// add to the (end of the) search list
CFArrayAppendValue(mySearchDomains, supplementalDomain);
- searchDomainAdded = TRUE;
}
}
CFRelease(supplementalDomain);
}
+ if (mySupplemental != NULL) CFRelease(mySupplemental);
- if (searchDomainAdded) {
- if (*defaultDomain == NULL) {
- *defaultDomain = CFDictionaryCreateMutable(NULL,
- 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks);
- }
- CFDictionarySetValue(*defaultDomain, kSCPropNetDNSSearchDomains, mySearchDomains);
+ // update the "search" domains
+ if (CFArrayGetCount(mySearchDomains) == 0) {
+ CFRelease(mySearchDomains);
+ mySearchDomains = NULL;
}
- CFRelease(mySearchDomains);
- if (mySupplemental != supplemental) CFRelease(mySupplemental);
- return;
+ // remove the "domain" name and "search" list
+ CFDictionaryRemoveValue(defaultDomain, kSCPropNetDNSDomainName);
+ CFDictionaryRemoveValue(defaultDomain, kSCPropNetDNSSearchDomains);
+
+ return mySearchDomains;
}
-static dns_create_resolver_t
-create_resolver(CFDictionaryRef dns)
+static void
+add_scoped_resolvers(CFMutableArrayRef scoped,
+ CFDictionaryRef services,
+ CFArrayRef service_order)
{
- CFArrayRef list;
- CFNumberRef num;
- dns_create_resolver_t _resolver;
- CFStringRef str;
+ const void * keys_q[N_QUICK];
+ const void ** keys = keys_q;
+ CFIndex i;
+ CFIndex n_order;
+ CFIndex n_services;
+ CFMutableArrayRef order;
+ CFMutableSetRef seen;
+
+ n_services = isA_CFDictionary(services) ? CFDictionaryGetCount(services) : 0;
+ if (n_services == 0) {
+ return; // if no services
+ }
+
+ // ensure that we process all services in order
+
+ n_order = isA_CFArray(service_order) ? CFArrayGetCount(service_order) : 0;
+ if (n_order > 0) {
+ order = CFArrayCreateMutableCopy(NULL, 0, service_order);
+ } else {
+ order = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+ }
+
+ if (n_services > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
+ keys = CFAllocatorAllocate(NULL, n_services * sizeof(CFTypeRef), 0);
+ }
+ CFDictionaryGetKeysAndValues(services, keys, NULL);
+ for (i = 0; i < n_services; i++) {
+ CFStringRef serviceID = (CFStringRef)keys[i];
+
+ if (!CFArrayContainsValue(order, CFRangeMake(0, n_order), serviceID)) {
+ CFArrayAppendValue(order, serviceID);
+ n_order++;
+ }
+ }
+ if (keys != keys_q) {
+ CFAllocatorDeallocate(NULL, keys);
+ }
+
+ // iterate over services
+
+ seen = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
+ for (i = 0; i < n_order; i++) {
+ CFDictionaryRef dns;
+ uint32_t dns_resolver_flags;
+ char if_name[IF_NAMESIZE];
+ CFStringRef interface;
+ CFMutableDictionaryRef newDNS;
+ uint32_t newFlags;
+ CFArrayRef searchDomains;
+ CFDictionaryRef service;
+ CFStringRef serviceID;
+ CFArrayRef servers;
+
+ serviceID = CFArrayGetValueAtIndex(order, i);
+ service = CFDictionaryGetValue(services, serviceID);
+ if (!isA_CFDictionary(service)) {
+ // if no service
+ continue;
+ }
+
+ dns = CFDictionaryGetValue(service, kSCEntNetDNS);
+ if (!isA_CFDictionary(dns)) {
+ // if no DNS
+ continue;
+ }
+
+ servers = CFDictionaryGetValue(dns, kSCPropNetDNSServerAddresses);
+ if (!isA_CFArray(servers) || (CFArrayGetCount(servers) == 0)) {
+ // if no DNS server addresses
+ continue;
+ }
+
+ interface = CFDictionaryGetValue(dns, kSCPropInterfaceName);
+ if ((interface == NULL) || CFEqual(interface, CFSTR("*"))) {
+ // if no [scoped] interface or supplemental configuration w/match-all
+ continue;
+ }
+
+ if (CFDictionaryContainsKey(dns, kSCPropNetDNSServiceIdentifier)) {
+ // if this is a service-specific resolver
+ continue;
+ }
+
+ if (CFSetContainsValue(seen, interface)) {
+ // if we've already processed this [scoped] interface
+ continue;
+ }
+ CFSetSetValue(seen, interface);
+
+ if ((_SC_cfstring_to_cstring(interface,
+ if_name,
+ sizeof(if_name),
+ kCFStringEncodingASCII) == NULL) ||
+ (my_if_nametoindex(if_name) == 0)) {
+ // if interface index not available
+ continue;
+ }
+
+ // add [scoped] resolver entry
+ newDNS = CFDictionaryCreateMutableCopy(NULL, 0, dns);
+
+ // set search list
+ searchDomains = extract_search_domains(newDNS, NULL);
+ if (searchDomains != NULL) {
+ CFDictionarySetValue(newDNS, kSCPropNetDNSSearchDomains, searchDomains);
+ CFRelease(searchDomains);
+ }
+
+ // get "Request A/AAAA query" flag(s)
+ dns_resolver_flags = dns_resolver_flags_service(service, 0);
+ if (dns_resolver_flags == 0) {
+ goto skip;
+ }
+
+ // set "scoped" configuration flag
+ newFlags = DNS_RESOLVER_FLAGS_SCOPED;
+
+ // add "Request A/AAAA query" flag(s)
+ newFlags |= dns_resolver_flags;
+
+ merge_configuration_flags(newDNS, newFlags);
+
+ // remove keys we don't want in a [scoped] resolver
+ CFDictionaryRemoveValue(newDNS, kSCPropNetDNSSupplementalMatchDomains);
+ CFDictionaryRemoveValue(newDNS, kSCPropNetDNSSupplementalMatchOrders);
+
+ // add the [scoped] resolver
+ add_resolver_signature(newDNS, "Scoped", serviceID, 0);
+ add_resolver(scoped, newDNS);
+
+ // add any supplemental resolver configurations for this interface
+ add_supplemental_resolvers(scoped, services, service_order, interface, service);
+
+ skip:
+ CFRelease(newDNS);
+ }
+
+ CFRelease(seen);
+ CFRelease(order);
+ return;
+}
+
+
+static void
+add_service_specific_resolvers(CFMutableArrayRef resolvers, CFDictionaryRef services)
+{
+ CFIndex i;
+ CFStringRef keys_q[N_QUICK];
+ CFStringRef *keys = keys_q;
+ CFIndex n_services;
+ CFMutableSetRef seen;
+ CFDictionaryRef vals_q[N_QUICK];
+ CFDictionaryRef *vals = vals_q;
+
+ n_services = isA_CFDictionary(services) ? CFDictionaryGetCount(services) : 0;
+ if (n_services == 0) {
+ return; // if no services
+ }
+
+ if (n_services > (CFIndex)(sizeof(keys_q) / sizeof(keys_q[0]))) {
+ keys = CFAllocatorAllocate(kCFAllocatorDefault, n_services * sizeof(keys[0]), 0);
+ vals = CFAllocatorAllocate(kCFAllocatorDefault, n_services * sizeof(vals[0]), 0);
+ }
+ CFDictionaryGetKeysAndValues(services, (const void **)keys, (const void **)vals);
+
+ seen = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
+ for (i = 0; i < n_services; i++) {
+ CFDictionaryRef dns;
+ CFNumberRef dns_service_identifier;
+ CFMutableDictionaryRef newDNS;
+ uint32_t newFlags = 0;
+ CFDictionaryRef service = vals[i];
+ CFStringRef serviceID = keys[i];
+ CFArrayRef searchDomains;
+
+ dns = CFDictionaryGetValue(service, kSCEntNetDNS);
+ if (!isA_CFDictionary(dns)) {
+ // if no DNS
+ continue;
+ }
+
+ dns_service_identifier = CFDictionaryGetValue(dns, kSCPropNetDNSServiceIdentifier);
+ if (!isA_CFNumber(dns_service_identifier)) {
+ // if no DNS [vpn] Service Identifier
+ continue;
+ }
+
+ if (CFSetContainsValue(seen, dns_service_identifier)) {
+ my_log(LOG_ERR, "add_service_specific_resolvers: got a resolver with a duplicate service identifier, skipping");
+ continue;
+ }
+ CFSetSetValue(seen, dns_service_identifier);
+
+ newDNS = CFDictionaryCreateMutableCopy(NULL, 0, dns);
+
+ // add "Request A/AAAA query" flag(s)
+ newFlags |= DNS_RESOLVER_FLAGS_REQUEST_ALL_RECORDS;
+
+ // set search list
+ searchDomains = extract_search_domains(newDNS, NULL);
+ if (searchDomains != NULL) {
+ CFDictionarySetValue(newDNS, kSCPropNetDNSSearchDomains, searchDomains);
+ CFRelease(searchDomains);
+ searchDomains = NULL;
+ }
+
+ CFDictionaryRemoveValue(newDNS, kSCPropNetDNSSupplementalMatchDomains);
+ CFDictionaryRemoveValue(newDNS, kSCPropNetDNSSupplementalMatchOrders);
+
+ if (CFDictionaryContainsKey(newDNS, kSCPropInterfaceName)) {
+ CFMutableDictionaryRef interfaceScopedDNS;
+ uint32_t interfaceScopedFlags;
+
+ // The dictionary has an interface, so add a interface-scoped resolver
+
+ CFDictionarySetValue(newDNS, DNS_CONFIGURATION_SCOPED_QUERY_KEY, kCFBooleanTrue);
+
+ interfaceScopedDNS = CFDictionaryCreateMutableCopy(NULL, 0, newDNS);
+ interfaceScopedFlags = newFlags;
+
+ // set "scoped" configuration flag
+ interfaceScopedFlags |= DNS_RESOLVER_FLAGS_SCOPED;
+ merge_configuration_flags(interfaceScopedDNS, interfaceScopedFlags);
+
+ CFDictionaryRemoveValue(interfaceScopedDNS, kSCPropNetDNSServiceIdentifier);
+
+ add_resolver_signature(interfaceScopedDNS, "Service", serviceID, 0);
+ add_resolver(resolvers, interfaceScopedDNS);
+ CFRelease(interfaceScopedDNS);
+ }
+
+ // set "service specific" configuration flag
+ newFlags |= DNS_RESOLVER_FLAGS_SERVICE_SPECIFIC;
+ merge_configuration_flags(newDNS, newFlags);
+
+ add_resolver_signature(newDNS, "Service", serviceID, 0);
+ add_resolver(resolvers, newDNS);
+ CFRelease(newDNS);
+ }
+ CFRelease(seen);
+
+ if (keys != keys_q) {
+ CFAllocatorDeallocate(kCFAllocatorDefault, keys);
+ CFAllocatorDeallocate(kCFAllocatorDefault, vals);
+ }
+
+ return;
+}
+
+
+static void
+add_default_resolver(CFMutableArrayRef resolvers,
+ CFDictionaryRef defaultResolver,
+ Boolean *orderAdded,
+ CFArrayRef *searchDomains)
+{
+ CFMutableDictionaryRef myDefault;
+ uint32_t myOrder = DEFAULT_SEARCH_ORDER;
+ CFNumberRef order;
+
+ if (defaultResolver == NULL) {
+ myDefault = CFDictionaryCreateMutable(NULL,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ } else {
+ myDefault = CFDictionaryCreateMutableCopy(NULL, 0, defaultResolver);
+ }
+ assert(myDefault != NULL);
+
+ // ensure that the default resolver has a search order
+
+ order = CFDictionaryGetValue(myDefault, kSCPropNetDNSSearchOrder);
+ if (!isA_CFNumber(order) ||
+ !CFNumberGetValue(order, kCFNumberSInt32Type, &myOrder)) {
+ myOrder = DEFAULT_SEARCH_ORDER;
+ order = CFNumberCreate(NULL, kCFNumberIntType, &myOrder);
+ CFDictionarySetValue(myDefault, kSCPropNetDNSSearchOrder, order);
+ CFRelease(order);
+ *orderAdded = TRUE;
+ }
+
+ // extract the "search" domain list for the default resolver (and
+ // any supplemental resolvers)
+
+ *searchDomains = extract_search_domains(myDefault, resolvers);
+
+ // add the default resolver
+
+ add_resolver_signature(myDefault, "Default", NULL, 0);
+ add_resolver(resolvers, myDefault);
+ CFRelease(myDefault);
+ return;
+}
+
+
+static dns_create_resolver_t
+create_resolver(CFDictionaryRef dns)
+{
+ CFArrayRef list;
+ CFNumberRef num;
+ dns_create_resolver_t _resolver;
+ CFStringRef str;
+ CFStringRef targetInterface = NULL;
+ unsigned int targetInterfaceIndex = 0;
_resolver = _dns_resolver_create();
// process domain
str = CFDictionaryGetValue(dns, kSCPropNetDNSDomainName);
- if (isA_CFString(str)) {
+ if (isA_CFString(str) && (CFStringGetLength(str) > 0)) {
char domain[NS_MAXDNAME];
if (_SC_cfstring_to_cstring(str, domain, sizeof(domain), kCFStringEncodingUTF8) != NULL) {
// add "search" domains
for (i = 0; i < n; i++) {
str = CFArrayGetValueAtIndex(list, i);
- if (isA_CFString(str)) {
+ if (isA_CFString(str) && (CFStringGetLength(str) > 0)) {
char search[NS_MAXDNAME];
if (_SC_cfstring_to_cstring(str, search, sizeof(search), kCFStringEncodingUTF8) != NULL) {
}
}
+ // process interface index
+ num = CFDictionaryGetValue(dns, DNS_CONFIGURATION_IF_INDEX_KEY);
+ if (isA_CFNumber(num)) {
+ int if_index;
+
+ if (CFNumberGetValue(num, kCFNumberIntType, &if_index)) {
+ char buf[IFNAMSIZ];
+ const char *if_name = NULL;
+
+ if (if_index != 0) {
+ if_name = my_if_indextoname(if_index, buf);
+ if (if_name != NULL) {
+ targetInterface = CFStringCreateWithCString(NULL,
+ if_name,
+ kCFStringEncodingASCII);
+ targetInterfaceIndex = if_index;
+ }
+ }
+
+ _dns_resolver_set_if_index(&_resolver, if_index, if_name);
+ }
+ }
+
+ // process flags
+ num = CFDictionaryGetValue(dns, DNS_CONFIGURATION_FLAGS_KEY);
+ if (isA_CFNumber(num)) {
+ uint32_t flags;
+
+ if (CFNumberGetValue(num, kCFNumberSInt32Type, &flags)) {
+ _dns_resolver_set_flags(&_resolver, flags);
+ }
+ }
+
// process nameserver addresses
+ // Note: the flags may be overwritten if the resolver has LOOPBACK addresses
list = CFDictionaryGetValue(dns, kSCPropNetDNSServerAddresses);
if (isA_CFArray(list)) {
CFIndex i;
- CFIndex n = CFArrayGetCount(list);
+ CFIndex n = CFArrayGetCount(list);
for (i = 0; i < n; i++) {
union {
- struct sockaddr sa;
- struct sockaddr_in sin;
- struct sockaddr_in6 sin6;
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
} addr;
- char buf[128];
+ char buf[64];
str = CFArrayGetValueAtIndex(list, i);
if (!isA_CFString(str)) {
continue;
}
- bzero(&addr, sizeof(addr));
- if (inet_aton(buf, &addr.sin.sin_addr) == 1) {
- /* if IPv4 address */
- addr.sin.sin_len = sizeof(addr.sin);
- addr.sin.sin_family = AF_INET;
- _dns_resolver_add_nameserver(&_resolver, &addr.sa);
- } else if (inet_pton(AF_INET6, buf, &addr.sin6.sin6_addr) == 1) {
- /* if IPv6 address */
- char *p;
-
- p = strchr(buf, '%');
- if (p != NULL) {
- addr.sin6.sin6_scope_id = if_nametoindex(p + 1);
- }
-
- addr.sin6.sin6_len = sizeof(addr.sin6);
- addr.sin6.sin6_family = AF_INET6;
- _dns_resolver_add_nameserver(&_resolver, &addr.sa);
- } else {
+ if (_SC_string_to_sockaddr(buf, AF_UNSPEC, (void *)&addr, sizeof(addr)) == NULL) {
continue;
}
+
+ if ((addr.sa.sa_family == AF_INET6) &&
+ IN6_IS_ADDR_LINKLOCAL(&addr.sin6.sin6_addr) &&
+ (addr.sin6.sin6_scope_id == 0) &&
+ (targetInterfaceIndex != 0)) {
+ // for link local [IPv6] addresses, if the scope id is not
+ // set then we should use the interface associated with the
+ // resolver configuration
+ addr.sin6.sin6_scope_id = targetInterfaceIndex;
+ }
+
+ _dns_resolver_add_nameserver(&_resolver, &addr.sa);
}
}
if (isA_CFNumber(num)) {
uint32_t order;
- if (CFNumberGetValue(num, kCFNumberIntType, &order)) {
+ if (CFNumberGetValue(num, kCFNumberSInt32Type, &order)) {
_dns_resolver_set_order(&_resolver, order);
}
}
+ // process sortlist
+ list = CFDictionaryGetValue(dns, kSCPropNetDNSSortList);
+ if (isA_CFArray(list)) {
+ CFIndex i;
+ CFIndex n = CFArrayGetCount(list);
+
+ for (i = 0; i < n; i++) {
+ char buf[128];
+ char *slash;
+ dns_sortaddr_t sortaddr;
+
+ str = CFArrayGetValueAtIndex(list, i);
+ if (!isA_CFString(str)) {
+ continue;
+ }
+
+ if (_SC_cfstring_to_cstring(str, buf, sizeof(buf), kCFStringEncodingASCII) == NULL) {
+ continue;
+ }
+
+ slash = strchr(buf, '/');
+ if (slash != NULL) {
+ *slash = '\0';
+ }
+
+ bzero(&sortaddr, sizeof(sortaddr));
+ if (inet_aton(buf, &sortaddr.address) != 1) {
+ /* if address not valid */
+ continue;
+ }
+
+ if (slash != NULL) {
+ if (inet_aton(slash + 1, &sortaddr.mask) != 1) {
+ /* if subnet mask not valid */
+ continue;
+ }
+ } else {
+ in_addr_t a;
+ in_addr_t m;
+
+ a = ntohl(sortaddr.address.s_addr);
+ if (IN_CLASSA(a)) {
+ m = IN_CLASSA_NET;
+ } else if (IN_CLASSB(a)) {
+ m = IN_CLASSB_NET;
+ } else if (IN_CLASSC(a)) {
+ m = IN_CLASSC_NET;
+ } else {
+ continue;
+ }
+
+ sortaddr.mask.s_addr = htonl(m);
+ }
+
+ _dns_resolver_add_sortaddr(&_resolver, &sortaddr);
+ }
+ }
+
// process port
num = CFDictionaryGetValue(dns, kSCPropNetDNSServerPort);
if (isA_CFNumber(num)) {
}
}
+ num = CFDictionaryGetValue(dns, kSCPropNetDNSServiceIdentifier);
+ if (isA_CFNumber(num)) {
+ int dns_service_identifier;
+
+ if (CFNumberGetValue(num, kCFNumberIntType, &dns_service_identifier)) {
+ _dns_resolver_set_service_identifier(&_resolver, (uint32_t)dns_service_identifier);
+ }
+ }
+
+ // process configuration ID
+ str = CFDictionaryGetValue(dns, DNS_CONFIGURATION_CONFIGURATION_ID);
+ if (isA_CFString(str) && (CFStringGetLength(str) > 0)) {
+ char *cID;
+
+ cID = _SC_cfstring_to_cstring(str, NULL, 0, kCFStringEncodingUTF8);
+ if (cID != NULL) {
+ _dns_resolver_set_configuration_identifier(&_resolver, cID);
+ CFAllocatorDeallocate(NULL, cID);
+ }
+ }
+
+ if (targetInterface != NULL) {
+ CFRelease(targetInterface);
+ }
+
return _resolver;
}
-__private_extern__
-void
-dns_configuration_set(CFDictionaryRef defaultResolver,
- CFDictionaryRef services,
- CFArrayRef serviceOrder)
+static __inline__ Boolean
+isScopedConfiguration(CFDictionaryRef dns)
{
- CFIndex i;
- CFMutableDictionaryRef myDefault;
- CFStringRef myDomain = NULL;
- uint32_t myOrder = DEFAULT_SEARCH_ORDER;
- Boolean myOrderAdded = FALSE;
- CFIndex n_supplemental;
- CFNumberRef order;
- dns_create_resolver_t resolver;
- CFArrayRef search;
- CFMutableArrayRef supplemental;
+ uint32_t flags;
+ CFNumberRef num;
- if (defaultResolver == NULL) {
- myDefault = CFDictionaryCreateMutable(NULL,
- 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks);
- } else {
- myDefault = CFDictionaryCreateMutableCopy(NULL, 0, defaultResolver);
+ if ((dns != NULL) &&
+ CFDictionaryGetValueIfPresent(dns, DNS_CONFIGURATION_FLAGS_KEY, (const void **)&num) &&
+ (num != NULL) &&
+ CFNumberGetValue(num, kCFNumberSInt32Type, &flags) &&
+ ((flags & DNS_RESOLVER_FLAGS_SCOPED) != 0)) {
+ // if scoped
+ return TRUE;
+ }
+
+ return FALSE;
+}
- // ensure that the default resolver has a search order
- order = CFDictionaryGetValue(myDefault, kSCPropNetDNSSearchOrder);
- if (!isA_CFNumber(order) ||
- !CFNumberGetValue(order, kCFNumberIntType, &myOrder)) {
- myOrderAdded = TRUE;
- myOrder = DEFAULT_SEARCH_ORDER;
- order = CFNumberCreate(NULL, kCFNumberIntType, &myOrder);
- CFDictionarySetValue(myDefault, kSCPropNetDNSSearchOrder, order);
- CFRelease(order);
+static CFComparisonResult
+compareDomain(const void *val1, const void *val2, void *context)
+{
+ CFDictionaryRef dns1 = (CFDictionaryRef)val1;
+ CFDictionaryRef dns2 = (CFDictionaryRef)val2;
+ CFStringRef domain1;
+ CFStringRef domain2;
+ CFArrayRef labels1 = NULL;
+ CFArrayRef labels2 = NULL;
+ CFIndex n1;
+ CFIndex n2;
+ CFComparisonResult result;
+ Boolean rev1;
+ Boolean rev2;
+ Boolean scoped1;
+ Boolean scoped2;
+
+ // "default" domains sort before "supplemental" domains
+ domain1 = CFDictionaryGetValue(dns1, kSCPropNetDNSDomainName);
+ domain2 = CFDictionaryGetValue(dns2, kSCPropNetDNSDomainName);
+ if (domain1 == NULL) {
+ return kCFCompareLessThan;
+ } else if (domain2 == NULL) {
+ return kCFCompareGreaterThan;
+ }
+
+ // sort non-scoped before scoped
+ scoped1 = isScopedConfiguration(dns1);
+ scoped2 = isScopedConfiguration(dns2);
+ if (scoped1 != scoped2) {
+ if (!scoped1) {
+ return kCFCompareLessThan;
+ } else {
+ return kCFCompareGreaterThan;
}
}
- // establish list of supplemental resolvers
+ // forward (A, AAAA) domains sort before reverse (PTR) domains
+ rev1 = CFStringHasSuffix(domain1, CFSTR(".arpa"));
+ rev2 = CFStringHasSuffix(domain2, CFSTR(".arpa"));
+ if (rev1 != rev2) {
+ if (rev1) {
+ return kCFCompareGreaterThan;
+ } else {
+ return kCFCompareLessThan;
+ }
+ }
+
+ labels1 = CFStringCreateArrayBySeparatingStrings(NULL, domain1, CFSTR("."));
+ n1 = CFArrayGetCount(labels1);
- supplemental = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+ labels2 = CFStringCreateArrayBySeparatingStrings(NULL, domain2, CFSTR("."));
+ n2 = CFArrayGetCount(labels2);
- // identify search[] list and/or domain name
+ while ((n1 > 0) && (n2 > 0)) {
+ CFStringRef label1 = CFArrayGetValueAtIndex(labels1, --n1);
+ CFStringRef label2 = CFArrayGetValueAtIndex(labels2, --n2);
- search = CFDictionaryGetValue(myDefault, kSCPropNetDNSSearchDomains);
- if (isA_CFArray(search) && (CFArrayGetCount(search) > 0)) {
- myDomain = CFArrayGetValueAtIndex(search, 0);
- myDomain = isA_CFString(myDomain);
+ // compare domain labels
+ result = CFStringCompare(label1, label2, kCFCompareCaseInsensitive);
+ if (result != kCFCompareEqualTo) {
+ goto done;
+ }
}
- if (myDomain == NULL) {
- myDomain = CFDictionaryGetValue(myDefault, kSCPropNetDNSDomainName);
- myDomain = isA_CFString(myDomain);
+ // longer labels (corp.apple.com) sort before shorter labels (apple.com)
+ if (n1 > n2) {
+ result = kCFCompareLessThan;
+ goto done;
+ } else if (n1 < n2) {
+ result = kCFCompareGreaterThan;
+ goto done;
}
- // add match for default domain
+ // sort by search order
+ result = compareBySearchOrder(val1, val2, context);
+
+ done :
- if (myDomain != NULL) {
- CFMutableDictionaryRef mySupplemental;
+ if (labels1 != NULL) CFRelease(labels1);
+ if (labels2 != NULL) CFRelease(labels2);
+ return result;
+}
+
+
+static __inline__ Boolean
+needsMergeWithDefaultConfiguration(CFDictionaryRef dns)
+{
+ uint32_t flags;
+ CFNumberRef num;
+
+ if ((dns != NULL) &&
+ CFDictionaryGetValueIfPresent(dns, DNS_CONFIGURATION_FLAGS_KEY, (const void **)&num) &&
+ (num != NULL) &&
+ CFNumberGetValue(num, kCFNumberSInt32Type, &flags)) {
- mySupplemental = CFDictionaryCreateMutableCopy(NULL, 0, myDefault);
- CFDictionarySetValue (mySupplemental, kSCPropNetDNSDomainName, myDomain);
- CFDictionaryRemoveValue(mySupplemental, kSCPropNetDNSSearchDomains);
- CFDictionaryRemoveValue(mySupplemental, kSCPropNetDNSSupplementalMatchDomains);
- CFDictionaryRemoveValue(mySupplemental, kSCPropNetDNSSupplementalMatchOrders);
- CFArrayAppendValue(supplemental, mySupplemental);
- CFRelease(mySupplemental);
+ // check if merge needed (at all)
+ if (dns_resolver_flags_all_queries(flags)) {
+ // if we are already querying for both A/AAAA
+ return FALSE;
+ }
+
+ // check if scoped or service-specific
+ if (((flags & DNS_RESOLVER_FLAGS_SCOPED ) != 0) ||
+ ((flags & DNS_RESOLVER_FLAGS_SERVICE_SPECIFIC) != 0)) {
+ // yes, skip merge
+ return FALSE;
+ }
}
- // collect (and add) any supplemental resolver configurations
+ return TRUE;
+}
- add_supplemental_resolvers(supplemental, services, serviceOrder);
- // update the "search" list
+__private_extern__
+Boolean
+dns_configuration_set(CFDictionaryRef defaultResolver,
+ CFDictionaryRef services,
+ CFArrayRef serviceOrder,
+ CFArrayRef multicastResolvers,
+ CFArrayRef privateResolvers)
+{
+ dns_create_config_t dns_create_config;
+ Boolean changed = FALSE;
+ CFIndex i;
+ CFMutableDictionaryRef myDefault;
+ Boolean myOrderAdded = FALSE;
+ CFArrayRef mySearchDomains = NULL;
+ CFIndex n_resolvers;
+ CFMutableArrayRef resolvers;
+ unsigned char signature[CC_SHA1_DIGEST_LENGTH];
+ static unsigned char signature_last[CC_SHA1_DIGEST_LENGTH];
- update_search_domains(&myDefault, supplemental);
+ // establish list of resolvers
- // add any pre-defined resolver configurations
+ resolvers = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+ assert(resolvers != NULL);
- add_predefined_resolvers(supplemental);
+ // collect (and add) any "supplemental" resolver configurations
- // check if the "match for default domain" (above) is really needed
+ add_supplemental_resolvers(resolvers, services, serviceOrder, NULL, NULL);
- if (myDomain != NULL) {
- Boolean sharedDomain = FALSE;
+ // collect (and add) any "private" resolver configurations
- n_supplemental = CFArrayGetCount(supplemental);
- for (i = 1; i < n_supplemental; i++) {
- CFStringRef domain;
- CFDictionaryRef mySupplemental;
+ add_private_resolvers(resolvers, privateResolvers);
- mySupplemental = CFArrayGetValueAtIndex(supplemental, i);
- domain = CFDictionaryGetValue(mySupplemental, kSCPropNetDNSDomainName);
- if (isA_CFString(domain)) {
- if (CFEqual(myDomain, domain)) {
- sharedDomain = TRUE;
- break;
- }
+ // add the "default" resolver
- if (CFStringHasSuffix(myDomain, domain)) {
- CFIndex dotIndex;
+ if (defaultResolver != NULL) {
+ CFArrayRef servers;
- dotIndex = CFStringGetLength(myDomain) - CFStringGetLength(domain) - 1;
- if (dotIndex > 0) {
- UniChar dot;
+ servers = CFDictionaryGetValue(defaultResolver, kSCPropNetDNSServerAddresses);
+ if (!isA_CFArray(servers) || (CFArrayGetCount(servers) == 0)) {
+ // if no DNS server addresses
+ defaultResolver = NULL;
+ }
+ }
- dot = CFStringGetCharacterAtIndex(myDomain, dotIndex);
- if (dot == (UniChar)'.') {
- sharedDomain = TRUE;
- break;
- }
- }
- }
- }
+ add_default_resolver(resolvers, defaultResolver, &myOrderAdded, &mySearchDomains);
+
+ // collect (and add) any "multicast" resolver configurations
+
+ add_multicast_resolvers(resolvers, multicastResolvers);
+
+ // collect (and add) any "scoped" resolver configurations
+
+ add_scoped_resolvers(resolvers, services, serviceOrder);
+
+ // collect (and add) any "service-specific" resolver configurations
+
+ add_service_specific_resolvers(resolvers, services);
+
+ // sort resolvers
+
+ n_resolvers = CFArrayGetCount(resolvers);
+ if (n_resolvers > 1) {
+ CFArraySortValues(resolvers, CFRangeMake(0, n_resolvers), compareDomain, NULL);
+ }
+
+ // cleanup
+
+ for (i = n_resolvers; --i > 0; ) {
+ CFDictionaryRef resolver;
+
+ resolver = CFArrayGetValueAtIndex(resolvers, i);
+ if (!CFDictionaryContainsKey(resolver, kSCPropNetDNSDomainName) &&
+ !CFDictionaryContainsKey(resolver, kSCPropNetDNSSearchDomains) &&
+ !CFDictionaryContainsKey(resolver, kSCPropNetDNSServerAddresses)) {
+ // remove empty resolver
+ CFArrayRemoveValueAtIndex(resolvers, i);
+ n_resolvers--;
}
+ }
+
+ // update the default resolver
- if (!sharedDomain) {
- // if the default resolver domain name is not shared
- CFArrayRemoveValueAtIndex(supplemental, 0);
+ myDefault = CFDictionaryCreateMutableCopy(NULL,
+ 0,
+ CFArrayGetValueAtIndex(resolvers, 0));
+ if (mySearchDomains != NULL) {
+ // add search domains to the default resolver
+ CFDictionarySetValue(myDefault, kSCPropNetDNSSearchDomains, mySearchDomains);
+ CFRelease(mySearchDomains);
+ }
+ if (myOrderAdded && (n_resolvers > 1)) {
+ CFDictionaryRef resolver;
+
+ resolver = CFArrayGetValueAtIndex(resolvers, 1);
+ if (CFDictionaryContainsKey(resolver, kSCPropNetDNSDomainName) ||
+ isScopedConfiguration(resolver)) {
+ // if not a supplemental "default" resolver (a domain name is
+ // present) or if it's a scoped configuration
+ CFDictionaryRemoveValue(myDefault, kSCPropNetDNSSearchOrder);
}
}
+ CFArraySetValueAtIndex(resolvers, 0, myDefault);
+ CFRelease(myDefault);
// establish resolver configuration
- n_supplemental = CFArrayGetCount(supplemental);
- if ((defaultResolver == NULL) && (n_supplemental == 0)) {
+ if ((defaultResolver == NULL) && (n_resolvers <= 1)) {
/*
- * if no default or supplemental resolvers
+ * if no default and no supplemental/scoped resolvers
*/
- if (!_dns_configuration_store(NULL)) {
- SCLog(TRUE, LOG_ERR, CFSTR("set_dns_configuration: could not store configuration"));
- }
+ dns_create_config = NULL;
} else {
- dns_create_config_t _config;
+ uint32_t default_resolver_flags = 0;
+ Boolean have_default_flags = FALSE;
/*
- * if default and/or supplemental resolvers are defined
+ * if default and/or supplemental/scoped resolvers are defined
*/
- _config = _dns_configuration_create();
+ dns_create_config = _dns_configuration_create();
- // add [default] resolver
+ for (i = 0; i < n_resolvers; i++) {
+ Boolean merge_default_flags;
+ CFDictionaryRef resolver;
+ dns_create_resolver_t _resolver;
- if ((n_supplemental == 0) && myOrderAdded) {
- CFDictionaryRemoveValue(myDefault, kSCPropNetDNSSearchOrder);
- }
- resolver = create_resolver(myDefault);
- _dns_configuration_add_resolver(&_config, resolver);
- _dns_resolver_free(&resolver);
+ resolver = CFArrayGetValueAtIndex(resolvers, i);
+
+ merge_default_flags = needsMergeWithDefaultConfiguration(resolver);
+ if (merge_default_flags) {
+ CFMutableDictionaryRef new_resolver;
+
+ if (!have_default_flags) {
+ CFDictionaryApplyFunction(services,
+ add_dns_resolver_flags,
+ &default_resolver_flags);
+ have_default_flags = TRUE;
+ }
- // add [supplemental] resolvers
+ new_resolver = CFDictionaryCreateMutableCopy(NULL, 0, resolver);
+ merge_configuration_flags(new_resolver, default_resolver_flags);
+ resolver = new_resolver;
+ }
- for (i = 0; i < n_supplemental; i++) {
- CFDictionaryRef supplementalResolver;
+ _resolver = create_resolver(resolver);
+ _dns_configuration_add_resolver(&dns_create_config, _resolver);
+ _dns_resolver_free(&_resolver);
- supplementalResolver = CFArrayGetValueAtIndex(supplemental, i);
- resolver = create_resolver(supplementalResolver);
- _dns_configuration_add_resolver(&_config, resolver);
- _dns_resolver_free(&resolver);
+ if (merge_default_flags) {
+ CFRelease(resolver);
+ }
}
- // save configuration
+#if !TARGET_OS_IPHONE
+ // add flatfile resolvers
- if (!_dns_configuration_store(&_config)) {
- SCLog(TRUE, LOG_ERR, CFSTR("set_dns_configuration() failed: could not store configuration"));
+ _dnsinfo_flatfile_set_flags(default_resolver_flags);
+ _dnsinfo_flatfile_add_resolvers(&dns_create_config);
+#endif // !TARGET_OS_IPHONE
+ }
+
+ // check if the configuration changed
+ _dns_configuration_signature(&dns_create_config, signature, sizeof(signature));
+ if (bcmp(signature, signature_last, sizeof(signature)) != 0) {
+ // save [new] signature
+ bcopy(signature, signature_last, sizeof(signature));
+
+ my_log(LOG_INFO, "Updating DNS configuration");
+ if (dns_create_config != NULL) {
+ dns_config_t *dns_config = NULL;
+ _dns_config_buf_t *dns_config_buf;
+ size_t n;
+
+ n = sizeof(_dns_config_buf_t);
+ n += ntohl(((_dns_config_buf_t *)dns_create_config)->n_attribute);
+ dns_config_buf = _dns_configuration_buffer_create((void *)dns_create_config, n);
+ if (dns_config_buf != NULL) {
+ dns_config = _dns_configuration_buffer_expand(dns_config_buf);
+ if (dns_config == NULL) {
+ // if we were unable to expand the configuration
+ _dns_configuration_buffer_free(&dns_config_buf);
+ }
+ }
+
+ if (dns_config != NULL) {
+ _dns_configuration_log(dns_config, TRUE, NULL);
+ free(dns_config);
+ }
+ } else {
+ my_log(LOG_INFO, "*** No DNS configuration");
+ }
+#ifndef MAIN
+ // save [new] configuration
+ if (!_dns_configuration_store(&dns_create_config)) {
+ my_log(LOG_ERR, "could not store configuration");
}
+#endif // MAIN
- _dns_configuration_free(&_config);
+ changed = TRUE;
}
- CFRelease(myDefault);
- CFRelease(supplemental);
+ if (dns_create_config != NULL) {
+ _dns_configuration_free(&dns_create_config);
+ }
- return;
+ CFRelease(resolvers);
+ return changed;
}
+#if !TARGET_OS_IPHONE
+static SCDynamicStoreRef dns_configuration_store;
+static SCDynamicStoreCallBack dns_configuration_callout;
+
static void
-load_predefined_resolvers(CFBundleRef bundle)
+dns_configuration_changed(CFMachPortRef port, void *msg, CFIndex size, void *info)
{
- Boolean ok;
- CFURLRef url;
- CFStringRef xmlError = NULL;
- CFDataRef xmlResolvers = NULL;
-
- url = CFBundleCopyResourceURL(bundle, CFSTR("Resolvers"), CFSTR("plist"), NULL);
- if (url == NULL) {
- return;
+#pragma unused(port)
+#pragma unused(msg)
+#pragma unused(size)
+#pragma unused(info)
+ os_activity_t activity;
+ static const CFStringRef key = CFSTR(_PATH_RESOLVER_DIR);
+ CFArrayRef keys;
+ Boolean resolvers_now;
+ static Boolean resolvers_save = FALSE;
+ struct stat statbuf;
+
+ activity = os_activity_create("processing DNS configuration change",
+ OS_ACTIVITY_CURRENT,
+ OS_ACTIVITY_FLAG_DEFAULT);
+ os_activity_scope(activity);
+
+ resolvers_now = (stat(_PATH_RESOLVER_DIR, &statbuf) == 0);
+ if (!resolvers_save && (resolvers_save == resolvers_now)) {
+ // if we did not (and still do not) have an "/etc/resolvers"
+ // directory than this notification is the result of a change
+ // to the "/etc" directory.
+ goto done;
}
+ resolvers_save = resolvers_now;
+
+ my_log(LOG_INFO, _PATH_RESOLVER_DIR " changed");
+
+ // fake a "DNS" change
+ keys = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks);
+ (*dns_configuration_callout)(dns_configuration_store, keys, NULL);
+ CFRelease(keys);
+
+ done :
+
+ os_release(activity);
+
+ return;
+}
+
- ok = CFURLCreateDataAndPropertiesFromResource(NULL, url, &xmlResolvers, NULL, NULL, NULL);
- CFRelease(url);
- if (!ok || (xmlResolvers == NULL)) {
+__private_extern__
+void
+dns_configuration_monitor(SCDynamicStoreRef store, SCDynamicStoreCallBack callout)
+{
+ CFMachPortRef mp;
+ mach_port_t notify_port;
+ int notify_token;
+ CFRunLoopSourceRef rls;
+ uint32_t status;
+
+ dns_configuration_store = store;
+ dns_configuration_callout = callout;
+
+ status = notify_register_mach_port(_PATH_RESOLVER_DIR, ¬ify_port, 0, ¬ify_token);
+ if (status != NOTIFY_STATUS_OK) {
+ my_log(LOG_ERR, "notify_register_mach_port() failed");
return;
}
- /* convert the XML data into a property list */
- S_predefined = CFPropertyListCreateFromXMLData(NULL, xmlResolvers, kCFPropertyListImmutable, &xmlError);
- CFRelease(xmlResolvers);
- if (S_predefined == NULL) {
- if (xmlError != NULL) {
- SCLog(TRUE, LOG_DEBUG, CFSTR("add_predefined_resolvers: %@"), xmlError);
- CFRelease(xmlError);
- }
+ status = notify_monitor_file(notify_token, "/private" _PATH_RESOLVER_DIR, 0);
+ if (status != NOTIFY_STATUS_OK) {
+ my_log(LOG_ERR, "notify_monitor_file() failed");
+ (void)notify_cancel(notify_token);
return;
}
- if (!isA_CFArray(S_predefined)) {
- CFRelease(S_predefined);
- S_predefined = NULL;
+ mp = _SC_CFMachPortCreateWithPort("IPMonitor/dns_configuration",
+ notify_port,
+ dns_configuration_changed,
+ NULL);
+
+ rls = CFMachPortCreateRunLoopSource(NULL, mp, -1);
+ if (rls == NULL) {
+ my_log(LOG_ERR, "SCDynamicStoreCreateRunLoopSource() failed");
+ CFRelease(mp);
+ (void)notify_cancel(notify_token);
return;
}
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+ CFRelease(rls);
+ CFRelease(mp);
return;
}
+#endif // !TARGET_OS_IPHONE
__private_extern__
void
dns_configuration_init(CFBundleRef bundle)
{
- load_predefined_resolvers(bundle);
+ CFDictionaryRef dict;
+
+ dict = CFBundleGetInfoDictionary(bundle);
+ if (isA_CFDictionary(dict)) {
+ S_mdns_timeout = CFDictionaryGetValue(dict, CFSTR("mdns_timeout"));
+ S_mdns_timeout = isA_CFNumber(S_mdns_timeout);
+
+ S_pdns_timeout = CFDictionaryGetValue(dict, CFSTR("pdns_timeout"));
+ S_pdns_timeout = isA_CFNumber(S_pdns_timeout);
+ }
+
return;
}
+
+#pragma mark -
+#pragma mark Standalone test code
+
+
+#ifdef MAIN
+
+static void
+split(const void * key, const void * value, void * context)
+{
+ CFArrayRef components;
+ CFStringRef entity_id;
+ CFStringRef service_id;
+ CFMutableDictionaryRef state_dict;
+
+ components = CFStringCreateArrayBySeparatingStrings(NULL, (CFStringRef)key, CFSTR("/"));
+ service_id = CFArrayGetValueAtIndex(components, 3);
+ entity_id = CFArrayGetValueAtIndex(components, 4);
+ state_dict = (CFMutableDictionaryRef)CFDictionaryGetValue(context, service_id);
+ if (state_dict != NULL) {
+ state_dict = CFDictionaryCreateMutableCopy(NULL, 0, state_dict);
+ } else {
+ state_dict = CFDictionaryCreateMutable(NULL,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ }
+
+ if (CFEqual(entity_id, kSCEntNetIPv4) ||
+ CFEqual(entity_id, kSCEntNetIPv6)) {
+ CFDictionaryRef dict;
+ CFStringRef interface;
+
+ if (CFEqual(entity_id, kSCEntNetIPv4)) {
+ dict = ipv4_dict_create(value);
+ }
+ else {
+ dict = ipv6_dict_create(value);
+ }
+ if (dict != NULL) {
+ CFDictionarySetValue(state_dict, entity_id, dict);
+ }
+
+ interface = CFDictionaryGetValue((CFDictionaryRef)value, kSCPropInterfaceName);
+ if (interface != NULL) {
+ CFDictionaryRef dns;
+ CFMutableDictionaryRef new_dns;
+
+ dns = CFDictionaryGetValue(state_dict, kSCEntNetDNS);
+ if (dns != NULL) {
+ new_dns = CFDictionaryCreateMutableCopy(NULL, 0, dns);
+ } else {
+ new_dns = CFDictionaryCreateMutable(NULL,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ }
+ CFDictionarySetValue(new_dns, kSCPropInterfaceName, interface);
+ CFDictionarySetValue(state_dict, kSCEntNetDNS, new_dns);
+ CFRelease(new_dns);
+ }
+ } else if (CFEqual(entity_id, kSCEntNetDNS)) {
+ CFDictionaryRef dns;
+
+ dns = CFDictionaryGetValue(state_dict, kSCEntNetDNS);
+ if (dns != NULL) {
+ CFStringRef interface;
+
+ interface = CFDictionaryGetValue(dns, kSCPropInterfaceName);
+ if (interface != NULL) {
+ CFMutableDictionaryRef new_dns;
+
+ new_dns = CFDictionaryCreateMutableCopy(NULL, 0, (CFDictionaryRef)value);
+ CFDictionarySetValue(new_dns, kSCPropInterfaceName, interface);
+ CFDictionarySetValue(state_dict, kSCEntNetDNS, new_dns);
+ CFRelease(new_dns);
+ } else {
+ CFDictionarySetValue(state_dict, kSCEntNetDNS, (CFDictionaryRef)value);
+ }
+ } else {
+ CFDictionarySetValue(state_dict, kSCEntNetDNS, (CFDictionaryRef)value);
+ }
+ } else {
+ CFDictionarySetValue(state_dict, entity_id, (CFDictionaryRef)value);
+ }
+
+ CFDictionarySetValue((CFMutableDictionaryRef)context, service_id, state_dict);
+ CFRelease(state_dict);
+ CFRelease(components);
+
+ return;
+}
+
+int
+main(int argc, char **argv)
+{
+ CFDictionaryRef entities;
+ CFStringRef key;
+ CFArrayRef multicast_resolvers;
+ CFStringRef pattern;
+ CFMutableArrayRef patterns;
+ CFStringRef primary = NULL;
+ CFDictionaryRef primaryDNS = NULL;
+ CFArrayRef private_resolvers;
+ CFArrayRef service_order = NULL;
+ CFMutableDictionaryRef service_state_dict;
+ CFDictionaryRef setup_global_ipv4;
+ CFDictionaryRef state_global_ipv4;
+ SCDynamicStoreRef store;
+
+ _sc_debug = TRUE;
+ _sc_log = FALSE;
+ _sc_verbose = (argc > 1) ? TRUE : FALSE;
+
+ store = SCDynamicStoreCreate(NULL, CFSTR("TEST"), NULL, NULL);
+
+ // get IPv4, IPv6, and DNS entities
+ patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+ pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
+ kSCDynamicStoreDomainState,
+ kSCCompAnyRegex,
+ kSCEntNetIPv4);
+ CFArrayAppendValue(patterns, pattern);
+ CFRelease(pattern);
+ pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
+ kSCDynamicStoreDomainState,
+ kSCCompAnyRegex,
+ kSCEntNetIPv6);
+ CFArrayAppendValue(patterns, pattern);
+ CFRelease(pattern);
+ pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
+ kSCDynamicStoreDomainState,
+ kSCCompAnyRegex,
+ kSCEntNetDNS);
+ CFArrayAppendValue(patterns, pattern);
+ CFRelease(pattern);
+ entities = SCDynamicStoreCopyMultiple(store, NULL, patterns);
+ CFRelease(patterns);
+
+ service_state_dict = CFDictionaryCreateMutable(NULL,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ CFDictionaryApplyFunction(entities, split, service_state_dict);
+ CFRelease(entities);
+
+ // get primary service ID
+ key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
+ kSCDynamicStoreDomainState,
+ kSCEntNetIPv4);
+ state_global_ipv4 = SCDynamicStoreCopyValue(store, key);
+ CFRelease(key);
+ if (state_global_ipv4 != NULL) {
+ primary = CFDictionaryGetValue(state_global_ipv4, kSCDynamicStorePropNetPrimaryService);
+ if (primary != NULL) {
+ CFDictionaryRef service_dict;
+
+ // get DNS configuration for primary service
+ service_dict = CFDictionaryGetValue(service_state_dict, primary);
+ if (service_dict != NULL) {
+ primaryDNS = CFDictionaryGetValue(service_dict, kSCEntNetDNS);
+ }
+ }
+ }
+
+ // get serviceOrder
+ key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
+ kSCDynamicStoreDomainSetup,
+ kSCEntNetIPv4);
+ setup_global_ipv4 = SCDynamicStoreCopyValue(store, key);
+ CFRelease(key);
+ if (setup_global_ipv4 != NULL) {
+ service_order = CFDictionaryGetValue(setup_global_ipv4, kSCPropNetServiceOrder);
+ }
+
+ // get multicast resolvers
+ key = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
+ kSCDynamicStoreDomainState,
+ kSCCompNetwork,
+ CFSTR(kDNSServiceCompMulticastDNS));
+ multicast_resolvers = SCDynamicStoreCopyValue(store, key);
+ CFRelease(key);
+
+ // get private resolvers
+ key = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
+ kSCDynamicStoreDomainState,
+ kSCCompNetwork,
+ CFSTR(kDNSServiceCompPrivateDNS));
+ private_resolvers = SCDynamicStoreCopyValue(store, key);
+ CFRelease(key);
+
+ // update DNS configuration
+ dns_configuration_init(CFBundleGetMainBundle());
+ (void)dns_configuration_set(primaryDNS,
+ service_state_dict,
+ service_order,
+ multicast_resolvers,
+ private_resolvers);
+
+ // cleanup
+ if (setup_global_ipv4 != NULL) CFRelease(setup_global_ipv4);
+ if (state_global_ipv4 != NULL) CFRelease(state_global_ipv4);
+ if (multicast_resolvers != NULL) CFRelease(multicast_resolvers);
+ if (private_resolvers != NULL) CFRelease(private_resolvers);
+ CFRelease(service_state_dict);
+ CFRelease(store);
+
+ /* not reached */
+ exit(0);
+ return 0;
+}
+#endif
+