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 }