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
@ -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, 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, 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 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 The purpose of this document is to list
and detail all of these properties.</P> 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> <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 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>
@ -73,7 +74,7 @@ of proxies.</P>
<P>The following proxy settings are used by the HTTP protocol handler.</P> <P>The following proxy settings are used by the HTTP protocol handler.</P>
<UL> <UL>
<LI><P><B>http.proxyHost</B> (default: &lt;none&gt;)<BR> <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> </P>
<LI><P><B>http.proxyPort</B> (default: 80)<BR> <LI><P><B>http.proxyPort</B> (default: 80)<BR>
The port number of the proxy server.</P> 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> <P>The following proxy settings are used by the HTTPS protocol handler.</P>
<UL> <UL>
<LI><P><B>https.proxyHost</B>(default: &lt;none&gt;)<BR> <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> </P>
<LI><P><B>https.proxyPort</B> (default: 443)<BR> <LI><P><B>https.proxyPort</B> (default: 443)<BR>
The port number of the proxy server.</P> 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> <P>The following proxy settings are used by the FTP protocol handler.</P>
<UL> <UL>
<LI><P><B>ftp.proxyHost</B>(default: &lt;none&gt;)<BR> <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> </P>
<LI><P><B>ftp.proxyPort</B> (default: 80)<BR> <LI><P><B>ftp.proxyPort</B> (default: 80)<BR>
The port number of the proxy server.</P> 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> <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 Defines the string sent in the User-Agent request header in http
requests. Note that the string &ldquo;Java/&lt;version&gt;&rdquo; will 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 -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 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> 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. * 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;
}
}
}