Java DNS查询内部实现

本文探讨了Java中InetAddress类如何实现DNS查询,包括其内部的缓存策略和NameService链。Java DNS解析默认使用系统配置的nameserver,但可以通过设置系统属性来指定特定的DNS服务器。此外,自定义NameService可以通过ServiceLoader加载,允许应用程序按需覆盖默认行为。
摘要由CSDN通过智能技术生成
源码分析

在Java中,DNS相关的操作都是通过通过InetAddress提供的API实现的。比如查询域名对应的IP地址:

String dottedQuadIpAddress = InetAddress.getByName( "blog.arganzheng.me" ).getHostAddress();

或者反过来IP对应域名:

        InetAddress[] addresses = InetAddress.getAllByName("8.8.8.8"); // ip or DNS name
        for (int i = 0; i < addresses.length; i++) {
            String hostname = addresses[i].getHostName();
            System.out.println(hostname);
        }

输出:

google-public-dns-a.google.com

那么InetAddress是如何实现DNS解析的呢?让我们深入代码一步步挖掘下去:

import java.net.UnknownHostException;

public class InetAddress extends java.net.InetAddress implements java.io.Serializable {

    public static InetAddress getByName(String host)
            throws UnknownHostException {
        return InetAddress.getAllByName(host)[0];
    }

    public static InetAddress[] getAllByName(String host)
            throws UnknownHostException {
        return getAllByName(host, null);
    }

    private static InetAddress[] getAllByName(String host, InetAddress reqAddr)
            throws UnknownHostException {

        // ... 省略对于IPV6地址判断,HostName或者IP地址判断
        // hostname, resolve it
        return getAllByName0(host, reqAddr, true);
    }


    private static InetAddress[] getAllByName0(String host, InetAddress reqAddr, boolean check)
            throws UnknownHostException {

    /* If it gets here it is presumed to be a hostname */
    /* Cache.get can return: null, unknownAddress, or InetAddress[] */

    /* make sure the connection to the host is allowed, before we
     * give out a hostname
     */
        if (check) {  // 安全检查
            SecurityManager security = System.getSecurityManager();
            if (security != null) {
                security.checkConnect(host, -1);
            }
        }

        // 从DNS Cache中获取
        InetAddress[] addresses = getCachedAddresses(host);

    /* If no entry in cache, then do the host lookup */
        if (addresses == null) {
            addresses = getAddressesFromNameService(host, reqAddr);
        }

        if (addresses == unknown_array)
            throw new UnknownHostException(host);

        return addresses.clone();
    }

这里需要注意,JVM会先查询DNS缓存。有一个问题:默认的缓存时间是永远!这个是JDK实现中比较坑的地方。

InetAddress Caching

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".

如果Cache miss,那么就会调用配置的nameServices执行真正DNS查询:

private static InetAddress[] getAddressesFromNameService(String host, InetAddress reqAddr)
    throws UnknownHostException
{
    InetAddress[] addresses = null;
    boolean success = false;
    UnknownHostException ex = null;

    // Check whether the host is in the lookupTable.
    // 1) If the host isn't in the lookupTable when
    //    checkLookupTable() is called, checkLookupTable()
    //    would add the host in the lookupTable and
    //    return null. So we will do the lookup.
    // 2) If the host is in the lookupTable when
    //    checkLookupTable() is called, the current thread
    //    would be blocked until the host is removed
    //    from the lookupTable. Then this thread
    //    should try to look up the addressCache.
    //     i) if it found the addresses in the
    //        addressCache, checkLookupTable()  would
    //        return the addresses.
    //     ii) if it didn't find the addresses in the
    //         addressCache for any reason,
    //         it should add the host in the
    //         lookupTable and return null so the
    //         following code would do  a lookup itself.
    if
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值