< prev index next >
src/java.base/windows/native/libnet/DefaultProxySelector.c
Print this page
rev 16207 : 8170868: DefaultProxySelector should use system defaults on Windows, MacOS and Gnome
Contributed-by: arno.zeller@sap.com
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
@@ -22,263 +22,355 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <windows.h>
+#include <Winhttp.h>
+
#include "jni.h"
#include "jni_util.h"
#include "jvm.h"
-#include "jlong.h"
+
+#include "proxy_util.h"
+
#include "sun_net_spi_DefaultProxySelector.h"
/**
* These functions are used by the sun.net.spi.DefaultProxySelector class
* to access some platform specific settings.
- * This is the Windows code using the registry settings.
+ * This is the Windows code using WinHTTP functions to get the system settings.
*/
-static jclass proxy_class;
-static jclass isaddr_class;
-static jclass ptype_class;
-static jmethodID isaddr_createUnresolvedID;
-static jmethodID proxy_ctrID;
-static jfieldID pr_no_proxyID;
-static jfieldID ptype_httpID;
-static jfieldID ptype_socksID;
+/* Keep one static session for all requests. */
+static HINTERNET session = NULL;
/*
* Class: sun_net_spi_DefaultProxySelector
* Method: init
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL
Java_sun_net_spi_DefaultProxySelector_init(JNIEnv *env, jclass clazz) {
- HKEY hKey;
- LONG ret;
- jclass cls;
-
- /**
- * Get all the method & field IDs for later use.
- */
- cls = (*env)->FindClass(env,"java/net/Proxy");
- CHECK_NULL_RETURN(cls, JNI_FALSE);
- proxy_class = (*env)->NewGlobalRef(env, cls);
- CHECK_NULL_RETURN(proxy_class, JNI_FALSE);
- cls = (*env)->FindClass(env,"java/net/Proxy$Type");
- CHECK_NULL_RETURN(cls, JNI_FALSE);
- ptype_class = (*env)->NewGlobalRef(env, cls);
- CHECK_NULL_RETURN(ptype_class, JNI_FALSE);
- cls = (*env)->FindClass(env, "java/net/InetSocketAddress");
- CHECK_NULL_RETURN(cls, JNI_FALSE);
- isaddr_class = (*env)->NewGlobalRef(env, cls);
- CHECK_NULL_RETURN(isaddr_class, JNI_FALSE);
- proxy_ctrID = (*env)->GetMethodID(env, proxy_class, "<init>",
- "(Ljava/net/Proxy$Type;Ljava/net/SocketAddress;)V");
- CHECK_NULL_RETURN(proxy_ctrID, JNI_FALSE);
- pr_no_proxyID = (*env)->GetStaticFieldID(env, proxy_class, "NO_PROXY", "Ljava/net/Proxy;");
- CHECK_NULL_RETURN(pr_no_proxyID, JNI_FALSE);
- ptype_httpID = (*env)->GetStaticFieldID(env, ptype_class, "HTTP", "Ljava/net/Proxy$Type;");
- CHECK_NULL_RETURN(ptype_httpID, JNI_FALSE);
- ptype_socksID = (*env)->GetStaticFieldID(env, ptype_class, "SOCKS", "Ljava/net/Proxy$Type;");
- CHECK_NULL_RETURN(ptype_socksID, JNI_FALSE);
- isaddr_createUnresolvedID = (*env)->GetStaticMethodID(env, isaddr_class, "createUnresolved",
- "(Ljava/lang/String;I)Ljava/net/InetSocketAddress;");
- CHECK_NULL_RETURN(isaddr_createUnresolvedID, JNI_FALSE);
-
- /**
- * Let's see if we can find the proper Registry entry.
- */
- ret = RegOpenKeyEx(HKEY_CURRENT_USER,
- "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
- 0, KEY_READ, (PHKEY)&hKey);
- if (ret == ERROR_SUCCESS) {
- RegCloseKey(hKey);
- /**
- * It worked, we can probably rely on it then.
+
+ /*
+ * Get one WinHTTP session handle to initialize the WinHTTP internal data structures
+ * We keep and use only this one for the whole life time.
*/
- return JNI_TRUE;
- }
+ session = WinHttpOpen(L"Only used internal", /* we need no real agent string here */
+ WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
+ WINHTTP_NO_PROXY_NAME,
+ WINHTTP_NO_PROXY_BYPASS,
+ 0);
+ if (session == NULL) {
+ return JNI_FALSE;
+ }
+
+ if (!initJavaClass(env)) {
+ return JNI_FALSE;
+ }
- return JNI_FALSE;
+ return JNI_TRUE;
}
+
#define MAX_STR_LEN 1024
+/* A linked list element for a proxy */
+typedef struct list_item {
+ wchar_t *host;
+ int port;
+ struct list_item *next;
+} list_item;
+
+/* Free the linked list */
+static void freeList(list_item *head) {
+ list_item *next = NULL;
+ list_item *current = head;
+ while (current != NULL) {
+ next = current->next;
+ free(current->host);
+ free(current);
+ current = next;
+ }
+}
+
+
/*
- * Class: sun_net_spi_DefaultProxySelector
- * Method: getSystemProxy
- * Signature: ([Ljava/lang/String;Ljava/lang/String;)Ljava/net/Proxy;
+ * Creates a linked list of list_item elements that has to be freed later on.
+ * Returns the size of the array as int.
*/
-JNIEXPORT jobject JNICALL
-Java_sun_net_spi_DefaultProxySelector_getSystemProxy(JNIEnv *env,
- jobject this,
- jstring proto,
- jstring host)
-{
- jobject isa = NULL;
- jobject proxy = NULL;
- jobject type_proxy = NULL;
- jobject no_proxy = NULL;
- jboolean isCopy;
- HKEY hKey;
- LONG ret;
- const char* cproto;
- const char* urlhost;
- char pproto[MAX_STR_LEN];
- char regserver[MAX_STR_LEN];
- char override[MAX_STR_LEN];
- char *s, *s2;
- char *ctx = NULL;
- int pport = 0;
- int defport = 0;
- char *phost;
-
- /**
- * Let's open the Registry entry. We'll check a few values in it:
- *
- * - ProxyEnable: 0 means no proxy, 1 means use the proxy
- * - ProxyServer: a string that can take 2 forms:
- * "server[:port]"
- * or
- * "protocol1=server[:port][;protocol2=server[:port]]..."
- * - ProxyOverride: a string containing a list of prefixes for hostnames.
- * e.g.: hoth;localhost;<local>
- */
- ret = RegOpenKeyEx(HKEY_CURRENT_USER,
- "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
- 0, KEY_READ, (PHKEY)&hKey);
- if (ret == ERROR_SUCCESS) {
- DWORD dwLen;
- DWORD dwProxyEnabled;
- ULONG ulType;
- dwLen = sizeof(dwProxyEnabled);
-
- /**
- * Let's see if the proxy settings are to be used.
+static int createProxyList(LPWSTR win_proxy, const WCHAR *pproto, list_item **head) {
+ static const wchar_t separators[] = L"\t\r\n ;";
+ list_item *current = NULL;
+ int nr_elems = 0;
+ wchar_t *context = NULL;
+ wchar_t *current_proxy = NULL;
+ BOOL error = FALSE;
+
+ /*
+ * The proxy server list contains one or more of the following strings separated by semicolons or whitespace.
+ * ([<scheme>=][<scheme>"://"]<server>[":"<port>])
*/
- ret = RegQueryValueEx(hKey, "ProxyEnable", NULL, &ulType,
- (LPBYTE)&dwProxyEnabled, &dwLen);
- if ((ret == ERROR_SUCCESS) && (dwProxyEnabled > 0)) {
- /*
- * Yes, ProxyEnable == 1
- */
- dwLen = sizeof(override);
- override[0] = 0;
- ret = RegQueryValueEx(hKey, "ProxyOverride", NULL, &ulType,
- (LPBYTE)&override, &dwLen);
- dwLen = sizeof(regserver);
- regserver[0] = 0;
- ret = RegQueryValueEx(hKey, "ProxyServer", NULL, &ulType,
- (LPBYTE)®server, &dwLen);
- RegCloseKey(hKey);
- if (ret == ERROR_SUCCESS) {
- if (strlen(override) > 0) {
- /**
- * we did get ProxyServer and may have an override.
- * So let's check the override list first, by walking down the list
- * The semicolons (;) separated entries have to be matched with the
- * the beginning of the hostname.
- */
- s = strtok_s(override, "; ", &ctx);
- urlhost = (*env)->GetStringUTFChars(env, host, &isCopy);
- if (urlhost == NULL) {
- if (!(*env)->ExceptionCheck(env))
- JNU_ThrowOutOfMemoryError(env, NULL);
- return NULL;
+ current_proxy = wcstok_s(win_proxy, separators, &context);
+ while (current_proxy != NULL) {
+ LPWSTR pport;
+ LPWSTR phost;
+ int portVal = 0;
+ wchar_t *next_proxy = NULL;
+ list_item *proxy = NULL;
+ wchar_t* pos = NULL;
+
+ /* Filter based on the scheme, if there is one */
+ pos = wcschr(current_proxy, L'=');
+ if (pos) {
+ *pos = L'\0';
+ if (wcscmp(current_proxy, pproto) != 0) {
+ current_proxy = wcstok_s(NULL, separators, &context);
+ continue;
}
- while (s != NULL) {
- if (strncmp(s, urlhost, strlen(s)) == 0) {
- /**
- * the URL host name matches with one of the prefixes,
- * therefore we have to use a direct connection.
- */
- if (isCopy == JNI_TRUE)
- (*env)->ReleaseStringUTFChars(env, host, urlhost);
- goto noproxy;
+ current_proxy = pos + 1;
+ }
+
+ /* Let's check for a scheme and ignore it. */
+ if ((phost = wcsstr(current_proxy, L"://")) != NULL) {
+ phost += 3;
+ } else {
+ phost = current_proxy;
+ }
+
+ /* Get the port */
+ pport = wcschr(phost, L':');
+ if (pport != NULL) {
+ *pport = 0;
+ pport++;
+ swscanf(pport, L"%d", &portVal);
+ }
+
+ proxy = (list_item *)malloc(sizeof(list_item));
+ if (proxy != NULL) {
+ proxy->next = NULL;
+ proxy->port = portVal;
+ proxy->host = _wcsdup(phost);
+
+ if (proxy->host != NULL) {
+ if (*head == NULL) {
+ *head = proxy; /* first elem */
+ }
+ if (current != NULL) {
+ current->next = proxy;
+ }
+ current = proxy;
+ nr_elems++;
+ } else {
+ free(proxy); /* cleanup */
}
- s = strtok_s(NULL, "; ", &ctx);
- }
- if (isCopy == JNI_TRUE)
- (*env)->ReleaseStringUTFChars(env, host, urlhost);
}
+ /* goto next proxy if available... */
+ current_proxy = wcstok_s(NULL, separators, &context);
+ }
+ return nr_elems;
+}
+
+
+
+/*
+ * Class: sun_net_spi_DefaultProxySelector
+ * Method: getSystemProxies
+ * Signature: ([Ljava/lang/String;Ljava/lang/String;)[Ljava/net/Proxy;
+ */
+JNIEXPORT jobjectArray JNICALL
+Java_sun_net_spi_DefaultProxySelector_getSystemProxies(JNIEnv *env,
+ jobject this,
+ jstring proto,
+ jstring host)
+{
+ jobjectArray proxy_array = NULL;
+ jobject type_proxy = NULL;
+ LPCWSTR lpProto;
+ LPCWSTR lpHost;
+ list_item *head = NULL;
+
+ BOOL use_auto_proxy = FALSE;
+ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ie_proxy_config;
+ WINHTTP_AUTOPROXY_OPTIONS auto_proxy_options;
+ WINHTTP_PROXY_INFO proxy_info;
+ LPWSTR win_proxy = NULL;
+ LPWSTR win_bypass_proxy = NULL;
+
+ memset(&ie_proxy_config, 0, sizeof(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG));
+ memset(&auto_proxy_options, 0, sizeof(WINHTTP_AUTOPROXY_OPTIONS));
+ memset(&proxy_info, 0, sizeof(WINHTTP_PROXY_INFO));
+
+ lpHost = (*env)->GetStringChars(env, host, NULL);
+ if (lpHost == NULL) {
+ if (!(*env)->ExceptionCheck(env))
+ JNU_ThrowOutOfMemoryError(env, NULL);
+ return NULL;
+ }
- cproto = (*env)->GetStringUTFChars(env, proto, &isCopy);
- if (cproto == NULL) {
- if (!(*env)->ExceptionCheck(env))
+ lpProto = (*env)->GetStringChars(env, proto, NULL);
+ if (lpProto == NULL) {
+ (*env)->ReleaseStringChars(env, host, lpHost);
+ if (!(*env)->ExceptionCheck(env))
JNU_ThrowOutOfMemoryError(env, NULL);
- return NULL;
+ return NULL;
+ }
+
+ if (WinHttpGetIEProxyConfigForCurrentUser(&ie_proxy_config) == FALSE) {
+ /* cleanup and exit */
+ (*env)->ReleaseStringChars(env, host, lpHost);
+ (*env)->ReleaseStringChars(env, proto, lpProto);
+ return NULL;
+ }
+
+ if (ie_proxy_config.fAutoDetect) {
+ /* Windows uses WPAD */
+ auto_proxy_options.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP |
+ WINHTTP_AUTO_DETECT_TYPE_DNS_A;
+ auto_proxy_options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
+ auto_proxy_options.fAutoLogonIfChallenged = TRUE;
+ use_auto_proxy = TRUE;
+ } else if (ie_proxy_config.lpszAutoConfigUrl != NULL) {
+ /* Windows uses PAC file */
+ auto_proxy_options.lpszAutoConfigUrl = ie_proxy_config.lpszAutoConfigUrl;
+ auto_proxy_options.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
+ use_auto_proxy = TRUE;
+ } else if (ie_proxy_config.lpszProxy != NULL) {
+ /* Windows uses manually entered proxy. */
+ use_auto_proxy = FALSE;
+ win_bypass_proxy = ie_proxy_config.lpszProxyBypass;
+ win_proxy = ie_proxy_config.lpszProxy;
+ }
+
+ if (use_auto_proxy) {
+ WCHAR url[MAX_STR_LEN];
+ /* Create url for WinHttpGetProxyForUrl */
+ _snwprintf(url, sizeof(url) - 1, L"%s://%s", lpProto, lpHost);
+ /* Get proxy for URL from Windows */
+ use_auto_proxy = WinHttpGetProxyForUrl(session, &url[0], &auto_proxy_options, &proxy_info);
+ if (use_auto_proxy) {
+ win_proxy = proxy_info.lpszProxy;
+ win_bypass_proxy = proxy_info.lpszProxyBypass;
}
+ }
+ /* Check the bypass entry. */
+ if (NULL != win_bypass_proxy) {
/*
- * Set default port value & proxy type from protocol.
+ * From MSDN:
+ * The proxy bypass list contains one or more server names separated by semicolons or
+ * whitespace. The proxy bypass list can also contain the string "<local>" to indicate
+ * that all local intranet sites are bypassed. Local intranet sites are considered to
+ * be all servers that do not contain a period in their name.
*/
- if ((strcmp(cproto, "http") == 0) ||
- (strcmp(cproto, "ftp") == 0) ||
- (strcmp(cproto, "gopher") == 0))
- defport = 80;
- if (strcmp(cproto, "https") == 0)
- defport = 443;
- if (strcmp(cproto, "socks") == 0) {
- defport = 1080;
- type_proxy = (*env)->GetStaticObjectField(env, ptype_class, ptype_socksID);
- } else {
- type_proxy = (*env)->GetStaticObjectField(env, ptype_class, ptype_httpID);
+ wchar_t *context = NULL;
+ LPWSTR s = wcstok_s(win_bypass_proxy, L"; ", &context);
+
+ while (s != NULL) {
+ size_t maxlen = wcslen(s);
+ if (wcsncmp(s, lpHost, maxlen) == 0) {
+ /*
+ * The URL host name matches with one of the prefixes, we have to use a direct
+ * connection.
+ */
+ goto noproxy;
+ }
+ if (wcsncmp(s, L"<local>", maxlen) == 0) {
+ /*
+ * All local intranet sites are bypassed - Microsoft considers all servers that
+ * do not contain a period in their name to be local.
+ */
+ if (wcschr(lpHost, '.') == NULL) {
+ goto noproxy;
+ }
+ }
+ s = wcstok_s(NULL, L"; ", &context);
}
+ }
+
+ if (win_proxy != NULL) {
+ wchar_t *context = NULL;
+ int defport = 0;
+ int nr_elems = 0;
- sprintf(pproto,"%s=", cproto);
- if (isCopy == JNI_TRUE)
- (*env)->ReleaseStringUTFChars(env, proto, cproto);
/**
- * Let's check the protocol specific form first.
+ * Set default port value & proxy type from protocol.
*/
- if ((s = strstr(regserver, pproto)) != NULL) {
- s += strlen(pproto);
+ if ((wcscmp(lpProto, L"http") == 0) ||
+ (wcscmp(lpProto, L"ftp") == 0) ||
+ (wcscmp(lpProto, L"gopher") == 0))
+ defport = 80;
+ if (wcscmp(lpProto, L"https") == 0)
+ defport = 443;
+ if (wcscmp(lpProto, L"socks") == 0) {
+ defport = 1080;
+ type_proxy = (*env)->GetStaticObjectField(env, ptype_class, ptype_socksID);
} else {
- /**
- * If we couldn't find *this* protocol but the string is in the
- * protocol specific format, then don't use proxy
- */
- if (strchr(regserver, '=') != NULL)
- goto noproxy;
- s = regserver;
+ type_proxy = (*env)->GetStaticObjectField(env, ptype_class, ptype_httpID);
}
- s2 = strchr(s, ';');
- if (s2 != NULL)
- *s2 = 0;
-
- /**
- * Is there a port specified?
- */
- s2 = strchr(s, ':');
- if (s2 != NULL) {
- *s2 = 0;
- s2++;
- sscanf(s2, "%d", &pport);
+ if (type_proxy == NULL || (*env)->ExceptionCheck(env)) {
+ goto noproxy;
}
- phost = s;
- if (phost != NULL) {
- /**
- * Let's create the appropriate Proxy object then.
- */
- jstring jhost;
- if (pport == 0)
- pport = defport;
- jhost = (*env)->NewStringUTF(env, phost);
- CHECK_NULL_RETURN(jhost, NULL);
- isa = (*env)->CallStaticObjectMethod(env, isaddr_class, isaddr_createUnresolvedID, jhost, pport);
- CHECK_NULL_RETURN(isa, NULL);
- proxy = (*env)->NewObject(env, proxy_class, proxy_ctrID, type_proxy, isa);
- return proxy;
+ nr_elems = createProxyList(win_proxy, lpProto, &head);
+ if (nr_elems != 0 && head != NULL) {
+ int index = 0;
+ proxy_array = (*env)->NewObjectArray(env, nr_elems, proxy_class, NULL);
+ if (proxy_array == NULL || (*env)->ExceptionCheck(env)) {
+ goto noproxy;
+ }
+ while (head != NULL && index < nr_elems) {
+ jstring jhost;
+ jobject isa;
+ jobject proxy;
+
+ if (head->host != NULL && proxy_array != NULL) {
+ /* Let's create the appropriate Proxy object then. */
+ if (head->port == 0) {
+ head->port = defport;
+ }
+ jhost = (*env)->NewString(env, head->host, (jsize)wcslen(head->host));
+ if (jhost == NULL || (*env)->ExceptionCheck(env)) {
+ proxy_array = NULL;
+ }
+ isa = (*env)->CallStaticObjectMethod(env, isaddr_class,
+ isaddr_createUnresolvedID, jhost,
+ head->port);
+ if (isa == NULL || (*env)->ExceptionCheck(env)) {
+ proxy_array = NULL;
+ }
+ proxy = (*env)->NewObject(env, proxy_class, proxy_ctrID, type_proxy, isa);
+ if (proxy == NULL || (*env)->ExceptionCheck(env)) {
+ proxy_array = NULL;
+ }
+ (*env)->SetObjectArrayElement(env, proxy_array, index, proxy);
+ if ((*env)->ExceptionCheck(env)) {
+ proxy_array = NULL;
+ }
+ index++;
+ }
+ head = head->next;
+ }
}
- }
- } else {
- /* ProxyEnable == 0 or Query failed */
- /* close the handle to the registry key */
- RegCloseKey(hKey);
}
- }
noproxy:
- no_proxy = (*env)->GetStaticObjectField(env, proxy_class, pr_no_proxyID);
- return no_proxy;
+ if (head != NULL) {
+ freeList(head);
+ }
+ if (proxy_info.lpszProxy != NULL)
+ GlobalFree(proxy_info.lpszProxy);
+ if (proxy_info.lpszProxyBypass != NULL)
+ GlobalFree(proxy_info.lpszProxyBypass);
+ if (ie_proxy_config.lpszAutoConfigUrl != NULL)
+ GlobalFree(ie_proxy_config.lpszAutoConfigUrl);
+ if (ie_proxy_config.lpszProxy != NULL)
+ GlobalFree(ie_proxy_config.lpszProxy);
+ if (ie_proxy_config.lpszProxyBypass != NULL)
+ GlobalFree(ie_proxy_config.lpszProxyBypass);
+ if (lpHost != NULL)
+ (*env)->ReleaseStringChars(env, host, lpHost);
+ if (lpProto != NULL)
+ (*env)->ReleaseStringChars(env, proto, lpProto);
+
+ return proxy_array;
}
< prev index next >