stasis/je-7.5.11/test/com/sleepycat/rep/utilint/LocalAliasNameService.java

239 lines
8.5 KiB
Java
Raw Permalink Normal View History

2019-06-25 20:12:40 +00:00
/*-
* Copyright (C) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
*
* This file was distributed by Oracle as part of a version of Oracle Berkeley
* DB Java Edition made available at:
*
* http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
*
* Please see the LICENSE file included in the top-level directory of the
* appropriate version of Oracle Berkeley DB Java Edition for a copy of the
* license and additional information.
*/
package com.sleepycat.je.rep.utilint;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Logger;
import sun.net.spi.nameservice.NameService;
import sun.net.spi.nameservice.NameServiceDescriptor;
import com.sleepycat.je.utilint.LoggerUtils;
/**
* Define a JDK name service provider that can be controlled by tests to
* simulate DNS failures. The idea is to define dummy DNS names that translate
* to the loopback address, and then undefine them as needed.
*
* <p>To use this class, you need to make a few modifications to the JVM that
* wants to use it: <ul>
*
* <li>Tell the JDK how to find this service provider by creating a resource,
* available in the class path, named
* <code>META-INF/services/sun.net.spi.nameservice.NameService</code>, which
* contains the fully qualified name of the {@link Descriptor} class. You can
* do that by create a file with that pathname relative to a component of the
* classpath.
*
* <li>Add the new name service provider as a second provider after the
* standard provider by setting the following system properties:
* <pre>
* -Dsun.net.spi.nameservice.provider.1=dns,default
* -Dsun.net.spi.nameservice.provider.2=dns,localalias
* </pre>
* Note that these properties need to be set on the command line so they are
* available at JVM startup time.
*
* <li>Disable the DNS cache so that changes made to this provider will take
* effect. Do this by calling {@link #setDNSCachePolicy} with 0 for both cache
* policies, and reverting to the original values afterwards.
*
* </ul>
*
* Although the name service provider facility is undocumented, at last check
* it appears to be supported by the J9 and icedtea JVM implementations.
*/
public class LocalAliasNameService implements NameService {
static final Logger logger =
LoggerUtils.getLoggerFixedPrefix(LocalAliasNameService.class, "Test");
/* Only referenced by getLocalHost */
private static InetAddress localHost = null;
private static boolean computingLocalHost = false;
/**
* The service descriptor that defines {@code LocalAliasNameService} as a
* "dns" provider named "localalias".
*/
public static class Descriptor implements NameServiceDescriptor {
@Override
public NameService createNameService() {
return new LocalAliasNameService();
}
@Override
public String getProviderName() {
return "localalias";
}
@Override
public String getType() {
return "dns";
}
}
private static final Set<String> aliases =
Collections.synchronizedSet(new HashSet<String>());
private LocalAliasNameService() {
logger.info("Created LocalAliasNameService");
}
/**
* Add a new alias for the loopback address.
*
* @param alias the new alias
*/
public static void addAlias(String alias) {
logger.info("LocalAliasNameService.addAlias: " + alias);
aliases.add(alias);
}
/**
* Remove an alias for the loopback address.
*
* @param alias the alias to remove
*/
public static void removeAlias(String alias) {
logger.info("LocalAliasNameService.removeAlias: " + alias);
aliases.remove(alias);
}
/**
* Remove all aliases for the loopback address.
*/
public static void clearAllAliases() {
logger.info("LocalAliasNameService.clearAllAliases");
aliases.clear();
}
/**
* Use reflection to set the internal DNS cache policy. The {@link
* InetAddress} class documents security properties for controlling this
* (networkaddress.cache.ttl and networkaddress.cache.negative.ttl), but
* those settings only take effect when they are specified before any
* address lookups are performed. The JUnit test infrastructure does
* address lookups before testing starts, so the security properties are
* not effective. Instead, use reflection to set the field values
* directly.
*
* <p>The cache policy values specify the time in milliseconds that the
* cache should remain valid, with 0 meaning don't cache and -1 meaning
* cache stays valid forever.
*
* @param positive the cache policy for successful lookups
* @param negative the cache policy for unsuccessful lookups
* @return an array of the previous cache policies, with the positive cache
* value appearing first
*/
public static int[] setDNSCachePolicy(int positive, int negative) {
try {
Class<?> cachePolicyClass
= Class.forName("sun.net.InetAddressCachePolicy");
Field positiveField =
cachePolicyClass.getDeclaredField("cachePolicy");
positiveField.setAccessible(true);
Field negativeField =
cachePolicyClass.getDeclaredField("negativeCachePolicy");
negativeField.setAccessible(true);
int[] result = new int[2];
synchronized (cachePolicyClass) {
result[0] = positiveField.getInt(null);
result[1] = negativeField.getInt(null);
positiveField.setInt(null, positive);
negativeField.setInt(null, negative);
}
return result;
} catch (Exception e) {
throw new RuntimeException(
"Unexpected exception when setting DNS cache: " + e, e);
}
}
/**
* This implementation returns the loopback address for hosts that match a
* current alias.
*/
@Override
public InetAddress[] lookupAllHostAddr(String host)
throws UnknownHostException {
if (!aliases.contains(host)) {
logger.info("LocalAliasNameService.lookupAllHostAddr:" +
" Unknown host: " + host);
throw new UnknownHostException("Unknown host: " + host);
}
final InetAddress lh = getLocalHost();
logger.info("LocalAliasNameService.lookupAllHostAddr:" + host +
" => " + lh);
return new InetAddress[] { lh };
}
/**
* Compute the local host if needed, avoiding circularities. The main name
* service provider should provide the local host when called from
* InetAddress.getLocalHost, so it should be OK to throw
* UnknownHostException if this method is called recursively.
*/
private static synchronized InetAddress getLocalHost()
throws UnknownHostException {
if (localHost == null) {
if (computingLocalHost) {
throw new UnknownHostException("Local host");
}
computingLocalHost = true;
try {
localHost = InetAddress.getLocalHost();
} finally {
computingLocalHost = false;
}
}
return localHost;
}
/**
* This implementation returns one of the current aliases if the argument
* matches the loopback address and there is at least one alias.
*/
@Override
public String getHostByAddr(byte[] addr)
throws UnknownHostException {
final InetAddress inetAddr = InetAddress.getByAddress(addr);
if (!getLocalHost().equals(inetAddr.getHostAddress())) {
logger.info("LocalAliasNameService.getHostByAddr:" +
" No mapping for address");
throw new UnknownHostException("No mapping for address");
}
synchronized (aliases) {
final Iterator<String> iter = aliases.iterator();
if (iter.hasNext()) {
String hostname = iter.next();
logger.info("LocalAliasNameService.getHostByAddr: " +
hostname);
return hostname;
}
}
logger.info("LocalAliasNameService.getHostByAddr:" +
" No mapping for address");
throw new UnknownHostException("No mapping for address");
}
}