【问题标题】:Cannot get hostname from getHostName无法从 getHostName 获取主机名
【发布时间】:2010-12-26 08:06:17
【问题描述】:

我正在尝试使用此方法获取主机名/计算机名。不幸的是,我只能获取本地主机,但不能获取其他计算机。

private String getHostName(String _strIP) {
    try {
        InetAddress inetAddress = InetAddress.getByName(_strIP);
        System.out.println("getHostAddress : " + inetAddress.getHostAddress());
        System.out.println("getHostName : " + inetAddress.getHostName());
        System.out.println("getCanonicalHostName : " + inetAddress.getCanonicalHostName());
        return inetAddress.getHostName();            
    } catch (UnknownHostException e) {
        e.printStackTrace();
    }
    return strDefaultHostName;
}

结果(不是本地主机)

getHostAddress : 192.168.2.139
getHostName : 192.168.2.139
getCanonicalHostName : 192.168.2.139

结果(本地主机)

getHostAddress : 127.0.0.1
getHostName : localhost
getCanonicalHostName : localhost

谢谢

【问题讨论】:

    标签: java


    【解决方案1】:

    我们已经大致确定了 tangens 回答中的问题所在。

    我认为您可以很简单地通过将主机名放入您的主机文件来解决问题。

    %SystemRoot%\system32\drivers\etc\hosts
    

    是您要查找的文件; localhost 在这里定义。您想为要解析的每个主机添加一个名称和地址行。

    我从来没有试过这个。如果它不起作用,你可以拿回你的钱。


    更新

    以上是“快速破解”解决方案。这实质上意味着每当有人手动更改您感兴趣的主机的 IP 地址时,必须同时更改任何想要访问这些主机的机器上的 hosts 文件。

    另一种选择是操作您自己的 DNS 服务器。当主机地址发生变化时,您仍然需要更新 IP 地址,但您只需在一个地方执行此操作,您就可以在整个网络中同时获得正向和反向名称解析。这需要更多的设置,但从长远来看更容易维护。

    这是一个非常有用的参考:http://www.dns.net/dnsrd/servers/windows.html

    他们提到“内置”Microsoft DNS 服务器是一个糟糕的解决方案(直到 Windows 2003 Server 中的那个),但至少提到了两个替代方案,一个是商业的,一个是免费的。 BIND 是目前在 DNS 方面将大部分 Internet 连接在一起的东西,而且它们也有 Windows 端口真是太好了。

    【讨论】:

      【解决方案2】:

      查看InetAddress.getHostName()(Sun JDK8)的源代码...

      该方法执行以下逻辑:

      1. 循环通过可用的sun.net.spi.nameservice.NameService's
      2. 执行反向 DNS 查找 - 例如192.168.0.23 -> frodo.baggins.com.au
      3. *检查java.lang.SecurityManager,查看“我们是否有权连接”到主机名
      4. *对主机名执行正向 DNS 查找,以防止欺骗 - 例如frodo.baggins.com.au -> 192.168.0.99
      5. 如果正向查找结果与原始地址匹配(例如192.168.0.23 == 192.168.0.99?),则返回主机名,否则返回getHostAddress()

      *如果第 3 步或第 4 步抛出 SecurityException/UnknownHostException,则返回 getHostAddress()

      对我来说,步骤 #2 成功解析了主机名,但在步骤 #4 失败并返回 UnknownHostException


      TLDR;您必须满足以下所有要求:

      1. SecurityManager 必须提供访问主机的权限
      2. 您必须能够转发和反向 DNS 查找您的InetAddress
      3. 正向查找详细信息必须与反向查找详细信息匹配

      只有这样 Java 才会给你主机名。


      或者,you could bypass these steps with the following method,然后获取主机名。

      @SuppressWarnings("unchecked")
      public static String getHostName(InetAddress addr) {
          String host = null;
          List<NameService> nameServicesImpl = new ArrayList<>();
          try {
              // do naughty things...
              Field nameServices = InetAddress.class.getDeclaredField("nameServices");
              nameServices.setAccessible(true);
              nameServicesImpl = (List<NameService>) nameServices.get(null);
          } catch (Throwable t) {
              throw new RuntimeException("Got caught doing naughty things.", t);
          }
          for (NameService nameService : nameServicesImpl) {
              try {
                  // lookup the hostname...
                  host = nameService.getHostByAddr(addr.getAddress());
              } catch (Throwable t) {
                  // NOOP: problem getting hostname from this name service, continue looping...
              }
          }
          return host != null ? host : addr.getHostAddress();
      }
      

      【讨论】:

      • 我在自己工作站上的两个程序之间建立连接时遇到了这个问题。我的主机名是 tom.initech.com;我们公司的内部 DNS 将其映射到我的 IP 地址,反之亦然。但是一些愚蠢的系统管理员在我的本地 /etc/hosts 文件中添加了一个条目,将我的主机名附加到 127.0.1.1。因此,正向匹配反向检查失败,我无法解析自己的主机名。
      • 请注意,此解决方案使用反射,这反过来对 InetAddress 的实现做出假设,根据定义,这可能会在任何未来的 Java 版本中中断。
      • @ThorbjørnRavnAndersen - 完全同意。
      【解决方案3】:

      您的 DNS 已损坏。然后返回 IP 号码。

      【讨论】:

      • 您能否详细说明如何解决该问题?我遇到了同样的问题,但我不知道如何为此配置 DNS。
      • @Bora 这是一个网络配置问题,您需要先修复它。与负责人交谈。如果是这样,您在超级用户网站上打开一个新问题
      • 我有这个问题,我的 DNS 没有损坏。命令行上的getent host 正确解析主机名; InetAddress:getHostName 没有。相反,问题在于 JDK 对此方法的特定规则,如 Nick Greely's answer 中所述。
      【解决方案4】:

      InetAddress.getCanonicalHostName() 的 javadoc 说:

      获取此 IP 地址的完全限定域名。尽力而为的方法,这意味着我们可能无法返回 FQDN,具体取决于底层系统配置。

      如果有安全管理器,这个方法首先调用它的checkConnect方法,以主机名和-1作为参数,看调用代码是否允许知道这个IP地址的主机名,即连接到主机. 如果不允许操作,则返回IP地址的文字表示

      您的系统配置似乎不正确。您是从小程序中运行的吗?

      【讨论】:

      • 这是一个基于 IP 地址的专用网络。很可能没有 DNS 服务器告诉他什么主机名对应于“192.168.2.139”。大多数便宜又好用的家用路由器不做内部 DNS,所以它不会映射回来
      • 我办公室的所有电脑都在运行 XP-pro 我知道需要在哪里配置才能允许此操作吗?谢谢
      【解决方案5】:

      为 Carl Smotricz 回复反馈

      很好的答案,但我们仍然不知道主机名是否已更新... 这就像我们硬编码一样。

      还是非常感谢你

      # Copyright (c) 1993-1999 Microsoft Corp.
      #
      # This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
      #
      # This file contains the mappings of IP addresses to host names. Each
      # entry should be kept on an individual line. The IP address should
      # be placed in the first column followed by the corresponding host name.
      # The IP address and the host name should be separated by at least one
      # space.
      #
      # Additionally, comments (such as these) may be inserted on individual
      # lines or following the machine name denoted by a '#' symbol.
      #
      # For example:
      #
      #      102.54.94.97     rhino.acme.com          # source server
      #       38.25.63.10     x.acme.com              # x client host
      
      127.0.0.1       localhost
      192.168.2.139       dev-testing
      

      【讨论】:

      • 不要将此作为答案发布,只需点击编辑更新问题即可。
      【解决方案6】:

      问题可能由多种原因引起。

      原因 1:IP 地址没有主机名

      这可能是最常见的原因,与安全管理员无关。

      如果 IP 地址由于没有主机名而无法解析为主机名,那么您会期望 getHostName() 返回 null 或抛出 UnknownHostException,但这不会发生。相反,getHostName() 只是将 IP 地址作为字符串再次返回。由于我不知道的原因,这种常见情况没有记录。

      所以如果IP地址与getHostName()返回的结果相同,则主机名不存在。

      详解

      以下 JDK 代码是导致此未记录问题的原因: https://github.com/openjdk/jdk/blob/jdk-17+35/src/java.base/share/classes/java/net/InetAddress.java#L697

      public class InetAddress implements java.io.Serializable {
          private static String getHostFromNameService(InetAddress addr, boolean check) {
              String host = null;
                  try {
                      // first lookup the hostname
                      host = nameService.getHostByAddr(addr.getAddress());
      
                      /* check to see if calling code is allowed to know
                       * the hostname for this IP address, ie, connect to the host
                       */
                      if (check) {
                          @SuppressWarnings("removal")
                          SecurityManager sec = System.getSecurityManager();
                          if (sec != null) {
                              sec.checkConnect(host, -1);
                          }
                      }
      
                      /* now get all the IP addresses for this hostname,
                       * and make sure one of them matches the original IP
                       * address. We do this to try and prevent spoofing.
                       */
      
                      InetAddress[] arr = InetAddress.getAllByName0(host, check);
                      boolean ok = false;
      
                      if(arr != null) {
                          for(int i = 0; !ok && i < arr.length; i++) {
                              ok = addr.equals(arr[i]);
                          }
                      }
      
                      //XXX: if it looks a spoof just return the address?
                      if (!ok) {
                          host = addr.getHostAddress();
                          return host;
                      }
                  } catch (SecurityException e) {
                      host = addr.getHostAddress();
                  } catch (UnknownHostException e) {
                      host = addr.getHostAddress();
                      // let next provider resolve the hostname
                  }
              return host;
          }
      }
      

      所以发生的情况是 IP 地址被传递给 NameService.getHostByAddr()NameService 是一个私有接口),它在源代码中有这个(私有)文档:

      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
      

      所以如果 IP 没有主机名,NameService.getHostByAddr() 会抛出 UnknownHostException,但 InetAddress.getHostFromNameService() 会吞下这个异常,而是返回提供的 IP 地址本身!!! IMO 它应该让异常被抛出而不是吞下它,因为吞下它会使客户端更难确定主机名是否存在。

      您可以使用nslookup 命令行工具检查IP 地址是否有主机名:nslookup 192.168.2.139。如果它返回类似: ** server can't find 139.2.168.192.in-addr.arpa: NXDOMAIN (Linux) 或 *** can't find 192.168.2.139: Non-existent domain (Windows) 则没有主机名。

      原因 2:应用了安全管理器

      默认情况下,Java 没有启用安全管理器。在这种情况下,这个理由不适用。

      安全管理器是为应用程序定义安全策略的对象。如果您有一个安全管理器并且想查明它是否是您的问题的原因,那么您应该检查它是否允许您打开一个连接到已解析主机名的套接字(如果有)。为此,首先使用nslookup 192.168.2.139 并验证主机名是否已解析。如果没有解析主机名,那么您的问题是由“原因 1”引起的。如果它确实解析为主机名,例如myhostname,那么试试这个:

      SecurityManager sec = System.getSecurityManager();
      if (sec != null) {
          sec.checkConnect("myhostname", -1);
      }
      

      如果 checkConnect() 抛出 SecurityException,则 SecurityManager 处于活动状态并导致问题。因此,您可以研究如何配置您的 securityManager 来解决问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-07-08
        • 1970-01-01
        • 1970-01-01
        • 2014-08-03
        • 2016-03-29
        • 2011-07-04
        • 2020-08-13
        相关资源
        最近更新 更多