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. */ 51 static 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 * This will resolve all PAC URLs in the given array and return an array containing no more 65 * PAC URLs. 66 */ 67 static CFArrayRef createExpandedProxiesArray(CFArrayRef proxies, CFURLRef url) { 68 69 CFIndex count; 70 CFIndex index; 71 CFMutableArrayRef expandedProxiesArray; 72 73 expandedProxiesArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 74 assert(expandedProxiesArray != NULL); 75 76 /* Iterate over the array of proxies */ 77 count = CFArrayGetCount(proxies); 78 for (index = 0; index < count ; index++) { 79 CFDictionaryRef currentProxy; 80 CFStringRef proxyType; 81 82 currentProxy = (CFDictionaryRef) CFArrayGetValueAtIndex(proxies, index); 83 assert(currentProxy != NULL); 84 proxyType = (CFStringRef) CFDictionaryGetValue(currentProxy, kCFProxyTypeKey); 85 assert(proxyType != NULL); 86 87 if (!CFEqual(proxyType, kCFProxyTypeAutoConfigurationURL)) { 88 /* This is a non PAC entry - just copy it to the new array. */ 89 CFArrayAppendValue(expandedProxiesArray, currentProxy); 90 } else { 91 /* This is a PAC URL and we have to resolve it. */ 92 CFRunLoopSourceRef runLoop; 93 CFURLRef scriptURL; 94 CFTypeRef result = NULL; 95 CFStreamClientContext context = { 0, &result, NULL, NULL, NULL }; 96 97 scriptURL = CFDictionaryGetValue(currentProxy, kCFProxyAutoConfigurationURLKey); 98 99 runLoop = CFNetworkExecuteProxyAutoConfigurationURL(scriptURL, url, proxyUrlCallback, 100 &context); 101 if (runLoop != NULL) { 102 /* 103 * Despite the fact that CFNetworkExecuteProxyAutoConfigurationURL has 104 * neither a "Create" nor a "Copy" in the name, we are required to 105 * release the return CFRunLoopSourceRef <rdar://problem/5533931>. 106 */ 107 CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoop, kResolveProxyRunLoopMode); 108 CFRunLoopRunInMode(kResolveProxyRunLoopMode, 1.0e10, false); 109 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runLoop, kResolveProxyRunLoopMode); 110 111 /* 112 * Once the runloop returns, we should have either an error result or a 113 * proxies array result. Do the appropriate thing with that result. 114 */ 115 if (result != NULL) { 116 if (CFGetTypeID(result) == CFArrayGetTypeID()) { 117 /* 118 * Append the new array from the PAC list - it does 119 * contain no more PAC URIs. 120 */ 121 CFArrayAppendArray(expandedProxiesArray, result, 122 CFRangeMake(0, CFArrayGetCount(result))); 123 } 124 CFRelease(result); 125 } 126 CFRelease(runLoop); 127 } 128 } 129 } 130 return expandedProxiesArray; 131 } 132 133 134 /* 135 * Class: sun_net_spi_DefaultProxySelector 136 * Method: init 137 * Signature: ()Z 138 */ 139 JNIEXPORT jboolean JNICALL 140 Java_sun_net_spi_DefaultProxySelector_init(JNIEnv *env, jclass clazz) { 141 if (!initJavaClass(env)) { 142 return JNI_FALSE; 143 } 144 return JNI_TRUE; 145 } 146 147 148 /* 149 * Class: sun_net_spi_DefaultProxySelector 150 * Method: getSystemProxies 151 * Signature: ([Ljava/lang/String;Ljava/lang/String;)[Ljava/net/Proxy; 152 */ 153 JNIEXPORT jobjectArray JNICALL 154 Java_sun_net_spi_DefaultProxySelector_getSystemProxies(JNIEnv *env, 155 jobject this, 156 jstring proto, 157 jstring host) 158 { 159 CFDictionaryRef proxyDicRef = NULL; 160 CFURLRef urlRef = NULL; 161 bool proxyFound = false; 162 jobjectArray proxyArray = NULL; 163 const char *cproto; 164 const char *chost; 165 166 /* Get system proxy settings */ 167 proxyDicRef = CFNetworkCopySystemProxySettings(); 168 if (proxyDicRef == NULL) { 169 return NULL; 170 } 171 172 /* Create CFURLRef from proto and host */ 173 cproto = (*env)->GetStringUTFChars(env, proto, NULL); 174 if (cproto != NULL) { 175 chost = (*env)->GetStringUTFChars(env, host, NULL); 176 if (chost != NULL) { 177 char* uri = NULL; 178 size_t protoLen = 0; 179 size_t hostLen = 0; 180 181 protoLen = strlen(cproto); 182 hostLen = strlen(chost); 183 184 /* Construct the uri, cproto + "://" + chost */ 185 uri = malloc(protoLen + hostLen + 4); 186 if (uri != NULL) { 187 memcpy(uri, cproto, protoLen); 188 memcpy(uri + protoLen, "://", 3); 189 memcpy(uri + protoLen + 3, chost, hostLen + 1); 190 191 urlRef = CFURLCreateWithBytes(NULL, (const UInt8 *) uri, strlen(uri), 192 kCFStringEncodingUTF8, NULL); 193 free(uri); 194 } 195 (*env)->ReleaseStringUTFChars(env, host, chost); 196 } 197 (*env)->ReleaseStringUTFChars(env, proto, cproto); 198 } 199 if (urlRef != NULL) { 200 CFArrayRef urlProxyArrayRef = CFNetworkCopyProxiesForURL(urlRef, proxyDicRef); 201 if (urlProxyArrayRef != NULL) { 202 CFIndex count; 203 CFIndex index; 204 205 CFArrayRef expandedProxyArray = createExpandedProxiesArray(urlProxyArrayRef, urlRef); 206 CFRelease(urlProxyArrayRef); 207 208 count = CFArrayGetCount(expandedProxyArray); 209 210 proxyArray = (*env)->NewObjectArray(env, count, proxy_class, NULL); 211 if (proxyArray != NULL || (*env)->ExceptionCheck(env)) { 212 /* Iterate over the expanded array of proxies */ 213 for (index = 0; index < count ; index++) { 214 CFDictionaryRef currentProxy; 215 CFStringRef proxyType; 216 jobject proxy = NULL; 217 218 currentProxy = (CFDictionaryRef) CFArrayGetValueAtIndex(expandedProxyArray, 219 index); 220 proxyType = (CFStringRef) CFDictionaryGetValue(currentProxy, kCFProxyTypeKey); 221 if (CFEqual(proxyType, kCFProxyTypeNone)) { 222 /* 223 * This entry states we should use no proxy - therefor we just create a 224 * NO_PROXY object. 225 */ 226 proxy = (*env)->GetStaticObjectField(env, proxy_class, pr_no_proxyID); 227 } else { 228 /* 229 * Create a proxy object for this entry. We have to differentiate between 230 * socks and http type. 231 */ 232 jfieldID typeID = ptype_httpID; 233 if (CFEqual(proxyType, kCFProxyTypeSOCKS)) { 234 typeID = ptype_socksID; 235 } 236 CFNumberRef portNumberRef = (CFNumberRef)CFDictionaryGetValue(currentProxy, 237 (const void*)kCFProxyPortNumberKey); 238 if (portNumberRef != NULL) { 239 int port = 0; 240 if (CFNumberGetValue(portNumberRef, kCFNumberSInt32Type, &port)) { 241 CFStringRef hostNameRef = (CFStringRef)CFDictionaryGetValue( 242 currentProxy, (const void*)kCFProxyHostNameKey); 243 if (hostNameRef != NULL) { 244 char hostNameBuffer[BUFFER_SIZE]; 245 if (CFStringGetCString(hostNameRef, hostNameBuffer, 246 BUFFER_SIZE, kCFStringEncodingUTF8)) { 247 proxy = createProxy(env, typeID, &hostNameBuffer[0], port); 248 } 249 } 250 } 251 } 252 } 253 if (proxy == NULL || (*env)->ExceptionCheck(env)) { 254 proxyArray = NULL; 255 break; 256 } 257 (*env)->SetObjectArrayElement(env, proxyArray, index, proxy); 258 if ((*env)->ExceptionCheck(env)) { 259 proxyArray = NULL; 260 break; 261 } 262 } 263 } 264 CFRelease(expandedProxyArray); 265 } 266 CFRelease(urlRef); 267 } 268 CFRelease(proxyDicRef); 269 270 return proxyArray; 271 }