8304885: Reuse stale data to improve DNS resolver resiliency

Reviewed-by: michaelm, dfuchs
This commit is contained in:
Sergey Bylokhov 2023-06-09 23:20:06 +00:00
parent beec734fdd
commit bdd81b3182
16 changed files with 461 additions and 87 deletions

@ -52,6 +52,7 @@ import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.atomic.AtomicLong;
import java.util.Arrays;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Stream;
@ -191,9 +192,9 @@ import static java.net.spi.InetAddressResolver.LookupPolicy.IPV6_FIRST;
* <p> If the default behavior is not desired, then a Java security property
* can be set to a different Time-to-live (TTL) value for positive
* caching. Likewise, a system admin can configure a different
* negative caching TTL value when needed.
* negative caching TTL value when needed or extend the usage of the stale data.
*
* <p> Two Java security properties control the TTL values used for
* <p> Three Java security properties control the TTL values used for
* positive and negative host name resolution caching:
*
* <dl style="margin-left:2em">
@ -205,6 +206,25 @@ import static java.net.spi.InetAddressResolver.LookupPolicy.IPV6_FIRST;
* <p>
* A value of -1 indicates "cache forever".
* </dd>
* <dt><b>networkaddress.cache.stale.ttl</b></dt>
* <dd>Indicates the caching policy for stale names. The value is specified as
* an integer to indicate the number of seconds that stale names will be kept in
* the cache. A name is considered stale if the TTL has expired and an attempt
* to lookup the host name again was not successful. This property is useful if
* it is preferable to use a stale name rather than fail due to an unsuccessful
* lookup. The default setting is to cache for an implementation specific period
* of time.
* <p>
* If the value of this property is larger than "networkaddress.cache.ttl" then
* "networkaddress.cache.ttl" will be used as a refresh interval of the name in
* the cache. For example, if this property is set to 1 day and
* "networkaddress.cache.ttl" is set to 30 seconds, then the positive response
* will be cached for 1 day but an attempt to refresh it will be done every
* 30 seconds.
* <p>
* A value of 0 (zero) or if the property is not set means do not use stale
* names. Negative values are ignored.
* </dd>
* <dt><b>networkaddress.cache.negative.ttl</b> (default: 10)</dt>
* <dd>Indicates the caching policy for un-successful name lookups
* from the name service. The value is specified as an integer to
@ -933,7 +953,7 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
// CachedAddresses that have to expire are kept ordered in this NavigableSet
// which is scanned on each access
private static final NavigableSet<CachedAddresses> expirySet =
private static final NavigableSet<CachedLookup> expirySet =
new ConcurrentSkipListSet<>();
// common interface
@ -941,15 +961,22 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
InetAddress[] get() throws UnknownHostException;
}
// a holder for cached addresses with required metadata
private static final class CachedAddresses implements Addresses, Comparable<CachedAddresses> {
/**
* A cached result of a name service lookup. The result can be either valid
* addresses or invalid (ie a failed lookup) containing no addresses.
*/
private static class CachedLookup implements Addresses, Comparable<CachedLookup> {
private static final AtomicLong seq = new AtomicLong();
final String host;
final InetAddress[] inetAddresses;
final long expiryTime; // time of expiry (in terms of System.nanoTime())
volatile InetAddress[] inetAddresses;
/**
* Time of expiry (in terms of System.nanoTime()). Can be modified only
* when the record is not added to the "expirySet".
*/
volatile long expiryTime;
final long id = seq.incrementAndGet(); // each instance is unique
CachedAddresses(String host, InetAddress[] inetAddresses, long expiryTime) {
CachedLookup(String host, InetAddress[] inetAddresses, long expiryTime) {
this.host = host;
this.inetAddresses = inetAddresses;
this.expiryTime = expiryTime;
@ -964,7 +991,7 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
}
@Override
public int compareTo(CachedAddresses other) {
public int compareTo(CachedLookup other) {
// natural order is expiry time -
// compare difference of expiry times rather than
// expiry times directly, to avoid possible overflow.
@ -975,6 +1002,106 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
// ties are broken using unique id
return Long.compare(this.id, other.id);
}
/**
* Checks if the current cache record is expired or not. Expired records
* are removed from the expirySet and cache.
*
* @return {@code true} if the record was removed
*/
public boolean tryRemoveExpiredAddress(long now) {
// compare difference of time instants rather than
// time instants directly, to avoid possible overflow.
// (see System.nanoTime() recommendations...)
if ((expiryTime - now) < 0L) {
// ConcurrentSkipListSet uses weakly consistent iterator,
// so removing while iterating is OK...
if (expirySet.remove(this)) {
// ... remove from cache
cache.remove(host, this);
}
return true;
}
return false;
}
}
/**
* A cached valid lookup containing addresses whose validity may be
* temporarily extended by an additional stale period pending the mapping
* being refreshed or updated.
*/
private static final class ValidCachedLookup extends CachedLookup {
/**
* Time to refresh (in terms of System.nanoTime()).
*/
private volatile long refreshTime;
/**
* For how long the stale data should be used after TTL expiration.
* Initially equal to the expiryTime, but increased over time after each
* successful lookup.
*/
private volatile long staleTime;
/**
* only one thread is doing lookup to name service
* for particular host at any time.
*/
private final Lock lookupLock = new ReentrantLock();
ValidCachedLookup(String host, InetAddress[] inetAddresses,
long staleTime, long refreshTime)
{
super(host, inetAddresses, staleTime);
this.refreshTime = refreshTime;
this.staleTime = staleTime;
}
@Override
public InetAddress[] get() {
long now = System.nanoTime();
if ((refreshTime - now) < 0L && lookupLock.tryLock()) {
try {
// cachePolicy is in [s] - we need [ns]
refreshTime = now + InetAddressCachePolicy.get() * 1000_000_000L;
// getAddressesFromNameService returns non-empty/non-null value
inetAddresses = getAddressesFromNameService(host);
// don't update the "expirySet", will do that later
staleTime = refreshTime + InetAddressCachePolicy.getStale() * 1000_000_000L;
} catch (UnknownHostException ignore) {
} finally {
lookupLock.unlock();
}
}
return inetAddresses;
}
/**
* Overrides the parent method to skip deleting the record from the
* cache if the stale data can still be used. Note to update the
* "expiryTime" field we have to remove the record from the expirySet
* and add it back. It is not necessary to remove/add it here, we can do
* that in the "get()" method above, but extracting it minimizes
* contention on "expirySet".
*/
@Override
public boolean tryRemoveExpiredAddress(long now) {
// compare difference of time instants rather than
// time instants directly, to avoid possible overflow.
// (see System.nanoTime() recommendations...)
if ((expiryTime - now) < 0L) {
if ((staleTime - now) < 0L) {
return super.tryRemoveExpiredAddress(now);
}
// ConcurrentSkipListSet uses weakly consistent iterator,
// so removing while iterating is OK...
if (expirySet.remove(this)) {
expiryTime = staleTime;
expirySet.add(this);
}
}
return false;
}
}
// a name service lookup based Addresses implementation which replaces itself
@ -1021,18 +1148,33 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
if (cachePolicy == InetAddressCachePolicy.NEVER) {
cache.remove(host, this);
} else {
CachedAddresses cachedAddresses = new CachedAddresses(
host,
inetAddresses,
cachePolicy == InetAddressCachePolicy.FOREVER
? 0L
// cachePolicy is in [s] - we need [ns]
: System.nanoTime() + 1000_000_000L * cachePolicy
);
if (cache.replace(host, this, cachedAddresses) &&
long now = System.nanoTime();
long expiryTime =
cachePolicy == InetAddressCachePolicy.FOREVER ?
0L
// cachePolicy is in [s] - we need [ns]
: now + 1000_000_000L * cachePolicy;
CachedLookup cachedLookup;
if (InetAddressCachePolicy.getStale() > 0 &&
ex == null && expiryTime > 0)
{
long refreshTime = expiryTime;
// staleCachePolicy is in [s] - we need [ns]
expiryTime = refreshTime + 1000_000_000L *
InetAddressCachePolicy.getStale();
cachedLookup = new ValidCachedLookup(host,
inetAddresses,
expiryTime,
refreshTime);
} else {
cachedLookup = new CachedLookup(host,
inetAddresses,
expiryTime);
}
if (cache.replace(host, this, cachedLookup) &&
cachePolicy != InetAddressCachePolicy.FOREVER) {
// schedule expiry
expirySet.add(cachedAddresses);
expirySet.add(cachedLookup);
}
}
if (inetAddresses == null || inetAddresses.length == 0) {
@ -1638,18 +1780,8 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
// remove expired addresses from cache - expirySet keeps them ordered
// by expiry time so we only need to iterate the prefix of the NavigableSet...
long now = System.nanoTime();
for (CachedAddresses caddrs : expirySet) {
// compare difference of time instants rather than
// time instants directly, to avoid possible overflow.
// (see System.nanoTime() recommendations...)
if ((caddrs.expiryTime - now) < 0L) {
// ConcurrentSkipListSet uses weakly consistent iterator,
// so removing while iterating is OK...
if (expirySet.remove(caddrs)) {
// ... remove from cache
cache.remove(caddrs.host, caddrs);
}
} else {
for (CachedLookup caddrs : expirySet) {
if (!caddrs.tryRemoveExpiredAddress(now)) {
// we encountered 1st element that expires in future
break;
}
@ -1662,7 +1794,7 @@ public sealed class InetAddress implements Serializable permits Inet4Address, In
} else {
addrs = cache.remove(host);
if (addrs != null) {
if (addrs instanceof CachedAddresses) {
if (addrs instanceof CachedLookup) {
// try removing from expirySet too if CachedAddresses
expirySet.remove(addrs);
}

@ -1,6 +1,6 @@
<!DOCTYPE HTML>
<!--
Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
This code is free software; you can redistribute it and/or modify it
@ -267,13 +267,22 @@ tuning on how the cache is operating.</P>
policy, while a value of 0 (zero) means no caching. The default value
is -1 (forever) if a security manager is installed, and implementation-specific
when no security manager is installed.</P>
<LI><P><B>{@systemProperty networkaddress.cache.stale.ttl}</B> (default: see below)<BR>
Value is an integer corresponding to the number of seconds that stale names
will be kept in the cache. A name is considered stale if the TTL has expired
and an attempt to lookup the host name again was not successful. This
property is useful if it is preferable to use a stale name rather than
fail due to an unsuccessful lookup.
A value of 0 (zero) or if the property is not set means do not use stale
names. Negative values are ignored.
The default value is implementation-specific.</P>
<LI><P><B>{@systemProperty networkaddress.cache.negative.ttl}</B> (default: {@code 10})<BR>
Value is an integer corresponding to the number of seconds an
unsuccessful name lookup will be kept in the cache. A value of -1,
or any negative value, means &ldquo;cache forever&rdquo;, while a
value of 0 (zero) means no caching.</P>
</UL>
<P>Since these 2 properties are part of the security policy, they are
<P>Since these 3 properties are part of the security policy, they are
not set by either the -D option or the {@code System.setProperty()} API,
instead they are set as security properties.</P>
<a id="Unixdomain"></a>

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -27,6 +27,7 @@ package sun.net;
import java.security.PrivilegedAction;
import java.security.Security;
import java.util.concurrent.TimeUnit;
@SuppressWarnings("removal")
public final class InetAddressCachePolicy {
@ -36,6 +37,12 @@ public final class InetAddressCachePolicy {
private static final String cachePolicyPropFallback =
"sun.net.inetaddr.ttl";
// Controls the cache stale policy for successful lookups only
private static final String cacheStalePolicyProp =
"networkaddress.cache.stale.ttl";
private static final String cacheStalePolicyPropFallback =
"sun.net.inetaddr.stale.ttl";
// Controls the cache policy for negative lookups only
private static final String negativeCachePolicyProp =
"networkaddress.cache.negative.ttl";
@ -59,6 +66,15 @@ public final class InetAddressCachePolicy {
*/
private static volatile int cachePolicy = FOREVER;
/* The Java-level namelookup cache stale policy:
*
* any positive value: the number of seconds to use the stale names
* zero: do not use stale names
*
* default value is never (NEVER).
*/
private static volatile int staleCachePolicy = NEVER;
/* The Java-level namelookup cache policy for negative lookups:
*
* -1: caching forever
@ -85,31 +101,7 @@ public final class InetAddressCachePolicy {
* Initialize
*/
static {
Integer tmp = java.security.AccessController.doPrivileged(
new PrivilegedAction<Integer>() {
public Integer run() {
try {
String tmpString = Security.getProperty(cachePolicyProp);
if (tmpString != null) {
return Integer.valueOf(tmpString);
}
} catch (NumberFormatException ignored) {
// Ignore
}
try {
String tmpString = System.getProperty(cachePolicyPropFallback);
if (tmpString != null) {
return Integer.decode(tmpString);
}
} catch (NumberFormatException ignored) {
// Ignore
}
return null;
}
});
Integer tmp = getProperty(cachePolicyProp, cachePolicyPropFallback);
if (tmp != null) {
cachePolicy = tmp < 0 ? FOREVER : tmp;
propertySet = true;
@ -121,40 +113,60 @@ public final class InetAddressCachePolicy {
cachePolicy = DEFAULT_POSITIVE;
}
}
tmp = java.security.AccessController.doPrivileged (
new PrivilegedAction<Integer>() {
public Integer run() {
try {
String tmpString = Security.getProperty(negativeCachePolicyProp);
if (tmpString != null) {
return Integer.valueOf(tmpString);
}
} catch (NumberFormatException ignored) {
// Ignore
}
try {
String tmpString = System.getProperty(negativeCachePolicyPropFallback);
if (tmpString != null) {
return Integer.decode(tmpString);
}
} catch (NumberFormatException ignored) {
// Ignore
}
return null;
}
});
tmp = getProperty(negativeCachePolicyProp,
negativeCachePolicyPropFallback);
if (tmp != null) {
negativeCachePolicy = tmp < 0 ? FOREVER : tmp;
propertyNegativeSet = true;
}
if (cachePolicy > 0) {
tmp = getProperty(cacheStalePolicyProp,
cacheStalePolicyPropFallback);
if (tmp != null) {
staleCachePolicy = tmp;
}
}
}
private static Integer getProperty(String cachePolicyProp,
String cachePolicyPropFallback)
{
return java.security.AccessController.doPrivileged(
new PrivilegedAction<Integer>() {
public Integer run() {
try {
String tmpString = Security.getProperty(
cachePolicyProp);
if (tmpString != null) {
return Integer.valueOf(tmpString);
}
} catch (NumberFormatException ignored) {
// Ignore
}
try {
String tmpString = System.getProperty(
cachePolicyPropFallback);
if (tmpString != null) {
return Integer.decode(tmpString);
}
} catch (NumberFormatException ignored) {
// Ignore
}
return null;
}
});
}
public static int get() {
return cachePolicy;
}
public static int getStale() {
return staleCachePolicy;
}
public static int getNegative() {
return negativeCachePolicy;
}

@ -357,6 +357,17 @@ ssl.TrustManagerFactory.algorithm=PKIX
#
#networkaddress.cache.ttl=-1
#
# The Java-level namelookup cache stale policy:
#
# any positive value: the number of seconds to use the stale names
# zero: do not use stale names
# negative values are ignored
#
# default value is 0 (NEVER).
#
#networkaddress.cache.stale.ttl=0
# The Java-level namelookup cache policy for failed lookups:
#
# any negative value: cache forever

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -37,9 +37,27 @@ import impl.SimpleResolverProviderImpl;
* @library lib providers/simple
* @build test.library/testlib.ResolutionRegistry
* simple.provider/impl.SimpleResolverProviderImpl AddressesCachingTest
* @run testng/othervm -Djava.security.properties=${test.src}/NeverCache.props
* @run testng/othervm -Djava.security.properties=${test.src}/props/NeverCache.props
* -Dtest.cachingDisabled=true AddressesCachingTest
* @run testng/othervm -Djava.security.properties=${test.src}/ForeverCache.props
* @run testng/othervm -Djava.security.properties=${test.src}/props/ForeverCache.props
* -Dtest.cachingDisabled=false AddressesCachingTest
* @run testng/othervm
* -Djava.security.properties=${test.src}/props/NeverCacheIgnoreMinusStale.props
* -Dtest.cachingDisabled=true AddressesCachingTest
* @run testng/othervm
* -Djava.security.properties=${test.src}/props/NeverCacheIgnorePositiveStale.props
* -Dtest.cachingDisabled=true AddressesCachingTest
* @run testng/othervm
* -Djava.security.properties=${test.src}/props/NeverCacheIgnoreZeroStale.props
* -Dtest.cachingDisabled=true AddressesCachingTest
* @run testng/othervm
* -Djava.security.properties=${test.src}/props/ForeverCacheIgnoreMinusStale.props
* -Dtest.cachingDisabled=false AddressesCachingTest
* @run testng/othervm
* -Djava.security.properties=${test.src}/props/ForeverCacheIgnorePositiveStale.props
* -Dtest.cachingDisabled=false AddressesCachingTest
* @run testng/othervm
* -Djava.security.properties=${test.src}/props/ForeverCacheIgnoreZeroStale.props
* -Dtest.cachingDisabled=false AddressesCachingTest
*/
public class AddressesCachingTest {

@ -0,0 +1,147 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import impl.SimpleResolverProviderImpl;
import org.testng.Assert;
import org.testng.annotations.Test;
/*
* @test
* @summary Test that stale InetAddress caching security properties work as
* expected when a custom resolver is installed.
* @library lib providers/simple
* @build test.library/testlib.ResolutionRegistry
* simple.provider/impl.SimpleResolverProviderImpl AddressesStaleCachingTest
* @run testng/othervm -Djava.security.properties=${test.src}/props/CacheStale.props AddressesStaleCachingTest
*/
public class AddressesStaleCachingTest {
private static class Lookup {
private final byte[] address;
private final long timestamp;
private Lookup(byte[] address, long timestamp) {
this.address = address;
this.timestamp = timestamp;
}
}
/**
* Validates successful and unsuccessful lookups when the stale cache is
* enabled.
*/
@Test
public void testRefresh() throws Exception{
// The first request is to save the data into the cache
Lookup first = doLookup(false, 0);
Thread.sleep(10000); // intentionally big delay > x2 stale property
// The refreshTime is expired, we will do the successful lookup.
Lookup second = doLookup(false, 0);
Assert.assertNotEquals(first.timestamp, second.timestamp,
"Two lookups are expected");
Thread.sleep(10000); // intentionally big delay > x2 stale property
// The refreshTime is expired again, we will do the failed lookup.
Lookup third = doLookup(true, 0);
Assert.assertNotEquals(second.timestamp, third.timestamp,
"Two lookups are expected");
// The stale cache is enabled, so we should get valid/same data for
// all requests(even for the failed request).
Assert.assertEquals(first.address, second.address,
"Same address is expected");
Assert.assertEquals(second.address, third.address,
"Same address is expected");
}
/**
* Validates that only one thread is blocked during "refresh", all others
* will continue to use the "stale" data.
*/
@Test
public void testOnlyOneThreadIsBlockedDuringRefresh() throws Exception {
long timeout = System.nanoTime() + TimeUnit.SECONDS.toNanos(12);
doLookup(false, timeout);
Thread.sleep(9000);
CountDownLatch blockServer = new CountDownLatch(1);
SimpleResolverProviderImpl.setBlocker(blockServer);
Thread ts[] = new Thread[10];
CountDownLatch wait9 = new CountDownLatch(ts.length - 1);
CountDownLatch wait10 = new CountDownLatch(ts.length);
CountDownLatch start = new CountDownLatch(ts.length);
for (int i = 0; i < ts.length; i++) {
ts[i] = new Thread(() -> {
start.countDown();
try {
start.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
doLookup(true, timeout);
wait9.countDown();
wait10.countDown();
});
}
for (Thread t : ts) {
t.start();
}
if (!wait9.await(10, TimeUnit.SECONDS)) {
blockServer.countDown();
throw new RuntimeException("Some threads hang");
}
blockServer.countDown();
if (!wait10.await(10, TimeUnit.SECONDS)) {
throw new RuntimeException("The last thread hangs");
}
}
private static Lookup doLookup(boolean error, long timeout) {
SimpleResolverProviderImpl.setUnreachableServer(error);
try {
byte[] firstAddress = InetAddress.getByName("javaTest.org").getAddress();
long firstTimestamp = SimpleResolverProviderImpl.getLastLookupTimestamp();
byte[] secondAddress = InetAddress.getByName("javaTest.org").getAddress();
long secondTimestamp = SimpleResolverProviderImpl.getLastLookupTimestamp();
Assert.assertEquals(firstAddress, secondAddress,
"Same address is expected");
if (timeout == 0 || timeout - System.nanoTime() > 0) {
Assert.assertEquals(firstTimestamp, secondTimestamp,
"Only one positive lookup is expected with caching enabled");
}
return new Lookup(firstAddress, firstTimestamp);
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,3 @@
networkaddress.cache.ttl=7
networkaddress.cache.negative.ttl=3
networkaddress.cache.stale.ttl=30

@ -0,0 +1,3 @@
networkaddress.cache.ttl=-1
networkaddress.cache.negative.ttl=-1
networkaddress.cache.stale.ttl=-1

@ -0,0 +1,3 @@
networkaddress.cache.ttl=-1
networkaddress.cache.negative.ttl=-1
networkaddress.cache.stale.ttl=10000

@ -0,0 +1,3 @@
networkaddress.cache.ttl=-1
networkaddress.cache.negative.ttl=-1
networkaddress.cache.stale.ttl=0

@ -0,0 +1,3 @@
networkaddress.cache.ttl=0
networkaddress.cache.negative.ttl=0
networkaddress.cache.stale.ttl=-1

@ -0,0 +1,3 @@
networkaddress.cache.ttl=0
networkaddress.cache.negative.ttl=0
networkaddress.cache.stale.ttl=10000

@ -0,0 +1,3 @@
networkaddress.cache.ttl=0
networkaddress.cache.negative.ttl=0
networkaddress.cache.stale.ttl=0

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -31,6 +31,7 @@ import java.net.spi.InetAddressResolverProvider;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.logging.Logger;
import java.util.stream.Stream;
@ -41,6 +42,8 @@ public class SimpleResolverProviderImpl extends InetAddressResolverProvider {
public static ResolutionRegistry registry = new ResolutionRegistry();
private static List<LookupPolicy> LOOKUP_HISTORY = Collections.synchronizedList(new ArrayList<>());
private static volatile long LAST_LOOKUP_TIMESTAMP;
private static volatile boolean unreachableServer;
private static volatile CountDownLatch blocker;
private static Logger LOGGER = Logger.getLogger(SimpleResolverProviderImpl.class.getName());
@Override
@ -51,14 +54,27 @@ public class SimpleResolverProviderImpl extends InetAddressResolverProvider {
public Stream<InetAddress> lookupByName(String host, LookupPolicy lookupPolicy) throws UnknownHostException {
LOGGER.info("Looking-up addresses for '" + host + "'. Lookup characteristics:" +
Integer.toString(lookupPolicy.characteristics(), 2));
if (blocker != null) {
try {
blocker.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
LOOKUP_HISTORY.add(lookupPolicy);
LAST_LOOKUP_TIMESTAMP = System.nanoTime();
if (unreachableServer) {
throw new UnknownHostException("unreachableServer");
}
return registry.lookupHost(host, lookupPolicy);
}
@Override
public String lookupByAddress(byte[] addr) throws UnknownHostException {
LOGGER.info("Looking host name for the following address:" + ResolutionRegistry.addressBytesToString(addr));
if (unreachableServer) {
throw new UnknownHostException("unreachableServer");
}
return registry.lookupAddress(addr);
}
};
@ -73,6 +89,14 @@ public class SimpleResolverProviderImpl extends InetAddressResolverProvider {
return LAST_LOOKUP_TIMESTAMP;
}
public static void setUnreachableServer(boolean unreachableServer) {
SimpleResolverProviderImpl.unreachableServer = unreachableServer;
}
public static void setBlocker(CountDownLatch blocker) {
SimpleResolverProviderImpl.blocker = blocker;
}
public static LookupPolicy lookupPolicyHistory(int position) {
if (LOOKUP_HISTORY.isEmpty()) {
throw new RuntimeException("No registered lookup policies");