diff --git a/src/java.base/share/classes/java/lang/RuntimePermission.java b/src/java.base/share/classes/java/lang/RuntimePermission.java index db985101f7c..7df4c5fac5b 100644 --- a/src/java.base/share/classes/java/lang/RuntimePermission.java +++ b/src/java.base/share/classes/java/lang/RuntimePermission.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -378,6 +378,16 @@ import java.lang.module.ModuleFinder; * {@linkplain ModuleFinder#ofSystem system modules} in the runtime image. * * + * + * inetAddressResolverProvider + * This {@code RuntimePermission} is required to be granted to + * classes which subclass and implement {@code java.net.spi.InetAddressResolverProvider}. + * The permission is checked during invocation of the abstract base class constructor. + * This permission ensures trust in classes which provide resolvers used by + * {@link java.net.InetAddress} hostname and address resolution methods. + * See {@link java.net.spi.InetAddressResolverProvider} for more information. + * + * * * * diff --git a/src/java.base/share/classes/java/net/Inet4AddressImpl.java b/src/java.base/share/classes/java/net/Inet4AddressImpl.java index 4cbe28f998b..c7feaf4c195 100644 --- a/src/java.base/share/classes/java/net/Inet4AddressImpl.java +++ b/src/java.base/share/classes/java/net/Inet4AddressImpl.java @@ -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; diff --git a/src/java.base/share/classes/java/net/Inet6AddressImpl.java b/src/java.base/share/classes/java/net/Inet6AddressImpl.java index f956a50e311..87fefbe37ba 100644 --- a/src/java.base/share/classes/java/net/Inet6AddressImpl.java +++ b/src/java.base/share/classes/java/net/Inet6AddressImpl.java @@ -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; diff --git a/src/java.base/share/classes/java/net/InetAddress.java b/src/java.base/share/classes/java/net/InetAddress.java index 38e7fc62921..3875216768b 100644 --- a/src/java.base/share/classes/java/net/InetAddress.java +++ b/src/java.base/share/classes/java/net/InetAddress.java @@ -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 Inet6Address#format. * - *

There is a couple of - * System Properties affecting how IPv4 and IPv6 addresses are used.

+ *

There is a couple of + * System Properties affecting how IPv4 and IPv6 addresses are used. * - *

Host Name Resolution

+ *

Host Name Resolution

* - * Host name-to-IP address resolution 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. + *

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}. + * + *

Host name-to-IP address resolution maps a host name to an IP address. + * For any host name, its corresponding IP address is returned. * *

Reverse name resolution means that for any IP address, * the host associated with the IP address is returned. * - *

The InetAddress class provides methods to resolve host names to - * their IP addresses and vice versa. + *

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. * - *

InetAddress Caching

+ *

{@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. + * + *

InetAddress Caching

* * 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 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 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 * *

The file format is that which corresponds with the /etc/hosts file * IP Address host alias list. * - *

When the file lookup is enabled it replaces the default NameService + *

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 lookupByName(String host, LookupPolicy lookupPolicy) throws UnknownHostException { String hostEntry; String addrStr; byte addr[]; + + Objects.requireNonNull(host); + Objects.requireNonNull(lookupPolicy); List inetAddresses = new ArrayList<>(); List inet4Addresses = new ArrayList<>(); List 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 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 concatAddresses(List firstPart, - List secondPart) { - List 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 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. * - *

The default NameService is the PlatformNameService, which typically + *

The default InetAddressResolver is the PlatformResolver, which typically * delegates name and address resolution calls to the underlying * OS network libraries. * - *

A HostsFileNameService is created if the {@code jdk.net.hosts.file} + *

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. * *

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}. * *

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 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]}. * - *

This method doesn't block, i.e. no reverse name service lookup - * is performed. + *

This method doesn't block, i.e. no reverse lookup is performed. * *

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 = diff --git a/src/java.base/share/classes/java/net/InetAddressImpl.java b/src/java.base/share/classes/java/net/InetAddressImpl.java index a2f8ea01052..f0364ffaedf 100644 --- a/src/java.base/share/classes/java/net/InetAddressImpl.java +++ b/src/java.base/share/classes/java/net/InetAddressImpl.java @@ -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(); diff --git a/src/java.base/share/classes/java/net/doc-files/net-properties.html b/src/java.base/share/classes/java/net/doc-files/net-properties.html index ea0311b7161..0c2d3e232da 100644 --- a/src/java.base/share/classes/java/net/doc-files/net-properties.html +++ b/src/java.base/share/classes/java/net/doc-files/net-properties.html @@ -1,6 +1,6 @@