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

*** 126,29 ***
   * For IPv4 address format, please refer to <A
   * HREF="Inet4Address.html#format">Inet4Address#format</A>; For IPv6
   * 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>
   *
!  * <h3> Host Name Resolution </h3>
   *
!  * 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> <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.
   *
!  * <h3> InetAddress Caching </h3>
   *
   * The InetAddress class has a cache to store successful as well as
   * unsuccessful host name resolutions.
   *
   * <p> By default, when a security manager is installed, in order to
--- 143,39 ---
   * For IPv4 address format, please refer to <A
   * HREF="Inet4Address.html#format">Inet4Address#format</A>; For IPv6
   * 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.
   *
!  * <h2 id="host-name-resolution"> Host Name Resolution </h2>
+  *
+  * <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 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.
+  *
+  * <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.
   *
   * <p> By default, when a security manager is installed, in order to

*** 196,14 ***
   * @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;
--- 223,10 ---

*** 212,13 ***
       * 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
--- 235,10 ---

*** 286,12 ***
  
      InetAddressHolder holder() {
          return holder;
      }
  
!     /* Used to store the name service provider */
!     private static transient NameService nameService;
  
      /**
       * Used to store the best available hostname.
       * Lazily initialized via a data race; safe because Strings are immutable.
       */
--- 306,15 ---
  
      InetAddressHolder holder() {
          return holder;
      }
  
!     /* 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.
       * Lazily initialized via a data race; safe because Strings are immutable.
       */

*** 299,40 ***
  
      /** use serialVersionUID from JDK 1.0.2 for interoperability */
      @java.io.Serial
      private static final long serialVersionUID = 3286316764910316507L;
  
      /*
       * 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;
-         }
          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) {
--- 322,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 {
!         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 int addressValue(Inet4Address inet4Address) {
                          return inet4Address.addressValue();
                      }
  
                      public byte[] addressBytes(Inet6Address inet6Address) {

*** 341,10 ***
--- 360,135 ---
                  }
          );
          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
       * the accept() method.  This InetAddress, however, is not
       * put in the address cache, since it is not created by name.

*** 553,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
       * is required, call
       * {@link #getCanonicalHostName() getCanonicalHostName}.
       *
       * <p>If there is a security manager, its
       * {@code checkConnect} method is first called
--- 697,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 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 ***
       * @param check make security check if true
       *
       * @see SecurityManager#checkConnect
       */
      private static String getHostFromNameService(InetAddress addr, boolean check) {
!         String host = null;
          try {
              // first lookup the hostname
!             host = nameService.getHostByAddr(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) {
--- 798,15 ---
       * @param check make security check if true
       *
       * @see SecurityManager#checkConnect
       */
      private static String getHostFromNameService(InetAddress addr, boolean check) {
!         String host;
+         var resolver = resolver();
          try {
              // first lookup the hostname
!             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
               */
              if (check) {

*** 689,15 ***
              //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) {
-             host = addr.getHostAddress();
-             // let next provider resolve the hostname
          }
          return host;
      }
  
      /**
--- 834,15 ---
              //XXX: if it looks like a spoof just return the address?
              if (!ok) {
                  host = addr.getHostAddress();
                  return host;
              }
!         // '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.
+         } catch (RuntimeException | UnknownHostException e) {
              host = addr.getHostAddress();
          }
          return host;
      }
  
      /**

*** 753,12 ***
      /**
       * 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.
       *
       * @return  a string representation of this IP address.
       */
      public String toString() {
          String hostName = holder().getHostName();
--- 898,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 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 ***
  
      // 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) {
              this.host = host;
-             this.reqAddr = reqAddr;
          }
  
          @Override
          public InetAddress[] get() throws UnknownHostException {
              Addresses addresses;
--- 965,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;
  
!         NameServiceAddresses(String host) {
              this.host = host;
          }
  
          @Override
          public InetAddress[] get() throws UnknownHostException {
              Addresses addresses;

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

*** 873,11 ***
                              cachePolicy != InetAddressCachePolicy.FOREVER) {
                              // schedule expiry
                              expirySet.add(cachedAddresses);
                          }
                      }
!                     if (inetAddresses == null) {
                          throw ex == null ? new UnknownHostException(host) : ex;
                      }
                      return inetAddresses;
                  }
                  // else addresses != this
--- 1017,11 ---
                              cachePolicy != InetAddressCachePolicy.FOREVER) {
                              // schedule expiry
                              expirySet.add(cachedAddresses);
                          }
                      }
!                     if (inetAddresses == null || inetAddresses.length == 0) {
                          throw ex == null ? new UnknownHostException(host) : ex;
                      }
                      return inetAddresses;
                  }
                  // else addresses != this

*** 887,106 ***
              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
       * OS network libraries to resolve host address mappings.
       *
       * @since 9
       */
