jdk/src/java.base/share/classes/java/net/InetAddress.java
changeset 36967 d041d2e80712
parent 33675 7d9d372a41df
child 37363 329dba26ffd2
--- a/jdk/src/java.base/share/classes/java/net/InetAddress.java	Sat Apr 09 20:12:13 2016 +0100
+++ b/jdk/src/java.base/share/classes/java/net/InetAddress.java	Mon Apr 11 03:00:50 2016 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -30,8 +30,10 @@
 import java.util.List;
 import java.util.ArrayList;
 import java.util.Objects;
-import java.util.ServiceLoader;
+import java.util.Scanner;
 import java.security.AccessController;
+import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.ObjectStreamException;
 import java.io.ObjectStreamField;
 import java.io.IOException;
@@ -49,7 +51,6 @@
 import sun.security.action.*;
 import sun.net.InetAddressCachePolicy;
 import sun.net.util.IPAddressUtil;
-import sun.net.spi.nameservice.*;
 
 /**
  * This class represents an Internet Protocol (IP) address.
@@ -207,6 +208,7 @@
     /* Specify address family preference */
     static transient boolean preferIPv6Address = false;
 
+
     static class InetAddressHolder {
         /**
          * Reserve the original application specified hostname.
@@ -279,7 +281,7 @@
     }
 
     /* Used to store the name service provider */
-    private static List<NameService> nameServices = null;
+    private static transient NameService nameService = null;
 
     /* Used to store the best available hostname */
     private transient String canonicalHostName = null;
@@ -623,7 +625,6 @@
      */
     private static String getHostFromNameService(InetAddress addr, boolean check) {
         String host = null;
-        for (NameService nameService : nameServices) {
             try {
                 // first lookup the hostname
                 host = nameService.getHostByAddr(addr.getAddress());
@@ -657,18 +658,12 @@
                     host = addr.getHostAddress();
                     return host;
                 }
-
-                break;
-
             } catch (SecurityException e) {
                 host = addr.getHostAddress();
-                break;
             } catch (UnknownHostException e) {
                 host = addr.getHostAddress();
                 // let next provider resolve the hostname
             }
-        }
-
         return host;
     }
 
@@ -860,88 +855,287 @@
         }
     }
 
