8016521: InetAddress should not always re-order addresses returned from name service
Reviewed-by: chegar
--- a/jdk/src/java.base/share/classes/java/net/Inet6AddressImpl.java Tue May 24 10:14:41 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/net/Inet6AddressImpl.java Tue May 24 20:15:18 2016 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2016, 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,7 +23,10 @@
* questions.
*/
package java.net;
+
import java.io.IOException;
+import static java.net.InetAddress.PREFER_IPV6_VALUE;
+import static java.net.InetAddress.PREFER_SYSTEM_VALUE;
/*
* Package private implementation of InetAddressImpl for dual
@@ -35,15 +38,23 @@
*
* @since 1.4
*/
+class Inet6AddressImpl implements InetAddressImpl {
-class Inet6AddressImpl implements InetAddressImpl {
public native String getLocalHostName() throws UnknownHostException;
- public native InetAddress[]
- lookupAllHostAddr(String hostname) throws UnknownHostException;
+
+ public native InetAddress[] lookupAllHostAddr(String hostname)
+ throws UnknownHostException;
+
public native String getHostByAddr(byte[] addr) throws UnknownHostException;
- private native boolean isReachable0(byte[] addr, int scope, int timeout, byte[] inf, int ttl, int if_scope) throws IOException;
+
+ private native boolean isReachable0(byte[] addr, int scope, int timeout,
+ byte[] inf, int ttl, int if_scope)
+ throws IOException;
- public boolean isReachable(InetAddress addr, int timeout, NetworkInterface netif, int ttl) throws IOException {
+ public boolean isReachable(InetAddress addr, int timeout,
+ NetworkInterface netif, int ttl)
+ throws IOException
+ {
byte[] ifaddr = null;
int scope = -1;
int netif_scope = -1;
@@ -79,7 +90,8 @@
public synchronized InetAddress anyLocalAddress() {
if (anyLocalAddress == null) {
- if (InetAddress.preferIPv6Address) {
+ if (InetAddress.preferIPv6Address == PREFER_IPV6_VALUE ||
+ InetAddress.preferIPv6Address == PREFER_SYSTEM_VALUE) {
anyLocalAddress = new Inet6Address();
anyLocalAddress.holder().hostName = "::";
} else {
@@ -91,7 +103,8 @@
public synchronized InetAddress loopbackAddress() {
if (loopbackAddress == null) {
- if (InetAddress.preferIPv6Address) {
+ if (InetAddress.preferIPv6Address == PREFER_IPV6_VALUE ||
+ InetAddress.preferIPv6Address == PREFER_SYSTEM_VALUE) {
byte[] loopback =
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
@@ -103,6 +116,6 @@
return loopbackAddress;
}
- private InetAddress anyLocalAddress;
- private InetAddress loopbackAddress;
+ private InetAddress anyLocalAddress;
+ private InetAddress loopbackAddress;
}
--- a/jdk/src/java.base/share/classes/java/net/InetAddress.java Tue May 24 10:14:41 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/net/InetAddress.java Tue May 24 20:15:18 2016 +0100
@@ -26,8 +26,6 @@
package java.net;
import java.util.NavigableSet;
-import java.util.Iterator;
-import java.util.List;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Scanner;
@@ -41,6 +39,7 @@
import java.io.ObjectInputStream.GetField;
import java.io.ObjectOutputStream;
import java.io.ObjectOutputStream.PutField;
+import java.lang.annotation.Native;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListSet;
@@ -193,6 +192,11 @@
*/
public
class InetAddress implements java.io.Serializable {
+
+ @Native static final int PREFER_IPV4_VALUE = 0;
+ @Native static final int PREFER_IPV6_VALUE = 1;
+ @Native static final int PREFER_SYSTEM_VALUE = 2;
+
/**
* Specify the address family: Internet Protocol, Version 4
* @since 1.4
@@ -206,8 +210,7 @@
static final int IPv6 = 2;
/* Specify address family preference */
- static transient boolean preferIPv6Address = false;
-
+ static transient final int preferIPv6Address;
static class InetAddressHolder {
/**
@@ -293,8 +296,19 @@
* Load net library into runtime, and perform initializations.
*/
static {
- preferIPv6Address = java.security.AccessController.doPrivileged(
- new GetBooleanAction("java.net.preferIPv6Addresses")).booleanValue();
+ String str = java.security.AccessController.doPrivileged(
+ new GetPropertyAction("java.net.preferIPv6Addresses"));
+ if (str == null) {
+ preferIPv6Address = PREFER_IPV4_VALUE;
+ } else if (str.equalsIgnoreCase("true")) {
+ preferIPv6Address = PREFER_IPV6_VALUE;
+ } else if (str.equalsIgnoreCase("false")) {
+ preferIPv6Address = PREFER_IPV4_VALUE;
+ } else if (str.equalsIgnoreCase("system")) {
+ preferIPv6Address = PREFER_SYSTEM_VALUE;
+ } else {
+ preferIPv6Address = PREFER_IPV4_VALUE;
+ }
AccessController.doPrivileged(
new java.security.PrivilegedAction<>() {
public Void run() {
--- a/jdk/src/java.base/share/classes/java/net/doc-files/net-properties.html Tue May 24 10:14:41 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/net/doc-files/net-properties.html Tue May 24 20:15:18 2016 +0100
@@ -1,5 +1,5 @@
<!--
- Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 1998, 2016, 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
@@ -35,7 +35,7 @@
java.net package. Some are checked only once at startup of the VM,
and therefore are best set using the -D option of the java command,
while others have a more dynamic nature and can also be changed using
-the <a href="../../lang/System.html#setProperty(java.lang.String,%20java.lang.String)">System.setProperty()</a> API.
+the <a href="../../lang/System.html#setProperty(java.lang.String,%20java.lang.String)">System.setProperty()</a> API.
The purpose of this document is to list
and detail all of these properties.</P>
<P>If there is no special note, a property value is checked every time it is used.</P>
@@ -58,7 +58,8 @@
applications that depend on the representation of an IPv4 address
(e.g. 192.168.1.1). This property can be set to <B>true</B> to
change that preference and use IPv6 addresses over IPv4 ones where
- possible.</P>
+ possible, or <B>system</B> to preserve the order of the addresses as
+ returned by the operating system.</P>
</UL>
<P>Both of these properties are checked only once, at startup.</P>
<a name="Proxies"></a>
@@ -73,7 +74,7 @@
<P>The following proxy settings are used by the HTTP protocol handler.</P>
<UL>
<LI><P><B>http.proxyHost</B> (default: <none>)<BR>
- The hostname, or address, of the proxy server
+ The hostname, or address, of the proxy server
</P>
<LI><P><B>http.proxyPort</B> (default: 80)<BR>
The port number of the proxy server.</P>
@@ -94,7 +95,7 @@
<P>The following proxy settings are used by the HTTPS protocol handler.</P>
<UL>
<LI><P><B>https.proxyHost</B>(default: <none>)<BR>
- The hostname, or address, of the proxy server
+ The hostname, or address, of the proxy server
</P>
<LI><P><B>https.proxyPort</B> (default: 443)<BR>
The port number of the proxy server.</P>
@@ -105,7 +106,7 @@
<P>The following proxy settings are used by the FTP protocol handler.</P>
<UL>
<LI><P><B>ftp.proxyHost</B>(default: <none>)<BR>
- The hostname, or address, of the proxy server
+ The hostname, or address, of the proxy server
</P>
<LI><P><B>ftp.proxyPort</B> (default: 80)<BR>
The port number of the proxy server.</P>
@@ -160,7 +161,7 @@
<LI><P><B>http.agent</B> (default: “Java/<version>”)<BR>
Defines the string sent in the User-Agent request header in http
requests. Note that the string “Java/<version>” will
- be appended to the one provided in the property (e.g. if
+ be appended to the one provided in the property (e.g. if
-Dhttp.agent=”foobar” is used, the User-Agent header will
contain “foobar Java/1.5.0” if the version of the VM is
1.5.0). This property is checked only once at startup.</P>
--- a/jdk/src/java.base/share/native/libnet/InetAddress.c Tue May 24 10:14:41 2016 -0700
+++ b/jdk/src/java.base/share/native/libnet/InetAddress.c Tue May 24 20:15:18 2016 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2016, 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
@@ -61,7 +61,7 @@
CHECK_NULL(iac_class);
ia_holderID = (*env)->GetFieldID(env, ia_class, "holder", "Ljava/net/InetAddress$InetAddressHolder;");
CHECK_NULL(ia_holderID);
- ia_preferIPv6AddressID = (*env)->GetStaticFieldID(env, ia_class, "preferIPv6Address", "Z");
+ ia_preferIPv6AddressID = (*env)->GetStaticFieldID(env, ia_class, "preferIPv6Address", "I");
CHECK_NULL(ia_preferIPv6AddressID);
iac_addressID = (*env)->GetFieldID(env, iac_class, "address", "I");
--- a/jdk/src/java.base/unix/native/libnet/Inet6AddressImpl.c Tue May 24 10:14:41 2016 -0700
+++ b/jdk/src/java.base/unix/native/libnet/Inet6AddressImpl.c Tue May 24 20:15:18 2016 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, 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
@@ -48,6 +48,7 @@
#include "java_net_Inet4AddressImpl.h"
#include "java_net_Inet6AddressImpl.h"
+#include "java_net_InetAddress.h"
/* the initial size of our hostent buffers */
#ifndef NI_MAXHOST
@@ -312,8 +313,8 @@
JNU_ReleaseStringPlatformChars(env, host, hostname);
return NULL;
} else {
- int i = 0;
- int inetCount = 0, inet6Count = 0, inetIndex, inet6Index;
+ int i = 0, addressPreference = -1;
+ int inetCount = 0, inet6Count = 0, inetIndex = 0, inet6Index = 0, originalIndex = 0;
struct addrinfo *itr, *last = NULL, *iterator = res;
while (iterator != NULL) {
int skip = 0;
@@ -394,14 +395,18 @@
goto cleanupAndReturn;
}
- if ((*env)->GetStaticBooleanField(env, ia_class, ia_preferIPv6AddressID)) {
+ addressPreference = (*env)->GetStaticIntField(env, ia_class, ia_preferIPv6AddressID);
+
+ if (addressPreference == java_net_InetAddress_PREFER_IPV6_VALUE) {
/* AF_INET addresses will be offset by inet6Count */
inetIndex = inet6Count;
inet6Index = 0;
- } else {
+ } else if (addressPreference == java_net_InetAddress_PREFER_IPV4_VALUE) {
/* AF_INET6 addresses will be offset by inetCount */
inetIndex = 0;
inet6Index = inetCount;
+ } else if (addressPreference == java_net_InetAddress_PREFER_SYSTEM_VALUE) {
+ inetIndex = inet6Index = originalIndex = 0;
}
while (iterator != NULL) {
@@ -414,7 +419,7 @@
}
setInetAddress_addr(env, iaObj, ntohl(((struct sockaddr_in*)iterator->ai_addr)->sin_addr.s_addr));
setInetAddress_hostName(env, iaObj, host);
- (*env)->SetObjectArrayElement(env, ret, inetIndex, iaObj);
+ (*env)->SetObjectArrayElement(env, ret, (inetIndex | originalIndex), iaObj);
inetIndex++;
} else if (iterator->ai_family == AF_INET6) {
jint scope = 0;
@@ -435,9 +440,13 @@
setInet6Address_scopeid(env, iaObj, scope);
}
setInetAddress_hostName(env, iaObj, host);
- (*env)->SetObjectArrayElement(env, ret, inet6Index, iaObj);
+ (*env)->SetObjectArrayElement(env, ret, (inet6Index | originalIndex), iaObj);
inet6Index++;
}
+ if (addressPreference == java_net_InetAddress_PREFER_SYSTEM_VALUE) {
+ originalIndex++;
+ inetIndex = inet6Index = 0;
+ }
iterator = iterator->ai_next;
}
}
--- a/jdk/src/java.base/windows/native/libnet/Inet6AddressImpl.c Tue May 24 10:14:41 2016 -0700
+++ b/jdk/src/java.base/windows/native/libnet/Inet6AddressImpl.c Tue May 24 20:15:18 2016 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, 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 @@
/* get the address preference */
preferIPv6Address
- = (*env)->GetStaticBooleanField(env, ia_class, ia_preferIPv6AddressID);
+ = (*env)->GetStaticIntField(env, ia_class, ia_preferIPv6AddressID);
/* Try once, with our static buffer. */
memset(&hints, 0, sizeof(hints));
@@ -122,7 +122,7 @@
}
} else {
int i = 0;
- int inetCount = 0, inet6Count = 0, inetIndex, inet6Index;
+ int inetCount = 0, inet6Count = 0, inetIndex = 0, inet6Index = 0, originalIndex = 0;
struct addrinfo *itr, *last, *iterator = res;
while (iterator != NULL) {
int skip = 0;
@@ -203,12 +203,14 @@
goto cleanupAndReturn;
}
- if (preferIPv6Address) {
+ if (preferIPv6Address == java_net_InetAddress_PREFER_IPV6_VALUE) {
inetIndex = inet6Count;
inet6Index = 0;
- } else {
+ } else if (preferIPv6Address == java_net_InetAddress_PREFER_IPV4_VALUE) {
inetIndex = 0;
inet6Index = inetCount;
+ } else if (preferIPv6Address == java_net_InetAddress_PREFER_SYSTEM_VALUE) {
+ inetIndex = inet6Index = originalIndex = 0;
}
while (iterator != NULL) {
@@ -220,7 +222,7 @@
}
setInetAddress_addr(env, iaObj, ntohl(((struct sockaddr_in*)iterator->ai_addr)->sin_addr.s_addr));
setInetAddress_hostName(env, iaObj, host);
- (*env)->SetObjectArrayElement(env, ret, inetIndex, iaObj);
+ (*env)->SetObjectArrayElement(env, ret, (inetIndex | originalIndex), iaObj);
inetIndex ++;
} else if (iterator->ai_family == AF_INET6) {
jint scope = 0;
@@ -240,9 +242,13 @@
setInet6Address_scopeid(env, iaObj, scope);
}
setInetAddress_hostName(env, iaObj, host);
- (*env)->SetObjectArrayElement(env, ret, inet6Index, iaObj);
+ (*env)->SetObjectArrayElement(env, ret, (inet6Index | originalIndex), iaObj);
inet6Index ++;
}
+ if (preferIPv6Address == java_net_InetAddress_PREFER_SYSTEM_VALUE) {
+ originalIndex++;
+ inetIndex = inet6Index = 0;
+ }
iterator = iterator->ai_next;
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/Inet6Address/PreferIPv6AddressesTest.java Tue May 24 20:15:18 2016 +0100
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2016, 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.
+ *
+ * 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.
+ */
+
+/**
+ * @test
+ * @bug 8016521
+ * @summary InetAddress should not always re-order addresses returned from name
+ * service
+ * @run main/othervm -Djava.net.preferIPv6Addresses=false PreferIPv6AddressesTest
+ * @run main/othervm -Djava.net.preferIPv6Addresses=true PreferIPv6AddressesTest
+ * @run main/othervm -Djava.net.preferIPv6Addresses=system PreferIPv6AddressesTest
+ * @run main/othervm PreferIPv6AddressesTest
+ */
+
+import java.io.IOException;
+import java.net.*;
+import java.nio.channels.DatagramChannel;
+import java.util.Arrays;
+import java.util.stream.IntStream;
+import static java.lang.System.out;
+
+public class PreferIPv6AddressesTest {
+
+ // A name, that if resolves, returns both IPv4 and IPv6 addresses.
+ static final String HOST_NAME = "www.google.com";
+
+ static final InetAddress LOOPBACK = InetAddress.getLoopbackAddress();
+
+ static final String preferIPV6Address =
+ System.getProperty("java.net.preferIPv6Addresses", "false");
+
+ public static void main(String args[]) throws IOException {
+
+ InetAddress addrs[];
+ try {
+ addrs = InetAddress.getAllByName(HOST_NAME);
+ } catch (UnknownHostException e) {
+ out.println("Unknown host " + HOST_NAME + ", cannot run test.");
+ return;
+ }
+
+ int firstIPv4Address = IntStream.range(0, addrs.length)
+ .filter(x -> addrs[x] instanceof Inet4Address)
+ .findFirst().orElse(-1);
+ int firstIPv6Address = IntStream.range(0, addrs.length)
+ .filter(x -> addrs[x] instanceof Inet6Address)
+ .findFirst().orElse(-1);
+
+ out.println("IPv6 supported: " + IPv6Supported());
+ out.println("Addresses: " + Arrays.asList(addrs));
+
+ if (preferIPV6Address.equalsIgnoreCase("true") && firstIPv6Address != -1) {
+ int off = firstIPv4Address != -1 ? firstIPv4Address : addrs.length;
+ assertAllv6Addresses(addrs, 0, off);
+ assertAllv4Addresses(addrs, off, addrs.length);
+ assertLoopbackAddress(Inet6Address.class);
+ assertAnyLocalAddress(Inet6Address.class);
+ } else if (preferIPV6Address.equalsIgnoreCase("false") && firstIPv4Address != -1) {
+ int off = firstIPv6Address != -1 ? firstIPv6Address : addrs.length;
+ assertAllv4Addresses(addrs, 0, off);
+ assertAllv6Addresses(addrs, off, addrs.length);
+ assertLoopbackAddress(Inet4Address.class);
+ assertAnyLocalAddress(Inet4Address.class);
+ } else if (preferIPV6Address.equalsIgnoreCase("system") && IPv6Supported()) {
+ assertLoopbackAddress(Inet6Address.class);
+ assertAnyLocalAddress(Inet6Address.class);
+ } else if (preferIPV6Address.equalsIgnoreCase("system") && !IPv6Supported()) {
+ assertLoopbackAddress(Inet4Address.class);
+ assertAnyLocalAddress(Inet4Address.class);
+ }
+ }
+
+ static void assertAllv4Addresses(InetAddress[] addrs, int off, int len) {
+ IntStream.range(off, len)
+ .mapToObj(x -> addrs[x])
+ .forEach(x -> {
+ if (!(x instanceof Inet4Address))
+ throw new RuntimeException("Expected IPv4, got " + x);
+ });
+ }
+
+ static void assertAllv6Addresses(InetAddress[] addrs, int off, int len) {
+ IntStream.range(off, len)
+ .mapToObj(x -> addrs[x])
+ .forEach(x -> {
+ if (!(x instanceof Inet6Address))
+ throw new RuntimeException("Expected IPv6, got " + x);
+ });
+ }
+
+ static void assertLoopbackAddress(Class<?> expectedType) {
+ if (!LOOPBACK.getClass().isAssignableFrom(expectedType))
+ throw new RuntimeException("Expected " + expectedType
+ + ", got " + LOOPBACK.getClass());
+ }
+
+ static void assertAnyLocalAddress(Class<?> expectedType) {
+ InetAddress anyAddr = (new InetSocketAddress(0)).getAddress();
+ if (!anyAddr.getClass().isAssignableFrom(expectedType))
+ throw new RuntimeException("Expected " + expectedType
+ + ", got " + anyAddr.getClass());
+ }
+
+ static boolean IPv6Supported() throws IOException {
+ try {
+ DatagramChannel.open(StandardProtocolFamily.INET6);
+ return true;
+ } catch (UnsupportedOperationException x) {
+ return false;
+ }
+ }
+}