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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -23,7 +23,10 @@
* questions. * questions.
*/ */
package java.net; package java.net;
import java.io.IOException; 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 * Package private implementation of InetAddressImpl for dual
@ -35,15 +38,23 @@ import java.io.IOException;
* *
* @since 1.4 * @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 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; byte[] ifaddr = null;
int scope = -1; int scope = -1;
int netif_scope = -1; int netif_scope = -1;
@ -79,7 +90,8 @@ class Inet6AddressImpl implements InetAddressImpl {
public synchronized InetAddress anyLocalAddress() { public synchronized InetAddress anyLocalAddress() {
if (anyLocalAddress == null) { if (anyLocalAddress == null) {
if (InetAddress.preferIPv6Address) { if (InetAddress.preferIPv6Address == PREFER_IPV6_VALUE ||
InetAddress.preferIPv6Address == PREFER_SYSTEM_VALUE) {
anyLocalAddress = new Inet6Address(); anyLocalAddress = new Inet6Address();
anyLocalAddress.holder().hostName = "::"; anyLocalAddress.holder().hostName = "::";
} else { } else {
@ -91,7 +103,8 @@ class Inet6AddressImpl implements InetAddressImpl {
public synchronized InetAddress loopbackAddress() { public synchronized InetAddress loopbackAddress() {
if (loopbackAddress == null) { if (loopbackAddress == null) {
if (InetAddress.preferIPv6Address) { if (InetAddress.preferIPv6Address == PREFER_IPV6_VALUE ||
InetAddress.preferIPv6Address == PREFER_SYSTEM_VALUE) {
byte[] loopback = byte[] loopback =
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
@ -103,6 +116,6 @@ class Inet6AddressImpl implements InetAddressImpl {
return loopbackAddress; return loopbackAddress;
} }
private InetAddress anyLocalAddress; private InetAddress anyLocalAddress;
private InetAddress loopbackAddress; private InetAddress loopbackAddress;
} }

View file

@ -26,8 +26,6 @@
package java.net; package java.net;
import java.util.NavigableSet; import java.util.NavigableSet;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Objects; import java.util.Objects;
import java.util.Scanner; import java.util.Scanner;
@ -41,6 +39,7 @@ import java.io.ObjectInputStream;
import java.io.ObjectInputStream.GetField; import java.io.ObjectInputStream.GetField;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.io.ObjectOutputStream.PutField; import java.io.ObjectOutputStream.PutField;
import java.lang.annotation.Native;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.ConcurrentSkipListSet;
@ -193,6 +192,11 @@ import sun.net.util.IPAddressUtil;
*/ */
public public
class InetAddress implements java.io.Serializable { 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 * Specify the address family: Internet Protocol, Version 4
* @since 1.4 * @since 1.4
@ -206,8 +210,7 @@ class InetAddress implements java.io.Serializable {
static final int IPv6 = 2; static final int IPv6 = 2;
/* Specify address family preference */ /* Specify address family preference */
static transient boolean preferIPv6Address = false; static transient final int preferIPv6Address;
static class InetAddressHolder { static class InetAddressHolder {
/** /**
@ -293,8 +296,19 @@ class InetAddress implements java.io.Serializable {
* Load net library into runtime, and perform initializations. * Load net library into runtime, and perform initializations.
*/ */
static { static {
preferIPv6Address = java.security.AccessController.doPrivileged( String str = java.security.AccessController.doPrivileged(
new GetBooleanAction("java.net.preferIPv6Addresses")).booleanValue(); 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( AccessController.doPrivileged(
new java.security.PrivilegedAction<>() { new java.security.PrivilegedAction<>() {
public Void run() { 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. DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
This code is free software; you can redistribute it and/or modify it This code is free software; you can redistribute it and/or modify it
@ -58,7 +58,8 @@ and detail all of these properties.</P>
applications that depend on the representation of an IPv4 address 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 (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 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> </UL>
<P>Both of these properties are checked only once, at startup.</P> <P>Both of these properties are checked only once, at startup.</P>
<a name="Proxies"></a> <a name="Proxies"></a>

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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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); CHECK_NULL(iac_class);
ia_holderID = (*env)->GetFieldID(env, ia_class, "holder", "Ljava/net/InetAddress$InetAddressHolder;"); ia_holderID = (*env)->GetFieldID(env, ia_class, "holder", "Ljava/net/InetAddress$InetAddressHolder;");
CHECK_NULL(ia_holderID); 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); CHECK_NULL(ia_preferIPv6AddressID);
iac_addressID = (*env)->GetFieldID(env, iac_class, "address", "I"); 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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_Inet4AddressImpl.h"
#include "java_net_Inet6AddressImpl.h" #include "java_net_Inet6AddressImpl.h"
#include "java_net_InetAddress.h"
/* the initial size of our hostent buffers */ /* the initial size of our hostent buffers */
#ifndef NI_MAXHOST #ifndef NI_MAXHOST
@ -312,8 +313,8 @@ Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
JNU_ReleaseStringPlatformChars(env, host, hostname); JNU_ReleaseStringPlatformChars(env, host, hostname);
return NULL; return NULL;
} else { } else {
int i = 0; int i = 0, addressPreference = -1;
int inetCount = 0, inet6Count = 0, inetIndex, inet6Index; int inetCount = 0, inet6Count = 0, inetIndex = 0, inet6Index = 0, originalIndex = 0;
struct addrinfo *itr, *last = NULL, *iterator = res; struct addrinfo *itr, *last = NULL, *iterator = res;
while (iterator != NULL) { while (iterator != NULL) {
int skip = 0; int skip = 0;
@ -394,14 +395,18 @@ Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
goto cleanupAndReturn; 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 */ /* AF_INET addresses will be offset by inet6Count */
inetIndex = inet6Count; inetIndex = inet6Count;
inet6Index = 0; inet6Index = 0;
} else { } else if (addressPreference == java_net_InetAddress_PREFER_IPV4_VALUE) {
/* AF_INET6 addresses will be offset by inetCount */ /* AF_INET6 addresses will be offset by inetCount */
inetIndex = 0; inetIndex = 0;
inet6Index = inetCount; inet6Index = inetCount;
} else if (addressPreference == java_net_InetAddress_PREFER_SYSTEM_VALUE) {
inetIndex = inet6Index = originalIndex = 0;
} }
while (iterator != NULL) { 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_addr(env, iaObj, ntohl(((struct sockaddr_in*)iterator->ai_addr)->sin_addr.s_addr));
setInetAddress_hostName(env, iaObj, host); setInetAddress_hostName(env, iaObj, host);
(*env)->SetObjectArrayElement(env, ret, inetIndex, iaObj); (*env)->SetObjectArrayElement(env, ret, (inetIndex | originalIndex), iaObj);
inetIndex++; inetIndex++;
} else if (iterator->ai_family == AF_INET6) { } else if (iterator->ai_family == AF_INET6) {
jint scope = 0; jint scope = 0;
@ -435,9 +440,13 @@ Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
setInet6Address_scopeid(env, iaObj, scope); setInet6Address_scopeid(env, iaObj, scope);
} }
setInetAddress_hostName(env, iaObj, host); setInetAddress_hostName(env, iaObj, host);
(*env)->SetObjectArrayElement(env, ret, inet6Index, iaObj); (*env)->SetObjectArrayElement(env, ret, (inet6Index | originalIndex), iaObj);
inet6Index++; inet6Index++;
} }
if (addressPreference == java_net_InetAddress_PREFER_SYSTEM_VALUE) {
originalIndex++;
inetIndex = inet6Index = 0;
}
iterator = iterator->ai_next; 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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 */ /* get the address preference */
preferIPv6Address preferIPv6Address
= (*env)->GetStaticBooleanField(env, ia_class, ia_preferIPv6AddressID); = (*env)->GetStaticIntField(env, ia_class, ia_preferIPv6AddressID);
/* Try once, with our static buffer. */ /* Try once, with our static buffer. */
memset(&hints, 0, sizeof(hints)); memset(&hints, 0, sizeof(hints));
@ -122,7 +122,7 @@ Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
} }
} else { } else {
int i = 0; 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; struct addrinfo *itr, *last, *iterator = res;
while (iterator != NULL) { while (iterator != NULL) {
int skip = 0; int skip = 0;
@ -203,12 +203,14 @@ Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
goto cleanupAndReturn; goto cleanupAndReturn;
} }
if (preferIPv6Address) { if (preferIPv6Address == java_net_InetAddress_PREFER_IPV6_VALUE) {
inetIndex = inet6Count; inetIndex = inet6Count;
inet6Index = 0; inet6Index = 0;
} else { } else if (preferIPv6Address == java_net_InetAddress_PREFER_IPV4_VALUE) {
inetIndex = 0; inetIndex = 0;
inet6Index = inetCount; inet6Index = inetCount;
} else if (preferIPv6Address == java_net_InetAddress_PREFER_SYSTEM_VALUE) {
inetIndex = inet6Index = originalIndex = 0;
} }
while (iterator != NULL) { 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_addr(env, iaObj, ntohl(((struct sockaddr_in*)iterator->ai_addr)->sin_addr.s_addr));
setInetAddress_hostName(env, iaObj, host); setInetAddress_hostName(env, iaObj, host);
(*env)->SetObjectArrayElement(env, ret, inetIndex, iaObj); (*env)->SetObjectArrayElement(env, ret, (inetIndex | originalIndex), iaObj);
inetIndex ++; inetIndex ++;
} else if (iterator->ai_family == AF_INET6) { } else if (iterator->ai_family == AF_INET6) {
jint scope = 0; jint scope = 0;
@ -240,9 +242,13 @@ Java_java_net_Inet6AddressImpl_lookupAllHostAddr(JNIEnv *env, jobject this,
setInet6Address_scopeid(env, iaObj, scope); setInet6Address_scopeid(env, iaObj, scope);
} }
setInetAddress_hostName(env, iaObj, host); setInetAddress_hostName(env, iaObj, host);
(*env)->SetObjectArrayElement(env, ret, inet6Index, iaObj); (*env)->SetObjectArrayElement(env, ret, (inet6Index | originalIndex), iaObj);
inet6Index ++; inet6Index ++;
} }
if (preferIPv6Address == java_net_InetAddress_PREFER_SYSTEM_VALUE) {
originalIndex++;
inetIndex = inet6Index = 0;
}
iterator = iterator->ai_next; 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;
}
}
}