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 }