dev@glassfish.java.net

proposed patch for appserv-commons NetUtils.java

From: Soon Leong <Soon-Leong.Yap_at_Sun.COM>
Date: Tue, 04 Oct 2005 13:06:48 +0800

The current implementation for isPortFree() method in NetUtils does not
determine correctly that a specific port is in use on a machine. Also,
it was found that on machines with a client firewall, putting in rules
to unblock the port or even disabling the firewall will not work.

One area in glassfish that is affected by this problem is the setup.xml,
which is used to setup glassfish binary. The following message is displayed.

[exec] CLI130 Could not create domain, domain1
[exec] Port 8080 is in use.

The proposed patch for isPortFree() method will be able to determine the
correct status on whether a specific port is in use even when a client
firewall is present on the machine.




/*
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (the "License"). You may not use this file except
 * in compliance with the License.
 *
 * You can obtain a copy of the license at
 * glassfish/bootstrap/legal/CDDLv1.0.txt or
 * https://glassfish.dev.java.net/public/CDDLv1.0.html.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL
 * HEADER in each file and include the License file at
 * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
 * add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your
 * own identifying information: Portions Copyright [yyyy]
 * [name of copyright owner]
 */

/*
 * NetUtils.java
 *
 * Created on April 2, 2002, 9:19 PM
 *
 * @author bnevins
 * @version $Revision: 1.2 $
 * <BR> <I>$Source: /opt/sourcecast/data/ccvs/repository/glassfish/appserv-commons/src/java/com/sun/enterprise/util/net/NetUtils.java,v $
 *
 * Copyright 2000-2001 by iPlanet/Sun Microsystems, Inc.,
 * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information
 * of iPlanet/Sun Microsystems, Inc. ("Confidential Information").
 * You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license
 * agreement you entered into with iPlanet/Sun Microsystems.
 *
 */

package com.sun.enterprise.util.net;
import java.net.*;
import java.util.*;
import java.io.*;

public class NetUtils
{
        private NetUtils()
        {
        }

    public static final int MAX_PORT = 65535;

        ///////////////////////////////////////////////////////////////////////////

        public static String getHostName()
        {
                try
                {
                        return InetAddress.getLocalHost().getHostName();
                }
                catch(Exception e)
                {
                        return null;
                }
        }

        ///////////////////////////////////////////////////////////////////////////


        /**
         * This method returns the fully qualified name of the host. If
         * the name can't be resolved (on windows if there isn't a domain specified), just
         * host name is returned
         *
         * @throws UnknownHostException so it can be handled on a case by case basis
         */
        public static String getCanonicalHostName() throws UnknownHostException {
            String hostname=null;
            String defaultHostname=InetAddress.getLocalHost().getHostName();
            // look for full name
            hostname=InetAddress.getLocalHost().getCanonicalHostName();

            // check to see if ip returned or canonical hostname is different than hostname
            // It is possible for dhcp connected computers to have an erroneous name returned
            // that is created by the dhcp server. If that happens, return just the default hostname
            if (hostname.equals(InetAddress.getLocalHost().getHostAddress()) ||
                !hostname.startsWith(defaultHostname)) {
                // don't want IP or canonical hostname, this will cause a lot of problems for dhcp users
                // get just plan host name instead
                hostname=defaultHostname;
            }

            return hostname;
        }

        ///////////////////////////////////////////////////////////////////////////

        public static InetAddress[] getHostAddresses()
        {
                try
                {
                        String hname = getHostName();

                        if(hname == null)
                                return null;

                        return InetAddress.getAllByName(hname);
                }
                catch(Exception e)
                {
                        return null;
                }
        }

        ///////////////////////////////////////////////////////////////////////////

        public static String[] getHostIPs()
        {
                try
                {
                        InetAddress[] adds = getHostAddresses();

                        if(adds == null)
                                return null;

                        String[] ips = new String[adds.length];

                        for(int i = 0; i < adds.length; i++)
                        {
                                String ip = trimIP(adds[i].toString());
                                ips[i] = ip;
                        }

                        return ips;
                }
                catch(Exception e)
                {
                        return null;
                }
        }

        ///////////////////////////////////////////////////////////////////////////

        public static String trimIP(String ip)
        {
                if(ip == null || ip.length() <= 0)
                        return ip;

                int index = ip.lastIndexOf('/');

                if(index >= 0)
                        return ip.substring(++index);

                return ip;
        }

        ///////////////////////////////////////////////////////////////////////////

        public static byte[] ip2bytes(String ip)
        {
                try
                {
                        // possibilities: "1.1.1.1", "frodo/1.1.1.1", "frodo.foo.com/1.1.1.1"

                        ip = trimIP(ip);
                        StringTokenizer stk = new StringTokenizer(ip, ".");

                        byte[] bytes = new byte[stk.countTokens()];

                        for(int i = 0; stk.hasMoreTokens(); i++)
                        {
                                String num = stk.nextToken();
                                int inum = Integer.parseInt(num);
                                bytes[i] = (byte)inum;
                                //System.out.println("token: " + inum);
                        }
                        return bytes;
                }
                catch(NumberFormatException nfe)
                {
                        return null;
                }
        }

        ///////////////////////////////////////////////////////////////////////////

