--- /home/cpdev/src/classpath/java/net/InetAddress.java	2005-07-02 21:03:35.000000000 +0000
+++ java/net/InetAddress.java	2005-06-30 05:34:40.000000000 +0000
@@ -38,13 +38,13 @@
 
 package java.net;
 
+import gnu.classpath.Configuration;
+
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.io.ObjectStreamException;
 import java.io.Serializable;
-import java.util.HashMap;
-import java.util.StringTokenizer;
 
 /**
  * This class models an Internet address.  It does not have a public
@@ -66,94 +66,25 @@
   private static final long serialVersionUID = 3286316764910316507L;
 
   /**
-   * The default DNS hash table size,
-   * Use a prime number happy with hash table.
-   */
-  private static final int DEFAULT_CACHE_SIZE = 89;
-
-  /**
-   * The default caching period in minutes.
-   */
-  private static final int DEFAULT_CACHE_PERIOD = 4 * 60;
-
-  /**
-   * Percentage of cache entries to purge when the table gets full.
-   */
-  private static final int DEFAULT_CACHE_PURGE_PCT = 30;
-
-  /**
-   * The special IP address INADDR_ANY.
-   */
-  private static InetAddress inaddr_any;
-
-  /**
    * Dummy InetAddress, used to bind socket to any (all) network interfaces.
    */
   static InetAddress ANY_IF;
+    
+  private static final byte[] loopbackAddress = { 127, 0, 0, 1 };
 
-  /**
-   * Stores static localhost address object.
-   */
-  static InetAddress LOCALHOST;
-
-  /**
-   * The size of the cache.
-   */
-  private static int cache_size = 0;
-
-  /**
-   * The length of time we will continue to read the address from cache
-   * before forcing another lookup.
-   */
-  private static int cache_period = 0;
-
-  /**
-   * What percentage of the cache we will purge if it gets full.
-   */
-  private static int cache_purge_pct = 0;
+  private static final InetAddress loopback 
+    = new Inet4Address(loopbackAddress, "localhost");
 
