8031753: JMXServiceURL should not use getLocalHost or its usage should be enhanced
authorhb
Mon, 21 Mar 2016 20:39:54 -0700
changeset 37318 fee950a46e67
parent 37317 6c2ff92720da
child 37319 98886757c5aa
8031753: JMXServiceURL should not use getLocalHost or its usage should be enhanced Summary: JMXServiceURL should not use getLocalHost or its usage should be enhanced Reviewed-by: jbachorik
jdk/src/java.management/share/classes/javax/management/remote/JMXServiceURL.java
--- a/jdk/src/java.management/share/classes/javax/management/remote/JMXServiceURL.java	Sat Mar 19 01:23:44 2016 +0100
+++ b/jdk/src/java.management/share/classes/javax/management/remote/JMXServiceURL.java	Mon Mar 21 20:39:54 2016 -0700
@@ -34,10 +34,15 @@
 import java.io.ObjectInputStream;
 
 import java.io.Serializable;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.MalformedURLException;
+import java.net.NetworkInterface;
+import java.net.SocketException;
 import java.net.UnknownHostException;
 import java.util.BitSet;
+import java.util.Enumeration;
 import java.util.Locale;
 import java.util.StringTokenizer;
 
@@ -236,10 +241,13 @@
      * @param protocol the protocol part of the URL.  If null, defaults
      * to <code>jmxmp</code>.
      *
-     * @param host the host part of the URL.  If null, defaults to the
-     * local host name, as determined by
-     * <code>InetAddress.getLocalHost().getHostName()</code>.  If it
-     * is a numeric IPv6 address, it can optionally be enclosed in
+     * @param host the host part of the URL. If host is null and if
+     * local host name can be resolved to an IP, then host defaults
+     * to local host name as determined by
+     * <code>InetAddress.getLocalHost().getHostName()</code>. If host is null
+     * and if local host name cannot be resolved to an IP, then host
+     * defaults to numeric IP address of one of the active network interfaces.
+     * If host is a numeric IPv6 address, it can optionally be enclosed in
      * square brackets <code>[]</code>.
      *
      * @param port the port part of the URL.
@@ -260,10 +268,13 @@
      * @param protocol the protocol part of the URL.  If null, defaults
      * to <code>jmxmp</code>.
      *
-     * @param host the host part of the URL.  If null, defaults to the
-     * local host name, as determined by
-     * <code>InetAddress.getLocalHost().getHostName()</code>.  If it
-     * is a numeric IPv6 address, it can optionally be enclosed in
+     * @param host the host part of the URL. If host is null and if
+     * local host name can be resolved to an IP, then host defaults
+     * to local host name as determined by
+     * <code>InetAddress.getLocalHost().getHostName()</code>. If host is null
+     * and if local host name cannot be resolved to an IP, then host
+     * defaults to numeric IP address of one of the active network interfaces.
+     * If host is a numeric IPv6 address, it can optionally be enclosed in
      * square brackets <code>[]</code>.
      *
      * @param port the port part of the URL.
@@ -286,32 +297,45 @@
             InetAddress local;
             try {
                 local = InetAddress.getLocalHost();
-            } catch (UnknownHostException e) {
-                throw new MalformedURLException("Local host name unknown: " +
-                                                e);
-            }
-
-            host = local.getHostName();
+                host = local.getHostName();
 
-            /* We might have a hostname that violates DNS naming
-               rules, for example that contains an `_'.  While we
-               could be strict and throw an exception, this is rather
-               user-hostile.  Instead we use its numerical IP address.
-               We can only reasonably do this for the host==null case.
-               If we're given an explicit host name that is illegal we
-               have to reject it.  (Bug 5057532.)  */
-            try {
-                validateHost(host, port);
-            } catch (MalformedURLException e) {
-                if (logger.fineOn()) {
+                /* We might have a hostname that violates DNS naming
+                rules, for example that contains an `_'.  While we
+                could be strict and throw an exception, this is rather
+                user-hostile.  Instead we use its numerical IP address.
+                We can only reasonably do this for the host==null case.
+                If we're given an explicit host name that is illegal we
+                have to reject it.  (Bug 5057532.)  */
+                try {
+                    validateHost(host, port);
+                } catch (MalformedURLException e) {
+                   if (logger.fineOn()) {
                     logger.fine("JMXServiceURL",
                                 "Replacing illegal local host name " +
                                 host + " with numeric IP address " +
                                 "(see RFC 1034)", e);
                 }
                 host = local.getHostAddress();
-                /* Use the numeric address, which could be either IPv4
-                   or IPv6.  validateHost will accept either.  */
+                }
+            } catch (UnknownHostException e) {
+                try {
+                    /*
+                    If hostname cannot be resolved, we will try and use numeric
+                    IPv4/IPv6 address. If host=null while starting agent,
+                    we know that it will be started on all interfaces - 0.0.0.0.
+                    Hence we will use IP address of first active non-loopback
+                    interface
+                    */
+                    host = getActiveNetworkInterfaceIP();
+                    if (host == null) {
+                        throw new MalformedURLException("Unable"
+                                + " to resolve hostname or "
+                                + "get valid IP address");
+                    }
+                } catch (SocketException ex) {
+                    throw new MalformedURLException("Unable"
+                            + " to resolve hostname or get valid IP address");
+                }
             }
         }
 
@@ -340,6 +364,33 @@
         validate();
     }
 
+    private String getActiveNetworkInterfaceIP() throws SocketException {
+        Enumeration<NetworkInterface>
+                networkInterface = NetworkInterface.getNetworkInterfaces();
+        String ipv6AddrStr = null;
+        while (networkInterface.hasMoreElements()) {
+            NetworkInterface nic = networkInterface.nextElement();
+            if (nic.isUp() && !nic.isLoopback()) {
+                Enumeration<InetAddress> inet = nic.getInetAddresses();
+                while (inet.hasMoreElements()) {
+                    InetAddress addr = inet.nextElement();
+                    if (addr instanceof Inet4Address
+                            && !addr.isLinkLocalAddress()) {
+                        return addr.getHostAddress();
+                    }else if (addr instanceof Inet6Address
+                            && !addr.isLinkLocalAddress()) {
+                        /*
+                        We save last seen IPv6 address which we will return
+                        if we do not find any interface with IPv4 address.
+                        */
+                        ipv6AddrStr = addr.getHostAddress();
+                    }
+                }
+            }
+        }
+        return ipv6AddrStr;
+    }
+
     private static final String INVALID_INSTANCE_MSG =
             "Trying to deserialize an invalid instance of JMXServiceURL";
     private void readObject(ObjectInputStream  inputStream) throws IOException, ClassNotFoundException {
@@ -540,7 +591,9 @@
      * constructor that takes a separate host parameter, the result is
      * the string that was specified.  If that string was null, the
      * result is
-     * <code>InetAddress.getLocalHost().getHostName()</code>.</p>
+     * <code>InetAddress.getLocalHost().getHostName()</code> if local host name
+     * can be resolved to an IP. Else numeric IP address of an active
+     * network interface will be used.</p>
      *
      * <p>In either case, if the host was specified using the
      * <code>[...]</code> syntax for numeric IPv6 addresses, the