< prev index next >

src/java.base/share/classes/java/net/InetAddress.java

Print this page
@@ -23,10 +23,15 @@
   * questions.
   */
  
  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;
  import java.util.Objects;
  import java.util.Scanner;

@@ -38,23 +43,34 @@
  import java.io.ObjectInputStream;
  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 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.
   *
   * <p> An IP address is either a 32-bit or 128-bit unsigned number
   * used by IP, a lower-level protocol on which protocols like UDP and

@@ -186,24 +202,59 @@
   * A value of 0 indicates "never cache".
   * A value of -1 indicates "cache forever".
   * </dd>
   * </dl>
   *
+  * <h3 id="resolverProviders"> InetAddress Resolver Providers </h3>
+  *
+  * <p> Host name resolution and reverse name resolution operations are delegated to a
+  * {@linkplain InetAddressResolver resolver}. Lookup operations performed by
+  * this class use the <i>system-wide resolver</i>. The system-wide resolver
+  * is set once, lazily, after the VM is fully initialized and when
+  * an invocation of a method in this class triggers the first lookup operation.
+  *
+  * <p> A <i>custom resolver</i> can be installed as the system-wide resolver
+  * by deploying a {@linkplain InetAddressResolverProvider resolver provider}.
+  * A resolver provider is essentially a factory for resolvers, and is used
+  * to instantiate a custom resolver. If no resolver provider
+  * is found, then the <i>built-in resolver</i> will be set as the
+  * system-wide resolver.
+  *
+  * <p> A custom resolver is found and installed as the system-wide resolver
+  * as follows:
+  * <ol>
+  *  <li>The {@link ServiceLoader} mechanism is used to locate an
+  *      {@link InetAddressResolverProvider 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 instantiated {@code InetAddressResolver} will be installed as the system-wide
+  *      resolver.
+  *  <li>If the previous step fails to find any resolver provider the
+  *      built-in resolver 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
+  * installed and the error or exception will be propagated to the calling thread.
+  * Otherwise, any lookup operation will be performed through the installed
+  * <i>system-wide resolver</i>.
+  * @implNote
+  * For any lookup operation that might occur before the VM is fully booted the <i>built-in
+  * resolver</i> will be used.
+  *
   * @author  Chris Warth
   * @see     java.net.InetAddress#getByAddress(byte[])
   * @see     java.net.InetAddress#getByAddress(java.lang.String, byte[])
   * @see     java.net.InetAddress#getAllByName(java.lang.String)
   * @see     java.net.InetAddress#getByName(java.lang.String)
   * @see     java.net.InetAddress#getLocalHost()
   * @since 1.0
   */
  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
       */
      @Native static final int IPv4 = 1;

@@ -212,13 +263,10 @@
       * Specify the address family: Internet Protocol, Version 6
       * @since 1.4
       */
      @Native static final int IPv6 = 2;
  
-     /* Specify address family preference */
-     static transient final int preferIPv6Address;
- 
      static class InetAddressHolder {
          /**
           * Reserve the original application specified hostname.
           *
           * The original hostname is useful for domain-based endpoint

@@ -286,12 +334,14 @@
  
      InetAddressHolder holder() {
          return holder;
      }
  
-     /* Used to store the name service provider */
-     private static transient NameService nameService;
+     /* Used to store the system-wide resolver */
+     private static volatile InetAddressResolver resolver;
+ 
+     private static final InetAddressResolver BUILTIN_RESOLVER;
  
      /**
       * Used to store the best available hostname.
       * Lazily initialized via a data race; safe because Strings are immutable.
       */

@@ -299,40 +349,36 @@
  
      /** use serialVersionUID from JDK 1.0.2 for interoperability */
      @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() {
                      public String getOriginalHostName(InetAddress ia) {
                          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();
                      }
  
                      public byte[] addressBytes(Inet6Address inet6Address) {

@@ -341,10 +387,129 @@
                  }
          );
          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();
+             try {
+                 cns = resolver;
+                 if (cns != null) {
+                     return cns;
+                 }
+                 // Protection against provider calling InetAddress APIs during initialization
+                 if (bootstrapResolver != null) {
+                     return bootstrapResolver;
+                 } else {
+                     bootstrapResolver = BUILTIN_RESOLVER;
+                 }
+                 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 {
+                 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
       * the accept() method.  This InetAddress, however, is not
       * put in the address cache, since it is not created by name.

@@ -553,11 +718,11 @@
       *
       * <p>If this InetAddress was created with a host name,
       * 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}.
       *
       * <p>If there is a security manager, its
       * {@code checkConnect} method is first called

@@ -654,14 +819,15 @@
       * @param check make security check if true
       *
       * @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.lookupHostName(addr.getAddress());
  
              /* check to see if calling code is allowed to know
               * the hostname for this IP address, ie, connect to the host
               */
              if (check) {

@@ -689,15 +855,12 @@
              //XXX: if it looks like a spoof just return the address?
              if (!ok) {
                  host = addr.getHostAddress();
                  return host;
              }
-         } catch (SecurityException e) {
-             host = addr.getHostAddress();
-         } catch (UnknownHostException e) {
+         } catch (RuntimeException | UnknownHostException e) {
              host = addr.getHostAddress();
-             // let next provider resolve the hostname
          }
          return host;
      }
  
      /**

@@ -753,12 +916,13 @@
      /**
       * Converts this IP address to a {@code String}. The
       * 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.
       */
      public String toString() {
          String hostName = holder().getHostName();

@@ -819,15 +983,13 @@
  
      // a name service lookup based Addresses implementation which replaces itself
      // 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
          public InetAddress[] get() throws UnknownHostException {
              Addresses addresses;

@@ -847,11 +1009,11 @@
                      // lookup name services
                      InetAddress[] inetAddresses;
                      UnknownHostException ex;
                      int cachePolicy;
                      try {
-                         inetAddresses = getAddressesFromNameService(host, reqAddr);
+                         inetAddresses = getAddressesFromNameService(host);
                          ex = null;
                          cachePolicy = InetAddressCachePolicy.get();
                      } catch (UnknownHostException uhe) {
                          inetAddresses = null;
                          ex = uhe;

@@ -887,106 +1049,75 @@
              return addresses.get();
          }
      }
  
      /**
-      * 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> lookupAddresses(String host, LookupPolicy policy)
+                 throws UnknownHostException {
+             Objects.requireNonNull(host);
+             return Arrays.stream(impl.lookupAllHostAddr(host, policy));
          }
  
-         public String getHostByAddr(byte[] addr)
-             throws UnknownHostException
-         {
+         public String lookupHostName(byte[] addr)
+                 throws UnknownHostException {
+             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;
          }
  
          /**
           * Lookup the host name  corresponding to the IP address provided.
           * Search the configured host file a host name corresponding to
           * the specified IP address.
           *
           * @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
           */
          @Override
-         public String getHostByAddr(byte[] addr) throws UnknownHostException {
+         public String lookupHostName(byte[] addr) throws UnknownHostException {
              String hostEntry;
              String host = null;
  
+             // 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("#")) {
                          hostEntry = removeComments(hostEntry);
                          String[] mapping = hostEntry.split("\\s+");

@@ -1018,26 +1149,33 @@
           *
           * <p>Search the configured hosts file for the addresses associated
           * 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 UnknownHostException
           *             if no IP address for the {@code host} could be found
           */
-         public InetAddress[] lookupAllHostAddr(String host)
+         public Stream<InetAddress> lookupAddresses(String host, LookupPolicy lookupPolicy)
                  throws UnknownHostException {
              String hostEntry;
              String addrStr;
              byte addr[];
              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;
+ 
+             Objects.requireNonNull(host);
  
              // 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("#")) {
                          hostEntry = removeComments(hostEntry);
                          if (hostEntry.contains(host)) {

@@ -1045,14 +1183,14 @@
                              if ((addrStr != null) && (!addrStr.isEmpty())) {
                                  addr = createAddressByteArray(addrStr);
                                  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);
                                      }
                                  }
                              }
                          }

@@ -1060,37 +1198,42 @@
                  }
              } catch (IOException e) {
                  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 number of found addresses:
+             // If none found - throw an exception
+             boolean noAddressFound = inetAddresses.isEmpty();
+             // needIPv4 == false and needIPv6 is not a valid combination. See LookupPolicy.of.
+             if (needIPv4 != needIPv6) {
+                 if (needIPv4) {
+                     noAddressFound = inet4Addresses.isEmpty();
+                 } else {
+                     noAddressFound = inet6Addresses.isEmpty();
+                 }
              }
- 
-             if (res.isEmpty()) {
+             if (noAddressFound) {
                  throw new UnknownHostException("Unable to resolve host " + host
                          + " in hosts file " + hostsFile);
              }
-             return res.toArray(EMPTY_ARRAY);
-         }
  
-         private static List<InetAddress> concatAddresses(List<InetAddress> firstPart,
-                                                          List<InetAddress> secondPart) {
-             List<InetAddress> result = new ArrayList<>(firstPart);
-             result.addAll(secondPart);
-             return result;
+             // If both address types are requested
+             if (needIPv4 == needIPv6) {
+                 if (systemAddressesOrder(flags)) {
+                     return inetAddresses.stream();
+                 } else if (ipv6AddressesFirst(flags)) {
+                     return Stream.concat(inet6Addresses.stream(), inet4Addresses.stream());
+                 } else if (ipv4AddressesFirst(flags)) {
+                     return Stream.concat(inet4Addresses.stream(), inet6Addresses.stream());
+                 }
+             }
+             // Only IPv4 addresses are requested
+             if (needIPv4) {
+                 return inet4Addresses.stream();
+             }
+             // Only IPv6 addresses are requested
+             return inet6Addresses.stream();
          }
  
          private String removeComments(String hostsEntry) {
              String filteredEntry = hostsEntry;
              int hashIndex;

@@ -1128,49 +1271,56 @@
          }
      }
  
      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.
+      * System {@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
       * address.
       * <p> No validity checking is done on the host name either.

@@ -1249,19 +1399,13 @@
      public static InetAddress getByName(String host)
          throws UnknownHostException {
          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
       * address. If a literal IP address is supplied, only the
       * validity of the address format is checked.

@@ -1296,15 +1440,10 @@
       *
       * @see SecurityManager#checkConnect
       */
      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];
              ret[0] = impl.loopbackAddress();
              return ret;

@@ -1362,11 +1501,11 @@
              }
          } else if (ipv6Expected) {
              // 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);
      }
  
      /**
       * Returns the loopback address.
       * <p>

@@ -1412,37 +1551,29 @@
              zone = (zone * 10) + digit;
          }
          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)
       * @return array of InetAddress(es)
       * @throws UnknownHostException if host name is not found
       */
      private static InetAddress[] getAllByName0(String host,
-                                                InetAddress reqAddr,
                                                 boolean check,
                                                 boolean useCache)
          throws UnknownHostException  {
  
          /* If it gets here it is presumed to be a hostname */

@@ -1496,71 +1627,52 @@
          if (addrs == null) {
              // create a NameServiceAddresses instance which will look up
              // 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;
              }
          }
  
          // ask Addresses to get an array of InetAddress(es) and clone it
          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.lookupAddresses(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) {
              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 addresses.toArray(InetAddress[]::new);
      }
  
      /**
       * Returns an {@code InetAddress} object given the raw IP address .
       * 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
       *
       * @param addr the raw IP address in network byte order

@@ -1635,11 +1747,11 @@
                  localAddr = impl.loopbackAddress();
              } else {
                  // 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 =
                          new UnknownHostException(local + ": " +
                                                   uhe.getMessage());
< prev index next >