2 * Copyright (c) 2000-2004, 2006-2013 Apple 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 * May 18, 2001 Allan Nathanson <ajn@apple.com>
31 #include <TargetConditionals.h>
32 #include <SystemConfiguration/SystemConfiguration.h>
33 #include <SystemConfiguration/SCValidation.h>
34 #include <SystemConfiguration/SCPrivate.h>
35 #include <SystemConfiguration/VPNAppLayerPrivate.h>
43 SCDynamicStoreKeyCreateProxies(CFAllocatorRef allocator
)
45 return SCDynamicStoreKeyCreateNetworkGlobalEntity(allocator
,
46 kSCDynamicStoreDomainState
,
52 validate_proxy_content(CFMutableDictionaryRef proxies
,
53 CFStringRef proxy_enable
,
54 CFStringRef proxy_host
,
55 CFStringRef proxy_port
,
56 const char * proxy_service
,
57 int proxy_defaultport
)
62 num
= CFDictionaryGetValue(proxies
, proxy_enable
);
64 if (!isA_CFNumber(num
) ||
65 !CFNumberGetValue(num
, kCFNumberIntType
, &enabled
)) {
66 goto disable
; // if we don't like the enabled key/value
70 if (proxy_host
!= NULL
) {
73 host
= CFDictionaryGetValue(proxies
, proxy_host
);
74 if ((enabled
== 0) && (host
!= NULL
)) {
75 goto disable
; // if not enabled, remove provided key/value
78 if ((enabled
!= 0) && !isA_CFString(host
)) {
79 goto disable
; // if enabled, not provided (or not valid)
83 if (proxy_port
!= NULL
) {
87 port
= CFDictionaryGetValue(proxies
, proxy_port
);
88 if ((enabled
== 0) && (port
!= NULL
)) {
89 goto disable
; // if not enabled, remove provided key/value
92 if ((enabled
!= 0) && (port
!= NULL
)) {
93 if (!isA_CFNumber(port
) ||
94 !CFNumberGetValue(port
, kCFNumberIntType
, &s_port
) ||
95 (s_port
> UINT16_MAX
)) {
96 goto disable
; // if enabled, not provided (or not valid)
100 port
= NULL
; // if no port # provided, use default
104 if ((enabled
!= 0) && (port
== NULL
)) {
105 struct servent
*service
;
107 service
= getservbyname(proxy_service
, "tcp");
108 if (service
!= NULL
) {
109 s_port
= ntohs(service
->s_port
);
111 s_port
= proxy_defaultport
;
113 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &s_port
);
114 CFDictionarySetValue(proxies
, proxy_port
, num
);
124 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &enabled
);
125 CFDictionarySetValue(proxies
, proxy_enable
, num
);
127 if (proxy_host
!= NULL
) {
128 CFDictionaryRemoveValue(proxies
, proxy_host
);
130 if (proxy_port
!= NULL
) {
131 CFDictionaryRemoveValue(proxies
, proxy_port
);
139 normalize_scoped_proxy(const void *key
, const void *value
, void *context
);
143 normalize_services_proxy(const void *key
, const void *value
, void *context
);
147 normalize_supplemental_proxy(const void *value
, void *context
);
150 static CF_RETURNS_RETAINED CFDictionaryRef
151 __SCNetworkProxiesCopyNormalized(CFDictionaryRef proxy
)
154 CFMutableDictionaryRef newProxy
;
156 CFDictionaryRef scoped
;
157 CFDictionaryRef services
;
158 CFArrayRef supplemental
;
160 if (!isA_CFDictionary(proxy
)) {
161 proxy
= CFDictionaryCreate(NULL
,
165 &kCFTypeDictionaryKeyCallBacks
,
166 &kCFTypeDictionaryValueCallBacks
);
170 newProxy
= CFDictionaryCreateMutableCopy(NULL
, 0, proxy
);
172 validate_proxy_content(newProxy
,
173 kSCPropNetProxiesFTPEnable
,
174 kSCPropNetProxiesFTPProxy
,
175 kSCPropNetProxiesFTPPort
,
178 validate_proxy_content(newProxy
,
179 kSCPropNetProxiesGopherEnable
,
180 kSCPropNetProxiesGopherProxy
,
181 kSCPropNetProxiesGopherPort
,
184 validate_proxy_content(newProxy
,
185 kSCPropNetProxiesHTTPEnable
,
186 kSCPropNetProxiesHTTPProxy
,
187 kSCPropNetProxiesHTTPPort
,
190 validate_proxy_content(newProxy
,
191 kSCPropNetProxiesHTTPSEnable
,
192 kSCPropNetProxiesHTTPSProxy
,
193 kSCPropNetProxiesHTTPSPort
,
196 validate_proxy_content(newProxy
,
197 kSCPropNetProxiesRTSPEnable
,
198 kSCPropNetProxiesRTSPProxy
,
199 kSCPropNetProxiesRTSPPort
,
202 validate_proxy_content(newProxy
,
203 kSCPropNetProxiesSOCKSEnable
,
204 kSCPropNetProxiesSOCKSProxy
,
205 kSCPropNetProxiesSOCKSPort
,
208 if (CFDictionaryContainsKey(newProxy
, kSCPropNetProxiesProxyAutoConfigURLString
)) {
209 validate_proxy_content(newProxy
,
210 kSCPropNetProxiesProxyAutoConfigEnable
,
211 kSCPropNetProxiesProxyAutoConfigURLString
,
216 // and we can't have both URLString and JavaScript keys
217 CFDictionaryRemoveValue(newProxy
, kSCPropNetProxiesProxyAutoConfigJavaScript
);
219 validate_proxy_content(newProxy
,
220 kSCPropNetProxiesProxyAutoConfigEnable
,
221 kSCPropNetProxiesProxyAutoConfigJavaScript
,
226 validate_proxy_content(newProxy
,
227 kSCPropNetProxiesProxyAutoDiscoveryEnable
,
233 validate_proxy_content(newProxy
,
234 kSCPropNetProxiesFallBackAllowed
,
240 // validate FTP passive setting
241 num
= CFDictionaryGetValue(newProxy
, kSCPropNetProxiesFTPPassive
);
245 if (!isA_CFNumber(num
) ||
246 !CFNumberGetValue(num
, kCFNumberIntType
, &enabled
)) {
247 // if we don't like the enabled key/value
249 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &enabled
);
250 CFDictionarySetValue(newProxy
,
251 kSCPropNetProxiesFTPPassive
,
257 // validate proxy exception list
258 array
= CFDictionaryGetValue(newProxy
, kSCPropNetProxiesExceptionsList
);
263 n
= isA_CFArray(array
) ? CFArrayGetCount(array
) : 0;
264 for (i
= 0; i
< n
; i
++) {
267 str
= CFArrayGetValueAtIndex(array
, i
);
268 if (!isA_CFString(str
)) {
269 // if we don't like the array contents
276 CFDictionaryRemoveValue(newProxy
, kSCPropNetProxiesExceptionsList
);
280 // validate exclude simple hostnames setting
281 num
= CFDictionaryGetValue(newProxy
, kSCPropNetProxiesExcludeSimpleHostnames
);
285 if (!isA_CFNumber(num
) ||
286 !CFNumberGetValue(num
, kCFNumberIntType
, &enabled
)) {
287 // if we don't like the enabled key/value
289 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &enabled
);
290 CFDictionarySetValue(newProxy
,
291 kSCPropNetProxiesExcludeSimpleHostnames
,
297 // cleanup scoped proxies
298 scoped
= CFDictionaryGetValue(newProxy
, kSCPropNetProxiesScoped
);
299 if (isA_CFDictionary(scoped
)) {
300 CFMutableDictionaryRef newScoped
;
302 newScoped
= CFDictionaryCreateMutable(NULL
,
304 &kCFTypeDictionaryKeyCallBacks
,
305 &kCFTypeDictionaryValueCallBacks
);
306 CFDictionaryApplyFunction(scoped
,
307 normalize_scoped_proxy
,
309 CFDictionarySetValue(newProxy
, kSCPropNetProxiesScoped
, newScoped
);
310 CFRelease(newScoped
);
313 // cleanup services proxies
314 services
= CFDictionaryGetValue(newProxy
, kSCPropNetProxiesServices
);
315 if (isA_CFDictionary(services
)) {
316 CFMutableDictionaryRef newServices
;
318 newServices
= CFDictionaryCreateMutable(NULL
,
320 &kCFTypeDictionaryKeyCallBacks
,
321 &kCFTypeDictionaryValueCallBacks
);
322 CFDictionaryApplyFunction(services
,
323 normalize_services_proxy
,
325 CFDictionarySetValue(newProxy
, kSCPropNetProxiesServices
, newServices
);
326 CFRelease(newServices
);
329 // cleanup split/supplemental proxies
330 supplemental
= CFDictionaryGetValue(newProxy
, kSCPropNetProxiesSupplemental
);
331 if (isA_CFArray(supplemental
)) {
332 CFMutableArrayRef newSupplemental
;
334 newSupplemental
= CFArrayCreateMutable(NULL
,
336 &kCFTypeArrayCallBacks
);
337 CFArrayApplyFunction(supplemental
,
338 CFRangeMake(0, CFArrayGetCount(supplemental
)),
339 normalize_supplemental_proxy
,
341 CFDictionarySetValue(newProxy
, kSCPropNetProxiesSupplemental
, newSupplemental
);
342 CFRelease(newSupplemental
);
345 proxy
= CFDictionaryCreateCopy(NULL
,newProxy
);
353 normalize_scoped_proxy(const void *key
, const void *value
, void *context
)
355 CFStringRef interface
= (CFStringRef
)key
;
356 CFDictionaryRef proxy
= (CFDictionaryRef
)value
;
357 CFMutableDictionaryRef newScoped
= (CFMutableDictionaryRef
)context
;
359 proxy
= __SCNetworkProxiesCopyNormalized(proxy
);
360 CFDictionarySetValue(newScoped
, interface
, proxy
);
367 normalize_services_proxy(const void *key
, const void *value
, void *context
)
369 CFStringRef serviceID
= (CFStringRef
)key
;
370 CFDictionaryRef proxy
= (CFDictionaryRef
)value
;
371 CFMutableDictionaryRef newServices
= (CFMutableDictionaryRef
)context
;
373 proxy
= __SCNetworkProxiesCopyNormalized(proxy
);
374 CFDictionarySetValue(newServices
, serviceID
, proxy
);
381 normalize_supplemental_proxy(const void *value
, void *context
)
383 CFDictionaryRef proxy
= (CFDictionaryRef
)value
;
384 CFMutableArrayRef newSupplemental
= (CFMutableArrayRef
)context
;
386 proxy
= __SCNetworkProxiesCopyNormalized(proxy
);
387 CFArrayAppendValue(newSupplemental
, proxy
);
394 SCDynamicStoreCopyProxies(SCDynamicStoreRef store
)
396 return SCDynamicStoreCopyProxiesWithOptions(store
, NULL
);
399 const CFStringRef kSCProxiesNoGlobal
= CFSTR("NO_GLOBAL");
402 SCDynamicStoreCopyProxiesWithOptions(SCDynamicStoreRef store
, CFDictionaryRef options
)
404 Boolean bypass
= FALSE
;
406 CFDictionaryRef proxies
;
408 if (options
!= NULL
) {
409 CFBooleanRef bypassGlobalOption
;
411 if (isA_CFDictionary(options
) == NULL
) {
412 _SCErrorSet(kSCStatusInvalidArgument
);
416 bypassGlobalOption
= CFDictionaryGetValue(options
, kSCProxiesNoGlobal
);
417 if (isA_CFBoolean(bypassGlobalOption
) && CFBooleanGetValue(bypassGlobalOption
)) {
423 /* copy proxy information from dynamic store */
425 key
= SCDynamicStoreKeyCreateProxies(NULL
);
426 proxies
= SCDynamicStoreCopyValue(store
, key
);
430 if (proxies
!= NULL
) {
431 CFDictionaryRef base
= proxies
;
433 proxies
= __SCNetworkProxiesCopyNormalized(base
);
436 proxies
= CFDictionaryCreate(NULL
,
440 &kCFTypeDictionaryKeyCallBacks
,
441 &kCFTypeDictionaryValueCallBacks
);
450 SCNetworkProxiesCopyMatching(CFDictionaryRef globalConfiguration
,
452 CFStringRef interface
)
454 CFMutableDictionaryRef newProxy
;
455 static const audit_token_t null_audit
= KERNEL_AUDIT_TOKEN_VALUE
;
456 UUID_DEFINE(null_uuid
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
457 CFArrayRef proxies
= NULL
;
458 CFDictionaryRef proxy
;
459 int sc_status
= kSCStatusOK
;
460 CFStringRef serviceID
;
461 CFStringRef trimmed
= NULL
;
463 if (!isA_CFDictionary(globalConfiguration
)) {
464 // if no proxy configuration
465 _SCErrorSet(kSCStatusOK
);
469 if (interface
!= NULL
) {
470 CFDictionaryRef scoped
;
472 if (!isA_CFString(interface
) ||
473 (CFStringGetLength(interface
) == 0)) {
474 _SCErrorSet(kSCStatusInvalidArgument
);
478 scoped
= CFDictionaryGetValue(globalConfiguration
, kSCPropNetProxiesScoped
);
479 if (scoped
== NULL
) {
480 // if no scoped proxy configurations
481 _SCErrorSet(kSCStatusOK
);
485 if (!isA_CFDictionary(scoped
)) {
486 // if corrupt proxy configuration
487 _SCErrorSet(kSCStatusFailed
);
491 proxy
= CFDictionaryGetValue(scoped
, interface
);
493 // if no scoped proxy configuration for this interface
494 _SCErrorSet(kSCStatusOK
);
498 if (!isA_CFDictionary(proxy
)) {
499 // if corrupt proxy configuration
500 _SCErrorSet(kSCStatusFailed
);
504 // return per-interface proxy configuration
505 proxies
= CFArrayCreate(NULL
, (const void **)&proxy
, 1, &kCFTypeArrayCallBacks
);
509 // Check for app-layer VPN proxy results (with or without server)
510 serviceID
= VPNAppLayerCopyMatchingService(null_audit
, 0, null_uuid
, NULL
, server
, NULL
, NULL
);
511 if (serviceID
!= NULL
) {
512 CFDictionaryRef serviceProxies
= NULL
;
514 serviceProxies
= CFDictionaryGetValue(globalConfiguration
, kSCPropNetProxiesServices
);
515 if (serviceProxies
== NULL
) {
516 _SCErrorSet(kSCStatusOK
);
517 CFRelease(serviceID
);
518 goto app_layer_no_proxies
;
520 if (!isA_CFDictionary(serviceProxies
)) {
521 _SCErrorSet(kSCStatusFailed
);
522 CFRelease(serviceID
);
523 goto app_layer_no_proxies
;
526 proxy
= CFDictionaryGetValue(serviceProxies
, serviceID
);
527 CFRelease(serviceID
);
529 _SCErrorSet(kSCStatusOK
);
530 goto app_layer_no_proxies
;
532 if (!isA_CFDictionary(proxy
)) {
533 _SCErrorSet(kSCStatusFailed
);
534 goto app_layer_no_proxies
;
537 proxies
= CFArrayCreate(NULL
, (const void **)&proxy
, 1, &kCFTypeArrayCallBacks
);
540 app_layer_no_proxies
:
543 * Rather than returning NULL, return an empty proxy configuration.
544 * This ensures that the global proxy configuration will not be used.
546 proxy
= CFDictionaryCreate(NULL
, NULL
, NULL
, 0, NULL
, NULL
);
547 proxies
= CFArrayCreate(NULL
, (const void **)&proxy
, 1, &kCFTypeArrayCallBacks
);
552 if (server
!= NULL
) {
554 CFMutableArrayRef matching
= NULL
;
557 CFArrayRef supplemental
;
559 trimmed
= _SC_trimDomain(server
);
560 if (trimmed
== NULL
) {
561 _SCErrorSet(kSCStatusInvalidArgument
);
566 server_len
= CFStringGetLength(server
);
568 supplemental
= CFDictionaryGetValue(globalConfiguration
, kSCPropNetProxiesSupplemental
);
569 if (supplemental
!= NULL
) {
570 if (!isA_CFArray(supplemental
)) {
571 // if corrupt proxy configuration
572 sc_status
= kSCStatusFailed
;
576 n
= CFArrayGetCount(supplemental
);
579 for (i
= 0; i
< n
; i
++) {
584 proxy
= CFArrayGetValueAtIndex(supplemental
, i
);
585 if (!isA_CFDictionary(proxy
)) {
586 // if corrupt proxy configuration
590 domain
= CFDictionaryGetValue(proxy
, kSCPropNetProxiesSupplementalMatchDomain
);
591 if (!isA_CFString(domain
)) {
592 // if corrupt proxy configuration
596 domain_len
= CFStringGetLength(domain
);
597 if (domain_len
> 0) {
598 if (!CFStringFindWithOptions(server
,
600 CFRangeMake(0, server_len
),
601 kCFCompareCaseInsensitive
|kCFCompareAnchored
|kCFCompareBackwards
,
603 // if server does not match this proxy domain (or host)
607 if ((server_len
> domain_len
) &&
608 !CFStringFindWithOptions(server
,
610 CFRangeMake(0, server_len
- domain_len
),
611 kCFCompareCaseInsensitive
|kCFCompareAnchored
|kCFCompareBackwards
,
613 // if server does not match this proxy domain
617 // // if this is a "default" (match all) proxy domain
620 if (matching
== NULL
) {
621 matching
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
623 n_matching
= CFArrayGetCount(matching
);
625 newProxy
= CFDictionaryCreateMutableCopy(NULL
, 0, proxy
);
626 CFDictionaryRemoveValue(newProxy
, kSCPropNetProxiesSupplementalMatchDomain
);
627 if ((n_matching
== 0) ||
628 !CFArrayContainsValue(matching
, CFRangeMake(0, n_matching
), newProxy
)) {
629 // add this matching proxy
630 CFArrayAppendValue(matching
, newProxy
);
635 if (matching
!= NULL
) {
636 // if we have any supplemental match domains
637 proxies
= CFArrayCreateCopy(NULL
, matching
);
643 // no matches, return "global" proxy configuration
645 newProxy
= CFDictionaryCreateMutableCopy(NULL
, 0, globalConfiguration
);
646 CFDictionaryRemoveValue(newProxy
, kSCPropNetProxiesScoped
);
647 CFDictionaryRemoveValue(newProxy
, kSCPropNetProxiesServices
);
648 CFDictionaryRemoveValue(newProxy
, kSCPropNetProxiesSupplemental
);
649 proxies
= CFArrayCreate(NULL
, (const void **)&newProxy
, 1, &kCFTypeArrayCallBacks
);
654 if (sc_status
!= kSCStatusOK
) {
655 if (proxies
!= NULL
) {
659 _SCErrorSet(sc_status
);
661 if (trimmed
!= NULL
) CFRelease(trimmed
);