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 缓存查看器的主要内容,如果未能解决你的问题,请参考以下文章

不同操作系统上DNS客户端操作区别汇总

Autodesk Forge 设计自动化/模型衍生 API - 查看器缓存 svf 文件

VideoCacheView(视频缓存查看器)

是否需要 DNS 缓存管理器才能准确测试我的 Web 应用程序的性能?

请大家帮忙解决事件查看器中的ID29和17的问题!!!!!

DNS管理器——域名映射各个IP地址