!     private static final class PlatformNameService implements NameService {
  
!         public InetAddress[] lookupAllHostAddr(String host)
!             throws UnknownHostException
!         {
!             return impl.lookupAllHostAddr(host);
          }
  
!         public String getHostByAddr(byte[] addr)
!             throws UnknownHostException
!         {
              return impl.getHostByAddr(addr);
          }
      }
  
      /**
!      * The HostsFileNameService 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
       * 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 final String hostsFile;
  
!         public HostsFileNameService(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
           */
          @Override
!         public String getHostByAddr(byte[] addr) throws UnknownHostException {
              String hostEntry;
              String host = null;
  
              try (Scanner hostsFileScanner = new Scanner(new File(hostsFile),
!                                                         UTF_8.INSTANCE))
-             {
                  while (hostsFileScanner.hasNextLine()) {
                      hostEntry = hostsFileScanner.nextLine();
                      if (!hostEntry.startsWith("#")) {
                          hostEntry = removeComments(hostEntry);
                          String[] mapping = hostEntry.split("\\s+");
--- 1031,77 ---
              return addresses.get();
          }
      }
  
      /**
!      * The default InetAddressResolver implementation, which delegates to the underlying
       * OS network libraries to resolve host address mappings.
       *
       * @since 9
       */
!     private static final class PlatformResolver implements InetAddressResolver {
  
!         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 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 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 InetAddressResolver
       * implementation
       *
       * @since 9
       */
!     private static final class HostsFileResolver implements InetAddressResolver {
  
          private final String hostsFile;
  
!         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 IllegalArgumentException if IP address is of illegal length
           */
          @Override
!         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)) {
                  while (hostsFileScanner.hasNextLine()) {
                      hostEntry = hostsFileScanner.nextLine();
                      if (!hostEntry.startsWith("#")) {
                          hostEntry = removeComments(hostEntry);
                          String[] mapping = hostEntry.split("\\s+");

*** 1018,26 ***
           *
           * <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
           * @throws UnknownHostException
           *             if no IP address for the {@code host} could be found
           */
!         public InetAddress[] lookupAllHostAddr(String host)
                  throws UnknownHostException {
              String hostEntry;
              String addrStr;
              byte addr[];
              List<InetAddress> inetAddresses = new ArrayList<>();
              List<InetAddress> inet4Addresses = new ArrayList<>();
              List<InetAddress> inet6Addresses = new ArrayList<>();
  
              // lookup the file and create a list InetAddress for the specified host
              try (Scanner hostsFileScanner = new Scanner(new File(hostsFile),
!                                                         UTF_8.INSTANCE)) {
                  while (hostsFileScanner.hasNextLine()) {
                      hostEntry = hostsFileScanner.nextLine();
                      if (!hostEntry.startsWith("#")) {
                          hostEntry = removeComments(hostEntry);
                          if (hostEntry.contains(host)) {
--- 1133,34 ---
           *
           * <p>Search the configured hosts file for the addresses associated
           * with the specified host name.
           *
           * @param host the specified hostname
!          * @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 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)) {
                  while (hostsFileScanner.hasNextLine()) {
                      hostEntry = hostsFileScanner.nextLine();
                      if (!hostEntry.startsWith("#")) {
                          hostEntry = removeComments(hostEntry);
                          if (hostEntry.contains(host)) {

*** 1045,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) {
                                          inet4Addresses.add(address);
                                      }
!                                     if (address instanceof Inet6Address) {
                                          inet6Addresses.add(address);
                                      }
                                  }
                              }
                          }
--- 1168,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 && needIPv4) {
                                          inet4Addresses.add(address);
                                      }
!                                     if (address instanceof Inet6Address && needIPv6) {
                                          inet6Addresses.add(address);
                                      }
                                  }
                              }
                          }

*** 1060,37 ***
                  }
              } 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;
-                 };
              }
! 
!             if (res.isEmpty()) {
!                 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;
          }
  
          private String removeComments(String hostsEntry) {
              String filteredEntry = hostsEntry;
              int hashIndex;
--- 1183,42 ---
                  }
              } catch (IOException e) {
                  throw new UnknownHostException("Unable to resolve host " + host
                          + " as hosts file " + hostsFile + " not found ");
              }
!             // Check if only IPv4 addresses are requested
!             if (needIPv4 && !needIPv6) {
!                 checkResultsList(inet4Addresses, host);
!                 return inet4Addresses.stream();
              }
!             // Check if only IPv6 addresses are requested
!             if (!needIPv4 && needIPv6) {
!                 checkResultsList(inet6Addresses, host);
!                 return inet6Addresses.stream();
              }
!             // 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();
          }
  
!         // 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) {
              String filteredEntry = hostsEntry;
              int hashIndex;

*** 1128,49 ***
          }
      }
  
      static final InetAddressImpl  impl;
  
      static {
          // create the impl
          impl = InetAddressImplFactory.create();
  
!         // create name service
!         nameService = createNameService();
      }
  
      /**
!      * Create an instance of the NameService interface based on
       * the setting of the {@code jdk.net.hosts.file} system property.
       *
!      * <p>The default NameService is the PlatformNameService, 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}
       * 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
       */
