【问题标题】:How to configure hostname resolution to use a custom DNS server in Java?如何配置主机名解析以在 Java 中使用自定义 DNS 服务器?
【发布时间】:2012-07-23 18:15:16
【问题描述】:

java.net.InetAddress 默认使用本地机器的默认主机名解析器解析主机名:

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

我们如何在不修改本地机器的默认主机名解析器的情况下配置此行为?

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

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

【问题讨论】:

    标签: java networking dns


    【解决方案1】:

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

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

    您可以按照this site. 的文档设置系统属性sun.net.spi.nameservice.nameservers

    【讨论】:

    • 如同一站点中所述:“未来版本可能不支持这些属性。”还有其他方法可以实现吗?
    • 没有。如果您想使用java.net.InetAddress,则不是。如果您可以使用不同的机制,那么您当然可以使用 3rd 方 DNS 库(例如 dnsjava )。属性可能发生变化的唯一真正原因是,如果 Oracle 在 Java 的某些未来版本中彻底检查了 java.net 实现。如果发生这种情况,他们很可能会在那个时候为这个问题提供一个新的解决方案。
    • 他们在 Java 9 中彻底改造了 java.net,因此实现这一目标所需的 sun.net 类不再存在。
    • 你是对的。您将需要使用从 Java 9 开始的第三方库。
    【解决方案2】:

    通过以下接口并允许访问 java.net.*,可以将自己的 DNS 提供程序与 JDK8 和 JDK9 一起使用。新的提供者通过安装 "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());
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      对于不超过 8 的 Java 版本,这是我编写的代码,用于在 Java 中硬编码 foo 系统名称 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();
          }
      }
      

      希望这会有所帮助,但还是要小心使用!

      【讨论】:

        【解决方案4】:

        Java 提供了一个新的系统参数 jdk.net.hosts.file 用于添加自定义 DNS 记录,如 source code 中指定的那样

         * 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 {
        

        示例:我们可以使用 JVM Option 启动 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
        

        它将根据 hosts_file 执行 DNS 技巧

        【讨论】:

          猜你喜欢
          • 2013-06-30
          • 2022-11-01
          • 2010-11-21
          • 2022-11-01
          • 1970-01-01
          • 2021-04-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多