Milán Major

netscape-revival

1 branch
Code

sun-java/classsrc/java/net/InetAddress.java

/*
 * @(#)InetAddress.java	1.20 95/12/01 Jonathan Payne
 *
 * Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and without
 * fee is hereby granted provided that this copyright notice
 * appears in all copies. Please refer to the file "copyright.html"
 * for further important copyright and licensing information.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 */

package java.net;

import java.util.Hashtable;
import java.lang.SecurityManager;

/**
 * A class that represents internet addresses.
 *
 * @version     1.20, 01 Dec 1995
 * @author 	Jonathan Payne
 * @author 	Arthur van Hoff
 * @author 	Chris Warth
 */
public final 
class InetAddress {
    String hostName;
    int address;    // Currently we only deal effectively with 32-bit addresses. 
		    // However this field can be expanded to be a byte array 
		    // or a 64-bit quantity without too much effort.
    int family;

    /*
     * Load net library into runtime.
     */
    static {
	SecurityManager.setScopePermission();
	System.loadLibrary("net");
    }

    /** 
     * 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.
     */
    InetAddress() {}

    /**
     * Creates an InetAddress with the specified host name and IP address.
     * @param hostName the specified host name
     * @param addr the specified IP address.  The address is expected in 
     *	      network byte order.
     * @exception UnknownHostException If the address is unknown.
     */
    InetAddress(String hostName, byte addr[]) {
	this.hostName = hostName;
	this.family = getInetFamily();
	/*
	 * We must be careful here to maintain the network byte
	 * order of the address.  As it comes in, the most
	 * significant byte of the address is in addr[0].  It
	 * actually doesn't matter what order they end up in the
	 * array, as long as it is documented and consistent.
	 */
	address  = addr[3] & 0xFF;
	address |= ((addr[2] << 8) & 0xFF00);
	address |= ((addr[1] << 16) & 0xFF0000);
	address |= ((addr[0] << 24) & 0xFF000000);
    }

    /**
     * Gets the hostname for this address; also the key in the above
     * hash table.
     * If the host is equal to null, then this address refers to any
     * of the local machine's available network addresses.
     */
    public String getHostName() {
	if (hostName == null) {
	    try {
		hostName = getHostByAddr(address);
	    } catch (UnknownHostException e) {
		hostName = getHostAddress();
	    }
	}

	return hostName;
    }

    /**
     * Returns the raw IP address in network byte order.  The
     * highest order byte position is in addr[0].  An array of bytes
     * is returned so we are prepared for 64-bit IP addresses.
     * @return raw IP address in network byte order.
     */
    public byte[] getAddress() {	
	byte[] addr = new byte[4];

	addr[0] = (byte) ((address >>> 24) & 0xFF);
	addr[1] = (byte) ((address >>> 16) & 0xFF);
	addr[2] = (byte) ((address >>> 8) & 0xFF);
	addr[3] = (byte) (address & 0xFF);
	return addr;
    }

    /**
     * Returns the IP address string "%d.%d.%d.%d"
     * @return raw IP address in a string format
     */
    public String getHostAddress() {	
        return ((address >>> 24) & 0xFF) + "." +
               ((address >>> 16) & 0xFF) + "." +
               ((address >>>  8) & 0xFF) + "." +
               ((address >>>  0) & 0xFF);
    }

    /**
     * Returns a hashcode for this InetAddress.
     */
    public int hashCode() {
	return address;
    }

    /**
     * Compares this object against the specified object.
     * @param obj the object to compare with
     * @return true if the objects are the same; false otherwise.
     */
    public boolean equals(Object obj) {
	return (obj != null) && (obj instanceof InetAddress) &&
	    (((InetAddress)obj).address == address);
    }

    /**
     * Converts the InetAddress to a String.
     */
    public String toString() {
	return ((hostName != null) ? hostName + "/" : "") +
               getHostAddress();
    }

    /* Cached addresses - our own litle nis, not! */
    static Hashtable	    addressCache = new Hashtable();
    static InetAddress	    unknownAddress;
    static InetAddress	    anyLocalAddress;
    static InetAddress      localHost;