!     private static NameService createNameService() {
! 
!         String hostsFileName =
!                 GetPropertyAction.privilegedGetProperty("jdk.net.hosts.file");
-         NameService theNameService;
-         if (hostsFileName != null) {
-             theNameService = new HostsFileNameService(hostsFileName);
          } else {
!             theNameService = new PlatformNameService();
          }
!         return theNameService;
      }
  
      /**
       * Creates an InetAddress based on the provided host name and IP address.
!      * No name service is checked for 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.
--- 1256,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();
  
!         // impl must be initialized before calling this method
!         PLATFORM_LOOKUP_POLICY = initializePlatformLookupPolicy();
+ 
+         // create built-in resolver
+         BUILTIN_RESOLVER = createBuiltinInetAddressResolver();
      }
  
      /**
!      * Create an instance of the InetAddressResolver interface based on
       * the setting of the {@code jdk.net.hosts.file} system property.
       *
!      * <p>The default InetAddressResolver is the PlatformResolver, which typically
       * delegates name and address resolution calls to the underlying
       * OS network libraries.
       *
!      * <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 an InetAddressResolver
       */
!     private static InetAddressResolver createBuiltinInetAddressResolver() {
!         InetAddressResolver theResolver;
!         if (HOSTS_FILE_NAME != null) {
!             theResolver = new HostsFileResolver(HOSTS_FILE_NAME);
          } else {
!             theResolver = new PlatformResolver();
          }
!         return theResolver;
      }
  
      /**
       * Creates an InetAddress based on the provided host name and IP 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 ***
      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.
       *
       * <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.
--- 1384,13 ---
      public static InetAddress getByName(String host)
          throws UnknownHostException {
          return InetAddress.getAllByName(host)[0];
      }
  
      /**
       * Given the name of a host, returns an array of its IP addresses,
!      * 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 ***
       *
       * @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;
--- 1425,10 ---

*** 1362,11 ***
              }
          } else if (ipv6Expected) {
              // We were expecting an IPv6 Literal, but got something else
              throw new UnknownHostException("["+host+"]");
          }
!         return getAllByName0(host, reqAddr, true, true);
      }
  
      /**
       * Returns the loopback address.
       * <p>
--- 1486,11 ---
              }
          } else if (ipv6Expected) {
              // We were expecting an IPv6 Literal, but got something else
              throw new UnknownHostException("["+host+"]");
          }
!         return getAllByName0(host, true, true);
      }
  
      /**
       * Returns the loopback address.
       * <p>

*** 1412,37 ***
              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);
      }
  
      /**
       * 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 */
--- 1536,29 ---
              zone = (zone * 10) + digit;
          }
          return zone;
      }
  
      /**
       * package private so SocketPermission can call it
       */
      static InetAddress[] getAllByName0 (String host, boolean check)
          throws UnknownHostException  {
!         return getAllByName0(host, check, true);
      }
  
      /**
       * Designated lookup method.
       *
       * @param host host name to look up
       * @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,
                                                 boolean check,
                                                 boolean useCache)
          throws UnknownHostException  {
  
          /* If it gets here it is presumed to be a hostname */

*** 1496,71 ***
          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)
              );
              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)
              throws UnknownHostException {
!         InetAddress[] addresses = null;
          UnknownHostException ex = null;
  
          try {
!             addresses = nameService.lookupAllHostAddr(host);
!         } catch (UnknownHostException uhe) {
              if (host.equalsIgnoreCase("localhost")) {
!                 addresses = new InetAddress[]{impl.loopbackAddress()};
!             } else {
                  ex = uhe;
              }
          }
! 
!         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;
      }
  
      /**
       * 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> 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
--- 1612,53 ---
          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)
              );
              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)
              throws UnknownHostException {
!         Stream<InetAddress> addresses = null;
          UnknownHostException ex = null;
  
+         var resolver = resolver();
          try {
!             addresses = resolver.lookupByName(host, PLATFORM_LOOKUP_POLICY);
!         } catch (RuntimeException | UnknownHostException x) {
              if (host.equalsIgnoreCase("localhost")) {
!                 addresses = Stream.of(impl.loopbackAddress());
!             } else if (x instanceof UnknownHostException uhe) {
                  ex = uhe;
+             } else {
+                 ex = new UnknownHostException();
+                 ex.initCause(x);
              }
          }
!         InetAddress[] result = addresses == null ? null
!                 : addresses.toArray(InetAddress[]::new);
+         if (result == null || result.length == 0) {
              throw ex == null ? new UnknownHostException(host) : ex;
          }
!         return result;
      }
  
      /**
       * 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 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 ***
                  localAddr = impl.loopbackAddress();
              } else {
                  // call getAllByName0 without security checks and
                  // without using cached data
                  try {
!                     localAddr = getAllByName0(local, null, false, false)[0];
                  } catch (UnknownHostException uhe) {
                      // Rethrow with a more informative error message.
                      UnknownHostException uhe2 =
                          new UnknownHostException(local + ": " +
                                                   uhe.getMessage());
--- 1733,11 ---
                  localAddr = impl.loopbackAddress();
              } else {
                  // call getAllByName0 without security checks and
                  // without using cached data
                  try {
!                     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 >