-  /**
-   * HashMap to use as DNS lookup cache.
-   * Use HashMap because all accesses to cache are already synchronized.
-   */
-  private static HashMap cache;
+  private static InetAddress localhost = null;
 
   static
   {
-    // Look for properties that override default caching behavior
-    cache_size =
-      Integer.getInteger("gnu.java.net.dns_cache_size", DEFAULT_CACHE_SIZE)
-             .intValue();
-    cache_period =
-      Integer.getInteger("gnu.java.net.dns_cache_period",
-                         DEFAULT_CACHE_PERIOD * 60 * 1000).intValue();
-
-    cache_purge_pct =
-      Integer.getInteger("gnu.java.net.dns_cache_purge_pct",
-                         DEFAULT_CACHE_PURGE_PCT).intValue();
-
-    // Fallback to  defaults if necessary
-    if ((cache_purge_pct < 1) || (cache_purge_pct > 100))
-      cache_purge_pct = DEFAULT_CACHE_PURGE_PCT;
-
-    // Create the cache
-    if (cache_size != 0)
-      cache = new HashMap(cache_size);
-
-    // precompute the ANY_IF address
-    try
-      {
-        ANY_IF = getInaddrAny();
-
-	byte[] ip_localhost = { 127, 0, 0, 1 };
-	LOCALHOST = new Inet4Address(ip_localhost, "localhost");
-      }
-    catch (UnknownHostException uhe)
-      {
-        // Hmmm, make one up and hope that it works.
-        byte[] zeros = { 0, 0, 0, 0 };
-        ANY_IF = new Inet4Address(zeros, "0.0.0.0");
-      }
+    // load the shared library needed for name resolution
+    if (Configuration.INIT_LOAD_LIBRARY)
+      System.loadLibrary("javanet");
+    
+    byte[] zeros = { 0, 0, 0, 0 };
+    ANY_IF = new Inet4Address(zeros, "0.0.0.0");
   }
 
   /**
@@ -174,11 +105,6 @@
   String hostName;
 
   /**
-   * The time this address was looked up.
-   */
-  transient long lookup_time;
-
-  /**
    * The field 'family' seems to be the AF_ value.
    * FIXME: Much of the code in the other java.net classes does not make
    * use of this family field.  A better implementation would be to make
@@ -200,9 +126,8 @@
     addr = (null == ipaddr) ? null : (byte[]) ipaddr.clone();
     hostName = hostname;
     
-    lookup_time = System.currentTimeMillis();
-
-    family = 2; /* AF_INET */
+    if (ipaddr != null)
+      family = getFamily(ipaddr);
   }
 
   /**
@@ -220,6 +145,10 @@
     if (addr.length == 4)
       return (addr[0] & 0xf0) == 0xe0;
 
+    // Mask against high order bits of 11111111
+    if (addr.length == 16)
+      return addr [0] == (byte) 0xFF;
+    
     return false;
   }
 
@@ -367,15 +296,10 @@
     if (hostName != null)
       return hostName;
 
-    try
-      {
-	hostName = VMInetAddress.getHostByAddr(addr);
-	return hostName;
-      }
-    catch (UnknownHostException e)
-      {
-	return getHostAddress();
-      }
+    // Lookup hostname and set field.
+    lookup (null, this, false);
+    
+    return hostName;
   }
 
   /**
@@ -422,6 +346,31 @@
     return (byte[]) addr.clone();
   }
 
+  /* Helper function due to a CNI limitation.  */
+  private static InetAddress[] allocArray (int count)
+  {
+    return new InetAddress [count];
+  }
+
+  /* Helper function due to a CNI limitation.  */
+  private static SecurityException checkConnect (String hostname)
+  {
+    SecurityManager s = System.getSecurityManager();
+    
+    if (s == null)
+      return null;
+    
+    try
+      {
+	s.checkConnect (hostname, -1);
+	return null;
+      }
+    catch (SecurityException ex)
+      {
+	return ex;
+      }
+  }
+
   /**
    * Returns the IP address of this object as a String.  The address is in
    * the dotted octet notation, for example, "127.0.0.1".
@@ -437,6 +386,39 @@
     int len = addr.length;
     int i = 0;
     
+    if (len == 16)
+      { // An IPv6 address.
+	for ( ; ; i += 2)
+	  {
+	    if (i >= 16)
+	      return sb.toString();
+	    
+	    int x = ((addr [i] & 0xFF) << 8) | (addr [i + 1] & 0xFF);
+	    boolean empty = sb.length() == 0;
+	    
+	    if (empty)
+	      {
+		if (i == 10 && x == 0xFFFF)
+		  { // IPv4-mapped IPv6 address.
+		    sb.append (":FFFF:");
+		    break;  // Continue as IPv4 address;
+		  }
+		else if (i == 12)
+		  { // IPv4-compatible IPv6 address.
+		    sb.append (':');
+		    break;  // Continue as IPv4 address.
+		  }
+		else if (i > 0)
+		  sb.append ("::");
+	      }
+	    else
+	      sb.append (':');
+	    
+	    if (x != 0 || i >= 14)
+	      sb.append (Integer.toHexString (x).toUpperCase());
+	  }
+      }
+    
     for ( ; ; )
       {
         sb.append(addr[i] & 0xff);
@@ -565,38 +547,28 @@
    *
    * @param hostname the name of the host
    */
