#include "ProxySupport.h"
#include <CFNetwork/CFNetworkPriv.h>
#include "CFNetworkInternal.h"
#include <CoreFoundation/CFStreamPriv.h>
#ifndef __WIN32__
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#endif
#if defined(__MACH__)
#include <JavaScriptGlue/JavaScriptGlue.h>
#include <SystemConfiguration/SystemConfiguration.h>
#ifdef __CONSTANT_CFSTRINGS__
#define _kProxySupportCFNetworkBundleID CFSTR("com.apple.CFNetwork")
#define _kProxySupportLocalhost CFSTR("localhost")
#define _kProxySupportIPv4Loopback CFSTR("127.0.0.1")
#define _kProxySupportIPv6Loopback CFSTR("::1")
#define _kProxySupportDot CFSTR(".")
#define _kProxySupportSlash CFSTR("/")
#define _kProxySupportDotZero CFSTR(".0")
#define _kProxySupportDotFormat CFSTR(".%@")
#define _kProxySupportStar CFSTR("*")
#define _kProxySupportColon CFSTR(":")
#define _kProxySupportSemiColon CFSTR(";")
#define _kProxySupportFILEScheme CFSTR("file")
#define _kProxySupportHTTPScheme CFSTR("http")
#define _kProxySupportHTTPSScheme CFSTR("https")
#define _kProxySupportHTTPPort CFSTR("80")
#define _kProxySupportFTPScheme CFSTR("ftp")
#define _kProxySupportFTPSScheme CFSTR("ftps")
#define _kProxySupportFTPPort CFSTR("21")
#define _kProxySupportSOCKS4Scheme CFSTR("socks4")
#define _kProxySupportSOCKS5Scheme CFSTR("socks5")
#define _kProxySupportSOCKSPort CFSTR("1080")
#define _kProxySupportDIRECT CFSTR("DIRECT")
#define _kProxySupportPROXY CFSTR("PROXY")
#define _kProxySupportSOCKS CFSTR("SOCKS")
#define _kProxySupportGETMethod CFSTR("GET")
#define _kProxySupportURLLongFormat CFSTR("%@://%@:%@@%@:%d")
#define _kProxySupportURLShortFormat CFSTR("%@://%@:%d")
#define _kProxySupportExceptionsList CFSTR("ExceptionsList")
#define _kProxySupportLoadingPacPrivateMode CFSTR("_kProxySupportLoadingPacPrivateMode")
#define _kProxySupportPacSupportFileName CFSTR("PACSupport")
#define _kProxySupportJSExtension CFSTR("js")
#define _kProxySupportExpiresHeader CFSTR("Expires")
#define _kProxySupportNowHeader CFSTR("Date")
#else
static CONST_STRING_DECL(_kProxySupportCFNetworkBundleID, "com.apple.CFNetwork")
static CONST_STRING_DECL(_kProxySupportLocalhost, "localhost")
static CONST_STRING_DECL(_kProxySupportIPv4Loopback, "127.0.0.1")
static CONST_STRING_DECL(_kProxySupportIPv6Loopback, "::1")
static CONST_STRING_DECL(_kProxySupportDot, ".")
static CONST_STRING_DECL(_kProxySupportSlash, "/")
static CONST_STRING_DECL(_kProxySupportDotZero, ".0")
static CONST_STRING_DECL(_kProxySupportDotFormat, ".%@")
static CONST_STRING_DECL(_kProxySupportStar, "*")
static CONST_STRING_DECL(_kProxySupportColon, ":")
static CONST_STRING_DECL(_kProxySupportSemiColon, ";")
static CONST_STRING_DECL(_kProxySupportFILEScheme, "file")
static CONST_STRING_DECL(_kProxySupportHTTPScheme, "http")
static CONST_STRING_DECL(_kProxySupportHTTPSScheme, "https")
static CONST_STRING_DECL(_kProxySupportHTTPPort, "80")
static CONST_STRING_DECL(_kProxySupportFTPScheme, "ftp")
static CONST_STRING_DECL(_kProxySupportFTPSScheme, "ftps")
static CONST_STRING_DECL(_kProxySupportFTPPort, "21")
static CONST_STRING_DECL(_kProxySupportSOCKS4Scheme, "socks4")
static CONST_STRING_DECL(_kProxySupportSOCKS5Scheme, "socks5")
static CONST_STRING_DECL(_kProxySupportSOCKSPort, "1080")
static CONST_STRING_DECL(_kProxySupportDIRECT, "DIRECT")
static CONST_STRING_DECL(_kProxySupportPROXY, "PROXY")
static CONST_STRING_DECL(_kProxySupportSOCKS, "SOCKS")
static CONST_STRING_DECL(_kProxySupportGETMethod, "GET")
static CONST_STRING_DECL(_kProxySupportURLLongFormat, "%@://%@:%@@%@:%d")
static CONST_STRING_DECL(_kProxySupportURLShortFormat, "%@://%@:%d")
static CONST_STRING_DECL(_kProxySupportExceptionsList, "ExceptionsList")
static CONST_STRING_DECL(_kProxySupportLoadingPacPrivateMode, "_kProxySupportLoadingPacPrivateMode")
static CONST_STRING_DECL(_kProxySupportPacSupportFileName, "PACSupport")
static CONST_STRING_DECL(_kProxySupportJSExtension, "js")
static CONST_STRING_DECL(_kProxySupportExpiresHeader, "Expires")
static CONST_STRING_DECL(_kProxySupportNowHeader, "Date")
#endif
static JSObjectRef _JSDnsResolveFunction(void* context, JSObjectRef ctxt, CFArrayRef args);
static JSObjectRef _JSPrimaryIpv4AddressesFunction(void* context, JSObjectRef ctxt, CFArrayRef args);
#elif defined(__WIN32__)
#include <winsock2.h>
#include <ws2tcpip.h> // for ipv6
#include <wininet.h> // for InternetTimeToSystemTime
#include <objbase.h>
typedef struct _JSRunGuts JSRun, *JSRunRef;
CF_EXPORT CFAbsoluteTime _CFAbsoluteTimeFromFileTime(FILETIME* ftime);
static const char *_CFDLLPath(void) {
static TCHAR cachedPath[MAX_PATH+1] = "";
if ('\0' == cachedPath[0]) {
#if defined(DEBUG)
char *DLLFileName = "CFNetwork_debug";
#elif defined(PROFILE)
char *DLLFileName = "CFNetwork_profile";
#else
char *DLLFileName = "CFNetwork";
#endif
HMODULE ourModule = GetModuleHandle(DLLFileName);
assert(ourModule);
DWORD wResult = GetModuleFileName(ourModule, cachedPath, MAX_PATH+1);
assert(wResult > 0); assert(wResult < MAX_PATH+1);
CFIndex idx;
for (idx = wResult - 1; idx; idx--) {
if ('\\' == cachedPath[idx]) {
cachedPath[idx] = '\0';
break;
}
}
}
return cachedPath;
}
#endif
static CFStringRef _JSFindProxyForURL(CFURLRef pac, CFURLRef url, CFStringRef host);
static CFStringRef _JSFindProxyForURLAsync(CFURLRef pac, CFURLRef url, CFStringRef host, Boolean *mustBlock);
#define PAC_STREAM_LOAD_TIMEOUT 30.0
static CFReadStreamRef BuildStreamForPACURL(CFAllocatorRef alloc, CFURLRef pacURL, CFURLRef targetURL, CFStringRef targetScheme, CFStringRef targetHost, _CFProxyStreamCallBack callback, void *clientInfo);
static CFStringRef _loadJSSupportFile(void);
static CFStringRef _loadPACFile(CFAllocatorRef alloc, CFURLRef pac, CFAbsoluteTime *expires, CFStreamError *err);
static JSRunRef _createJSRuntime(CFAllocatorRef alloc, CFStringRef js_support, CFStringRef js_pac);
static void _freeJSRuntime(JSRunRef runtime);
static CFStringRef _callPACFunction(CFAllocatorRef alloc, JSRunRef runtime, CFURLRef url, CFStringRef host);
static CFArrayRef _resolveDNSName(CFStringRef name);
static CFReadStreamRef _streamForPACFile(CFAllocatorRef alloc, CFURLRef pac, Boolean *isFile);
CFStringRef _stringFromLoadedPACStream(CFAllocatorRef alloc, CFMutableDataRef contents, CFReadStreamRef stream, CFAbsoluteTime *expires);
static void _JSSetEnvironmentForPAC(CFAllocatorRef alloc, CFURLRef url, CFAbsoluteTime expires, CFStringRef pacString);
static inline Boolean _proxyEnabled(CFTypeRef enabledEntry) {
if (!enabledEntry) return TRUE;
if (CFGetTypeID(enabledEntry) == CFNumberGetTypeID()) {
SInt32 val;
CFNumberGetValue(enabledEntry, kCFNumberSInt32Type, &val);
return (val != 0);
}
return (enabledEntry == kCFBooleanTrue);
}
#if 0
Boolean
_CFNetworkCFHostDoesNeedProxy(CFHostRef host, CFArrayRef bypasses, CFBooleanRef localBypass) {
CFArrayRef names = CFHostGetNames(host, NULL);
localBypass = _proxyEnabled(localBypass) ? kCFBooleanTrue : kCFBooleanFalse;
if (names && CFArrayGetCount(names)) {
return _CFNetworkDoesNeedProxy((CFStringRef)CFArrayGetValueAtIndex(names, 0), bypasses, localBypass);
}
names = CFHostGetAddressing(host, NULL);
if (names && CFArrayGetCount(names)) {
CFDataRef saData = (CFDataRef)CFArrayGetValueAtIndex(names, 0);
CFStringRef name = stringFromAddr((const struct sockaddr*)CFDataGetBytePtr(saData), CFDataGetLength(saData));
if (name) {
Boolean result = _CFNetworkDoesNeedProxy(name, bypasses, localBypass);
CFRelease(name);
return result;
}
return FALSE;
}
return TRUE;
}
#endif
Boolean
_CFNetworkDoesNeedProxy(CFStringRef hostname, CFArrayRef bypasses, CFBooleanRef localBypass) {
Boolean result = TRUE;
CFIndex i, hostnc, count, length;
CFArrayRef hostnodes;
struct in_addr ip;
Boolean is_ip = FALSE;
if (!hostname) return TRUE;
localBypass = _proxyEnabled(localBypass) ? kCFBooleanTrue : kCFBooleanFalse;
if (CFStringCompare(hostname, _kProxySupportLocalhost, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
return FALSE;
if (CFStringCompare(hostname, _kProxySupportIPv4Loopback, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
return FALSE;
if (CFStringCompare(hostname, _kProxySupportIPv6Loopback, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
return FALSE;
length = CFStringGetLength(hostname);
if (localBypass && CFEqual(localBypass, kCFBooleanTrue) && CFStringFind(hostname, _kProxySupportDot, 0).location == kCFNotFound)
return FALSE;
count = bypasses ? CFArrayGetCount(bypasses) : 0;
if (!count) return result;
hostnodes = CFStringCreateArrayBySeparatingStrings(NULL, hostname, _kProxySupportDot);
hostnc = hostnodes ? CFArrayGetCount(hostnodes) : 0;
if (!hostnc) {
CFRelease(hostnodes);
hostnodes = NULL;
}
if (((hostnc == 4) || ((hostnc == 5) && (CFStringGetLength((CFStringRef)CFArrayGetValueAtIndex(hostnodes, 4)) == 0))) &&
(length <= 16))
{
UInt8 stack_buffer[32];
UInt8* buffer = stack_buffer;
CFIndex bufferLength = sizeof(stack_buffer);
CFAllocatorRef allocator = CFGetAllocator(hostname);
buffer = _CFStringGetOrCreateCString(allocator, hostname, buffer, &bufferLength, kCFStringEncodingASCII);
if (inet_pton(AF_INET, (const char*)buffer, &ip) == 1)
is_ip = TRUE;
if (buffer != stack_buffer)
CFAllocatorDeallocate(allocator, buffer);
}
for (i = 0; result && (i < count); i++) {
CFStringRef bypass = (CFStringRef)CFArrayGetValueAtIndex(bypasses, i);
if (CFStringCompare(hostname, bypass, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
result = FALSE;
}
else if (is_ip && CFStringFindWithOptions(bypass, _kProxySupportSlash, CFRangeMake(0, CFStringGetLength(bypass)), 0, FALSE)) {
CFArrayRef pieces = CFStringCreateArrayBySeparatingStrings(NULL, bypass, _kProxySupportSlash);
if (pieces && (CFArrayGetCount(pieces) == 2)) {
SInt32 cidr = CFStringGetIntValue((CFStringRef)CFArrayGetValueAtIndex(pieces, 1));
if ((cidr > 0) && (cidr < 33)) {
CFArrayRef bypassnodes = CFStringCreateArrayBySeparatingStrings(NULL,
(CFStringRef)CFArrayGetValueAtIndex(pieces, 0),
_kProxySupportDot);
if (bypassnodes) {
CFIndex bypassnc = CFArrayGetCount(bypassnodes);
if (bypassnc <= 4) {
CFIndex n;
CFMutableStringRef cp = CFStringCreateMutableCopy(CFGetAllocator(hostname),
0,
(CFStringRef)CFArrayGetValueAtIndex(bypassnodes, 0));
for (n = 1; n < 4; n++) {
if (n >= bypassnc)
CFStringAppend(cp, _kProxySupportDotZero);
else {
CFStringRef piece = (CFStringRef)CFArrayGetValueAtIndex(bypassnodes, n);
if (CFStringGetLength(piece))
CFStringAppendFormat(cp, NULL, _kProxySupportDotFormat, piece);
else
CFStringAppend(cp, _kProxySupportDotZero);
}
}
n = CFStringGetLength(cp);
if (n <= 16) {
UInt8 stack_buffer[32];
UInt8* buffer = stack_buffer;
CFIndex bufferLength = sizeof(stack_buffer);
CFAllocatorRef allocator = CFGetAllocator(hostname);
struct in_addr bypassip;
buffer = _CFStringGetOrCreateCString(allocator, cp, buffer, &bufferLength, kCFStringEncodingASCII);
if ((inet_pton(AF_INET, (const char*)buffer, &bypassip) == 1) &&
(((((1 << cidr) - 1) << (32 - cidr)) & ntohl(*((uint32_t*)(&ip)))) == ntohl(*((uint32_t*)(&bypassip)))))
{
result = FALSE;
}
if (buffer != stack_buffer)
CFAllocatorDeallocate(allocator, buffer);
}
CFRelease(cp);
}
CFRelease(bypassnodes);
}
}
}
if (pieces)
CFRelease(pieces);
}
else if (hostnodes) {
CFIndex bypassnc;
CFArrayRef bypassnodes = CFStringCreateArrayBySeparatingStrings(NULL, bypass, _kProxySupportDot);
if (!bypassnodes) continue;
bypassnc = CFArrayGetCount(bypassnodes);
if (bypassnc > 1) {
CFIndex j = hostnc - 1;
CFIndex k = bypassnc - 1;
while ((j >= 0) && (k >= 0)) {
CFStringRef hostnode = (CFStringRef)CFArrayGetValueAtIndex(hostnodes, j);
CFStringRef bypassnode = (CFStringRef)CFArrayGetValueAtIndex(bypassnodes, k);
if ((k == 0) && (CFStringGetLength(bypassnode) == 0))
bypassnode = _kProxySupportStar;
if (CFStringCompare(hostnode, bypassnode, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
if (!j && !k) {
result = FALSE;
break;
}
else {
j--, k--;
}
}
else if (CFStringCompare(bypassnode, _kProxySupportStar, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
while (k >= 0) {
bypassnode = (CFStringRef)CFArrayGetValueAtIndex(bypassnodes, k);
if ((k == 0) && (CFStringGetLength(bypassnode) == 0))
bypassnode = _kProxySupportStar;
if (CFStringCompare(bypassnode, _kProxySupportStar, kCFCompareCaseInsensitive) != kCFCompareEqualTo)
break;
k--;
}
if (k < 0) {
result = FALSE;
break;
}
else {
while (j >= 0) {
hostnode = (CFStringRef)CFArrayGetValueAtIndex(hostnodes, j);
if (CFStringCompare(bypassnode, hostnode, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
break;
j--;
}
if (j < 0)
break;
}
}
else
break;
}
}
CFRelease(bypassnodes);
}
}
if (hostnodes)
CFRelease(hostnodes);
return result;
}
static CFURLRef _URLForProxyEntry(CFAllocatorRef alloc, CFStringRef entry, CFIndex startIndex, CFStringRef scheme) {
CFCharacterSetRef whitespaceSet = CFCharacterSetGetPredefined(kCFCharacterSetWhitespace);
CFIndex len = CFStringGetLength(entry);
CFRange colonRange = CFStringFind(entry, _kProxySupportColon, 0);
CFStringRef host;
CFStringRef portString;
Boolean hasPort = TRUE;
CFMutableStringRef urlString;
CFURLRef url;
if (colonRange.location == kCFNotFound) {
colonRange.location = len;
hasPort = FALSE;
}
while (startIndex < len && CFCharacterSetIsCharacterMember(whitespaceSet, CFStringGetCharacterAtIndex(entry, startIndex))) {
startIndex ++;
}
if (startIndex >= colonRange.location) {
return NULL;
}
host = CFStringCreateWithSubstring(CFGetAllocator(entry), entry, CFRangeMake(startIndex, colonRange.location - startIndex));
if (hasPort) {
portString = CFStringCreateWithSubstring(CFGetAllocator(entry), entry, CFRangeMake(colonRange.location + 1, len - colonRange.location - 1));
} else if (CFStringCompare(scheme, _kProxySupportHTTPScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo ||
CFStringCompare(scheme, _kProxySupportHTTPSScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
portString = _kProxySupportHTTPPort;
CFRetain(portString);
} else if (CFStringCompare(scheme, _kProxySupportFTPScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
portString = _kProxySupportFTPPort;
CFRetain(portString);
} else if (CFStringCompare(scheme, _kProxySupportSOCKS4Scheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo || CFStringCompare(scheme, _kProxySupportSOCKS5Scheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
portString = _kProxySupportSOCKSPort;
CFRetain(portString);
} else {
CFRelease(host);
return NULL;
}
urlString = CFStringCreateMutable(alloc, CFStringGetLength(scheme) + CFStringGetLength(host) + CFStringGetLength(portString) + 4);
CFStringAppend(urlString, scheme);
CFStringAppendCString(urlString, "://", kCFStringEncodingASCII);
CFStringAppend(urlString, host);
CFRelease(host);
CFStringAppendCString(urlString, ":", kCFStringEncodingASCII);
CFStringAppend(urlString, portString);
CFRelease(portString);
url = CFURLCreateWithString(NULL, urlString, NULL);
CFRelease(urlString);
return url;
}
static void _appendProxiesFromPACResponse(CFAllocatorRef alloc, CFMutableArrayRef proxyList, CFStringRef pacResponse, CFStringRef scheme) {
CFArrayRef entries;
CFIndex i, c;
if (!pacResponse) return;
entries = CFStringCreateArrayBySeparatingStrings(alloc, pacResponse, _kProxySupportSemiColon);
c = CFArrayGetCount(entries);
Boolean isFTP = (CFStringCompare(scheme, _kProxySupportFTPScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo);
for (i = 0; i < c; i++) {
CFURLRef to_add = NULL;
CFStringRef untrimmedEntry = (CFStringRef)CFArrayGetValueAtIndex(entries, i);
CFMutableStringRef entry = untrimmedEntry ? CFStringCreateMutableCopy(alloc, CFStringGetLength(untrimmedEntry), untrimmedEntry) : NULL;
CFIndex entryLength;
CFRange range;
if (!entry) continue;
CFStringTrimWhitespace(entry);
entryLength = CFStringGetLength(entry);
if (entryLength >= 6 && CFStringFindWithOptions(entry, _kProxySupportDIRECT, CFRangeMake(0, 6), kCFCompareCaseInsensitive, NULL)) {
CFArrayAppendValue(proxyList, kCFNull);
}
else if (entryLength >= 5 && CFStringFindWithOptions(entry, _kProxySupportPROXY, CFRangeMake(0, 5), kCFCompareCaseInsensitive, &range)) {
CFIndex urlStart = 5;
to_add = _URLForProxyEntry(CFGetAllocator(proxyList), entry, urlStart, scheme);
if (to_add && isFTP) {
CFURLRef extra = _URLForProxyEntry(CFGetAllocator(proxyList), entry, urlStart, _kProxySupportHTTPScheme);
if (extra) {
CFArrayAppendValue(proxyList, extra);
CFRelease(extra);
}
}
}
else if (entryLength >= 5 && CFStringFindWithOptions(entry, _kProxySupportSOCKS, CFRangeMake(0, 5), kCFCompareCaseInsensitive, &range)) {
to_add = _URLForProxyEntry(CFGetAllocator(proxyList), entry, 5, _kProxySupportSOCKS5Scheme);
}
if (to_add) {
CFArrayAppendValue(proxyList, to_add);
CFRelease(to_add);
}
CFRelease(entry);
}
CFRelease(entries);
}
static CFURLRef proxyURLForComponents(CFAllocatorRef alloc, CFStringRef scheme, CFStringRef host, SInt32 port, CFStringRef user, CFStringRef password) {
CFStringRef urlString;
CFURLRef url;
if (user) {
urlString = CFStringCreateWithFormat(alloc, NULL, _kProxySupportURLLongFormat, scheme, user, password, host, port);
} else {
urlString = CFStringCreateWithFormat(alloc, NULL, _kProxySupportURLShortFormat, scheme, host, port);
}
url = CFURLCreateWithString(alloc, urlString, NULL);
CFRelease(urlString);
return url;
}
CFMutableArrayRef
_CFNetworkFindProxyForURLAsync(CFStringRef scheme, CFURLRef url, CFStringRef host, CFDictionaryRef proxies, _CFProxyStreamCallBack callback, void *clientInfo, CFReadStreamRef *proxyStream) {
CFAllocatorRef alloc = url ? CFGetAllocator(url) : host ? CFGetAllocator(host) : proxies ? CFGetAllocator(proxies) : kCFAllocatorDefault;
CFMutableArrayRef result = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
if (!proxies) {
CFArrayAppendValue(result, kCFNull);
return result;
}
if (host) {
CFRetain(host);
} else {
if (!url) {
CFArrayAppendValue(result, kCFNull);
return result;
}
host = CFURLCopyHostName(url);
if (!host) {
CFArrayAppendValue(result, kCFNull);
return result;
}
}
if (!scheme)
scheme = url ? CFURLCopyScheme(url) : NULL;
else
CFRetain(scheme);
do {
CFStringRef proxy;
CFNumberRef port;
CFTypeRef enabled = NULL;
CFArrayRef bypass = (CFArrayRef)CFDictionaryGetValue(proxies, _kProxySupportExceptionsList);
CFBooleanRef localBypass = _proxyEnabled(CFDictionaryGetValue(proxies, kCFStreamPropertyProxyLocalBypass)) ? kCFBooleanTrue : kCFBooleanFalse;
if (bypass && CFGetTypeID(bypass) != CFArrayGetTypeID()) {
bypass = NULL;
}
if (scheme) {
SInt32 default_port = 0;
CFStringRef proxy_key = NULL, port_key = NULL;
#if defined(__MACH__)
CFStringRef enable_key = NULL;
#endif
#if defined(__MACH__)
enabled = CFDictionaryGetValue(proxies, _kCFStreamPropertyHTTPProxyProxyAutoConfigEnable);
#endif
if (_proxyEnabled(enabled)) {
proxy = (CFStringRef)CFDictionaryGetValue(proxies, _kCFStreamPropertyHTTPProxyProxyAutoConfigURLString);
if (proxy && CFGetTypeID(proxy) == CFStringGetTypeID()) {
CFStringRef url_str = CFURLCreateStringByAddingPercentEscapes(alloc, proxy, NULL, NULL, kCFStringEncodingUTF8);
CFURLRef pac = CFURLCreateWithString(alloc, url_str, NULL);
CFRelease(url_str);
if (pac) {
Boolean mustLoad = FALSE;
CFStringRef list;
if (callback) {
list = _JSFindProxyForURLAsync(pac, url, host, &mustLoad);
} else {
list = _JSFindProxyForURL(pac, url, host);
mustLoad = FALSE;
}
if (mustLoad) {
*proxyStream = BuildStreamForPACURL(alloc, pac, url, scheme, host, callback, clientInfo);
CFRelease(result);
result = NULL; } else {
_appendProxiesFromPACResponse(alloc, result, list, scheme);
if (list) CFRelease(list);
}
CFRelease(pac);
}
break; } }
if (CFStringCompare(scheme, _kProxySupportHTTPScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
proxy_key = kCFStreamPropertyHTTPProxyHost;
port_key = kCFStreamPropertyHTTPProxyPort;
#if defined(__MACH__)
enable_key = kSCPropNetProxiesHTTPEnable;
#endif
default_port = 80;
}
else if (CFStringCompare(scheme, _kProxySupportHTTPSScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
proxy_key = kCFStreamPropertyHTTPSProxyHost;
port_key = kCFStreamPropertyHTTPSProxyPort;
#if defined(__MACH__)
enable_key = kSCPropNetProxiesHTTPSEnable;
#endif
default_port = 80;
}
else if (CFStringCompare(scheme, _kProxySupportFTPScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
proxy_key = kCFStreamPropertyFTPProxyHost;
port_key = kCFStreamPropertyFTPProxyPort;
#if defined(__MACH__)
enable_key = kSCPropNetProxiesFTPEnable;
#endif
default_port = 21;
}
else if (CFStringCompare(scheme, _kProxySupportFTPSScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
proxy_key = kCFStreamPropertyFTPProxyHost;
port_key = kCFStreamPropertyFTPProxyPort;
#if defined(__MACH__)
enable_key = kSCPropNetProxiesFTPEnable;
#endif
default_port = 990;
}
if (proxy_key) {
#if defined(__MACH__)
enabled = CFDictionaryGetValue(proxies, enable_key);
#endif
if (_proxyEnabled(enabled)) {
proxy = (CFStringRef)CFDictionaryGetValue(proxies, proxy_key);
if (proxy && CFGetTypeID(proxy) == CFStringGetTypeID() && _CFNetworkDoesNeedProxy(host, bypass, localBypass)) {
CFURLRef to_add;
SInt32 portNum;
port = (CFNumberRef)CFDictionaryGetValue(proxies, port_key);
if (!port || CFGetTypeID(port) != CFNumberGetTypeID() || !CFNumberGetValue(port, kCFNumberSInt32Type, &portNum)) {
portNum = default_port;
}
to_add = proxyURLForComponents(alloc, scheme, proxy, portNum, NULL, NULL);
if (proxy_key == kCFStreamPropertyFTPProxyHost) {
CFURLRef extra = proxyURLForComponents(alloc, _kProxySupportHTTPScheme, proxy, portNum == 21 ? 80 : portNum, NULL, NULL);
if (extra) {
CFArrayAppendValue(result, extra);
CFRelease(extra);
}
}
if (to_add) {
CFArrayAppendValue(result, to_add);
CFRelease(to_add);
}
break; } }
}
}
#if defined(__MACH__)
enabled = CFDictionaryGetValue(proxies, kSCPropNetProxiesSOCKSEnable);
#endif
if (_proxyEnabled(enabled)) {
CFStringRef user = (CFStringRef)CFDictionaryGetValue(proxies, kCFStreamPropertySOCKSUser);
CFStringRef pass = (CFStringRef)CFDictionaryGetValue(proxies, kCFStreamPropertySOCKSPassword);
CFStringRef version = (CFStringRef)CFDictionaryGetValue(proxies, kCFStreamPropertySOCKSVersion);
proxy = (CFStringRef)CFDictionaryGetValue(proxies, kCFStreamPropertySOCKSProxyHost);
if (proxy && CFGetTypeID(proxy) == CFStringGetTypeID() && (!bypass || !host || _CFNetworkDoesNeedProxy(host, bypass, localBypass))) {
CFStringRef socksScheme;
SInt32 portNum;
CFURLRef to_add;
port = (CFNumberRef)CFDictionaryGetValue(proxies, kCFStreamPropertySOCKSProxyPort);
if (!port || CFGetTypeID(port) != CFNumberGetTypeID() || !CFNumberGetValue(port, kCFNumberSInt32Type, &portNum)) {
portNum = 1080;
}
if (!user || !pass || CFGetTypeID(user) != CFStringGetTypeID() || CFGetTypeID(pass) != CFStringGetTypeID()) {
user = NULL;
pass = NULL;
}
socksScheme = (version && CFEqual(version, kCFStreamSocketSOCKSVersion4)) ? _kProxySupportSOCKS4Scheme : _kProxySupportSOCKS5Scheme;
to_add = proxyURLForComponents(alloc, socksScheme, proxy, portNum, user, pass);
if (to_add) {
CFArrayAppendValue(result, to_add);
CFRelease(to_add);
}
break; } }
CFArrayAppendValue(result, kCFNull);
} while (0);
if (scheme) CFRelease(scheme);
if (host) CFRelease(host);
return result;
}
static void
_ReadStreamClientCallBack(CFReadStreamRef stream, CFStreamEventType type, CFMutableDataRef contents) {
if (type == kCFStreamEventHasBytesAvailable) {
UInt8 buffer[8192];
CFIndex bytesRead = CFReadStreamRead(stream, buffer, sizeof(buffer));
if (bytesRead > 0)
CFDataAppendBytes(contents, buffer, bytesRead);
}
}
static void
_RunLoopTimerCallBack(CFRunLoopTimerRef timer, Boolean* timedout) {
*timedout = TRUE;
}
static CFStreamError
_LoadStreamIntoData(CFReadStreamRef stream, CFMutableDataRef contents, CFTimeInterval timeout, Boolean isFile) {
CFStreamError result = {0, 0};
CFRunLoopRef rl = CFRunLoopGetCurrent();
CFStreamClientContext ctxt = {0, contents, NULL, NULL, NULL};
CFReadStreamSetClient(stream,
kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred,
(CFReadStreamClientCallBack)_ReadStreamClientCallBack,
&ctxt);
CFReadStreamScheduleWithRunLoop(stream, rl, _kProxySupportLoadingPacPrivateMode);
if (!CFReadStreamOpen(stream))
result = CFReadStreamGetError(stream);
else {
Boolean timedout = FALSE;
CFRunLoopTimerContext timerCtxt = {0, &timedout, NULL, NULL, NULL};
CFRunLoopTimerRef t = timeout ? CFRunLoopTimerCreate(CFGetAllocator(stream),
CFAbsoluteTimeGetCurrent() + timeout,
0,
0,
0,
(CFRunLoopTimerCallBack)_RunLoopTimerCallBack,
&timerCtxt) : NULL;
if (t)
CFRunLoopAddTimer(rl, t, _kProxySupportLoadingPacPrivateMode);
do {
CFStreamStatus status = CFReadStreamGetStatus(stream);
if (status == kCFStreamStatusAtEnd || status == kCFStreamStatusError)
break;
CFRunLoopRunInMode(_kProxySupportLoadingPacPrivateMode, 1e+20, TRUE);
} while (!timedout);
if (t) {
CFRunLoopRemoveTimer(rl, t, _kProxySupportLoadingPacPrivateMode);
CFRelease(t);
}
result = CFReadStreamGetError(stream);
if (result.error || timedout) {
CFIndex len = CFDataGetLength(contents);
if (len)
CFDataDeleteBytes(contents, CFRangeMake(0, len));
}
if (timedout) {
result.domain = kCFStreamErrorDomainCustom;
result.error = -1;
}
CFReadStreamClose(stream);
}
CFReadStreamUnscheduleFromRunLoop(stream, rl, _kProxySupportLoadingPacPrivateMode);
CFReadStreamSetClient(stream, 0, NULL, NULL);
return result;
}
CFStringRef
_loadJSSupportFile(void) {
static CFURLRef _JSRuntimeFunctionsLocation = NULL;
static CFStringRef _JSRuntimeFunctions = NULL;
if (_JSRuntimeFunctionsLocation == NULL) {
#if !defined(__WIN32__)
CFBundleRef cfNetworkBundle = CFBundleGetBundleWithIdentifier(_kProxySupportCFNetworkBundleID);
_JSRuntimeFunctionsLocation = CFBundleCopyResourceURL(cfNetworkBundle, _kProxySupportPacSupportFileName, _kProxySupportJSExtension, NULL);
#else
static UInt8 path[MAX_PATH+1];
const char *resourcePath = _CFDLLPath();
strcpy(path, resourcePath);
strcat(path, "\\PACSupport.js");
_JSRuntimeFunctionsLocation = CFURLCreateFromFileSystemRepresentation(NULL, path, strlen(path), FALSE);
#endif
CFReadStreamRef stream = CFReadStreamCreateWithFile(NULL, _JSRuntimeFunctionsLocation);
if (stream) {
CFMutableDataRef contents = CFDataCreateMutable(NULL, 0);
_LoadStreamIntoData(stream, contents, PAC_STREAM_LOAD_TIMEOUT, TRUE);
CFRelease(stream);
CFIndex bytesRead = CFDataGetLength(contents);
if (bytesRead) {
_JSRuntimeFunctions = CFStringCreateWithBytes(NULL, CFDataGetBytePtr(contents), bytesRead, kCFStringEncodingUTF8, TRUE);
}
CFRelease(contents);
}
}
return _JSRuntimeFunctions;
}
static CFReadStreamRef _streamForPACFile(CFAllocatorRef alloc, CFURLRef pac, Boolean *isFile) {
CFStringRef scheme = CFURLCopyScheme(pac);
CFReadStreamRef stream = NULL;
*isFile = FALSE;
if (!scheme || (CFStringCompare(scheme, _kProxySupportFILEScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo)) {
stream = CFReadStreamCreateWithFile(alloc, pac);
*isFile = TRUE;
}
else if ((CFStringCompare(scheme, _kProxySupportHTTPScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo) ||
(CFStringCompare(scheme, _kProxySupportHTTPSScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo))
{
CFHTTPMessageRef msg = CFHTTPMessageCreateRequest(alloc, _kProxySupportGETMethod, pac, kCFHTTPVersion1_0);
if (msg) {
stream = CFReadStreamCreateForHTTPRequest(alloc, msg);
if (stream)
CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue);
CFRelease(msg);
}
}
else if ((CFStringCompare(scheme, _kProxySupportFTPScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo) ||
(CFStringCompare(scheme, _kProxySupportFTPSScheme, kCFCompareCaseInsensitive) == kCFCompareEqualTo))
{
stream = CFReadStreamCreateWithFTPURL(alloc, pac);
}
return stream;
}
CFStringRef _stringFromLoadedPACStream(CFAllocatorRef alloc, CFMutableDataRef contents, CFReadStreamRef stream, CFAbsoluteTime *expires) {
CFHTTPMessageRef msg = (CFHTTPMessageRef)CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPResponseHeader);
CFStringRef result = NULL;
CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
*expires = now > 1 ? now - 1 : now;
if (msg) {
UInt32 code = CFHTTPMessageGetResponseStatusCode(msg);
if (code > 299) {
CFDataSetLength(contents, 0);
}
else {
CFStringRef expiryString = CFHTTPMessageCopyHeaderFieldValue(msg, _kProxySupportExpiresHeader);
if (expiryString) {
CFGregorianDate expiryDate;
CFTimeZoneRef expiryTZ = NULL;
if (_CFGregorianDateCreateWithString(alloc, expiryString, &expiryDate, &expiryTZ)) {
CFStringRef nowString = CFHTTPMessageCopyHeaderFieldValue(msg, _kProxySupportNowHeader);
if (nowString) {
CFTimeZoneRef nowTZ;
CFGregorianDate nowDate;
if (_CFGregorianDateCreateWithString(alloc, nowString, &nowDate, &nowTZ)) {
*expires = now + (CFGregorianDateGetAbsoluteTime(expiryDate, expiryTZ) - CFGregorianDateGetAbsoluteTime(nowDate, nowTZ));
} else {
*expires = CFGregorianDateGetAbsoluteTime(expiryDate, expiryTZ);
}
CFRelease(nowString);
} else {
*expires = CFGregorianDateGetAbsoluteTime(expiryDate, expiryTZ);
}
}
CFRelease(expiryString);
}
}
CFRelease(msg);
}
if (*expires < now) {
*expires = now + (24 * 60 * 60);
}
CFIndex bytesRead = CFDataGetLength(contents);
if (bytesRead) {
result = CFStringCreateWithBytes(alloc, CFDataGetBytePtr(contents), bytesRead, kCFStringEncodingUTF8, TRUE);
}
return result;
}
CFStringRef
_loadPACFile(CFAllocatorRef alloc, CFURLRef pac, CFAbsoluteTime *expires, CFStreamError *err) {
Boolean isFile;
CFReadStreamRef stream = _streamForPACFile(alloc, pac, &isFile);
CFStringRef result = NULL;
if (stream) {
CFMutableDataRef contents = CFDataCreateMutable(alloc, 0);
*err = _LoadStreamIntoData(stream, contents, PAC_STREAM_LOAD_TIMEOUT, isFile);
if (err->domain == 0) {
result = _stringFromLoadedPACStream(alloc, contents, stream, expires);
}
CFRelease(contents);
CFRelease(stream);
}
return result;
}
#if !defined(__WIN32__)
JSRunRef
_createJSRuntime(CFAllocatorRef alloc, CFStringRef js_support, CFStringRef js_pac) {
CFStringRef allCode = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@\n%@\n"), js_support, js_pac);
JSRunRef runtime = NULL;
if (allCode) {
JSLockInterpreter();
runtime = JSRunCreate(allCode, 0);
JSUnlockInterpreter();
}
if (!JSRunCheckSyntax(runtime)) {
JSRelease(runtime);
runtime = NULL;
}
if (runtime) {
JSObjectRef g = JSRunCopyGlobalObject(runtime);
JSObjectCallBacks c = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
if (g) {
JSObjectRef func;
c.callFunction = (JSObjectCallFunctionProcPtr)_JSDnsResolveFunction;
JSLockInterpreter();
func = JSObjectCreate(NULL, &c);
if (func) {
JSObjectSetProperty(g, CFSTR("__dnsResolve"), func);
JSRelease(func);
}
c.callFunction = (JSObjectCallFunctionProcPtr)_JSPrimaryIpv4AddressesFunction;
func = JSObjectCreate(NULL, &c);
if (func) {
JSObjectSetProperty(g, CFSTR("__primaryIPv4Addresses"), func);
JSRelease(func);
}
JSObjectRef strObj = JSObjectCreateWithCFType(CFSTR("MACH"));
JSObjectSetProperty(g, CFSTR("__platformName"), strObj);
JSRelease(strObj);
JSUnlockInterpreter();
JSRelease(g);
}
g = JSRunEvaluate(runtime);
if (g) JSRelease(g);
}
return runtime;
}
void
_freeJSRuntime(JSRunRef runtime) {
JSRelease(runtime);
}
CFStringRef
_callPACFunction(CFAllocatorRef alloc, JSRunRef runtime, CFURLRef url, CFStringRef host) {
JSObjectRef jsResult = NULL;
CFStringRef result = NULL;
JSObjectRef g = runtime ? JSRunCopyGlobalObject(runtime) : NULL;
JSObjectRef func = g ? JSObjectCopyProperty(g, CFSTR("__Apple_FindProxyForURL")) : NULL;
if (func) {
CFArrayRef cfArgs = NULL;
CFMutableArrayRef jsArgs;
CFTypeRef args[2];
CFURLRef absURL = CFURLCopyAbsoluteURL(url);
args[0] = CFURLGetString(absURL);
args[1] = host;
cfArgs = CFArrayCreate(alloc, args, 2, &kCFTypeArrayCallBacks);
CFRelease(absURL);
JSLockInterpreter();
jsArgs = JSCreateJSArrayFromCFArray(cfArgs);
CFRelease(cfArgs);
jsResult = JSObjectCallFunction(func, g, jsArgs);
JSUnlockInterpreter();
JSRelease(func);
CFRelease(jsArgs);
if (jsResult) {
result = (CFStringRef)JSObjectCopyCFValue(jsResult);
JSRelease(jsResult);
if (result && CFGetTypeID(result) != CFStringGetTypeID()) {
CFRelease(result);
result = NULL;
}
}
}
if (g) JSRelease(g);
return result;
}
#else
#define COBJMACROS
#include <ACTIVSCP.H>
#ifdef DO_COM_LOGGING
#define COM_LOG printf
#else
#define COM_LOG while(0) printf
#endif
struct _JSRunGuts {
IActiveScript* pActiveScript;
IActiveScriptParse* pActiveScriptParse;
};
static HRESULT _parseCodeChunk(CFStringRef code, JSRunRef runtime);
static void _prepareStringParam(CFStringRef str, VARIANTARG *variant);
static void _prepareStringArrayReturnValue(CFArrayRef cfArray, VARIANT *pVarResult);
static CFArrayRef _JSPrimaryIpv4AddressesFunction(void);
#define CHECK_HRESULT(hr, func) if (!SUCCEEDED(hr)) { \
CFLog(0, CFSTR("%s failed: 0x%X"), func, hResult); \
break; \
} else COM_LOG("==== Call to %s succeeded ====\n", func);
#define ITEM_NAME_ADDED_TO_JS OLESTR("_CFNetwork_Global_Routines_")
typedef struct {
IDispatch iDispatch; UInt32 refCount;
} Dispatcher;
static inline Dispatcher *
thisFromIDispatch(IDispatch *disp) {
return (Dispatcher*)((char *)disp - offsetof(Dispatcher, iDispatch));
}
enum {
DNSResolveDISPID = 22, PrimaryIPv4AddressesDISPID = 33, PlatformNameDISPID = 44 };
static HRESULT STDMETHODCALLTYPE DispatchQueryInterface(IDispatch *disp, REFIID riid, void **ppv);
static ULONG STDMETHODCALLTYPE DispatchAddRef(IDispatch *disp);
static ULONG STDMETHODCALLTYPE DispatchRelease(IDispatch *disp);
static HRESULT STDMETHODCALLTYPE DispatchGetTypeInfoCount(IDispatch *disp, unsigned int *pctinfo);
static HRESULT STDMETHODCALLTYPE DispatchGetTypeInfo(IDispatch *disp, unsigned int iTInfo, LCID lcid, ITypeInfo **ppTInfo);
static HRESULT STDMETHODCALLTYPE DispatchGetIDsOfNames(IDispatch *disp, REFIID riid, OLECHAR ** rgszNames, unsigned intcNames, LCID lcid, DISPID *rgDispId);
static HRESULT STDMETHODCALLTYPE DispatchInvoke(IDispatch *disp, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, unsigned int *puArgErr);
static IDispatchVtbl DispatchVTable = {
DispatchQueryInterface,
DispatchAddRef,
DispatchRelease,
DispatchGetTypeInfoCount,
DispatchGetTypeInfo,
DispatchGetIDsOfNames,
DispatchInvoke
};
static HRESULT STDMETHODCALLTYPE
DispatchQueryInterface(IDispatch *disp, REFIID riid, void **ppv) {
#ifdef DO_COM_LOGGING
LPOLESTR interfaceString;
StringFromIID(riid, &interfaceString);
COM_LOG("DispatchQueryInterface: %ls\n", interfaceString);
CoTaskMemFree(interfaceString);
#endif
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID (riid, &IID_IDispatch)) {
*ppv = disp;
DispatchAddRef(disp);
return NOERROR;
}
else {
*ppv = 0;
return E_NOINTERFACE;
}
}
static ULONG STDMETHODCALLTYPE
DispatchAddRef(IDispatch *disp) {
COM_LOG("DispatchAddRef\n");
Dispatcher *this = thisFromIDispatch(disp);
return ++this->refCount;
}
static ULONG STDMETHODCALLTYPE
DispatchRelease(IDispatch *disp) {
COM_LOG("DispatchRelease\n");
Dispatcher *this = thisFromIDispatch(disp);
if (--this->refCount == 0) {
CFAllocatorDeallocate(NULL, this);
return 0;
}
return this->refCount;
}
static HRESULT STDMETHODCALLTYPE
DispatchGetTypeInfoCount(IDispatch *disp, unsigned int *pctinfo) {
COM_LOG("DispatchGetTypeInfoCount\n");
if (pctinfo == NULL) {
return E_INVALIDARG;
}
else {
*pctinfo = 0;
return NOERROR;
}
}
static HRESULT STDMETHODCALLTYPE
DispatchGetTypeInfo(IDispatch *disp, unsigned int iTInfo, LCID lcid, ITypeInfo **ppTInfo) {
COM_LOG("DispatchGetTypeInfo\n");
assert(FALSE); return E_NOTIMPL;
}
static HRESULT STDMETHODCALLTYPE
DispatchGetIDsOfNames(IDispatch *disp, REFIID riid, OLECHAR **rgszNames, unsigned intcNames, LCID lcid, DISPID *rgDispId) {
HRESULT retVal = S_OK;
int i;
for (i = 0; i < intcNames; i++) {
if (wcscmp(OLESTR("__dnsResolve"), rgszNames[i]) == 0) {
COM_LOG("DispatchGetIDsOfNames - resolved DNSResolveDISPID\n");
rgDispId[i] = DNSResolveDISPID;
}
else if (wcscmp(OLESTR("__primaryIPv4Addresses"), rgszNames[i]) == 0) {
COM_LOG("DispatchGetIDsOfNames - resolved PrimaryIPv4AddressesDISPID\n");
rgDispId[i] = PrimaryIPv4AddressesDISPID;
}
else if (wcscmp(OLESTR("__platformName"), rgszNames[i]) == 0) {
COM_LOG("DispatchGetIDsOfNames - resolved PlatformNameDISPID\n");
rgDispId[i] = PlatformNameDISPID;
}
else {
COM_LOG("DispatchGetIDsOfNames - unknown member %ls\n", rgszNames[i]);
rgDispId[i] = DISPID_UNKNOWN;
retVal = DISP_E_UNKNOWNNAME;
}
}
return retVal;
}
static HRESULT STDMETHODCALLTYPE
DispatchInvoke(IDispatch *disp, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, unsigned int *puArgErr)
{
if (dispIdMember == DNSResolveDISPID) {
COM_LOG("DispatchInvoke - DNSResolveDISPID, wFlags=%d\n", wFlags);
assert(wFlags & DISPATCH_METHOD);
if (pDispParams->cArgs != 1)
return DISP_E_BADPARAMCOUNT;
if (pDispParams->cNamedArgs != 0)
return DISP_E_NONAMEDARGS;
if (pDispParams->rgvarg[0].vt != VT_BSTR) {
puArgErr = 0;
return DISP_E_TYPEMISMATCH;
}
BSTR bstrNameParam = pDispParams->rgvarg[0].bstrVal;
COM_LOG("DispatchInvoke - DNSResolveDISPID - input=%ls\n", pDispParams->rgvarg[0].bstrVal);
CFStringRef cfNameParam = CFStringCreateWithCharactersNoCopy(NULL, bstrNameParam, SysStringLen(bstrNameParam), kCFAllocatorNull);
CFArrayRef cfAddrList = _resolveDNSName(cfNameParam);
CFRelease(cfNameParam);
_prepareStringArrayReturnValue(cfAddrList, pVarResult);
if (cfAddrList) CFRelease(cfAddrList);
return S_OK;
}
else if (dispIdMember == PrimaryIPv4AddressesDISPID) {
COM_LOG("DispatchInvoke - PrimaryIPv4AddressesDISPID, wFlags=%d\n", wFlags);
assert(wFlags & DISPATCH_METHOD);
if (pDispParams->cArgs != 0)
return DISP_E_BADPARAMCOUNT;
if (pDispParams->cNamedArgs != 0)
return DISP_E_NONAMEDARGS;
CFArrayRef cfAddrList = _JSPrimaryIpv4AddressesFunction();
_prepareStringArrayReturnValue(cfAddrList, pVarResult);
if (cfAddrList) CFRelease(cfAddrList);
return S_OK;
}
else if (dispIdMember == PlatformNameDISPID) {
COM_LOG("DispatchInvoke - PlatformNameDISPID, wFlags=%d\n", wFlags);
assert(wFlags == DISPATCH_PROPERTYGET);
if (pDispParams->cArgs != 0)
return DISP_E_BADPARAMCOUNT;
if (pDispParams->cNamedArgs != 0)
return DISP_E_NONAMEDARGS;
VariantInit(pVarResult);
pVarResult->vt = VT_BSTR;
pVarResult->bstrVal = SysAllocString(OLESTR("Win32"));
return S_OK;
}
else {
COM_LOG("DispatchInvoke - UNKNOWN MEMBER REQUESTED\n");
return DISP_E_MEMBERNOTFOUND;
}
}
typedef struct {
IActiveScriptSite iSite; UInt32 refCount;
CFAllocatorRef alloc;
} ScriptSite;
static inline ScriptSite *
thisFromISite(IActiveScriptSite *site) {
return (ScriptSite*)((char *)site - offsetof(ScriptSite, iSite));
}
static HRESULT STDMETHODCALLTYPE SiteQueryInterface(IActiveScriptSite *site, REFIID riid, void **ppv);
static ULONG STDMETHODCALLTYPE SiteAddRef(IActiveScriptSite *site);
static ULONG STDMETHODCALLTYPE SiteRelease(IActiveScriptSite *site);
static HRESULT STDMETHODCALLTYPE SiteGetLCID(IActiveScriptSite *site, LCID *plcid);
static HRESULT STDMETHODCALLTYPE SiteGetItemInfo(IActiveScriptSite *site, LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown **ppunkItem, ITypeInfo **ppTypeInfo);
static HRESULT STDMETHODCALLTYPE SiteGetDocVersionString(IActiveScriptSite *site, BSTR *pbstrVersionString);
static HRESULT STDMETHODCALLTYPE SiteOnScriptTerminate(IActiveScriptSite *site, const VARIANT *pvarResult, const EXCEPINFO *pexcepinfo);
static HRESULT STDMETHODCALLTYPE SiteOnStateChange(IActiveScriptSite *site, SCRIPTSTATE ssScriptState);
static HRESULT STDMETHODCALLTYPE SiteOnScriptError(IActiveScriptSite *site, IActiveScriptError *pase);
static HRESULT STDMETHODCALLTYPE SiteOnEnterScript(IActiveScriptSite *site);
static HRESULT STDMETHODCALLTYPE SiteOnLeaveScript(IActiveScriptSite *site);
static const IActiveScriptSiteVtbl ScriptSiteVTable = {
SiteQueryInterface,
SiteAddRef,
SiteRelease,
SiteGetLCID,
SiteGetItemInfo,
SiteGetDocVersionString,
SiteOnScriptTerminate,
SiteOnStateChange,
SiteOnScriptError,
SiteOnEnterScript,
SiteOnLeaveScript
};
static HRESULT STDMETHODCALLTYPE
SiteQueryInterface(IActiveScriptSite *site, REFIID riid, void **ppv) {
#ifdef DO_COM_LOGGING
LPOLESTR interfaceString;
StringFromIID(riid, &interfaceString);
COM_LOG("SiteQueryInterface: %ls\n", interfaceString);
CoTaskMemFree(interfaceString);
#endif
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID (riid, &IID_IActiveScriptSite)) {
*ppv = site;
SiteAddRef(site);
return NOERROR;
}
else {
*ppv = 0;
return E_NOINTERFACE;
}
}
static ULONG STDMETHODCALLTYPE
SiteAddRef(IActiveScriptSite *site) {
COM_LOG("SiteAddRef\n");
ScriptSite *this = thisFromISite(site);
return ++this->refCount;
}
static ULONG STDMETHODCALLTYPE
SiteRelease(IActiveScriptSite *site) {
COM_LOG("SiteRelease\n");
ScriptSite *this = thisFromISite(site);
if (--this->refCount == 0) {
CFAllocatorDeallocate(NULL, this);
return 0;
}
return this->refCount;
}
static HRESULT STDMETHODCALLTYPE
SiteGetLCID(IActiveScriptSite *site, LCID *plcid) {
COM_LOG("SiteGetLCID\n");
return(plcid == NULL) ? E_POINTER : E_NOTIMPL;
}
static HRESULT STDMETHODCALLTYPE
SiteGetItemInfo(IActiveScriptSite *site,
LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown **ppunkItem, ITypeInfo **ppTypeInfo) {
COM_LOG("SiteGetItemInfo: %ls %lu\n", pstrName, dwReturnMask);
if (dwReturnMask & SCRIPTINFO_IUNKNOWN) {
if (!ppunkItem)
return E_INVALIDARG;
if (wcscmp(ITEM_NAME_ADDED_TO_JS, pstrName) == 0) {
COM_LOG("SiteGetItemInfo: returning C gateway object\n");
ScriptSite *this = thisFromISite(site);
Dispatcher *newDispatcher = CFAllocatorAllocate(this->alloc, sizeof(Dispatcher), 0);
newDispatcher->iDispatch.lpVtbl = &DispatchVTable;
newDispatcher->refCount = 0;
DispatchAddRef(&(newDispatcher->iDispatch));
*ppunkItem = (IUnknown *)&(newDispatcher->iDispatch);
return S_OK;
}
else
*ppunkItem = NULL;
}
if (dwReturnMask & SCRIPTINFO_ITYPEINFO) {
if (!ppTypeInfo)
return E_INVALIDARG;
*ppTypeInfo = NULL;
}
return TYPE_E_ELEMENTNOTFOUND;
}
static HRESULT STDMETHODCALLTYPE
SiteGetDocVersionString(IActiveScriptSite *site, BSTR *pbstrVersionString) {
COM_LOG("SiteGetDocVersionString\n");
return (pbstrVersionString == NULL) ? E_POINTER : E_NOTIMPL;
}
static HRESULT STDMETHODCALLTYPE
SiteOnScriptTerminate(IActiveScriptSite *site,
const VARIANT *pvarResult, const EXCEPINFO *pexcepinfo) {
COM_LOG("SiteOnScriptTerminate\n");
return S_OK;
}
static HRESULT STDMETHODCALLTYPE
SiteOnStateChange(IActiveScriptSite *site, SCRIPTSTATE ssScriptState) {
COM_LOG("SiteOnStateChange: %d\n", ssScriptState);
return S_OK;
}
static HRESULT STDMETHODCALLTYPE
SiteOnScriptError(IActiveScriptSite *site, IActiveScriptError *pase) {
COM_LOG("SiteOnScriptError\n");
do {
char buffer[1024];
EXCEPINFO exception;
HRESULT hResult = IActiveScriptError_GetExceptionInfo(pase, &exception);
CHECK_HRESULT(hResult, "IActiveScriptError_GetExceptionInfo");
wcstombs(buffer, exception.bstrDescription, 1024);
CFLog(0, CFSTR("JavaScript error when processing PAC file: %s"), buffer);
BSTR sourceLine;
hResult = IActiveScriptError_GetSourceLineText(pase, &sourceLine);
if (hResult == S_OK) {
wcstombs(buffer, sourceLine, 1024);
CFLog(0, CFSTR(" offending code: %s"), buffer);
}
} while (0);
return S_OK;
}
static HRESULT STDMETHODCALLTYPE
SiteOnEnterScript(IActiveScriptSite *site) {
COM_LOG("SiteOnEnterScript\n");
return S_OK;
}
static HRESULT STDMETHODCALLTYPE
SiteOnLeaveScript(IActiveScriptSite *site) {
COM_LOG("SiteOnLeaveScript\n");
return S_OK;
}
HRESULT
_parseCodeChunk(CFStringRef code, JSRunRef runtime) {
CFIndex len = CFStringGetLength(code);
UniChar stackBuffer[12*1024];
UniChar *uniBuf;
if (len >= 12*1024) {
uniBuf = malloc((len+1) * sizeof(UniChar));
} else {
uniBuf = stackBuffer;
}
CFStringGetCharacters(code, CFRangeMake(0, len), uniBuf);
uniBuf[len] = 0;
EXCEPINFO exception = { 0 };
HRESULT hResult = IActiveScriptParse_ParseScriptText(runtime->pActiveScriptParse, uniBuf, NULL, NULL, NULL, 0, 0, SCRIPTTEXT_ISVISIBLE, NULL, &exception);
if (uniBuf != stackBuffer)
free(uniBuf);
return hResult;
}
JSRunRef
_createJSRuntime(CFAllocatorRef alloc, CFStringRef js_support, CFStringRef js_pac) {
JSRunRef runtime = (JSRunRef)CFAllocatorAllocate(alloc, sizeof(JSRun), 0);
runtime->pActiveScript = NULL;
runtime->pActiveScriptParse = NULL;
ScriptSite *newSite = CFAllocatorAllocate(alloc, sizeof(ScriptSite), 0);
newSite->iSite.lpVtbl = &ScriptSiteVTable;
newSite->refCount = 0;
newSite->alloc = alloc;
SiteAddRef(&(newSite->iSite));
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
HRESULT hResult = S_OK;
do {
CLSID clsid;
hResult = CLSIDFromProgID( L"JavaScript", &clsid);
CHECK_HRESULT(hResult, "CLSIDFromProgID");
hResult = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IActiveScript, (void**)&(runtime->pActiveScript));
CHECK_HRESULT(hResult, "CoCreateInstance");
hResult = IActiveScript_QueryInterface(runtime->pActiveScript, &IID_IActiveScriptParse, (void **)&(runtime->pActiveScriptParse));
CHECK_HRESULT(hResult, "QueryInterface:IID_IActiveScriptParse");
hResult = IActiveScript_SetScriptSite(runtime->pActiveScript, &(newSite->iSite));
CHECK_HRESULT(hResult, "IActiveScript_SetScriptSite");
hResult = IActiveScriptParse_InitNew(runtime->pActiveScriptParse);
CHECK_HRESULT(hResult, "IActiveScriptParse_InitNew");
hResult = IActiveScript_AddNamedItem(runtime->pActiveScript, ITEM_NAME_ADDED_TO_JS, SCRIPTITEM_GLOBALMEMBERS|SCRIPTITEM_ISVISIBLE);
CHECK_HRESULT(hResult, "IActiveScript_AddNamedItem");
hResult = IActiveScript_SetScriptState(runtime->pActiveScript, SCRIPTSTATE_STARTED);
CHECK_HRESULT(hResult, "IActiveScript_SetScriptState");
hResult = _parseCodeChunk(js_support, runtime);
CHECK_HRESULT(hResult, "IActiveScriptParse_ParseScriptText - js_support");
hResult = _parseCodeChunk(js_pac, runtime);
CHECK_HRESULT(hResult, "IActiveScriptParse_ParseScriptText - js_pac");
} while (0);
SiteRelease(&(newSite->iSite));
if (hResult == S_OK) {
return runtime;
}
else {
_freeJSRuntime(runtime);
return NULL;
}
}
void
_freeJSRuntime(JSRunRef runtime) {
do {
if (runtime->pActiveScript) {
HRESULT hResult = IActiveScript_Close(runtime->pActiveScript);
CHECK_HRESULT(hResult, "IActiveScript_Close");
}
long int refs;
#ifdef DO_COM_LOGGING
if (runtime->pActiveScriptParse) {
IActiveScriptParse_AddRef(runtime->pActiveScriptParse);
refs = IActiveScriptParse_Release(runtime->pActiveScriptParse);
COM_LOG("IActiveScriptParse started with %ld refs in _freeJSRuntime, should be 2\n", refs);
}
if (runtime->pActiveScript) {
IActiveScript_AddRef(runtime->pActiveScript);
refs = IActiveScript_Release(runtime->pActiveScript);
COM_LOG("IActiveScript started with %ld refs in _freeJSRuntime, should be 2\n", refs);
}
#endif
if (runtime->pActiveScriptParse) {
refs = IActiveScriptParse_Release(runtime->pActiveScriptParse);
COM_LOG("IActiveScriptParse has %ld refs in _freeJSRuntime, should be 1\n", refs);
}
if (runtime->pActiveScriptParse) {
refs = IActiveScript_Release(runtime->pActiveScript);
COM_LOG("IActiveScript has %ld refs in _freeJSRuntime, should be 0\n", refs);
}
} while (0);
CFAllocatorDeallocate(NULL, runtime);
}
void
_prepareStringParam(CFStringRef str, VARIANTARG *variant) {
VariantInit(variant);
variant->vt = VT_BSTR;
CFIndex len = CFStringGetLength(str);
UniChar stackBuffer[1024];
UniChar *uniBuf;
if (len > 1024) {
uniBuf = malloc(len * sizeof(UniChar));
} else {
uniBuf = stackBuffer;
}
CFStringGetCharacters(str, CFRangeMake(0, len), uniBuf);
variant->bstrVal = SysAllocStringLen(uniBuf, len);
if (uniBuf != stackBuffer)
free(uniBuf);
}
void
_prepareStringArrayReturnValue(CFArrayRef cfArray, VARIANT *pVarResult) {
CFIndex count = cfArray ? CFArrayGetCount(cfArray) : 0;
VariantInit(pVarResult);
pVarResult->vt = VT_ARRAY | VT_VARIANT;
pVarResult->parray = SafeArrayCreateVector(VT_VARIANT, 0, count);
long i;
for (i = 0; i < count; i++) {
CFStringRef cfValue = CFArrayGetValueAtIndex(cfArray, i);
UniChar stackBuffer[1024];
UniChar *uniBuf;
CFIndex len = CFStringGetLength(cfValue);
if (len > 1024)
uniBuf = malloc(len * sizeof(UniChar));
else
uniBuf = stackBuffer;
CFStringGetCharacters(cfValue, CFRangeMake(0, len), uniBuf);
BSTR bstrValue = SysAllocStringLen(uniBuf, len);
if (uniBuf != stackBuffer)
free(uniBuf);
COM_LOG("DispatchInvoke - converting result string=%ls\n", bstrValue);
VARIANT variant;
VariantInit(&variant);
variant.vt = VT_BSTR;
variant.bstrVal = bstrValue;
HRESULT hResult = SafeArrayPutElement(pVarResult->parray, &i, &variant);
CHECK_HRESULT(hResult, "SafeArrayPutElement");
SysFreeString(bstrValue);
}
}
CFStringRef
_callPACFunction(CFAllocatorRef alloc, JSRunRef runtime, CFURLRef url, CFStringRef host) {
CFStringRef result = NULL;
HRESULT hResult = S_OK;
IDispatch *pDisp = NULL;
VARIANTARG paramValues[2];
memset(paramValues, 0, sizeof(paramValues));
do {
hResult = IActiveScript_GetScriptDispatch(runtime->pActiveScript, NULL, &pDisp);
CHECK_HRESULT(hResult, "IActiveScript_GetScriptDispatch");
LPOLESTR funcName = OLESTR("__Apple_FindProxyForURL");
DISPID dispid;
hResult = (pDisp->lpVtbl->GetIDsOfNames)(pDisp, &IID_NULL, &funcName, 1, LOCALE_NEUTRAL, &dispid);
CHECK_HRESULT(hResult, "IDispatch_GetIDsOfNames");
_prepareStringParam(host, ¶mValues[0]);
CFURLRef absURL = CFURLCopyAbsoluteURL(url);
_prepareStringParam(CFURLGetString(absURL), ¶mValues[1]);
CFRelease(absURL);
DISPPARAMS params = { paramValues, NULL, 2, 0 };
VARIANT pvarRetVal;
hResult = (pDisp->lpVtbl->Invoke)(pDisp, dispid, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, ¶ms, &pvarRetVal, NULL, NULL);
CHECK_HRESULT(hResult, "IDispatch_Invoke");
if (pvarRetVal.vt == VT_BSTR) {
result = CFStringCreateWithCharacters(alloc, pvarRetVal.bstrVal, SysStringLen(pvarRetVal.bstrVal));
COM_LOG("FindProxyForURL returned %ls\n", pvarRetVal.bstrVal);
}
else {
COM_LOG("FindProxyForURL returned unexpected type %d\n", pvarRetVal.vt);
}
SysFreeString(pvarRetVal.bstrVal);
} while (0);
if (pDisp)
(pDisp->lpVtbl->Release)(pDisp);
if (paramValues[0].bstrVal)
SysFreeString(paramValues[0].bstrVal);
if (paramValues[1].bstrVal)
SysFreeString(paramValues[1].bstrVal);
return result;
}
CFArrayRef
_JSPrimaryIpv4AddressesFunction(void) {
CFMutableArrayRef list = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock != INVALID_SOCKET) {
INTERFACE_INFO interfaces[20]; DWORD len = sizeof(interfaces);
DWORD resultLen;
if (WSAIoctl(sock, SIO_GET_INTERFACE_LIST, NULL, 0, (LPVOID)interfaces, len, &resultLen, NULL, NULL) != SOCKET_ERROR) {
int i;
for (i = 0; i < (resultLen/sizeof(INTERFACE_INFO)); i++) {
if ((interfaces[i].iiFlags & IFF_UP)
&& !(interfaces[i].iiFlags & IFF_LOOPBACK)
&& interfaces[i].iiAddress.Address.sa_family == AF_INET)
{
CFStringRef str = stringFromAddr(&(interfaces[i].iiAddress.Address), sizeof(struct sockaddr_in));
if (str) {
CFArrayAppendValue(list, str);
CFRelease(str);
}
}
}
}
closesocket(sock);
}
return list;
}
#endif
static CFURLRef _JSPacFileLocation = NULL;
static CFAbsoluteTime _JSPacFileExpiration = 0;
static JSRunRef _JSRuntime = NULL;
static void _JSSetEnvironmentForPAC(CFAllocatorRef alloc, CFURLRef url, CFAbsoluteTime expires, CFStringRef pacString) {
if (_JSRuntime) {
_freeJSRuntime(_JSRuntime);
_JSRuntime = NULL;
}
_JSPacFileExpiration = 0;
if (_JSPacFileLocation) {
CFRelease(_JSPacFileLocation);
_JSPacFileLocation = NULL;
}
CFStringRef js_support = _loadJSSupportFile();
if (js_support) {
_JSRuntime = _createJSRuntime(alloc, js_support, pacString);
if (_JSRuntime) {
_JSPacFileExpiration = expires;
_JSPacFileLocation = CFRetain(url);
}
}
}
static CFSpinLock_t _JSLock = 0;
CFStringRef
_JSFindProxyForURL(CFURLRef pac, CFURLRef url, CFStringRef host) {
CFAllocatorRef alloc = CFGetAllocator(pac);
CFStringRef result = NULL;
CFStreamError err = {0, 0};
if (!host) {
return CFRetain(_kProxySupportDIRECT);
}
__CFSpinLock(&_JSLock);
if (!_JSRuntime ||
!_JSPacFileExpiration || (CFAbsoluteTimeGetCurrent() > _JSPacFileExpiration) ||
!_JSPacFileLocation || !CFEqual(pac, _JSPacFileLocation)) {
CFAbsoluteTime expires;
CFStringRef js_pac = _loadPACFile(alloc, pac, &expires, &err);
if (js_pac) {
_JSSetEnvironmentForPAC(alloc, pac, expires, js_pac);
CFRelease(js_pac);
}
}
if (_JSRuntime) {
result = _callPACFunction(alloc, _JSRuntime, url, host);
#if 0
_freeJSRuntime(_JSRuntime);
_JSRuntime = NULL;
#endif
}
else if ((err.domain == kCFStreamErrorDomainNetDB) || (err.domain == kCFStreamErrorDomainSystemConfiguration) || (err.domain == kCFStreamErrorDomainSSL) || (err.domain == _kCFStreamErrorDomainNativeSockets) || (err.domain == kCFStreamErrorDomainCustom)) {
result = CFRetain(_kProxySupportDIRECT);
}
__CFSpinUnlock(&_JSLock);
return result;
}
static CFStringRef
_JSFindProxyForURLAsync(CFURLRef pac, CFURLRef url, CFStringRef host, Boolean *mustBlock) {
CFStringRef result = NULL;
CFAllocatorRef alloc = CFGetAllocator(pac);
if (!host) {
return CFRetain(_kProxySupportDIRECT);
}
__CFSpinLock(&_JSLock);
if (!_JSRuntime || !_JSPacFileExpiration ||
(CFAbsoluteTimeGetCurrent() > _JSPacFileExpiration) ||
!_JSPacFileLocation || !CFEqual(pac, _JSPacFileLocation)) {
*mustBlock = TRUE;
} else {
result = _callPACFunction(alloc, _JSRuntime, url, host);
*mustBlock = FALSE;
}
__CFSpinUnlock(&_JSLock);
return result;
}
static CFArrayRef
_resolveDNSName(CFStringRef name) {
CFStreamError error;
CFMutableArrayRef list = NULL;
CFHostRef lookup = CFHostCreateWithName(kCFAllocatorDefault, name);
if (lookup && CFHostStartInfoResolution(lookup, kCFHostAddresses, &error) && !error.error) {
CFArrayRef addrs = CFHostGetAddressing(lookup, NULL);
CFIndex count = addrs ? CFArrayGetCount(addrs) : 0;
if (count) {
list = CFArrayCreateMutable(kCFAllocatorDefault, count, &kCFTypeArrayCallBacks);
if (list) {
CFIndex i;
for (i = 0; i < count; i++) {
CFDataRef saData = (CFDataRef)CFArrayGetValueAtIndex(addrs, i);
CFStringRef str = _CFNetworkCFStringCreateWithCFDataAddress(kCFAllocatorDefault, saData);
if (str) {
CFArrayAppendValue(list, str);
CFRelease(str);
}
}
if (!CFArrayGetCount(list)) {
CFRelease(list);
list = NULL;
}
}
}
}
if (lookup) CFRelease(lookup);
return list;
}
#if defined(__MACH__)
static JSObjectRef
_JSDnsResolveFunction(void* context, JSObjectRef ctxt, CFArrayRef args) {
JSObjectRef result = NULL;
if (args && CFArrayGetCount(args) == 1) {
CFTypeRef name = JSObjectCopyCFValue((JSObjectRef)CFArrayGetValueAtIndex(args, 0));
if (name && CFGetTypeID(name) == CFStringGetTypeID()) {
CFArrayRef list = _resolveDNSName((CFStringRef)name);
if (list) {
result = JSObjectCreateWithCFType(list);
CFRelease(list);
}
}
if (name) CFRelease(name);
}
return result;
}
JSObjectRef
_JSPrimaryIpv4AddressesFunction(void* context, JSObjectRef ctxt, CFArrayRef args) {
JSObjectRef result = NULL;
do {
CFStringRef key = NULL;
CFDictionaryRef value = NULL;
CFStringRef interface = NULL;
SCDynamicStoreRef store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("JSEvaluator"), NULL, NULL);
if (!store)
break;
key = SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault, kSCDynamicStoreDomainState, kSCEntNetIPv4);
if (!key) {
CFRelease(store);
break;
}
value = SCDynamicStoreCopyValue(store, key);
CFRelease(key);
if (!value) {
CFRelease(store);
break;
}
interface = CFDictionaryGetValue(value, kSCDynamicStorePropNetPrimaryInterface);
if (!interface) {
CFRelease(value);
CFRelease(store);
break;
}
key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(kCFAllocatorDefault, kSCDynamicStoreDomainState, interface, kSCEntNetIPv4);
CFRelease(value);
if (!key) {
CFRelease(store);
break;
}
value = SCDynamicStoreCopyValue(store, key);
if (!value) {
CFRelease(store);
break;
}
result = JSObjectCreateWithCFType(value);
CFRelease(value);
CFRelease(key);
} while (0);
return result;
}
#endif
#define BUF_SIZE 4096
static void releasePACStreamContext(void *info);
typedef struct {
CFURLRef pacURL;
CFURLRef targetURL;
CFStringRef targetScheme;
CFStringRef targetHost;
CFMutableDataRef data;
void *clientInfo;
_CFProxyStreamCallBack cb;
} _PACStreamContext;
static void releasePACStreamContext(void *info) {
_PACStreamContext *ctxt = (_PACStreamContext *)info;
CFAllocatorRef alloc = CFGetAllocator(ctxt->data);
CFRelease(ctxt->pacURL);
CFRelease(ctxt->targetURL);
CFRelease(ctxt->targetScheme);
CFRelease(ctxt->targetHost);
CFRelease(ctxt->data);
CFAllocatorDeallocate(alloc, ctxt);
}
static void readBytesFromProxyStream(CFReadStreamRef proxyStream, _PACStreamContext *ctxt) {
UInt8 buf[BUF_SIZE];
CFIndex bytesRead = CFReadStreamRead(proxyStream, buf, BUF_SIZE);
if (bytesRead > 0) {
CFDataAppendBytes(ctxt->data, buf, bytesRead);
}
}
static void proxyStreamCallback(CFReadStreamRef proxyStream, CFStreamEventType type, void *clientCallBackInfo) {
_PACStreamContext *ctxt = (_PACStreamContext *)clientCallBackInfo;
switch (type) {
case kCFStreamEventHasBytesAvailable:
readBytesFromProxyStream(proxyStream, ctxt);
break;
case kCFStreamEventEndEncountered:
case kCFStreamEventErrorOccurred:
ctxt->cb(proxyStream, ctxt->clientInfo);
break;
default:
;
}
}
static CFReadStreamRef BuildStreamForPACURL(CFAllocatorRef alloc, CFURLRef pacURL, CFURLRef targetURL, CFStringRef targetScheme, CFStringRef targetHost, _CFProxyStreamCallBack callback, void *clientInfo) {
Boolean isFile;
CFReadStreamRef stream = _streamForPACFile(alloc, pacURL, &isFile);
if (stream) {
_PACStreamContext *pacContext = CFAllocatorAllocate(alloc, sizeof(_PACStreamContext), 0);
CFRetain(pacURL);
pacContext->pacURL = pacURL;
CFRetain(targetURL);
pacContext->targetURL = targetURL;
CFRetain(targetScheme);
pacContext->targetScheme = targetScheme;
CFRetain(targetHost);
pacContext->targetHost = targetHost;
pacContext->data = CFDataCreateMutable(alloc, 0);
pacContext->clientInfo = clientInfo;
pacContext->cb = callback;
CFStreamClientContext streamContext;
streamContext.version = 0;
streamContext.info = pacContext;
streamContext.retain = NULL;
streamContext.release = releasePACStreamContext;
streamContext.copyDescription = NULL;
CFReadStreamSetClient(stream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, proxyStreamCallback, &streamContext);
CFReadStreamOpen(stream);
}
return stream;
}
CFMutableArrayRef _CFNetworkCopyProxyFromProxyStream(CFReadStreamRef proxyStream, Boolean *isComplete) {
CFStreamStatus status = CFReadStreamGetStatus(proxyStream);
if (status == kCFStreamStatusOpen && CFReadStreamHasBytesAvailable(proxyStream)) {
_PACStreamContext *pacContext = (_PACStreamContext *)_CFReadStreamGetClient(proxyStream);
readBytesFromProxyStream(proxyStream, pacContext);
status = CFReadStreamGetStatus(proxyStream);
}
if (status == kCFStreamStatusAtEnd) {
_PACStreamContext *pacContext = (_PACStreamContext *)_CFReadStreamGetClient(proxyStream);
CFAllocatorRef alloc = CFGetAllocator(pacContext->data);
CFStringRef pacString;
CFStringRef pacResult;
CFAbsoluteTime expiry;
*isComplete = TRUE;
__CFSpinLock(&_JSLock);
pacString = _stringFromLoadedPACStream(alloc, pacContext->data, proxyStream, &expiry);
_JSSetEnvironmentForPAC(alloc, pacContext->pacURL, expiry, pacString);
if (pacString)
CFRelease(pacString);
pacResult = _callPACFunction(alloc, _JSRuntime, pacContext->targetURL, pacContext->targetHost);
__CFSpinUnlock(&_JSLock);
CFMutableArrayRef result = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
_appendProxiesFromPACResponse(alloc, result, pacResult, pacContext->targetScheme);
if(pacResult) CFRelease(pacResult);
CFReadStreamClose(proxyStream);
return result;
} else if (status == kCFStreamStatusError) {
CFMutableArrayRef result = CFArrayCreateMutable(CFGetAllocator(proxyStream), 0, &kCFTypeArrayCallBacks);
CFArrayAppendValue(result, kCFNull);
*isComplete = TRUE;
return result;
} else {
*isComplete = FALSE;
return NULL;
}
}