问题可能由多种原因引起。
原因 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 来解决问题。