Chandra Patni
Chandra Patni

Reputation: 17587

Java DNS cache viewer

Is there a way to view/dump DNS cached used by java.net api?

Upvotes: 21

Views: 26049

Answers (5)

oldratlee
oldratlee

Reputation: 2189

https://github.com/alibaba/java-dns-cache-manipulator

A simple 0-dependency thread-safe Java™ lib for setting/viewing dns programmatically without touching host file, make unit/integration test portable; and a tool for setting/viewing dns of running JVM process.

This lib/tool read and set java dns cache by reflection, with concerns:

  • compatibility with different java version(support Java 6/8/11/17).
    dns cache implementation in java.net.InetAddress is different in different java version.
  • thread-safety
  • support IPv6

Upvotes: 0

Ankit
Ankit

Reputation: 109

The above answer does not work with Java 11. In Java 11, both positive and negative cache entries can be retrieved using the 'cache' instance variable. Here are new adaptations:

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);


    }
}}

Upvotes: 2

Simon Flandergan
Simon Flandergan

Reputation: 51

Above answer does not work in Java 8 anymore. Here a slight adaption:

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);
        }
    }
}

Upvotes: 5

Chandra Patni
Chandra Patni

Reputation: 17587

Here is a script to print the positive and negative DNS address cache.

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);
    }
  }
}

Upvotes: 22

Pascal Thivent
Pascal Thivent

Reputation: 570475

The java.net.InetAddress uses caching of successful and unsuccessful host name resolutions.

From its javadoc:

The InetAddress class has a cache to store successful as well as unsuccessful host name resolutions.

By default, when a security manager is installed, in order to protect against DNS spoofing attacks, the result of positive host name resolutions are cached forever. When a security manager is not installed, the default behavior is to cache entries for a finite (implementation dependent) period of time. The result of unsuccessful host name resolution is cached for a very short period of time (10 seconds) to improve performance.

If the default behavior is not desired, then a Java security property can be set to a different Time-to-live (TTL) value for positive caching. Likewise, a system admin can configure a different negative caching TTL value when needed.

Two Java security properties control the TTL values used for positive and negative host name resolution caching:

  • networkaddress.cache.ttl
    Indicates the caching policy for successful name lookups from the name service. The value is specified as as integer to indicate the number of seconds to cache the successful lookup. The default setting is to cache for an implementation specific period of time.

    A value of -1 indicates "cache forever".

  • networkaddress.cache.negative.ttl (default: 10)
    Indicates the caching policy for un-successful name lookups from the name service. The value is specified as as integer to indicate the number of seconds to cache the failure for un-successful lookups.

    A value of 0 indicates "never cache". A value of -1 indicates "cache forever".

If what you have in mind is dumping the caches (of type java.net.InetAddress$Cache) used by java.net.InetAddress , they are internal implementation details and thus 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);

So I doubt you'll find anything doing this out of the box and guess that you'll have to play with reflection to achieve your goal.

Upvotes: 18

Related Questions