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>
#ifdef MAIN
#undef MAIN
#include "dnsinfo_copy.c"
#define MAIN
#endif // MAIN
#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 resolvers, CFMutableDictionaryRef resolver)
{
CFIndex i;
CFIndex n_resolvers;
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_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, kCFNumberIntType, &match_order_val)) {
match_order_val = 0;
}
if (order_val < match_order_val ) {
CFArraySetValueAtIndex(resolvers, i, resolver);
}
return;
}
}
}
order = CFNumberCreate(NULL, kCFNumberIntType, &n_resolvers);
CFDictionarySetValue(resolver, CFSTR("*ORDER*"), order);
CFRelease(order);
CFArrayAppendValue(resolvers, resolver);
return;
}
static void
add_supplemental(CFMutableArrayRef resolvers, 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_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(resolvers, match_resolver);
CFRelease(match_resolver);
}
return;
}
#define N_QUICK 32
static void
add_supplemental_resolvers(CFMutableArrayRef resolvers, 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(resolvers, dns, defaultOrder);
}
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) * 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(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) * 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(resolvers, resolver);
CFRelease(resolver);
CFRelease(domain);
}
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 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 = NULL;
CFIndex n_supplemental;
mySearchDomains = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
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);
}
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 = _SC_trimDomain(search);
if (search != NULL) {
CFArrayAppendValue(mySearchDomains, search);
CFRelease(search);
}
}
} else {
defaultDomainName = _SC_trimDomain(defaultDomainName);
if (defaultDomainName != NULL) {
CFStringRef defaultOptions;
char *domain;
int domain_parts = 1;
char *dp;
int ndots = 1;
#define NDOTS_OPT "ndots="
#define NDOTS_OPT_LEN (sizeof("ndots=") - 1)
defaultOptions = CFDictionaryGetValue(defaultDomain, kSCPropNetDNSOptions);
if (defaultOptions != NULL) {
char *cp;
char *options;
options = _SC_cfstring_to_cstring(defaultOptions,
NULL,
0,
kCFStringEncodingUTF8);
cp = strstr(options, NDOTS_OPT);
if ((cp != NULL) &&
((cp == options) || isspace(cp[-1])) &&
((cp[NDOTS_OPT_LEN] != '\0') && isdigit(cp[NDOTS_OPT_LEN]))) {
char *end;
long val;
cp += NDOTS_OPT_LEN;
errno = 0;
val = strtol(cp, &end, 10);
if ((*cp != '\0') && (cp != end) && (errno == 0) &&
((*end == '\0') || isspace(*end)) && (val > 0)) {
ndots = val;
}
}
CFAllocatorDeallocate(NULL, options);
}
domain = _SC_cfstring_to_cstring(defaultDomainName,
NULL,
0,
kCFStringEncodingUTF8);
CFRelease(defaultDomainName);
for (dp = domain; *dp != '\0'; dp++) {
if (*dp == '.') {
domain_parts++;
}
}
i = LOCALDOMAINPARTS;
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 (++i <= (domain_parts - ndots));
CFAllocatorDeallocate(NULL, domain);
}
}
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++) {
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 = _SC_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++;
} 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 flags;
unsigned int if_index;
char if_name[IF_NAMESIZE];
CFStringRef interface;
CFMutableDictionaryRef newDNS;
CFNumberRef num;
CFArrayRef searchDomains;
CFDictionaryRef service;
CFStringRef serviceID;
serviceID = CFArrayGetValueAtIndex(order, i);
service = CFDictionaryGetValue(services, serviceID);
if (!isA_CFDictionary(service)) {
continue;
}
dns = CFDictionaryGetValue(service, kSCEntNetDNS);
if (!isA_CFDictionary(dns)) {
continue;
}
interface = CFDictionaryGetValue(dns, kSCPropInterfaceName);
if (interface == NULL) {
continue;
}
if (CFSetContainsValue(seen, interface)) {
continue;
}
CFSetSetValue(seen, interface);
if ((_SC_cfstring_to_cstring(interface,
if_name,
sizeof(if_name),
kCFStringEncodingASCII) == NULL) ||
((if_index = 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);
}
num = CFNumberCreate(NULL, kCFNumberIntType, &if_index);
CFDictionarySetValue(newDNS, CFSTR("*IF_INDEX*"), num);
CFRelease(num);
flags = DNS_RESOLVER_FLAGS_SCOPED;
num = CFNumberCreate(NULL, kCFNumberSInt32Type, &flags);
CFDictionarySetValue(newDNS, CFSTR("*FLAGS*"), num);
CFRelease(num);
CFDictionaryRemoveValue(newDNS, kSCPropNetDNSSupplementalMatchDomains);
CFDictionaryRemoveValue(newDNS, kSCPropNetDNSSupplementalMatchOrders);
add_resolver(scoped, newDNS);
CFRelease(newDNS);
}
CFRelease(seen);
CFRelease(order);
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);
}
order = CFDictionaryGetValue(myDefault, kSCPropNetDNSSearchOrder);
if (!isA_CFNumber(order) ||
!CFNumberGetValue(order, kCFNumberIntType, &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(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;
_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);
}
}
num = CFDictionaryGetValue(dns, CFSTR("*IF_INDEX*"));
if (isA_CFNumber(num)) {
int if_index;
if (CFNumberGetValue(num, kCFNumberIntType, &if_index)) {
_dns_resolver_set_if_index(&_resolver, if_index);
}
}
num = CFDictionaryGetValue(dns, CFSTR("*FLAGS*"));
if (isA_CFNumber(num)) {
uint32_t flags;
if (CFNumberGetValue(num, kCFNumberSInt32Type, &flags)) {
_dns_resolver_set_flags(&_resolver, flags);
}
}
return _resolver;
}
static __inline__ Boolean
isScopedDNS(CFDictionaryRef dns)
{
uint32_t flags;
CFNumberRef num;
if ((dns != NULL) &&
CFDictionaryGetValueIfPresent(dns, CFSTR("*FLAGS*"), (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 = isScopedDNS(dns1);
scoped2 = isScopedDNS(dns2);
if (scoped1 != scoped2) {
if (!scoped1) {
return kCFCompareLessThan;
} else {
return kCFCompareGreaterThan;
}
}
if ((domain1 == NULL) || (domain2 == NULL)) {
return kCFCompareEqualTo;
}
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;
}
__private_extern__
void
dns_configuration_set(CFDictionaryRef defaultResolver,
CFDictionaryRef services,
CFArrayRef serviceOrder,
CFArrayRef multicastResolvers,
CFArrayRef privateResolvers)
{
CFIndex i;
CFMutableDictionaryRef myDefault;
Boolean myOrderAdded = FALSE;
CFArrayRef mySearchDomains = NULL;
CFIndex n_resolvers;
CFMutableArrayRef resolvers;
resolvers = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
add_supplemental_resolvers(resolvers, services, serviceOrder);
add_private_resolvers(resolvers, privateResolvers);
add_default_resolver(resolvers, defaultResolver, &myOrderAdded, &mySearchDomains);
add_multicast_resolvers(resolvers, multicastResolvers);
add_scoped_resolvers(resolvers, services, serviceOrder);
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) ||
isScopedDNS(resolver)) {
CFDictionaryRemoveValue(myDefault, kSCPropNetDNSSearchOrder);
}
}
CFArraySetValueAtIndex(resolvers, 0, myDefault);
CFRelease(myDefault);
if ((defaultResolver == NULL) && (n_resolvers <= 1)) {
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();
for (i = 0; i < n_resolvers; i++) {
CFDictionaryRef resolver;
dns_create_resolver_t _resolver;
resolver = CFArrayGetValueAtIndex(resolvers, i);
_resolver = create_resolver(resolver);
_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(resolvers);
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 = _SC_CFMachPortCreateWithPort("IPMonitor/dns_configuration",
notify_port,
dns_configuration_changed,
NULL);
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
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)) {
CFStringRef interface;
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_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());
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