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