Java DNS 缓存查看器
Posted
技术标签:
【中文标题】Java DNS 缓存查看器【英文标题】:Java DNS cache viewer 【发布时间】:2010-12-22 13:37:35 【问题描述】:有没有办法查看/转储 java.net api 使用的 DNS 缓存?
【问题讨论】:
我的理解是你必须有一个DNS服务器来执行缓存——主机本身不缓存DNS请求。 没有主机缓存。实际上,JRE(与安全管理器一起运行)和浏览器“固定”DNS 查找。 dns 解析器库通常会缓存 dns 结果,因此它们不必过多地打扰服务器,并且可以更快地响应调用 我不认为 java.net 做任何 DNS 缓存——这可能是由操作系统处理的,可能无法直接访问。 嗯.... 一点 google-fu 证明我错了。例如见verisign.com/stellent/groups/www_ndscs/documents/… 【参考方案1】:这是一个打印正负 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("***.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<InetAddress> 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<String, Object> cache = (Map<String, Object>) cf.get(addressCache);
for (Map.Entry<String, Object> 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<String> ads = new ArrayList<String>(addresses.length);
for (InetAddress address : addresses)
ads.add(address.getHostAddress());
System.out.println(hi.getKey() + " "+new Date(expires) +" " +ads);
【讨论】:
如果有人想使用安全管理器运行上述代码。 (或将其转换为在使用安全管理器运行的 servlet 引擎中运行),策略文件中的以下条目将有所帮助:grant permission java.lang.RuntimePermission "accessClassInPackage.sun.net";权限 java.lang.RuntimePermission "accessDeclaredMembers";权限 java.lang.reflect.ReflectPermission "suppressAccessChecks";权限 java.net.SocketPermission "*","accept,connect,resolve"; 如何在安卓上运行?【参考方案2】:java.net.InetAddress
使用缓存成功和不成功的主机名解析。
来自它的 javadoc:
InetAddress 类有一个缓存到 商店成功以及 主机名解析失败。
默认情况下,当安全管理器 安装,以防止 DNS 欺骗攻击的结果 积极的主机名解析是 永久缓存。当一个安全 没有安装manager,默认 行为是缓存条目 有限的(依赖于实现) 一段的时间。的结果 不成功的主机名解析是 缓存很短的时间 (10 秒)以提高性能。
如果默认行为不是 需要,然后是 Java 安全属性 可以设置为不同的生存时间 (TTL) 正缓存值。 同样,系统管理员可以配置 不同的负缓存 TTL 值 需要时。
两个Java安全属性控件 用于正和的 TTL 值 负主机名解析缓存:
networkaddress.cache.ttl 表示缓存策略 从名称中成功查找名称 服务。该值指定为 整数表示数量 秒缓存成功 抬头。默认设置是 特定于实现的缓存 一段时间。
值 -1 表示“缓存 永远”。
networkaddress.cache.negative.ttl(默认值:10) 表示缓存 不成功的名称查找策略 来自名称服务。值为 指定为整数以表示 缓存的秒数 不成功的查找失败。
值 0 表示“从不缓存”。 -1 值表示“缓存 永远”。
如果您的想法是转储 java.net.InetAddress
使用的缓存(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);
所以我怀疑你会发现任何开箱即用的方法,并猜测你必须通过反思来实现你的目标。
【讨论】:
【参考方案3】:以上答案在 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("***.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<InetAddress> 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<String, Object> cache = (Map<String, Object>) cf.get(addressCache);
for (Map.Entry<String, Object> 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<String> ads = new ArrayList<String>(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);
【讨论】:
【参考方案4】:上述答案不适用于 Java 11。在 Java 11 中,可以使用“缓存”实例变量检索正缓存条目和负缓存条目。 以下是新的改编:
import java.net.InetAddress;
import java.net.UnknownHostException;
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;
import java.util.concurrent.ConcurrentHashMap;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
public class DnsCacheFetcher
static long startTimeinNano = System.nanoTime();
public static void main(String[] args) throws Exception
System.out.println("SecurityManager: " + System.getSecurityManager());
InetAddress.getByName("***.com");
InetAddress.getByName("www.google.com");
InetAddress.getByName("www.yahoo.com");
InetAddress.getByName("www.ankit.com");
try
InetAddress.getByName("nowhere.example.com");
catch (UnknownHostException e)
System.out.println("Unknown host: " + e);
String addressCache = "cache";
System.out.println(">>>>" + addressCache);
printDNSCache(addressCache);
/*
* String negativeCache = "negativeCache"; System.out.println(">>>>" +
* negativeCache); printDNSCache(negativeCache);
*/
private static void printDNSCache(String cacheName) throws Exception
Class<InetAddress> klass = InetAddress.class;
Field[] fields = klass.getDeclaredFields();
/*
* for (Field field : fields) System.out.println(field.getName());
*/
Field acf = klass.getDeclaredField(cacheName);
acf.setAccessible(true);
Object addressCache = acf.get(null);
Class cacheKlass = addressCache.getClass();
Map<String, Object> cache = (Map<String, Object>) acf.get(addressCache);
for (Map.Entry<String, Object> hi : cache.entrySet())
/* System.out.println("Fetching cache for: " + hi.getKey()); */
Object cacheEntry = hi.getValue();
Class cacheEntryKlass = cacheEntry.getClass();
Field expf = cacheEntryKlass.getDeclaredField("expiryTime");
expf.setAccessible(true);
long expires = (Long) expf.get(cacheEntry);
Field af = cacheEntryKlass.getDeclaredField("inetAddresses");
af.setAccessible(true);
InetAddress[] addresses = (InetAddress[]) af.get(cacheEntry);
List<String> ads = null;
if (addresses != null)
ads = new ArrayList<String>(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. inetAddresses: " + ads);
*/
/*
* System.nanoTime() + 1000_000_000L * cachePolicy : this how java 11 set
* expiryTime
*/
System.out.println(hi.getKey() + " expires in approx " + (expires - startTimeinNano) / 1000_000_000L
+ " seconds. inetAddresses: " + ads);
【讨论】:
请注意,最后一个右大括号不在代码块中,但需要。所以不会让我编辑,因为更改不是至少 6 个字符。 已更正。谢谢!【参考方案5】:https://github.com/alibaba/java-dns-cache-manipulator
一个简单的 0 依赖线程安全 Java™ 库,用于以编程方式设置/查看 dns 而无需接触主机文件,使单元/集成测试可移植;以及用于设置/查看正在运行的 JVM 进程的 dns 的工具。
这个库/工具通过反射读取和设置java dns缓存,关注:
兼容不同的java版本(支持Java 6/8/11/17)。java.net.InetAddress
中的dns缓存实现在不同的java版本中有所不同。
线程安全
支持 IPv6
【讨论】:
以上是关于Java DNS 缓存查看器的主要内容,如果未能解决你的问题,请参考以下文章
Autodesk Forge 设计自动化/模型衍生 API - 查看器缓存 svf 文件