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>
#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 <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCPrivate.h>
#include <SystemConfiguration/SCValidation.h>
#include <dnsinfo.h>
#include <dnsinfo_create.h>
#include <dns_sd.h>
#ifndef kDNSServiceCompMulticastDNS
#define kDNSServiceCompMulticastDNS "MulticastDNS"
#endif
#ifndef kDNSServiceCompPrivateDNS
#define kDNSServiceCompPrivateDNS "PrivateDNS"
#endif
static CFNumberRef S_mdns_timeout = NULL;
static CFNumberRef S_pdns_timeout = NULL;
static void
add_resolver(CFMutableArrayRef supplemental, CFMutableDictionaryRef resolver)
{
CFIndex i;
CFIndex n_supplemental;
CFNumberRef order;
uint32_t order_val = 0;
order = CFDictionaryGetValue(resolver, kSCPropNetDNSSearchOrder);
if (!isA_CFNumber(order) ||
!CFNumberGetValue(order, kCFNumberIntType, &order_val)) {
order = NULL;
order_val = 0;
}
n_supplemental = CFArrayGetCount(supplemental);
for (i = 0; i < n_supplemental; i++) {
CFDictionaryRef supplemental_resolver;
supplemental_resolver = CFArrayGetValueAtIndex(supplemental, i);
if (CFEqual(resolver, supplemental_resolver)) {
return;
}
if (order != NULL) {
CFMutableDictionaryRef compare;
Boolean match;
compare = CFDictionaryCreateMutableCopy(NULL, 0, supplemental_resolver);
CFDictionarySetValue(compare, kSCPropNetDNSSearchOrder, order);
match = CFEqual(resolver, compare);
CFRelease(compare);
if (match) {
CFNumberRef supplemental_order;
uint32_t supplemental_order_val = 0;
supplemental_order = CFDictionaryGetValue(supplemental_resolver, kSCPropNetDNSSearchOrder);
if (!isA_CFNumber(supplemental_order) ||
!CFNumberGetValue(supplemental_order, kCFNumberIntType, &supplemental_order_val)) {
supplemental_order_val = 0;
}
if (order_val < supplemental_order_val ) {
CFArraySetValueAtIndex(supplemental, i, resolver);
}
return;
}
}
}
order = CFNumberCreate(NULL, kCFNumberIntType, &n_supplemental);
CFDictionarySetValue(resolver, CFSTR("*ORDER*"), order);
CFRelease(order);
CFArrayAppendValue(supplemental, resolver);
return;
}
static void
add_supplemental(CFMutableArrayRef supplemental, CFDictionaryRef dns, uint32_t defaultOrder)
{
CFArrayRef domains;
CFIndex i;
CFIndex n_domains;
CFArrayRef orders;
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;
}
}
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_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);
CFDictionaryRemoveValue(match_resolver, kSCPropNetDNSSortList);
if (CFStringGetLength(match_domain) > 0) {
CFDictionarySetValue(match_resolver, kSCPropNetDNSDomainName, match_domain);
} else {
CFDictionaryRemoveValue(match_resolver, kSCPropNetDNSDomainName);
}
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++; }
add_resolver(supplemental, match_resolver);
CFRelease(match_resolver);
}
return;
}
#define N_QUICK 32
static void
add_supplemental_resolvers(CFMutableArrayRef supplemental, CFDictionaryRef services, CFArrayRef service_order)
{
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;
CFDictionaryRef service = (CFDictionaryRef)vals[i];
if (!isA_CFDictionary(service)) {
continue;
}
dns = CFDictionaryGetValue(service, kSCEntNetDNS);
if (!isA_CFDictionary(dns)) {
continue;
}
defaultOrder = DEFAULT_SEARCH_ORDER
- (DEFAULT_SEARCH_ORDER / 2)
+ ((DEFAULT_SEARCH_ORDER / 1000) * i);
if ((n_order > 0) &&
!CFArrayContainsValue(service_order, CFRangeMake(0, n_order), keys[i])) {
defaultOrder += (DEFAULT_SEARCH_ORDER / 1000) * n_services;
}
add_supplemental(supplemental, dns, defaultOrder);
}
if (keys != keys_q) {
CFAllocatorDeallocate(NULL, keys);
CFAllocatorDeallocate(NULL, vals);
}
return;
}
static void
add_multicast_resolvers(CFMutableArrayRef supplemental, 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);
if (!isA_CFString(domain) || (CFStringGetLength(domain) == 0)) {
continue;
}
defaultOrder = DEFAULT_SEARCH_ORDER
+ (DEFAULT_SEARCH_ORDER / 2)
+ ((DEFAULT_SEARCH_ORDER / 1000) * 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(supplemental, resolver);
CFRelease(resolver);
}
return;
}
static void
add_private_resolvers(CFMutableArrayRef supplemental, 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);
if (!isA_CFString(domain) || (CFStringGetLength(domain) == 0)) {
continue;
}
defaultOrder = DEFAULT_SEARCH_ORDER
- (DEFAULT_SEARCH_ORDER / 4)
+ ((DEFAULT_SEARCH_ORDER / 1000) * 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(supplemental, resolver);
CFRelease(resolver);
}
return;
}
static CFComparisonResult
compareBySearchOrder(const void *val1, const void *val2, void *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, kCFNumberIntType, &order1)) {
order1 = DEFAULT_SEARCH_ORDER;
}
num2 = CFDictionaryGetValue(dns2, kSCPropNetDNSSearchOrder);
if (!isA_CFNumber(num2) ||
!CFNumberGetValue(num2, kCFNumberIntType, &order2)) {
order2 = DEFAULT_SEARCH_ORDER;
}
if (order1 == order2) {
if (CFDictionaryGetValueIfPresent(dns1, CFSTR("*ORDER*"), (const void **)&num1) &&
CFDictionaryGetValueIfPresent(dns2, CFSTR("*ORDER*"), (const void **)&num2) &&
isA_CFNumber(num1) &&
isA_CFNumber(num2) &&
CFNumberGetValue(num1, kCFNumberIntType, &order1) &&
CFNumberGetValue(num2, kCFNumberIntType, &order2)) {
if (order1 == order2) {
return kCFCompareEqualTo;
} else {
return (order1 < order2) ? kCFCompareLessThan : kCFCompareGreaterThan;
}
}
return kCFCompareEqualTo;
}
return (order1 < order2) ? kCFCompareLessThan : kCFCompareGreaterThan;
}
static CFStringRef
trimDomain(CFStringRef domain)
{
CFIndex length;
if (!isA_CFString(domain)) {
return NULL;
}
length = CFStringGetLength(domain);
if ((length > 0) &&
(CFStringFindWithOptions(domain,
CFSTR("."),
CFRangeMake(0, 1),
kCFCompareAnchored,
NULL) ||
CFStringFindWithOptions(domain,
CFSTR("."),
CFRangeMake(0, length),
kCFCompareAnchored|kCFCompareBackwards,
NULL))) {
CFMutableStringRef trimmed;
trimmed = CFStringCreateMutableCopy(NULL, 0, domain);
CFStringTrim(trimmed, CFSTR("."));
domain = (CFStringRef)trimmed;
length = CFStringGetLength(domain);
} else {
CFRetain(domain);
}
if (length == 0) {
CFRelease(domain);
domain = NULL;
}
return domain;
}
static void
update_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 = NULL;
CFIndex n_supplemental;
Boolean searchDomainAdded = FALSE;
n_supplemental = CFArrayGetCount(supplemental);
if (n_supplemental == 0) {
return;
}
if (*defaultDomain != NULL) {
CFNumberRef num;
num = CFDictionaryGetValue(*defaultDomain, kSCPropNetDNSSearchOrder);
if (!isA_CFNumber(num) ||
!CFNumberGetValue(num, kCFNumberIntType, &defaultOrder)) {
defaultOrder = DEFAULT_SEARCH_ORDER;
}
defaultDomainName = CFDictionaryGetValue(*defaultDomain, kSCPropNetDNSDomainName);
defaultSearchDomains = CFDictionaryGetValue(*defaultDomain, kSCPropNetDNSSearchDomains);
}
mySearchDomains = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (isA_CFArray(defaultSearchDomains)) {
CFIndex n_search;
n_search = CFArrayGetCount(defaultSearchDomains);
for (i = 0; i < n_search; i++) {
CFStringRef search;
search = CFArrayGetValueAtIndex(defaultSearchDomains, i);
search = 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,
NULL,
0,
kCFStringEncodingUTF8);
CFRelease(defaultDomainName);
for (dp = domain; *dp != '\0'; dp++) {
if (*dp == '.') {
domain_parts++;
}
}
dp = domain;
for (i = LOCALDOMAINPARTS; i <= domain_parts; i++) {
CFStringRef search;
search = CFStringCreateWithCString(NULL,
dp,
kCFStringEncodingUTF8);
CFArrayAppendValue(mySearchDomains, search);
CFRelease(search);
dp = strchr(dp, '.') + 1;
}
CFAllocatorDeallocate(NULL, domain);
}
}
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++) {
CFDictionaryRef dns;
CFIndex domainIndex;
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 = trimDomain(supplementalDomain);
if (supplementalDomain == NULL) {
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, kCFNumberIntType, &supplementalOrder)) {
supplementalOrder = DEFAULT_SEARCH_ORDER;
}
if (supplementalOrder < defaultOrder) {
if (domainIndex != kCFNotFound) {
CFArrayRemoveValueAtIndex(mySearchDomains, domainIndex);
if (domainIndex < defaultSearchIndex) {
defaultSearchIndex--;
}
}
CFArrayInsertValueAtIndex(mySearchDomains,
defaultSearchIndex,
supplementalDomain);
defaultSearchIndex++;
searchDomainAdded = TRUE;
} else {
if (domainIndex == kCFNotFound) {
CFArrayAppendValue(mySearchDomains, supplementalDomain);
searchDomainAdded = TRUE;
}
}
CFRelease(supplementalDomain);
}
if (searchDomainAdded) {
if (*defaultDomain == NULL) {
*defaultDomain = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
}
CFDictionarySetValue(*defaultDomain, kSCPropNetDNSSearchDomains, mySearchDomains);
}
CFRelease(mySearchDomains);
if (mySupplemental != NULL) CFRelease(mySupplemental);
return;
}
static dns_create_resolver_t
create_resolver(CFDictionaryRef dns)
{
CFArrayRef list;
CFNumberRef num;
dns_create_resolver_t _resolver;
CFStringRef str;
_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);
}
}
}
}
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[128];
str = CFArrayGetValueAtIndex(list, i);
if (!isA_CFString(str)) {
continue;
}
if (_SC_cfstring_to_cstring(str, buf, sizeof(buf), kCFStringEncodingASCII) == NULL) {
continue;
}
bzero(&addr, sizeof(addr));
if (inet_aton(buf, &addr.sin.sin_addr) == 1) {
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) {
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 {
continue;
}
}
}
num = CFDictionaryGetValue(dns, kSCPropNetDNSSearchOrder);
if (isA_CFNumber(num)) {
uint32_t order;
if (CFNumberGetValue(num, kCFNumberIntType, &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';
}
bzero(&sortaddr, 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);
}
}
return _resolver;
}
__private_extern__
void
dns_configuration_set(CFDictionaryRef defaultResolver,
CFDictionaryRef services,
CFArrayRef serviceOrder,
CFArrayRef multicastResolvers,
CFArrayRef privateResolvers)
{
CFIndex i;
CFMutableDictionaryRef myDefault;
uint32_t myOrder = DEFAULT_SEARCH_ORDER;
Boolean myOrderAdded = FALSE;
CFIndex n_supplemental;
CFNumberRef order;
dns_create_resolver_t resolver;
CFMutableArrayRef supplemental;
#ifdef SPECIAL_HANDLING_FOR_DEFAULT_DOMAIN
CFStringRef myDomain = NULL;
CFArrayRef search;
#endif // SPECIAL_HANDLING_FOR_DEFAULT_DOMAIN
if (defaultResolver == NULL) {
myDefault = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
} else {
myDefault = CFDictionaryCreateMutableCopy(NULL, 0, defaultResolver);
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);
}
}
supplemental = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
#ifdef SPECIAL_HANDLING_FOR_DEFAULT_DOMAIN
search = CFDictionaryGetValue(myDefault, kSCPropNetDNSSearchDomains);
if (isA_CFArray(search) && (CFArrayGetCount(search) > 0)) {
myDomain = CFArrayGetValueAtIndex(search, 0);
myDomain = isA_CFString(myDomain);
}
if (myDomain == NULL) {
myDomain = CFDictionaryGetValue(myDefault, kSCPropNetDNSDomainName);
myDomain = isA_CFString(myDomain);
}
if (myDomain != NULL) {
CFMutableDictionaryRef mySupplemental;
mySupplemental = CFDictionaryCreateMutableCopy(NULL, 0, myDefault);
CFDictionarySetValue (mySupplemental, kSCPropNetDNSDomainName, myDomain);
CFDictionaryRemoveValue(mySupplemental, kSCPropNetDNSSearchDomains);
CFDictionaryRemoveValue(mySupplemental, kSCPropNetDNSSupplementalMatchDomains);
CFDictionaryRemoveValue(mySupplemental, kSCPropNetDNSSupplementalMatchOrders);
add_resolver(supplemental, mySupplemental);
CFRelease(mySupplemental);
}
#endif // SPECIAL_HANDLING_FOR_DEFAULT_DOMAIN
add_supplemental_resolvers(supplemental, services, serviceOrder);
add_private_resolvers(supplemental, privateResolvers);
update_search_domains(&myDefault, supplemental);
add_multicast_resolvers(supplemental, multicastResolvers);
#ifdef SPECIAL_HANDLING_FOR_DEFAULT_DOMAIN
if (myDomain != NULL) {
Boolean sharedDomain = FALSE;
n_supplemental = CFArrayGetCount(supplemental);
for (i = 1; i < n_supplemental; i++) {
CFStringRef domain;
CFDictionaryRef mySupplemental;
mySupplemental = CFArrayGetValueAtIndex(supplemental, i);
domain = CFDictionaryGetValue(mySupplemental, kSCPropNetDNSDomainName);
if (isA_CFString(domain)) {
if (CFEqual(myDomain, domain)) {
sharedDomain = TRUE;
break;
}
if (CFStringHasSuffix(myDomain, domain)) {
CFIndex dotIndex;
dotIndex = CFStringGetLength(myDomain) - CFStringGetLength(domain) - 1;
if (dotIndex > 0) {
UniChar dot;
dot = CFStringGetCharacterAtIndex(myDomain, dotIndex);
if (dot == (UniChar)'.') {
sharedDomain = TRUE;
break;
}
}
}
}
}
if (!sharedDomain) {
CFArrayRemoveValueAtIndex(supplemental, 0);
}
}
#endif // SPECIAL_HANDLING_FOR_DEFAULT_DOMAIN
n_supplemental = CFArrayGetCount(supplemental);
if ((defaultResolver == NULL) && (n_supplemental == 0)) {
if (!_dns_configuration_store(NULL)) {
SCLog(TRUE, LOG_ERR, CFSTR("dns_configuration_set: could not store configuration"));
}
} else {
dns_create_config_t _config;
_config = _dns_configuration_create();
if ((n_supplemental == 0) && myOrderAdded) {
CFDictionaryRemoveValue(myDefault, kSCPropNetDNSSearchOrder);
}
resolver = create_resolver(myDefault);
_dns_configuration_add_resolver(&_config, resolver);
_dns_resolver_free(&resolver);
for (i = 0; i < n_supplemental; i++) {
CFDictionaryRef supplementalResolver;
supplementalResolver = CFArrayGetValueAtIndex(supplemental, i);
resolver = create_resolver(supplementalResolver);
_dns_configuration_add_resolver(&_config, resolver);
_dns_resolver_free(&resolver);
}
#if !TARGET_OS_IPHONE
_dnsinfo_flatfile_add_resolvers(&_config);
#endif // !TARGET_OS_IPHONE
if (!_dns_configuration_store(&_config)) {
SCLog(TRUE, LOG_ERR, CFSTR("dns_configuration_set: could not store configuration"));
}
_dns_configuration_free(&_config);
}
CFRelease(myDefault);
CFRelease(supplemental);
return;
}
#if !TARGET_OS_IPHONE
static SCDynamicStoreRef dns_configuration_store;
static SCDynamicStoreCallBack dns_configuration_callout;
static void
dns_configuration_changed(CFMachPortRef port, void *msg, CFIndex size, void *info)
{
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;
SCLog(TRUE, LOG_DEBUG, CFSTR(_PATH_RESOLVER_DIR " changed"));
keys = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks);
(*dns_configuration_callout)(dns_configuration_store, keys, NULL);
CFRelease(keys);
return;
}
__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) {
SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("notify_register_mach_port() failed"));
return;
}
status = notify_monitor_file(notify_token, "/private" _PATH_RESOLVER_DIR, 0);
if (status != NOTIFY_STATUS_OK) {
SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("notify_monitor_file() failed"));
(void)notify_cancel(notify_token);
return;
}
mp = CFMachPortCreateWithPort(NULL, notify_port, dns_configuration_changed, NULL, NULL);
if (mp == NULL) {
SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("CFMachPortCreateWithPort() failed"));
(void)notify_cancel(notify_token);
return;
}
rls = CFMachPortCreateRunLoopSource(NULL, mp, -1);
if (rls == NULL) {
SCLOG(NULL, NULL, ASL_LEVEL_ERR, CFSTR("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)
{
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;
}
#ifdef MAIN
#undef 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 = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
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_log = FALSE;
_sc_verbose = (argc > 1) ? TRUE : FALSE;
store = SCDynamicStoreCreate(NULL, CFSTR("TEST"), NULL, NULL);
pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainState,
kSCCompAnyRegex,
kSCEntNetDNS);
patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
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());
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