-  private static byte[] aton(String hostname)
-  {
-    StringTokenizer st = new StringTokenizer(hostname, ".");
-
-    if (st.countTokens() == 4)
-      {
-	int index;
-	byte[] address = new byte[4];
-
-	for (index = 0; index < 4; index++)
-	  {
-	    try
-	      {
-		short n = Short.parseShort(st.nextToken());
-
-		if ((n < 0) || (n > 255))
-		  break;
+  private static native byte[] aton(String hostname);
 
-		address[index] = (byte) n;
-	      }
-	    catch (NumberFormatException e)
-	      {
-		break;
-	      }
-	  }
-
-	if (index == 4)
-	  return address;
-      }
+  /**
+   * Looks up all addresses of a given host.
+   *
+   * @param hostname the host to lookup
+   * @param ipaddr the IP address to lookup
+   * @param all return all known addresses for one host
+   *
+   * @return an array with all found addresses
+   */
+  private static native InetAddress[] lookup (String hostname,
+		                              InetAddress ipaddr, boolean all);
 
-    return null;
-  }
+  /**
+   * Returns tha family type of an IP address.
+   *
+   * @param addr the IP address
+   *
+   * @return the family
+   */
+  private static native int getFamily (byte[] ipaddr);
 
   /**
    * Returns an InetAddress object representing the IP address of the given
@@ -619,8 +591,42 @@
   public static InetAddress getByName(String hostname)
     throws UnknownHostException
   {
-    InetAddress[] addresses = getAllByName(hostname);
-    return addresses[0];
+    // If null or the empty string is supplied, the loopback address
+    // is returned. Note that this is permitted without a security check.
+    if (hostname == null || hostname.length() == 0)
+      return loopback;
+
+    SecurityManager s = System.getSecurityManager();
+    if (s != null)
+      s.checkConnect(hostname, -1);
+
+    // Assume that the host string is an IP address
+    byte[] address = aton(hostname);
+    if (address != null)
+      {
+        if (address.length == 4)
+          return new Inet4Address (address, null);
+        else if (address.length == 16)
+          {
+	    if ((address [10] == 0xFF) && (address [11] == 0xFF))
+	      {
+		byte[] ip4addr = new byte [4];
+		ip4addr [0] = address [12];
+		ip4addr [1] = address [13];
+		ip4addr [2] = address [14];
+		ip4addr [3] = address [15];
+		return new Inet4Address (ip4addr, null);
+	      }
+            return new Inet6Address (address, null);
+	  }
+	else
+          throw new UnknownHostException ("Address has invalid length");
+      }
+
+    // Try to resolve the host by DNS
+    InetAddress result = new InetAddress(null, null);
+    lookup (hostname, result, false);
+    return result;
   }
 
   /**
@@ -643,124 +649,37 @@
   public static InetAddress[] getAllByName(String hostname)
     throws UnknownHostException
   {
+    // If null or the empty string is supplied, the loopback address
+    // is returned. Note that this is permitted without a security check.
+    if (hostname == null || hostname.length() == 0)
+      return new InetAddress[] {loopback};
+
     SecurityManager s = System.getSecurityManager();
     if (s != null)
       s.checkConnect(hostname, -1);
 
-    InetAddress[] addresses;
-
-    // Default to current host if necessary
-    if (hostname == null)
+    // Check if hostname is an IP address
+    byte[] address = aton (hostname);
+    if (address != null)
       {
-	addresses = new InetAddress[1];
-	addresses[0] = LOCALHOST;
-	return addresses;
+	InetAddress[] result = new InetAddress [1];
+	result [0] = new InetAddress (address, null);
+	return result;
       }
 
-    // Check the cache for this host before doing a lookup
-    addresses = checkCacheFor(hostname);
-
-    if (addresses != null)
-      return addresses;
-
-    // Not in cache, try the lookup
-    byte[][] iplist = VMInetAddress.getHostByName(hostname);
-
-    if (iplist.length == 0)
-      throw new UnknownHostException(hostname);
-
-    addresses = new InetAddress[iplist.length];
-
-    for (int i = 0; i < iplist.length; i++)
-      {
-	if (iplist[i].length != 4)
-	  throw new UnknownHostException(hostname);
-
-	addresses[i] = new Inet4Address(iplist[i], hostname);
-      }
-
-    addToCache(hostname, addresses);
-    return addresses;
+    // Try to resolve the hostname by DNS
+    return lookup (hostname, null, true);
   }
 
   /**
-   * This method checks the DNS cache to see if we have looked this hostname
-   * up before. If so, we return the cached addresses unless it has been in the
-   * cache too long.
-   *
-   * @param hostname The hostname to check for
+   * This native method looks up the hostname of the local machine
+   * we are on.  If the actual hostname cannot be determined, then the
+   * value "localhost" will be used.  This native method wrappers the
+   * "gethostname" function.
    *
-   * @return The InetAddress for this hostname or null if not available
+   * @return The local hostname.
    */
