如何配置主机名解析以在Java中使用自定义DNS服务器?

Pac*_*ier 10 java dns networking

默认情况下,java.net.InetAddress使用本地计算机的默认主机名解析程序解析主机名:

主机名到IP地址解析是通过使用本地机器配置信息和网络命名服务(如域名系统(DNS)和网络信息服务(NIS))的组合来完成的.默认情况下,正在使用的特定命名服务是本地计算机配置的一个.对于任何主机名,将返回其对应的IP地址.[资源]

如何在不修改本地计算机的默认主机名解析程序的情况下配置此行为?

例如,无论如何都要配置java.net.InetAddress,以便通过OpenDNS(208.67.222.222,208.67.220.220)或Google Public DNS(2001:4860:4860 :: 8888,2001:4860:4860)解析主机名: :8844)?

或者是显式创建DNS数据包请求的唯一解决方案,通过java.net.DatagramSocketjava.net.Socket将它们发送到服务器,并解析响应?

Pac*_*ace 11

Java 9删除了此功能.您将需要使用第三方DNS客户端库.

如果您使用的是Java 8或更早版本,则可以:

您可以sun.net.spi.nameservice.nameservers按照此站点的说明设置系统属性.

  • 不.如果你想使用`java.net.InetAddress`,那就没有.如果你可以使用不同的机制,那么你当然可以使用第三方DNS库(例如[dnsjava](http://www.dnsjava.org/)).这些属性可能改变的唯一真正原因是Oracle在未来的Java版本中是否会改进`java.net`实现.如果发生这种情况,他们也可能在那时为这个问题提供一个新的解决方案. (2认同)

Hap*_*ppy 11

Java提供了一个新的系统参数jdk.net.hosts.file来添加自定义的DNS记录,如源代码中指定的

 * The HostsFileNameService provides host address mapping
 * by reading the entries in a hosts file, which is specified by
 * {@code jdk.net.hosts.file} system property
 *
 * <p>The file format is that which corresponds with the /etc/hosts file
 * IP Address host alias list.
 *
 * <p>When the file lookup is enabled it replaces the default NameService
 * implementation
 *
 * @since 9
 */
private static final class HostsFileNameService implements NameService {
Run Code Online (Sandbox Code Playgroud)

示例:我们可以使用 JVM 选项启动 Java 应用程序

  • -Djdk.net.hosts.file=/path/to/alternative/hosts_file

内容可能是hosts_file

127.0.0.1    myserver-a.local
10.0.5.10    myserver-b.local
192.168.0.1  myserver-c.local
Run Code Online (Sandbox Code Playgroud)

它将根据以下内容执行 DNS 技巧hosts_file


Ska*_*out 10

使用以下接口并允许访问 java.net.* 可以使用带有 JDK8 和 JDK9 的自己的 DNS 提供程序。新的 Provider 是通过“INameService.install(new MyNameService());”安装的

public interface INameService extends InvocationHandler {
    public static void install(final INameService dns) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException, ClassNotFoundException {
        final Class<?> inetAddressClass = InetAddress.class;
        Object neu;
        Field nameServiceField;
        try {
            final Class<?> iface = Class.forName("java.net.InetAddress$NameService");
            nameServiceField = inetAddressClass.getDeclaredField("nameService");
            neu = Proxy.newProxyInstance(iface.getClassLoader(), new Class<?>[] { iface }, dns);
        } catch(final ClassNotFoundException|NoSuchFieldException e) {
            nameServiceField = inetAddressClass.getDeclaredField("nameServices");
            final Class<?> iface = Class.forName("sun.net.spi.nameservice.NameService");
            neu = Arrays.asList(Proxy.newProxyInstance(iface.getClassLoader(), new Class<?>[] { iface }, dns));
        }
        nameServiceField.setAccessible(true);
        nameServiceField.set(inetAddressClass, neu);
    }

    /**
     * Lookup a host mapping by name. Retrieve the IP addresses associated with a host
     *
     * @param host the specified hostname
     * @return array of IP addresses for the requested host
     * @throws UnknownHostException  if no IP address for the {@code host} could be found
     */
    InetAddress[] lookupAllHostAddr(final String host) throws UnknownHostException;

    /**
     * Lookup the host corresponding to the IP address provided
     *
     * @param addr byte array representing an IP address
     * @return {@code String} representing the host name mapping
     * @throws UnknownHostException
     *             if no host found for the specified IP address
     */
    String getHostByAddr(final byte[] addr) throws UnknownHostException;

    @Override default public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
        switch(method.getName()) {
        case "lookupAllHostAddr": return lookupAllHostAddr((String)args[0]);
        case "getHostByAddr"    : return getHostByAddr    ((byte[])args[0]);
        default                 :
            final StringBuilder o = new StringBuilder();
            o.append(method.getReturnType().getCanonicalName()+" "+method.getName()+"(");
            final Class<?>[] ps = method.getParameterTypes();
            for(int i=0;i<ps.length;++i) {
                if(i>0) o.append(", ");
                o.append(ps[i].getCanonicalName()).append(" p").append(i);
            }
            o.append(")");
            throw new UnsupportedOperationException(o.toString());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Yve*_*tin 6

对于不超过8的Java版本,这是我编写的用于foo对Java中的系统名称DNS解析进行硬编码以供测试用例通过的代码。它的优点是将您的特定条目附加到默认的Java运行时DNS解析中。

我建议不要在生产中运行它。使用强制反射访问和Java运行时非公共实现类!

private static final String FOO_IP = "10.10.8.111";

/** Fake "foo" DNS resolution */
@SuppressWarnings("restriction")
public static class MyHostNameService implements sun.net.spi.nameservice.NameService {
    @Override
    public InetAddress[] lookupAllHostAddr(String paramString) throws UnknownHostException {
        if ("foo".equals(paramString) || "foo.domain.tld".equals(paramString)) {
            final byte[] arrayOfByte = sun.net.util.IPAddressUtil.textToNumericFormatV4(FOO_IP);
            final InetAddress address = InetAddress.getByAddress(paramString, arrayOfByte);
            return new InetAddress[] { address };
        } else {
            throw new UnknownHostException();
        }
    }
    @Override
    public String getHostByAddr(byte[] paramArrayOfByte) throws UnknownHostException {
        throw new UnknownHostException();
    }
}

static {
    // Force to load fake hostname resolution for tests to pass
    try {
        List<sun.net.spi.nameservice.NameService> nameServices =
            (List<sun.net.spi.nameservice.NameService>)
            org.apache.commons.lang3.reflect.FieldUtils.readStaticField(InetAddress.class, "nameServices", true);
        nameServices.add(new MyHostNameService());
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}
Run Code Online (Sandbox Code Playgroud)

希望这会有所帮助,但再次小心使用!