# HG changeset patch # User zeller # Date 1485851254 -3600 # Tue Jan 31 09:27:34 2017 +0100 # Node ID b9c9c24923f6c4ce82bc70e170b6a5e158139a31 # Parent 9098b2b9d997d65af0026fc2f39cf75234e26bc5 8170868: DefaultProxySelector should use system defaults on Windows, MacOS and Gnome Contributed-by: arno.zeller@sap.com diff --git a/make/lib/NetworkingLibraries.gmk b/make/lib/NetworkingLibraries.gmk --- a/make/lib/NetworkingLibraries.gmk +++ b/make/lib/NetworkingLibraries.gmk @@ -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 @@ -25,11 +25,16 @@ LIBNET_SRC_DIRS := $(call FindSrcDirsForLib, java.base, net) +ifeq ($(OPENJDK_TARGET_OS), macosx) + LIBNET_EXCLUDE_FILES := $(JDK_TOPDIR)/src/java.base/unix/native/libnet/DefaultProxySelector.c +endif + $(eval $(call SetupNativeCompilation,BUILD_LIBNET, \ LIBRARY := net, \ OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \ SRC := $(LIBNET_SRC_DIRS), \ OPTIMIZATION := LOW, \ + EXCLUDE_FILES := $(LIBNET_EXCLUDE_FILES), \ CFLAGS := $(CFLAGS_JDKLIB) -I$(SUPPORT_OUTPUTDIR)/headers/java.base \ $(LIBJAVA_HEADER_FLAGS) $(addprefix -I, $(LIBNET_SRC_DIRS)), \ DISABLED_WARNINGS_gcc := format-nonliteral, \ @@ -44,8 +49,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" \ diff --git a/make/mapfiles/libnet/mapfile-vers b/make/mapfiles/libnet/mapfile-vers --- a/make/mapfiles/libnet/mapfile-vers +++ b/make/mapfiles/libnet/mapfile-vers @@ -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; diff --git a/src/java.base/macosx/native/libnet/DefaultProxySelector.c b/src/java.base/macosx/native/libnet/DefaultProxySelector.c new file mode 100644 --- /dev/null +++ b/src/java.base/macosx/native/libnet/DefaultProxySelector.c @@ -0,0 +1,271 @@ +/* + * 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 +#include +#include + +#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()); +} + +/* + * This will resolve all PAC URLs in the given array and return an array containing no more + * PAC URLs. + */ +static CFArrayRef createExpandedProxiesArray(CFArrayRef proxies, CFURLRef url) { + + CFIndex count; + CFIndex index; + CFMutableArrayRef expandedProxiesArray; + + expandedProxiesArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + assert(expandedProxiesArray != 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); + assert(currentProxy != NULL); + proxyType = (CFStringRef) CFDictionaryGetValue(currentProxy, kCFProxyTypeKey); + assert(proxyType != NULL); + + if (!CFEqual(proxyType, kCFProxyTypeAutoConfigurationURL)) { + /* This is a non PAC entry - just copy it to the new array. */ + CFArrayAppendValue(expandedProxiesArray, currentProxy); + } else { + /* This is a PAC URL and we have to resolve it. */ + CFRunLoopSourceRef runLoop; + CFURLRef scriptURL; + CFTypeRef result = NULL; + CFStreamClientContext context = { 0, &result, NULL, NULL, NULL }; + + 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 . + */ + CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoop, kResolveProxyRunLoopMode); + CFRunLoopRunInMode(kResolveProxyRunLoopMode, 1.0e10, false); + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runLoop, kResolveProxyRunLoopMode); + + /* + * Once the runloop returns, we should have 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 does + * contain no more PAC URIs. + */ + 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); + + 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 we should use no proxy - therefor we just create a + * NO_PROXY object. + */ + proxy = (*env)->GetStaticObjectField(env, proxy_class, pr_no_proxyID); + } else { + /* + * Create a proxy object for this entry. We have to 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; +} diff --git a/src/java.base/share/classes/java/net/doc-files/net-properties.html b/src/java.base/share/classes/java/net/doc-files/net-properties.html --- a/src/java.base/share/classes/java/net/doc-files/net-properties.html +++ b/src/java.base/share/classes/java/net/doc-files/net-properties.html @@ -149,7 +149,7 @@ the user.name property will be used with no password.

  • java.net.useSystemProxies (default: false)
    - On recent Windows systems and on Gnome 2.x systems it is possible to + On Windows systems, macOS systems and on Gnome 2.x systems it is possible to tell the java.net stack, setting this property to true, to use the system proxy settings (both these systems let you set proxies globally through their user interface). Note that this property is diff --git a/src/java.base/share/classes/sun/net/spi/DefaultProxySelector.java b/src/java.base/share/classes/sun/net/spi/DefaultProxySelector.java --- a/src/java.base/share/classes/sun/net/spi/DefaultProxySelector.java +++ b/src/java.base/share/classes/sun/net/spi/DefaultProxySelector.java @@ -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,7 +30,7 @@ import java.net.ProxySelector; import java.net.SocketAddress; import java.net.URI; -import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.io.IOException; import java.security.AccessController; @@ -87,6 +87,8 @@ private static boolean hasSystemProxies = false; + private static final List NO_PROXY_LIST = List.of(Proxy.NO_PROXY); + static { final String key = "java.net.useSystemProxies"; Boolean b = AccessController.doPrivileged( @@ -149,8 +151,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 select(URI uri) { if (uri == null) { @@ -185,7 +188,6 @@ if (protocol == null || host == null) { throw new IllegalArgumentException("protocol = "+protocol+" host = "+host); } - List proxyl = new ArrayList(1); NonProxyInfo pinfo = null; @@ -214,9 +216,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() { - public Proxy run() { + Proxy[] proxyArray = AccessController.doPrivileged( + new PrivilegedAction() { + public Proxy[] run() { int i, j; String phost = null; int pport = 0; @@ -239,8 +241,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 +250,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 +280,7 @@ } } if (shouldNotUseProxyFor(nprop.pattern, urlhost)) { - return Proxy.NO_PROXY; + return null; } } } @@ -311,22 +310,17 @@ 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 no specific proxy was found we return our standard list containing + // only one NO_PROXY entry. + return proxyArray == null ? NO_PROXY_LIST : List.of(proxyArray); } public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { @@ -354,7 +348,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 diff --git a/src/java.base/share/native/libnet/proxy_util.c b/src/java.base/share/native/libnet/proxy_util.c new file mode 100644 --- /dev/null +++ b/src/java.base/share/native/libnet/proxy_util.c @@ -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, "", + "(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; +} diff --git a/src/java.base/share/native/libnet/proxy_util.h b/src/java.base/share/native/libnet/proxy_util.h new file mode 100644 --- /dev/null +++ b/src/java.base/share/native/libnet/proxy_util.h @@ -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); diff --git a/src/java.base/unix/native/libnet/DefaultProxySelector.c b/src/java.base/unix/native/libnet/DefaultProxySelector.c --- a/src/java.base/unix/native/libnet/DefaultProxySelector.c +++ b/src/java.base/unix/native/libnet/DefaultProxySelector.c @@ -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 +#include +#include +#include + #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 -#include -#include -#if defined(__linux__) || defined(_ALLBSD_SOURCE) -#include -#else -#include -#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, "", - "(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; } diff --git a/src/java.base/windows/native/libnet/DefaultProxySelector.c b/src/java.base/windows/native/libnet/DefaultProxySelector.c --- a/src/java.base/windows/native/libnet/DefaultProxySelector.c +++ b/src/java.base/windows/native/libnet/DefaultProxySelector.c @@ -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 +#include + #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 @@ -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, "", - "(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 + * We 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. + * ([=]["://"][":"]) + */ + 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; - */ - 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 "" 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, we have to use a direct + * connection. + */ + goto noproxy; } - s = strtok_s(NULL, "; ", &ctx); - } - if (isCopy == JNI_TRUE) - (*env)->ReleaseStringUTFChars(env, host, urlhost); + if (wcsncmp(s, L"", 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; + + /** + * Set 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; } diff --git a/test/java/net/ProxySelector/SystemProxies.java b/test/java/net/ProxySelector/SystemProxies.java --- a/test/java/net/ProxySelector/SystemProxies.java +++ b/test/java/net/ProxySelector/SystemProxies.java @@ -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;