OpenJDK / amber / amber
changeset 43501:243c346dc905
8170868: DefaultProxySelector should use system defaults on Windows, MacOS and Gnome
Reviewed-by: chegar, simonis, clanger, stuefe, erikj
Contributed-by: arno.zeller@sap.com
author | clanger |
---|---|
date | Thu, 02 Feb 2017 10:28:47 +0100 |
parents | c55815d5774f |
children | aec39566b45e |
files | jdk/make/lib/NetworkingLibraries.gmk jdk/make/mapfiles/libnet/mapfile-vers jdk/src/java.base/macosx/native/libnet/DefaultProxySelector.c jdk/src/java.base/share/classes/java/net/doc-files/net-properties.html jdk/src/java.base/share/classes/sun/net/spi/DefaultProxySelector.java jdk/src/java.base/share/native/libnet/proxy_util.c jdk/src/java.base/share/native/libnet/proxy_util.h jdk/src/java.base/unix/native/libnet/DefaultProxySelector.c jdk/src/java.base/windows/native/libnet/DefaultProxySelector.c jdk/test/java/net/ProxySelector/SystemProxies.java |
diffstat | 10 files changed, 875 insertions(+), 380 deletions(-) [+] |
line wrap: on
line diff
--- a/jdk/make/lib/NetworkingLibraries.gmk Wed Feb 01 23:33:39 2017 +0300 +++ b/jdk/make/lib/NetworkingLibraries.gmk Thu Feb 02 10:28:47 2017 +0100 @@ -1,5 +1,5 @@ # -# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, 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 @@ -44,8 +44,9 @@ LIBS_linux := $(LIBDL) -lpthread, \ LIBS_solaris := -lnsl -lsocket $(LIBDL) -lc, \ LIBS_aix := $(LIBDL),\ - LIBS_windows := ws2_32.lib jvm.lib secur32.lib iphlpapi.lib \ + LIBS_windows := ws2_32.lib jvm.lib secur32.lib iphlpapi.lib winhttp.lib \ delayimp.lib $(WIN_JAVA_LIB) advapi32.lib, \ + LIBS_macosx := -framework CoreFoundation -framework CoreServices, \ VERSIONINFO_RESOURCE := $(GLOBAL_VERSION_INFO_RESOURCE), \ RC_FLAGS := $(RC_FLAGS) \ -D "JDK_FNAME=net.dll" \
--- a/jdk/make/mapfiles/libnet/mapfile-vers Wed Feb 01 23:33:39 2017 +0300 +++ b/jdk/make/mapfiles/libnet/mapfile-vers Thu Feb 02 10:28:47 2017 +0100 @@ -1,5 +1,5 @@ # -# Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1997, 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 @@ -97,7 +97,7 @@ Java_sun_net_sdp_SdpSupport_convert0; Java_sun_net_sdp_SdpSupport_create0; Java_sun_net_spi_DefaultProxySelector_init; - Java_sun_net_spi_DefaultProxySelector_getSystemProxy; + Java_sun_net_spi_DefaultProxySelector_getSystemProxies; NET_SockaddrToInetAddress; NET_SockaddrEqualsInetAddress; NET_InetAddressToSockaddr;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/macosx/native/libnet/DefaultProxySelector.c Thu Feb 02 10:28:47 2017 +0100 @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017 SAP SE. 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 + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include <string.h> +#include <CoreFoundation/CoreFoundation.h> +#include <CoreServices/CoreServices.h> + +#include "jni.h" +#include "jni_util.h" +#include "jvm.h" +#include "jvm_md.h" + +#include "proxy_util.h" + +#include "sun_net_spi_DefaultProxySelector.h" + + +/** + * For more information on how to use the APIs in "CFProxySupport.h" see: + * https://developer.apple.com/legacy/library/samplecode/CFProxySupportTool/Introduction/Intro.html + */ + +#define kResolveProxyRunLoopMode CFSTR("sun.net.spi.DefaultProxySelector") + +#define BUFFER_SIZE 1024 + +/* Callback for CFNetworkExecuteProxyAutoConfigurationURL. */ +static void proxyUrlCallback(void * client, CFArrayRef proxies, CFErrorRef error) { + /* client is a pointer to a CFTypeRef and holds either proxies or an error. */ + CFTypeRef* resultPtr = (CFTypeRef *)client; + + if (error != NULL) { + *resultPtr = CFRetain(error); + } else { + *resultPtr = CFRetain(proxies); + } + CFRunLoopStop(CFRunLoopGetCurrent()); +} + +/* + * Returns a new array of proxies containing all the given non-PAC proxies as + * well as the results of executing all the given PAC-based proxies, for the + * specified URL. 'proxies' is a list that may contain both PAC and non-PAC + * proxies. + */ +static CFArrayRef createExpandedProxiesArray(CFArrayRef proxies, CFURLRef url) { + + CFIndex count; + CFIndex index; + CFMutableArrayRef expandedProxiesArray; + + expandedProxiesArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (expandedProxiesArray == NULL) + return NULL; + + /* Iterate over the array of proxies */ + count = CFArrayGetCount(proxies); + for (index = 0; index < count ; index++) { + CFDictionaryRef currentProxy; + CFStringRef proxyType; + + currentProxy = (CFDictionaryRef) CFArrayGetValueAtIndex(proxies, index); + if(currentProxy == NULL) { + CFRelease(expandedProxiesArray); + return NULL; + } + proxyType = (CFStringRef) CFDictionaryGetValue(currentProxy, kCFProxyTypeKey); + if (proxyType == NULL) { + CFRelease(expandedProxiesArray); + return NULL; + } + + if (!CFEqual(proxyType, kCFProxyTypeAutoConfigurationURL)) { + /* Non-PAC entry, just copy it to the new array */ + CFArrayAppendValue(expandedProxiesArray, currentProxy); + } else { + /* PAC-based URL, execute its script append its results */ + CFRunLoopSourceRef runLoop; + CFURLRef scriptURL; + CFTypeRef result = NULL; + CFStreamClientContext context = { 0, &result, NULL, NULL, NULL }; + CFTimeInterval timeout = 5; + + scriptURL = CFDictionaryGetValue(currentProxy, kCFProxyAutoConfigurationURLKey); + + runLoop = CFNetworkExecuteProxyAutoConfigurationURL(scriptURL, url, proxyUrlCallback, + &context); + if (runLoop != NULL) { + /* + * Despite the fact that CFNetworkExecuteProxyAutoConfigurationURL has + * neither a "Create" nor a "Copy" in the name, we are required to + * release the return CFRunLoopSourceRef <rdar://problem/5533931>. + */ + CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoop, kResolveProxyRunLoopMode); + CFRunLoopRunInMode(kResolveProxyRunLoopMode, timeout, false); + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runLoop, kResolveProxyRunLoopMode); + + /* + * Once the runloop returns, there will be either an error result or + * a proxies array result. Do the appropriate thing with that result. + */ + if (result != NULL) { + if (CFGetTypeID(result) == CFArrayGetTypeID()) { + /* + * Append the new array from the PAC list - it contains + * only non-PAC entries. + */ + CFArrayAppendArray(expandedProxiesArray, result, + CFRangeMake(0, CFArrayGetCount(result))); + } + CFRelease(result); + } + CFRelease(runLoop); + } + } + } + return expandedProxiesArray; +} + + +/* + * Class: sun_net_spi_DefaultProxySelector + * Method: init + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL +Java_sun_net_spi_DefaultProxySelector_init(JNIEnv *env, jclass clazz) { + if (!initJavaClass(env)) { + return JNI_FALSE; + } + return JNI_TRUE; +} + + +/* + * 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) +{ + CFDictionaryRef proxyDicRef = NULL; + CFURLRef urlRef = NULL; + bool proxyFound = false; + jobjectArray proxyArray = NULL; + const char *cproto; + const char *chost; + + /* Get system proxy settings */ + proxyDicRef = CFNetworkCopySystemProxySettings(); + if (proxyDicRef == NULL) { + return NULL; + } + + /* Create CFURLRef from proto and host */ + cproto = (*env)->GetStringUTFChars(env, proto, NULL); + if (cproto != NULL) { + chost = (*env)->GetStringUTFChars(env, host, NULL); + if (chost != NULL) { + char* uri = NULL; + size_t protoLen = 0; + size_t hostLen = 0; + + protoLen = strlen(cproto); + hostLen = strlen(chost); + + /* Construct the uri, cproto + "://" + chost */ + uri = malloc(protoLen + hostLen + 4); + if (uri != NULL) { + memcpy(uri, cproto, protoLen); + memcpy(uri + protoLen, "://", 3); + memcpy(uri + protoLen + 3, chost, hostLen + 1); + + urlRef = CFURLCreateWithBytes(NULL, (const UInt8 *) uri, strlen(uri), + kCFStringEncodingUTF8, NULL); + free(uri); + } + (*env)->ReleaseStringUTFChars(env, host, chost); + } + (*env)->ReleaseStringUTFChars(env, proto, cproto); + } + if (urlRef != NULL) { + CFArrayRef urlProxyArrayRef = CFNetworkCopyProxiesForURL(urlRef, proxyDicRef); + if (urlProxyArrayRef != NULL) { + CFIndex count; + CFIndex index; + + CFArrayRef expandedProxyArray = createExpandedProxiesArray(urlProxyArrayRef, urlRef); + CFRelease(urlProxyArrayRef); + + if (expandedProxyArray == NULL) { + CFRelease(urlRef); + CFRelease(proxyDicRef); + return NULL; + } + + count = CFArrayGetCount(expandedProxyArray); + + proxyArray = (*env)->NewObjectArray(env, count, proxy_class, NULL); + if (proxyArray != NULL || (*env)->ExceptionCheck(env)) { + /* Iterate over the expanded array of proxies */ + for (index = 0; index < count ; index++) { + CFDictionaryRef currentProxy; + CFStringRef proxyType; + jobject proxy = NULL; + + currentProxy = (CFDictionaryRef) CFArrayGetValueAtIndex(expandedProxyArray, + index); + proxyType = (CFStringRef) CFDictionaryGetValue(currentProxy, kCFProxyTypeKey); + if (CFEqual(proxyType, kCFProxyTypeNone)) { + /* This entry states no proxy, therefore just add a NO_PROXY object. */ + proxy = (*env)->GetStaticObjectField(env, proxy_class, pr_no_proxyID); + } else { + /* + * Create a proxy object for this entry. + * Differentiate between SOCKS and HTTP type. + */ + jfieldID typeID = ptype_httpID; + if (CFEqual(proxyType, kCFProxyTypeSOCKS)) { + typeID = ptype_socksID; + } + CFNumberRef portNumberRef = (CFNumberRef)CFDictionaryGetValue(currentProxy, + (const void*)kCFProxyPortNumberKey); + if (portNumberRef != NULL) { + int port = 0; + if (CFNumberGetValue(portNumberRef, kCFNumberSInt32Type, &port)) { + CFStringRef hostNameRef = (CFStringRef)CFDictionaryGetValue( + currentProxy, (const void*)kCFProxyHostNameKey); + if (hostNameRef != NULL) { + char hostNameBuffer[BUFFER_SIZE]; + if (CFStringGetCString(hostNameRef, hostNameBuffer, + BUFFER_SIZE, kCFStringEncodingUTF8)) { + proxy = createProxy(env, typeID, &hostNameBuffer[0], port); + } + } + } + } + } + if (proxy == NULL || (*env)->ExceptionCheck(env)) { + proxyArray = NULL; + break; + } + (*env)->SetObjectArrayElement(env, proxyArray, index, proxy); + if ((*env)->ExceptionCheck(env)) { + proxyArray = NULL; + break; + } + } + } + CFRelease(expandedProxyArray); + } + CFRelease(urlRef); + } + CFRelease(proxyDicRef); + + return proxyArray; +}
--- a/jdk/src/java.base/share/classes/java/net/doc-files/net-properties.html Wed Feb 01 23:33:39 2017 +0300 +++ b/jdk/src/java.base/share/classes/java/net/doc-files/net-properties.html Thu Feb 02 10:28:47 2017 +0100 @@ -149,7 +149,7 @@ the <B>user.name</B> property will be used with no password.</P> </UL> <LI><P><B>java.net.useSystemProxies</B> (default: false)<BR> - On recent Windows systems and on Gnome 2.x systems it is possible to + On Windows systems, macOS systems and on Gnome systems it is possible to tell the java.net stack, setting this property to <B>true</B>, to use the system proxy settings (both these systems let you set proxies globally through their user interface). Note that this property is
--- a/jdk/src/java.base/share/classes/sun/net/spi/DefaultProxySelector.java Wed Feb 01 23:33:39 2017 +0300 +++ b/jdk/src/java.base/share/classes/sun/net/spi/DefaultProxySelector.java Thu Feb 02 10:28:47 2017 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -30,16 +30,19 @@ import java.net.ProxySelector; import java.net.SocketAddress; import java.net.URI; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.io.IOException; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.StringJoiner; import java.util.regex.Pattern; +import java.util.stream.Stream; import sun.net.NetProperties; import sun.net.SocksProxy; import static java.util.regex.Pattern.quote; +import static java.util.stream.Collectors.collectingAndThen; +import static java.util.stream.Collectors.toList; /** * Supports proxy settings using system properties This proxy selector @@ -87,6 +90,8 @@ private static boolean hasSystemProxies = false; + private static final List<Proxy> NO_PROXY_LIST = List.of(Proxy.NO_PROXY); + static { final String key = "java.net.useSystemProxies"; Boolean b = AccessController.doPrivileged( @@ -149,8 +154,9 @@ * select() method. Where all the hard work is done. * Build a list of proxies depending on URI. * Since we're only providing compatibility with the system properties - * from previous releases (see list above), that list will always - * contain 1 single proxy, default being NO_PROXY. + * from previous releases (see list above), that list will typically + * contain one single proxy, default being NO_PROXY. + * If we can get a system proxy it might contain more entries. */ public java.util.List<Proxy> select(URI uri) { if (uri == null) { @@ -185,7 +191,6 @@ if (protocol == null || host == null) { throw new IllegalArgumentException("protocol = "+protocol+" host = "+host); } - List<Proxy> proxyl = new ArrayList<Proxy>(1); NonProxyInfo pinfo = null; @@ -214,9 +219,9 @@ * System properties it does help having only 1 call to doPrivileged. * Be mindful what you do in here though! */ - Proxy p = AccessController.doPrivileged( - new PrivilegedAction<Proxy>() { - public Proxy run() { + Proxy[] proxyArray = AccessController.doPrivileged( + new PrivilegedAction<Proxy[]>() { + public Proxy[] run() { int i, j; String phost = null; int pport = 0; @@ -239,8 +244,8 @@ /** * No system property defined for that * protocol. Let's check System Proxy - * settings (Gnome & Windows) if we were - * instructed to. + * settings (Gnome, MacOsX & Windows) if + * we were instructed to. */ if (hasSystemProxies) { String sproto; @@ -248,12 +253,9 @@ sproto = "socks"; else sproto = proto; - Proxy sproxy = getSystemProxy(sproto, urlhost); - if (sproxy != null) { - return sproxy; - } + return getSystemProxies(sproto, urlhost); } - return Proxy.NO_PROXY; + return null; } // If a Proxy Host is defined for that protocol // Let's get the NonProxyHosts property @@ -281,7 +283,7 @@ } } if (shouldNotUseProxyFor(nprop.pattern, urlhost)) { - return Proxy.NO_PROXY; + return null; } } } @@ -311,22 +313,24 @@ saddr = InetSocketAddress.createUnresolved(phost, pport); // Socks is *always* the last on the list. if (j == (props[i].length - 1)) { - return SocksProxy.create(saddr, socksProxyVersion()); - } else { - return new Proxy(Proxy.Type.HTTP, saddr); + return new Proxy[] {SocksProxy.create(saddr, socksProxyVersion())}; } + return new Proxy[] {new Proxy(Proxy.Type.HTTP, saddr)}; } } - return Proxy.NO_PROXY; + return null; }}); - proxyl.add(p); - /* - * If no specific property was set for that URI, we should be - * returning an iterator to an empty List. - */ - return proxyl; + if (proxyArray != null) { + // Remove duplicate entries, while preserving order. + return Stream.of(proxyArray).distinct().collect( + collectingAndThen(toList(), Collections::unmodifiableList)); + } + + // If no specific proxy was found, return a standard list containing + // only one NO_PROXY entry. + return NO_PROXY_LIST; } public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { @@ -354,7 +358,7 @@ } private static native boolean init(); - private synchronized native Proxy getSystemProxy(String protocol, String host); + private synchronized native Proxy[] getSystemProxies(String protocol, String host); /** * @return {@code true} if given this pattern for non-proxy hosts and this
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/native/libnet/proxy_util.c Thu Feb 02 10:28:47 2017 +0100 @@ -0,0 +1,101 @@ +/* + * Copyright (c) 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 + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "jni.h" +#include "jni_util.h" + +#include "proxy_util.h" + +jclass proxy_class; +jclass isaddr_class; +jclass ptype_class; +jmethodID isaddr_createUnresolvedID; +jmethodID proxy_ctrID; +jfieldID pr_no_proxyID; +jfieldID ptype_httpID; +jfieldID ptype_socksID; + +int initJavaClass(JNIEnv *env) { + jclass proxy_cls = NULL; + jclass ptype_cls = NULL; + jclass isaddr_cls = NULL; + + // Proxy initialization + proxy_cls = (*env)->FindClass(env,"java/net/Proxy"); + CHECK_NULL_RETURN(proxy_cls, 0); + proxy_class = (*env)->NewGlobalRef(env, proxy_cls); + CHECK_NULL_RETURN(proxy_class, 0); + proxy_ctrID = (*env)->GetMethodID(env, proxy_class, "<init>", + "(Ljava/net/Proxy$Type;Ljava/net/SocketAddress;)V"); + CHECK_NULL_RETURN(proxy_ctrID, 0); + + // Proxy$Type initialization + ptype_cls = (*env)->FindClass(env,"java/net/Proxy$Type"); + CHECK_NULL_RETURN(ptype_cls, 0); + ptype_class = (*env)->NewGlobalRef(env, ptype_cls); + CHECK_NULL_RETURN(ptype_class, 0); + ptype_httpID = (*env)->GetStaticFieldID(env, ptype_class, "HTTP", + "Ljava/net/Proxy$Type;"); + CHECK_NULL_RETURN(ptype_httpID, 0); + ptype_socksID = (*env)->GetStaticFieldID(env, ptype_class, "SOCKS", + "Ljava/net/Proxy$Type;"); + CHECK_NULL_RETURN(ptype_socksID, 0); + + // NO_PROXY + pr_no_proxyID = (*env)->GetStaticFieldID(env, proxy_class, "NO_PROXY", + "Ljava/net/Proxy;"); + CHECK_NULL_RETURN(pr_no_proxyID, 0); + + // InetSocketAddress initialization + isaddr_cls = (*env)->FindClass(env, "java/net/InetSocketAddress"); + CHECK_NULL_RETURN(isaddr_cls, 0); + isaddr_class = (*env)->NewGlobalRef(env, isaddr_cls); + CHECK_NULL_RETURN(isaddr_class, 0); + isaddr_createUnresolvedID = (*env)->GetStaticMethodID(env, isaddr_class, + "createUnresolved", + "(Ljava/lang/String;I)Ljava/net/InetSocketAddress;"); + + return isaddr_createUnresolvedID != NULL ? 1 : 0; +} + +jobject createProxy(JNIEnv *env, jfieldID ptype_ID, const char* phost, unsigned short pport) { + jobject jProxy = NULL; + jobject type_proxy = NULL; + type_proxy = (*env)->GetStaticObjectField(env, ptype_class, ptype_ID); + if (type_proxy) { + jstring jhost = NULL; + jhost = (*env)->NewStringUTF(env, phost); + if (jhost) { + jobject isa = NULL; + isa = (*env)->CallStaticObjectMethod(env, isaddr_class, + isaddr_createUnresolvedID, jhost, pport); + if (isa) { + jProxy = (*env)->NewObject(env, proxy_class, proxy_ctrID, + type_proxy, isa); + } + } + } + return jProxy; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/native/libnet/proxy_util.h Thu Feb 02 10:28:47 2017 +0100 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 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 + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +extern jclass proxy_class; +extern jclass isaddr_class; +extern jclass ptype_class; +extern jmethodID isaddr_createUnresolvedID; +extern jmethodID proxy_ctrID; +extern jfieldID pr_no_proxyID; +extern jfieldID ptype_httpID; +extern jfieldID ptype_socksID; + +int initJavaClass(JNIEnv *env); + +jobject createProxy(JNIEnv *env, jfieldID ptype_ID, const char* phost, unsigned short pport);
--- a/jdk/src/java.base/unix/native/libnet/DefaultProxySelector.c Wed Feb 01 23:33:39 2017 +0300 +++ b/jdk/src/java.base/unix/native/libnet/DefaultProxySelector.c Thu Feb 02 10:28:47 2017 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2013, 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 @@ -23,24 +23,20 @@ * questions. */ +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + #include "jni.h" #include "jni_util.h" #include "jvm.h" #include "jvm_md.h" -#include "jlong.h" + +#include "proxy_util.h" + #include "sun_net_spi_DefaultProxySelector.h" -#include <dlfcn.h> -#include <stdio.h> -#include <stdlib.h> -#if defined(__linux__) || defined(_ALLBSD_SOURCE) -#include <string.h> -#else -#include <strings.h> -#endif -#ifndef CHECK_NULL_RETURN -#define CHECK_NULL_RETURN(x, y) if ((x) == NULL) return y; -#endif /** * These functions are used by the sun.net.spi.DefaultProxySelector class @@ -112,43 +108,11 @@ static g_network_address_get_port_func* g_network_address_get_port = NULL; static g_strfreev_func* g_strfreev = NULL; - -static jclass proxy_class; -static jclass isaddr_class; -static jclass ptype_class; -static jmethodID isaddr_createUnresolvedID; -static jmethodID proxy_ctrID; -static jfieldID ptype_httpID; -static jfieldID ptype_socksID; - - static void* gconf_client = NULL; static int use_gproxyResolver = 0; static int use_gconf = 0; -static jobject createProxy(JNIEnv *env, jfieldID ptype_ID, - const char* phost, unsigned short pport) -{ - jobject jProxy = NULL; - jobject type_proxy = NULL; - type_proxy = (*env)->GetStaticObjectField(env, ptype_class, ptype_ID); - if (type_proxy) { - jstring jhost = NULL; - jhost = (*env)->NewStringUTF(env, phost); - if (jhost) { - jobject isa = NULL; - isa = (*env)->CallStaticObjectMethod(env, isaddr_class, - isaddr_createUnresolvedID, jhost, pport); - if (isa) { - jProxy = (*env)->NewObject(env, proxy_class, proxy_ctrID, - type_proxy, isa); - } - } - } - return jProxy; -} - static int initGConf() { /** * Let's try to load GConf-2 library @@ -196,18 +160,18 @@ return 0; } -static jobject getProxyByGConf(JNIEnv *env, const char* cproto, - const char* chost) +static jobjectArray getProxyByGConf(JNIEnv *env, const char* cproto, + const char* chost) { char *phost = NULL; char *mode = NULL; int pport = 0; int use_proxy = 0; int use_same_proxy = 0; - jobject proxy = NULL; + jobjectArray proxy_array = NULL; jfieldID ptype_ID = ptype_httpID; - // We only check manual proxy configurations + /* We only check manual proxy configurations */ mode = (*my_get_string_func)(gconf_client, "/system/proxy/mode", NULL); if (mode && !strcasecmp(mode, "manual")) { /* @@ -293,7 +257,7 @@ char *s; /** - * check for the exclude list (aka "No Proxy For" list). + * Check for the exclude list (aka "No Proxy For" list). * It's a list of comma separated suffixes (e.g. domain name). */ noproxyfor = (*my_get_string_func)(gconf_client, "/system/proxy/no_proxy_for", NULL); @@ -313,11 +277,25 @@ s = strtok_r(NULL, ", ", tmpbuf); } } - if (use_proxy) + if (use_proxy) { + jobject proxy = NULL; + /* create a proxy array with one element. */ + proxy_array = (*env)->NewObjectArray(env, 1, proxy_class, NULL); + if (proxy_array == NULL || (*env)->ExceptionCheck(env)) { + return NULL; + } proxy = createProxy(env, ptype_ID, phost, pport); + if (proxy == NULL || (*env)->ExceptionCheck(env)) { + return NULL; + } + (*env)->SetObjectArrayElement(env, proxy_array, 0, proxy); + if ((*env)->ExceptionCheck(env)) { + return NULL; + } + } } - return proxy; + return proxy_array; } static int initGProxyResolver() { @@ -371,8 +349,8 @@ return 1; } -static jobject getProxyByGProxyResolver(JNIEnv *env, const char* cproto, - const char* chost) +static jobjectArray getProxyByGProxyResolver(JNIEnv *env, const char *cproto, + const char *chost) { GProxyResolver* resolver = NULL; char** proxies = NULL; @@ -382,19 +360,19 @@ size_t hostLen = 0; char* uri = NULL; - jobject jProxy = NULL; + jobjectArray proxy_array = NULL; resolver = (*g_proxy_resolver_get_default)(); if (resolver == NULL) { return NULL; } - // Construct the uri, cproto + "://" + chost + /* Construct the uri, cproto + "://" + chost */ protoLen = strlen(cproto); hostLen = strlen(chost); uri = malloc(protoLen + hostLen + 4); if (!uri) { - // Out of memory + /* Out of memory */ return NULL; } memcpy(uri, cproto, protoLen); @@ -414,22 +392,56 @@ if (proxies) { if (!error) { int i; - for(i = 0; proxies[i] && !jProxy; i++) { - if (strcmp(proxies[i], "direct://")) { - GSocketConnectable* conn = - (*g_network_address_parse_uri)(proxies[i], 0, - &error); - if (conn && !error) { - const char* phost = NULL; - unsigned short pport = 0; - phost = (*g_network_address_get_hostname)(conn); - pport = (*g_network_address_get_port)(conn); - if (phost && pport > 0) { - jfieldID ptype_ID = ptype_httpID; - if (!strncmp(proxies[i], "socks", 5)) - ptype_ID = ptype_socksID; + int nr_proxies = 0; + char** p = proxies; + /* count the elements in the null terminated string vector. */ + while (*p) { + nr_proxies++; + p++; + } + /* create a proxy array that has to be filled. */ + proxy_array = (*env)->NewObjectArray(env, nr_proxies, proxy_class, NULL); + if (proxy_array != NULL && !(*env)->ExceptionCheck(env)) { + for (i = 0; proxies[i]; i++) { + if (strncmp(proxies[i], "direct://", 9)) { + GSocketConnectable* conn = + (*g_network_address_parse_uri)(proxies[i], 0, + &error); + if (conn && !error) { + const char *phost = NULL; + unsigned short pport = 0; + phost = (*g_network_address_get_hostname)(conn); + pport = (*g_network_address_get_port)(conn); + if (phost && pport > 0) { + jobject proxy = NULL; + jfieldID ptype_ID = ptype_httpID; + if (!strncmp(proxies[i], "socks", 5)) + ptype_ID = ptype_socksID; - jProxy = createProxy(env, ptype_ID, phost, pport); + proxy = createProxy(env, ptype_ID, phost, pport); + if (proxy == NULL || (*env)->ExceptionCheck(env)) { + proxy_array = NULL; + break; + } + (*env)->SetObjectArrayElement(env, proxy_array, i, proxy); + if ((*env)->ExceptionCheck(env)) { + proxy_array = NULL; + break; + } + } + } + } else { + /* direct connection - no proxy */ + jobject proxy = (*env)->GetStaticObjectField(env, proxy_class, + pr_no_proxyID); + if (proxy == NULL || (*env)->ExceptionCheck(env)) { + proxy_array = NULL; + break; + } + (*env)->SetObjectArrayElement(env, proxy_array, i, proxy); + if ((*env)->ExceptionCheck(env)) { + proxy_array = NULL; + break; } } } @@ -438,48 +450,9 @@ (*g_strfreev)(proxies); } - return jProxy; + return proxy_array; } -static int initJavaClass(JNIEnv *env) { - jclass proxy_cls = NULL; - jclass ptype_cls = NULL; - jclass isaddr_cls = NULL; - - // Proxy initialization - proxy_cls = (*env)->FindClass(env,"java/net/Proxy"); - CHECK_NULL_RETURN(proxy_cls, 0); - proxy_class = (*env)->NewGlobalRef(env, proxy_cls); - CHECK_NULL_RETURN(proxy_class, 0); - proxy_ctrID = (*env)->GetMethodID(env, proxy_class, "<init>", - "(Ljava/net/Proxy$Type;Ljava/net/SocketAddress;)V"); - CHECK_NULL_RETURN(proxy_ctrID, 0); - - // Proxy$Type initialization - ptype_cls = (*env)->FindClass(env,"java/net/Proxy$Type"); - CHECK_NULL_RETURN(ptype_cls, 0); - ptype_class = (*env)->NewGlobalRef(env, ptype_cls); - CHECK_NULL_RETURN(ptype_class, 0); - ptype_httpID = (*env)->GetStaticFieldID(env, ptype_class, "HTTP", - "Ljava/net/Proxy$Type;"); - CHECK_NULL_RETURN(ptype_httpID, 0); - ptype_socksID = (*env)->GetStaticFieldID(env, ptype_class, "SOCKS", - "Ljava/net/Proxy$Type;"); - CHECK_NULL_RETURN(ptype_socksID, 0); - - // InetSocketAddress initialization - isaddr_cls = (*env)->FindClass(env, "java/net/InetSocketAddress"); - CHECK_NULL_RETURN(isaddr_cls, 0); - isaddr_class = (*env)->NewGlobalRef(env, isaddr_cls); - CHECK_NULL_RETURN(isaddr_class, 0); - isaddr_createUnresolvedID = (*env)->GetStaticMethodID(env, isaddr_class, - "createUnresolved", - "(Ljava/lang/String;I)Ljava/net/InetSocketAddress;"); - - return isaddr_createUnresolvedID != NULL ? 1 : 0; -} - - /* * Class: sun_net_spi_DefaultProxySelector * Method: init @@ -500,14 +473,14 @@ /* * Class: sun_net_spi_DefaultProxySelector - * Method: getSystemProxy - * Signature: ([Ljava/lang/String;Ljava/lang/String;)Ljava/net/Proxy; + * Method: getSystemProxies + * Signature: ([Ljava/lang/String;Ljava/lang/String;)[Ljava/net/Proxy; */ -JNIEXPORT jobject JNICALL -Java_sun_net_spi_DefaultProxySelector_getSystemProxy(JNIEnv *env, - jobject this, - jstring proto, - jstring host) +JNIEXPORT jobjectArray JNICALL +Java_sun_net_spi_DefaultProxySelector_getSystemProxies(JNIEnv *env, + jobject this, + jstring proto, + jstring host) { const char* cproto; const char* chost; @@ -515,7 +488,7 @@ jboolean isProtoCopy; jboolean isHostCopy; - jobject proxy = NULL; + jobjectArray proxyArray = NULL; cproto = (*env)->GetStringUTFChars(env, proto, &isProtoCopy); @@ -523,16 +496,15 @@ chost = (*env)->GetStringUTFChars(env, host, &isHostCopy); if (chost != NULL) { if (use_gproxyResolver) - proxy = getProxyByGProxyResolver(env, cproto, chost); + proxyArray = getProxyByGProxyResolver(env, cproto, chost); else if (use_gconf) - proxy = getProxyByGConf(env, cproto, chost); - + proxyArray = getProxyByGConf(env, cproto, chost); if (isHostCopy == JNI_TRUE) (*env)->ReleaseStringUTFChars(env, host, chost); } if (isProtoCopy == JNI_TRUE) (*env)->ReleaseStringUTFChars(env, proto, cproto); } - return proxy; + return proxyArray; }
--- a/jdk/src/java.base/windows/native/libnet/DefaultProxySelector.c Wed Feb 01 23:33:39 2017 +0300 +++ b/jdk/src/java.base/windows/native/libnet/DefaultProxySelector.c Thu Feb 02 10:28:47 2017 +0100 @@ -1,5 +1,5 @@ /* - * 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 @@ -24,26 +24,24 @@ */ #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. + * On Windows use 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 @@ -52,233 +50,327 @@ */ 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); + /* + * Get one WinHTTP session handle to initialize the WinHTTP internal data + * structures. Keep and use only this one for the whole life time. + */ + 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; + } - /** - * 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. - */ + if (!initJavaClass(env)) { + return JNI_FALSE; + } + return JNI_TRUE; - } +} - return JNI_FALSE; -} #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; + } +} + + +/* + * Creates a linked list of list_item elements that has to be freed later on. + * Returns the size of the array as int. + */ +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>]) + */ + 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; + } + 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 */ + } + } + /* goto next proxy if available... */ + current_proxy = wcstok_s(NULL, separators, &context); + } + return nr_elems; +} + + + /* * Class: sun_net_spi_DefaultProxySelector - * Method: getSystemProxy - * Signature: ([Ljava/lang/String;Ljava/lang/String;)Ljava/net/Proxy; + * Method: getSystemProxies + * Signature: ([Ljava/lang/String;Ljava/lang/String;)[Ljava/net/Proxy; */ -JNIEXPORT jobject JNICALL -Java_sun_net_spi_DefaultProxySelector_getSystemProxy(JNIEnv *env, - jobject this, - jstring proto, - jstring host) +JNIEXPORT jobjectArray JNICALL +Java_sun_net_spi_DefaultProxySelector_getSystemProxies(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; + jobjectArray proxy_array = NULL; + jobject type_proxy = NULL; + LPCWSTR lpProto; + LPCWSTR lpHost; + list_item *head = NULL; - /** - * 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); + 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; - /** - * Let's see if the proxy settings are to be used. - */ - 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; - } - 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; + 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; + } + + lpProto = (*env)->GetStringChars(env, proto, NULL); + if (lpProto == NULL) { + (*env)->ReleaseStringChars(env, host, lpHost); + if (!(*env)->ExceptionCheck(env)) + JNU_ThrowOutOfMemoryError(env, 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) { + /* + * 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. + */ + 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, use a + * direct connection. + */ + goto noproxy; } - s = strtok_s(NULL, "; ", &ctx); - } - if (isCopy == JNI_TRUE) - (*env)->ReleaseStringUTFChars(env, host, urlhost); + if (wcsncmp(s, L"<local>", maxlen) == 0) { + /* + * All local intranet sites are bypassed - Microsoft consider 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; + + /* Set the default port value & proxy type from protocol. */ + 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 { + type_proxy = (*env)->GetStaticObjectField(env, ptype_class, ptype_httpID); + } + if (type_proxy == NULL || (*env)->ExceptionCheck(env)) { + goto noproxy; } - cproto = (*env)->GetStringUTFChars(env, proto, &isCopy); - if (cproto == NULL) { - if (!(*env)->ExceptionCheck(env)) - JNU_ThrowOutOfMemoryError(env, NULL); - return NULL; + 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; + } } - - /* - * Set default port value & proxy type from protocol. - */ - 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); - } - - sprintf(pproto,"%s=", cproto); - if (isCopy == JNI_TRUE) - (*env)->ReleaseStringUTFChars(env, proto, cproto); - /** - * Let's check the protocol specific form first. - */ - if ((s = strstr(regserver, pproto)) != NULL) { - s += strlen(pproto); - } 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; - } - 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); - } - 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; - } - } - } 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; }
--- a/jdk/test/java/net/ProxySelector/SystemProxies.java Wed Feb 01 23:33:39 2017 +0300 +++ b/jdk/test/java/net/ProxySelector/SystemProxies.java Thu Feb 02 10:28:47 2017 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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 @@ -22,9 +22,13 @@ */ /* - * This is a manual test to determine the proxies set on the system for various - * protocols. See bug 6912868. + * @test + * @bug 6912868 8170868 + * @summary Basic test to provide some coverage of system proxy code. Will + * always pass. Should be run manually for specific systems to inspect output. + * @run main/othervm -Djava.net.useSystemProxies=true SystemProxies */ + import java.net.Proxy; import java.net.ProxySelector; import java.net.URI;