OSDN Git Service

2006-09-20 Gary Benson <gbenson@redhat.com>
[pf3gnuchains/gcc-fork.git] / libjava / classpath / java / net / ResolverCache.java
1 /* ResolverCache.java -- A cache of resolver lookups for InetAddress.
2    Copyright (C) 2006 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package java.net;
40
41 import java.security.Security;
42 import java.util.HashMap;
43 import java.util.Iterator;
44 import java.util.LinkedList;
45
46 /**
47  * This class provides a cache of name service resolutions.  By
48  * default successful resolutions are cached forever to guard
49  * against DNS spoofing attacks and failed resolutions are cached
50  * for 10 seconds to improve performance.  The length of time that
51  * results remain in the cache is determined by the following
52  * security properties:
53  * <dl>
54  *   <dt><code>networkaddress.cache.ttl</code></dt>
55  *   <dd>
56  *     This property specifies the length of time in seconds that
57  *     successful resolutions remain in the cache.  The default is
58  *     -1, indicating to cache forever.
59  *   </dd>
60  *   <dt><code>networkaddress.cache.negative.ttl</code></dt>
61  *   <dd>
62  *     This property specifies the length of time in seconds that
63  *     unsuccessful resolutions remain in the cache.  The default
64  *     is 10, indicating to cache for 10 seconds.
65  *   </dd>
66  * In both cases, a value of -1 indicates to cache forever and a
67  * value of 0 indicates not to cache.
68  *
69  * @author Gary Benson (gbenson@redhat.com)
70  */
71 class ResolverCache
72 {
73   /**
74    * The time in seconds for which successful lookups are cached.
75    */
76   private static final int POSITIVE_TTL =
77     getTTL("networkaddress.cache.ttl", -1);
78
79   /**
80    * The time in seconds for which unsuccessful lookups are cached.
81    */
82   private static final int NEGATIVE_TTL =
83     getTTL("networkaddress.cache.negative.ttl", 10);
84
85   /**
86    * Helper function to set the TTLs.
87    */
88   private static int getTTL(String propName, int defaultValue)
89   {
90     String propValue = Security.getProperty(propName);
91     if (propValue == null)
92       return defaultValue;
93
94     return Integer.parseInt(propValue);
95   }
96
97   /**
98    * The cache itself.
99    */
100   private static HashMap cache = new HashMap();
101
102   /**
103    * List of entries which may expire.
104    */
105   private static LinkedList killqueue = new LinkedList();
106
107   /**
108    * Return the hostname for the specified IP address.
109    *
110    * @param ip The IP address as a byte array
111    *
112    * @return The hostname
113    *
114    * @exception UnknownHostException If the reverse lookup fails
115    */
116   public static String getHostByAddr(byte[] addr) throws UnknownHostException
117   {
118     Object key = makeHashableAddress(addr);
119     Entry entry = (Entry) get(key);
120     if (entry != null)
121       {
122         if (entry.value == null)
123           throw new UnknownHostException();
124         return (String) entry.value;
125       }
126
127     try
128       {
129         String hostname = VMInetAddress.getHostByAddr(addr);
130         put(new Entry(key, hostname));
131         return hostname;
132       }
133     catch (UnknownHostException e)
134       {
135         put(new Entry(key, null));
136         throw e;
137       }
138   }
139
140   /**
141    * Return a list of all IP addresses for the specified hostname.
142    *
143    * @param hostname The hostname
144    *
145    * @return An list of IP addresses as byte arrays
146    *
147    * @exception UnknownHostException If the lookup fails
148    */
149   public static byte[][] getHostByName(String hostname)
150     throws UnknownHostException
151   {
152     Entry entry = (Entry) get(hostname);
153     if (entry != null)
154       {
155         if (entry.value == null)
156           throw new UnknownHostException();
157         return (byte[][]) entry.value;
158       }
159
160     try
161       {
162         byte[][] addrs = VMInetAddress.getHostByName(hostname);
163         put(new Entry(hostname, addrs));
164         return addrs;
165       }
166     catch (UnknownHostException e)
167       {
168         put(new Entry(hostname, null));
169         throw e;
170       }
171   }
172
173   /**
174    * Convert an IP address expressed as a byte array into something
175    * we can use as a hashtable key.
176    */
177   private static Object makeHashableAddress(byte[] addr)
178   {
179     char[] chars = new char[addr.length];
180     for (int i = 0; i < addr.length; i++)
181       chars[i] = (char) addr[i];
182     return new String(chars);
183   }
184
185   /**
186    * Return the entry in the cache associated with the supplied key,
187    * or <code>null</code> if the cache does not contain an entry
188    * associated with this key.
189    */
190   private static synchronized Entry get(Object key)
191   {
192     reap();
193     return (Entry) cache.get(key);
194   }
195
196   /**
197    * Insert the supplied entry into the cache.
198    */
199   private static synchronized void put(Entry entry)
200   {
201     reap();
202     if (entry.expires != 0)
203       {
204         if (entry.expires != -1)
205           killqueue.add(entry);
206         cache.put(entry.key, entry);
207       }
208   }
209
210   /**
211    * Clear expired entries.  This method is not synchronized, so
212    * it must only be called by methods that are.
213    */
214   private static void reap()
215   {
216     if (!killqueue.isEmpty())
217       {
218         long now = System.currentTimeMillis();
219
220         Iterator iter = killqueue.iterator();
221         while (iter.hasNext())
222           {
223             Entry entry = (Entry) iter.next();
224             if (entry.expires > now)
225               break;
226             cache.remove(entry.key);
227             iter.remove();
228           }
229       }
230   }
231   
232   /**
233    * An entry in the cache.
234    */
235   private static class Entry
236   {
237     /**
238      * The key by which this entry is referenced.
239      */
240     public final Object key;
241
242     /**
243      * The entry itself.  A null value indicates a failed lookup.
244      */
245     public final Object value;
246     
247     /**
248      * The time when this cache entry expires.  If set to -1 then
249      * this entry will never expire.  If set to 0 then this entry
250      * expires immediately and will not be inserted into the cache.
251      */
252     public final long expires;
253
254     /**
255      * Constructor.
256      */
257     public Entry(Object key, Object value)
258     {
259       this.key = key;
260       this.value = value;
261
262       int ttl = value != null ? POSITIVE_TTL : NEGATIVE_TTL;
263       if (ttl < 1)
264         expires = ttl;
265       else
266         expires = System.currentTimeMillis() + ttl * 1000;
267     }
268   }
269 }