001: // serverDNSCache.java
002: // -----------------------------
003: // (C) 2007 by Michael Peter Christen; mc@yacy.net, Frankfurt a. M., Germany
004: // first published 23.07.2007 on http://yacy.net
005: //
006: // $LastChangedDate: 2006-04-02 22:40:07 +0200 (So, 02 Apr 2006) $
007: // $LastChangedRevision: 1986 $
008: // $LastChangedBy: orbiter $
009: //
010: // LICENSE
011: //
012: // This program is free software; you can redistribute it and/or modify
013: // it under the terms of the GNU General Public License as published by
014: // the Free Software Foundation; either version 2 of the License, or
015: // (at your option) any later version.
016: //
017: // This program is distributed in the hope that it will be useful,
018: // but WITHOUT ANY WARRANTY; without even the implied warranty of
019: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
020: // GNU General Public License for more details.
021: //
022: // You should have received a copy of the GNU General Public License
023: // along with this program; if not, write to the Free Software
024: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
025:
026: package de.anomic.server;
027:
028: import java.net.InetAddress;
029: import java.net.UnknownHostException;
030: import java.util.Collections;
031: import java.util.HashMap;
032: import java.util.HashSet;
033: import java.util.Iterator;
034: import java.util.LinkedList;
035: import java.util.List;
036: import java.util.Map;
037: import java.util.Set;
038:
039: import de.anomic.kelondro.kelondroMScoreCluster;
040: import de.anomic.plasma.plasmaSwitchboard;
041:
042: public class serverDomains {
043:
044: // a dns cache
045: private static final Map<String, InetAddress> nameCacheHit = Collections
046: .synchronizedMap(new HashMap<String, InetAddress>()); // a not-synchronized map resulted in deadlocks
047: private static final Set<String> nameCacheMiss = Collections
048: .synchronizedSet(new HashSet<String>());
049: private static final kelondroMScoreCluster<String> nameCacheHitAges = new kelondroMScoreCluster<String>();
050: private static final kelondroMScoreCluster<String> nameCacheMissAges = new kelondroMScoreCluster<String>();
051: private static final int maxNameCacheHitAge = 24 * 60 * 60; // 24 hours in minutes
052: private static final int maxNameCacheMissAge = 24 * 60 * 60; // 24 hours in minutes
053: private static final int maxNameCacheHitSize = 3000;
054: private static final int maxNameCacheMissSize = 3000;
055: public static final List<String> nameCacheNoCachingPatterns = Collections
056: .synchronizedList(new LinkedList<String>());
057: private static final Set<String> nameCacheNoCachingList = Collections
058: .synchronizedSet(new HashSet<String>());
059: private static final long startTime = System.currentTimeMillis();
060:
061: /**
062: * Converts the time to a non negative int
063: *
064: * @param longTime Time in miliseconds since 01/01/1970 00:00 GMT
065: * @return int seconds since startTime
066: */
067: private static int intTime(long longTime) {
068: return (int) Math.max(0, ((longTime - startTime) / 1000));
069: }
070:
071: /**
072: * Does an DNS-Check to resolve a hostname to an IP.
073: *
074: * @param host Hostname of the host in demand.
075: * @return String with the ip. null, if the host could not be resolved.
076: */
077: public static InetAddress dnsResolveFromCache(String host)
078: throws UnknownHostException {
079: if ((host == null) || (host.length() == 0))
080: return null;
081: host = host.toLowerCase().trim();
082:
083: // trying to resolve host by doing a name cache lookup
084: InetAddress ip = (InetAddress) nameCacheHit.get(host);
085: if (ip != null)
086: return ip;
087:
088: if (nameCacheMiss.contains(host))
089: return null;
090: throw new UnknownHostException("host not in cache");
091: }
092:
093: public static InetAddress dnsResolve(String host) {
094: if ((host == null) || (host.length() == 0))
095: return null;
096: host = host.toLowerCase().trim();
097:
098: // trying to resolve host by doing a name cache lookup
099: InetAddress ip = (InetAddress) nameCacheHit.get(host);
100: if (ip != null)
101: return ip;
102:
103: if (nameCacheMiss.contains(host))
104: return null;
105: //System.out.println("***DEBUG dnsResolve(" + host + ")");
106: try {
107: boolean doCaching = true;
108: ip = InetAddress.getByName(host);
109: if ((ip == null)
110: || (ip.isLoopbackAddress())
111: || (nameCacheNoCachingList.contains(ip
112: .getHostName()))) {
113: doCaching = false;
114: } else {
115: Iterator<String> noCachingPatternIter = nameCacheNoCachingPatterns
116: .iterator();
117: String nextPattern;
118: while (noCachingPatternIter.hasNext()) {
119: nextPattern = noCachingPatternIter.next();
120: if (ip.getHostName().matches(nextPattern)) {
121: // disallow dns caching for this host
122: nameCacheNoCachingList.add(ip.getHostName());
123: doCaching = false;
124: break;
125: }
126: }
127: }
128:
129: if (doCaching) {
130: // remove old entries
131: flushHitNameCache();
132:
133: // add new entries
134: synchronized (nameCacheHit) {
135: nameCacheHit.put(ip.getHostName(), ip);
136: nameCacheHitAges.setScore(ip.getHostName(),
137: intTime(System.currentTimeMillis()));
138: }
139: }
140: return ip;
141: } catch (UnknownHostException e) {
142: // remove old entries
143: flushMissNameCache();
144:
145: // add new entries
146: nameCacheMiss.add(host);
147: nameCacheMissAges.setScore(host, intTime(System
148: .currentTimeMillis()));
149: }
150: return null;
151: }
152:
153: // /**
154: // * Checks wether an hostname already is in the DNS-cache.
155: // * FIXME: This method should use dnsResolve, as the code is 90% identical?
156: // *
157: // * @param host Searched for hostname.
158: // * @return true, if the hostname already is in the cache.
159: // */
160: // public static boolean dnsFetch(String host) {
161: // if ((nameCacheHit.get(host) != null) /*|| (nameCacheMiss.contains(host)) */) return false;
162: // try {
163: // String ip = InetAddress.getByName(host).getHostAddress();
164: // if ((ip != null) && (!(ip.equals("127.0.0.1"))) && (!(ip.equals("localhost")))) {
165: // nameCacheHit.put(host, ip);
166: // return true;
167: // }
168: // return false;
169: // } catch (UnknownHostException e) {
170: // //nameCacheMiss.add(host);
171: // return false;
172: // }
173: // }
174:
175: /**
176: * Returns the number of entries in the nameCacheHit map
177: *
178: * @return int The number of entries in the nameCacheHit map
179: */
180: public static int nameCacheHitSize() {
181: return nameCacheHit.size();
182: }
183:
184: public static int nameCacheMissSize() {
185: return nameCacheMiss.size();
186: }
187:
188: /**
189: * Returns the number of entries in the nameCacheNoCachingList list
190: *
191: * @return int The number of entries in the nameCacheNoCachingList list
192: */
193: public static int nameCacheNoCachingListSize() {
194: return nameCacheNoCachingList.size();
195: }
196:
197: /**
198: * Removes old entries from the dns hit cache
199: */
200: public static void flushHitNameCache() {
201: int cutofftime = intTime(System.currentTimeMillis())
202: - maxNameCacheHitAge;
203: String k;
204: while ((nameCacheHitAges.size() > maxNameCacheHitSize)
205: || (nameCacheHitAges.getMinScore() < cutofftime)) {
206: k = (String) nameCacheHitAges.getMinObject();
207: if (nameCacheHit.remove(k) == null)
208: break; // ensure termination
209: nameCacheHitAges.deleteScore(k);
210: }
211:
212: }
213:
214: /**
215: * Removes old entries from the dns miss cache
216: */
217: public static void flushMissNameCache() {
218: int cutofftime = intTime(System.currentTimeMillis())
219: - maxNameCacheMissAge;
220: String k;
221: while ((nameCacheMissAges.size() > maxNameCacheMissSize)
222: || (nameCacheMissAges.getMinScore() < cutofftime)) {
223: k = (String) nameCacheMissAges.getMinObject();
224: if (!nameCacheMiss.remove(k))
225: break; // ensure termination
226: nameCacheMissAges.deleteScore(k);
227: }
228:
229: }
230:
231: private static InetAddress[] localAddresses = null;
232: static {
233: try {
234: localAddresses = InetAddress.getAllByName(InetAddress
235: .getLocalHost().getHostName());
236: } catch (UnknownHostException e) {
237: localAddresses = new InetAddress[0];
238: }
239: }
240:
241: public static boolean isLocal(String address) {
242:
243: // attention! because this method does a dns resolve to look up an IP address,
244: // the result may be very slow. Consider 100 milliseconds per access
245:
246: assert (address != null);
247:
248: // check local ip addresses
249: if (address.equals("localhost")
250: || address.startsWith("127")
251: || address.startsWith("192.168")
252: || address.startsWith("10.")
253: || address.startsWith("169.254")
254: ||
255: // 172.16.0.0-172.31.255.255 (I think this is faster than a regex)
256: (address.startsWith("172.") && (address
257: .startsWith("172.16.")
258: || address.startsWith("172.17.")
259: || address.startsWith("172.18.")
260: || address.startsWith("172.19.")
261: || address.startsWith("172.20.")
262: || address.startsWith("172.21.")
263: || address.startsWith("172.22.")
264: || address.startsWith("172.23.")
265: || address.startsWith("172.24.")
266: || address.startsWith("172.25.")
267: || address.startsWith("172.26.")
268: || address.startsWith("172.27.")
269: || address.startsWith("172.28.")
270: || address.startsWith("172.29.")
271: || address.startsWith("172.30.") || address
272: .startsWith("172.31."))))
273: return true;
274:
275: // make a dns resolve if a hostname is given and check again
276: final InetAddress clientAddress = dnsResolve(address);
277: if (clientAddress != null) {
278: if ((clientAddress.isAnyLocalAddress())
279: || (clientAddress.isLoopbackAddress()))
280: return true;
281: if (address.charAt(0) > '9')
282: address = clientAddress.getHostAddress();
283: }
284:
285: // finally check if there are other local IP adresses that are not in
286: // the standard IP range
287: for (int i = 0; i < localAddresses.length; i++) {
288: if (localAddresses[i].equals(clientAddress))
289: return true;
290: }
291:
292: // the address must be a global address
293: return false;
294: }
295:
296: public static String myPublicIP() {
297: try {
298:
299: // if a static IP was configured, we have to return it here ...
300: plasmaSwitchboard sb = plasmaSwitchboard.getSwitchboard();
301: if (sb != null) {
302: String staticIP = sb.getConfig("staticIP", "");
303: if ((!staticIP.equals(""))) {
304: return staticIP;
305: }
306: }
307:
308: // If port forwarding was enabled we need to return the remote IP
309: // Address
310: if ((serverCore.portForwardingEnabled)
311: && (serverCore.portForwarding != null)) {
312: // does not return serverCore.portForwarding.getHost(), because
313: // hostnames are not valid, except in DebugMode
314: return InetAddress.getByName(
315: serverCore.portForwarding.getHost())
316: .getHostAddress();
317: }
318:
319: // otherwise we return the real IP address of this host
320: InetAddress pLIP = myPublicLocalIP();
321: if (pLIP != null)
322: return pLIP.getHostAddress();
323: return null;
324: } catch (java.net.UnknownHostException e) {
325: System.err.println("ERROR: (internal) " + e.getMessage());
326: return null;
327: }
328: }
329:
330: public static InetAddress myPublicLocalIP() {
331: try {
332: String hostName;
333: try {
334: hostName = InetAddress.getLocalHost().getHostName();
335: } catch (java.net.UnknownHostException e) {
336: hostName = "localhost"; // hopin' nothing serious happened only the hostname changed while running yacy
337: System.err.println("ERROR: (internal) "
338: + e.getMessage());
339: }
340: // list all addresses
341: InetAddress[] ia = InetAddress.getAllByName(hostName);
342: // for (int i = 0; i < ia.length; i++) System.out.println("IP: " +
343: // ia[i].getHostAddress()); // DEBUG
344: if (ia.length == 0) {
345: try {
346: return InetAddress.getLocalHost();
347: } catch (UnknownHostException e) {
348: try {
349: return InetAddress.getByName("127.0.0.1");
350: } catch (UnknownHostException ee) {
351: return null;
352: }
353: }
354: }
355: if (ia.length == 1) {
356: // only one network connection available
357: return ia[0];
358: }
359: // we have more addresses, find an address that is not local
360: int b0, b1;
361: for (int i = 0; i < ia.length; i++) {
362: b0 = 0Xff & ia[i].getAddress()[0];
363: b1 = 0Xff & ia[i].getAddress()[1];
364: if ((b0 != 10) && // class A reserved
365: (b0 != 127) && // loopback
366: ((b0 != 172) || (b1 < 16) || (b1 > 31)) && // class B reserved
367: ((b0 != 192) || (b1 != 168)) && // class C reserved
368: (ia[i].getHostAddress().indexOf(":") < 0))
369: return ia[i];
370: }
371: // there is only a local address, we filter out the possibly
372: // returned loopback address 127.0.0.1
373: for (int i = 0; i < ia.length; i++) {
374: if (((0Xff & ia[i].getAddress()[0]) != 127)
375: && (ia[i].getHostAddress().indexOf(":") < 0))
376: return ia[i];
377: }
378: // if all fails, give back whatever we have
379: for (int i = 0; i < ia.length; i++) {
380: if (ia[i].getHostAddress().indexOf(":") < 0)
381: return ia[i];
382: }
383: return ia[0];
384: } catch (java.net.UnknownHostException e) {
385: System.err.println("ERROR: (internal) " + e.getMessage());
386: return null;
387: }
388: }
389:
390: }
|