-    static InetAddressImpl  impl;
+    /**
+     * 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;
 
-    private static NameService createNSProvider(String provider) {
-        if (provider == null)
-            return null;
+        /**
+         * 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;
 
-        NameService nameService = null;
-        if (provider.equals("default")) {
-            // initialize the default name service
-            nameService = new NameService() {
+    }
+
+    /**
+     * 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);
+
+                            }
+
+        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 final String hostsFile;
+
+        public HostsFileNameService (String hostsFileName) {
+            this.hostsFile = hostsFileName;
+        }
+
+        private  String addrToString(byte addr[]) {
+          String stringifiedAddress = null;
+
+            if (addr.length == Inet4Address.INADDRSZ) {
+                stringifiedAddress = Inet4Address.numericToTextFormat(addr);
+            } else { // treat as an IPV6 jobby
+                byte[] newAddr
+                    = IPAddressUtil.convertFromIPv4MappedAddress(addr);
+                if (newAddr != null) {
+                   stringifiedAddress = Inet4Address.numericToTextFormat(addr);
+                } else {
+                    stringifiedAddress = Inet6Address.numericToTextFormat(addr);
                 }
-            };
-        } else {
-            final String providerName = provider;
-            try {
-                nameService = java.security.AccessController.doPrivileged(
-                    new java.security.PrivilegedExceptionAction<>() {
-                        public NameService run() {
-                            Iterator<NameServiceDescriptor> itr =
-                                ServiceLoader.load(NameServiceDescriptor.class)
-                                    .iterator();
-                            while (itr.hasNext()) {
-                                NameServiceDescriptor nsd = itr.next();
-                                if (providerName.
-                                    equalsIgnoreCase(nsd.getType()+","
-                                        +nsd.getProviderName())) {
-                                    try {
-                                        return nsd.createNameService();
-                                    } catch (Exception e) {
-                                        e.printStackTrace();
-                                        System.err.println(
-                                            "Cannot create name service:"
-                                             +providerName+": " + e);
-                                    }
+            }
+            return stringifiedAddress;
+        }
+
+        /**
+         * 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;
+
+            String addrString = addrToString(addr);
+            try (Scanner hostsFileScanner = new Scanner(new File(hostsFile), "UTF-8")) {
+                while (hostsFileScanner.hasNextLine()) {
+                    hostEntry = hostsFileScanner.nextLine();
+                    if (!hostEntry.startsWith("#")) {
+                        hostEntry = removeComments(hostEntry);
+                        if (hostEntry.contains(addrString)) {
+                            host = extractHost(hostEntry, addrString);
+                            if (host != null) {
+                                break;
+                            }
+                        }
+                    }
+                }
+            } catch (FileNotFoundException e) {
+                throw new UnknownHostException("Unable to resolve address "
+                        + addrString + " as hosts file " + hostsFile
+                        + " not found ");
+            }
+
+            if ((host == null) || (host.equals("")) || (host.equals(" "))) {
+                throw new UnknownHostException("Requested address "
+                        + addrString
+                        + " resolves to an invalid entry in hosts file "
+                        + hostsFile);
+            }
+            return host;
+        }
+
+
+        /**
+         * <p>Lookup a host mapping by name. Retrieve the IP addresses
+         * associated with a host.
+         *
+         * <p>Search the configured hosts file for the addresses assocaited with
+         * 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 = null;
+            InetAddress[] res = null;
+            byte addr[] = new byte[4];
+            ArrayList<InetAddress> inetAddresses = null;
+
+            // lookup the file and create a list InetAddress for the specfied host
+            try (Scanner hostsFileScanner = new Scanner(new File(hostsFile), "UTF-8")) {
+                while (hostsFileScanner.hasNextLine()) {
+                    hostEntry = hostsFileScanner.nextLine();
+                    if (!hostEntry.startsWith("#")) {
+                        hostEntry = removeComments(hostEntry);
+                        if (hostEntry.contains(host)) {
+                            addrStr = extractHostAddr(hostEntry, host);
+                            if ((addrStr != null) && (!addrStr.equals(""))) {
+                                addr = createAddressByteArray(addrStr);
+                                if (inetAddresses == null) {
+                                    inetAddresses = new ArrayList<>(1);
+                                }
+                                if (addr != null) {
+                                    inetAddresses.add(InetAddress.getByAddress(host, addr));
                                 }
                             }
-
-                            return null;
                         }
                     }
-                );
-            } catch (java.security.PrivilegedActionException e) {
+                }
+            } catch (FileNotFoundException e) {
+                throw new UnknownHostException("Unable to resolve host " + host
+                        + " as hosts file " + hostsFile + " not found ");
+            }
+
+            if (inetAddresses != null) {
+                res = inetAddresses.toArray(new InetAddress[inetAddresses.size()]);
+            } else {
+                throw new UnknownHostException("Unable to resolve host " + host
+                        + " in hosts file " + hostsFile);
             }
+            return res;
+        }
+
+        private String removeComments(String hostsEntry) {
+            String filteredEntry = hostsEntry;
+            int hashIndex;
+
+            if ((hashIndex = hostsEntry.indexOf("#")) != -1) {
+                filteredEntry = hostsEntry.substring(0, hashIndex);
+            }
+            return filteredEntry;
+        }
+
+        private byte [] createAddressByteArray(String addrStr) {
+            byte[] addrArray;
+            // check if IPV4 address - most likely
+            addrArray = IPAddressUtil.textToNumericFormatV4(addrStr);
+            if (addrArray == null) {
+                addrArray = IPAddressUtil.textToNumericFormatV6(addrStr);
+            }
+            return addrArray;
         }
 
-        return nameService;
+        /** host to ip address mapping */
+        private String extractHostAddr(String hostEntry, String host) {
+            String[] mapping = hostEntry.split("\\s+");
+            String hostAddr = null;
+
+            if (mapping.length >= 2) {
+                // look at the host aliases
+                for (int i = 1; i < mapping.length; i++) {
+                    if (mapping[i].equalsIgnoreCase(host)) {
+                        hostAddr = mapping[0];
+                    }
+                }
+            }
+            return hostAddr;
+        }
+
+        /**
+         * IP Address to host mapping
+         * use first host alias in list
+         */
+        private String extractHost(String hostEntry, String addrString) {
+            String[] mapping = hostEntry.split("\\s+");
+            String host = null;
+
+            if (mapping.length >= 2) {
+                if (mapping[0].equalsIgnoreCase(addrString)) {
+                    host = mapping[1];
+                }
+            }
+            return host;
+        }
     }
 
+    static final InetAddressImpl  impl;
+
     static {
         // create the impl
         impl = InetAddressImplFactory.create();
 
-        // get name service if provided and requested
-        String provider = null;;
-        String propPrefix = "sun.net.spi.nameservice.provider.";
-        int n = 1;
-        nameServices = new ArrayList<>();
-        provider = AccessController.doPrivileged(
-                new GetPropertyAction(propPrefix + n));
-        while (provider != null) {
-            NameService ns = createNSProvider(provider);
-            if (ns != null)
-                nameServices.add(ns);
-
-            n++;
-            provider = AccessController.doPrivileged(
-                    new GetPropertyAction(propPrefix + n));
+        // create name service
+        nameService = createNameService();
         }
 
-        // if not designate any name services provider,
-        // create a default one
-        if (nameServices.size() == 0) {
-            NameService ns = createNSProvider("default");
-            nameServices.add(ns);
+    /**
+     * Create an instance of the NameService interface based on
+     * the setting of the {@codejdk.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 = AccessController
+                .doPrivileged(new GetPropertyAction("jdk.net.hosts.file"));
+        NameService theNameService;
+        if (hostsFileName != null) {
+            theNameService = new HostsFileNameService(hostsFileName);
+        } else {
+            theNameService = new PlatformNameService();
         }
+        return theNameService;
     }
 
     /**
@@ -1286,20 +1480,16 @@
         InetAddress[] addresses = null;
         UnknownHostException ex = null;
 
-        for (NameService nameService : nameServices) {
             try {
                 addresses = nameService.lookupAllHostAddr(host);
-                break;
             } catch (UnknownHostException uhe) {
                 if (host.equalsIgnoreCase("localhost")) {
                     addresses = new InetAddress[] { impl.loopbackAddress() };
-                    break;
                 }
                 else {
                     ex = uhe;
                 }
             }
-        }
 
         if (addresses == null) {
             throw ex == null ? new UnknownHostException(host) : ex;