InetAddress.getCanonicalHostName()返回IP而不是Hostname

我在堆栈溢出中寻找了如何在Java中进行IP查找,但答案与我已经在做的事情相匹配,并没有解决我的问题。

这是我的代码:

public void printHostname( String ip ) { System.out.println( InetAddresses.forString( ip ).getCanonicalHostName( ) ); } 

InetAddresses只是一个来自guava库的实用程序类来获取InetAdress

问题:此代码与某些IP地址一样正常工作,而不是其他一些地址。

一个工作的例子

例如,对于IP 157.55.39.29,输出为:

 msnbot-157-55-39-29.search.msn.com 

根据Linux host命令,此结果似乎正确:

 > host 157.55.39.29 29.39.55.157.in-addr.arpa domain name pointer msnbot-157-55-39-29.search.msn.com. 

一个不成功的例子

对于IP 123.125.71.75, host命令返回:

 > host 123.125.71.75 75.71.125.123.in-addr.arpa domain name pointer baiduspider-123-125-71-75.crawl.baidu.com. 

但是我的Java代码的输出是:

 123.125.71.75 

而预期的产出应该是

 baiduspider-123-125-71-75.crawl.baidu.com 

getCanonicalHostName方法的javadoc说:

返回:
此IP地址的完全限定域名,或者如果安全检查不允许操作,则为IP地址的文本表示。

但我很确定这不是一个安全检查的问题……或者我不明白什么是错的。

你有什么建议来解释这种行为吗? 你有解决方法吗?

编辑#1

在寻找解决方案时,我尝试在JDK中逐步调试实现:

 // 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) { 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); 

在此代码中,变量host包含正确的值,但调用getAllByName0的最后一个语句抛出UnknownHostException ,该UnknownHostException通过仅返回请求的IP来处理。 内部方法getAddressesFromNameService抛出exception,并带有消息: "java.net.UnknownHostException: baiduspider-123-125-71-75.crawl.baidu.com"

我不知道为什么。

我可以绕过内部exception获取host变量值吗?

问题在于java.net.InetAdress具有针对ip-spoofing的特定过程。

它首先将名称解析为(a)IP地址。 这很好用。 在您的情况下,结果是两个IP地址。 然后, InetAdress会检查这些地址中是否(至少有一个)解析为原始输入名称。

如果他们不这样做,它只返回原来的IP地址。 下图显示检查baiduspider-123-125-71-75.crawl.baidu.com后的情况

调试后检查`原始ip反向查找 -/>名称 – > dns查找名称”> </p>
<p> 注意: <code>getAllByName0</code>解析的ip地址与via <code>nslookup</code>相同,即: </p>
<pre> <code>nslookup baiduspider-123-125-71-75.crawl.baidu.com Server: 192.168.2.1 Address: 192.168.2.1#53 Non-authoritative answer: Name: baiduspider-123-125-71-75.crawl.baidu.com Address: 62.157.140.133 Name: baiduspider-123-125-71-75.crawl.baidu.com Address: 80.156.86.78</code> </pre>
<p>  <strong>解决方案</strong>是使用dnsjava库。 它会跳过欺骗检查,因此工作正常。 </p>
<p>  <strong>dnsjava示例:</strong> </p>
<p> <code>String addr = Address.getHostName(InetAddress.getByName( 输出与预期一样baiduspider-123-125-71-75.crawl.baidu.com

免责声明:由于我是Java开发人员而非安全专家,我并不完全了解使用欺骗性IP地址的安全隐患。

我没有深入研究这个,所以我不知道它为什么会发生,但是一些检查DNS服务器健康状况的在线工具( 比如这个 )表明它们有一些可能相关或可能不相关的问题。

正如@jah所说,Java试图仔细检查主机名是否具有它所说的ip。 尝试执行此操作时,本机代码会抛出exception。 事实上,在我的情况下,尝试在命令行上validation,nslookup无法从名称中获取ip,这表明在DNS服务器上有一些配置阻止了这个(可能是故意的?我也不是DNS的专家) 。

当它工作时:

 $ nslookup msnbot-157-55-39-29.search.msn.com Server: 192.168.1.1 Address: 192.168.1.1#53 Non-authoritative answer: Name: msnbot-157-55-39-29.search.msn.com Address: 157.55.39.29 

什么时候不起作用:

 $ nslookup baiduspider-123-125-71-75.crawl.baidu.com Server: 192.168.1.1 Address: 192.168.1.1#53 ** server can't find baiduspider-123-125-71-75.crawl.baidu.com: NXDOMAIN 

当它工作时:

 $ getent hosts msnbot-157-55-39-29.search.msn.com 157.55.39.29 msnbot-157-55-39-29.search.msn.com 

当它没有:

 $ getent hosts baiduspider-123-125-71-75.crawl.baidu.com $ 

作为替代方案,您可以使用DNS服务提供程序进行JNDI 。 文档有一个例子,但我会留下一个工作片段供你测试:

 String[] octets = "123.125.71.75".split("\\."); String host = String.join(".", octets[3], octets[2], octets[1], octets[0], "in-addr.arpa"); Properties props = new Properties(); props.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory"); DirContext dirContext = new InitialDirContext(props); Attributes attrs = dirContext.getAttributes(host, new String[] {"PTR"}); System.out.println(attrs.get("PTR").get());