--- a/src/java.base/share/classes/sun/net/util/IPAddressUtil.java Thu Jun 13 12:22:28 2019 +0530
+++ b/src/java.base/share/classes/sun/net/util/IPAddressUtil.java Thu Jun 13 09:10:51 2019 +0100
@@ -25,6 +25,20 @@
package sun.net.util;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.security.AccessController;
+import java.security.PrivilegedExceptionAction;
+import java.security.PrivilegedActionException;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
public class IPAddressUtil {
private static final int INADDR4SZ = 4;
private static final int INADDR16SZ = 16;
@@ -287,4 +301,75 @@
}
return false;
}
+ /**
+ * Mapping from unscoped local Inet(6)Address to the same address
+ * including the correct scope-id, determined from NetworkInterface.
+ */
+ private final static ConcurrentHashMap<InetAddress,InetAddress>
+ cache = new ConcurrentHashMap<>();
+
+ /**
+ * Returns a scoped version of the supplied local, link-local ipv6 address
+ * if that scope-id can be determined from local NetworkInterfaces.
+ * If the address already has a scope-id or if the address is not local, ipv6
+ * or link local, then the original address is returned.
+ *
+ * @param addr
+ * @exception SocketException if the given ipv6 link local address is found
+ * on more than one local interface
+ * @return
+ */
+ public static InetAddress toScopedAddress(InetAddress address)
+ throws SocketException {
+
+ if (address instanceof Inet6Address && address.isLinkLocalAddress()
+ && ((Inet6Address) address).getScopeId() == 0) {
+
+ InetAddress cached = null;
+ try {
+ cached = cache.computeIfAbsent(address, k -> findScopedAddress(k));
+ } catch (UncheckedIOException e) {
+ throw (SocketException)e.getCause();
+ }
+ return cached != null ? cached : address;
+ } else {
+ return address;
+ }
+ }
+
+ /**
+ * Same as above for InetSocketAddress
+ */
+ public static InetSocketAddress toScopedAddress(InetSocketAddress address)
+ throws SocketException {
+ InetAddress addr;
+ InetAddress orig = address.getAddress();
+ if ((addr = toScopedAddress(orig)) == orig) {
+ return address;
+ } else {
+ return new InetSocketAddress(addr, address.getPort());
+ }
+ }
+
+ private static InetAddress findScopedAddress(InetAddress address) {
+ PrivilegedExceptionAction<List<InetAddress>> pa = () -> NetworkInterface.networkInterfaces()
+ .flatMap(NetworkInterface::inetAddresses)
+ .filter(a -> (a instanceof Inet6Address)
+ && address.equals(a)
+ && ((Inet6Address) a).getScopeId() != 0)
+ .collect(Collectors.toList());
+ List<InetAddress> result;
+ try {
+ result = AccessController.doPrivileged(pa);
+ var sz = result.size();
+ if (sz == 0)
+ return null;
+ if (sz > 1)
+ throw new UncheckedIOException(new SocketException(
+ "Duplicate link local addresses: must specify scope-id"));
+ return result.get(0);
+ } catch (PrivilegedActionException pae) {
+ return null;
+ }
+ }
}