From 884d9d372eb7518d50b6bd77d1420909a0e701cf Mon Sep 17 00:00:00 2001 From: Gary Benson Date: Wed, 20 Sep 2006 07:53:18 +0000 Subject: [PATCH] InetAddress.java: Updated to latest. 2006-09-20 Gary Benson * classpath/java/net/InetAddress.java: Updated to latest. * classpath/java/net/Inet4Address.java: Likewise. * classpath/java/net/Inet6Address.java: Likewise. * classpath/java/net/ResolverCache.java: Likewise. * classpath/java/net/SocketPermission.java: Likewise. * classpath/java/net/Inet4Address.java (AF_INET): Renamed to FAMILY. (, writeReplace): Reflect the above. * classpath/java/net/Inet6Address.java (AF_INET6): Renamed to FAMILY. (): Reflect the above. From-SVN: r117074 --- libjava/classpath/ChangeLog.gcj | 15 + libjava/classpath/java/net/Inet4Address.java | 68 +++- libjava/classpath/java/net/Inet6Address.java | 9 +- libjava/classpath/java/net/InetAddress.java | 377 ++++++++---------- libjava/classpath/java/net/ResolverCache.java | 269 +++++++++++++ .../classpath/java/net/SocketPermission.java | 167 ++++++-- 6 files changed, 652 insertions(+), 253 deletions(-) create mode 100644 libjava/classpath/java/net/ResolverCache.java diff --git a/libjava/classpath/ChangeLog.gcj b/libjava/classpath/ChangeLog.gcj index fa1bbf37bf5..2f098d7e40c 100644 --- a/libjava/classpath/ChangeLog.gcj +++ b/libjava/classpath/ChangeLog.gcj @@ -1,3 +1,18 @@ +2006-09-20 Gary Benson + + * classpath/java/net/InetAddress.java: Updated to latest. + * classpath/java/net/Inet4Address.java: Likewise. + * classpath/java/net/Inet6Address.java: Likewise. + * classpath/java/net/ResolverCache.java: Likewise. + * classpath/java/net/SocketPermission.java: Likewise. + + * classpath/java/net/Inet4Address.java + (AF_INET): Renamed to FAMILY. + (, writeReplace): Reflect the above. + * classpath/java/net/Inet6Address.java + (AF_INET6): Renamed to FAMILY. + (): Reflect the above. + 2006-09-18 Tom Tromey * gnu/javax/net/ssl/provider/SSLSocket.java (isBound, isClosed, diff --git a/libjava/classpath/java/net/Inet4Address.java b/libjava/classpath/java/net/Inet4Address.java index c80f1f175a2..28018a39c1c 100644 --- a/libjava/classpath/java/net/Inet4Address.java +++ b/libjava/classpath/java/net/Inet4Address.java @@ -1,5 +1,5 @@ /* Inet4Address.java -- - Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -57,11 +57,16 @@ public final class Inet4Address extends InetAddress static final long serialVersionUID = 3286316764910316507L; /** - * needed for serialization + * The address family of these addresses (used for serialization). + */ + private static final int FAMILY = 2; // AF_INET + + /** + * Inet4Address objects are serialized as InetAddress objects. */ private Object writeReplace() throws ObjectStreamException { - return new InetAddress(addr, hostName); + return new InetAddress(addr, hostName, FAMILY); } /** @@ -74,7 +79,7 @@ public final class Inet4Address extends InetAddress */ Inet4Address(byte[] addr, String host) { - super(addr, host); + super(addr, host, FAMILY); } /** @@ -84,7 +89,7 @@ public final class Inet4Address extends InetAddress */ public boolean isMulticastAddress() { - return super.isMulticastAddress(); + return (addr[0] & 0xf0) == 0xe0; } /** @@ -92,7 +97,7 @@ public final class Inet4Address extends InetAddress */ public boolean isLoopbackAddress() { - return super.isLoopbackAddress(); + return (addr[0] & 0xff) == 0x7f; } /** @@ -102,7 +107,7 @@ public final class Inet4Address extends InetAddress */ public boolean isAnyLocalAddress() { - return super.isAnyLocalAddress(); + return equals(InetAddress.ANY_IF); } /** @@ -112,7 +117,7 @@ public final class Inet4Address extends InetAddress */ public boolean isLinkLocalAddress() { - return super.isLinkLocalAddress(); + return false; } /** @@ -122,7 +127,19 @@ public final class Inet4Address extends InetAddress */ public boolean isSiteLocalAddress() { - return super.isSiteLocalAddress(); + // 10.0.0.0/8 + if ((addr[0] & 0xff) == 0x0a) + return true; + + // 172.16.0.0/12 + if ((addr[0] & 0xff) == 0xac && (addr[1] & 0xf0) == 0x10) + return true; + + // 192.168.0.0/16 + if ((addr[0] & 0xff) == 0xc0 && (addr[1] & 0xff) == 0xa8) + return true; + + return false; } /** @@ -132,7 +149,7 @@ public final class Inet4Address extends InetAddress */ public boolean isMCGlobal() { - return super.isMCGlobal(); + return false; } /** @@ -142,7 +159,7 @@ public final class Inet4Address extends InetAddress */ public boolean isMCNodeLocal() { - return super.isMCNodeLocal(); + return false; } /** @@ -152,7 +169,12 @@ public final class Inet4Address extends InetAddress */ public boolean isMCLinkLocal() { - return super.isMCLinkLocal(); + if (! isMulticastAddress()) + return false; + + return ((addr[0] & 0xff) == 0xe0 + && (addr[1] & 0xff) == 0x00 + && (addr[2] & 0xff) == 0x00); } /** @@ -162,7 +184,7 @@ public final class Inet4Address extends InetAddress */ public boolean isMCSiteLocal() { - return super.isMCSiteLocal(); + return false; } /** @@ -172,7 +194,7 @@ public final class Inet4Address extends InetAddress */ public boolean isMCOrgLocal() { - return super.isMCOrgLocal(); + return false; } /** @@ -190,7 +212,23 @@ public final class Inet4Address extends InetAddress */ public String getHostAddress() { - return super.getHostAddress(); + StringBuffer sb = new StringBuffer(40); + + int len = addr.length; + int i = 0; + + for ( ; ; ) + { + sb.append(addr[i] & 0xff); + i++; + + if (i == len) + break; + + sb.append('.'); + } + + return sb.toString(); } /** diff --git a/libjava/classpath/java/net/Inet6Address.java b/libjava/classpath/java/net/Inet6Address.java index 8d834a6fd28..2015fe1eb96 100644 --- a/libjava/classpath/java/net/Inet6Address.java +++ b/libjava/classpath/java/net/Inet6Address.java @@ -1,5 +1,5 @@ /* Inet6Address.java -- - Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2002, 2003, 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -92,6 +92,11 @@ public final class Inet6Address extends InetAddress */ private transient NetworkInterface nif; + /** + * The address family of these addresses (used for serialization). + */ + private static final int FAMILY = 10; // AF_INET6 + /** * Create an Inet6Address object * @@ -100,7 +105,7 @@ public final class Inet6Address extends InetAddress */ Inet6Address(byte[] addr, String host) { - super(addr, host); + super(addr, host, FAMILY); // Super constructor clones the addr. Get a reference to the clone. this.ipaddress = this.addr; ifname = null; diff --git a/libjava/classpath/java/net/InetAddress.java b/libjava/classpath/java/net/InetAddress.java index ce65bc773b5..f6f97285fe6 100644 --- a/libjava/classpath/java/net/InetAddress.java +++ b/libjava/classpath/java/net/InetAddress.java @@ -1,5 +1,6 @@ /* InetAddress.java -- Class to model an Internet address - Copyright (C) 1998, 1999, 2002, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2002, 2004, 2005, 2006 + Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -43,7 +44,6 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamException; import java.io.Serializable; -import java.util.StringTokenizer; /** * This class models an Internet address. It does not have a public @@ -57,6 +57,7 @@ import java.util.StringTokenizer; * * @author Aaron M. Renn (arenn@urbanophile.com) * @author Per Bothner + * @author Gary Benson (gbenson@redhat.com) * * @specnote This class is not final since JK 1.4 */ @@ -64,38 +65,48 @@ public class InetAddress implements Serializable { private static final long serialVersionUID = 3286316764910316507L; - /** - * 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; - + static + { + byte[] addr; + try + { + addr = VMInetAddress.lookupInaddrAny(); + } + catch (UnknownHostException e) + { + // Make one up and hope it works. + addr = new byte[] {0, 0, 0, 0}; + } + try + { + ANY_IF = getByAddress(addr); + } + catch (UnknownHostException e) + { + throw new RuntimeException("should never happen", e); + } + ANY_IF.hostName = ANY_IF.getHostName(); + } + /** * Stores static localhost address object. */ static InetAddress LOCALHOST; - static { - // precompute the ANY_IF address try { - ANY_IF = getInaddrAny(); - - byte[] ip_localhost = { 127, 0, 0, 1 }; - LOCALHOST = new Inet4Address(ip_localhost, "localhost"); + LOCALHOST = getByAddress("localhost", new byte[] {127, 0, 0, 1}); } - catch (UnknownHostException uhe) + catch (UnknownHostException e) { - // Hmmm, make one up and hope that it works. - byte[] zeros = { 0, 0, 0, 0 }; - ANY_IF = new Inet4Address(zeros, "0.0.0.0"); + throw new RuntimeException("should never happen", e); } - } + } /** * The Serialized Form specifies that an int 'address' is saved/restored. @@ -115,28 +126,28 @@ public class InetAddress implements Serializable String hostName; /** - * 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 - * use of getaddrinfo() and have other methods just check the family - * field rather than examining the length of the address each time. + * Needed for serialization. */ - int family; + private int family; /** - * Initializes this object's addr instance variable from the passed in - * byte array. Note that this constructor is protected and is called - * only by static methods in this class. + * Constructor. Prior to the introduction of IPv6 support in 1.4, + * methods such as InetAddress.getByName() would return InetAddress + * objects. From 1.4 such methods returned either Inet4Address or + * Inet6Address objects, but for compatibility Inet4Address objects + * are serialized as InetAddresses. As such, there are only two + * places where it is appropriate to invoke this constructor: within + * subclasses constructors and within Inet4Address.writeReplace(). * * @param ipaddr The IP number of this address as an array of bytes * @param hostname The hostname of this IP address. + * @param family The address family of this IP address. */ - InetAddress(byte[] ipaddr, String hostname) + InetAddress(byte[] ipaddr, String hostname, int family) { addr = (null == ipaddr) ? null : (byte[]) ipaddr.clone(); hostName = hostname; - - family = 2; /* AF_INET */ + this.family = family; } /** @@ -144,150 +155,144 @@ public class InetAddress implements Serializable * An address is multicast if the high four bits are "1110". These are * also known as "Class D" addresses. * + *

This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

+ * * @return true if mulitcast, false if not * * @since 1.1 */ public boolean isMulticastAddress() { - // Mask against high order bits of 1110 - if (addr.length == 4) - return (addr[0] & 0xf0) == 0xe0; - - return false; + throw new UnsupportedOperationException(); } /** * Utility routine to check if the InetAddress in a wildcard address * + *

This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

+ * * @since 1.4 */ public boolean isAnyLocalAddress() { - // This is the IPv4 implementation. - // Any class derived from InetAddress should override this. - return equals(ANY_IF); + throw new UnsupportedOperationException(); } /** * Utility routine to check if the InetAddress is a loopback address * + *

This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

+ * * @since 1.4 */ public boolean isLoopbackAddress() { - // This is the IPv4 implementation. - // Any class derived from InetAddress should override this. - return (addr[0] & 0xff) == 0x7f; + throw new UnsupportedOperationException(); } /** * Utility routine to check if InetAddress is a link local address * + *

This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

+ * * @since 1.4 */ public boolean isLinkLocalAddress() { - // This is the IPv4 implementation. - // Any class derived from InetAddress should override this. - // XXX: This seems to not exist with IPv4 addresses - return false; + throw new UnsupportedOperationException(); } /** * Utility routine to check if InetAddress is a site local address * + *

This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

+ * * @since 1.4 */ public boolean isSiteLocalAddress() { - // This is the IPv4 implementation. - // Any class derived from InetAddress should override this. - - // 10.0.0.0/8 - if ((addr[0] & 0xff) == 0x0a) - return true; - - // 172.16.0.0/12 - if ((addr[0] & 0xff) == 0xac && (addr[1] & 0xf0) == 0x10) - return true; - - // 192.168.0.0/16 - if ((addr[0] & 0xff) == 0xc0 && (addr[1] & 0xff) == 0xa8) - return true; - - // XXX: Do we need to check more addresses here ? - return false; + throw new UnsupportedOperationException(); } /** * Utility routine to check if InetAddress is a global multicast address * + *

This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

+ * * @since 1.4 */ public boolean isMCGlobal() { - // This is the IPv4 implementation. - // Any class derived from InetAddress should override this. - // XXX: This seems to not exist with IPv4 addresses - return false; + throw new UnsupportedOperationException(); } /** * Utility routine to check if InetAddress is a node local multicast address. * + *

This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

+ * * @since 1.4 */ public boolean isMCNodeLocal() { - // This is the IPv4 implementation. - // Any class derived from InetAddress should override this. - // XXX: This seems to not exist with IPv4 addresses - return false; + throw new UnsupportedOperationException(); } /** * Utility routine to check if InetAddress is a link local multicast address. * + *

This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

+ * * @since 1.4 */ public boolean isMCLinkLocal() { - // This is the IPv4 implementation. - // Any class derived from InetAddress should override this. - if (! isMulticastAddress()) - return false; - - return ((addr[0] & 0xff) == 0xe0 - && (addr[1] & 0xff) == 0x00 - && (addr[2] & 0xff) == 0x00); + throw new UnsupportedOperationException(); } /** * Utility routine to check if InetAddress is a site local multicast address. * + *

This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

+ * * @since 1.4 */ public boolean isMCSiteLocal() { - // This is the IPv4 implementation. - // Any class derived from InetAddress should override this. - // XXX: This seems to not exist with IPv4 addresses - return false; + throw new UnsupportedOperationException(); } /** * Utility routine to check if InetAddress is a organization local * multicast address. * + *

This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

+ * * @since 1.4 */ public boolean isMCOrgLocal() { - // This is the IPv4 implementation. - // Any class derived from InetAddress should override this. - // XXX: This seems to not exist with IPv4 addresses - return false; + throw new UnsupportedOperationException(); } /** @@ -298,13 +303,20 @@ public class InetAddress implements Serializable */ public String getHostName() { - if (hostName != null) - return hostName; + if (hostName == null) + hostName = getCanonicalHostName(); + return hostName; + } + + /** + * Returns the canonical hostname represented by this InetAddress + */ + String internalGetCanonicalHostName() + { try { - hostName = VMInetAddress.getHostByAddr(addr); - return hostName; + return ResolverCache.getHostByAddr(addr); } catch (UnknownHostException e) { @@ -319,12 +331,14 @@ public class InetAddress implements Serializable */ public String getCanonicalHostName() { + String hostname = internalGetCanonicalHostName(); + SecurityManager sm = System.getSecurityManager(); if (sm != null) { try { - sm.checkConnect(hostName, -1); + sm.checkConnect(hostname, -1); } catch (SecurityException e) { @@ -332,16 +346,7 @@ public class InetAddress implements Serializable } } - // Try to find the FDQN now - InetAddress address; - byte[] ipaddr = getAddress(); - - if (ipaddr.length == 16) - address = new Inet6Address(getAddress(), null); - else - address = new Inet4Address(getAddress(), null); - - return address.getHostName(); + return hostname; } /** @@ -357,32 +362,19 @@ public class InetAddress implements Serializable } /** - * Returns the IP address of this object as a String. The address is in - * the dotted octet notation, for example, "127.0.0.1". + * Returns the IP address of this object as a String. * + *

This method cannot be abstract for backward compatibility reasons. By + * default it always throws {@link UnsupportedOperationException} unless + * overridden.

+ * * @return The IP address of this object in String form * * @since 1.0.2 */ public String getHostAddress() { - StringBuffer sb = new StringBuffer(40); - - int len = addr.length; - int i = 0; - - for ( ; ; ) - { - sb.append(addr[i] & 0xff); - i++; - - if (i == len) - break; - - sb.append('.'); - } - - return sb.toString(); + throw new UnsupportedOperationException(); } /** @@ -488,48 +480,50 @@ public class InetAddress implements Serializable return new Inet4Address(addr, host); if (addr.length == 16) - return new Inet6Address(addr, host); + { + for (int i = 0; i < 12; i++) + { + if (addr[i] != (i < 10 ? 0 : (byte) 0xFF)) + return new Inet6Address(addr, host); + } + + byte[] ip4addr = new byte[4]; + ip4addr[0] = addr[12]; + ip4addr[1] = addr[13]; + ip4addr[2] = addr[14]; + ip4addr[3] = addr[15]; + return new Inet4Address(ip4addr, host); + } throw new UnknownHostException("IP address has illegal length"); } /** - * If hostname is a valid numeric IP address, return the numeric address. - * Otherwise, return null. + * Returns an InetAddress object representing the IP address of + * the given literal IP address in dotted decimal format such as + * "127.0.0.1". This is used by SocketPermission.setHostPort() + * to parse literal IP addresses without performing a DNS lookup. * - * @param hostname the name of the host + * @param literal The literal IP address to create the InetAddress + * object from + * + * @return The address of the host as an InetAddress object, or + * null if the IP address is invalid. */ - private static byte[] aton(String hostname) + static InetAddress getByLiteral(String literal) { - StringTokenizer st = new StringTokenizer(hostname, "."); - - if (st.countTokens() == 4) + byte[] address = VMInetAddress.aton(literal); + if (address == null) + return null; + + try { - 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; - - address[index] = (byte) n; - } - catch (NumberFormatException e) - { - break; - } - } - - if (index == 4) - return address; + return getByAddress(address); + } + catch (UnknownHostException e) + { + throw new RuntimeException("should never happen", e); } - - return null; } /** @@ -577,62 +571,33 @@ public class InetAddress implements Serializable public static InetAddress[] getAllByName(String hostname) throws UnknownHostException { - SecurityManager s = System.getSecurityManager(); - if (s != null) - s.checkConnect(hostname, -1); + // If null or the empty string is supplied, the loopback address + // is returned. + if (hostname == null || hostname.length() == 0) + return new InetAddress[] {LOCALHOST}; - InetAddress[] addresses; + // Check if hostname is an IP address + InetAddress address = getByLiteral(hostname); + if (address != null) + return new InetAddress[] {address}; - if (hostname != null) - hostname = hostname.trim(); - - // Default to current host if necessary - if (hostname == null || hostname.equals("")) - { - addresses = new InetAddress[1]; - addresses[0] = LOCALHOST; - return addresses; - } - - // Not in cache, try the lookup - byte[][] iplist = VMInetAddress.getHostByName(hostname); + // Perform security check before resolving + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkConnect(hostname, -1); + // Resolve the hostname + byte[][] iplist = ResolverCache.getHostByName(hostname); if (iplist.length == 0) throw new UnknownHostException(hostname); - addresses = new InetAddress[iplist.length]; - + InetAddress[] 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); - } + addresses[i] = getByAddress(hostname, iplist[i]); return addresses; } - /** - * 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); - inaddr_any.hostName = inaddr_any.getHostName(); - } - - return inaddr_any; - } - /** * Returns an InetAddress object representing the address of the current * host. @@ -645,11 +610,19 @@ public class InetAddress implements Serializable public static InetAddress getLocalHost() throws UnknownHostException { String hostname = VMInetAddress.getLocalHostname(); - return getByName(hostname); + try + { + return getByName(hostname); + } + catch (SecurityException e) + { + return LOCALHOST; + } } - /* - * Needed for serialization + /** + * Inet4Address objects are serialized as InetAddress objects. + * This deserializes them back into Inet4Address objects. */ private Object readResolve() throws ObjectStreamException { @@ -665,8 +638,6 @@ public class InetAddress implements Serializable for (int i = 2; i >= 0; --i) addr[i] = (byte) (address >>= 8); - - family = 2; /* AF_INET */ } private void writeObject(ObjectOutputStream oos) throws IOException diff --git a/libjava/classpath/java/net/ResolverCache.java b/libjava/classpath/java/net/ResolverCache.java new file mode 100644 index 00000000000..f8790666a0a --- /dev/null +++ b/libjava/classpath/java/net/ResolverCache.java @@ -0,0 +1,269 @@ +/* ResolverCache.java -- A cache of resolver lookups for InetAddress. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.net; + +import java.security.Security; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; + +/** + * This class provides a cache of name service resolutions. By + * default successful resolutions are cached forever to guard + * against DNS spoofing attacks and failed resolutions are cached + * for 10 seconds to improve performance. The length of time that + * results remain in the cache is determined by the following + * security properties: + *
+ *
networkaddress.cache.ttl
+ *
+ * This property specifies the length of time in seconds that + * successful resolutions remain in the cache. The default is + * -1, indicating to cache forever. + *
+ *
networkaddress.cache.negative.ttl
+ *
+ * This property specifies the length of time in seconds that + * unsuccessful resolutions remain in the cache. The default + * is 10, indicating to cache for 10 seconds. + *
+ * In both cases, a value of -1 indicates to cache forever and a + * value of 0 indicates not to cache. + * + * @author Gary Benson (gbenson@redhat.com) + */ +class ResolverCache +{ + /** + * The time in seconds for which successful lookups are cached. + */ + private static final int POSITIVE_TTL = + getTTL("networkaddress.cache.ttl", -1); + + /** + * The time in seconds for which unsuccessful lookups are cached. + */ + private static final int NEGATIVE_TTL = + getTTL("networkaddress.cache.negative.ttl", 10); + + /** + * Helper function to set the TTLs. + */ + private static int getTTL(String propName, int defaultValue) + { + String propValue = Security.getProperty(propName); + if (propValue == null) + return defaultValue; + + return Integer.parseInt(propValue); + } + + /** + * The cache itself. + */ + private static HashMap cache = new HashMap(); + + /** + * List of entries which may expire. + */ + private static LinkedList killqueue = new LinkedList(); + + /** + * Return the hostname for the specified IP address. + * + * @param ip The IP address as a byte array + * + * @return The hostname + * + * @exception UnknownHostException If the reverse lookup fails + */ + public static String getHostByAddr(byte[] addr) throws UnknownHostException + { + Object key = makeHashableAddress(addr); + Entry entry = (Entry) get(key); + if (entry != null) + { + if (entry.value == null) + throw new UnknownHostException(); + return (String) entry.value; + } + + try + { + String hostname = VMInetAddress.getHostByAddr(addr); + put(new Entry(key, hostname)); + return hostname; + } + catch (UnknownHostException e) + { + put(new Entry(key, null)); + throw e; + } + } + + /** + * Return a list of all IP addresses for the specified hostname. + * + * @param hostname The hostname + * + * @return An list of IP addresses as byte arrays + * + * @exception UnknownHostException If the lookup fails + */ + public static byte[][] getHostByName(String hostname) + throws UnknownHostException + { + Entry entry = (Entry) get(hostname); + if (entry != null) + { + if (entry.value == null) + throw new UnknownHostException(); + return (byte[][]) entry.value; + } + + try + { + byte[][] addrs = VMInetAddress.getHostByName(hostname); + put(new Entry(hostname, addrs)); + return addrs; + } + catch (UnknownHostException e) + { + put(new Entry(hostname, null)); + throw e; + } + } + + /** + * Convert an IP address expressed as a byte array into something + * we can use as a hashtable key. + */ + private static Object makeHashableAddress(byte[] addr) + { + char[] chars = new char[addr.length]; + for (int i = 0; i < addr.length; i++) + chars[i] = (char) addr[i]; + return new String(chars); + } + + /** + * Return the entry in the cache associated with the supplied key, + * or null if the cache does not contain an entry + * associated with this key. + */ + private static synchronized Entry get(Object key) + { + reap(); + return (Entry) cache.get(key); + } + + /** + * Insert the supplied entry into the cache. + */ + private static synchronized void put(Entry entry) + { + reap(); + if (entry.expires != 0) + { + if (entry.expires != -1) + killqueue.add(entry); + cache.put(entry.key, entry); + } + } + + /** + * Clear expired entries. This method is not synchronized, so + * it must only be called by methods that are. + */ + private static void reap() + { + if (!killqueue.isEmpty()) + { + long now = System.currentTimeMillis(); + + Iterator iter = killqueue.iterator(); + while (iter.hasNext()) + { + Entry entry = (Entry) iter.next(); + if (entry.expires > now) + break; + cache.remove(entry.key); + iter.remove(); + } + } + } + + /** + * An entry in the cache. + */ + private static class Entry + { + /** + * The key by which this entry is referenced. + */ + public final Object key; + + /** + * The entry itself. A null value indicates a failed lookup. + */ + public final Object value; + + /** + * The time when this cache entry expires. If set to -1 then + * this entry will never expire. If set to 0 then this entry + * expires immediately and will not be inserted into the cache. + */ + public final long expires; + + /** + * Constructor. + */ + public Entry(Object key, Object value) + { + this.key = key; + this.value = value; + + int ttl = value != null ? POSITIVE_TTL : NEGATIVE_TTL; + if (ttl < 1) + expires = ttl; + else + expires = System.currentTimeMillis() + ttl * 1000; + } + } +} diff --git a/libjava/classpath/java/net/SocketPermission.java b/libjava/classpath/java/net/SocketPermission.java index 97e93dcbb35..2d6343dc570 100644 --- a/libjava/classpath/java/net/SocketPermission.java +++ b/libjava/classpath/java/net/SocketPermission.java @@ -117,10 +117,17 @@ public final class SocketPermission extends Permission implements Serializable static final long serialVersionUID = -7204263841984476862L; /** - * A hostname (possibly wildcarded) or IP address (IPv4 or IPv6). + * A hostname (possibly wildcarded). Will be set if and only if + * this object was initialized with a hostname. */ - private transient String host; + private transient String hostname = null; + /** + * An IP address (IPv4 or IPv6). Will be set if and only if this + * object was initialized with a single literal IP address. + */ + private transient InetAddress address = null; + /** * A range of ports. */ @@ -225,7 +232,7 @@ public final class SocketPermission extends Permission implements Serializable private void setHostPort(String hostport) { // Split into host and ports - String ports; + String host, ports; if (hostport.charAt(0) == '[') { // host is a bracketed IPv6 address @@ -234,6 +241,10 @@ public final class SocketPermission extends Permission implements Serializable throw new IllegalArgumentException("Unmatched '['"); host = hostport.substring(1, end); + address = InetAddress.getByLiteral(host); + if (address == null) + throw new IllegalArgumentException("Bad IPv6 address"); + if (end == hostport.length() - 1) ports = ""; else if (hostport.charAt(end + 1) == ':') @@ -255,6 +266,15 @@ public final class SocketPermission extends Permission implements Serializable host = hostport.substring(0, sep); ports = hostport.substring(sep + 1); } + + address = InetAddress.getByLiteral(host); + if (address == null) + { + if (host.lastIndexOf('*') > 0) + throw new IllegalArgumentException("Bad hostname"); + + hostname = host; + } } // Parse and validate the ports @@ -362,10 +382,25 @@ public final class SocketPermission extends Permission implements Serializable else return false; - return p.actionmask == actionmask && - p.minport == minport && - p.maxport == maxport && - p.host.equals(host); + if (p.actionmask != actionmask || + p.minport != minport || + p.maxport != maxport) + return false; + + if (address != null) + { + if (p.address == null) + return false; + else + return p.address.equals(address); + } + else + { + if (p.hostname == null) + return false; + else + return p.hostname.equals(hostname); + } } /** @@ -376,7 +411,12 @@ public final class SocketPermission extends Permission implements Serializable */ public int hashCode() { - return actionmask + minport + maxport + host.hashCode(); + int code = actionmask + minport + maxport; + if (address != null) + code += address.hashCode(); + else + code += hostname.hashCode(); + return code; } /** @@ -415,6 +455,44 @@ public final class SocketPermission extends Permission implements Serializable return null; } + /** + * Returns an array of all IP addresses represented by this object. + */ + private InetAddress[] getAddresses() + { + if (address != null) + return new InetAddress[] {address}; + + try + { + return InetAddress.getAllByName(hostname); + } + catch (UnknownHostException e) + { + return new InetAddress[0]; + } + } + + /** + * Returns the canonical hostname represented by this object, + * or null if this object represents a wildcarded domain. + */ + private String getCanonicalHostName() + { + if (address != null) + return address.internalGetCanonicalHostName(); + if (hostname.charAt(0) == '*') + return null; + try + { + return InetAddress.getByName(hostname).internalGetCanonicalHostName(); + } + catch (UnknownHostException e) + { + return null; + } + } + /** * Returns true if the permission object passed it is implied by the * this permission. This will be true if: @@ -450,6 +528,11 @@ public final class SocketPermission extends Permission implements Serializable else return false; + // If p was initialised with an empty hostname then we do not + // imply it. This is not part of the spec, but it seems necessary. + if (p.hostname != null && p.hostname.length() == 0) + return false; + // Next check the actions if ((p.actionmask & actionmask) != p.actionmask) return false; @@ -459,36 +542,54 @@ public final class SocketPermission extends Permission implements Serializable return false; // Finally check the hosts - if (host.equals(p.host)) - return true; + String p_canon = null; - // Try the canonical names - String ourcanonical = null; - String theircanonical = null; - try + // Return true if this object was initialized with a single + // IP address which one of p's IP addresses is equal to. + if (address != null) { - ourcanonical = InetAddress.getByName(host).getHostName(); - theircanonical = InetAddress.getByName(p.host).getHostName(); - } - catch (UnknownHostException e) - { - // Who didn't resolve? Just assume current address is canonical enough - // Is this ok to do? - if (ourcanonical == null) - ourcanonical = host; - if (theircanonical == null) - theircanonical = p.host; + InetAddress[] addrs = p.getAddresses(); + for (int i = 0; i < addrs.length; i++) + { + if (address.equals(addrs[i])) + return true; + } } - if (ourcanonical.equals(theircanonical)) - return true; - - // Well, last chance. Try for a wildcard - if (host.indexOf("*.") != -1) + // Return true if this object is a wildcarded domain that + // p's canonical name matches. + if (hostname != null && hostname.charAt(0) == '*') { - String wild_domain = - host.substring(host.indexOf("*" + 1)); - if (theircanonical.endsWith(wild_domain)) + p_canon = p.getCanonicalHostName(); + if (p_canon != null && p_canon.endsWith(hostname.substring(1))) + return true; + + } + + // Return true if this one of this object's IP addresses + // is equal to one of p's. + if (address == null) + { + InetAddress[] addrs = p.getAddresses(); + InetAddress[] p_addrs = p.getAddresses(); + + for (int i = 0; i < addrs.length; i++) + { + for (int j = 0; j < p_addrs.length; j++) + { + if (addrs[i].equals(p_addrs[j])) + return true; + } + } + } + + // Return true if this object's canonical name equals p's. + String canon = getCanonicalHostName(); + if (canon != null) + { + if (p_canon == null) + p_canon = p.getCanonicalHostName(); + if (p_canon != null && canon.equals(p_canon)) return true; }