        public static boolean isLocalHost(String ip)
        {
                if(ip == null)
                        return false;

                ip = trimIP(ip);

                return ip.equals(LOCALHOST_IP);
        }

        ///////////////////////////////////////////////////////////////////////////

        public static boolean isLocal(String ip)
        {
                if(ip == null)
                        return false;

                ip = trimIP(ip);

                if(isLocalHost(ip))
                        return true;

                String[] myIPs = getHostIPs();

                if(myIPs == null)
                        return false;

                for(int i = 0; i < myIPs.length; i++)
                {
                        if(ip.equals(myIPs[i]))
                                return true;
                }

                return false;
        }

        ///////////////////////////////////////////////////////////////////////////

        public static boolean isRemote(String ip)
        {
                return !isLocal(ip);
        }


    /**
     * Get the next free port (incrementing by 1)
     * @param hostName The host name on which the port is to be obtained
     * @param port The port number
     * @return The next incremental port number or 0 if a port cannot be found.
     */
    public static int getNextFreePort(String hostName, int port)
    {
        while (port++ < MAX_PORT) {
            if (isPortFree(hostName, port)) {
                return port;
            }
        }
        return 0;
    }

    /**
     * Returns a random port in the specified range
     * @param hostName The host on which the port is to be obtained.
     * @param startingPort starting port in the range
     * @param endingPort ending port in the range
     * @return the new port or 0 if the range is invalid.
     */
    public static int getFreePort(String hostName, int startingPort, int endingPort)
    {
        int range = endingPort - startingPort;
        int port = 0;
        if (range > 0) {
            Random r = new Random();
            while (true) {
                port = r.nextInt(range + 1) + startingPort;
                if (isPortFree(hostName, port)) {
                    break;
                }
            }
        }
        return port;
    }

    public static boolean isPortValid(int portNumber)
    {
        if (portNumber >=0 && portNumber <= MAX_PORT) {
            return true;
        } else {
            return false;
        }
    }

    public static boolean isPortStringValid(String portNumber)
    {
        try {
            return isPortValid(Integer.parseInt(portNumber));
        } catch (NumberFormatException ex) {
            return false;
        }
    }

        ///////////////////////////////////////////////////////////////////////////

    public static boolean isPortFree(String hostName, int portNumber)
        {
                if(portNumber <= 0 || portNumber > MAX_PORT)
                        return false;
                try
                {
                        // WBN - I have no idea why I'm messing with these streams!
                        // I lifted the code from installer. Apparently if you just
                        // open a socket on a free port and catch the exception something
                        // will go wrong in Windows.
                        // Feel free to change it if you know EXACTLY what you'return doing

            //If the host name is null, assume localhost
            if (hostName == null) {
                hostName = getHostName();
            }
                        ServerSocket socket = new ServerSocket(portNumber);
                        socket.close();
                        socket = null;
                }
                catch (Exception e)
                {
                        // this port is in use
                        return false;
                }

                return true;
        }

        public static boolean isPortFree(int portNumber)
        {
                return isPortFree(null, portNumber);
        }

    /**
        Gets a free port at the time of call to this method.
        The logic leverages the built in java.net.ServerSocket implementation
        which binds a server socket to a free port when instantiated with
        a port <code> 0 </code>.
        <P> Note that this method guarantees the availability of the port
        only at the time of call. The method does not bind to this port.
        <p> Checking for free port can fail for several reasons which may
        indicate potential problems with the system. This method acknowledges
        the fact and following is the general contract:
        <li> Best effort is made to find a port which can be bound to. All
        the exceptional conditions in the due course are considered SEVERE.
        <li> If any exceptional condition is experienced, <code> 0 </code>
        is returned, indicating that the method failed for some reasons and
        the callers should take the corrective action. (The method need not
        always throw an exception for this).
        <li> Method is synchronized on this class.
        @return integer depicting the free port number available at this time
        0 otherwise.
    */
    public static int getFreePort()
    {
        int freePort = 0;
        boolean portFound = false;
        ServerSocket serverSocket = null;

        synchronized (NetUtils.class)
        {
            try
            {
                /*following call normally returns the free port,
                  to which the ServerSocket is bound. */
                serverSocket = new ServerSocket(0);
                freePort = serverSocket.getLocalPort();
                portFound = true;
            }
            catch(Exception e)
            {
                //squelch the exception
            }
            finally
            {
                if (!portFound)
                {
                    freePort = 0;
                }
                try
                {
                    if (serverSocket != null)
                    {
                        serverSocket.close();
                        if (! serverSocket.isClosed())
                        {
                            throw new Exception("local exception ...");
                        }
                    }
                }
                catch(Exception e)
                {
                    //squelch the exception
                    freePort = 0;
                }
            }
            return freePort;
        }
    }

        ///////////////////////////////////////////////////////////////////////////

        private static final String LOCALHOST_IP = "127.0.0.1";

        ///////////////////////////////////////////////////////////////////////////

        public static void main(String[] args)
        {
                System.out.println("80: " + isPortFree(80));
                System.out.println("777: " + isPortFree(777));
                System.out.println("8000: " + isPortFree(8000));
                System.out.println("8080: " + isPortFree(8080));
        }
}