+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+/*
+ * Modification History
+ *
+ * January 3, 2011 Allan Nathanson <ajn@apple.com>
+ * - initial revision
+ */
+
+#include <TargetConditionals.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <SystemConfiguration/SystemConfiguration.h>
+#include <SystemConfiguration/SCPrivate.h>
+#include <SystemConfiguration/SCValidation.h>
+
+
+#define DEFAULT_MATCH_ORDER 200000 /* match order for the "default" proxy configuration */
+
+
+#define PROXY_MATCH_ORDER_KEY CFSTR("__MATCH_ORDER__")
+#define ORDER_KEY CFSTR("__ORDER__")
+
+
+static CFBooleanRef S_proxies_follow_dns = NULL;
+
+
+static void
+add_proxy(CFMutableArrayRef proxies, CFMutableDictionaryRef proxy)
+{
+ CFIndex i;
+ CFIndex n_proxies;
+ CFNumberRef order;
+
+ n_proxies = CFArrayGetCount(proxies);
+ for (i = 0; i < n_proxies; i++) {
+ CFDictionaryRef match_proxy;
+
+ match_proxy = CFArrayGetValueAtIndex(proxies, i);
+ if (CFEqual(proxy, match_proxy)) {
+ // a real duplicate
+ return;
+ }
+ }
+
+ order = CFNumberCreate(NULL, kCFNumberIntType, &n_proxies);
+ CFDictionarySetValue(proxy, ORDER_KEY, order);
+ CFRelease(order);
+
+ CFArrayAppendValue(proxies, proxy);
+ return;
+}
+
+
+static void
+add_supplemental(CFMutableArrayRef proxies, CFDictionaryRef proxy, uint32_t defaultOrder)
+{
+ CFArrayRef domains;
+ CFIndex i;
+ CFIndex n_domains;
+ CFArrayRef orders;
+
+ domains = CFDictionaryGetValue(proxy, kSCPropNetProxiesSupplementalMatchDomains);
+ n_domains = isA_CFArray(domains) ? CFArrayGetCount(domains) : 0;
+ if (n_domains == 0) {
+ return;
+ }
+
+ orders = CFDictionaryGetValue(proxy, kSCPropNetProxiesSupplementalMatchOrders);
+ if (orders != NULL) {
+ if (!isA_CFArray(orders) || (n_domains != CFArrayGetCount(orders))) {
+ return;
+ }
+ }
+
+ /*
+ * yes, this is a "supplemental" proxy configuration, expand
+ * the match domains and add each to the proxies list.
+ */
+ for (i = 0; i < n_domains; i++) {
+ CFStringRef match_domain;
+ CFNumberRef match_order;
+ CFMutableDictionaryRef match_proxy;
+
+ match_domain = CFArrayGetValueAtIndex(domains, i);
+ if (!isA_CFString(match_domain)) {
+ continue;
+ }
+
+ match_proxy = CFDictionaryCreateMutableCopy(NULL, 0, proxy);
+
+ // set supplemental proxy match "domain"
+ match_domain = _SC_trimDomain(match_domain);
+ if (match_domain != NULL) {
+ CFDictionarySetValue(match_proxy, kSCPropNetProxiesSupplementalMatchDomain, match_domain);
+ CFRelease(match_domain);
+ } else {
+ CFDictionaryRemoveValue(match_proxy, kSCPropNetProxiesSupplementalMatchDomain);
+ }
+
+ // set supplemental proxy match "order"
+ match_order = (orders != NULL) ? CFArrayGetValueAtIndex(orders, i) : NULL;
+ if (isA_CFNumber(match_order)) {
+ CFDictionarySetValue(match_proxy, PROXY_MATCH_ORDER_KEY, match_order);
+ } else {
+ CFNumberRef num;
+
+ num = CFNumberCreate(NULL, kCFNumberIntType, &defaultOrder);
+ CFDictionarySetValue(match_proxy, PROXY_MATCH_ORDER_KEY, num);
+ CFRelease(num);
+
+ defaultOrder++; // if multiple domains, maintain ordering
+ }
+
+ // remove keys we don't want in a supplemental proxy
+ CFDictionaryRemoveValue(match_proxy, kSCPropNetProxiesSupplementalMatchDomains);
+ CFDictionaryRemoveValue(match_proxy, kSCPropNetProxiesSupplementalMatchOrders);
+ CFDictionaryRemoveValue(match_proxy, kSCPropInterfaceName);
+
+ add_proxy(proxies, match_proxy);
+ CFRelease(match_proxy);
+ }
+
+ return;
+}
+
+
+#define N_QUICK 32
+
+
+static void
+add_supplemental_proxies(CFMutableArrayRef proxies, 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 no services
+ }
+
+ 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 proxy;
+ CFMutableDictionaryRef proxyWithDNS = NULL;
+ CFDictionaryRef service = (CFDictionaryRef)vals[i];
+
+ if (!isA_CFDictionary(service)) {
+ continue;
+ }
+
+ proxy = CFDictionaryGetValue(service, kSCEntNetProxies);
+ if (!isA_CFDictionary(proxy)) {
+ continue;
+ }
+
+ if ((S_proxies_follow_dns != NULL) && CFBooleanGetValue(S_proxies_follow_dns)) {
+ CFDictionaryRef dns;
+ CFArrayRef matchDomains;
+ CFArrayRef matchOrders;
+
+ if (!CFDictionaryContainsKey(proxy, kSCPropNetProxiesSupplementalMatchDomains) &&
+ CFDictionaryGetValueIfPresent(service, kSCEntNetDNS, (const void **)&dns) &&
+ isA_CFDictionary(dns) &&
+ CFDictionaryGetValueIfPresent(dns, kSCPropNetDNSSupplementalMatchDomains, (const void **)&matchDomains) &&
+ isA_CFArray(matchDomains)) {
+ proxyWithDNS = CFDictionaryCreateMutableCopy(NULL, 0, proxy);
+ CFDictionarySetValue(proxyWithDNS, kSCPropNetProxiesSupplementalMatchDomains, matchDomains);
+ if (CFDictionaryGetValueIfPresent(dns, kSCPropNetDNSSupplementalMatchOrders, (const void **)&matchOrders) &&
+ isA_CFArray(matchOrders)) {
+ CFDictionarySetValue(proxyWithDNS, kSCPropNetProxiesSupplementalMatchOrders, matchOrders);
+ } else {
+ CFDictionaryRemoveValue(proxyWithDNS, kSCPropNetProxiesSupplementalMatchOrders);
+ }
+ proxy = proxyWithDNS;
+ }
+ }
+
+ defaultOrder = DEFAULT_MATCH_ORDER
+ - (DEFAULT_MATCH_ORDER / 2)
+ + ((DEFAULT_MATCH_ORDER / 1000) * i);
+ if ((n_order > 0) &&
+ !CFArrayContainsValue(service_order, CFRangeMake(0, n_order), keys[i])) {
+ // push out services not specified in service order
+ defaultOrder += (DEFAULT_MATCH_ORDER / 1000) * n_services;
+ }
+
+ add_supplemental(proxies, proxy, defaultOrder);
+ if (proxyWithDNS != NULL) CFRelease(proxyWithDNS);
+ }
+
+ if (keys != keys_q) {
+ CFAllocatorDeallocate(NULL, keys);
+ CFAllocatorDeallocate(NULL, vals);
+ }
+
+ return;
+}
+
+
+static CFComparisonResult
+compareBySearchOrder(const void *val1, const void *val2, void *context)
+{
+ CFDictionaryRef proxy1 = (CFDictionaryRef)val1;
+ CFDictionaryRef proxy2 = (CFDictionaryRef)val2;
+ CFNumberRef num1;
+ CFNumberRef num2;
+ uint32_t order1 = DEFAULT_MATCH_ORDER;
+ uint32_t order2 = DEFAULT_MATCH_ORDER;
+
+ num1 = CFDictionaryGetValue(proxy1, PROXY_MATCH_ORDER_KEY);
+ if (!isA_CFNumber(num1) ||
+ !CFNumberGetValue(num1, kCFNumberIntType, &order1)) {
+ order1 = DEFAULT_MATCH_ORDER;
+ }
+
+ num2 = CFDictionaryGetValue(proxy2, PROXY_MATCH_ORDER_KEY);
+ if (!isA_CFNumber(num2) ||
+ !CFNumberGetValue(num2, kCFNumberIntType, &order2)) {
+ order2 = DEFAULT_MATCH_ORDER;
+ }
+
+ if (order1 == order2) {
+ // if same match "order", retain original ordering for configurations
+ if (CFDictionaryGetValueIfPresent(proxy1, ORDER_KEY, (const void **)&num1) &&
+ CFDictionaryGetValueIfPresent(proxy2, ORDER_KEY, (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 __inline__ Boolean
+isSupplementalProxy(CFDictionaryRef proxy)
+{
+ if ((proxy != NULL) &&
+ CFDictionaryContainsKey(proxy, kSCPropNetProxiesSupplementalMatchDomain)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static CFArrayRef
+copy_supplemental_proxies(CFArrayRef proxies, Boolean skip)
+{
+ CFIndex i;
+ CFIndex n_proxies;
+ CFMutableArrayRef supplemental = NULL;
+
+ // iterate over services
+
+ n_proxies = isA_CFArray(proxies) ? CFArrayGetCount(proxies) : 0;
+ for (i = 0; i < n_proxies; i++) {
+ CFDictionaryRef proxy;
+
+ proxy = CFArrayGetValueAtIndex(proxies, i);
+ if (!isSupplementalProxy(proxy)) {
+ // if not supplemental proxy (i.e. no match domain)
+ continue;
+ }
+
+ // add [supplemental] proxy entry
+ if (supplemental == NULL) {
+ supplemental = CFArrayCreateMutable(NULL,
+ 0,
+ &kCFTypeArrayCallBacks);
+ }
+ CFArrayAppendValue(supplemental, proxy);
+ }
+
+ return supplemental;
+}
+
+
+static CFDictionaryRef
+copy_scoped_proxies(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;
+ CFMutableDictionaryRef scoped = NULL;
+
+ n_services = isA_CFDictionary(services) ? CFDictionaryGetCount(services) : 0;
+ if (n_services == 0) {
+ return NULL; // if no services
+ }
+
+ // ensure that we process all services in order
+
+ 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);
+ }
+
+ // iterate over services
+
+ for (i = 0; i < n_order; i++) {
+ CFDictionaryRef proxy;
+ char if_name[IF_NAMESIZE];
+ CFStringRef interface;
+ CFMutableDictionaryRef newProxy;
+ CFDictionaryRef service;
+ CFStringRef serviceID;
+
+ serviceID = CFArrayGetValueAtIndex(order, i);
+ service = CFDictionaryGetValue(services, serviceID);
+ if (!isA_CFDictionary(service)) {
+ // if no service
+ continue;
+ }
+
+ proxy = CFDictionaryGetValue(service, kSCEntNetProxies);
+ if (!isA_CFDictionary(proxy)) {
+ // if no proxy
+ continue;
+ }
+
+ interface = CFDictionaryGetValue(proxy, kSCPropInterfaceName);
+ if (interface == NULL) {
+ // if no [scoped] interface
+ continue;
+ }
+ if ((scoped != NULL) &&
+ CFDictionaryContainsKey(scoped, interface)) {
+ // if we've already processed this [scoped] interface
+ continue;
+ }
+
+ if ((_SC_cfstring_to_cstring(interface,
+ if_name,
+ sizeof(if_name),
+ kCFStringEncodingASCII) == NULL) ||
+ ((if_nametoindex(if_name)) == 0)) {
+ // if interface index not available
+ continue;
+ }
+
+ // add [scoped] proxy entry
+ // ... and remove keys we don't want in a [scoped] proxy
+ CFRetain(interface);
+ newProxy = CFDictionaryCreateMutableCopy(NULL, 0, proxy);
+ CFDictionaryRemoveValue(newProxy, kSCPropNetProxiesSupplementalMatchDomains);
+ CFDictionaryRemoveValue(newProxy, kSCPropNetProxiesSupplementalMatchOrders);
+ CFDictionaryRemoveValue(newProxy, kSCPropInterfaceName);
+ if (scoped == NULL) {
+ scoped = CFDictionaryCreateMutable(NULL,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ }
+ CFDictionarySetValue(scoped, interface, newProxy);
+ CFRelease(newProxy);
+ CFRelease(interface);
+ }
+
+ CFRelease(order);
+ return scoped;
+}
+
+
+static void
+add_default_proxy(CFMutableArrayRef proxies,
+ CFDictionaryRef defaultProxy,
+ Boolean *orderAdded)
+{
+ CFMutableDictionaryRef myDefault;
+ uint32_t myOrder = DEFAULT_MATCH_ORDER;
+ CFNumberRef order = NULL;
+
+ if (defaultProxy == NULL) {
+ myDefault = CFDictionaryCreateMutable(NULL,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ } else {
+ myDefault = CFDictionaryCreateMutableCopy(NULL, 0, defaultProxy);
+ CFDictionaryRemoveValue(myDefault, kSCPropInterfaceName);
+ order = CFDictionaryGetValue(myDefault, PROXY_MATCH_ORDER_KEY);
+ }
+
+ // ensure that the default proxy has a search order
+
+ if (!isA_CFNumber(order) ||
+ !CFNumberGetValue(order, kCFNumberIntType, &myOrder)) {
+ myOrder = DEFAULT_MATCH_ORDER;
+ order = CFNumberCreate(NULL, kCFNumberIntType, &myOrder);
+ CFDictionarySetValue(myDefault, PROXY_MATCH_ORDER_KEY, order);
+ CFRelease(order);
+ *orderAdded = TRUE;
+ }
+
+ // add the default proxy
+
+ add_proxy(proxies, myDefault);
+ CFRelease(myDefault);
+ return;
+}
+
+
+static CFComparisonResult
+compareDomain(const void *val1, const void *val2, void *context)
+{
+ CFDictionaryRef proxy1 = (CFDictionaryRef)val1;
+ CFDictionaryRef proxy2 = (CFDictionaryRef)val2;
+ CFStringRef domain1;
+ CFStringRef domain2;
+ CFArrayRef labels1 = NULL;
+ CFArrayRef labels2 = NULL;
+ CFIndex n1;
+ CFIndex n2;
+ CFComparisonResult result;
+ Boolean rev1;
+ Boolean rev2;
+
+ // "default" domains sort before "supplemental" domains
+ domain1 = CFDictionaryGetValue(proxy1, kSCPropNetProxiesSupplementalMatchDomain);
+ domain2 = CFDictionaryGetValue(proxy2, kSCPropNetProxiesSupplementalMatchDomain);
+ if (domain1 == NULL) {
+ if (domain2 == NULL) {
+ return kCFCompareEqualTo;
+ }
+ return kCFCompareLessThan;
+ } else if (domain2 == NULL) {
+ return kCFCompareGreaterThan;
+ }
+
+ // forward (A, AAAA) domains sort before reverse (PTR) domains
+ 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);
+
+ // compare domain labels
+ result = CFStringCompare(label1, label2, kCFCompareCaseInsensitive);
+ if (result != kCFCompareEqualTo) {
+ goto done;
+ }
+ }
+
+ // longer labels (corp.apple.com) sort before shorter labels (apple.com)
+ if (n1 > n2) {
+ result = kCFCompareLessThan;
+ goto done;
+ } else if (n1 < n2) {
+ result = kCFCompareGreaterThan;
+ goto done;
+ }
+
+ // sort by search order
+ result = compareBySearchOrder(val1, val2, context);
+
+ done :
+
+ if (labels1 != NULL) CFRelease(labels1);
+ if (labels2 != NULL) CFRelease(labels2);
+ return result;
+}
+
+
+__private_extern__
+CFDictionaryRef
+proxy_configuration_update(CFDictionaryRef defaultProxy,
+ CFDictionaryRef services,
+ CFArrayRef serviceOrder)
+{
+ CFIndex i;
+ CFMutableDictionaryRef myDefault;
+ Boolean myOrderAdded = FALSE;
+ CFMutableDictionaryRef newProxy = NULL;
+ CFIndex n_proxies;
+ CFDictionaryRef proxy;
+ CFMutableArrayRef proxies;
+
+ SCLog(TRUE, LOG_DEBUG, CFSTR("defaultProxy : %@"), defaultProxy ? defaultProxy : (CFTypeRef)CFSTR("NULL"));
+ SCLog(TRUE, LOG_DEBUG, CFSTR("services : %@"), services ? services : (CFTypeRef)CFSTR("NULL"));
+ SCLog(TRUE, LOG_DEBUG, CFSTR("serviceOrder : %@"), serviceOrder ? serviceOrder : (CFTypeRef)CFSTR("NULL"));
+
+ // establish full list of proxies
+
+ proxies = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+
+ // collect (and add) any "supplemental" proxy configurations
+
+ add_supplemental_proxies(proxies, services, serviceOrder);
+
+ // add the "default" proxy
+
+ add_default_proxy(proxies, defaultProxy, &myOrderAdded);
+
+ // sort proxies, cleanup
+
+ n_proxies = CFArrayGetCount(proxies);
+ if (n_proxies > 1) {
+ CFArraySortValues(proxies, CFRangeMake(0, n_proxies), compareDomain, NULL);
+ }
+
+ // cleanup
+
+ for (i = n_proxies - 1; i >= 0; i--) {
+ proxy = CFArrayGetValueAtIndex(proxies, i);
+
+ if ((i > 0) &&
+ !CFDictionaryContainsKey(proxy, kSCPropNetProxiesSupplementalMatchDomain)) {
+ // remove non-supplemental proxy
+ CFArrayRemoveValueAtIndex(proxies, i);
+ n_proxies--;
+ continue;
+ }
+
+ newProxy = CFDictionaryCreateMutableCopy(NULL, 0, proxy);
+ CFDictionaryRemoveValue(newProxy, PROXY_MATCH_ORDER_KEY);
+ CFDictionaryRemoveValue(newProxy, ORDER_KEY);
+ CFArraySetValueAtIndex(proxies, i, newProxy);
+ CFRelease(newProxy);
+ }
+
+ // update the default proxy
+
+ myDefault = CFDictionaryCreateMutableCopy(NULL,
+ 0,
+ CFArrayGetValueAtIndex(proxies, 0));
+ if (myOrderAdded && (n_proxies > 1)) {
+ CFDictionaryRef proxy;
+
+ proxy = CFArrayGetValueAtIndex(proxies, 1);
+ if (CFDictionaryContainsKey(proxy, kSCPropNetProxiesSupplementalMatchDomain)) {
+ // if not a supplemental "default" proxy (a match domain name is
+ // present)
+ CFDictionaryRemoveValue(myDefault, PROXY_MATCH_ORDER_KEY);
+ }
+ }
+ CFArraySetValueAtIndex(proxies, 0, myDefault);
+ CFRelease(myDefault);
+
+ // establish proxy configuration
+
+ if (n_proxies > 0) {
+ CFDictionaryRef scoped;
+ Boolean skip = FALSE;
+ CFArrayRef supplemental;
+
+ proxy = CFArrayGetValueAtIndex(proxies, 0);
+ if (!CFDictionaryContainsKey(proxy, kSCPropNetProxiesSupplementalMatchDomain)) {
+ // if we have "a" default (non-supplemental) proxy
+ newProxy = CFDictionaryCreateMutableCopy(NULL, 0, proxy);
+ CFDictionaryRemoveValue(newProxy, kSCPropNetProxiesSupplementalMatchDomains);
+ CFDictionaryRemoveValue(newProxy, kSCPropNetProxiesSupplementalMatchOrders);
+ skip = TRUE;
+ } else {
+ newProxy = CFDictionaryCreateMutable(NULL,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ }
+
+ // collect (and add) any "supplemental" proxy configurations
+
+ supplemental = copy_supplemental_proxies(proxies, skip);
+ if (supplemental != NULL) {
+ CFDictionarySetValue(newProxy, kSCPropNetProxiesSupplemental, supplemental);
+ CFRelease(supplemental);
+ }
+
+ // collect (and add) any "scoped" proxy configurations
+
+ scoped = copy_scoped_proxies(services, serviceOrder);
+ if (scoped != NULL) {
+ CFDictionarySetValue(newProxy, kSCPropNetProxiesScoped, scoped);
+ CFRelease(scoped);
+ }
+ } else {
+ newProxy = NULL;
+ }
+
+ CFRelease(proxies);
+ return newProxy;
+}
+
+
+__private_extern__
+void
+proxy_configuration_init(CFBundleRef bundle)
+{
+ CFDictionaryRef dict;
+
+ dict = CFBundleGetInfoDictionary(bundle);
+ if (isA_CFDictionary(dict)) {
+ S_proxies_follow_dns = CFDictionaryGetValue(dict, CFSTR("SupplementalProxiesFollowSupplementalDNS"));
+ S_proxies_follow_dns = isA_CFBoolean(S_proxies_follow_dns);
+ }
+
+ return;
+}
+
+
+#ifdef MAIN
+
+static void
+mergeDict(const void *key, const void *value, void *context)
+{
+ CFMutableDictionaryRef newDict = (CFMutableDictionaryRef)context;
+
+ CFDictionarySetValue(newDict, key, value);
+ return;
+}
+
+
+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 proxy;
+ CFMutableDictionaryRef new_proxy;
+
+ proxy = CFDictionaryGetValue(state_dict, kSCEntNetProxies);
+ if (proxy != NULL) {
+ new_proxy = CFDictionaryCreateMutableCopy(NULL, 0, proxy);
+ } else {
+ new_proxy = CFDictionaryCreateMutable(NULL,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ }
+ CFDictionarySetValue(new_proxy, kSCPropInterfaceName, interface);
+ CFDictionarySetValue(state_dict, kSCEntNetProxies, new_proxy);
+ CFRelease(new_proxy);
+ }
+ } else if (CFEqual(entity_id, kSCEntNetProxies)) {
+ CFDictionaryRef proxy;
+
+ proxy = CFDictionaryGetValue(state_dict, kSCEntNetProxies);
+ if (proxy != NULL) {
+ CFStringRef domain;
+ CFMutableDictionaryRef new_proxy;
+
+ // if we already have some Setup: or State: proxy content
+ domain = CFArrayGetValueAtIndex(components, 0);
+ if (CFEqual(domain, kSCDynamicStoreDomainState)) {
+ // if we've already seen the Setup: key
+ new_proxy = CFDictionaryCreateMutableCopy(NULL, 0, (CFDictionaryRef)value);
+ CFDictionaryApplyFunction(proxy, mergeDict, new_proxy);
+ } else {
+ // if we've already seen the State: key
+ new_proxy = CFDictionaryCreateMutableCopy(NULL, 0, proxy);
+ CFDictionaryApplyFunction((CFDictionaryRef)value, mergeDict, new_proxy);
+ }
+ CFDictionarySetValue(state_dict, kSCEntNetProxies, new_proxy);
+ CFRelease(new_proxy);
+ } else {
+ CFDictionarySetValue(state_dict, kSCEntNetProxies, (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;
+ CFDictionaryRef newProxy = NULL;
+ CFStringRef pattern;
+ CFMutableArrayRef patterns;
+ CFStringRef primary = NULL;
+ CFMutableDictionaryRef primary_proxy = NULL;
+ 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);
+
+ // get IPv4, IPv6, and Proxies entities
+ 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,
+ kSCDynamicStoreDomainSetup,
+ kSCCompAnyRegex,
+ kSCEntNetProxies);
+ CFArrayAppendValue(patterns, pattern);
+ CFRelease(pattern);
+ pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
+ kSCDynamicStoreDomainState,
+ kSCCompAnyRegex,
+ kSCEntNetProxies);
+ 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);
+
+ // get primary service ID
+ 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;
+
+ // get proxy configuration for primary service
+ service_dict = CFDictionaryGetValue(service_state_dict, primary);
+ if (service_dict != NULL) {
+ CFDictionaryRef service_proxy;
+
+ service_proxy = CFDictionaryGetValue(service_dict, kSCEntNetProxies);
+ if (service_proxy != NULL) {
+ primary_proxy = CFDictionaryCreateMutableCopy(NULL, 0, service_proxy);
+ CFDictionaryRemoveValue(primary_proxy, kSCPropInterfaceName);
+ }
+ }
+ }
+ }
+
+ // get serviceOrder
+ 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);
+ }
+
+ // update proxy configuration
+ proxy_configuration_init(CFBundleGetMainBundle());
+ newProxy = proxy_configuration_update(primary_proxy,
+ service_state_dict,
+ service_order);
+ if (newProxy != NULL) {
+ SCPrint(TRUE, stdout, CFSTR("%@\n"), newProxy);
+ CFRelease(newProxy);
+ }
+
+ // cleanup
+ if (setup_global_ipv4 != NULL) CFRelease(setup_global_ipv4);
+ if (state_global_ipv4 != NULL) CFRelease(state_global_ipv4);
+ CFRelease(service_state_dict);
+ CFRelease(store);
+
+ /* not reached */
+ exit(0);
+ return 0;
+}
+#endif
+