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 _SCNetworkProxiesCopyMatchingInternal(CFDictionaryRef globalConfiguration
,
452 CFStringRef interface
,
453 CFDictionaryRef options
)
455 CFMutableDictionaryRef newProxy
;
457 CFArrayRef proxies
= NULL
;
458 CFDictionaryRef proxy
;
459 int sc_status
= kSCStatusOK
;
460 CFStringRef trimmed
= NULL
;
463 if (!isA_CFDictionary(globalConfiguration
)) {
464 // if no proxy configuration
465 _SCErrorSet(kSCStatusOK
);
469 uuid_clear(match_uuid
);
471 if (isA_CFDictionary(options
)) {
474 interface
= CFDictionaryGetValue(options
, kSCProxiesMatchInterface
);
475 interface
= isA_CFString(interface
);
477 server
= CFDictionaryGetValue(options
, kSCProxiesMatchServer
);
478 server
= isA_CFString(server
);
480 euuid
= CFDictionaryGetValue(options
, kSCProxiesMatchExecutableUUID
);
481 euuid
= isA_CFType(euuid
, CFUUIDGetTypeID());
484 CFUUIDBytes uuid_bytes
= CFUUIDGetUUIDBytes(euuid
);
485 uuid_copy(match_uuid
, (const uint8_t *)&uuid_bytes
);
489 if (interface
!= NULL
) {
490 CFDictionaryRef scoped
;
492 if (!isA_CFString(interface
) ||
493 (CFStringGetLength(interface
) == 0)) {
494 _SCErrorSet(kSCStatusInvalidArgument
);
498 scoped
= CFDictionaryGetValue(globalConfiguration
, kSCPropNetProxiesScoped
);
499 if (scoped
== NULL
) {
500 // if no scoped proxy configurations
501 _SCErrorSet(kSCStatusOK
);
505 if (!isA_CFDictionary(scoped
)) {
506 // if corrupt proxy configuration
507 _SCErrorSet(kSCStatusFailed
);
511 proxy
= CFDictionaryGetValue(scoped
, interface
);
513 // if no scoped proxy configuration for this interface
514 _SCErrorSet(kSCStatusOK
);
518 if (!isA_CFDictionary(proxy
)) {
519 // if corrupt proxy configuration
520 _SCErrorSet(kSCStatusFailed
);
524 // return per-interface proxy configuration
525 proxies
= CFArrayCreate(NULL
, (const void **)&proxy
, 1, &kCFTypeArrayCallBacks
);
530 if (server
!= NULL
) {
532 CFMutableArrayRef matching
= NULL
;
535 CFArrayRef supplemental
;
537 trimmed
= _SC_trimDomain(server
);
538 if (trimmed
== NULL
) {
539 _SCErrorSet(kSCStatusInvalidArgument
);
544 server_len
= CFStringGetLength(server
);
546 supplemental
= CFDictionaryGetValue(globalConfiguration
, kSCPropNetProxiesSupplemental
);
547 if (supplemental
!= NULL
) {
548 if (!isA_CFArray(supplemental
)) {
549 // if corrupt proxy configuration
550 sc_status
= kSCStatusFailed
;
554 n
= CFArrayGetCount(supplemental
);
557 for (i
= 0; i
< n
; i
++) {
562 proxy
= CFArrayGetValueAtIndex(supplemental
, i
);
563 if (!isA_CFDictionary(proxy
)) {
564 // if corrupt proxy configuration
568 domain
= CFDictionaryGetValue(proxy
, kSCPropNetProxiesSupplementalMatchDomain
);
569 if (!isA_CFString(domain
)) {
570 // if corrupt proxy configuration
574 domain_len
= CFStringGetLength(domain
);
575 if (domain_len
> 0) {
576 if (!CFStringFindWithOptions(server
,
578 CFRangeMake(0, server_len
),
579 kCFCompareCaseInsensitive
|kCFCompareAnchored
|kCFCompareBackwards
,
581 // if server does not match this proxy domain (or host)
585 if ((server_len
> domain_len
) &&
586 !CFStringFindWithOptions(server
,
588 CFRangeMake(0, server_len
- domain_len
),
589 kCFCompareCaseInsensitive
|kCFCompareAnchored
|kCFCompareBackwards
,
591 // if server does not match this proxy domain
595 // // if this is a "default" (match all) proxy domain
598 if (matching
== NULL
) {
599 matching
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
601 n_matching
= CFArrayGetCount(matching
);
603 newProxy
= CFDictionaryCreateMutableCopy(NULL
, 0, proxy
);
604 CFDictionaryRemoveValue(newProxy
, kSCPropNetProxiesSupplementalMatchDomain
);
605 if ((n_matching
== 0) ||
606 !CFArrayContainsValue(matching
, CFRangeMake(0, n_matching
), newProxy
)) {
607 // add this matching proxy
608 CFArrayAppendValue(matching
, newProxy
);
613 if (matching
!= NULL
) {
614 // if we have any supplemental match domains
615 proxies
= CFArrayCreateCopy(NULL
, matching
);
621 // no matches, return "global" proxy configuration
623 newProxy
= CFDictionaryCreateMutableCopy(NULL
, 0, globalConfiguration
);
624 CFDictionaryRemoveValue(newProxy
, kSCPropNetProxiesScoped
);
625 CFDictionaryRemoveValue(newProxy
, kSCPropNetProxiesServices
);
626 CFDictionaryRemoveValue(newProxy
, kSCPropNetProxiesSupplemental
);
627 proxies
= CFArrayCreate(NULL
, (const void **)&newProxy
, 1, &kCFTypeArrayCallBacks
);
632 if (sc_status
!= kSCStatusOK
) {
633 if (proxies
!= NULL
) {
637 _SCErrorSet(sc_status
);
639 if (trimmed
!= NULL
) CFRelease(trimmed
);
645 SCNetworkProxiesCopyMatching(CFDictionaryRef globalConfiguration
,
647 CFStringRef interface
)
649 return _SCNetworkProxiesCopyMatchingInternal(globalConfiguration
, server
, interface
, NULL
);
653 SCNetworkProxiesCopyMatchingWithOptions(CFDictionaryRef globalConfiguration
,
654 CFDictionaryRef options
)
656 return _SCNetworkProxiesCopyMatchingInternal(globalConfiguration
, NULL
, NULL
, options
);