2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * March 22, 2004 Allan Nathanson <ajn@apple.com>
35 #include <sys/types.h>
36 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41 #include <arpa/nameser.h>
44 #include <CoreFoundation/CoreFoundation.h>
45 #include <SystemConfiguration/SystemConfiguration.h>
46 #include <SystemConfiguration/SCPrivate.h>
47 #include <SystemConfiguration/SCValidation.h>
50 #include <dnsinfo_create.h>
53 /* pre-defined (supplemental) resolver configurations */
54 static CFArrayRef S_predefined
= NULL
;
58 add_supplemental(CFMutableArrayRef supplemental
, CFDictionaryRef dns
, uint32_t defaultOrder
)
66 domains
= CFDictionaryGetValue(dns
, kSCPropNetDNSSupplementalMatchDomains
);
67 n_domains
= isA_CFArray(domains
) ? CFArrayGetCount(domains
) : 0;
72 orders
= CFDictionaryGetValue(dns
, kSCPropNetDNSSupplementalMatchOrders
);
74 if (!isA_CFArray(orders
) || (n_domains
!= CFArrayGetCount(orders
))) {
80 * yes, this is a "supplemental" resolver configuration, expand
81 * the match domains and add each to the supplemental list.
83 for (i
= 0; i
< n_domains
; i
++) {
85 CFStringRef match_domain
;
86 CFNumberRef match_order
;
87 uint32_t match_order_val
= 0;
88 CFMutableDictionaryRef match_resolver
;
89 CFIndex n_supplemental
;
91 match_domain
= CFArrayGetValueAtIndex(domains
, i
);
92 if (!isA_CFString(match_domain
)) {
96 match_order
= (orders
!= NULL
) ? CFArrayGetValueAtIndex(orders
, i
) : NULL
;
98 match_resolver
= CFDictionaryCreateMutableCopy(NULL
, 0, dns
);
99 CFDictionaryRemoveValue(match_resolver
, kSCPropNetDNSSupplementalMatchDomains
);
100 CFDictionaryRemoveValue(match_resolver
, kSCPropNetDNSSupplementalMatchOrders
);
101 CFDictionaryRemoveValue(match_resolver
, kSCPropNetDNSSearchDomains
);
102 CFDictionarySetValue(match_resolver
, kSCPropNetDNSDomainName
, match_domain
);
103 if (isA_CFNumber(match_order
)) {
104 CFDictionarySetValue(match_resolver
, kSCPropNetDNSSearchOrder
, match_order
);
105 } else if (!CFDictionaryContainsKey(match_resolver
, kSCPropNetDNSSearchOrder
)) {
108 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &defaultOrder
);
109 CFDictionarySetValue(match_resolver
, kSCPropNetDNSSearchOrder
, num
);
113 match_order
= CFDictionaryGetValue(match_resolver
, kSCPropNetDNSSearchOrder
);
114 if (!isA_CFNumber(match_order
) ||
115 !CFNumberGetValue(match_order
, kCFNumberIntType
, &match_order_val
)) {
120 n_supplemental
= CFArrayGetCount(supplemental
);
121 for (j
= 0; j
< n_supplemental
; j
++) {
122 CFMutableDictionaryRef compare
;
124 CFDictionaryRef supplemental_resolver
;
126 supplemental_resolver
= CFArrayGetValueAtIndex(supplemental
, j
);
127 if (CFEqual(match_resolver
, supplemental_resolver
)) {
129 CFRelease(match_resolver
);
130 match_resolver
= NULL
;
134 compare
= CFDictionaryCreateMutableCopy(NULL
, 0, supplemental_resolver
);
135 if (match_order
!= NULL
) {
136 CFDictionarySetValue(compare
, kSCPropNetDNSSearchOrder
, match_order
);
138 match
= CFEqual(match_resolver
, compare
);
142 CFNumberRef supplemental_order
;
143 uint32_t supplemental_order_val
= 0;
145 // if only the search order's are different
146 supplemental_order
= CFDictionaryGetValue(supplemental_resolver
, kSCPropNetDNSSearchOrder
);
147 if (!isA_CFNumber(supplemental_order
) ||
148 !CFNumberGetValue(supplemental_order
, kCFNumberIntType
, &supplemental_order_val
)) {
149 supplemental_order_val
= 0;
152 if (match_order_val
< supplemental_order_val
) {
153 // if we should prefer this match resolver, else just skip it
154 CFArraySetValueAtIndex(supplemental
, j
, match_resolver
);
157 CFRelease(match_resolver
);
158 match_resolver
= NULL
;
163 if (match_resolver
!= NULL
) {
164 CFArrayAppendValue(supplemental
, match_resolver
);
165 CFRelease(match_resolver
);
174 add_predefined_resolvers(CFMutableArrayRef supplemental
)
179 if (S_predefined
== NULL
) {
183 n
= CFArrayGetCount(S_predefined
);
184 for (i
= 0; i
< n
; i
++) {
185 uint32_t defaultOrder
;
188 dns
= CFArrayGetValueAtIndex(S_predefined
, i
);
189 if (!isA_CFDictionary(dns
)) {
193 defaultOrder
= DEFAULT_SEARCH_ORDER
+
194 (DEFAULT_SEARCH_ORDER
/ 2) +
195 ((DEFAULT_SEARCH_ORDER
/ 1000) * i
);
196 add_supplemental(supplemental
, dns
, defaultOrder
);
207 add_supplemental_resolvers(CFMutableArrayRef supplemental
, CFDictionaryRef services
, CFArrayRef service_order
)
209 const void * keys_q
[N_QUICK
];
210 const void ** keys
= keys_q
;
214 const void * vals_q
[N_QUICK
];
215 const void ** vals
= vals_q
;
217 n_services
= isA_CFDictionary(services
) ? CFDictionaryGetCount(services
) : 0;
218 if (n_services
== 0) {
219 return; // if no services
222 if (n_services
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) {
223 keys
= CFAllocatorAllocate(NULL
, n_services
* sizeof(CFTypeRef
), 0);
224 vals
= CFAllocatorAllocate(NULL
, n_services
* sizeof(CFTypeRef
), 0);
227 n_order
= isA_CFArray(service_order
) ? CFArrayGetCount(service_order
) : 0;
229 CFDictionaryGetKeysAndValues(services
, keys
, vals
);
230 for (i
= 0; i
< n_services
; i
++) {
231 uint32_t defaultOrder
;
233 CFDictionaryRef service
= (CFDictionaryRef
)vals
[i
];
235 if (!isA_CFDictionary(service
)) {
239 dns
= CFDictionaryGetValue(service
, kSCEntNetDNS
);
240 if (!isA_CFDictionary(dns
)) {
244 defaultOrder
= DEFAULT_SEARCH_ORDER
-
245 (DEFAULT_SEARCH_ORDER
/ 2) +
246 ((DEFAULT_SEARCH_ORDER
/ 1000) * i
);
248 !CFArrayContainsValue(service_order
, CFRangeMake(0, n_order
), keys
[i
])) {
249 // push out services not specified in service order
250 defaultOrder
+= (DEFAULT_SEARCH_ORDER
/ 1000) * n_services
;
253 add_supplemental(supplemental
, dns
, defaultOrder
);
256 if (keys
!= keys_q
) {
257 CFAllocatorDeallocate(NULL
, keys
);
258 CFAllocatorDeallocate(NULL
, vals
);
265 static CFComparisonResult
266 compareBySearchOrder(const void *val1
, const void *val2
, void *context
)
268 CFDictionaryRef dns1
= (CFDictionaryRef
)val1
;
269 CFDictionaryRef dns2
= (CFDictionaryRef
)val2
;
271 uint32_t order1
= DEFAULT_SEARCH_ORDER
;
272 uint32_t order2
= DEFAULT_SEARCH_ORDER
;
274 num
= CFDictionaryGetValue(dns1
, kSCPropNetDNSSearchOrder
);
275 if (!isA_CFNumber(num
) ||
276 !CFNumberGetValue(num
, kCFNumberIntType
, &order1
)) {
277 order1
= DEFAULT_SEARCH_ORDER
;
280 num
= CFDictionaryGetValue(dns2
, kSCPropNetDNSSearchOrder
);
281 if (!isA_CFNumber(num
) ||
282 !CFNumberGetValue(num
, kCFNumberIntType
, &order2
)) {
283 order2
= DEFAULT_SEARCH_ORDER
;
286 if (order1
== order2
) {
287 return kCFCompareEqualTo
;
290 return (order1
< order2
) ? kCFCompareLessThan
: kCFCompareGreaterThan
;
295 update_search_domains(CFMutableDictionaryRef
*defaultDomain
, CFArrayRef supplemental
)
297 CFStringRef defaultDomainName
= NULL
;
298 uint32_t defaultOrder
= DEFAULT_SEARCH_ORDER
;
299 CFArrayRef defaultSearchDomains
= NULL
;
300 CFIndex defaultSearchIndex
= 0;
303 CFMutableArrayRef mySearchDomains
;
304 CFMutableArrayRef mySupplemental
= (CFMutableArrayRef
)supplemental
;
305 Boolean searchDomainAdded
= FALSE
;
307 n
= CFArrayGetCount(supplemental
);
309 // if no supplemental domains
313 if (*defaultDomain
!= NULL
) {
316 num
= CFDictionaryGetValue(*defaultDomain
, kSCPropNetDNSSearchOrder
);
317 if (!isA_CFNumber(num
) ||
318 !CFNumberGetValue(num
, kCFNumberIntType
, &defaultOrder
)) {
319 defaultOrder
= DEFAULT_SEARCH_ORDER
;
322 defaultDomainName
= CFDictionaryGetValue(*defaultDomain
, kSCPropNetDNSDomainName
);
323 defaultSearchDomains
= CFDictionaryGetValue(*defaultDomain
, kSCPropNetDNSSearchDomains
);
326 if (isA_CFArray(defaultSearchDomains
)) {
327 mySearchDomains
= CFArrayCreateMutableCopy(NULL
, 0, defaultSearchDomains
);
329 mySearchDomains
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
330 if (isA_CFString(defaultDomainName
)) {
332 int domain_parts
= 1;
335 domain
= _SC_cfstring_to_cstring(defaultDomainName
,
338 kCFStringEncodingUTF8
);
340 for (dp
= domain
; *dp
!= '\0'; dp
++) {
347 for (i
= LOCALDOMAINPARTS
; i
<= domain_parts
; i
++) {
348 CFStringRef searchDomain
;
350 searchDomain
= CFStringCreateWithCString(NULL
,
352 kCFStringEncodingUTF8
);
353 CFArrayAppendValue(mySearchDomains
, searchDomain
);
354 CFRelease(searchDomain
);
356 dp
= strchr(dp
, '.') + 1;
359 CFAllocatorDeallocate(NULL
, domain
);
364 mySupplemental
= CFArrayCreateMutableCopy(NULL
, 0, supplemental
);
365 CFArraySortValues(mySupplemental
,
367 compareBySearchOrder
,
371 for (i
= 0; i
< n
; i
++) {
374 CFStringRef supplementalDomain
;
375 uint32_t supplementalOrder
;
377 dns
= CFArrayGetValueAtIndex(mySupplemental
, i
);
379 supplementalDomain
= CFDictionaryGetValue(dns
, kSCPropNetDNSDomainName
);
380 if (CFArrayContainsValue(mySearchDomains
,
381 CFRangeMake(0, CFArrayGetCount(mySearchDomains
)),
382 supplementalDomain
)) {
383 // if supplemental domain is already in the search list
387 num
= CFDictionaryGetValue(dns
, kSCPropNetDNSSearchOrder
);
388 if (!isA_CFNumber(num
) ||
389 !CFNumberGetValue(num
, kCFNumberIntType
, &supplementalOrder
)) {
390 supplementalOrder
= DEFAULT_SEARCH_ORDER
;
393 if (supplementalOrder
< defaultOrder
) {
394 CFArrayInsertValueAtIndex(mySearchDomains
,
397 defaultSearchIndex
++;
399 CFArrayAppendValue(mySearchDomains
, supplementalDomain
);
402 searchDomainAdded
= TRUE
;
405 if (searchDomainAdded
) {
406 if (*defaultDomain
== NULL
) {
407 *defaultDomain
= CFDictionaryCreateMutable(NULL
,
409 &kCFTypeDictionaryKeyCallBacks
,
410 &kCFTypeDictionaryValueCallBacks
);
412 CFDictionarySetValue(*defaultDomain
, kSCPropNetDNSSearchDomains
, mySearchDomains
);
415 CFRelease(mySearchDomains
);
416 if (mySupplemental
!= supplemental
) CFRelease(mySupplemental
);
421 static dns_create_resolver_t
422 create_resolver(CFDictionaryRef dns
)
426 dns_create_resolver_t _resolver
;
429 _resolver
= _dns_resolver_create();
432 str
= CFDictionaryGetValue(dns
, kSCPropNetDNSDomainName
);
433 if (isA_CFString(str
)) {
434 char domain
[NS_MAXDNAME
];
436 if (_SC_cfstring_to_cstring(str
, domain
, sizeof(domain
), kCFStringEncodingUTF8
) != NULL
) {
437 _dns_resolver_set_domain(&_resolver
, domain
);
441 // process search domains
442 list
= CFDictionaryGetValue(dns
, kSCPropNetDNSSearchDomains
);
443 if (isA_CFArray(list
)) {
445 CFIndex n
= CFArrayGetCount(list
);
447 // add "search" domains
448 for (i
= 0; i
< n
; i
++) {
449 str
= CFArrayGetValueAtIndex(list
, i
);
450 if (isA_CFString(str
)) {
451 char search
[NS_MAXDNAME
];
453 if (_SC_cfstring_to_cstring(str
, search
, sizeof(search
), kCFStringEncodingUTF8
) != NULL
) {
454 _dns_resolver_add_search(&_resolver
, search
);
460 // process nameserver addresses
461 list
= CFDictionaryGetValue(dns
, kSCPropNetDNSServerAddresses
);
462 if (isA_CFArray(list
)) {
464 CFIndex n
= CFArrayGetCount(list
);
466 for (i
= 0; i
< n
; i
++) {
469 struct sockaddr_in sin
;
470 struct sockaddr_in6 sin6
;
474 str
= CFArrayGetValueAtIndex(list
, i
);
475 if (!isA_CFString(str
)) {
479 if (_SC_cfstring_to_cstring(str
, buf
, sizeof(buf
), kCFStringEncodingASCII
) == NULL
) {
483 bzero(&addr
, sizeof(addr
));
484 if (inet_aton(buf
, &addr
.sin
.sin_addr
) == 1) {
485 /* if IPv4 address */
486 addr
.sin
.sin_len
= sizeof(addr
.sin
);
487 addr
.sin
.sin_family
= AF_INET
;
488 _dns_resolver_add_nameserver(&_resolver
, &addr
.sa
);
489 } else if (inet_pton(AF_INET6
, buf
, &addr
.sin6
.sin6_addr
) == 1) {
490 /* if IPv6 address */
493 p
= strchr(buf
, '%');
495 addr
.sin6
.sin6_scope_id
= if_nametoindex(p
+1);
498 addr
.sin6
.sin6_len
= sizeof(addr
.sin6
);
499 addr
.sin6
.sin6_family
= AF_INET6
;
500 _dns_resolver_add_nameserver(&_resolver
, &addr
.sa
);
507 // process search order
508 num
= CFDictionaryGetValue(dns
, kSCPropNetDNSSearchOrder
);
509 if (isA_CFNumber(num
)) {
512 if (CFNumberGetValue(num
, kCFNumberIntType
, &order
)) {
513 _dns_resolver_set_order(&_resolver
, order
);
518 num
= CFDictionaryGetValue(dns
, kSCPropNetDNSServerPort
);
519 if (isA_CFNumber(num
)) {
522 if (CFNumberGetValue(num
, kCFNumberIntType
, &port
)) {
523 _dns_resolver_set_port(&_resolver
, (uint16_t)port
);
528 num
= CFDictionaryGetValue(dns
, kSCPropNetDNSServerTimeout
);
529 if (isA_CFNumber(num
)) {
532 if (CFNumberGetValue(num
, kCFNumberIntType
, &timeout
)) {
533 _dns_resolver_set_timeout(&_resolver
, (uint32_t)timeout
);
538 str
= CFDictionaryGetValue(dns
, kSCPropNetDNSOptions
);
539 if (isA_CFString(str
)) {
542 options
= _SC_cfstring_to_cstring(str
, NULL
, 0, kCFStringEncodingUTF8
);
543 if (options
!= NULL
) {
544 _dns_resolver_set_options(&_resolver
, options
);
545 CFAllocatorDeallocate(NULL
, options
);
555 dns_configuration_set(CFDictionaryRef defaultResolver
,
556 CFDictionaryRef services
,
557 CFArrayRef serviceOrder
)
560 CFMutableDictionaryRef myDefault
;
561 CFStringRef myDomain
= NULL
;
562 uint32_t myOrder
= DEFAULT_SEARCH_ORDER
;
563 Boolean myOrderAdded
= FALSE
;
564 CFIndex n_supplemental
;
566 dns_create_resolver_t resolver
;
568 CFMutableArrayRef supplemental
;
570 if (defaultResolver
== NULL
) {
571 myDefault
= CFDictionaryCreateMutable(NULL
,
573 &kCFTypeDictionaryKeyCallBacks
,
574 &kCFTypeDictionaryValueCallBacks
);
576 myDefault
= CFDictionaryCreateMutableCopy(NULL
, 0, defaultResolver
);
578 // ensure that the default resolver has a search order
580 order
= CFDictionaryGetValue(myDefault
, kSCPropNetDNSSearchOrder
);
581 if (!isA_CFNumber(order
) ||
582 !CFNumberGetValue(order
, kCFNumberIntType
, &myOrder
)) {
584 myOrder
= DEFAULT_SEARCH_ORDER
;
585 order
= CFNumberCreate(NULL
, kCFNumberIntType
, &myOrder
);
586 CFDictionarySetValue(myDefault
, kSCPropNetDNSSearchOrder
, order
);
591 // establish list of supplemental resolvers
593 supplemental
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
595 // identify search[] list and/or domain name
597 search
= CFDictionaryGetValue(myDefault
, kSCPropNetDNSSearchDomains
);
598 if (isA_CFArray(search
) && (CFArrayGetCount(search
) > 0)) {
599 myDomain
= CFArrayGetValueAtIndex(search
, 0);
600 myDomain
= isA_CFString(myDomain
);
603 if (myDomain
== NULL
) {
604 myDomain
= CFDictionaryGetValue(myDefault
, kSCPropNetDNSDomainName
);
605 myDomain
= isA_CFString(myDomain
);
608 // add match for default domain
610 if (myDomain
!= NULL
) {
611 CFMutableDictionaryRef mySupplemental
;
613 mySupplemental
= CFDictionaryCreateMutableCopy(NULL
, 0, myDefault
);
614 CFDictionarySetValue (mySupplemental
, kSCPropNetDNSDomainName
, myDomain
);
615 CFDictionaryRemoveValue(mySupplemental
, kSCPropNetDNSSearchDomains
);
616 CFDictionaryRemoveValue(mySupplemental
, kSCPropNetDNSSupplementalMatchDomains
);
617 CFDictionaryRemoveValue(mySupplemental
, kSCPropNetDNSSupplementalMatchOrders
);
618 CFArrayAppendValue(supplemental
, mySupplemental
);
619 CFRelease(mySupplemental
);
622 // collect (and add) any supplemental resolver configurations
624 add_supplemental_resolvers(supplemental
, services
, serviceOrder
);
626 // update the "search" list
628 update_search_domains(&myDefault
, supplemental
);
630 // add any pre-defined resolver configurations
632 add_predefined_resolvers(supplemental
);
634 // check if the "match for default domain" (above) is really needed
636 if (myDomain
!= NULL
) {
637 Boolean sharedDomain
= FALSE
;
639 n_supplemental
= CFArrayGetCount(supplemental
);
640 for (i
= 1; i
< n_supplemental
; i
++) {
642 CFDictionaryRef mySupplemental
;
644 mySupplemental
= CFArrayGetValueAtIndex(supplemental
, i
);
645 domain
= CFDictionaryGetValue(mySupplemental
, kSCPropNetDNSDomainName
);
646 if (isA_CFString(domain
)) {
647 if (CFEqual(myDomain
, domain
)) {
652 if (CFStringHasSuffix(myDomain
, domain
)) {
655 dotIndex
= CFStringGetLength(myDomain
) - CFStringGetLength(domain
) - 1;
659 dot
= CFStringGetCharacterAtIndex(myDomain
, dotIndex
);
660 if (dot
== (UniChar
)'.') {
670 // if the default resolver domain name is not shared
671 CFArrayRemoveValueAtIndex(supplemental
, 0);
675 // establish resolver configuration
677 n_supplemental
= CFArrayGetCount(supplemental
);
678 if ((defaultResolver
== NULL
) && (n_supplemental
== 0)) {
680 * if no default or supplemental resolvers
682 if (!_dns_configuration_store(NULL
)) {
683 SCLog(TRUE
, LOG_ERR
, CFSTR("set_dns_configuration: could not store configuration"));
686 dns_create_config_t _config
;
689 * if default and/or supplemental resolvers are defined
691 _config
= _dns_configuration_create();
693 // add [default] resolver
695 if ((n_supplemental
== 0) && myOrderAdded
) {
696 CFDictionaryRemoveValue(myDefault
, kSCPropNetDNSSearchOrder
);
698 resolver
= create_resolver(myDefault
);
699 _dns_configuration_add_resolver(&_config
, resolver
);
700 _dns_resolver_free(&resolver
);
702 // add [supplemental] resolvers
704 for (i
= 0; i
< n_supplemental
; i
++) {
705 CFDictionaryRef supplementalResolver
;
707 supplementalResolver
= CFArrayGetValueAtIndex(supplemental
, i
);
708 resolver
= create_resolver(supplementalResolver
);
709 _dns_configuration_add_resolver(&_config
, resolver
);
710 _dns_resolver_free(&resolver
);
713 // save configuration
715 if (!_dns_configuration_store(&_config
)) {
716 SCLog(TRUE
, LOG_ERR
, CFSTR("set_dns_configuration() failed: could not store configuration"));
719 _dns_configuration_free(&_config
);
722 CFRelease(myDefault
);
723 CFRelease(supplemental
);
730 load_predefined_resolvers(CFBundleRef bundle
)
734 CFStringRef xmlError
= NULL
;
735 CFDataRef xmlResolvers
= NULL
;
737 url
= CFBundleCopyResourceURL(bundle
, CFSTR("Resolvers"), CFSTR("plist"), NULL
);
742 ok
= CFURLCreateDataAndPropertiesFromResource(NULL
, url
, &xmlResolvers
, NULL
, NULL
, NULL
);
744 if (!ok
|| (xmlResolvers
== NULL
)) {
748 /* convert the XML data into a property list */
749 S_predefined
= CFPropertyListCreateFromXMLData(NULL
, xmlResolvers
, kCFPropertyListImmutable
, &xmlError
);
750 CFRelease(xmlResolvers
);
751 if (S_predefined
== NULL
) {
752 if (xmlError
!= NULL
) {
753 SCLog(TRUE
, LOG_DEBUG
, CFSTR("add_predefined_resolvers: %@"), xmlError
);
759 if (!isA_CFArray(S_predefined
)) {
760 CFRelease(S_predefined
);
771 dns_configuration_init(CFBundleRef bundle
)
773 load_predefined_resolvers(bundle
);