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 */
67typedef void* gconf_client_get_default_func();
68typedef char* gconf_client_get_string_func(void *, char *, void**);
69typedef int gconf_client_get_int_func(void*, char *, void**);
70typedef int gconf_client_get_bool_func(void*, char *, void**);
71typedef int gconf_init_func(int, char**, void**);
72typedef void g_type_init_func ();
73gconf_client_get_default_func* my_get_default_func = NULL;
74gconf_client_get_string_func* my_get_string_func = NULL;
75gconf_client_get_int_func* my_get_int_func = NULL;
76gconf_client_get_bool_func* my_get_bool_func = NULL;
77gconf_init_func* my_gconf_init_func = NULL;
78g_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 */
92typedef struct _GProxyResolver GProxyResolver;
93typedef struct _GSocketConnectable GSocketConnectable;
94typedef struct GError GError;
95typedef GProxyResolver* g_proxy_resolver_get_default_func();
96typedef char** g_proxy_resolver_lookup_func();
97typedef GSocketConnectable* g_network_address_parse_uri_func();
98typedef const char* g_network_address_get_hostname_func();
99typedef unsigned short g_network_address_get_port_func();
100typedef void g_strfreev_func();
101
102static g_proxy_resolver_get_default_func* g_proxy_resolver_get_default = NULL;
103static g_proxy_resolver_lookup_func* g_proxy_resolver_lookup = NULL;
104static g_network_address_parse_uri_func* g_network_address_parse_uri = NULL;
105static g_network_address_get_hostname_func* g_network_address_get_hostname = NULL;
106static g_network_address_get_port_func* g_network_address_get_port = NULL;
107static g_strfreev_func* g_strfreev = NULL;
108
109static void* gconf_client = NULL;
110static int use_gproxyResolver = 0;
111static int use_gconf = 0;
112
113
114static 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
161static 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
287static 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
338static 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 */
447JNIEXPORT jboolean JNICALL
448Java_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 */
465JNIEXPORT jobjectArray JNICALL
466Java_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