-  private static synchronized InetAddress[] checkCacheFor(String hostname)
-  {
-    InetAddress[] addresses = null;
-
-    if (cache_size == 0)
-      return null;
-
-    Object obj = cache.get(hostname);
-    if (obj == null)
-      return null;
-
-    if (obj instanceof InetAddress[])
-      addresses = (InetAddress[]) obj;
-
-    if (addresses == null)
-      return null;
-
-    if (cache_period != -1)
-      if ((System.currentTimeMillis() - addresses[0].lookup_time) > cache_period)
-        {
-	  cache.remove(hostname);
-	  return null;
-        }
-
-    return addresses;
-  }
-
-  /**
-   * This method adds an InetAddress object to our DNS cache.  Note that
-   * if the cache is full, then we run a purge to get rid of old entries.
-   * This will cause a performance hit, thus applications using lots of
-   * lookups should set the cache size to be very large.
-   *
-   * @param hostname The hostname to cache this address under
-   * @param obj The InetAddress or InetAddress array to store
-   */
-  private static synchronized void addToCache(String hostname, Object obj)
-  {
-    if (cache_size == 0)
-      return;
-
-    // Check to see if hash table is full
-    if (cache_size != -1)
-      if (cache.size() == cache_size)
-        {
-	  // FIXME Add code to purge later.
-        }
-
-    cache.put(hostname, obj);
-  }
-
-  /**
-   * Returns the special address INADDR_ANY used for binding to a local
-   * port on all IP addresses hosted by a the local host.
-   *
-   * @return An InetAddress object representing INDADDR_ANY
-   *
-   * @exception UnknownHostException If an error occurs
-   */
-  static InetAddress getInaddrAny() throws UnknownHostException
-  {
-    if (inaddr_any == null)
-      {
-	byte[] tmp = VMInetAddress.lookupInaddrAny();
-	inaddr_any = new Inet4Address(tmp, null);
-      }
-
-    return inaddr_any;
-  }
+  private static native String getLocalHostname();
 
   /**
    * Returns an InetAddress object representing the address of the current
@@ -773,11 +692,66 @@
    */
   public static InetAddress getLocalHost() throws UnknownHostException
   {
-    String hostname = VMInetAddress.getLocalHostname();
-    return getByName(hostname);
+    SecurityManager s = System.getSecurityManager();
+    
+    // Experimentation shows that JDK1.2 does cache the result.
+    // However, if there is a security manager, and the cached result
+    // is other than "localhost", we need to check again.
+    if (localhost == null
+	|| (s != null && ! localhost.isLoopbackAddress()))
+      getLocalHost (s);
+    
+    return localhost;
+  }
+
+  private static synchronized void getLocalHost (SecurityManager s)
+    throws UnknownHostException
+  {
+    // Check the localhost cache again, now that we've synchronized.
+    if (s == null && localhost != null)
+      return;
+    
+    String hostname = getLocalHostname();
+    
+    if (s != null)
+      {
+	// "The Java Class Libraries" suggests that if the security
+	// manager disallows getting the local host name, then
+	// we use the loopback host.
+	// However, the JDK 1.2 API claims to throw SecurityException,
+	// which seems to suggest SecurityException is *not* caught.
+	// In this case, experimentation shows that former is correct.
+	try
+	  {
+	    // This is wrong, if the name returned from getLocalHostname()
+	    // is not a fully qualified name.  FIXME.
+	    s.checkConnect (hostname, -1);
+	  }
+	catch (SecurityException ex)
+	  {
+	    hostname = null;
+	  }
+      }
+    
+    if (hostname != null && hostname.length() != 0)
+      {
+	try
+	  {
+	    localhost = new InetAddress (null, null);
+	    lookup (hostname, localhost, false);
+	  }
+	catch (Exception ex)
+	  {
+	  }
+      }
+    else
+      throw new UnknownHostException();
+    
+    if (localhost == null)
+      localhost = new InetAddress (loopbackAddress, "localhost");
   }
 
-  /*
+  /**
    * Needed for serialization
    */
   private void readResolve() throws ObjectStreamException
@@ -795,7 +769,12 @@
     for (int i = 2; i >= 0; --i)
       addr[i] = (byte) (address >>= 8);
 
-    family = 2; /* AF_INET  */
+    // Ignore family from serialized data.  Since the saved address is 32 bits
+    // the deserialized object will have an IPv4 address i.e. AF_INET family.
+    // FIXME: An alternative is to call the aton method on the deserialized
+    // hostname to get a new address.  The Serialized Form doc is silent
+    // on how these fields are used.
+    family = getFamily (addr);
   }
 
   private void writeObject(ObjectOutputStream oos) throws IOException
@@ -806,7 +785,7 @@
     int i = len - 4;
 
     for (; i < len; i++)
-      address = address << 8 | (addr[i] & 0xff);
+      address = address << 8 | (((int) addr[i]) & 0xFF);
 
     oos.defaultWriteObject();
   }
