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