    static {
	unknownAddress = new InetAddress();
	anyLocalAddress = new InetAddress();
	makeAnyLocalAddress(anyLocalAddress);
	try {
	    localHost = getByName(getLocalHostName());
	} catch (Exception ex) {
	    localHost = unknownAddress;
	}
    }

    /**
     * Returns a network address for the indicated host.  A host name
     * of null refers to default address for the local machine.  A local
     * cache is used to speed access to addresses.  If a all
     * addresses for host are needed, use the getAllByName() method.
     * @param host the specified host
     * @exception UnknownHostException If the address is unknown.
     */
    public static InetAddress getByName(String host)
	throws UnknownHostException
    {
	if (host == null || host.length() == 0) {
	    return getLocalHost();
	}
	return getAllByName(host)[0]; 
    }

    /** 
     * Given a hostname, return an array of all the corresponding InetAddresses.  
     * @exception UnknownHostException If the host name could not be resolved
     */
    public static InetAddress getAllByName(String host)[]
	throws UnknownHostException
    {
	if (host == null) {
	    throw new UnknownHostException(host);
	}

	/* make sure the connection to the host is allowed, before we
	   create the InetAddress */
	SecurityManager security = System.getSecurityManager();
	if (security != null && !security.getInCheck()) {
	    security.checkConnect(host, -1);
	}

	Object obj; // object we recall from our cache
        synchronized(addressCache) {	
            /* Cache.get can return: null, unknownAddress, or InetAddress[] */
	    obj = addressCache.get(host);

	    /* If no entry in cache, then do the host lookup */
	    if (obj == null) {
	        try {
                    /*
                    * Do not put the call to lookup() inside the
                    * constructor.  if you do you will still be
                    * allocating space when the lookup fails.
                    */
		    byte[][] byte_array =  lookupAllHostAddr(host);
                    if (0 == byte_array.length)
                        throw new UnknownHostException(host);
		    InetAddress[] addr_array = new InetAddress[byte_array.length];
		    for (int i = 0; i < byte_array.length; i++) {
		        byte addr[] = byte_array[i];
		        addr_array[i] = new InetAddress(host, addr);
		    }
       		    obj = addr_array;
                    // System.err.println("InetAddress cache added " + ((InetAddress[])obj)[0]);
	        } catch (UnknownHostException e) {
		    obj  = unknownAddress;
	        }
	        addressCache.put(host, obj);
	    } 
	} // Release synchronous lock on addressCache
	    
	if (obj == unknownAddress) {
	    /*
	     * We currently cache the fact that a host is unknown.
	     */
	    throw new UnknownHostException(host);
	}

        /* if (host.charAt(0) >= '0' && host.charAt(0) <= '9')
                System.err.println("Tranlated host name " + host + " to " + 
                        ((InetAddress[])obj).length + " host " +
                        ((InetAddress[])obj)[0]);
        */

        // Copy array, so that hacker can't molest contents of our cache
    	try {
    	      obj = ((InetAddress [])obj).clone();
    	} catch (CloneNotSupportedException cnse) {
    	      cnse.printStackTrace(); // internal error : hack: what to throw??
              obj = null;
    	}
    	return (InetAddress [])obj;
    }

    /**
     * Returns the local host.
     * @exception UnknownHostException If the host name could not be resolved
     */
    public static InetAddress getLocalHost() throws UnknownHostException {
        if (localHost.equals(unknownAddress)) {
	    throw new UnknownHostException();
	}
	return localHost;
    }

    private static native String getLocalHostName() throws UnknownHostException;
    private static native void makeAnyLocalAddress(InetAddress addr);
    private static native byte[] lookupHostAddr(String hostname) throws UnknownHostException;
    private static native byte[][]
        lookupAllHostAddr(String hostname) throws UnknownHostException;
    private static native String getHostByAddr(int addr) throws UnknownHostException;
    private static native int getInetFamily();
}