【问题标题】:Java DNS cache viewerJava DNS 缓存查看器
【发布时间】: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/…

标签: java networking dns


【解决方案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("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<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("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<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("stackoverflow.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

      【讨论】:

        猜你喜欢
        • 2016-12-16
        • 1970-01-01
        • 2019-08-04
        • 2012-08-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多