1/* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 * Copyright (c) 2017 SAP SE. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27#include <string.h> 28#include <CoreFoundation/CoreFoundation.h> 29#include <CoreServices/CoreServices.h> 30 31#include "jni.h" 32#include "jni_util.h" 33#include "jvm.h" 34#include "jvm_md.h" 35 36#include "proxy_util.h" 37 38#include "sun_net_spi_DefaultProxySelector.h" 39 40 41/** 42 * For more information on how to use the APIs in "CFProxySupport.h" see: 43 * https://developer.apple.com/legacy/library/samplecode/CFProxySupportTool/Introduction/Intro.html 44 */ 45 46#define kResolveProxyRunLoopMode CFSTR("sun.net.spi.DefaultProxySelector") 47 48#define BUFFER_SIZE 1024 49 50/* Callback for CFNetworkExecuteProxyAutoConfigurationURL. */ 51static void proxyUrlCallback(void * client, CFArrayRef proxies, CFErrorRef error) { 52 /* client is a pointer to a CFTypeRef and holds either proxies or an error. */ 53 CFTypeRef* resultPtr = (CFTypeRef *)client; 54 55 if (error != NULL) { 56 *resultPtr = CFRetain(error); 57 } else { 58 *resultPtr = CFRetain(proxies); 59 } 60 CFRunLoopStop(CFRunLoopGetCurrent()); 61} 62 63/* 64 * Returns a new array of proxies containing all the given non-PAC proxies as 65 * well as the results of executing all the given PAC-based proxies, for the 66 * specified URL. 'proxies' is a list that may contain both PAC and non-PAC 67 * proxies. 68 */ 69static CFArrayRef createExpandedProxiesArray(CFArrayRef proxies, CFURLRef url) { 70 71 CFIndex count; 72 CFIndex index; 73 CFMutableArrayRef expandedProxiesArray; 74 75 expandedProxiesArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 76 if (expandedProxiesArray == NULL) 77 return NULL; 78 79 /* Iterate over the array of proxies */ 80 count = CFArrayGetCount(proxies); 81 for (index = 0; index < count ; index++) { 82 CFDictionaryRef currentProxy; 83 CFStringRef proxyType; 84 85 currentProxy = (CFDictionaryRef) CFArrayGetValueAtIndex(proxies, index); 86 if(currentProxy == NULL) { 87 CFRelease(expandedProxiesArray); 88 return NULL; 89 } 90 proxyType = (CFStringRef) CFDictionaryGetValue(currentProxy, kCFProxyTypeKey); 91 if (proxyType == NULL) { 92 CFRelease(expandedProxiesArray); 93 return NULL; 94 } 95 96 if (!CFEqual(proxyType, kCFProxyTypeAutoConfigurationURL)) { 97 /* Non-PAC entry, just copy it to the new array */ 98 CFArrayAppendValue(expandedProxiesArray, currentProxy); 99 } else { 100 /* PAC-based URL, execute its script append its results */ 101 CFRunLoopSourceRef runLoop; 102 CFURLRef scriptURL; 103 CFTypeRef result = NULL; 104 CFStreamClientContext context = { 0, &result, NULL, NULL, NULL }; 105 CFTimeInterval timeout = 5; 106 107 scriptURL = CFDictionaryGetValue(currentProxy, kCFProxyAutoConfigurationURLKey); 108 109 runLoop = CFNetworkExecuteProxyAutoConfigurationURL(scriptURL, url, proxyUrlCallback, 110 &context); 111 if (runLoop != NULL) { 112 /* 113 * Despite the fact that CFNetworkExecuteProxyAutoConfigurationURL has 114 * neither a "Create" nor a "Copy" in the name, we are required to 115 * release the return CFRunLoopSourceRef <rdar://problem/5533931>. 116 */ 117 CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoop, kResolveProxyRunLoopMode); 118 CFRunLoopRunInMode(kResolveProxyRunLoopMode, timeout, false); 119 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runLoop, kResolveProxyRunLoopMode); 120 121 /* 122 * Once the runloop returns, there will be either an error result or 123 * a proxies array result. Do the appropriate thing with that result. 124 */ 125 if (result != NULL) { 126 if (CFGetTypeID(result) == CFArrayGetTypeID()) { 127 /* 128 * Append the new array from the PAC list - it contains 129 * only non-PAC entries. 130 */ 131 CFArrayAppendArray(expandedProxiesArray, result, 132 CFRangeMake(0, CFArrayGetCount(result))); 133 } 134 CFRelease(result); 135 } 136 CFRelease(runLoop); 137 } 138 } 139 } 140 return expandedProxiesArray; 141} 142 143 144/* 145 * Class: sun_net_spi_DefaultProxySelector 146 * Method: init 147 * Signature: ()Z 148 */ 149JNIEXPORT jboolean JNICALL 150Java_sun_net_spi_DefaultProxySelector_init(JNIEnv *env, jclass clazz) { 151 if (!initJavaClass(env)) { 152 return JNI_FALSE; 153 } 154 return JNI_TRUE; 155} 156 157 158/* 159 * Class: sun_net_spi_DefaultProxySelector 160 * Method: getSystemProxies 161 * Signature: ([Ljava/lang/String;Ljava/lang/String;)[Ljava/net/Proxy; 162 */ 163JNIEXPORT jobjectArray JNICALL 164Java_sun_net_spi_DefaultProxySelector_getSystemProxies(JNIEnv *env, 165 jobject this, 166 jstring proto, 167 jstring host) 168{ 169 CFDictionaryRef proxyDicRef = NULL; 170 CFURLRef urlRef = NULL; 171 bool proxyFound = false; 172 jobjectArray proxyArray = NULL; 173 const char *cproto; 174 const char *chost; 175 176 /* Get system proxy settings */ 177 proxyDicRef = CFNetworkCopySystemProxySettings(); 178 if (proxyDicRef == NULL) { 179 return NULL; 180 } 181 182 /* Create CFURLRef from proto and host */ 183 cproto = (*env)->GetStringUTFChars(env, proto, NULL); 184 if (cproto != NULL) { 185 chost = (*env)->GetStringUTFChars(env, host, NULL); 186 if (chost != NULL) { 187 char* uri = NULL; 188 size_t protoLen = 0; 189 size_t hostLen = 0; 190 191 protoLen = strlen(cproto); 192 hostLen = strlen(chost); 193 194 /* Construct the uri, cproto + "://" + chost */ 195 uri = malloc(protoLen + hostLen + 4); 196 if (uri != NULL) { 197 memcpy(uri, cproto, protoLen); 198 memcpy(uri + protoLen, "://", 3); 199 memcpy(uri + protoLen + 3, chost, hostLen + 1); 200 201 urlRef = CFURLCreateWithBytes(NULL, (const UInt8 *) uri, strlen(uri), 202 kCFStringEncodingUTF8, NULL); 203 free(uri); 204 } 205 (*env)->ReleaseStringUTFChars(env, host, chost); 206 } 207 (*env)->ReleaseStringUTFChars(env, proto, cproto); 208 } 209 if (urlRef != NULL) { 210 CFArrayRef urlProxyArrayRef = CFNetworkCopyProxiesForURL(urlRef, proxyDicRef); 211 if (urlProxyArrayRef != NULL) { 212 CFIndex count; 213 CFIndex index; 214 215 CFArrayRef expandedProxyArray = createExpandedProxiesArray(urlProxyArrayRef, urlRef); 216 CFRelease(urlProxyArrayRef); 217 218 if (expandedProxyArray == NULL) { 219 CFRelease(urlRef); 220 CFRelease(proxyDicRef); 221 return NULL; 222 } 223 224 count = CFArrayGetCount(expandedProxyArray); 225 226 proxyArray = (*env)->NewObjectArray(env, count, proxy_class, NULL); 227 if (proxyArray != NULL || (*env)->ExceptionCheck(env)) { 228 /* Iterate over the expanded array of proxies */ 229 for (index = 0; index < count ; index++) { 230 CFDictionaryRef currentProxy; 231 CFStringRef proxyType; 232 jobject proxy = NULL; 233 234 currentProxy = (CFDictionaryRef) CFArrayGetValueAtIndex(expandedProxyArray, 235 index); 236 proxyType = (CFStringRef) CFDictionaryGetValue(currentProxy, kCFProxyTypeKey); 237 if (CFEqual(proxyType, kCFProxyTypeNone)) { 238 /* This entry states no proxy, therefore just add a NO_PROXY object. */ 239 proxy = (*env)->GetStaticObjectField(env, proxy_class, pr_no_proxyID); 240 } else { 241 /* 242 * Create a proxy object for this entry. 243 * Differentiate between SOCKS and HTTP type. 244 */ 245 jfieldID typeID = ptype_httpID; 246 if (CFEqual(proxyType, kCFProxyTypeSOCKS)) { 247 typeID = ptype_socksID; 248 } 249 CFNumberRef portNumberRef = (CFNumberRef)CFDictionaryGetValue(currentProxy, 250 (const void*)kCFProxyPortNumberKey); 251 if (portNumberRef != NULL) { 252 int port = 0; 253 if (CFNumberGetValue(portNumberRef, kCFNumberSInt32Type, &port)) { 254 CFStringRef hostNameRef = (CFStringRef)CFDictionaryGetValue( 255 currentProxy, (const void*)kCFProxyHostNameKey); 256 if (hostNameRef != NULL) { 257 char hostNameBuffer[BUFFER_SIZE]; 258 if (CFStringGetCString(hostNameRef, hostNameBuffer, 259 BUFFER_SIZE, kCFStringEncodingUTF8)) { 260 proxy = createProxy(env, typeID, &hostNameBuffer[0], port); 261 } 262 } 263 } 264 } 265 } 266 if (proxy == NULL || (*env)->ExceptionCheck(env)) { 267 proxyArray = NULL; 268 break; 269 } 270 (*env)->SetObjectArrayElement(env, proxyArray, index, proxy); 271 if ((*env)->ExceptionCheck(env)) { 272 proxyArray = NULL; 273 break; 274 } 275 } 276 } 277 CFRelease(expandedProxyArray); 278 } 279 CFRelease(urlRef); 280 } 281 CFRelease(proxyDicRef); 282 283 return proxyArray; 284} 285