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