dns-configuration.c [plain text]
#include <TargetConditionals.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <notify.h>
extern uint32_t notify_monitor_file(int token, const char *name, int flags);
#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_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__")
static CFNumberRef S_mdns_timeout = NULL;
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)
{
if (((resolver_flags & DNS_RESOLVER_FLAGS_REQUEST_A_RECORDS) == 0) &&
service_is_routable(service, AF_INET)) {
resolver_flags |= DNS_RESOLVER_FLAGS_REQUEST_A_RECORDS;
}
if (((resolver_flags & DNS_RESOLVER_FLAGS_REQUEST_AAAA_RECORDS) == 0) &&
service_is_routable(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;
uint32_t *resolver_flags = (uint32_t *)context;
if (service_is_scoped_only(service)) {
return;
}
*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)) {
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;
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 ) {
CFArraySetValueAtIndex(resolvers, i, resolver);
}
return;
}
}
}
order = CFNumberCreate(NULL, kCFNumberCFIndexType, &n_resolvers);
CFDictionarySetValue(resolver, DNS_CONFIGURATION_ORDER_KEY, order);
CFRelease(order);
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) &&
(
(CFDictionaryGetValueIfPresent(resolver, DNS_CONFIGURATION_FLAGS_KEY, (const void **)&num) &&
isA_CFNumber(num) &&
CFNumberGetValue(num, kCFNumberSInt32Type, &flags) &&
(flags & DNS_RESOLVER_FLAGS_SCOPED) != 0)
||
(CFDictionaryGetValueIfPresent(resolver, DNS_CONFIGURATION_SCOPED_QUERY_KEY, (const void **)&val) &&
isA_CFBoolean(val) &&
CFBooleanGetValue(val))
)
) {
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 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) {
return;
}
orders = CFDictionaryGetValue(dns, kSCPropNetDNSSupplementalMatchOrders);
if (orders != NULL) {
if (!isA_CFArray(orders) || (n_domains != CFArrayGetCount(orders))) {
return;
}
}
servers = CFDictionaryGetValue(dns, kSCPropNetDNSServerAddresses);
if (!isA_CFArray(servers) || (CFArrayGetCount(servers) == 0)) {
return;
}
for (i = 0; i < n_domains; i++) {
CFStringRef match_domain;
CFNumberRef match_order;
CFMutableDictionaryRef match_resolver;
match_domain = CFArrayGetValueAtIndex(domains, i);
if (!isA_CFString(match_domain)) {
continue;
}
match_resolver = CFDictionaryCreateMutableCopy(NULL, 0, dns);
if (CFStringGetLength(match_domain) > 0) {
CFDictionarySetValue(match_resolver, kSCPropNetDNSDomainName, match_domain);
} else {
CFDictionaryRemoveValue(match_resolver, kSCPropNetDNSDomainName);
}
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)) {
CFNumberRef num;
num = CFNumberCreate(NULL, kCFNumberIntType, &defaultOrder);
CFDictionarySetValue(match_resolver, kSCPropNetDNSSearchOrder, num);
CFRelease(num);
defaultOrder++; }
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
merge_configuration_flags(CFMutableDictionaryRef newDNS, uint32_t mergeFlags)
{
uint32_t flags;
CFNumberRef num;
if (!CFDictionaryGetValueIfPresent(newDNS, DNS_CONFIGURATION_FLAGS_KEY, (const void **)&num) ||
!isA_CFNumber(num) ||
!CFNumberGetValue(num, kCFNumberSInt32Type, &flags)) {
flags = 0;
}
flags |= mergeFlags;
num = CFNumberCreate(NULL, kCFNumberSInt32Type, &flags);
CFDictionarySetValue(newDNS, DNS_CONFIGURATION_FLAGS_KEY, num);
CFRelease(num);
return;
}
static void
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;
CFIndex i;
CFIndex n_order;
CFIndex n_services;
const void * vals_q[N_QUICK];
const void ** vals = vals_q;
n_services = isA_CFDictionary(services) ? CFDictionaryGetCount(services) : 0;
if (n_services == 0) {
return; }
if (n_services > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
keys = CFAllocatorAllocate(NULL, n_services * sizeof(CFTypeRef), 0);
vals = CFAllocatorAllocate(NULL, n_services * sizeof(CFTypeRef), 0);
}
n_order = isA_CFArray(service_order) ? CFArrayGetCount(service_order) : 0;
CFDictionaryGetKeysAndValues(services, keys, vals);
for (i = 0; i < n_services; 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;
if (!isA_CFDictionary(service)) {
continue;
}
dns = CFDictionaryGetValue(service, kSCEntNetDNS);
dns = isA_CFDictionary(dns);
if (dns == NULL) {
continue;
}
interface = CFDictionaryGetValue(dns, kSCPropInterfaceName);
if (scoped_interface != NULL) {
if (CFDictionaryContainsKey(service, kSCEntNetIPv4) ||
CFDictionaryContainsKey(service, kSCEntNetIPv6)) {
continue;
}
if (!_SC_CFEqual(interface, CFSTR("*")) &&
!_SC_CFEqual(interface, scoped_interface)) {
continue;
}
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])) {
defaultOrder += (DEFAULT_SEARCH_ORDER / 1000) * n_services;
}
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)) {
trusted = TRUE;
} else {
CFDictionaryRemoveValue(newDNS, kSCPropInterfaceName);
}
}
newFlags = DNS_RESOLVER_FLAGS_SUPPLEMENTAL;
if (scoped_interface != NULL) {
newFlags |= DNS_RESOLVER_FLAGS_SCOPED;
newFlags |= dns_resolver_flags;
} else if (trusted) {
newFlags |= dns_resolver_flags_service(service, 0);
}
merge_configuration_flags(newDNS, newFlags);
add_supplemental(resolvers, serviceID, newDNS, defaultOrder, (scoped_interface != NULL));
CFRelease(newDNS);
}
if (keys != keys_q) {
CFAllocatorDeallocate(NULL, keys);
CFAllocatorDeallocate(NULL, vals);
}
return;
}
static void
add_multicast_resolvers(CFMutableArrayRef resolvers, CFArrayRef multicastResolvers)
{
CFIndex i;
CFIndex n;
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;
}
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);
}
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;
}
static CFComparisonResult
compareBySearchOrder(const void *val1, const void *val2, void *context)
{
#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;
num1 = CFDictionaryGetValue(dns1, kSCPropNetDNSSearchOrder);
if (!isA_CFNumber(num1) ||
!CFNumberGetValue(num1, kCFNumberSInt32Type, &order1)) {
order1 = DEFAULT_SEARCH_ORDER;
}
num2 = CFDictionaryGetValue(dns2, kSCPropNetDNSSearchOrder);
if (!isA_CFNumber(num2) ||
!CFNumberGetValue(num2, kCFNumberSInt32Type, &order2)) {
order2 = DEFAULT_SEARCH_ORDER;
}
if (order1 == order2) {
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;
}
}
return kCFCompareEqualTo;
}
return (order1 < order2) ? kCFCompareLessThan : kCFCompareGreaterThan;
}
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;
CFMutableArrayRef mySearchDomains;
CFMutableArrayRef mySupplemental = NULL;
CFIndex n_supplemental;
CFStringRef trimmedDomainName;
mySearchDomains = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (defaultDomain != NULL) {
CFNumberRef num;
num = CFDictionaryGetValue(defaultDomain, kSCPropNetDNSSearchOrder);
if (!isA_CFNumber(num) ||
!CFNumberGetValue(num, kCFNumberSInt32Type, &defaultOrder)) {
defaultOrder = DEFAULT_SEARCH_ORDER;
}
defaultDomainName = CFDictionaryGetValue(defaultDomain, kSCPropNetDNSDomainName);
defaultSearchDomains = CFDictionaryGetValue(defaultDomain, kSCPropNetDNSSearchDomains);
}
if (isA_CFArray(defaultSearchDomains)) {
CFIndex n_search;
n_search = CFArrayGetCount(defaultSearchDomains);
for (int i = 0; i < n_search; i++) {
CFStringRef search;
search = CFArrayGetValueAtIndex(defaultSearchDomains, i);
search = _SC_trimDomain(search);
if (search != NULL) {
CFArrayAppendValue(mySearchDomains, search);
CFRelease(search);
}
}
} else {
trimmedDomainName = _SC_trimDomain(defaultDomainName);
#ifdef PERFORM_DOMAIN_EXPANSION
if ((trimmedDomainName != NULL) &&
CFStringHasSuffix(defaultDomainName, CFSTR("."))) {
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(trimmedDomainName);
for (dp = domain; *dp != '\0'; dp++) {
if (*dp == '.') {
domain_parts++;
}
}
dp = domain;
do {
CFStringRef 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
if (trimmedDomainName != NULL) {
CFArrayAppendValue(mySearchDomains, trimmedDomainName);
CFRelease(trimmedDomainName);
}
#endif // PERFORM_DOMAIN_EXPANSION
}
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 (int i = 0; i < n_supplemental; i++) {
CFDictionaryRef dns;
CFIndex domainIndex;
int noSearch;
CFNumberRef num;
CFStringRef options;
CFStringRef supplementalDomain;
uint32_t supplementalOrder;
dns = CFArrayGetValueAtIndex(supplemental, i);
options = CFDictionaryGetValue(dns, kSCPropNetDNSOptions);
if (isA_CFString(options)) {
CFRange range;
if (CFEqual(options, CFSTR("pdns"))) {
continue;
}
range = CFStringFind(options, CFSTR("interface="), 0);
if (range.location != kCFNotFound) {
continue;
}
}
supplementalDomain = CFDictionaryGetValue(dns, kSCPropNetDNSDomainName);
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);
continue;
}
domainIndex = CFArrayGetFirstIndexOfValue(mySearchDomains,
CFRangeMake(0, CFArrayGetCount(mySearchDomains)),
supplementalDomain);
num = CFDictionaryGetValue(dns, kSCPropNetDNSSearchOrder);
if (!isA_CFNumber(num) ||
!CFNumberGetValue(num, kCFNumberSInt32Type, &supplementalOrder)) {
supplementalOrder = DEFAULT_SEARCH_ORDER;
}
if (supplementalOrder < defaultOrder) {
if (domainIndex != kCFNotFound) {
CFArrayRemoveValueAtIndex(mySearchDomains, domainIndex);
if (domainIndex < defaultSearchIndex) {
defaultSearchIndex--;
}
}
CFArrayInsertValueAtIndex(mySearchDomains,
defaultSearchIndex,
supplementalDomain);
defaultSearchIndex++;
} else {
if (domainIndex == kCFNotFound) {
CFArrayAppendValue(mySearchDomains, supplementalDomain);
}
}
CFRelease(supplementalDomain);
}
if (mySupplemental != NULL) CFRelease(mySupplemental);
if (CFArrayGetCount(mySearchDomains) == 0) {
CFRelease(mySearchDomains);
mySearchDomains = NULL;
}
CFDictionaryRemoveValue(defaultDomain, kSCPropNetDNSDomainName);
CFDictionaryRemoveValue(defaultDomain, kSCPropNetDNSSearchDomains);
return mySearchDomains;
}
static void
add_scoped_resolvers(CFMutableArrayRef scoped,
CFDictionaryRef services,
CFArrayRef service_order)
{
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; }
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);
}
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)) {
continue;
}
dns = CFDictionaryGetValue(service, kSCEntNetDNS);
if (!isA_CFDictionary(dns)) {
continue;
}
servers = CFDictionaryGetValue(dns, kSCPropNetDNSServerAddresses);
if (!isA_CFArray(servers) || (CFArrayGetCount(servers) == 0)) {
continue;
}
interface = CFDictionaryGetValue(dns, kSCPropInterfaceName);
if ((interface == NULL) || CFEqual(interface, CFSTR("*"))) {
continue;
}
if (CFDictionaryContainsKey(dns, kSCPropNetDNSServiceIdentifier)) {
continue;
}
if (CFSetContainsValue(seen, interface)) {
continue;
}
CFSetSetValue(seen, interface);
if ((_SC_cfstring_to_cstring(interface,
if_name,
sizeof(if_name),
kCFStringEncodingASCII) == NULL) ||
(my_if_nametoindex(if_name) == 0)) {
continue;
}
newDNS = CFDictionaryCreateMutableCopy(NULL, 0, dns);
searchDomains = extract_search_domains(newDNS, NULL);
if (searchDomains != NULL) {
CFDictionarySetValue(newDNS, kSCPropNetDNSSearchDomains, searchDomains);
CFRelease(searchDomains);
}
dns_resolver_flags = dns_resolver_flags_service(service, 0);
if (dns_resolver_flags == 0) {
goto skip;
}
newFlags = DNS_RESOLVER_FLAGS_SCOPED;
newFlags |= dns_resolver_flags;
merge_configuration_flags(newDNS, newFlags);
CFDictionaryRemoveValue(newDNS, kSCPropNetDNSSupplementalMatchDomains);
CFDictionaryRemoveValue(newDNS, kSCPropNetDNSSupplementalMatchOrders);
add_resolver_signature(newDNS, "Scoped", serviceID, 0);
add_resolver(scoped, newDNS);
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 (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)) {
continue;
}
dns_service_identifier = CFDictionaryGetValue(dns, kSCPropNetDNSServiceIdentifier);
if (!isA_CFNumber(dns_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);
newFlags |= DNS_RESOLVER_FLAGS_REQUEST_ALL_RECORDS;
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;
CFDictionarySetValue(newDNS, DNS_CONFIGURATION_SCOPED_QUERY_KEY, kCFBooleanTrue);
interfaceScopedDNS = CFDictionaryCreateMutableCopy(NULL, 0, newDNS);
interfaceScopedFlags = newFlags;
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);
}
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);
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;
}
*searchDomains = extract_search_domains(myDefault, resolvers);
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();
str = CFDictionaryGetValue(dns, kSCPropNetDNSDomainName);
if (isA_CFString(str) && (CFStringGetLength(str) > 0)) {
char domain[NS_MAXDNAME];
if (_SC_cfstring_to_cstring(str, domain, sizeof(domain), kCFStringEncodingUTF8) != NULL) {
_dns_resolver_set_domain(&_resolver, domain);
}
}
list = CFDictionaryGetValue(dns, kSCPropNetDNSSearchDomains);
if (isA_CFArray(list)) {
CFIndex i;
CFIndex n = CFArrayGetCount(list);
for (i = 0; i < n; i++) {
str = CFArrayGetValueAtIndex(list, i);
if (isA_CFString(str) && (CFStringGetLength(str) > 0)) {
char search[NS_MAXDNAME];
if (_SC_cfstring_to_cstring(str, search, sizeof(search), kCFStringEncodingUTF8) != NULL) {
_dns_resolver_add_search(&_resolver, search);
}
}
}
}
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);
}
}
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);
}
}
list = CFDictionaryGetValue(dns, kSCPropNetDNSServerAddresses);
if (isA_CFArray(list)) {
CFIndex i;
CFIndex n = CFArrayGetCount(list);
for (i = 0; i < n; i++) {
union {
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
} addr;
char buf[64];
str = CFArrayGetValueAtIndex(list, i);
if (!isA_CFString(str)) {
continue;
}
if (_SC_cfstring_to_cstring(str, buf, sizeof(buf), kCFStringEncodingASCII) == NULL) {
continue;
}
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)) {
addr.sin6.sin6_scope_id = targetInterfaceIndex;
}
_dns_resolver_add_nameserver(&_resolver, &addr.sa);
}
}
num = CFDictionaryGetValue(dns, kSCPropNetDNSSearchOrder);
if (isA_CFNumber(num)) {
uint32_t order;
if (CFNumberGetValue(num, kCFNumberSInt32Type, &order)) {
_dns_resolver_set_order(&_resolver, order);
}
}
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';
}
memset(&sortaddr, 0, sizeof(sortaddr));
if (inet_aton(buf, &sortaddr.address) != 1) {
continue;
}
if (slash != NULL) {
if (inet_aton(slash + 1, &sortaddr.mask) != 1) {
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);
}
}
num = CFDictionaryGetValue(dns, kSCPropNetDNSServerPort);
if (isA_CFNumber(num)) {
int port;
if (CFNumberGetValue(num, kCFNumberIntType, &port)) {
_dns_resolver_set_port(&_resolver, (uint16_t)port);
}
}
num = CFDictionaryGetValue(dns, kSCPropNetDNSServerTimeout);
if (isA_CFNumber(num)) {
int timeout;
if (CFNumberGetValue(num, kCFNumberIntType, &timeout)) {
_dns_resolver_set_timeout(&_resolver, (uint32_t)timeout);
}
}
str = CFDictionaryGetValue(dns, kSCPropNetDNSOptions);
if (isA_CFString(str)) {
char *options;
options = _SC_cfstring_to_cstring(str, NULL, 0, kCFStringEncodingUTF8);
if (options != NULL) {
_dns_resolver_set_options(&_resolver, options);
CFAllocatorDeallocate(NULL, options);
}
}
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);
}
}
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;
}
static __inline__ Boolean
isScopedConfiguration(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) &&
((flags & DNS_RESOLVER_FLAGS_SCOPED) != 0)) {
return TRUE;
}
return FALSE;
}
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;
domain1 = CFDictionaryGetValue(dns1, kSCPropNetDNSDomainName);
domain2 = CFDictionaryGetValue(dns2, kSCPropNetDNSDomainName);
if (domain1 == NULL) {
return kCFCompareLessThan;
} else if (domain2 == NULL) {
return kCFCompareGreaterThan;
}
scoped1 = isScopedConfiguration(dns1);
scoped2 = isScopedConfiguration(dns2);
if (scoped1 != scoped2) {
if (!scoped1) {
return kCFCompareLessThan;
} else {
return kCFCompareGreaterThan;
}
}
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);
labels2 = CFStringCreateArrayBySeparatingStrings(NULL, domain2, CFSTR("."));
n2 = CFArrayGetCount(labels2);
while ((n1 > 0) && (n2 > 0)) {
CFStringRef label1 = CFArrayGetValueAtIndex(labels1, --n1);
CFStringRef label2 = CFArrayGetValueAtIndex(labels2, --n2);
result = CFStringCompare(label1, label2, kCFCompareCaseInsensitive);
if (result != kCFCompareEqualTo) {
goto done;
}
}
if (n1 > n2) {
result = kCFCompareLessThan;
goto done;
} else if (n1 < n2) {
result = kCFCompareGreaterThan;
goto done;
}
result = compareBySearchOrder(val1, val2, context);
done :
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)) {
if (dns_resolver_flags_all_queries(flags)) {
return FALSE;
}
if (((flags & DNS_RESOLVER_FLAGS_SCOPED ) != 0) ||
((flags & DNS_RESOLVER_FLAGS_SERVICE_SPECIFIC) != 0)) {
return FALSE;
}
}
return TRUE;
}
__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_SHA256_DIGEST_LENGTH];
static unsigned char signature_last[CC_SHA256_DIGEST_LENGTH];
resolvers = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
assert(resolvers != NULL);
add_supplemental_resolvers(resolvers, services, serviceOrder, NULL, NULL);
add_private_resolvers(resolvers, privateResolvers);
if (defaultResolver != NULL) {
CFArrayRef servers;
servers = CFDictionaryGetValue(defaultResolver, kSCPropNetDNSServerAddresses);
if (!isA_CFArray(servers) || (CFArrayGetCount(servers) == 0)) {
defaultResolver = NULL;
}
}
add_default_resolver(resolvers, defaultResolver, &myOrderAdded, &mySearchDomains);
add_multicast_resolvers(resolvers, multicastResolvers);
add_scoped_resolvers(resolvers, services, serviceOrder);
add_service_specific_resolvers(resolvers, services);
n_resolvers = CFArrayGetCount(resolvers);
if (n_resolvers > 1) {
CFArraySortValues(resolvers, CFRangeMake(0, n_resolvers), compareDomain, NULL);
}
for (i = n_resolvers; --i > 0; ) {
CFDictionaryRef resolver;
resolver = CFArrayGetValueAtIndex(resolvers, i);
if (!CFDictionaryContainsKey(resolver, kSCPropNetDNSDomainName) &&
!CFDictionaryContainsKey(resolver, kSCPropNetDNSSearchDomains) &&
!CFDictionaryContainsKey(resolver, kSCPropNetDNSServerAddresses)) {
CFArrayRemoveValueAtIndex(resolvers, i);
n_resolvers--;
}
}
myDefault = CFDictionaryCreateMutableCopy(NULL,
0,
CFArrayGetValueAtIndex(resolvers, 0));
if (mySearchDomains != NULL) {
CFDictionarySetValue(myDefault, kSCPropNetDNSSearchDomains, mySearchDomains);
CFRelease(mySearchDomains);
}
if (myOrderAdded && (n_resolvers > 1)) {
CFDictionaryRef resolver;
resolver = CFArrayGetValueAtIndex(resolvers, 1);
if (CFDictionaryContainsKey(resolver, kSCPropNetDNSDomainName) ||
isScopedConfiguration(resolver)) {
CFDictionaryRemoveValue(myDefault, kSCPropNetDNSSearchOrder);
}
}
CFArraySetValueAtIndex(resolvers, 0, myDefault);
CFRelease(myDefault);
if ((defaultResolver == NULL) && (n_resolvers <= 1)) {
dns_create_config = NULL;
} else {
uint32_t default_resolver_flags = 0;
Boolean have_default_flags = FALSE;
dns_create_config = _dns_configuration_create();
for (i = 0; i < n_resolvers; i++) {
Boolean merge_default_flags;
CFDictionaryRef resolver;
dns_create_resolver_t _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;
}
new_resolver = CFDictionaryCreateMutableCopy(NULL, 0, resolver);
merge_configuration_flags(new_resolver, default_resolver_flags);
resolver = new_resolver;
}
_resolver = create_resolver(resolver);
_dns_configuration_add_resolver(&dns_create_config, _resolver);
_dns_resolver_free(&_resolver);
if (merge_default_flags) {
CFRelease(resolver);
}
}
_dnsinfo_flatfile_set_flags(default_resolver_flags);
_dnsinfo_flatfile_add_resolvers(&dns_create_config);
}
_dns_configuration_signature(&dns_create_config, signature, sizeof(signature));
if (bcmp(signature, signature_last, sizeof(signature)) != 0) {
memcpy(signature_last, signature, 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) {
_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
if (!_dns_configuration_store(&dns_create_config)) {
my_log(LOG_ERR, "could not store configuration");
}
#endif // MAIN
changed = TRUE;
}
if (dns_create_config != NULL) {
_dns_configuration_free(&dns_create_config);
}
CFRelease(resolvers);
return changed;
}
static SCDynamicStoreRef dns_configuration_store;
static SCDynamicStoreCallBack dns_configuration_callout;
static void
dns_configuration_changed(CFMachPortRef port, void *msg, CFIndex size, void *info)
{
#pragma unused(port)
#pragma unused(msg)
#pragma unused(size)
#pragma unused(info)
static const CFStringRef key = CFSTR(_PATH_RESOLVER_DIR);
CFArrayRef keys;
Boolean resolvers_now;
static Boolean resolvers_save = FALSE;
struct stat statbuf;
resolvers_now = (stat(_PATH_RESOLVER_DIR, &statbuf) == 0);
if (!resolvers_save && (resolvers_save == resolvers_now)) {
return;
}
resolvers_save = resolvers_now;
my_log(LOG_INFO, _PATH_RESOLVER_DIR " changed");
keys = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks);
(*dns_configuration_callout)(dns_configuration_store, keys, NULL);
CFRelease(keys);
return;
}
static Boolean
normalize_path(const char *file_name, char resolved_name[PATH_MAX])
{
char *ch;
char path[PATH_MAX];
strlcpy(path, file_name, sizeof(path));
if (realpath(path, resolved_name) != NULL) {
return TRUE;
}
ch = strrchr(path, '/');
if (ch != NULL) {
*ch = '\0';
if (realpath(path, resolved_name) != NULL) {
strlcat(resolved_name, "/", PATH_MAX);
strlcat(resolved_name, ch+1, PATH_MAX);
return TRUE;
}
}
return FALSE;
}
__private_extern__
void
dns_configuration_monitor(SCDynamicStoreRef store, SCDynamicStoreCallBack callout)
{
CFMachPortRef mp;
mach_port_t notify_port;
int notify_token;
char resolver_directory_path[PATH_MAX];
CFRunLoopSourceRef rls;
uint32_t status;
if (!normalize_path(_PATH_RESOLVER_DIR, resolver_directory_path)) {
my_log(LOG_ERR, "Not monitoring \"%s\", could not resolve directory path", _PATH_RESOLVER_DIR);
return;
}
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;
}
status = notify_monitor_file(notify_token, resolver_directory_path, 0);
if (status != NOTIFY_STATUS_OK) {
my_log(LOG_ERR, "notify_monitor_file() failed");
(void)notify_cancel(notify_token);
return;
}
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;
}
__private_extern__
void
dns_configuration_init(CFBundleRef 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);
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);
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;
service_dict = CFDictionaryGetValue(service_state_dict, primary);
if (service_dict != NULL) {
primaryDNS = CFDictionaryGetValue(service_dict, kSCEntNetDNS);
}
}
}
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);
}
key = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
kSCDynamicStoreDomainState,
kSCCompNetwork,
CFSTR(kDNSServiceCompMulticastDNS));
multicast_resolvers = SCDynamicStoreCopyValue(store, key);
CFRelease(key);
key = SCDynamicStoreKeyCreate(NULL, CFSTR("%@/%@/%@"),
kSCDynamicStoreDomainState,
kSCCompNetwork,
CFSTR(kDNSServiceCompPrivateDNS));
private_resolvers = SCDynamicStoreCopyValue(store, key);
CFRelease(key);
dns_configuration_init(CFBundleGetMainBundle());
(void)dns_configuration_set(primaryDNS,
service_state_dict,
service_order,
multicast_resolvers,
private_resolvers);
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);
exit(0);
return 0;
}
#endif