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
后的情况
输出与预期一样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());