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