Have hosts file support for DnsNameResolver, close #4074
Motivation: On contrary to `DefaultNameResolver`, `DnsNameResolver` doesn't currently honor hosts file. Modifications: * Introduce `HostsFileParser` that parses `/etc/hosts` or `C:\Windows\system32\drivers\etc\hosts` depending on the platform * Introduce `HostsFileEntriesResolver` that uses the former to resolve host names * Make `DnsNameResolver` check his `HostsFileEntriesResolver` prior to trying to resolve names against the DNS server * Introduce `DnsNameResolverBuilder` so we now have a builder for `DnsNameResolver`s * Additionally introduce a `CompositeNameResolver` that takes several `NameResolver`s and tries to resolve names by delegating sequentially * Change `DnsNameResolver.asAddressResolver` to return a composite and honor hosts file Result: Hosts file support when using `DnsNameResolver`. Consistent behavior with JDK implementation.
This commit is contained in:
parent
4e467f5c6f
commit
8d4db050f3
@ -32,4 +32,48 @@ public final class ObjectUtil {
|
|||||||
}
|
}
|
||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that the given argument is strictly positive. If it is, throws {@link IllegalArgumentException}.
|
||||||
|
* Otherwise, returns the argument.
|
||||||
|
*/
|
||||||
|
public static int checkPositive(int i, String name) {
|
||||||
|
if (i <= 0) {
|
||||||
|
throw new IllegalArgumentException(name + ": " + i + " (expected: > 0)");
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that the given argument is strictly positive. If it is, throws {@link IllegalArgumentException}.
|
||||||
|
* Otherwise, returns the argument.
|
||||||
|
*/
|
||||||
|
public static long checkPositive(long i, String name) {
|
||||||
|
if (i <= 0) {
|
||||||
|
throw new IllegalArgumentException(name + ": " + i + " (expected: > 0)");
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that the given argument is positive or zero. If it is, throws {@link IllegalArgumentException}.
|
||||||
|
* Otherwise, returns the argument.
|
||||||
|
*/
|
||||||
|
public static int checkPositiveOrZero(int i, String name) {
|
||||||
|
if (i < 0) {
|
||||||
|
throw new IllegalArgumentException(name + ": " + i + " (expected: >= 0)");
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that the given argument is neither null nor empty.
|
||||||
|
* If it is, throws {@link NullPointerException} or {@link IllegalArgumentException}.
|
||||||
|
* Otherwise, returns the argument.
|
||||||
|
*/
|
||||||
|
public static <T> T[] checkNonEmpty(T[] array, String name) {
|
||||||
|
checkNotNull(array, name);
|
||||||
|
checkPositive(array.length, name + ".length");
|
||||||
|
return array;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,11 @@ public class DnsAddressResolverGroup extends AddressResolverGroup<InetSocketAddr
|
|||||||
EventLoop eventLoop, ChannelFactory<? extends DatagramChannel> channelFactory,
|
EventLoop eventLoop, ChannelFactory<? extends DatagramChannel> channelFactory,
|
||||||
InetSocketAddress localAddress, DnsServerAddresses nameServerAddresses) throws Exception {
|
InetSocketAddress localAddress, DnsServerAddresses nameServerAddresses) throws Exception {
|
||||||
|
|
||||||
return new DnsNameResolver(eventLoop, channelFactory, localAddress, nameServerAddresses)
|
return new DnsNameResolverBuilder(eventLoop)
|
||||||
|
.channelFactory(channelFactory)
|
||||||
|
.localAddress(localAddress)
|
||||||
|
.nameServerAddresses(nameServerAddresses)
|
||||||
|
.build()
|
||||||
.asAddressResolver();
|
.asAddressResolver();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@ import io.netty.channel.ChannelInboundHandlerAdapter;
|
|||||||
import io.netty.channel.ChannelInitializer;
|
import io.netty.channel.ChannelInitializer;
|
||||||
import io.netty.channel.EventLoop;
|
import io.netty.channel.EventLoop;
|
||||||
import io.netty.channel.FixedRecvByteBufAllocator;
|
import io.netty.channel.FixedRecvByteBufAllocator;
|
||||||
import io.netty.channel.ReflectiveChannelFactory;
|
|
||||||
import io.netty.channel.socket.DatagramChannel;
|
import io.netty.channel.socket.DatagramChannel;
|
||||||
import io.netty.channel.socket.InternetProtocolFamily;
|
import io.netty.channel.socket.InternetProtocolFamily;
|
||||||
import io.netty.handler.codec.dns.DatagramDnsQueryEncoder;
|
import io.netty.handler.codec.dns.DatagramDnsQueryEncoder;
|
||||||
@ -33,6 +32,7 @@ import io.netty.handler.codec.dns.DatagramDnsResponse;
|
|||||||
import io.netty.handler.codec.dns.DatagramDnsResponseDecoder;
|
import io.netty.handler.codec.dns.DatagramDnsResponseDecoder;
|
||||||
import io.netty.handler.codec.dns.DnsQuestion;
|
import io.netty.handler.codec.dns.DnsQuestion;
|
||||||
import io.netty.handler.codec.dns.DnsResponse;
|
import io.netty.handler.codec.dns.DnsResponse;
|
||||||
|
import io.netty.resolver.HostsFileEntriesResolver;
|
||||||
import io.netty.resolver.InetNameResolver;
|
import io.netty.resolver.InetNameResolver;
|
||||||
import io.netty.util.NetUtil;
|
import io.netty.util.NetUtil;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
@ -56,7 +56,7 @@ import java.util.Map.Entry;
|
|||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
import static io.netty.util.internal.ObjectUtil.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A DNS-based {@link InetNameResolver}.
|
* A DNS-based {@link InetNameResolver}.
|
||||||
@ -67,7 +67,7 @@ public class DnsNameResolver extends InetNameResolver {
|
|||||||
|
|
||||||
static final InetSocketAddress ANY_LOCAL_ADDR = new InetSocketAddress(0);
|
static final InetSocketAddress ANY_LOCAL_ADDR = new InetSocketAddress(0);
|
||||||
|
|
||||||
private static final InternetProtocolFamily[] DEFAULT_RESOLVE_ADDRESS_TYPES = new InternetProtocolFamily[2];
|
static final InternetProtocolFamily[] DEFAULT_RESOLVE_ADDRESS_TYPES = new InternetProtocolFamily[2];
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// Note that we did not use SystemPropertyUtil.getBoolean() here to emulate the behavior of JDK.
|
// Note that we did not use SystemPropertyUtil.getBoolean() here to emulate the behavior of JDK.
|
||||||
@ -97,7 +97,7 @@ public class DnsNameResolver extends InetNameResolver {
|
|||||||
/**
|
/**
|
||||||
* Cache for {@link #doResolve(String, Promise)} and {@link #doResolveAll(String, Promise)}.
|
* Cache for {@link #doResolve(String, Promise)} and {@link #doResolveAll(String, Promise)}.
|
||||||
*/
|
*/
|
||||||
final ConcurrentMap<String, List<DnsCacheEntry>> resolveCache = PlatformDependent.newConcurrentHashMap();
|
private final ConcurrentMap<String, List<DnsCacheEntry>> resolveCache = PlatformDependent.newConcurrentHashMap();
|
||||||
|
|
||||||
private final FastThreadLocal<DnsServerAddressStream> nameServerAddrStream =
|
private final FastThreadLocal<DnsServerAddressStream> nameServerAddrStream =
|
||||||
new FastThreadLocal<DnsServerAddressStream>() {
|
new FastThreadLocal<DnsServerAddressStream>() {
|
||||||
@ -107,68 +107,18 @@ public class DnsNameResolver extends InetNameResolver {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private final DnsResponseHandler responseHandler = new DnsResponseHandler();
|
private final long queryTimeoutMillis;
|
||||||
|
|
||||||
private volatile long queryTimeoutMillis = 5000;
|
|
||||||
|
|
||||||
// The default TTL values here respect the TTL returned by the DNS server and do not cache the negative response.
|
// The default TTL values here respect the TTL returned by the DNS server and do not cache the negative response.
|
||||||
private volatile int minTtl;
|
private final int minTtl;
|
||||||
private volatile int maxTtl = Integer.MAX_VALUE;
|
private final int maxTtl;
|
||||||
private volatile int negativeTtl;
|
private final int negativeTtl;
|
||||||
private volatile int maxQueriesPerResolve = 3;
|
private final int maxQueriesPerResolve;
|
||||||
private volatile boolean traceEnabled = true;
|
private final boolean traceEnabled;
|
||||||
|
private final InternetProtocolFamily[] resolvedAddressTypes;
|
||||||
private volatile InternetProtocolFamily[] resolveAddressTypes = DEFAULT_RESOLVE_ADDRESS_TYPES;
|
private final boolean recursionDesired;
|
||||||
private volatile boolean recursionDesired = true;
|
private final int maxPayloadSize;
|
||||||
|
private final boolean optResourceEnabled;
|
||||||
private volatile int maxPayloadSize;
|
private final HostsFileEntriesResolver hostsFileEntriesResolver;
|
||||||
private volatile boolean optResourceEnabled = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new DNS-based name resolver that communicates with the specified list of DNS servers.
|
|
||||||
*
|
|
||||||
* @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers
|
|
||||||
* @param channelType the type of the {@link DatagramChannel} to create
|
|
||||||
* @param nameServerAddresses the addresses of the DNS server. For each DNS query, a new stream is created from
|
|
||||||
* this to determine which DNS server should be contacted for the next retry in case
|
|
||||||
* of failure.
|
|
||||||
*/
|
|
||||||
public DnsNameResolver(
|
|
||||||
EventLoop eventLoop, Class<? extends DatagramChannel> channelType,
|
|
||||||
DnsServerAddresses nameServerAddresses) {
|
|
||||||
this(eventLoop, channelType, ANY_LOCAL_ADDR, nameServerAddresses);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new DNS-based name resolver that communicates with the specified list of DNS servers.
|
|
||||||
*
|
|
||||||
* @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers
|
|
||||||
* @param channelType the type of the {@link DatagramChannel} to create
|
|
||||||
* @param localAddress the local address of the {@link DatagramChannel}
|
|
||||||
* @param nameServerAddresses the addresses of the DNS server. For each DNS query, a new stream is created from
|
|
||||||
* this to determine which DNS server should be contacted for the next retry in case
|
|
||||||
* of failure.
|
|
||||||
*/
|
|
||||||
public DnsNameResolver(
|
|
||||||
EventLoop eventLoop, Class<? extends DatagramChannel> channelType,
|
|
||||||
InetSocketAddress localAddress, DnsServerAddresses nameServerAddresses) {
|
|
||||||
this(eventLoop, new ReflectiveChannelFactory<DatagramChannel>(channelType), localAddress, nameServerAddresses);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new DNS-based name resolver that communicates with the specified list of DNS servers.
|
|
||||||
*
|
|
||||||
* @param eventLoop the {@link EventLoop} which will perform the communication with the DNS servers
|
|
||||||
* @param channelFactory the {@link ChannelFactory} that will create a {@link DatagramChannel}
|
|
||||||
* @param nameServerAddresses the addresses of the DNS server. For each DNS query, a new stream is created from
|
|
||||||
* this to determine which DNS server should be contacted for the next retry in case
|
|
||||||
* of failure.
|
|
||||||
*/
|
|
||||||
public DnsNameResolver(
|
|
||||||
EventLoop eventLoop, ChannelFactory<? extends DatagramChannel> channelFactory,
|
|
||||||
DnsServerAddresses nameServerAddresses) {
|
|
||||||
this(eventLoop, channelFactory, ANY_LOCAL_ADDR, nameServerAddresses);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new DNS-based name resolver that communicates with the specified list of DNS servers.
|
* Creates a new DNS-based name resolver that communicates with the specified list of DNS servers.
|
||||||
@ -179,22 +129,58 @@ public class DnsNameResolver extends InetNameResolver {
|
|||||||
* @param nameServerAddresses the addresses of the DNS server. For each DNS query, a new stream is created from
|
* @param nameServerAddresses the addresses of the DNS server. For each DNS query, a new stream is created from
|
||||||
* this to determine which DNS server should be contacted for the next retry in case
|
* this to determine which DNS server should be contacted for the next retry in case
|
||||||
* of failure.
|
* of failure.
|
||||||
|
* @param minTtl the minimum TTL of cached DNS records
|
||||||
|
* @param maxTtl the maximum TTL of cached DNS records
|
||||||
|
* @param negativeTtl the TTL for failed cached queries
|
||||||
|
* @param queryTimeoutMillis timeout of each DNS query in millis
|
||||||
|
* @param resolvedAddressTypes list of the protocol families
|
||||||
|
* @param recursionDesired if recursion desired flag must be set
|
||||||
|
* @param maxQueriesPerResolve the maximum allowed number of DNS queries for a given name resolution
|
||||||
|
* @param traceEnabled if trace is enabled
|
||||||
|
* @param maxPayloadSize the capacity of the datagram packet buffer
|
||||||
|
* @param optResourceEnabled if automatic inclusion of a optional records is enabled
|
||||||
|
* @param hostsFileEntriesResolver the {@link HostsFileEntriesResolver} used to check for local aliases
|
||||||
*/
|
*/
|
||||||
public DnsNameResolver(
|
public DnsNameResolver(
|
||||||
EventLoop eventLoop, ChannelFactory<? extends DatagramChannel> channelFactory,
|
EventLoop eventLoop,
|
||||||
InetSocketAddress localAddress, DnsServerAddresses nameServerAddresses) {
|
ChannelFactory<? extends DatagramChannel> channelFactory,
|
||||||
|
InetSocketAddress localAddress,
|
||||||
|
DnsServerAddresses nameServerAddresses,
|
||||||
|
int minTtl,
|
||||||
|
int maxTtl,
|
||||||
|
int negativeTtl,
|
||||||
|
long queryTimeoutMillis,
|
||||||
|
InternetProtocolFamily[] resolvedAddressTypes,
|
||||||
|
boolean recursionDesired,
|
||||||
|
int maxQueriesPerResolve,
|
||||||
|
boolean traceEnabled,
|
||||||
|
int maxPayloadSize,
|
||||||
|
boolean optResourceEnabled,
|
||||||
|
HostsFileEntriesResolver hostsFileEntriesResolver) {
|
||||||
|
|
||||||
super(eventLoop);
|
super(eventLoop);
|
||||||
|
|
||||||
checkNotNull(channelFactory, "channelFactory");
|
checkNotNull(channelFactory, "channelFactory");
|
||||||
checkNotNull(nameServerAddresses, "nameServerAddresses");
|
|
||||||
checkNotNull(localAddress, "localAddress");
|
checkNotNull(localAddress, "localAddress");
|
||||||
|
this.nameServerAddresses = checkNotNull(nameServerAddresses, "nameServerAddresses");
|
||||||
|
this.minTtl = checkPositiveOrZero(minTtl, "minTtl");
|
||||||
|
this.maxTtl = checkPositiveOrZero(maxTtl, "maxTtl");
|
||||||
|
if (minTtl > maxTtl) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"minTtl: " + minTtl + ", maxTtl: " + maxTtl + " (expected: 0 <= minTtl <= maxTtl)");
|
||||||
|
}
|
||||||
|
this.negativeTtl = checkPositiveOrZero(negativeTtl, "negativeTtl");
|
||||||
|
this.queryTimeoutMillis = checkPositive(queryTimeoutMillis, "queryTimeoutMillis");
|
||||||
|
this.resolvedAddressTypes = checkNonEmpty(resolvedAddressTypes, "resolvedAddressTypes");
|
||||||
|
this.recursionDesired = recursionDesired;
|
||||||
|
this.maxQueriesPerResolve = checkPositive(maxQueriesPerResolve, "maxQueriesPerResolve");
|
||||||
|
this.traceEnabled = traceEnabled;
|
||||||
|
this.maxPayloadSize = checkPositive(maxPayloadSize, "maxPayloadSize");
|
||||||
|
this.optResourceEnabled = optResourceEnabled;
|
||||||
|
this.hostsFileEntriesResolver = checkNotNull(hostsFileEntriesResolver, "hostsFileEntriesResolver");
|
||||||
|
|
||||||
this.nameServerAddresses = nameServerAddresses;
|
|
||||||
bindFuture = newChannel(channelFactory, localAddress);
|
bindFuture = newChannel(channelFactory, localAddress);
|
||||||
ch = (DatagramChannel) bindFuture.channel();
|
ch = (DatagramChannel) bindFuture.channel();
|
||||||
|
ch.config().setRecvByteBufAllocator(new FixedRecvByteBufAllocator(maxPayloadSize));
|
||||||
setMaxPayloadSize(4096);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelFuture newChannel(
|
private ChannelFuture newChannel(
|
||||||
@ -203,6 +189,7 @@ public class DnsNameResolver extends InetNameResolver {
|
|||||||
Bootstrap b = new Bootstrap();
|
Bootstrap b = new Bootstrap();
|
||||||
b.group(executor());
|
b.group(executor());
|
||||||
b.channelFactory(channelFactory);
|
b.channelFactory(channelFactory);
|
||||||
|
final DnsResponseHandler responseHandler = new DnsResponseHandler();
|
||||||
b.handler(new ChannelInitializer<DatagramChannel>() {
|
b.handler(new ChannelInitializer<DatagramChannel>() {
|
||||||
@Override
|
@Override
|
||||||
protected void initChannel(DatagramChannel ch) throws Exception {
|
protected void initChannel(DatagramChannel ch) throws Exception {
|
||||||
@ -225,7 +212,6 @@ public class DnsNameResolver extends InetNameResolver {
|
|||||||
* Returns the minimum TTL of the cached DNS resource records (in seconds).
|
* Returns the minimum TTL of the cached DNS resource records (in seconds).
|
||||||
*
|
*
|
||||||
* @see #maxTtl()
|
* @see #maxTtl()
|
||||||
* @see #setTtl(int, int)
|
|
||||||
*/
|
*/
|
||||||
public int minTtl() {
|
public int minTtl() {
|
||||||
return minTtl;
|
return minTtl;
|
||||||
@ -235,236 +221,56 @@ public class DnsNameResolver extends InetNameResolver {
|
|||||||
* Returns the maximum TTL of the cached DNS resource records (in seconds).
|
* Returns the maximum TTL of the cached DNS resource records (in seconds).
|
||||||
*
|
*
|
||||||
* @see #minTtl()
|
* @see #minTtl()
|
||||||
* @see #setTtl(int, int)
|
|
||||||
*/
|
*/
|
||||||
public int maxTtl() {
|
public int maxTtl() {
|
||||||
return maxTtl;
|
return maxTtl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the minimum and maximum TTL of the cached DNS resource records (in seconds). If the TTL of the DNS resource
|
|
||||||
* record returned by the DNS server is less than the minimum TTL or greater than the maximum TTL, this resolver
|
|
||||||
* will ignore the TTL from the DNS server and use the minimum TTL or the maximum TTL instead respectively.
|
|
||||||
* The default value is {@code 0} and {@link Integer#MAX_VALUE}, which practically tells this resolver to respect
|
|
||||||
* the TTL from the DNS server.
|
|
||||||
*
|
|
||||||
* @return {@code this}
|
|
||||||
*
|
|
||||||
* @see #minTtl()
|
|
||||||
* @see #maxTtl()
|
|
||||||
*/
|
|
||||||
public DnsNameResolver setTtl(int minTtl, int maxTtl) {
|
|
||||||
if (minTtl < 0) {
|
|
||||||
throw new IllegalArgumentException("minTtl: " + minTtl + " (expected: >= 0)");
|
|
||||||
}
|
|
||||||
if (maxTtl < 0) {
|
|
||||||
throw new IllegalArgumentException("maxTtl: " + maxTtl + " (expected: >= 0)");
|
|
||||||
}
|
|
||||||
if (minTtl > maxTtl) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"minTtl: " + minTtl + ", maxTtl: " + maxTtl + " (expected: 0 <= minTtl <= maxTtl)");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.maxTtl = maxTtl;
|
|
||||||
this.minTtl = minTtl;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the TTL of the cache for the failed DNS queries (in seconds). The default value is {@code 0}, which
|
* Returns the TTL of the cache for the failed DNS queries (in seconds). The default value is {@code 0}, which
|
||||||
* disables the cache for negative results.
|
* disables the cache for negative results.
|
||||||
*
|
|
||||||
* @see #setNegativeTtl(int)
|
|
||||||
*/
|
*/
|
||||||
public int negativeTtl() {
|
public int negativeTtl() {
|
||||||
return negativeTtl;
|
return negativeTtl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the TTL of the cache for the failed DNS queries (in seconds).
|
|
||||||
*
|
|
||||||
* @return {@code this}
|
|
||||||
*
|
|
||||||
* @see #negativeTtl()
|
|
||||||
*/
|
|
||||||
public DnsNameResolver setNegativeTtl(int negativeTtl) {
|
|
||||||
if (negativeTtl < 0) {
|
|
||||||
throw new IllegalArgumentException("negativeTtl: " + negativeTtl + " (expected: >= 0)");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.negativeTtl = negativeTtl;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the timeout of each DNS query performed by this resolver (in milliseconds).
|
* Returns the timeout of each DNS query performed by this resolver (in milliseconds).
|
||||||
* The default value is 5 seconds.
|
* The default value is 5 seconds.
|
||||||
*
|
|
||||||
* @see #setQueryTimeoutMillis(long)
|
|
||||||
*/
|
*/
|
||||||
public long queryTimeoutMillis() {
|
public long queryTimeoutMillis() {
|
||||||
return queryTimeoutMillis;
|
return queryTimeoutMillis;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the timeout of each DNS query performed by this resolver (in milliseconds).
|
|
||||||
*
|
|
||||||
* @return {@code this}
|
|
||||||
*
|
|
||||||
* @see #queryTimeoutMillis()
|
|
||||||
*/
|
|
||||||
public DnsNameResolver setQueryTimeoutMillis(long queryTimeoutMillis) {
|
|
||||||
if (queryTimeoutMillis < 0) {
|
|
||||||
throw new IllegalArgumentException("queryTimeoutMillis: " + queryTimeoutMillis + " (expected: >= 0)");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.queryTimeoutMillis = queryTimeoutMillis;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the list of the protocol families of the address resolved by {@link #resolve(String)}
|
* Returns the list of the protocol families of the address resolved by {@link #resolve(String)}
|
||||||
* in the order of preference.
|
* in the order of preference.
|
||||||
* The default value depends on the value of the system property {@code "java.net.preferIPv6Addresses"}.
|
* The default value depends on the value of the system property {@code "java.net.preferIPv6Addresses"}.
|
||||||
*
|
|
||||||
* @see #setResolveAddressTypes(InternetProtocolFamily...)
|
|
||||||
*/
|
*/
|
||||||
public List<InternetProtocolFamily> resolveAddressTypes() {
|
public List<InternetProtocolFamily> resolvedAddressTypes() {
|
||||||
return Arrays.asList(resolveAddressTypes);
|
return Arrays.asList(resolvedAddressTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
InternetProtocolFamily[] resolveAddressTypesUnsafe() {
|
InternetProtocolFamily[] resolveAddressTypesUnsafe() {
|
||||||
return resolveAddressTypes;
|
return resolvedAddressTypes;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the list of the protocol families of the address resolved by {@link #resolve(String)}.
|
|
||||||
* Usually, both {@link InternetProtocolFamily#IPv4} and {@link InternetProtocolFamily#IPv6} are specified in the
|
|
||||||
* order of preference. To enforce the resolve to retrieve the address of a specific protocol family, specify
|
|
||||||
* only a single {@link InternetProtocolFamily}.
|
|
||||||
*
|
|
||||||
* @return {@code this}
|
|
||||||
*
|
|
||||||
* @see #resolveAddressTypes()
|
|
||||||
*/
|
|
||||||
public DnsNameResolver setResolveAddressTypes(InternetProtocolFamily... resolveAddressTypes) {
|
|
||||||
checkNotNull(resolveAddressTypes, "resolveAddressTypes");
|
|
||||||
|
|
||||||
final List<InternetProtocolFamily> list =
|
|
||||||
new ArrayList<InternetProtocolFamily>(InternetProtocolFamily.values().length);
|
|
||||||
|
|
||||||
for (InternetProtocolFamily f: resolveAddressTypes) {
|
|
||||||
if (f == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid duplicate entries.
|
|
||||||
if (list.contains(f)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
list.add(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (list.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException("no protocol family specified");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.resolveAddressTypes = list.toArray(new InternetProtocolFamily[list.size()]);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the list of the protocol families of the address resolved by {@link #resolve(String)}.
|
|
||||||
* Usually, both {@link InternetProtocolFamily#IPv4} and {@link InternetProtocolFamily#IPv6} are specified in the
|
|
||||||
* order of preference. To enforce the resolve to retrieve the address of a specific protocol family, specify
|
|
||||||
* only a single {@link InternetProtocolFamily}.
|
|
||||||
*
|
|
||||||
* @return {@code this}
|
|
||||||
*
|
|
||||||
* @see #resolveAddressTypes()
|
|
||||||
*/
|
|
||||||
public DnsNameResolver setResolveAddressTypes(Iterable<InternetProtocolFamily> resolveAddressTypes) {
|
|
||||||
checkNotNull(resolveAddressTypes, "resolveAddressTypes");
|
|
||||||
|
|
||||||
final List<InternetProtocolFamily> list =
|
|
||||||
new ArrayList<InternetProtocolFamily>(InternetProtocolFamily.values().length);
|
|
||||||
|
|
||||||
for (InternetProtocolFamily f: resolveAddressTypes) {
|
|
||||||
if (f == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid duplicate entries.
|
|
||||||
if (list.contains(f)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
list.add(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (list.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException("no protocol family specified");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.resolveAddressTypes = list.toArray(new InternetProtocolFamily[list.size()]);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@code true} if and only if this resolver sends a DNS query with the RD (recursion desired) flag set.
|
* Returns {@code true} if and only if this resolver sends a DNS query with the RD (recursion desired) flag set.
|
||||||
* The default value is {@code true}.
|
* The default value is {@code true}.
|
||||||
*
|
|
||||||
* @see #setRecursionDesired(boolean)
|
|
||||||
*/
|
*/
|
||||||
public boolean isRecursionDesired() {
|
public boolean isRecursionDesired() {
|
||||||
return recursionDesired;
|
return recursionDesired;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets if this resolver has to send a DNS query with the RD (recursion desired) flag set.
|
|
||||||
*
|
|
||||||
* @return {@code this}
|
|
||||||
*
|
|
||||||
* @see #isRecursionDesired()
|
|
||||||
*/
|
|
||||||
public DnsNameResolver setRecursionDesired(boolean recursionDesired) {
|
|
||||||
this.recursionDesired = recursionDesired;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the maximum allowed number of DNS queries to send when resolving a host name.
|
* Returns the maximum allowed number of DNS queries to send when resolving a host name.
|
||||||
* The default value is {@code 8}.
|
* The default value is {@code 8}.
|
||||||
*
|
|
||||||
* @see #setMaxQueriesPerResolve(int)
|
|
||||||
*/
|
*/
|
||||||
public int maxQueriesPerResolve() {
|
public int maxQueriesPerResolve() {
|
||||||
return maxQueriesPerResolve;
|
return maxQueriesPerResolve;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the maximum allowed number of DNS queries to send when resolving a host name.
|
|
||||||
*
|
|
||||||
* @return {@code this}
|
|
||||||
*
|
|
||||||
* @see #maxQueriesPerResolve()
|
|
||||||
*/
|
|
||||||
public DnsNameResolver setMaxQueriesPerResolve(int maxQueriesPerResolve) {
|
|
||||||
if (maxQueriesPerResolve <= 0) {
|
|
||||||
throw new IllegalArgumentException("maxQueriesPerResolve: " + maxQueriesPerResolve + " (expected: > 0)");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.maxQueriesPerResolve = maxQueriesPerResolve;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns if this resolver should generate the detailed trace information in an exception message so that
|
* Returns if this resolver should generate the detailed trace information in an exception message so that
|
||||||
* it is easier to understand the cause of resolution failure. The default value if {@code true}.
|
* it is easier to understand the cause of resolution failure. The default value if {@code true}.
|
||||||
@ -473,57 +279,13 @@ public class DnsNameResolver extends InetNameResolver {
|
|||||||
return traceEnabled;
|
return traceEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets if this resolver should generate the detailed trace information in an exception message so that
|
|
||||||
* it is easier to understand the cause of resolution failure.
|
|
||||||
*/
|
|
||||||
public DnsNameResolver setTraceEnabled(boolean traceEnabled) {
|
|
||||||
this.traceEnabled = traceEnabled;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the capacity of the datagram packet buffer (in bytes). The default value is {@code 4096} bytes.
|
* Returns the capacity of the datagram packet buffer (in bytes). The default value is {@code 4096} bytes.
|
||||||
*
|
|
||||||
* @see #setMaxPayloadSize(int)
|
|
||||||
*/
|
*/
|
||||||
public int maxPayloadSize() {
|
public int maxPayloadSize() {
|
||||||
return maxPayloadSize;
|
return maxPayloadSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the capacity of the datagram packet buffer (in bytes). The default value is {@code 4096} bytes.
|
|
||||||
*
|
|
||||||
* @return {@code this}
|
|
||||||
*
|
|
||||||
* @see #maxPayloadSize()
|
|
||||||
*/
|
|
||||||
public DnsNameResolver setMaxPayloadSize(int maxPayloadSize) {
|
|
||||||
if (maxPayloadSize <= 0) {
|
|
||||||
throw new IllegalArgumentException("maxPayloadSize: " + maxPayloadSize + " (expected: > 0)");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.maxPayloadSize == maxPayloadSize) {
|
|
||||||
// Same value; no need to instantiate DnsClass and RecvByteBufAllocator again.
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.maxPayloadSize = maxPayloadSize;
|
|
||||||
ch.config().setRecvByteBufAllocator(new FixedRecvByteBufAllocator(maxPayloadSize));
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable the automatic inclusion of a optional records that tries to give the remote DNS server a hint about how
|
|
||||||
* much data the resolver can read per response. Some DNSServer may not support this and so fail to answer
|
|
||||||
* queries. If you find problems you may want to disable this.
|
|
||||||
*/
|
|
||||||
public DnsNameResolver setOptResourceEnabled(boolean optResourceEnabled) {
|
|
||||||
this.optResourceEnabled = optResourceEnabled;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the automatic inclusion of a optional records that tries to give the remote DNS server a hint about how
|
* Returns the automatic inclusion of a optional records that tries to give the remote DNS server a hint about how
|
||||||
* much data the resolver can read per response is enabled.
|
* much data the resolver can read per response is enabled.
|
||||||
@ -532,6 +294,14 @@ public class DnsNameResolver extends InetNameResolver {
|
|||||||
return optResourceEnabled;
|
return optResourceEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the component that tries to resolve hostnames against the hosts file prior to asking to
|
||||||
|
* remotes DNS servers.
|
||||||
|
*/
|
||||||
|
public HostsFileEntriesResolver hostsFileEntriesResolver() {
|
||||||
|
return hostsFileEntriesResolver;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears all the resolved addresses cached by this resolver.
|
* Clears all the resolved addresses cached by this resolver.
|
||||||
*
|
*
|
||||||
@ -590,6 +360,10 @@ public class DnsNameResolver extends InetNameResolver {
|
|||||||
return (EventLoop) super.executor();
|
return (EventLoop) super.executor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private InetAddress resolveHostsFileEntry(String hostname) {
|
||||||
|
return hostsFileEntriesResolver != null ? hostsFileEntriesResolver.address(hostname) : null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doResolve(String inetHost, Promise<InetAddress> promise) throws Exception {
|
protected void doResolve(String inetHost, Promise<InetAddress> promise) throws Exception {
|
||||||
final byte[] bytes = NetUtil.createByteArrayFromIpAddressString(inetHost);
|
final byte[] bytes = NetUtil.createByteArrayFromIpAddressString(inetHost);
|
||||||
@ -601,6 +375,12 @@ public class DnsNameResolver extends InetNameResolver {
|
|||||||
|
|
||||||
final String hostname = hostname(inetHost);
|
final String hostname = hostname(inetHost);
|
||||||
|
|
||||||
|
InetAddress hostsFileEntry = resolveHostsFileEntry(hostname);
|
||||||
|
if (hostsFileEntry != null) {
|
||||||
|
promise.setSuccess(hostsFileEntry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!doResolveCached(hostname, promise)) {
|
if (!doResolveCached(hostname, promise)) {
|
||||||
doResolveUncached(hostname, promise);
|
doResolveUncached(hostname, promise);
|
||||||
}
|
}
|
||||||
@ -622,7 +402,7 @@ public class DnsNameResolver extends InetNameResolver {
|
|||||||
cause = cachedEntries.get(0).cause();
|
cause = cachedEntries.get(0).cause();
|
||||||
} else {
|
} else {
|
||||||
// Find the first entry with the preferred address type.
|
// Find the first entry with the preferred address type.
|
||||||
for (InternetProtocolFamily f : resolveAddressTypes) {
|
for (InternetProtocolFamily f : resolvedAddressTypes) {
|
||||||
for (int i = 0; i < numEntries; i++) {
|
for (int i = 0; i < numEntries; i++) {
|
||||||
final DnsCacheEntry e = cachedEntries.get(i);
|
final DnsCacheEntry e = cachedEntries.get(i);
|
||||||
if (f.addressType().isInstance(e.address())) {
|
if (f.addressType().isInstance(e.address())) {
|
||||||
@ -687,6 +467,12 @@ public class DnsNameResolver extends InetNameResolver {
|
|||||||
|
|
||||||
final String hostname = hostname(inetHost);
|
final String hostname = hostname(inetHost);
|
||||||
|
|
||||||
|
InetAddress hostsFileEntry = resolveHostsFileEntry(hostname);
|
||||||
|
if (hostsFileEntry != null) {
|
||||||
|
promise.setSuccess(Collections.singletonList(hostsFileEntry));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!doResolveAllCached(hostname, promise)) {
|
if (!doResolveAllCached(hostname, promise)) {
|
||||||
doResolveAllUncached(hostname, promise);
|
doResolveAllUncached(hostname, promise);
|
||||||
}
|
}
|
||||||
@ -707,7 +493,7 @@ public class DnsNameResolver extends InetNameResolver {
|
|||||||
if (cachedEntries.get(0).cause() != null) {
|
if (cachedEntries.get(0).cause() != null) {
|
||||||
cause = cachedEntries.get(0).cause();
|
cause = cachedEntries.get(0).cause();
|
||||||
} else {
|
} else {
|
||||||
for (InternetProtocolFamily f : resolveAddressTypes) {
|
for (InternetProtocolFamily f : resolvedAddressTypes) {
|
||||||
for (int i = 0; i < numEntries; i++) {
|
for (int i = 0; i < numEntries; i++) {
|
||||||
final DnsCacheEntry e = cachedEntries.get(i);
|
final DnsCacheEntry e = cachedEntries.get(i);
|
||||||
if (f.addressType().isInstance(e.address())) {
|
if (f.addressType().isInstance(e.address())) {
|
||||||
|
@ -0,0 +1,312 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package io.netty.resolver.dns;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelFactory;
|
||||||
|
import io.netty.channel.EventLoop;
|
||||||
|
import io.netty.channel.ReflectiveChannelFactory;
|
||||||
|
import io.netty.channel.socket.DatagramChannel;
|
||||||
|
import io.netty.channel.socket.InternetProtocolFamily;
|
||||||
|
import io.netty.resolver.HostsFileEntriesResolver;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link DnsNameResolver} builder.
|
||||||
|
*/
|
||||||
|
public final class DnsNameResolverBuilder {
|
||||||
|
|
||||||
|
private final EventLoop eventLoop;
|
||||||
|
private ChannelFactory<? extends DatagramChannel> channelFactory;
|
||||||
|
private InetSocketAddress localAddress = DnsNameResolver.ANY_LOCAL_ADDR;
|
||||||
|
private DnsServerAddresses nameServerAddresses;
|
||||||
|
private int minTtl;
|
||||||
|
private int maxTtl = Integer.MAX_VALUE;
|
||||||
|
private int negativeTtl;
|
||||||
|
private long queryTimeoutMillis = 5000;
|
||||||
|
private InternetProtocolFamily[] resolvedAddressTypes = DnsNameResolver.DEFAULT_RESOLVE_ADDRESS_TYPES;
|
||||||
|
private boolean recursionDesired = true;
|
||||||
|
private int maxQueriesPerResolve = 3;
|
||||||
|
private boolean traceEnabled;
|
||||||
|
private int maxPayloadSize = 4096;
|
||||||
|
private boolean optResourceEnabled = true;
|
||||||
|
private HostsFileEntriesResolver hostsFileEntriesResolver = HostsFileEntriesResolver.DEFAULT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new builder.
|
||||||
|
*
|
||||||
|
* @param eventLoop the {@link EventLoop} the {@link EventLoop} which will perform the communication with the DNS
|
||||||
|
* servers.
|
||||||
|
*/
|
||||||
|
public DnsNameResolverBuilder(EventLoop eventLoop) {
|
||||||
|
this.eventLoop = eventLoop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link ChannelFactory} that will create a {@link DatagramChannel}.
|
||||||
|
*
|
||||||
|
* @param channelFactory the {@link ChannelFactory}
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public DnsNameResolverBuilder channelFactory(ChannelFactory<? extends DatagramChannel> channelFactory) {
|
||||||
|
this.channelFactory = channelFactory;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link ChannelFactory} as a {@link ReflectiveChannelFactory} of this type.
|
||||||
|
* Use as an alternative to {@link #channelFactory(ChannelFactory)}.
|
||||||
|
*
|
||||||
|
* @param channelType
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public DnsNameResolverBuilder channelType(Class<? extends DatagramChannel> channelType) {
|
||||||
|
return channelFactory(new ReflectiveChannelFactory<DatagramChannel>(channelType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the local address of the {@link DatagramChannel}
|
||||||
|
*
|
||||||
|
* @param localAddress the local address
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public DnsNameResolverBuilder localAddress(InetSocketAddress localAddress) {
|
||||||
|
this.localAddress = localAddress;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the addresses of the DNS server.
|
||||||
|
*
|
||||||
|
* @param nameServerAddresses the DNS server addresses
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public DnsNameResolverBuilder nameServerAddresses(DnsServerAddresses nameServerAddresses) {
|
||||||
|
this.nameServerAddresses = nameServerAddresses;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the minimum and maximum TTL of the cached DNS resource records (in seconds). If the TTL of the DNS
|
||||||
|
* resource record returned by the DNS server is less than the minimum TTL or greater than the maximum TTL,
|
||||||
|
* this resolver will ignore the TTL from the DNS server and use the minimum TTL or the maximum TTL instead
|
||||||
|
* respectively.
|
||||||
|
* The default value is {@code 0} and {@link Integer#MAX_VALUE}, which practically tells this resolver to
|
||||||
|
* respect the TTL from the DNS server.
|
||||||
|
*
|
||||||
|
* @param minTtl the minimum TTL
|
||||||
|
* @param maxTtl the maximum TTL
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public DnsNameResolverBuilder ttl(int minTtl, int maxTtl) {
|
||||||
|
this.maxTtl = maxTtl;
|
||||||
|
this.minTtl = minTtl;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the TTL of the cache for the failed DNS queries (in seconds).
|
||||||
|
*
|
||||||
|
* @param negativeTtl the TTL for failed cached queries
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public DnsNameResolverBuilder negativeTtl(int negativeTtl) {
|
||||||
|
this.negativeTtl = negativeTtl;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the timeout of each DNS query performed by this resolver (in milliseconds).
|
||||||
|
*
|
||||||
|
* @param queryTimeoutMillis the query timeout
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public DnsNameResolverBuilder queryTimeoutMillis(long queryTimeoutMillis) {
|
||||||
|
this.queryTimeoutMillis = queryTimeoutMillis;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the list of the protocol families of the address resolved.
|
||||||
|
* Usually, both {@link InternetProtocolFamily#IPv4} and {@link InternetProtocolFamily#IPv6} are specified in
|
||||||
|
* the order of preference. To enforce the resolve to retrieve the address of a specific protocol family,
|
||||||
|
* specify only a single {@link InternetProtocolFamily}.
|
||||||
|
*
|
||||||
|
* @param resolvedAddressTypes the address types
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public DnsNameResolverBuilder resolvedAddressTypes(InternetProtocolFamily... resolvedAddressTypes) {
|
||||||
|
checkNotNull(resolvedAddressTypes, "resolvedAddressTypes");
|
||||||
|
|
||||||
|
final List<InternetProtocolFamily> list =
|
||||||
|
new ArrayList<InternetProtocolFamily>(InternetProtocolFamily.values().length);
|
||||||
|
|
||||||
|
for (InternetProtocolFamily f : resolvedAddressTypes) {
|
||||||
|
if (f == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid duplicate entries.
|
||||||
|
if (list.contains(f)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.add(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("no protocol family specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.resolvedAddressTypes = list.toArray(new InternetProtocolFamily[list.size()]);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the list of the protocol families of the address resolved.
|
||||||
|
* Usually, both {@link InternetProtocolFamily#IPv4} and {@link InternetProtocolFamily#IPv6} are specified in
|
||||||
|
* the order of preference. To enforce the resolve to retrieve the address of a specific protocol family,
|
||||||
|
* specify only a single {@link InternetProtocolFamily}.
|
||||||
|
*
|
||||||
|
* @param resolvedAddressTypes the address types
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public DnsNameResolverBuilder resolvedAddressTypes(Iterable<InternetProtocolFamily> resolvedAddressTypes) {
|
||||||
|
checkNotNull(resolvedAddressTypes, "resolveAddressTypes");
|
||||||
|
|
||||||
|
final List<InternetProtocolFamily> list =
|
||||||
|
new ArrayList<InternetProtocolFamily>(InternetProtocolFamily.values().length);
|
||||||
|
|
||||||
|
for (InternetProtocolFamily f : resolvedAddressTypes) {
|
||||||
|
if (f == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid duplicate entries.
|
||||||
|
if (list.contains(f)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.add(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("no protocol family specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.resolvedAddressTypes = list.toArray(new InternetProtocolFamily[list.size()]);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets if this resolver has to send a DNS query with the RD (recursion desired) flag set.
|
||||||
|
*
|
||||||
|
* @param recursionDesired true if recursion is desired
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public DnsNameResolverBuilder recursionDesired(boolean recursionDesired) {
|
||||||
|
this.recursionDesired = recursionDesired;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the maximum allowed number of DNS queries to send when resolving a host name.
|
||||||
|
*
|
||||||
|
* @param maxQueriesPerResolve the max number of queries
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public DnsNameResolverBuilder maxQueriesPerResolve(int maxQueriesPerResolve) {
|
||||||
|
this.maxQueriesPerResolve = maxQueriesPerResolve;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets if this resolver should generate the detailed trace information in an exception message so that
|
||||||
|
* it is easier to understand the cause of resolution failure.
|
||||||
|
*
|
||||||
|
* @param traceEnabled true if trace is enabled
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public DnsNameResolverBuilder traceEnabled(boolean traceEnabled) {
|
||||||
|
this.traceEnabled = traceEnabled;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the capacity of the datagram packet buffer (in bytes). The default value is {@code 4096} bytes.
|
||||||
|
*
|
||||||
|
* @param maxPayloadSize the capacity of the datagram packet buffer
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public DnsNameResolverBuilder maxPayloadSize(int maxPayloadSize) {
|
||||||
|
this.maxPayloadSize = maxPayloadSize;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the automatic inclusion of a optional records that tries to give the remote DNS server a hint about
|
||||||
|
* how much data the resolver can read per response. Some DNSServer may not support this and so fail to answer
|
||||||
|
* queries. If you find problems you may want to disable this.
|
||||||
|
*
|
||||||
|
* @param optResourceEnabled if optional records inclusion is enabled
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public DnsNameResolverBuilder optResourceEnabled(boolean optResourceEnabled) {
|
||||||
|
this.optResourceEnabled = optResourceEnabled;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param hostsFileEntriesResolver the {@link HostsFileEntriesResolver} used to first check
|
||||||
|
* if the hostname is locally aliased.
|
||||||
|
* @param hostsFileEntriesResolver the {@link HostsFileEntriesResolver}
|
||||||
|
* @return {@code this}
|
||||||
|
*/
|
||||||
|
public DnsNameResolverBuilder hostsFileEntriesResolver(HostsFileEntriesResolver hostsFileEntriesResolver) {
|
||||||
|
this.hostsFileEntriesResolver = hostsFileEntriesResolver;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new {@link DnsNameResolver} instance.
|
||||||
|
*
|
||||||
|
* @return a {@link DnsNameResolver}
|
||||||
|
*/
|
||||||
|
public DnsNameResolver build() {
|
||||||
|
return new DnsNameResolver(
|
||||||
|
eventLoop,
|
||||||
|
channelFactory,
|
||||||
|
localAddress,
|
||||||
|
nameServerAddresses,
|
||||||
|
minTtl,
|
||||||
|
maxTtl,
|
||||||
|
negativeTtl,
|
||||||
|
queryTimeoutMillis,
|
||||||
|
resolvedAddressTypes,
|
||||||
|
recursionDesired,
|
||||||
|
maxQueriesPerResolve,
|
||||||
|
traceEnabled,
|
||||||
|
maxPayloadSize,
|
||||||
|
optResourceEnabled,
|
||||||
|
hostsFileEntriesResolver);
|
||||||
|
}
|
||||||
|
}
|
@ -31,11 +31,6 @@ public final class DnsNameResolverException extends RuntimeException {
|
|||||||
private final InetSocketAddress remoteAddress;
|
private final InetSocketAddress remoteAddress;
|
||||||
private final DnsQuestion question;
|
private final DnsQuestion question;
|
||||||
|
|
||||||
public DnsNameResolverException(InetSocketAddress remoteAddress, DnsQuestion question) {
|
|
||||||
this.remoteAddress = validateRemoteAddress(remoteAddress);
|
|
||||||
this.question = validateQuestion(question);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DnsNameResolverException(InetSocketAddress remoteAddress, DnsQuestion question, String message) {
|
public DnsNameResolverException(InetSocketAddress remoteAddress, DnsQuestion question, String message) {
|
||||||
super(message);
|
super(message);
|
||||||
this.remoteAddress = validateRemoteAddress(remoteAddress);
|
this.remoteAddress = validateRemoteAddress(remoteAddress);
|
||||||
@ -49,12 +44,6 @@ public final class DnsNameResolverException extends RuntimeException {
|
|||||||
this.question = validateQuestion(question);
|
this.question = validateQuestion(question);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DnsNameResolverException(InetSocketAddress remoteAddress, DnsQuestion question, Throwable cause) {
|
|
||||||
super(cause);
|
|
||||||
this.remoteAddress = validateRemoteAddress(remoteAddress);
|
|
||||||
this.question = validateQuestion(question);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static InetSocketAddress validateRemoteAddress(InetSocketAddress remoteAddress) {
|
private static InetSocketAddress validateRemoteAddress(InetSocketAddress remoteAddress) {
|
||||||
return ObjectUtil.checkNotNull(remoteAddress, "remoteAddress");
|
return ObjectUtil.checkNotNull(remoteAddress, "remoteAddress");
|
||||||
}
|
}
|
||||||
@ -63,6 +52,13 @@ public final class DnsNameResolverException extends RuntimeException {
|
|||||||
return ObjectUtil.checkNotNull(question, "question");
|
return ObjectUtil.checkNotNull(question, "question");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link InetSocketAddress} of the DNS query that has failed.
|
||||||
|
*/
|
||||||
|
public InetSocketAddress remoteAddress() {
|
||||||
|
return remoteAddress;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link DnsQuestion} of the DNS query that has failed.
|
* Returns the {@link DnsQuestion} of the DNS query that has failed.
|
||||||
*/
|
*/
|
||||||
|
@ -58,7 +58,6 @@ import org.apache.mina.filter.codec.ProtocolEncoder;
|
|||||||
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
|
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
|
||||||
import org.apache.mina.transport.socket.DatagramAcceptor;
|
import org.apache.mina.transport.socket.DatagramAcceptor;
|
||||||
import org.apache.mina.transport.socket.DatagramSessionConfig;
|
import org.apache.mina.transport.socket.DatagramSessionConfig;
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -265,15 +264,23 @@ public class DnsNameResolverTest {
|
|||||||
|
|
||||||
private static final TestDnsServer dnsServer = new TestDnsServer();
|
private static final TestDnsServer dnsServer = new TestDnsServer();
|
||||||
private static final EventLoopGroup group = new NioEventLoopGroup(1);
|
private static final EventLoopGroup group = new NioEventLoopGroup(1);
|
||||||
private static DnsNameResolver resolver;
|
|
||||||
|
private DnsNameResolverBuilder newResolver() {
|
||||||
|
return new DnsNameResolverBuilder(group.next())
|
||||||
|
.channelType(NioDatagramChannel.class)
|
||||||
|
.nameServerAddresses(DnsServerAddresses.singleton(dnsServer.localAddress()))
|
||||||
|
.maxQueriesPerResolve(1)
|
||||||
|
.optResourceEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DnsNameResolverBuilder newResolver(InternetProtocolFamily... resolvedAddressTypes) {
|
||||||
|
return newResolver()
|
||||||
|
.resolvedAddressTypes(resolvedAddressTypes);
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void init() throws Exception {
|
public static void init() throws Exception {
|
||||||
dnsServer.start();
|
dnsServer.start();
|
||||||
resolver = new DnsNameResolver(group.next(), NioDatagramChannel.class,
|
|
||||||
DnsServerAddresses.singleton(dnsServer.localAddress()));
|
|
||||||
resolver.setMaxQueriesPerResolve(1);
|
|
||||||
resolver.setOptResourceEnabled(false);
|
|
||||||
}
|
}
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void destroy() {
|
public static void destroy() {
|
||||||
@ -281,37 +288,40 @@ public class DnsNameResolverTest {
|
|||||||
group.shutdownGracefully();
|
group.shutdownGracefully();
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
|
||||||
public void reset() throws Exception {
|
|
||||||
resolver.clearCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResolveAorAAAA() throws Exception {
|
public void testResolveAorAAAA() throws Exception {
|
||||||
testResolve0(EXCLUSIONS_RESOLVE_A, InternetProtocolFamily.IPv4, InternetProtocolFamily.IPv6);
|
DnsNameResolver resolver = newResolver(InternetProtocolFamily.IPv4, InternetProtocolFamily.IPv6).build();
|
||||||
|
try {
|
||||||
|
testResolve0(resolver, EXCLUSIONS_RESOLVE_A);
|
||||||
|
} finally {
|
||||||
|
resolver.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResolveAAAAorA() throws Exception {
|
public void testResolveAAAAorA() throws Exception {
|
||||||
testResolve0(EXCLUSIONS_RESOLVE_A, InternetProtocolFamily.IPv6, InternetProtocolFamily.IPv4);
|
DnsNameResolver resolver = newResolver(InternetProtocolFamily.IPv6, InternetProtocolFamily.IPv4).build();
|
||||||
|
try {
|
||||||
|
testResolve0(resolver, EXCLUSIONS_RESOLVE_A);
|
||||||
|
} finally {
|
||||||
|
resolver.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResolveA() throws Exception {
|
public void testResolveA() throws Exception {
|
||||||
final int oldMinTtl = resolver.minTtl();
|
DnsNameResolver resolver = newResolver(InternetProtocolFamily.IPv4)
|
||||||
final int oldMaxTtl = resolver.maxTtl();
|
// Cache for eternity
|
||||||
|
.ttl(Integer.MAX_VALUE, Integer.MAX_VALUE)
|
||||||
// Cache for eternity.
|
.build();
|
||||||
resolver.setTtl(Integer.MAX_VALUE, Integer.MAX_VALUE);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final Map<String, InetAddress> resultA = testResolve0(EXCLUSIONS_RESOLVE_A, InternetProtocolFamily.IPv4);
|
final Map<String, InetAddress> resultA = testResolve0(resolver, EXCLUSIONS_RESOLVE_A);
|
||||||
|
|
||||||
// Now, try to resolve again to see if it's cached.
|
// Now, try to resolve again to see if it's cached.
|
||||||
// This test works because the DNS servers usually randomizes the order of the records in a response.
|
// This test works because the DNS servers usually randomizes the order of the records in a response.
|
||||||
// If cached, the resolved addresses must be always same, because we reuse the same response.
|
// If cached, the resolved addresses must be always same, because we reuse the same response.
|
||||||
|
|
||||||
final Map<String, InetAddress> resultB = testResolve0(EXCLUSIONS_RESOLVE_A, InternetProtocolFamily.IPv4);
|
final Map<String, InetAddress> resultB = testResolve0(resolver, EXCLUSIONS_RESOLVE_A);
|
||||||
|
|
||||||
// Ensure the result from the cache is identical from the uncached one.
|
// Ensure the result from the cache is identical from the uncached one.
|
||||||
assertThat(resultB.size(), is(resultA.size()));
|
assertThat(resultB.size(), is(resultA.size()));
|
||||||
@ -325,61 +335,56 @@ public class DnsNameResolverTest {
|
|||||||
assertThat(actual, is(expected));
|
assertThat(actual, is(expected));
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
// Restore the TTL configuration.
|
resolver.close();
|
||||||
resolver.setTtl(oldMinTtl, oldMaxTtl);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResolveAAAA() throws Exception {
|
public void testResolveAAAA() throws Exception {
|
||||||
testResolve0(EXCLUSIONS_RESOLVE_AAAA, InternetProtocolFamily.IPv6);
|
DnsNameResolver resolver = newResolver(InternetProtocolFamily.IPv6).build();
|
||||||
|
try {
|
||||||
|
testResolve0(resolver, EXCLUSIONS_RESOLVE_AAAA);
|
||||||
|
} finally {
|
||||||
|
resolver.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<String, InetAddress> testResolve0(
|
private Map<String, InetAddress> testResolve0(DnsNameResolver resolver, Set<String> excludedDomains)
|
||||||
Set<String> excludedDomains, InternetProtocolFamily... famililies) throws InterruptedException {
|
throws InterruptedException {
|
||||||
|
|
||||||
final List<InternetProtocolFamily> oldResolveAddressTypes = resolver.resolveAddressTypes();
|
|
||||||
|
|
||||||
assertThat(resolver.isRecursionDesired(), is(true));
|
assertThat(resolver.isRecursionDesired(), is(true));
|
||||||
assertThat(oldResolveAddressTypes.size(), is(InternetProtocolFamily.values().length));
|
|
||||||
|
|
||||||
resolver.setResolveAddressTypes(famililies);
|
|
||||||
|
|
||||||
final Map<String, InetAddress> results = new HashMap<String, InetAddress>();
|
final Map<String, InetAddress> results = new HashMap<String, InetAddress>();
|
||||||
try {
|
final Map<String, Future<InetAddress>> futures =
|
||||||
final Map<String, Future<InetAddress>> futures =
|
new LinkedHashMap<String, Future<InetAddress>>();
|
||||||
new LinkedHashMap<String, Future<InetAddress>>();
|
|
||||||
|
|
||||||
for (String name : DOMAINS) {
|
for (String name : DOMAINS) {
|
||||||
if (excludedDomains.contains(name)) {
|
if (excludedDomains.contains(name)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
resolve(futures, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Entry<String, Future<InetAddress>> e : futures.entrySet()) {
|
resolve(resolver, futures, name);
|
||||||
String unresolved = e.getKey();
|
}
|
||||||
InetAddress resolved = e.getValue().sync().getNow();
|
|
||||||
|
|
||||||
logger.info("{}: {}", unresolved, resolved.getHostAddress());
|
for (Entry<String, Future<InetAddress>> e : futures.entrySet()) {
|
||||||
|
String unresolved = e.getKey();
|
||||||
|
InetAddress resolved = e.getValue().sync().getNow();
|
||||||
|
|
||||||
assertThat(resolved.getHostName(), is(unresolved));
|
logger.info("{}: {}", unresolved, resolved.getHostAddress());
|
||||||
|
|
||||||
boolean typeMatches = false;
|
assertThat(resolved.getHostName(), is(unresolved));
|
||||||
for (InternetProtocolFamily f: famililies) {
|
|
||||||
Class<?> resolvedType = resolved.getClass();
|
boolean typeMatches = false;
|
||||||
if (f.addressType().isAssignableFrom(resolvedType)) {
|
for (InternetProtocolFamily f: resolver.resolvedAddressTypes()) {
|
||||||
typeMatches = true;
|
Class<?> resolvedType = resolved.getClass();
|
||||||
}
|
if (f.addressType().isAssignableFrom(resolvedType)) {
|
||||||
|
typeMatches = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
assertThat(typeMatches, is(true));
|
|
||||||
|
|
||||||
results.put(resolved.getHostName(), resolved);
|
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
resolver.setResolveAddressTypes(oldResolveAddressTypes);
|
assertThat(typeMatches, is(true));
|
||||||
|
|
||||||
|
results.put(resolved.getHostName(), resolved);
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
@ -387,61 +392,65 @@ public class DnsNameResolverTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testQueryMx() throws Exception {
|
public void testQueryMx() throws Exception {
|
||||||
assertThat(resolver.isRecursionDesired(), is(true));
|
DnsNameResolver resolver = newResolver().build();
|
||||||
|
try {
|
||||||
|
assertThat(resolver.isRecursionDesired(), is(true));
|
||||||
|
|
||||||
Map<String, Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>> futures =
|
Map<String, Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>> futures =
|
||||||
new LinkedHashMap<String, Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>>();
|
new LinkedHashMap<String, Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>>();
|
||||||
for (String name: DOMAINS) {
|
for (String name: DOMAINS) {
|
||||||
if (EXCLUSIONS_QUERY_MX.contains(name)) {
|
if (EXCLUSIONS_QUERY_MX.contains(name)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
queryMx(futures, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Entry<String, Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>> e: futures.entrySet()) {
|
|
||||||
String hostname = e.getKey();
|
|
||||||
Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> f = e.getValue().awaitUninterruptibly();
|
|
||||||
|
|
||||||
DnsResponse response = f.getNow().content();
|
|
||||||
assertThat(response.code(), is(DnsResponseCode.NOERROR));
|
|
||||||
|
|
||||||
final int answerCount = response.count(DnsSection.ANSWER);
|
|
||||||
final List<DnsRecord> mxList = new ArrayList<DnsRecord>(answerCount);
|
|
||||||
for (int i = 0; i < answerCount; i ++) {
|
|
||||||
final DnsRecord r = response.recordAt(DnsSection.ANSWER, i);
|
|
||||||
if (r.type() == DnsRecordType.MX) {
|
|
||||||
mxList.add(r);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
queryMx(resolver, futures, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
assertThat(mxList.size(), is(greaterThan(0)));
|
for (Entry<String, Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>> e: futures.entrySet()) {
|
||||||
StringBuilder buf = new StringBuilder();
|
String hostname = e.getKey();
|
||||||
for (DnsRecord r: mxList) {
|
Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> f = e.getValue().awaitUninterruptibly();
|
||||||
ByteBuf recordContent = ((ByteBufHolder) r).content();
|
|
||||||
|
|
||||||
buf.append(StringUtil.NEWLINE);
|
DnsResponse response = f.getNow().content();
|
||||||
buf.append('\t');
|
assertThat(response.code(), is(DnsResponseCode.NOERROR));
|
||||||
buf.append(r.name());
|
|
||||||
buf.append(' ');
|
final int answerCount = response.count(DnsSection.ANSWER);
|
||||||
buf.append(r.type().name());
|
final List<DnsRecord> mxList = new ArrayList<DnsRecord>(answerCount);
|
||||||
buf.append(' ');
|
for (int i = 0; i < answerCount; i ++) {
|
||||||
buf.append(recordContent.readUnsignedShort());
|
final DnsRecord r = response.recordAt(DnsSection.ANSWER, i);
|
||||||
buf.append(' ');
|
if (r.type() == DnsRecordType.MX) {
|
||||||
buf.append(DnsNameResolverContext.decodeDomainName(recordContent));
|
mxList.add(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(mxList.size(), is(greaterThan(0)));
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
for (DnsRecord r: mxList) {
|
||||||
|
ByteBuf recordContent = ((ByteBufHolder) r).content();
|
||||||
|
|
||||||
|
buf.append(StringUtil.NEWLINE);
|
||||||
|
buf.append('\t');
|
||||||
|
buf.append(r.name());
|
||||||
|
buf.append(' ');
|
||||||
|
buf.append(r.type().name());
|
||||||
|
buf.append(' ');
|
||||||
|
buf.append(recordContent.readUnsignedShort());
|
||||||
|
buf.append(' ');
|
||||||
|
buf.append(DnsNameResolverContext.decodeDomainName(recordContent));
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("{} has the following MX records:{}", hostname, buf);
|
||||||
|
response.release();
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
logger.info("{} has the following MX records:{}", hostname, buf);
|
resolver.close();
|
||||||
response.release();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNegativeTtl() throws Exception {
|
public void testNegativeTtl() throws Exception {
|
||||||
final int oldNegativeTtl = resolver.negativeTtl();
|
final DnsNameResolver resolver = newResolver().negativeTtl(10).build();
|
||||||
resolver.setNegativeTtl(10);
|
|
||||||
try {
|
try {
|
||||||
resolveNonExistentDomain();
|
resolveNonExistentDomain(resolver);
|
||||||
|
|
||||||
final int size = 10000;
|
final int size = 10000;
|
||||||
final List<UnknownHostException> exceptions = new ArrayList<UnknownHostException>();
|
final List<UnknownHostException> exceptions = new ArrayList<UnknownHostException>();
|
||||||
@ -451,7 +460,7 @@ public class DnsNameResolverTest {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
exceptions.add(resolveNonExistentDomain());
|
exceptions.add(resolveNonExistentDomain(resolver));
|
||||||
if (isInterrupted()) {
|
if (isInterrupted()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -469,11 +478,11 @@ public class DnsNameResolverTest {
|
|||||||
|
|
||||||
assertThat(exceptions, hasSize(size));
|
assertThat(exceptions, hasSize(size));
|
||||||
} finally {
|
} finally {
|
||||||
resolver.setNegativeTtl(oldNegativeTtl);
|
resolver.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UnknownHostException resolveNonExistentDomain() {
|
private UnknownHostException resolveNonExistentDomain(DnsNameResolver resolver) {
|
||||||
try {
|
try {
|
||||||
resolver.resolve("non-existent.netty.io").sync();
|
resolver.resolve("non-existent.netty.io").sync();
|
||||||
fail();
|
fail();
|
||||||
@ -486,17 +495,23 @@ public class DnsNameResolverTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResolveIp() {
|
public void testResolveIp() {
|
||||||
InetAddress address = resolver.resolve("10.0.0.1").syncUninterruptibly().getNow();
|
DnsNameResolver resolver = newResolver().build();
|
||||||
|
try {
|
||||||
|
InetAddress address = resolver.resolve("10.0.0.1").syncUninterruptibly().getNow();
|
||||||
|
|
||||||
assertEquals("10.0.0.1", address.getHostName());
|
assertEquals("10.0.0.1", address.getHostName());
|
||||||
|
} finally {
|
||||||
|
resolver.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void resolve(Map<String, Future<InetAddress>> futures, String hostname) {
|
private void resolve(DnsNameResolver resolver, Map<String, Future<InetAddress>> futures, String hostname) {
|
||||||
|
|
||||||
futures.put(hostname, resolver.resolve(hostname));
|
futures.put(hostname, resolver.resolve(hostname));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void queryMx(
|
private static void queryMx(
|
||||||
|
DnsNameResolver resolver,
|
||||||
Map<String, Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>> futures,
|
Map<String, Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>> futures,
|
||||||
String hostname) throws Exception {
|
String hostname) throws Exception {
|
||||||
futures.put(hostname, resolver.query(new DefaultDnsQuestion(hostname, DnsRecordType.MX)));
|
futures.put(hostname, resolver.query(new DefaultDnsQuestion(hostname, DnsRecordType.MX)));
|
||||||
|
@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package io.netty.resolver;
|
||||||
|
|
||||||
|
import io.netty.util.concurrent.EventExecutor;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.FutureListener;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static io.netty.util.internal.ObjectUtil.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A composite {@link SimpleNameResolver} that resolves a host name against a sequence of {@link NameResolver}s.
|
||||||
|
*
|
||||||
|
* In case of a failure, only the last one will be reported.
|
||||||
|
*/
|
||||||
|
public final class CompositeNameResolver<T> extends SimpleNameResolver<T> {
|
||||||
|
|
||||||
|
private final NameResolver<T>[] resolvers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param executor the {@link EventExecutor} which is used to notify the listeners of the {@link Future} returned
|
||||||
|
* by {@link #resolve(String)}
|
||||||
|
* @param resolvers the {@link NameResolver}s to be tried sequentially
|
||||||
|
*/
|
||||||
|
public CompositeNameResolver(EventExecutor executor, NameResolver<T>... resolvers) {
|
||||||
|
super(executor);
|
||||||
|
checkNotNull(resolvers, "resolvers");
|
||||||
|
for (int i = 0; i < resolvers.length; i++) {
|
||||||
|
if (resolvers[i] == null) {
|
||||||
|
throw new NullPointerException("resolvers[" + i + ']');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resolvers.length < 2) {
|
||||||
|
throw new IllegalArgumentException("resolvers: " + Arrays.asList(resolvers) +
|
||||||
|
" (expected: at least 2 resolvers)");
|
||||||
|
}
|
||||||
|
this.resolvers = resolvers.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doResolve(String inetHost, Promise<T> promise) throws Exception {
|
||||||
|
doResolveRec(inetHost, promise, 0, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doResolveRec(final String inetHost,
|
||||||
|
final Promise<T> promise,
|
||||||
|
final int resolverIndex,
|
||||||
|
Throwable lastFailure) throws Exception {
|
||||||
|
if (resolverIndex >= resolvers.length) {
|
||||||
|
promise.setFailure(lastFailure);
|
||||||
|
} else {
|
||||||
|
NameResolver resolver = resolvers[resolverIndex];
|
||||||
|
resolver.resolve(inetHost).addListener(new FutureListener<T>() {
|
||||||
|
@Override
|
||||||
|
public void operationComplete(Future<T> future) throws Exception {
|
||||||
|
if (future.isSuccess()) {
|
||||||
|
promise.setSuccess(future.getNow());
|
||||||
|
} else {
|
||||||
|
doResolveRec(inetHost, promise, resolverIndex + 1, future.cause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doResolveAll(String inetHost, Promise<List<T>> promise) throws Exception {
|
||||||
|
doResolveAllRec(inetHost, promise, 0, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doResolveAllRec(final String inetHost,
|
||||||
|
final Promise<List<T>> promise,
|
||||||
|
final int resolverIndex,
|
||||||
|
Throwable lastFailure) throws Exception {
|
||||||
|
if (resolverIndex >= resolvers.length) {
|
||||||
|
promise.setFailure(lastFailure);
|
||||||
|
} else {
|
||||||
|
NameResolver resolver = resolvers[resolverIndex];
|
||||||
|
resolver.resolveAll(inetHost).addListener(new FutureListener<List<T>>() {
|
||||||
|
@Override
|
||||||
|
public void operationComplete(Future<List<T>> future) throws Exception {
|
||||||
|
if (future.isSuccess()) {
|
||||||
|
promise.setSuccess(future.getNow());
|
||||||
|
} else {
|
||||||
|
doResolveAllRec(inetHost, promise, resolverIndex + 1, future.cause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package io.netty.resolver;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default {@link HostsFileEntriesResolver} that resolves hosts file entries only once.
|
||||||
|
*/
|
||||||
|
public final class DefaultHostsFileEntriesResolver implements HostsFileEntriesResolver {
|
||||||
|
|
||||||
|
private final Map<String, InetAddress> entries = HostsFileParser.parseSilently();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InetAddress address(String inetHost) {
|
||||||
|
return entries.get(inetHost);
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,6 @@ import io.netty.util.concurrent.EventExecutor;
|
|||||||
import io.netty.util.concurrent.Promise;
|
import io.netty.util.concurrent.Promise;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package io.netty.resolver;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a hostname against the hosts file entries.
|
||||||
|
*/
|
||||||
|
public interface HostsFileEntriesResolver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default instance: a {@link DefaultHostsFileEntriesResolver}.
|
||||||
|
*/
|
||||||
|
HostsFileEntriesResolver DEFAULT = new DefaultHostsFileEntriesResolver();
|
||||||
|
|
||||||
|
InetAddress address(String inetHost);
|
||||||
|
}
|
171
resolver/src/main/java/io/netty/resolver/HostsFileParser.java
Normal file
171
resolver/src/main/java/io/netty/resolver/HostsFileParser.java
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package io.netty.resolver;
|
||||||
|
|
||||||
|
import io.netty.util.NetUtil;
|
||||||
|
import io.netty.util.internal.PlatformDependent;
|
||||||
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static io.netty.util.internal.ObjectUtil.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A parser for hosts files.
|
||||||
|
*/
|
||||||
|
public final class HostsFileParser {
|
||||||
|
|
||||||
|
private static final String WINDOWS_DEFAULT_SYSTEM_ROOT = "C:\\Windows";
|
||||||
|
private static final String WINDOWS_HOSTS_FILE_RELATIVE_PATH = "\\system32\\drivers\\etc\\hosts";
|
||||||
|
private static final String X_PLATFORMS_HOSTS_FILE_PATH = "/etc/hosts";
|
||||||
|
|
||||||
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(HostsFileParser.class);
|
||||||
|
|
||||||
|
private static File locateHostsFile() {
|
||||||
|
File hostsFile;
|
||||||
|
if (PlatformDependent.isWindows()) {
|
||||||
|
hostsFile = new File(System.getenv("SystemRoot") + WINDOWS_HOSTS_FILE_RELATIVE_PATH);
|
||||||
|
if (!hostsFile.exists()) {
|
||||||
|
hostsFile = new File(WINDOWS_DEFAULT_SYSTEM_ROOT + WINDOWS_HOSTS_FILE_RELATIVE_PATH);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hostsFile = new File(X_PLATFORMS_HOSTS_FILE_PATH);
|
||||||
|
}
|
||||||
|
return hostsFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse hosts file at standard OS location.
|
||||||
|
*
|
||||||
|
* @return a map of hostname or alias to {@link InetAddress}
|
||||||
|
*/
|
||||||
|
public static Map<String, InetAddress> parseSilently() {
|
||||||
|
File hostsFile = locateHostsFile();
|
||||||
|
try {
|
||||||
|
return parse(hostsFile);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.warn("Failed to load and parse hosts file at " + hostsFile.getPath(), e);
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse hosts file at standard OS location.
|
||||||
|
*
|
||||||
|
* @return a map of hostname or alias to {@link InetAddress}
|
||||||
|
* @throws IOException file could not be read
|
||||||
|
*/
|
||||||
|
public static Map<String, InetAddress> parse() throws IOException {
|
||||||
|
return parse(locateHostsFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a hosts file.
|
||||||
|
*
|
||||||
|
* @param file the file to be parsed
|
||||||
|
* @return a map of hostname or alias to {@link InetAddress}
|
||||||
|
* @throws IOException file could not be read
|
||||||
|
*/
|
||||||
|
public static Map<String, InetAddress> parse(File file) throws IOException {
|
||||||
|
checkNotNull(file, "file");
|
||||||
|
if (file.exists() && file.isFile()) {
|
||||||
|
return parse(new BufferedReader(new FileReader(file)));
|
||||||
|
} else {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a reader of hosts file format.
|
||||||
|
*
|
||||||
|
* @param reader the file to be parsed
|
||||||
|
* @return a map of hostname or alias to {@link InetAddress}
|
||||||
|
* @throws IOException file could not be read
|
||||||
|
*/
|
||||||
|
public static Map<String, InetAddress> parse(Reader reader) throws IOException {
|
||||||
|
checkNotNull(reader, "reader");
|
||||||
|
BufferedReader buff = new BufferedReader(reader);
|
||||||
|
try {
|
||||||
|
Map<String, InetAddress> entries = new HashMap<String, InetAddress>();
|
||||||
|
String line;
|
||||||
|
while ((line = buff.readLine()) != null) {
|
||||||
|
// remove comment
|
||||||
|
int commentPosition = line.indexOf('#');
|
||||||
|
if (commentPosition != -1) {
|
||||||
|
line = line.substring(0, commentPosition);
|
||||||
|
}
|
||||||
|
// skip empty lines
|
||||||
|
line = line.trim();
|
||||||
|
if (line.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// split
|
||||||
|
List<String> lineParts = new ArrayList<String>();
|
||||||
|
for (String s: line.split("[ \t]+")) {
|
||||||
|
if (!s.isEmpty()) {
|
||||||
|
lineParts.add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// a valid line should be [IP, hostname, alias*]
|
||||||
|
if (lineParts.size() < 2) {
|
||||||
|
// skip invalid line
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] ipBytes = NetUtil.createByteArrayFromIpAddressString(lineParts.get(0));
|
||||||
|
|
||||||
|
if (ipBytes == null) {
|
||||||
|
// skip invalid IP
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
InetAddress inetAddress = InetAddress.getByAddress(ipBytes);
|
||||||
|
|
||||||
|
// loop over hostname and aliases
|
||||||
|
for (int i = 1; i < lineParts.size(); i ++) {
|
||||||
|
String hostname = lineParts.get(i);
|
||||||
|
if (!entries.containsKey(hostname)) {
|
||||||
|
// trying to map a host to multiple IPs is wrong
|
||||||
|
// only the first entry is honored
|
||||||
|
entries.put(hostname, inetAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entries;
|
||||||
|
} finally {
|
||||||
|
buff.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can't be instantiated.
|
||||||
|
*/
|
||||||
|
private HostsFileParser() {
|
||||||
|
}
|
||||||
|
}
|
@ -37,7 +37,8 @@ public abstract class InetNameResolver extends SimpleNameResolver<InetAddress> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link AddressResolver} that will use this name resolver underneath.
|
* Return a {@link AddressResolver} that will use this name resolver underneath.
|
||||||
|
* It's cached internally, so the same instance is always returned.
|
||||||
*/
|
*/
|
||||||
public AddressResolver<InetSocketAddress> asAddressResolver() {
|
public AddressResolver<InetSocketAddress> asAddressResolver() {
|
||||||
AddressResolver<InetSocketAddress> result = addressResolver;
|
AddressResolver<InetSocketAddress> result = addressResolver;
|
||||||
|
@ -17,7 +17,7 @@ package io.netty.resolver;
|
|||||||
|
|
||||||
import io.netty.util.concurrent.EventExecutor;
|
import io.netty.util.concurrent.EventExecutor;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.concurrent.GenericFutureListener;
|
import io.netty.util.concurrent.FutureListener;
|
||||||
import io.netty.util.concurrent.Promise;
|
import io.netty.util.concurrent.Promise;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
@ -53,7 +53,7 @@ public class InetSocketAddressResolver extends AbstractAddressResolver<InetSocke
|
|||||||
// Note that InetSocketAddress.getHostName() will never incur a reverse lookup here,
|
// Note that InetSocketAddress.getHostName() will never incur a reverse lookup here,
|
||||||
// because an unresolved address always has a host name.
|
// because an unresolved address always has a host name.
|
||||||
nameResolver.resolve(unresolvedAddress.getHostName())
|
nameResolver.resolve(unresolvedAddress.getHostName())
|
||||||
.addListener(new GenericFutureListener<Future<InetAddress>>() {
|
.addListener(new FutureListener<InetAddress>() {
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(Future<InetAddress> future) throws Exception {
|
public void operationComplete(Future<InetAddress> future) throws Exception {
|
||||||
if (future.isSuccess()) {
|
if (future.isSuccess()) {
|
||||||
@ -71,7 +71,7 @@ public class InetSocketAddressResolver extends AbstractAddressResolver<InetSocke
|
|||||||
// Note that InetSocketAddress.getHostName() will never incur a reverse lookup here,
|
// Note that InetSocketAddress.getHostName() will never incur a reverse lookup here,
|
||||||
// because an unresolved address always has a host name.
|
// because an unresolved address always has a host name.
|
||||||
nameResolver.resolveAll(unresolvedAddress.getHostName())
|
nameResolver.resolveAll(unresolvedAddress.getHostName())
|
||||||
.addListener(new GenericFutureListener<Future<List<InetAddress>>>() {
|
.addListener(new FutureListener<List<InetAddress>>() {
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(Future<List<InetAddress>> future) throws Exception {
|
public void operationComplete(Future<List<InetAddress>> future) throws Exception {
|
||||||
if (future.isSuccess()) {
|
if (future.isSuccess()) {
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package io.netty.resolver;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class HostsFileParserTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParse() throws IOException {
|
||||||
|
String hostsString = new StringBuilder()
|
||||||
|
.append("127.0.0.1 host1").append("\n") // single hostname, separated with blanks
|
||||||
|
.append("\n") // empty line
|
||||||
|
.append("192.168.0.1\thost2").append("\n") // single hostname, separated with tabs
|
||||||
|
.append("#comment").append("\n") // comment at the beginning of the line
|
||||||
|
.append(" #comment ").append("\n") // comment in the middle of the line
|
||||||
|
.append("192.168.0.2 host3 #comment").append("\n") // comment after hostname
|
||||||
|
.append("192.168.0.3 host4 host5 host6").append("\n") // multiple aliases
|
||||||
|
.append("192.168.0.4 host4").append("\n") // host mapped to a second address, must be ignored
|
||||||
|
.toString();
|
||||||
|
|
||||||
|
Map<String, InetAddress> entries = HostsFileParser.parse(new BufferedReader(new StringReader(hostsString)));
|
||||||
|
|
||||||
|
assertEquals("Expected 6 entries", 6, entries.size());
|
||||||
|
assertEquals("127.0.0.1", entries.get("host1").getHostAddress());
|
||||||
|
assertEquals("192.168.0.1", entries.get("host2").getHostAddress());
|
||||||
|
assertEquals("192.168.0.2", entries.get("host3").getHostAddress());
|
||||||
|
assertEquals("192.168.0.3", entries.get("host4").getHostAddress());
|
||||||
|
assertEquals("192.168.0.3", entries.get("host5").getHostAddress());
|
||||||
|
assertEquals("192.168.0.3", entries.get("host6").getHostAddress());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user