8244202: Implementation of JEP 418: Internet-Address Resolution SPI

Co-authored-by: Chris Hegarty <chegar@openjdk.org>
Co-authored-by: Daniel Fuchs <dfuchs@openjdk.org>
Co-authored-by: Alan Bateman <alanb@openjdk.org>
Reviewed-by: dfuchs, alanb, michaelm, chegar
This commit is contained in:
Aleksei Efimov 2021-11-11 14:33:58 +00:00
parent c29cab8ab4
commit 2ca4ff87b7
56 changed files with 2986 additions and 293 deletions

View file

@ -24,6 +24,9 @@
*/
package java.net;
import java.io.IOException;
import java.net.spi.InetAddressResolver.LookupPolicy;
import static java.net.spi.InetAddressResolver.LookupPolicy.IPV4;
/*
* Package private implementation of InetAddressImpl for IPv4.
@ -32,8 +35,14 @@ import java.io.IOException;
*/
final class Inet4AddressImpl implements InetAddressImpl {
public native String getLocalHostName() throws UnknownHostException;
public native InetAddress[]
lookupAllHostAddr(String hostname) throws UnknownHostException;
public InetAddress[] lookupAllHostAddr(String hostname, LookupPolicy lookupPolicy)
throws UnknownHostException {
if ((lookupPolicy.characteristics() & IPV4) == 0) {
throw new UnknownHostException(hostname);
}
return lookupAllHostAddr(hostname);
}
private native InetAddress[] lookupAllHostAddr(String hostname) throws UnknownHostException;
public native String getHostByAddr(byte[] addr) throws UnknownHostException;
private native boolean isReachable0(byte[] addr, int timeout, byte[] ifaddr, int ttl) throws IOException;

View file

@ -25,10 +25,9 @@
package java.net;
import java.io.IOException;
import java.net.spi.InetAddressResolver.LookupPolicy;
import static java.net.InetAddress.IPv6;
import static java.net.InetAddress.PREFER_IPV6_VALUE;
import static java.net.InetAddress.PREFER_SYSTEM_VALUE;
import static java.net.InetAddress.PLATFORM_LOOKUP_POLICY;
/*
* Package private implementation of InetAddressImpl for dual
@ -48,8 +47,13 @@ final class Inet6AddressImpl implements InetAddressImpl {
public native String getLocalHostName() throws UnknownHostException;
public native InetAddress[] lookupAllHostAddr(String hostname)
throws UnknownHostException;
public InetAddress[] lookupAllHostAddr(String hostname, LookupPolicy lookupPolicy)
throws UnknownHostException {
return lookupAllHostAddr(hostname, lookupPolicy.characteristics());
}
private native InetAddress[] lookupAllHostAddr(String hostname, int characteristics)
throws UnknownHostException;
public native String getHostByAddr(byte[] addr) throws UnknownHostException;
@ -96,8 +100,9 @@ final class Inet6AddressImpl implements InetAddressImpl {
public synchronized InetAddress anyLocalAddress() {
if (anyLocalAddress == null) {
if (InetAddress.preferIPv6Address == PREFER_IPV6_VALUE ||
InetAddress.preferIPv6Address == PREFER_SYSTEM_VALUE) {
int flags = PLATFORM_LOOKUP_POLICY.characteristics();
if (InetAddress.ipv6AddressesFirst(flags) ||
InetAddress.systemAddressesOrder(flags)) {
anyLocalAddress = new Inet6Address();
anyLocalAddress.holder().hostName = "::";
} else {
@ -109,9 +114,9 @@ final class Inet6AddressImpl implements InetAddressImpl {
public synchronized InetAddress loopbackAddress() {
if (loopbackAddress == null) {
boolean preferIPv6Address =
InetAddress.preferIPv6Address == PREFER_IPV6_VALUE ||
InetAddress.preferIPv6Address == PREFER_SYSTEM_VALUE;
int flags = PLATFORM_LOOKUP_POLICY.characteristics();
boolean preferIPv6Address = InetAddress.ipv6AddressesFirst(flags) ||
InetAddress.systemAddressesOrder(flags);
for (int i = 0; i < 2; i++) {
InetAddress address;

View file

@ -25,6 +25,11 @@
package java.net;
import java.net.spi.InetAddressResolver;
import java.net.spi.InetAddressResolverProvider;
import java.net.spi.InetAddressResolver.LookupPolicy;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.List;
import java.util.NavigableSet;
import java.util.ArrayList;
@ -40,19 +45,31 @@ import java.io.ObjectInputStream.GetField;
import java.io.ObjectOutputStream;
import java.io.ObjectOutputStream.PutField;
import java.lang.annotation.Native;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.atomic.AtomicLong;
import java.util.Arrays;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Stream;
import jdk.internal.misc.VM;
import jdk.internal.access.JavaNetInetAddressAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.vm.annotation.Stable;
import sun.net.ResolverProviderConfiguration;
import sun.security.action.*;
import sun.net.InetAddressCachePolicy;
import sun.net.util.IPAddressUtil;
import sun.nio.cs.UTF_8;
import static java.net.spi.InetAddressResolver.LookupPolicy.IPV4;
import static java.net.spi.InetAddressResolver.LookupPolicy.IPV4_FIRST;
import static java.net.spi.InetAddressResolver.LookupPolicy.IPV6;
import static java.net.spi.InetAddressResolver.LookupPolicy.IPV6_FIRST;
/**
* This class represents an Internet Protocol (IP) address.
*
@ -128,25 +145,35 @@ import sun.nio.cs.UTF_8;
* address format, please refer to <A
* HREF="Inet6Address.html#format">Inet6Address#format</A>.
*
* <P>There is a <a href="doc-files/net-properties.html#Ipv4IPv6">couple of
* System Properties</a> affecting how IPv4 and IPv6 addresses are used.</P>
* <p> There is a <a href="doc-files/net-properties.html#Ipv4IPv6">couple of
* System Properties</a> affecting how IPv4 and IPv6 addresses are used.
*
* <h3> Host Name Resolution </h3>
* <h2 id="host-name-resolution"> Host Name Resolution </h2>
*
* Host name-to-IP address <i>resolution</i> is accomplished through
* the use of a combination of local machine configuration information
* and network naming services such as the Domain Name System (DNS)
* and Network Information Service(NIS). The particular naming
* services(s) being used is by default the local machine configured
* one. For any host name, its corresponding IP address is returned.
* <p> The InetAddress class provides methods to resolve host names to
* their IP addresses and vice versa. The actual resolution is delegated to an
* {@linkplain InetAddressResolver InetAddress resolver}.
*
* <p> <i>Host name-to-IP address resolution</i> maps a host name to an IP address.
* For any host name, its corresponding IP address is returned.
*
* <p> <i>Reverse name resolution</i> means that for any IP address,
* the host associated with the IP address is returned.
*
* <p> The InetAddress class provides methods to resolve host names to
* their IP addresses and vice versa.
* <p id="built-in-resolver"> The built-in InetAddress resolver implementation does
* host name-to-IP address resolution and vice versa through the use of
* a combination of local machine configuration information and network
* naming services such as the Domain Name System (DNS) and the Lightweight Directory
* Access Protocol (LDAP).
* The particular naming services that the built-in resolver uses by default
* depends on the configuration of the local machine.
*
* <h3> InetAddress Caching </h3>
* <p> {@code InetAddress} has a service provider mechanism for InetAddress resolvers
* that allows a custom InetAddress resolver to be used instead of the built-in implementation.
* {@link InetAddressResolverProvider} is the service provider class. Its API docs provide all the
* details on this mechanism.
*
* <h2> InetAddress Caching </h2>
*
* The InetAddress class has a cache to store successful as well as
* unsuccessful host name resolutions.
@ -198,10 +225,6 @@ import sun.nio.cs.UTF_8;
*/
public 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
* @since 1.4
@ -214,9 +237,6 @@ public class InetAddress implements java.io.Serializable {
*/
@Native static final int IPv6 = 2;
/* Specify address family preference */
static final transient int preferIPv6Address;
static class InetAddressHolder {
/**
* Reserve the original application specified hostname.
@ -288,8 +308,11 @@ public class InetAddress implements java.io.Serializable {
return holder;
}
/* Used to store the name service provider */
private static transient NameService nameService;
/* Used to store the system-wide resolver */
@Stable
private static volatile InetAddressResolver resolver;
private static final InetAddressResolver BUILTIN_RESOLVER;
/**
* Used to store the best available hostname.
@ -301,22 +324,25 @@ public class InetAddress implements java.io.Serializable {
@java.io.Serial
private static final long serialVersionUID = 3286316764910316507L;
// "java.net.preferIPv4Stack" system property value
private static final String PREFER_IPV4_STACK_VALUE;
// "java.net.preferIPv6Addresses" system property value
private static final String PREFER_IPV6_ADDRESSES_VALUE;
// "jdk.net.hosts.file" system property value
private static final String HOSTS_FILE_NAME;
/*
* Load net library into runtime, and perform initializations.
*/
static {
String str = GetPropertyAction.privilegedGetProperty("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;
}
PREFER_IPV4_STACK_VALUE =
GetPropertyAction.privilegedGetProperty("java.net.preferIPv4Stack");
PREFER_IPV6_ADDRESSES_VALUE =
GetPropertyAction.privilegedGetProperty("java.net.preferIPv6Addresses");
HOSTS_FILE_NAME =
GetPropertyAction.privilegedGetProperty("jdk.net.hosts.file");
jdk.internal.loader.BootLoader.loadLibrary("net");
SharedSecrets.setJavaNetInetAddressAccess(
new JavaNetInetAddressAccess() {
@ -324,13 +350,6 @@ public class InetAddress implements java.io.Serializable {
return ia.holder.getOriginalHostName();
}
public InetAddress getByName(String hostName,
InetAddress hostAddress)
throws UnknownHostException
{
return InetAddress.getByName(hostName, hostAddress);
}
public int addressValue(Inet4Address inet4Address) {
return inet4Address.addressValue();
}
@ -343,6 +362,131 @@ public class InetAddress implements java.io.Serializable {
init();
}
/**
* Creates an address lookup policy from {@code "java.net.preferIPv4Stack"},
* {@code "java.net.preferIPv6Addresses"} system property values, and O/S configuration.
*/
private static final LookupPolicy initializePlatformLookupPolicy() {
// Calculate AddressFamily value first
boolean ipv4Available = isIPv4Available();
if ("true".equals(PREFER_IPV4_STACK_VALUE) && ipv4Available) {
return LookupPolicy.of(IPV4);
}
// Check if IPv6 is not supported
if (InetAddress.impl instanceof Inet4AddressImpl) {
return LookupPolicy.of(IPV4);
}
// Check if system supports IPv4, if not use IPv6
if (!ipv4Available) {
return LookupPolicy.of(IPV6);
}
// If both address families are needed - check preferIPv6Addresses value
if (PREFER_IPV6_ADDRESSES_VALUE != null) {
if (PREFER_IPV6_ADDRESSES_VALUE.equalsIgnoreCase("true")) {
return LookupPolicy.of(IPV4 | IPV6 | IPV6_FIRST);
}
if (PREFER_IPV6_ADDRESSES_VALUE.equalsIgnoreCase("false")) {
return LookupPolicy.of(IPV4 | IPV6 | IPV4_FIRST);
}
if (PREFER_IPV6_ADDRESSES_VALUE.equalsIgnoreCase("system")) {
return LookupPolicy.of(IPV4 | IPV6);
}
}
// Default value with both address families needed - IPv4 addresses come first
return LookupPolicy.of(IPV4 | IPV6 | IPV4_FIRST);
}
static boolean systemAddressesOrder(int lookupCharacteristics) {
return (lookupCharacteristics & (IPV4_FIRST | IPV6_FIRST)) == 0;
}
static boolean ipv4AddressesFirst(int lookupCharacteristics) {
return (lookupCharacteristics & IPV4_FIRST) != 0;
}
static boolean ipv6AddressesFirst(int lookupCharacteristics) {
return (lookupCharacteristics & IPV6_FIRST) != 0;
}
// Native method to check if IPv4 is available
private static native boolean isIPv4Available();
/**
* The {@code RuntimePermission("inetAddressResolverProvider")} is
* necessary to subclass and instantiate the {@code InetAddressResolverProvider}
* class, as well as to obtain resolver from an instance of that class,
* and it is also required to obtain the operating system name resolution configurations.
*/
private static final RuntimePermission INET_ADDRESS_RESOLVER_PERMISSION =
new RuntimePermission("inetAddressResolverProvider");
private static final ReentrantLock RESOLVER_LOCK = new ReentrantLock();
private static volatile InetAddressResolver bootstrapResolver;
@SuppressWarnings("removal")
private static InetAddressResolver resolver() {
InetAddressResolver cns = resolver;
if (cns != null) {
return cns;
}
if (VM.isBooted()) {
RESOLVER_LOCK.lock();
boolean bootstrapSet = false;
try {
cns = resolver;
if (cns != null) {
return cns;
}
// Protection against provider calling InetAddress APIs during initialization
if (bootstrapResolver != null) {
return bootstrapResolver;
}
bootstrapResolver = BUILTIN_RESOLVER;
bootstrapSet = true;
if (HOSTS_FILE_NAME != null) {
// The default resolver service is already host file resolver
cns = BUILTIN_RESOLVER;
} else if (System.getSecurityManager() != null) {
PrivilegedAction<InetAddressResolver> pa = InetAddress::loadResolver;
cns = AccessController.doPrivileged(
pa, null, INET_ADDRESS_RESOLVER_PERMISSION);
} else {
cns = loadResolver();
}
InetAddress.resolver = cns;
return cns;
} finally {
// We want to clear bootstrap resolver reference only after an attempt to
// instantiate a resolver has been completed.
if (bootstrapSet) {
bootstrapResolver = null;
}
RESOLVER_LOCK.unlock();
}
} else {
return BUILTIN_RESOLVER;
}
}
private static InetAddressResolver loadResolver() {
return ServiceLoader.load(InetAddressResolverProvider.class)
.findFirst()
.map(nsp -> nsp.get(builtinConfiguration()))
.orElse(BUILTIN_RESOLVER);
}
private static InetAddressResolverProvider.Configuration builtinConfiguration() {
return new ResolverProviderConfiguration(BUILTIN_RESOLVER, () -> {
try {
return impl.getLocalHostName();
} catch (UnknownHostException unknownHostException) {
return "localhost";
}
});
}
/**
* Constructor for the Socket.accept() method.
* This creates an empty InetAddress, which is filled in by
@ -555,7 +699,7 @@ public class InetAddress implements java.io.Serializable {
* this host name will be remembered and returned;
* otherwise, a reverse name lookup will be performed
* and the result will be returned based on the system
* configured name lookup service. If a lookup of the name service
* configured resolver. If a lookup of the name service
* is required, call
* {@link #getCanonicalHostName() getCanonicalHostName}.
*
@ -656,10 +800,11 @@ public class InetAddress implements java.io.Serializable {
* @see SecurityManager#checkConnect
*/
private static String getHostFromNameService(InetAddress addr, boolean check) {
String host = null;
String host;
var resolver = resolver();
try {
// first lookup the hostname
host = nameService.getHostByAddr(addr.getAddress());
host = resolver.lookupByAddress(addr.getAddress());
/* check to see if calling code is allowed to know
* the hostname for this IP address, ie, connect to the host
@ -691,11 +836,12 @@ public class InetAddress implements java.io.Serializable {
host = addr.getHostAddress();
return host;
}
} catch (SecurityException e) {
} catch (RuntimeException | UnknownHostException e) {
// 'resolver.lookupByAddress' and 'InetAddress.getAllByName0' delegate to
// the system-wide resolver, which could be a custom one. At that point we
// treat any unexpected RuntimeException thrown by the resolver as we would
// treat an UnknownHostException or an unmatched host name.
host = addr.getHostAddress();
} catch (UnknownHostException e) {
host = addr.getHostAddress();
// let next provider resolve the hostname
}
return host;
}
@ -755,8 +901,9 @@ public class InetAddress implements java.io.Serializable {
* string returned is of the form: hostname / literal IP
* address.
*
* If the host name is unresolved, no reverse name service lookup
* is performed. The hostname part will be represented by an empty string.
* If the host name is unresolved, no reverse lookup
* is performed. The hostname part will be represented
* by an empty string.
*
* @return a string representation of this IP address.
*/
@ -821,11 +968,9 @@ public class InetAddress implements java.io.Serializable {
// in cache when the result is obtained
private static final class NameServiceAddresses implements Addresses {
private final String host;
private final InetAddress reqAddr;
NameServiceAddresses(String host, InetAddress reqAddr) {
NameServiceAddresses(String host) {
this.host = host;
this.reqAddr = reqAddr;
}
@Override
@ -849,7 +994,7 @@ public class InetAddress implements java.io.Serializable {
UnknownHostException ex;
int cachePolicy;
try {
inetAddresses = getAddressesFromNameService(host, reqAddr);
inetAddresses = getAddressesFromNameService(host);
ex = null;
cachePolicy = InetAddressCachePolicy.get();
} catch (UnknownHostException uhe) {
@ -875,7 +1020,7 @@ public class InetAddress implements java.io.Serializable {
expirySet.add(cachedAddresses);
}
}
if (inetAddresses == null) {
if (inetAddresses == null || inetAddresses.length == 0) {
throw ex == null ? new UnknownHostException(host) : ex;
}
return inetAddresses;
@ -889,81 +1034,48 @@ public class InetAddress implements java.io.Serializable {
}
/**
* NameService provides host and address lookup service
*
* @since 9
*/
private interface NameService {
/**
* Lookup a host mapping by name. Retrieve the IP addresses
* associated with a host
*
* @param host the specified hostname
* @return array of IP addresses for the requested host
* @throws UnknownHostException
* if no IP address for the {@code host} could be found
*/
InetAddress[] lookupAllHostAddr(String host)
throws UnknownHostException;
/**
* Lookup the host corresponding to the IP address provided
*
* @param addr byte array representing an IP address
* @return {@code String} representing the host name mapping
* @throws UnknownHostException
* if no host found for the specified IP address
*/
String getHostByAddr(byte[] addr) throws UnknownHostException;
}
/**
* The default NameService implementation, which delegates to the underlying
* The default InetAddressResolver implementation, which delegates to the underlying
* OS network libraries to resolve host address mappings.
*
* @since 9
*/
private static final class PlatformNameService implements NameService {
private static final class PlatformResolver implements InetAddressResolver {
public InetAddress[] lookupAllHostAddr(String host)
throws UnknownHostException
{
return impl.lookupAllHostAddr(host);
public Stream<InetAddress> lookupByName(String host, LookupPolicy policy)
throws UnknownHostException {
Objects.requireNonNull(host);
Objects.requireNonNull(policy);
return Arrays.stream(impl.lookupAllHostAddr(host, policy));
}
public String getHostByAddr(byte[] addr)
throws UnknownHostException
{
public String lookupByAddress(byte[] addr)
throws UnknownHostException {
Objects.requireNonNull(addr);
if (addr.length != Inet4Address.INADDRSZ && addr.length != Inet6Address.INADDRSZ) {
throw new IllegalArgumentException("Invalid address length");
}
return impl.getHostByAddr(addr);
}
}
/**
* The HostsFileNameService provides host address mapping
* The HostsFileResolver provides host address mapping
* by reading the entries in a hosts file, which is specified by
* {@code jdk.net.hosts.file} system property
*
* <p>The file format is that which corresponds with the /etc/hosts file
* IP Address host alias list.
*
* <p>When the file lookup is enabled it replaces the default NameService
* <p>When the file lookup is enabled it replaces the default InetAddressResolver
* implementation
*
* @since 9
*/
private static final class HostsFileNameService implements NameService {
private static final InetAddress[] EMPTY_ARRAY = new InetAddress[0];
// Specify if only IPv4 addresses should be returned by HostsFileService implementation
private static final boolean preferIPv4Stack = Boolean.parseBoolean(
GetPropertyAction.privilegedGetProperty("java.net.preferIPv4Stack"));
private static final class HostsFileResolver implements InetAddressResolver {
private final String hostsFile;
public HostsFileNameService(String hostsFileName) {
public HostsFileResolver(String hostsFileName) {
this.hostsFile = hostsFileName;
}
@ -974,17 +1086,22 @@ public class InetAddress implements java.io.Serializable {
*
* @param addr byte array representing an IP address
* @return {@code String} representing the host name mapping
* @throws UnknownHostException
* if no host found for the specified IP address
* @throws UnknownHostException if no host found for the specified IP address
* @throws IllegalArgumentException if IP address is of illegal length
* @throws NullPointerException if addr is {@code null}
*/
@Override
public String getHostByAddr(byte[] addr) throws UnknownHostException {
public String lookupByAddress(byte[] addr) throws UnknownHostException {
String hostEntry;
String host = null;
Objects.requireNonNull(addr);
// Check the length of the address array
if (addr.length != Inet4Address.INADDRSZ && addr.length != Inet6Address.INADDRSZ) {
throw new IllegalArgumentException("Invalid address length");
}
try (Scanner hostsFileScanner = new Scanner(new File(hostsFile),
UTF_8.INSTANCE))
{
UTF_8.INSTANCE)) {
while (hostsFileScanner.hasNextLine()) {
hostEntry = hostsFileScanner.nextLine();
if (!hostEntry.startsWith("#")) {
@ -1020,22 +1137,31 @@ public class InetAddress implements java.io.Serializable {
* with the specified host name.
*
* @param host the specified hostname
* @return array of IP addresses for the requested host
* @param lookupPolicy IP addresses lookup policy which specifies addresses
* family and their order
* @return stream of IP addresses for the requested host
* @throws NullPointerException if either parameter is {@code null}
* @throws UnknownHostException
* if no IP address for the {@code host} could be found
*/
public InetAddress[] lookupAllHostAddr(String host)
public Stream<InetAddress> lookupByName(String host, LookupPolicy lookupPolicy)
throws UnknownHostException {
String hostEntry;
String addrStr;
byte addr[];
Objects.requireNonNull(host);
Objects.requireNonNull(lookupPolicy);
List<InetAddress> inetAddresses = new ArrayList<>();
List<InetAddress> inet4Addresses = new ArrayList<>();
List<InetAddress> inet6Addresses = new ArrayList<>();
int flags = lookupPolicy.characteristics();
boolean needIPv4 = (flags & IPv4) != 0;
boolean needIPv6 = (flags & IPv6) != 0;
// lookup the file and create a list InetAddress for the specified host
try (Scanner hostsFileScanner = new Scanner(new File(hostsFile),
UTF_8.INSTANCE)) {
UTF_8.INSTANCE)) {
while (hostsFileScanner.hasNextLine()) {
hostEntry = hostsFileScanner.nextLine();
if (!hostEntry.startsWith("#")) {
@ -1047,10 +1173,10 @@ public class InetAddress implements java.io.Serializable {
if (addr != null) {
InetAddress address = InetAddress.getByAddress(host, addr);
inetAddresses.add(address);
if (address instanceof Inet4Address) {
if (address instanceof Inet4Address && needIPv4) {
inet4Addresses.add(address);
}
if (address instanceof Inet6Address) {
if (address instanceof Inet6Address && needIPv6) {
inet6Addresses.add(address);
}
}
@ -1062,33 +1188,38 @@ public class InetAddress implements java.io.Serializable {
throw new UnknownHostException("Unable to resolve host " + host
+ " as hosts file " + hostsFile + " not found ");
}
List<InetAddress> res;
// If "preferIPv4Stack" system property is set to "true" then return
// only IPv4 addresses
if (preferIPv4Stack) {
res = inet4Addresses;
} else {
// Otherwise, analyse "preferIPv6Addresses" value
res = switch (preferIPv6Address) {
case PREFER_IPV4_VALUE -> concatAddresses(inet4Addresses, inet6Addresses);
case PREFER_IPV6_VALUE -> concatAddresses(inet6Addresses, inet4Addresses);
default -> inetAddresses;
};
// Check if only IPv4 addresses are requested
if (needIPv4 && !needIPv6) {
checkResultsList(inet4Addresses, host);
return inet4Addresses.stream();
}
if (res.isEmpty()) {
throw new UnknownHostException("Unable to resolve host " + host
+ " in hosts file " + hostsFile);
// Check if only IPv6 addresses are requested
if (!needIPv4 && needIPv6) {
checkResultsList(inet6Addresses, host);
return inet6Addresses.stream();
}
return res.toArray(EMPTY_ARRAY);
// If both type of addresses are requested:
// First, check if there is any results. Then arrange
// addresses according to LookupPolicy value.
checkResultsList(inetAddresses, host);
if (ipv6AddressesFirst(flags)) {
return Stream.concat(inet6Addresses.stream(), inet4Addresses.stream());
} else if (ipv4AddressesFirst(flags)) {
return Stream.concat(inet4Addresses.stream(), inet6Addresses.stream());
}
// Only "system" addresses order is possible at this stage
assert systemAddressesOrder(flags);
return inetAddresses.stream();
}
private static List<InetAddress> concatAddresses(List<InetAddress> firstPart,
List<InetAddress> secondPart) {
List<InetAddress> result = new ArrayList<>(firstPart);
result.addAll(secondPart);
return result;
// Checks if result list with addresses is not empty.
// If it is empty throw an UnknownHostException.
private void checkResultsList(List<InetAddress> addressesList, String hostName)
throws UnknownHostException {
if (addressesList.isEmpty()) {
throw new UnknownHostException("Unable to resolve host " + hostName
+ " in hosts file " + hostsFile);
}
}
private String removeComments(String hostsEntry) {
@ -1130,45 +1261,52 @@ public class InetAddress implements java.io.Serializable {
static final InetAddressImpl impl;
/**
* Platform-wide {@code LookupPolicy} initialized from {@code "java.net.preferIPv4Stack"},
* {@code "java.net.preferIPv6Addresses"} system properties.
*/
static final LookupPolicy PLATFORM_LOOKUP_POLICY;
static {
// create the impl
impl = InetAddressImplFactory.create();
// create name service
nameService = createNameService();
// impl must be initialized before calling this method
PLATFORM_LOOKUP_POLICY = initializePlatformLookupPolicy();
// create built-in resolver
BUILTIN_RESOLVER = createBuiltinInetAddressResolver();
}
/**
* Create an instance of the NameService interface based on
* Create an instance of the InetAddressResolver interface based on
* the setting of the {@code jdk.net.hosts.file} system property.
*
* <p>The default NameService is the PlatformNameService, which typically
* <p>The default InetAddressResolver is the PlatformResolver, which typically
* delegates name and address resolution calls to the underlying
* OS network libraries.
*
* <p> A HostsFileNameService is created if the {@code jdk.net.hosts.file}
* <p> A HostsFileResolver is created if the {@code jdk.net.hosts.file}
* system property is set. If the specified file doesn't exist, the name or
* address lookup will result in an UnknownHostException. Thus, non existent
* hosts file is handled as if the file is empty.
*
* @return a NameService
* @return an InetAddressResolver
*/
private static NameService createNameService() {
String hostsFileName =
GetPropertyAction.privilegedGetProperty("jdk.net.hosts.file");
NameService theNameService;
if (hostsFileName != null) {
theNameService = new HostsFileNameService(hostsFileName);
private static InetAddressResolver createBuiltinInetAddressResolver() {
InetAddressResolver theResolver;
if (HOSTS_FILE_NAME != null) {
theResolver = new HostsFileResolver(HOSTS_FILE_NAME);
} else {
theNameService = new PlatformNameService();
theResolver = new PlatformResolver();
}
return theNameService;
return theResolver;
}
/**
* Creates an InetAddress based on the provided host name and IP address.
* No name service is checked for the validity of the address.
* The system-wide {@linkplain InetAddressResolver resolver} is not used to check
* the validity of the address.
*
* <p> The host name can either be a machine name, such as
* "{@code www.example.com}", or a textual representation of its IP
@ -1251,15 +1389,9 @@ public class InetAddress implements java.io.Serializable {
return InetAddress.getAllByName(host)[0];
}
// called from deployment cache manager
private static InetAddress getByName(String host, InetAddress reqAddr)
throws UnknownHostException {
return InetAddress.getAllByName(host, reqAddr)[0];
}
/**
* Given the name of a host, returns an array of its IP addresses,
* based on the configured name service on the system.
* based on the configured system {@linkplain InetAddressResolver resolver}.
*
* <p> The host name can either be a machine name, such as
* "{@code www.example.com}", or a textual representation of its IP
@ -1298,11 +1430,6 @@ public class InetAddress implements java.io.Serializable {
*/
public static InetAddress[] getAllByName(String host)
throws UnknownHostException {
return getAllByName(host, null);
}
private static InetAddress[] getAllByName(String host, InetAddress reqAddr)
throws UnknownHostException {
if (host == null || host.isEmpty()) {
InetAddress[] ret = new InetAddress[1];
@ -1364,7 +1491,7 @@ public class InetAddress implements java.io.Serializable {
// We were expecting an IPv6 Literal, but got something else
throw new UnknownHostException("["+host+"]");
}
return getAllByName0(host, reqAddr, true, true);
return getAllByName0(host, true, true);
}
/**
@ -1414,25 +1541,18 @@ public class InetAddress implements java.io.Serializable {
return zone;
}
private static InetAddress[] getAllByName0 (String host)
throws UnknownHostException
{
return getAllByName0(host, true);
}
/**
* package private so SocketPermission can call it
*/
static InetAddress[] getAllByName0 (String host, boolean check)
throws UnknownHostException {
return getAllByName0 (host, null, check, true);
return getAllByName0(host, check, true);
}
/**
* Designated lookup method.
*
* @param host host name to look up
* @param reqAddr requested address to be the 1st in returned array
* @param check perform security check
* @param useCache use cached value if not expired else always
* perform name service lookup (and cache the result)
@ -1440,7 +1560,6 @@ public class InetAddress implements java.io.Serializable {
* @throws UnknownHostException if host name is not found
*/
private static InetAddress[] getAllByName0(String host,
InetAddress reqAddr,
boolean check,
boolean useCache)
throws UnknownHostException {
@ -1498,7 +1617,7 @@ public class InetAddress implements java.io.Serializable {
// the name service and install it within cache...
Addresses oldAddrs = cache.putIfAbsent(
host,
addrs = new NameServiceAddresses(host, reqAddr)
addrs = new NameServiceAddresses(host)
);
if (oldAddrs != null) { // lost putIfAbsent race
addrs = oldAddrs;
@ -1509,47 +1628,30 @@ public class InetAddress implements java.io.Serializable {
return addrs.get().clone();
}
static InetAddress[] getAddressesFromNameService(String host, InetAddress reqAddr)
static InetAddress[] getAddressesFromNameService(String host)
throws UnknownHostException {
InetAddress[] addresses = null;
Stream<InetAddress> addresses = null;
UnknownHostException ex = null;
var resolver = resolver();
try {
addresses = nameService.lookupAllHostAddr(host);
} catch (UnknownHostException uhe) {
addresses = resolver.lookupByName(host, PLATFORM_LOOKUP_POLICY);
} catch (RuntimeException | UnknownHostException x) {
if (host.equalsIgnoreCase("localhost")) {
addresses = new InetAddress[]{impl.loopbackAddress()};
} else {
addresses = Stream.of(impl.loopbackAddress());
} else if (x instanceof UnknownHostException uhe) {
ex = uhe;
} else {
ex = new UnknownHostException();
ex.initCause(x);
}
}
if (addresses == null) {
InetAddress[] result = addresses == null ? null
: addresses.toArray(InetAddress[]::new);
if (result == null || result.length == 0) {
throw ex == null ? new UnknownHostException(host) : ex;
}
// More to do?
if (reqAddr != null && addresses.length > 1 && !addresses[0].equals(reqAddr)) {
// Find it?
int i = 1;
for (; i < addresses.length; i++) {
if (addresses[i].equals(reqAddr)) {
break;
}
}
// Rotate
if (i < addresses.length) {
InetAddress tmp, tmp2 = reqAddr;
for (int j = 0; j < i; j++) {
tmp = addresses[j];
addresses[j] = tmp2;
tmp2 = tmp;
}
addresses[i] = tmp2;
}
}
return addresses;
return result;
}
/**
@ -1557,8 +1659,7 @@ public class InetAddress implements java.io.Serializable {
* The argument is in network byte order: the highest order
* byte of the address is in {@code getAddress()[0]}.
*
* <p> This method doesn't block, i.e. no reverse name service lookup
* is performed.
* <p> This method doesn't block, i.e. no reverse lookup is performed.
*
* <p> IPv4 address byte array must be 4 bytes long and IPv6 byte array
* must be 16 bytes long
@ -1637,7 +1738,7 @@ public class InetAddress implements java.io.Serializable {
// call getAllByName0 without security checks and
// without using cached data
try {
localAddr = getAllByName0(local, null, false, false)[0];
localAddr = getAllByName0(local, false, false)[0];
} catch (UnknownHostException uhe) {
// Rethrow with a more informative error message.
UnknownHostException uhe2 =

View file

@ -24,7 +24,10 @@
*/
package java.net;
import java.io.IOException;
import java.net.spi.InetAddressResolver.LookupPolicy;
/*
* Package private interface to "implementation" used by
* {@link InetAddress}.
@ -38,7 +41,7 @@ sealed interface InetAddressImpl permits Inet4AddressImpl, Inet6AddressImpl {
String getLocalHostName() throws UnknownHostException;
InetAddress[]
lookupAllHostAddr(String hostname) throws UnknownHostException;
lookupAllHostAddr(String hostname, LookupPolicy lookupPolicy) throws UnknownHostException;
String getHostByAddr(byte[] addr) throws UnknownHostException;
InetAddress anyLocalAddress();

View file

@ -1,6 +1,6 @@
<!DOCTYPE HTML>
<!--
Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 1998, 2021, 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
@ -61,7 +61,7 @@ If there is no special note, a property value is checked every time it is used.<
(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
possible, or <B>system</B> to preserve the order of the addresses as
returned by the operating system.</P>
returned by the system-wide {@linkplain java.net.spi.InetAddressResolver resolver}.</P>
</UL>
<P>Both of these properties are checked only once, at startup.</P>
<a id="Proxies"></a>

View file

@ -0,0 +1,201 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package java.net.spi;
import java.lang.annotation.Native;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.stream.Stream;
/**
* This interface defines operations for looking up host names and IP addresses.
* {@link InetAddress} delegates all lookup operations to the <i>system-wide
* resolver</i>.
*
* <p> The <i>system-wide resolver</i> can be customized by
* <a href="InetAddressResolverProvider.html#system-wide-resolver">
* deploying an implementation</a> of {@link InetAddressResolverProvider}.
*
* @since 18
*/
public interface InetAddressResolver {
/**
* Given the name of a host, returns a stream of IP addresses of the requested
* address family associated with a provided hostname.
*
* <p> {@code host} should be a machine name, such as "{@code www.example.com}",
* not a textual representation of its IP address. No validation is performed on
* the given {@code host} name: if a textual representation is supplied, the name
* resolution is likely to fail and {@link UnknownHostException} may be thrown.
*
* <p> The address family type and addresses order are specified by the
* {@code LookupPolicy} instance. Lookup operation characteristics could be
* acquired with {@link LookupPolicy#characteristics()}.
* If {@link InetAddressResolver.LookupPolicy#IPV4} and
* {@link InetAddressResolver.LookupPolicy#IPV6} characteristics provided then this
* method returns addresses of both IPV4 and IPV6 families.
*
* @param host the specified hostname
* @param lookupPolicy the address lookup policy
* @return a stream of IP addresses for the requested host
* @throws NullPointerException if either parameter is {@code null}
* @throws UnknownHostException if no IP address for the {@code host} could be found
* @see LookupPolicy
*/
Stream<InetAddress> lookupByName(String host, LookupPolicy lookupPolicy) throws UnknownHostException;
/**
* Lookup the host name corresponding to the raw IP address provided.
*
* <p> {@code addr} argument is in network byte order: the highest order byte of the address
* is in {@code addr[0]}.
*
* <p> IPv4 address byte array must be 4 bytes long and IPv6 byte array
* must be 16 bytes long.
*
* @param addr byte array representing a raw IP address
* @return {@code String} representing the host name mapping
* @throws UnknownHostException if no host name is found for the specified IP address
* @throws IllegalArgumentException if the length of the provided byte array doesn't correspond
* to a valid IP address length
* @throws NullPointerException if addr is {@code null}
*/
String lookupByAddress(byte[] addr) throws UnknownHostException;
/**
* A {@code LookupPolicy} object describes characteristics that can be applied to a lookup operation.
* In particular, it is used to specify the ordering and which filtering should be performed when
* {@linkplain InetAddressResolver#lookupByName(String, LookupPolicy) looking up host addresses}.
*
* <p> The default platform-wide lookup policy is constructed by consulting
* <a href="doc-files/net-properties.html#Ipv4IPv6">System Properties</a> which affect
* how IPv4 and IPv6 addresses are returned.
*
* @since 18
*/
final class LookupPolicy {
/**
* Characteristic value signifying if IPv4 addresses need to be queried during lookup.
*/
@Native
public static final int IPV4 = 1 << 0;
/**
* Characteristic value signifying if IPv6 addresses need to be queried during lookup.
*/
@Native
public static final int IPV6 = 1 << 1;
/**
* Characteristic value signifying if IPv4 addresses should be returned
* first by {@code InetAddressResolver}.
*/
@Native
public static final int IPV4_FIRST = 1 << 2;
/**
* Characteristic value signifying if IPv6 addresses should be returned
* first by {@code InetAddressResolver}.
*/
@Native
public static final int IPV6_FIRST = 1 << 3;
private final int characteristics;
private LookupPolicy(int characteristics) {
this.characteristics = characteristics;
}
/**
* This factory method creates a {@link LookupPolicy LookupPolicy} instance with
* the given {@code characteristics} value.
*
* <p> The {@code characteristics} value is an integer bit mask which defines
* parameters of a forward lookup operation. These parameters define at least:
* <ul>
* <li>the family type of the returned addresses</li>
* <li>the order in which a {@linkplain InetAddressResolver resolver}
* implementation should return its results</li>
* </ul>
*
* <p> To request addresses of specific family types the following bit masks can be combined:
* <ul>
* <li>{@link LookupPolicy#IPV4}: to request IPv4 addresses</li>
* <li>{@link LookupPolicy#IPV6}: to request IPv6 addresses</li>
* </ul>
* <br>It is an error if neither {@link LookupPolicy#IPV4} or {@link LookupPolicy#IPV6} are set.
*
* <p> To request a specific ordering of the results:
* <ul>
* <li>{@link LookupPolicy#IPV4_FIRST}: return IPv4 addresses before any IPv6 address</li>
* <li>{@link LookupPolicy#IPV6_FIRST}: return IPv6 addresses before any IPv4 address</li>
* </ul>
* <br>If neither {@link LookupPolicy#IPV4_FIRST} or {@link LookupPolicy#IPV6_FIRST} are set it
* implies <a href="{@docRoot}/java.base/java/net/doc-files/net-properties.html#Ipv4IPv6">"system"</a>
* order of addresses.
* It is an error to request both {@link LookupPolicy#IPV4_FIRST} and {@link LookupPolicy#IPV6_FIRST}.
*
* @param characteristics a value which represents the set of lookup characteristics
* @return an instance of {@code InetAddressResolver.LookupPolicy}
* @throws IllegalArgumentException if an illegal characteristics bit mask is provided
* @see InetAddressResolver#lookupByName(String, LookupPolicy)
*/
public static LookupPolicy of(int characteristics) {
// At least one type of addresses should be requested
if ((characteristics & IPV4) == 0 && (characteristics & IPV6) == 0) {
throw new IllegalArgumentException("No address type specified");
}
// Requested order of addresses couldn't be determined
if ((characteristics & IPV4_FIRST) != 0 && (characteristics & IPV6_FIRST) != 0) {
throw new IllegalArgumentException("Addresses order cannot be determined");
}
// If IPv4 addresses requested to be returned first then they should be requested too
if ((characteristics & IPV4_FIRST) != 0 && (characteristics & IPV4) == 0) {
throw new IllegalArgumentException("Addresses order and type do not match");
}
// If IPv6 addresses requested to be returned first then they should be requested too
if ((characteristics & IPV6_FIRST) != 0 && (characteristics & IPV6) == 0) {
throw new IllegalArgumentException("Addresses order and type do not match");
}
return new LookupPolicy(characteristics);
}
/**
* Returns the set of characteristics of this lookup policy.
*
* @return a characteristics value
* @see InetAddressResolver#lookupByName(String, LookupPolicy)
*/
public int characteristics() {
return characteristics;
}
}
}

View file

@ -0,0 +1,160 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package java.net.spi;
import sun.net.ResolverProviderConfiguration;
import java.net.InetAddress;
import java.util.ServiceLoader;
/**
* Service-provider class for {@linkplain InetAddressResolver InetAddress resolvers}.
*
* <p> A resolver provider is a factory for custom implementations of {@linkplain
* InetAddressResolver InetAddress resolvers}. A resolver defines operations for
* looking up (resolving) host names and IP addresses.
* <p>A resolver provider is a concrete subclass of this class that has a
* zero-argument constructor and implements the abstract methods specified below.
*
* <p> A given invocation of the Java virtual machine maintains a single
* system-wide resolver instance, which is used by
* <a href="{@docRoot}/java.base/java/net/InetAddress.html#host-name-resolution">
* InetAddress</a>. It is set after the VM is fully initialized and when an
* invocation of a method in {@link InetAddress} class triggers the first lookup
* operation.
*
* <p id="system-wide-resolver"> A resolver provider is located and loaded by
* {@link InetAddress} to create the system-wide resolver as follows:
* <ol>
* <li>The {@link ServiceLoader} mechanism is used to locate an
* {@code InetAddressResolverProvider} using the
* system class loader. The order in which providers are located is
* {@linkplain ServiceLoader#load(java.lang.Class, java.lang.ClassLoader)
* implementation specific}.
* The first provider found will be used to instantiate the
* {@link InetAddressResolver InetAddressResolver} by invoking the
* {@link InetAddressResolverProvider#get(InetAddressResolverProvider.Configuration)}
* method. The returned {@code InetAddressResolver} will be set as the
* system-wide resolver.
* <li>If the previous step fails to find any resolver provider the
* <a href="{@docRoot}/java.base/java/net/InetAddress.html#built-in-resolver">
* built-in resolver</a> will be set as the system-wide resolver.
* </ol>
*
* <p> If instantiating a custom resolver from a provider discovered in
* step 1 throws an error or exception, the system-wide resolver will not be
* set and the error or exception will be propagated to the calling thread.
* Otherwise, any lookup operation will be performed using the
* <i>system-wide resolver</i>.
*
* @implNote {@link InetAddress} will use the <i>built-in resolver</i> for any lookup operation
* that might occur before the VM is fully booted.
*
* @since 18
*/
public abstract class InetAddressResolverProvider {
/**
* Initialize and return an {@link InetAddressResolver} provided by
* this provider. This method is called by {@link InetAddress} when
* <a href="#system-wide-resolver">installing</a>
* the system-wide resolver implementation.
*
* <p> Any error or exception thrown by this method is considered as
* a failure of {@code InetAddressResolver} instantiation and will be propagated to
* the calling thread.
* @param configuration a {@link Configuration} instance containing platform built-in address
* resolution configuration.
* @return the resolver provided by this provider
*/
public abstract InetAddressResolver get(Configuration configuration);
/**
* {@return the name of this provider, or {@code null} if unnamed}
*/
public abstract String name();
/**
* The {@code RuntimePermission("inetAddressResolverProvider")} is
* necessary to subclass and instantiate the {@code InetAddressResolverProvider} class,
* as well as to obtain resolver from an instance of that class,
* and it is also required to obtain the operating system name resolution configurations.
*/
private static final RuntimePermission INET_ADDRESS_RESOLVER_PERMISSION =
new RuntimePermission("inetAddressResolverProvider");
/**
* Creates a new instance of {@code InetAddressResolverProvider}.
*
* @throws SecurityException if a security manager is present and its
* {@code checkPermission} method doesn't allow the
* {@code RuntimePermission("inetAddressResolverProvider")}.
* @implNote It is recommended that an {@code InetAddressResolverProvider} service
* implementation initialization should be as simple as possible, in order to avoid
* possible risks of deadlock or class loading cycles during the instantiation of the
* service provider.
*/
protected InetAddressResolverProvider() {
this(checkPermission());
}
private InetAddressResolverProvider(Void unused) {
}
@SuppressWarnings("removal")
private static Void checkPermission() {
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(INET_ADDRESS_RESOLVER_PERMISSION);
}
return null;
}
/**
* A {@code Configuration} object is supplied to the
* {@link InetAddressResolverProvider#get(Configuration)} method when
* setting the system-wide resolver.
* A resolver implementation can then delegate to the built-in resolver
* provided by this interface if it needs to.
*
* @since 18
*/
public sealed interface Configuration permits ResolverProviderConfiguration {
/**
* Returns the built-in {@linkplain InetAddressResolver resolver}.
*
* @return the JDK built-in resolver.
*/
InetAddressResolver builtinResolver();
/**
* Reads the localhost name from the system configuration.
*
* @return the localhost name.
*/
String lookupLocalHostName();
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2021, 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
@ -26,8 +26,8 @@
/**
* Service-provider classes for the {@link java.net} package.
*
* <p> Only developers who are defining new URL stream handler providers
* should need to make direct use of this package.
* <p> Only developers who are defining new URL stream handler providers or implementing
* a custom resolver provider should need to make direct use of this package.
*
* @since 9
*/