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. */
proxyUrlCallback(void * client,CFArrayRef proxies,CFErrorRef error)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  */
createExpandedProxiesArray(CFArrayRef proxies,CFURLRef url)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             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  */
149 JNIEXPORT jboolean JNICALL
Java_sun_net_spi_DefaultProxySelector_init(JNIEnv * env,jclass clazz)150 Java_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  */
163 JNIEXPORT jobjectArray JNICALL
Java_sun_net_spi_DefaultProxySelector_getSystemProxies(JNIEnv * env,jobject this,jstring proto,jstring host)164 Java_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