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