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