1 | /* |
2 | * Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved. |
3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 | * |
5 | * This code is free software; you can redistribute it and/or modify it |
6 | * under the terms of the GNU General Public License version 2 only, as |
7 | * published by the Free Software Foundation. Oracle designates this |
8 | * particular file as subject to the "Classpath" exception as provided |
9 | * by Oracle in the LICENSE file that accompanied this code. |
10 | * |
11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
14 | * version 2 for more details (a copy is included in the LICENSE file that |
15 | * accompanied this code). |
16 | * |
17 | * You should have received a copy of the GNU General Public License version |
18 | * 2 along with this work; if not, write to the Free Software Foundation, |
19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
20 | * |
21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
22 | * or visit www.oracle.com if you need additional information or have any |
23 | * questions. |
24 | */ |
25 | |
26 | #include <dlfcn.h> |
27 | #include <stdio.h> |
28 | #include <stdlib.h> |
29 | #include <string.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 | * These functions are used by the sun.net.spi.DefaultProxySelector class |
43 | * to access some platform specific settings. |
44 | * This is the Solaris/Linux Gnome 2.x code using the GConf-2 library. |
45 | * Everything is loaded dynamically so no hard link with any library exists. |
46 | * The GConf-2 settings used are: |
47 | * - /system/http_proxy/use_http_proxy boolean |
48 | * - /system/http_proxy/use_authentcation boolean |
49 | * - /system/http_proxy/use_same_proxy boolean |
50 | * - /system/http_proxy/host string |
51 | * - /system/http_proxy/authentication_user string |
52 | * - /system/http_proxy/authentication_password string |
53 | * - /system/http_proxy/port int |
54 | * - /system/proxy/socks_host string |
55 | * - /system/proxy/mode string |
56 | * - /system/proxy/ftp_host string |
57 | * - /system/proxy/secure_host string |
58 | * - /system/proxy/socks_port int |
59 | * - /system/proxy/ftp_port int |
60 | * - /system/proxy/secure_port int |
61 | * - /system/proxy/no_proxy_for list |
62 | * |
63 | * The following keys are not used in the new gnome 3 |
64 | * - /system/http_proxy/use_http_proxy |
65 | * - /system/http_proxy/use_same_proxy |
66 | */ |
67 | typedef void* gconf_client_get_default_func(); |
68 | typedef char* gconf_client_get_string_func(void *, char *, void**); |
69 | typedef int gconf_client_get_int_func(void*, char *, void**); |
70 | typedef int gconf_client_get_bool_func(void*, char *, void**); |
71 | typedef int gconf_init_func(int, char**, void**); |
72 | typedef void g_type_init_func (); |
73 | gconf_client_get_default_func* my_get_default_func = NULL; |
74 | gconf_client_get_string_func* my_get_string_func = NULL; |
75 | gconf_client_get_int_func* my_get_int_func = NULL; |
76 | gconf_client_get_bool_func* my_get_bool_func = NULL; |
77 | gconf_init_func* my_gconf_init_func = NULL; |
78 | g_type_init_func* my_g_type_init_func = NULL; |
79 | |
80 | |
81 | /* |
82 | * GProxyResolver provides synchronous and asynchronous network |
83 | * proxy resolution. It is based on GSettings, which is the standard |
84 | * of Gnome 3, to get system settings. |
85 | * |
86 | * In the current implementation, GProxyResolver has a higher priority |
87 | * than the old GConf. And we only resolve the proxy synchronously. In |
88 | * the future, we can also do the asynchronous network proxy resolution |
89 | * if necessary. |
90 | * |
91 | */ |
92 | typedef struct _GProxyResolver GProxyResolver; |
93 | typedef struct _GSocketConnectable GSocketConnectable; |
94 | typedef struct GError GError; |
95 | typedef GProxyResolver* g_proxy_resolver_get_default_func(); |
96 | typedef char** g_proxy_resolver_lookup_func(); |
97 | typedef GSocketConnectable* g_network_address_parse_uri_func(); |
98 | typedef const char* g_network_address_get_hostname_func(); |
99 | typedef unsigned short g_network_address_get_port_func(); |
100 | typedef void g_strfreev_func(); |
101 | |
102 | static g_proxy_resolver_get_default_func* g_proxy_resolver_get_default = NULL; |
103 | static g_proxy_resolver_lookup_func* g_proxy_resolver_lookup = NULL; |
104 | static g_network_address_parse_uri_func* g_network_address_parse_uri = NULL; |
105 | static g_network_address_get_hostname_func* g_network_address_get_hostname = NULL; |
106 | static g_network_address_get_port_func* g_network_address_get_port = NULL; |
107 | static g_strfreev_func* g_strfreev = NULL; |
108 | |
109 | static void* gconf_client = NULL; |
110 | static int use_gproxyResolver = 0; |
111 | static int use_gconf = 0; |
112 | |
113 | |
114 | static int initGConf() { |
115 | /** |
116 | * Let's try to load GConf-2 library |
117 | */ |
118 | if (dlopen(JNI_LIB_NAME("gconf-2" ), RTLD_GLOBAL | RTLD_LAZY) != NULL || |
119 | dlopen(VERSIONED_JNI_LIB_NAME("gconf-2" , "4" ), |
120 | RTLD_GLOBAL | RTLD_LAZY) != NULL) |
121 | { |
122 | /* |
123 | * Now let's get pointer to the functions we need. |
124 | */ |
125 | my_g_type_init_func = |
126 | (g_type_init_func*)dlsym(RTLD_DEFAULT, "g_type_init" ); |
127 | my_get_default_func = |
128 | (gconf_client_get_default_func*)dlsym(RTLD_DEFAULT, |
129 | "gconf_client_get_default" ); |
130 | |
131 | if (my_g_type_init_func != NULL && my_get_default_func != NULL) { |
132 | /** |
133 | * Try to connect to GConf. |
134 | */ |
135 | (*my_g_type_init_func)(); |
136 | gconf_client = (*my_get_default_func)(); |
137 | if (gconf_client != NULL) { |
138 | my_get_string_func = |
139 | (gconf_client_get_string_func*)dlsym(RTLD_DEFAULT, |
140 | "gconf_client_get_string" ); |
141 | my_get_int_func = |
142 | (gconf_client_get_int_func*)dlsym(RTLD_DEFAULT, |
143 | "gconf_client_get_int" ); |
144 | my_get_bool_func = |
145 | (gconf_client_get_bool_func*)dlsym(RTLD_DEFAULT, |
146 | "gconf_client_get_bool" ); |
147 | if (my_get_int_func != NULL && my_get_string_func != NULL && |
148 | my_get_bool_func != NULL) |
149 | { |
150 | /** |
151 | * We did get all we need. Let's enable the System Proxy Settings. |
152 | */ |
153 | return 1; |
154 | } |
155 | } |
156 | } |
157 | } |
158 | return 0; |
159 | } |
160 | |
161 | static jobjectArray getProxyByGConf(JNIEnv *env, const char* cproto, |
162 | const char* chost) |
163 | { |
164 | char *phost = NULL; |
165 | char *mode = NULL; |
166 | int pport = 0; |
167 | int use_proxy = 0; |
168 | int use_same_proxy = 0; |
169 | jobjectArray proxy_array = NULL; |
170 | jfieldID ptype_ID = ptype_httpID; |
171 | |
172 | /* We only check manual proxy configurations */ |
173 | mode = (*my_get_string_func)(gconf_client, "/system/proxy/mode" , NULL); |
174 | if (mode && !strcasecmp(mode, "manual" )) { |
175 | /* |
176 | * Even though /system/http_proxy/use_same_proxy is no longer used, |
177 | * its value is set to false in gnome 3. So it is not harmful to check |
178 | * it first in case jdk is used with an old gnome. |
179 | */ |
180 | use_same_proxy = (*my_get_bool_func)(gconf_client, "/system/http_proxy/use_same_proxy" , NULL); |
181 | if (use_same_proxy) { |
182 | phost = (*my_get_string_func)(gconf_client, "/system/http_proxy/host" , NULL); |
183 | pport = (*my_get_int_func)(gconf_client, "/system/http_proxy/port" , NULL); |
184 | use_proxy = (phost != NULL && pport != 0); |
185 | } |
186 | |
187 | if (!use_proxy) { |
188 | /** |
189 | * HTTP: |
190 | * /system/http_proxy/use_http_proxy (boolean) - it's no longer used |
191 | * /system/http_proxy/host (string) |
192 | * /system/http_proxy/port (integer) |
193 | */ |
194 | if (strcasecmp(cproto, "http" ) == 0) { |
195 | phost = (*my_get_string_func)(gconf_client, "/system/http_proxy/host" , NULL); |
196 | pport = (*my_get_int_func)(gconf_client, "/system/http_proxy/port" , NULL); |
197 | use_proxy = (phost != NULL && pport != 0); |
198 | } |
199 | |
200 | /** |
201 | * HTTPS: |
202 | * /system/proxy/mode (string) [ "manual" means use proxy settings ] |
203 | * /system/proxy/secure_host (string) |
204 | * /system/proxy/secure_port (integer) |
205 | */ |
206 | if (strcasecmp(cproto, "https" ) == 0) { |
207 | phost = (*my_get_string_func)(gconf_client, "/system/proxy/secure_host" , NULL); |
208 | pport = (*my_get_int_func)(gconf_client, "/system/proxy/secure_port" , NULL); |
209 | use_proxy = (phost != NULL && pport != 0); |
210 | } |
211 | |
212 | /** |
213 | * FTP: |
214 | * /system/proxy/mode (string) [ "manual" means use proxy settings ] |
215 | * /system/proxy/ftp_host (string) |
216 | * /system/proxy/ftp_port (integer) |
217 | */ |
218 | if (strcasecmp(cproto, "ftp" ) == 0) { |
219 | phost = (*my_get_string_func)(gconf_client, "/system/proxy/ftp_host" , NULL); |
220 | pport = (*my_get_int_func)(gconf_client, "/system/proxy/ftp_port" , NULL); |
221 | use_proxy = (phost != NULL && pport != 0); |
222 | } |
223 | |
224 | /** |
225 | * SOCKS: |
226 | * /system/proxy/mode (string) [ "manual" means use proxy settings ] |
227 | * /system/proxy/socks_host (string) |
228 | * /system/proxy/socks_port (integer) |
229 | */ |
230 | if (strcasecmp(cproto, "socks" ) == 0) { |
231 | phost = (*my_get_string_func)(gconf_client, "/system/proxy/socks_host" , NULL); |
232 | pport = (*my_get_int_func)(gconf_client, "/system/proxy/socks_port" , NULL); |
233 | use_proxy = (phost != NULL && pport != 0); |
234 | if (use_proxy) |
235 | ptype_ID = ptype_socksID; |
236 | } |
237 | } |
238 | } |
239 | |
240 | if (use_proxy) { |
241 | jstring jhost; |
242 | char *noproxyfor; |
243 | char *s; |
244 | |
245 | /** |
246 | * Check for the exclude list (aka "No Proxy For" list). |
247 | * It's a list of comma separated suffixes (e.g. domain name). |
248 | */ |
249 | noproxyfor = (*my_get_string_func)(gconf_client, "/system/proxy/no_proxy_for" , NULL); |
250 | if (noproxyfor != NULL) { |
251 | char *tmpbuf[512]; |
252 | s = strtok_r(noproxyfor, ", " , tmpbuf); |
253 | |
254 | while (s != NULL && strlen(s) <= strlen(chost)) { |
255 | if (strcasecmp(chost+(strlen(chost) - strlen(s)), s) == 0) { |
256 | /** |
257 | * the URL host name matches with one of the sufixes, |
258 | * therefore we have to use a direct connection. |
259 | */ |
260 | use_proxy = 0; |
261 | break; |
262 | } |
263 | s = strtok_r(NULL, ", " , tmpbuf); |
264 | } |
265 | } |
266 | if (use_proxy) { |
267 | jobject proxy = NULL; |
268 | /* create a proxy array with one element. */ |
269 | proxy_array = (*env)->NewObjectArray(env, 1, proxy_class, NULL); |
270 | if (proxy_array == NULL || (*env)->ExceptionCheck(env)) { |
271 | return NULL; |
272 | } |
273 | proxy = createProxy(env, ptype_ID, phost, pport); |
274 | if (proxy == NULL || (*env)->ExceptionCheck(env)) { |
275 | return NULL; |
276 | } |
277 | (*env)->SetObjectArrayElement(env, proxy_array, 0, proxy); |
278 | if ((*env)->ExceptionCheck(env)) { |
279 | return NULL; |
280 | } |
281 | } |
282 | } |
283 | |
284 | return proxy_array; |
285 | } |
286 | |
287 | static int initGProxyResolver() { |
288 | void *gio_handle; |
289 | |
290 | gio_handle = dlopen("libgio-2.0.so" , RTLD_LAZY); |
291 | if (!gio_handle) { |
292 | gio_handle = dlopen("libgio-2.0.so.0" , RTLD_LAZY); |
293 | if (!gio_handle) { |
294 | return 0; |
295 | } |
296 | } |
297 | |
298 | my_g_type_init_func = (g_type_init_func*)dlsym(gio_handle, "g_type_init" ); |
299 | |
300 | g_proxy_resolver_get_default = |
301 | (g_proxy_resolver_get_default_func*)dlsym(gio_handle, |
302 | "g_proxy_resolver_get_default" ); |
303 | |
304 | g_proxy_resolver_lookup = |
305 | (g_proxy_resolver_lookup_func*)dlsym(gio_handle, |
306 | "g_proxy_resolver_lookup" ); |
307 | |
308 | g_network_address_parse_uri = |
309 | (g_network_address_parse_uri_func*)dlsym(gio_handle, |
310 | "g_network_address_parse_uri" ); |
311 | |
312 | g_network_address_get_hostname = |
313 | (g_network_address_get_hostname_func*)dlsym(gio_handle, |
314 | "g_network_address_get_hostname" ); |
315 | |
316 | g_network_address_get_port = |
317 | (g_network_address_get_port_func*)dlsym(gio_handle, |
318 | "g_network_address_get_port" ); |
319 | |
320 | g_strfreev = (g_strfreev_func*)dlsym(gio_handle, "g_strfreev" ); |
321 | |
322 | if (!my_g_type_init_func || |
323 | !g_proxy_resolver_get_default || |
324 | !g_proxy_resolver_lookup || |
325 | !g_network_address_parse_uri || |
326 | !g_network_address_get_hostname || |
327 | !g_network_address_get_port || |
328 | !g_strfreev) |
329 | { |
330 | dlclose(gio_handle); |
331 | return 0; |
332 | } |
333 | |
334 | (*my_g_type_init_func)(); |
335 | return 1; |
336 | } |
337 | |
338 | static jobjectArray getProxyByGProxyResolver(JNIEnv *env, const char *cproto, |
339 | const char *chost) |
340 | { |
341 | GProxyResolver* resolver = NULL; |
342 | char** proxies = NULL; |
343 | GError *error = NULL; |
344 | |
345 | size_t protoLen = 0; |
346 | size_t hostLen = 0; |
347 | char* uri = NULL; |
348 | |
349 | jobjectArray proxy_array = NULL; |
350 | |
351 | resolver = (*g_proxy_resolver_get_default)(); |
352 | if (resolver == NULL) { |
353 | return NULL; |
354 | } |
355 | |
356 | /* Construct the uri, cproto + "://" + chost */ |
357 | protoLen = strlen(cproto); |
358 | hostLen = strlen(chost); |
359 | uri = malloc(protoLen + hostLen + 4); |
360 | if (!uri) { |
361 | /* Out of memory */ |
362 | return NULL; |
363 | } |
364 | memcpy(uri, cproto, protoLen); |
365 | memcpy(uri + protoLen, "://" , 3); |
366 | memcpy(uri + protoLen + 3, chost, hostLen + 1); |
367 | |
368 | /* |
369 | * Looks into the system proxy configuration to determine what proxy, |
370 | * if any, to use to connect to uri. The returned proxy URIs are of |
371 | * the form <protocol>://[user[:password]@]host:port or direct://, |
372 | * where <protocol> could be http, rtsp, socks or other proxying protocol. |
373 | * direct:// is used when no proxy is needed. |
374 | */ |
375 | proxies = (*g_proxy_resolver_lookup)(resolver, uri, NULL, &error); |
376 | free(uri); |
377 | |
378 | if (proxies) { |
379 | if (!error) { |
380 | int i; |
381 | int nr_proxies = 0; |
382 | char** p = proxies; |
383 | /* count the elements in the null terminated string vector. */ |
384 | while (*p) { |
385 | nr_proxies++; |
386 | p++; |
387 | } |
388 | /* create a proxy array that has to be filled. */ |
389 | proxy_array = (*env)->NewObjectArray(env, nr_proxies, proxy_class, NULL); |
390 | if (proxy_array != NULL && !(*env)->ExceptionCheck(env)) { |
391 | for (i = 0; proxies[i]; i++) { |
392 | if (strncmp(proxies[i], "direct://" , 9)) { |
393 | GSocketConnectable* conn = |
394 | (*g_network_address_parse_uri)(proxies[i], 0, |
395 | &error); |
396 | if (conn && !error) { |
397 | const char *phost = NULL; |
398 | unsigned short pport = 0; |
399 | phost = (*g_network_address_get_hostname)(conn); |
400 | pport = (*g_network_address_get_port)(conn); |
401 | if (phost && pport > 0) { |
402 | jobject proxy = NULL; |
403 | jfieldID ptype_ID = ptype_httpID; |
404 | if (!strncmp(proxies[i], "socks" , 5)) |
405 | ptype_ID = ptype_socksID; |
406 | |
407 | proxy = createProxy(env, ptype_ID, phost, pport); |
408 | if (proxy == NULL || (*env)->ExceptionCheck(env)) { |
409 | proxy_array = NULL; |
410 | break; |
411 | } |
412 | (*env)->SetObjectArrayElement(env, proxy_array, i, proxy); |
413 | if ((*env)->ExceptionCheck(env)) { |
414 | proxy_array = NULL; |
415 | break; |
416 | } |
417 | } |
418 | } |
419 | } else { |
420 | /* direct connection - no proxy */ |
421 | jobject proxy = (*env)->GetStaticObjectField(env, proxy_class, |
422 | pr_no_proxyID); |
423 | if (proxy == NULL || (*env)->ExceptionCheck(env)) { |
424 | proxy_array = NULL; |
425 | break; |
426 | } |
427 | (*env)->SetObjectArrayElement(env, proxy_array, i, proxy); |
428 | if ((*env)->ExceptionCheck(env)) { |
429 | proxy_array = NULL; |
430 | break; |
431 | } |
432 | } |
433 | } |
434 | } |
435 | } |
436 | (*g_strfreev)(proxies); |
437 | } |
438 | |
439 | return proxy_array; |
440 | } |
441 | |
442 | /* |
443 | * Class: sun_net_spi_DefaultProxySelector |
444 | * Method: init |
445 | * Signature: ()Z |
446 | */ |
447 | JNIEXPORT jboolean JNICALL |
448 | Java_sun_net_spi_DefaultProxySelector_init(JNIEnv *env, jclass clazz) { |
449 | use_gproxyResolver = initGProxyResolver(); |
450 | if (!use_gproxyResolver) |
451 | use_gconf = initGConf(); |
452 | |
453 | if (use_gproxyResolver || use_gconf) { |
454 | if (initJavaClass(env)) |
455 | return JNI_TRUE; |
456 | } |
457 | return JNI_FALSE; |
458 | } |
459 | |
460 | /* |
461 | * Class: sun_net_spi_DefaultProxySelector |
462 | * Method: getSystemProxies |
463 | * Signature: ([Ljava/lang/String;Ljava/lang/String;)[Ljava/net/Proxy; |
464 | */ |
465 | JNIEXPORT jobjectArray JNICALL |
466 | Java_sun_net_spi_DefaultProxySelector_getSystemProxies(JNIEnv *env, |
467 | jobject this, |
468 | jstring proto, |
469 | jstring host) |
470 | { |
471 | const char* cproto; |
472 | const char* chost; |
473 | |
474 | jboolean isProtoCopy; |
475 | jboolean isHostCopy; |
476 | |
477 | jobjectArray proxyArray = NULL; |
478 | |
479 | cproto = (*env)->GetStringUTFChars(env, proto, &isProtoCopy); |
480 | |
481 | if (cproto != NULL && (use_gproxyResolver || use_gconf)) { |
482 | chost = (*env)->GetStringUTFChars(env, host, &isHostCopy); |
483 | if (chost != NULL) { |
484 | if (use_gproxyResolver) |
485 | proxyArray = getProxyByGProxyResolver(env, cproto, chost); |
486 | else if (use_gconf) |
487 | proxyArray = getProxyByGConf(env, cproto, chost); |
488 | if (isHostCopy == JNI_TRUE) |
489 | (*env)->ReleaseStringUTFChars(env, host, chost); |
490 | } |
491 | if (isProtoCopy == JNI_TRUE) |
492 | (*env)->ReleaseStringUTFChars(env, proto, cproto); |
493 | } |
494 | return proxyArray; |
495 | } |
496 | |
497 | |