dev@glassfish.java.net

Re: proposed patch for appserv-commons NetUtils.java

From: Kedar Mhaswade <kedar.mhaswade_at_Sun.COM>
Date: Tue, 04 Oct 2005 08:14:27 -0700

Hello Soon,
        Thanks for your patch. But there must be a careful refactoring before this
could be incorporated. If you see, this method takes a host-name parameter and
the reason it is doing so is not quite evident because one would think it is the
host that this code executing on, that matters. But this code is also used to
make a best-case attempt to determine if a port is free on a remote host!

Localhost is just the default. If, in fact the host is not localhost, one should
not open a ServerSocket to determine the port availability, but a listen socket
for the same.

        I agree that if the host is "localhost", then one could open a "ServerSocket"
instead of just a "Socket" to determine port availability.

        What do you think?

Regards,
Kedar


Soon Leong wrote:
> 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));
> }
> }
>
>
>
>
>
>
>
> ------------------------------------------------------------------------
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe_at_glassfish.dev.java.net
> For additional commands, e-mail: dev-help_at_glassfish.dev.java.net