该应用程序运行在Linux(Centos 5)上的Java 5中。
-D
标志或调用System.setProperty
来设置networkaddress.cache.ttl
或networkaddress.cache.negative.ttl
作为系统属性,因为它们不是系统属性,而是安全属性。-D
标志或调用System.setProperty
),你需要设置以下系统属性:-Dsun.net.inetaddr.ttl=0
-D
标志,并选择从代码中调用此功能:java.security.Security.setProperty("networkaddress.cache.ttl" , "0")
这段代码必须在JVM中的任何其他代码尝试执行网络操作之前执行。
这很重要,因为例如,如果你在一个.war文件中调用了Security.setProperty
并部署了该.war到Tomcat,这将不起作用:Tomcat使用Java网络堆栈在你的.war代码执行之前初始化自身。由于这种“竞争条件”,通常更方便在启动JVM进程时使用-D
标志。
如果你不使用-Dsun.net.inetaddr.ttl=0
或调用Security.setProperty
,你需要编辑$JRE_HOME/lib/security/java.security
并在该文件中设置这些安全属性,例如:
networkaddress.cache.ttl = 0
networkaddress.cache.negative.ttl = 0
请注意评论中有关这些属性的安全警告。只有当您相当有信心不容易受到 DNS欺骗攻击时,才可以执行此操作。
值得注意的是, Oracle JDK 8文档推荐的方法是使用networkaddress.cache.ttl
和networkaddress.cache.negative.ttl
,正如官方文档中已经描述的那样。这两个属性无法通过命令行设置,而应该使用setProperty()
或使用安全策略文件进行设置。
Java在DNS缓存方面有一些非常奇怪的行为。最好的方法是关闭DNS缓存或将其设置为低于5秒的时间。
networkaddress.cache.ttl(默认值:-1)
指示名称服务中成功查找的缓存策略。该值指定为整数,以表示缓存成功查找的秒数。值为-1表示“永久缓存”。networkaddress.cache.negative.ttl(默认值:10)
指示名称服务中未成功查找的缓存策略。该值指定为整数,以表示对未成功查找的故障进行缓存的秒数。值为0表示“永不缓存”。值为-1表示“永久缓存”。
System.getSecurityManager()
。Java 8 文档链接:https://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getSecurityManager-- - gesellix显然这在更新的版本(SE 6和7)中已经得到了修正。当我运行以下代码片段时,使用tcpdump观察53端口活动,我最多会经历30秒的缓存时间。
/**
* https://dev59.com/sHM_5IYBdhLWcg3wt1rD
*
* Result: Java 6 distributed with Ubuntu 12.04 and Java 7 u15 downloaded from Oracle have
* an expiry time for dns lookups of approx. 30 seconds.
*/
import java.util.*;
import java.text.*;
import java.security.*;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
public class Test {
final static String hostname = "www.google.com";
public static void main(String[] args) {
// only required for Java SE 5 and lower:
//Security.setProperty("networkaddress.cache.ttl", "30");
System.out.println(Security.getProperty("networkaddress.cache.ttl"));
System.out.println(System.getProperty("networkaddress.cache.ttl"));
System.out.println(Security.getProperty("networkaddress.cache.negative.ttl"));
System.out.println(System.getProperty("networkaddress.cache.negative.ttl"));
while(true) {
int i = 0;
try {
makeRequest();
InetAddress inetAddress = InetAddress.getLocalHost();
System.out.println(new Date());
inetAddress = InetAddress.getByName(hostname);
displayStuff(hostname, inetAddress);
} catch (UnknownHostException e) {
e.printStackTrace();
}
try {
Thread.sleep(5L*1000L);
} catch(Exception ex) {}
i++;
}
}
public static void displayStuff(String whichHost, InetAddress inetAddress) {
System.out.println("Which Host:" + whichHost);
System.out.println("Canonical Host Name:" + inetAddress.getCanonicalHostName());
System.out.println("Host Name:" + inetAddress.getHostName());
System.out.println("Host Address:" + inetAddress.getHostAddress());
}
public static void makeRequest() {
try {
URL url = new URL("http://"+hostname+"/");
URLConnection conn = url.openConnection();
conn.connect();
InputStream is = conn.getInputStream();
InputStreamReader ird = new InputStreamReader(is);
BufferedReader rd = new BufferedReader(ird);
String res;
while((res = rd.readLine()) != null) {
System.out.println(res);
break;
}
rd.close();
} catch(Exception ex) {
ex.printStackTrace();
}
}
}
在拜伦的回答基础上,我认为您需要编辑位于%JRE_HOME%\lib\security
目录中的java.security
文件以实现此更改。
以下是相关部分:
#
# The Java-level namelookup cache policy for successful lookups:
#
# any negative value: caching forever
# any positive value: the number of seconds to cache an address for
# zero: do not cache
#
# default value is forever (FOREVER). For security reasons, this
# caching is made forever when a security manager is set. When a security
# manager is not set, the default behavior is to cache for 30 seconds.
#
# NOTE: setting this to anything other than the default value can have
# serious security implications. Do not set it unless
# you are sure you are not exposed to DNS spoofing attack.
#
#networkaddress.cache.ttl=-1
关于java.security
文件的文档在这里。
<jre-path>/lib/security/java.security
中设置属性networkaddress.cache.ttl
的值来调整DNS查找的缓存。请注意,这不是系统属性而是安全属性。我能够使用以下命令设置它:java.security.Security.setProperty("networkaddress.cache.ttl", "<value>");
这也可以通过系统属性-Dsun.net.inetaddr.ttl
进行设置,但是如果已经在其他位置设置了安全属性,则无法覆盖该属性。
我还想补充一点,如果您在WebSphere中的Web服务中遇到此问题(就像我一样),那么仅设置networkaddress.cache.ttl
是不够的。您需要将系统属性disableWSAddressCaching
设置为true
。与TTL属性不同,这可以作为JVM参数或通过System.setProperty
设置。
IBM在这里对WebSphere如何处理DNS缓存有一个非常详细的文章。与上面相关的一段是:
要禁用Web服务的地址缓存,请将附加的JVM自定义属性
disableWSAddressCaching
设置为true。使用此属性禁用Web服务的地址缓存。如果您的系统通常运行许多客户端线程,并且在wsAddrCache缓存上遇到锁争用,则可以将此自定义属性设置为true,以防止Web服务数据的缓存。
sun.net.inetaddr.ttl
是Sun实现的具体属性,"可能在未来的版本中不被支持"。 "优选的方式是使用安全性属性"networkaddress.cache.ttl
。
String tmpString = Security.getProperty("networkaddress.cache.ttl");
if (tmpString != null) {
tmp = Integer.valueOf(tmpString);
return;
}
...
String tmpString = System.getProperty("sun.net.inetaddr.ttl");
if (tmpString != null) {
tmp = Integer.valueOf(tmpString);
return;
}
...
if (tmp != null) {
cachePolicy = tmp < 0 ? FOREVER : tmp;
propertySet = true;
} else {
/* No properties defined for positive caching. If there is no
* security manager then use the default positive cache value.
*/
if (System.getSecurityManager() == null) {
cachePolicy = 30;
}
}
cachePolicy
值将设置为该数字,或者如果它们持有低于-1的值,则设置为-1(FOREVER)
。如果没有设置任何内容,则默认为30秒。事实证明,对于OpenJDK来说,几乎总是这种情况,因为默认情况下java.security
不设置该值,只设置负值。#networkaddress.cache.ttl=-1 <- this line is commented out
networkaddress.cache.negative.ttl=10
顺便提一下,如果未设置networkaddress.cache.negative.ttl
(从文件中删除),则Java类内部的默认值为0。文档在这方面是错误的。这就是让我犯错的原因。
String remoteIp = org.xbill.DNS.Address.getByName(remoteHostname).getHostAddress();
java.security.Security
(至少在 jdk7 中) - Pablo Fernandezjava.security
文件自 Java 11 起位于$JRE_HOME/conf/security/java.security
。参考链接 - jebeaudet