8216417: cleanup of IPv6 scope-id handling

Reviewed-by: alanb, chegar, dfuchs
This commit is contained in:
Michael McMahon 2019-06-13 09:10:51 +01:00
parent 04ea6ae9b3
commit 247a6a2ce4
23 changed files with 761 additions and 595 deletions

View file

@ -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 @@ public class IPAddressUtil {
}
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;
}
}
}