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