【Java踩坑记】——一个关于dns修改的需求

目录

问题描述

方案查找

解决问题


  • 问题描述

前些天遇到一个需求,我们需要修改本地的dns解析,去验证业务的正确性,想到的第一个方案就是修改本地的hosts文件。但是后来考量到这样需要频繁的修改本地磁盘文件。于是开始寻觅其他的方案。

  • 方案查找

经过一番持续不断的百度^_^,查找到一个工具包(https://github.com/tanhaichao/javahost)然后查看了一下他的核心代码

public class DnsImpl extends AbstractDns {
    //省略代码
    ……

    public boolean update(String host, String ip) {
        return this.update(host, new String[]{ip});
    }

    public boolean update(String host, String[] ips) {
        Object entry = this.createCacheEntry(host, ips);
        this.getAddressCache().put(host, entry);
        return true;
    }

   
    //省略代码
    ……
   
}
public abstract class AbstractDns implements Dns {
   
    // 省略代码
    ……

    protected Map<String, Object> getAddressCache() {
        try {
            Field cacheField = InetAddress.class.getDeclaredField("addressCache");
            cacheField.setAccessible(true);
            Object addressCache = cacheField.get(InetAddress.class);
            Class clazz = addressCache.getClass();
            Field cacheMapField = clazz.getDeclaredField("cache");
            cacheMapField.setAccessible(true);
            return (Map)cacheMapField.get(addressCache);
        } catch (Exception var5) {
            throw new RuntimeException(var5.getMessage(), var5);
        }
    }

   
    protected Object createCacheEntry(String host, String[] ips) {
        try {
            long expiration = System.currentTimeMillis() + 315360000000L;
            InetAddress[] addresses = new InetAddress[ips.length];

            for(int i = 0; i < addresses.length; ++i) {
                addresses[i] = InetAddress.getByAddress(host, InetAddress.getByName(ips[i]).getAddress());
            }

            String className = "java.net.InetAddress$CacheEntry";
            Class<?> clazz = Class.forName(className);
            Constructor<?> constructor = clazz.getDeclaredConstructors()[0];
            constructor.setAccessible(true);
            return constructor.newInstance(addresses, expiration);
        } catch (Exception var9) {
            throw new RuntimeException(var9.getMessage(), var9);
        }
    }


    // 省略代码
    ……
}

可以看到,这个工具类实际是通过反射机制,去修改了InetAddress中的cache值,来实现dns解析的修改。

  • 解决问题

在解决问题的过程中,当使用CloseableHttpClient发起get请求的时候,能够正常使用。分析具体原因如下:


public class DefaultHttpClientConnectionOperator implements HttpClientConnectionOperator {
    
    //省略代码
    ……

    public void connect(ManagedHttpClientConnection conn, HttpHost host, InetSocketAddress localAddress, int connectTimeout, SocketConfig socketConfig, HttpContext context) throws IOException {
        Lookup<ConnectionSocketFactory> registry = this.getSocketFactoryRegistry(context);
        ConnectionSocketFactory sf = (ConnectionSocketFactory)registry.lookup(host.getSchemeName());
        if (sf == null) {
            throw new UnsupportedSchemeException(host.getSchemeName() + " protocol is not supported");
        } else {
            InetAddress[] addresses = host.getAddress() != null ? new InetAddress[]{host.getAddress()} : this.dnsResolver.resolve(host.getHostName());
            int port = this.schemePortResolver.resolve(host);

            for(int i = 0; i < addresses.length; ++i) {
                InetAddress address = addresses[i];
                boolean last = i == addresses.length - 1;
                Socket sock = sf.createSocket(context);
                sock.setSoTimeout(socketConfig.getSoTimeout());
                sock.setReuseAddress(socketConfig.isSoReuseAddress());
                sock.setTcpNoDelay(socketConfig.isTcpNoDelay());
                sock.setKeepAlive(socketConfig.isSoKeepAlive());
                if (socketConfig.getRcvBufSize() > 0) {
                    sock.setReceiveBufferSize(socketConfig.getRcvBufSize());
                }

                if (socketConfig.getSndBufSize() > 0) {
                    sock.setSendBufferSize(socketConfig.getSndBufSize());
                }

                int linger = socketConfig.getSoLinger();
                if (linger >= 0) {
                    sock.setSoLinger(true, linger);
                }

                conn.bind(sock);
                InetSocketAddress remoteAddress = new InetSocketAddress(address, port);
                if (this.log.isDebugEnabled()) {
                    this.log.debug("Connecting to " + remoteAddress);
                }

                try {
                    sock = sf.connectSocket(connectTimeout, sock, host, remoteAddress, localAddress, context);
                    conn.bind(sock);
                    if (this.log.isDebugEnabled()) {
                        this.log.debug("Connection established " + conn);
                    }

                    return;
                } catch (SocketTimeoutException var19) {
                    if (last) {
                        throw new ConnectTimeoutException(var19, host, addresses);
                    }
                } catch (ConnectException var20) {
                    if (last) {
                        String msg = var20.getMessage();
                        if ("Connection timed out".equals(msg)) {
                            throw new ConnectTimeoutException(var20, host, addresses);
                        }

                        throw new HttpHostConnectException(var20, host, addresses);
                    }
                } catch (NoRouteToHostException var21) {
                    if (last) {
                        throw var21;
                    }
                }

                if (this.log.isDebugEnabled()) {
                    this.log.debug("Connect to " + remoteAddress + " timed out. " + "Connection will be retried using another IP address");
                }
            }

        }
    }

   
}

在上述关键代码中,我们看到CloseableHttpClient方法在做connect的时候,利用下述方法先获取DNS数据。

InetAddress[] addresses = host.getAddress() != null ? new InetAddress[]{host.getAddress()} : this.dnsResolver.resolve(host.getHostName());

this.dnsResolver.resolve(host.getHostName())跟踪该方法,我们能够看到它是根据InetAddress.getAllByName获取到相应的DNS解析记录,然后根据dns解析去建立socket链接。

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

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值