diff -r 5c4f1b7c753b -r 96c7427456f9 src/java.base/share/classes/sun/net/util/IPAddressUtil.java --- 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 + 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> pa = () -> NetworkInterface.networkInterfaces() + .flatMap(NetworkInterface::inetAddresses) + .filter(a -> (a instanceof Inet6Address) + && address.equals(a) + && ((Inet6Address) a).getScopeId() != 0) + .collect(Collectors.toList()); + List 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; + } + } }