Java DNS缓存查看器

有没有办法查看/转储java.net api使用的DNS缓存?

这是一个打印正负DNS地址缓存的脚本。

import java.lang.reflect.Field; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; public class DNSCache { public static void main(String[] args) throws Exception { InetAddress.getByName("stackoverflow.com"); InetAddress.getByName("www.google.com"); InetAddress.getByName("www.yahoo.com"); InetAddress.getByName("www.example.com"); try { InetAddress.getByName("nowhere.example.com"); } catch (UnknownHostException e) { } String addressCache = "addressCache"; System.out.println(addressCache); printDNSCache(addressCache); String negativeCache = "negativeCache"; System.out.println(negativeCache); printDNSCache(negativeCache); } private static void printDNSCache(String cacheName) throws Exception { Class klass = InetAddress.class; Field acf = klass.getDeclaredField(cacheName); acf.setAccessible(true); Object addressCache = acf.get(null); Class cacheKlass = addressCache.getClass(); Field cf = cacheKlass.getDeclaredField("cache"); cf.setAccessible(true); Map cache = (Map) cf.get(addressCache); for (Map.Entry hi : cache.entrySet()) { Object cacheEntry = hi.getValue(); Class cacheEntryKlass = cacheEntry.getClass(); Field expf = cacheEntryKlass.getDeclaredField("expiration"); expf.setAccessible(true); long expires = (Long) expf.get(cacheEntry); Field af = cacheEntryKlass.getDeclaredField("address"); af.setAccessible(true); InetAddress[] addresses = (InetAddress[]) af.get(cacheEntry); List ads = new ArrayList(addresses.length); for (InetAddress address : addresses) { ads.add(address.getHostAddress()); } System.out.println(hi.getKey() + " "+new Date(expires) +" " +ads); } } } 

java.net.InetAddress使用成功和不成功主机名解析的缓存。

从它的javadoc:

InetAddress类具有用于存储成功和不成功主机名解析的缓存。

默认情况下,安装安全管理器时,为了防止DNS欺骗攻击,可以永久缓存正主机名解析的结果。 未安装安全管理器时,默认行为是缓存有限(依赖于实现)时间段的条目。 主机名解析失败的结果将在非常短的时间(10秒)内缓存,以提高性能。

如果不需要默认行为,则可以将Java安全属性设置为正缓存的不同生存时间(TTL)值。 同样,系统管理员可以在需要时配置不同的负缓存TTL值。

两个Java安全属性控制用于正面和负面主机名解析缓存的TTL值:

  • networkaddress.cache.ttl
    表示从名称服务成功进行名称查找的缓存策略。 该值指定为整数,以指示缓存成功查找的秒数。 默认设置是缓存特定于实现的时间段。

    值-1表示“永远缓存”。

  • networkaddress.cache.negative.ttl (默认值:10)
    表示名称服务中未成功的名称查找的缓存策略。 该值指定为整数,以指示缓存未成功查找失败的秒数。

    值为0表示“从不缓存”。 值-1表示“永远缓存”。

如果你想到的是转储java.net.InetAddress$Cache使用的缓存(类型为java.net.InetAddress$Cache ),它们是内部实现细节,因此是private

 /* * Cached addresses - our own litle nis, not! */ private static Cache addressCache = new Cache(Cache.Type.Positive); private static Cache negativeCache = new Cache(Cache.Type.Negative); 

所以我怀疑你会发现任何开箱即用的东西,并猜测你必须玩reflection来实现你的目标。

以上答案在Java 8中不再起作用。 这里稍作改编:

 import java.lang.reflect.Field; import java.net.InetAddress; import java.net.UnknownHostException; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; public class DNSCache { public static void main(String[] args) throws Exception { InetAddress.getByName("stackoverflow.com"); InetAddress.getByName("www.google.com"); InetAddress.getByName("www.yahoo.com"); InetAddress.getByName("www.example.com"); try { InetAddress.getByName("nowhere.example.com"); } catch (UnknownHostException e) { } String addressCache = "addressCache"; System.out.println(addressCache); printDNSCache(addressCache); String negativeCache = "negativeCache"; System.out.println(negativeCache); printDNSCache(negativeCache); } private static void printDNSCache(String cacheName) throws Exception { Class klass = InetAddress.class; Field acf = klass.getDeclaredField(cacheName); acf.setAccessible(true); Object addressCache = acf.get(null); Class cacheKlass = addressCache.getClass(); Field cf = cacheKlass.getDeclaredField("cache"); cf.setAccessible(true); Map cache = (Map) cf.get(addressCache); for (Map.Entry hi : cache.entrySet()) { Object cacheEntry = hi.getValue(); Class cacheEntryKlass = cacheEntry.getClass(); Field expf = cacheEntryKlass.getDeclaredField("expiration"); expf.setAccessible(true); long expires = (Long) expf.get(cacheEntry); Field af = cacheEntryKlass.getDeclaredField("addresses"); af.setAccessible(true); InetAddress[] addresses = (InetAddress[]) af.get(cacheEntry); List ads = new ArrayList(addresses.length); for (InetAddress address : addresses) { ads.add(address.getHostAddress()); } System.out.println(hi.getKey() + " expires in " + Instant.now().until(Instant.ofEpochMilli(expires), ChronoUnit.SECONDS) + " seconds " + ads); } } }