8016521: InetAddress should not always re-order addresses returned from name service

Reviewed-by: chegar
This commit is contained in:
Vyom Tewari 2016-05-24 20:15:18 +01:00
parent 27a77176a6
commit af7a591d39
7 changed files with 216 additions and 41 deletions

View File

@ -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 @@ import java.io.IOException;
*
* @since 1.4
*/
class Inet6AddressImpl implements InetAddressImpl {
public native String getLocalHostName() 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;
public boolean isReachable(InetAddress addr, int timeout, NetworkInterface netif, int ttl) throws IOException {
public native String getLocalHostName() 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;
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 @@ class Inet6AddressImpl implements InetAddressImpl {
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 @@ class Inet6AddressImpl implements InetAddressImpl {
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 @@ class Inet6AddressImpl implements InetAddressImpl {
return loopbackAddress;
}
private InetAddress anyLocalAddress;
private InetAddress loopbackAddress;
private InetAddress anyLocalAddress;
private InetAddress loopbackAddress;
}

View File

@ -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;
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 @@ import sun.net.util.IPAddressUtil;
*/
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 @@ class InetAddress implements java.io.Serializable {
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 @@ class InetAddress implements java.io.Serializable {
* 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() {

View File

@ -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 @@ alter the mechanisms and behavior of the various classes of the
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 @@ and detail all of these properties.</P>
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 @@ of proxies.</P>
<P>The following proxy settings are used by the HTTP protocol handler.</P>
<UL>
<LI><P><B>http.proxyHost</B> (default: &lt;none&gt;)<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 @@ of proxies.</P>
<P>The following proxy settings are used by the HTTPS protocol handler.</P>
<UL>
<LI><P><B>https.proxyHost</B>(default: &lt;none&gt;)<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 @@ of proxies.</P>
<P>The following proxy settings are used by the FTP protocol handler.</P>
<UL>
<LI><P><B>ftp.proxyHost</B>(default: &lt;none&gt;)<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 @@ of proxies.</P>
<LI><P><B>http.agent</B> (default: &ldquo;Java/&lt;version&gt;&rdquo;)<BR>
Defines the string sent in the User-Agent request header in http
requests. Note that the string &ldquo;Java/&lt;version&gt;&rdquo; 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=&rdquo;foobar&rdquo; is used, the User-Agent header will
contain &ldquo;foobar Java/1.5.0&rdquo; if the version of the VM is
1.5.0). This property is checked only once at startup.</P>

View File

@ -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 @@ Java_java_net_InetAddress_init(JNIEnv *env, jclass cls) {
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");

View File

@ -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 @@ Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
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 @@ Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
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 @@ Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
}
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 @@ Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
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;
}
}

View File

@ -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 @@ Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
/* 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 @@ Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
}
} 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 @@ Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
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 @@ Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
}
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 @@ Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
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;
}
}

View File

@ -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;
}
}
}