HostsFileParser should allow both IPv4 and IPv6 for a given host
Motivation: HostsFileParser only retains the first address for each given hostname. This is wrong, and it’s allowed to have both an IPv4 and an IPv6. Modifications: * Have `HostsFileParser` now return a `HostsFileEntries` that contains IPv4 entries and IPv6 entries * Introduce `ResolvedAddressTypes` to describe resolved address types preferences * Add a new `ResolvedAddressTypes` parameter to `HostsFileEntriesResolver::address` to account for address types preferences * Change `DnsNameResolver` constructor to take a `ResolvedAddressTypes`, allowing for a null value that would use default * Change `DnsNameResolverBuilder::resolvedAddressTypes` to take a `ResolvedAddressTypes` * Make `DnsNameResolver::resolvedAddressTypes` return a `ResolvedAddressTypes` * Add a static `DnsNameResolverBuilder::computeResolvedAddressTypes` to ease converting from `InternetProtocolFamily` Result: We now support hosts files that contains IPv4 and IPv6 pairs for a same hostname.
This commit is contained in:
parent
64abef5f5b
commit
81f9de423c
@ -39,6 +39,7 @@ import io.netty.handler.codec.dns.DnsRecordType;
|
||||
import io.netty.handler.codec.dns.DnsResponse;
|
||||
import io.netty.resolver.HostsFileEntriesResolver;
|
||||
import io.netty.resolver.InetNameResolver;
|
||||
import io.netty.resolver.ResolvedAddressTypes;
|
||||
import io.netty.util.NetUtil;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import io.netty.util.concurrent.FastThreadLocal;
|
||||
@ -56,13 +57,10 @@ import java.net.IDN;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static io.netty.util.internal.ObjectUtil.checkNonEmpty;
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
@ -78,24 +76,37 @@ public class DnsNameResolver extends InetNameResolver {
|
||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(DnsNameResolver.class);
|
||||
private static final String LOCALHOST = "localhost";
|
||||
private static final InetAddress LOCALHOST_ADDRESS;
|
||||
private static final DnsRecord[] EMTPY_ADDITIONALS = new DnsRecord[0];
|
||||
private static final DnsRecord[] EMPTY_ADDITIONALS = new DnsRecord[0];
|
||||
private static final DnsRecordType[] IPV4_ONLY_RESOLVED_RECORD_TYPES =
|
||||
new DnsRecordType[] {DnsRecordType.A};
|
||||
private static final InternetProtocolFamily[] IPV4_ONLY_RESOLVED_PROTOCOL_FAMILIES =
|
||||
new InternetProtocolFamily[] {InternetProtocolFamily.IPv4};
|
||||
private static final DnsRecordType[] IPV4_PREFERRED_RESOLVED_RECORD_TYPES =
|
||||
new DnsRecordType[] {DnsRecordType.A, DnsRecordType.AAAA};
|
||||
private static final InternetProtocolFamily[] IPV4_PREFERRED_RESOLVED_PROTOCOL_FAMILIES =
|
||||
new InternetProtocolFamily[] {InternetProtocolFamily.IPv4, InternetProtocolFamily.IPv6};
|
||||
private static final DnsRecordType[] IPV6_ONLY_RESOLVED_RECORD_TYPES =
|
||||
new DnsRecordType[] {DnsRecordType.AAAA};
|
||||
private static final InternetProtocolFamily[] IPV6_ONLY_RESOLVED_PROTOCOL_FAMILIES =
|
||||
new InternetProtocolFamily[] {InternetProtocolFamily.IPv6};
|
||||
private static final DnsRecordType[] IPV6_PREFERRED_RESOLVED_RECORD_TYPES =
|
||||
new DnsRecordType[] {DnsRecordType.AAAA, DnsRecordType.A};
|
||||
private static final InternetProtocolFamily[] IPV6_PREFERRED_RESOLVED_PROTOCOL_FAMILIES =
|
||||
new InternetProtocolFamily[] {InternetProtocolFamily.IPv6, InternetProtocolFamily.IPv4};
|
||||
|
||||
static final InternetProtocolFamily[] DEFAULT_RESOLVE_ADDRESS_TYPES;
|
||||
static final String[] DEFAULT_SEACH_DOMAINS;
|
||||
static final ResolvedAddressTypes DEFAULT_RESOLVE_ADDRESS_TYPES;
|
||||
static final String[] DEFAULT_SEARCH_DOMAINS;
|
||||
|
||||
static {
|
||||
if (NetUtil.isIpV4StackPreferred()) {
|
||||
DEFAULT_RESOLVE_ADDRESS_TYPES = new InternetProtocolFamily[] { InternetProtocolFamily.IPv4 };
|
||||
DEFAULT_RESOLVE_ADDRESS_TYPES = ResolvedAddressTypes.IPV4_ONLY;
|
||||
LOCALHOST_ADDRESS = NetUtil.LOCALHOST4;
|
||||
} else {
|
||||
DEFAULT_RESOLVE_ADDRESS_TYPES = new InternetProtocolFamily[2];
|
||||
if (NetUtil.isIpV6AddressesPreferred()) {
|
||||
DEFAULT_RESOLVE_ADDRESS_TYPES[0] = InternetProtocolFamily.IPv6;
|
||||
DEFAULT_RESOLVE_ADDRESS_TYPES[1] = InternetProtocolFamily.IPv4;
|
||||
DEFAULT_RESOLVE_ADDRESS_TYPES = ResolvedAddressTypes.IPV6_PREFERRED;
|
||||
LOCALHOST_ADDRESS = NetUtil.LOCALHOST6;
|
||||
} else {
|
||||
DEFAULT_RESOLVE_ADDRESS_TYPES[0] = InternetProtocolFamily.IPv4;
|
||||
DEFAULT_RESOLVE_ADDRESS_TYPES[1] = InternetProtocolFamily.IPv6;
|
||||
DEFAULT_RESOLVE_ADDRESS_TYPES = ResolvedAddressTypes.IPV4_PREFERRED;
|
||||
LOCALHOST_ADDRESS = NetUtil.LOCALHOST4;
|
||||
}
|
||||
}
|
||||
@ -116,7 +127,7 @@ public class DnsNameResolver extends InetNameResolver {
|
||||
// Failed to get the system name search domain list.
|
||||
searchDomains = EmptyArrays.EMPTY_STRINGS;
|
||||
}
|
||||
DEFAULT_SEACH_DOMAINS = searchDomains;
|
||||
DEFAULT_SEARCH_DOMAINS = searchDomains;
|
||||
}
|
||||
|
||||
private static final DatagramDnsResponseDecoder DECODER = new DatagramDnsResponseDecoder();
|
||||
@ -148,7 +159,8 @@ public class DnsNameResolver extends InetNameResolver {
|
||||
private final long queryTimeoutMillis;
|
||||
private final int maxQueriesPerResolve;
|
||||
private final boolean traceEnabled;
|
||||
private final InternetProtocolFamily[] resolvedAddressTypes;
|
||||
private final ResolvedAddressTypes resolvedAddressTypes;
|
||||
private final InternetProtocolFamily[] resolvedInternetProtocolFamilies;
|
||||
private final boolean recursionDesired;
|
||||
private final int maxPayloadSize;
|
||||
private final boolean optResourceEnabled;
|
||||
@ -161,49 +173,6 @@ public class DnsNameResolver extends InetNameResolver {
|
||||
private final DnsRecordType[] resolveRecordTypes;
|
||||
private final boolean decodeIdn;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param resolveCache the DNS resolved entries cache
|
||||
* @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
|
||||
* @param searchDomains the list of search domain
|
||||
* @param ndots the ndots value
|
||||
* @deprecated use {@link DnsNameResolver#DnsNameResolver(EventLoop, ChannelFactory, DnsServerAddresses, DnsCache,
|
||||
* DnsCache, long, InternetProtocolFamily[], boolean, int, boolean, int, boolean,
|
||||
* HostsFileEntriesResolver, String[], int, boolean)}
|
||||
*/
|
||||
@Deprecated
|
||||
public DnsNameResolver(
|
||||
EventLoop eventLoop,
|
||||
ChannelFactory<? extends DatagramChannel> channelFactory,
|
||||
DnsServerAddresses nameServerAddresses,
|
||||
final DnsCache resolveCache,
|
||||
long queryTimeoutMillis,
|
||||
InternetProtocolFamily[] resolvedAddressTypes,
|
||||
boolean recursionDesired,
|
||||
int maxQueriesPerResolve,
|
||||
boolean traceEnabled,
|
||||
int maxPayloadSize,
|
||||
boolean optResourceEnabled,
|
||||
HostsFileEntriesResolver hostsFileEntriesResolver,
|
||||
String[] searchDomains,
|
||||
int ndots) {
|
||||
this(eventLoop, channelFactory, nameServerAddresses, resolveCache, NoopDnsCache.INSTANCE, queryTimeoutMillis,
|
||||
resolvedAddressTypes, recursionDesired, maxQueriesPerResolve, traceEnabled, maxPayloadSize,
|
||||
optResourceEnabled, hostsFileEntriesResolver, searchDomains, ndots, true);
|
||||
}
|
||||
/**
|
||||
* Creates a new DNS-based name resolver that communicates with the specified list of DNS servers.
|
||||
*
|
||||
@ -215,7 +184,7 @@ public class DnsNameResolver extends InetNameResolver {
|
||||
* @param resolveCache the DNS resolved entries cache
|
||||
* @param authoritativeDnsServerCache the cache used to find the authoritative DNS server for a domain
|
||||
* @param queryTimeoutMillis timeout of each DNS query in millis
|
||||
* @param resolvedAddressTypes list of the protocol families
|
||||
* @param resolvedAddressTypes the preferred address types
|
||||
* @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
|
||||
@ -234,7 +203,7 @@ public class DnsNameResolver extends InetNameResolver {
|
||||
final DnsCache resolveCache,
|
||||
DnsCache authoritativeDnsServerCache,
|
||||
long queryTimeoutMillis,
|
||||
InternetProtocolFamily[] resolvedAddressTypes,
|
||||
ResolvedAddressTypes resolvedAddressTypes,
|
||||
boolean recursionDesired,
|
||||
int maxQueriesPerResolve,
|
||||
boolean traceEnabled,
|
||||
@ -248,7 +217,7 @@ public class DnsNameResolver extends InetNameResolver {
|
||||
checkNotNull(channelFactory, "channelFactory");
|
||||
this.nameServerAddresses = checkNotNull(nameServerAddresses, "nameServerAddresses");
|
||||
this.queryTimeoutMillis = checkPositive(queryTimeoutMillis, "queryTimeoutMillis");
|
||||
this.resolvedAddressTypes = checkNonEmpty(resolvedAddressTypes, "resolvedAddressTypes");
|
||||
this.resolvedAddressTypes = resolvedAddressTypes != null ? resolvedAddressTypes : DEFAULT_RESOLVE_ADDRESS_TYPES;
|
||||
this.recursionDesired = recursionDesired;
|
||||
this.maxQueriesPerResolve = checkPositive(maxQueriesPerResolve, "maxQueriesPerResolve");
|
||||
this.traceEnabled = traceEnabled;
|
||||
@ -261,31 +230,38 @@ public class DnsNameResolver extends InetNameResolver {
|
||||
this.ndots = checkPositiveOrZero(ndots, "ndots");
|
||||
this.decodeIdn = decodeIdn;
|
||||
|
||||
boolean supportsARecords = false;
|
||||
boolean supportsAAAARecords = false;
|
||||
// Use LinkedHashSet to maintain correct ordering.
|
||||
Set<DnsRecordType> recordTypes = new LinkedHashSet<DnsRecordType>(resolvedAddressTypes.length);
|
||||
for (InternetProtocolFamily family: resolvedAddressTypes) {
|
||||
switch (family) {
|
||||
case IPv4:
|
||||
switch (this.resolvedAddressTypes) {
|
||||
case IPV4_ONLY:
|
||||
supportsAAAARecords = false;
|
||||
supportsARecords = true;
|
||||
recordTypes.add(DnsRecordType.A);
|
||||
resolveRecordTypes = IPV4_ONLY_RESOLVED_RECORD_TYPES;
|
||||
resolvedInternetProtocolFamilies = IPV4_ONLY_RESOLVED_PROTOCOL_FAMILIES;
|
||||
preferredAddressType = InternetProtocolFamily.IPv4;
|
||||
break;
|
||||
case IPv6:
|
||||
case IPV4_PREFERRED:
|
||||
supportsAAAARecords = true;
|
||||
recordTypes.add(DnsRecordType.AAAA);
|
||||
supportsARecords = true;
|
||||
resolveRecordTypes = IPV4_PREFERRED_RESOLVED_RECORD_TYPES;
|
||||
resolvedInternetProtocolFamilies = IPV4_PREFERRED_RESOLVED_PROTOCOL_FAMILIES;
|
||||
preferredAddressType = InternetProtocolFamily.IPv4;
|
||||
break;
|
||||
case IPV6_ONLY:
|
||||
supportsAAAARecords = true;
|
||||
supportsARecords = false;
|
||||
resolveRecordTypes = IPV6_ONLY_RESOLVED_RECORD_TYPES;
|
||||
resolvedInternetProtocolFamilies = IPV6_ONLY_RESOLVED_PROTOCOL_FAMILIES;
|
||||
preferredAddressType = InternetProtocolFamily.IPv6;
|
||||
break;
|
||||
case IPV6_PREFERRED:
|
||||
supportsAAAARecords = true;
|
||||
supportsARecords = true;
|
||||
resolveRecordTypes = IPV6_PREFERRED_RESOLVED_RECORD_TYPES;
|
||||
resolvedInternetProtocolFamilies = IPV6_PREFERRED_RESOLVED_PROTOCOL_FAMILIES;
|
||||
preferredAddressType = InternetProtocolFamily.IPv6;
|
||||
break;
|
||||
default:
|
||||
throw new Error();
|
||||
throw new IllegalArgumentException("Unknown ResolvedAddressTypes " + resolvedAddressTypes);
|
||||
}
|
||||
}
|
||||
|
||||
// One of both must be always true.
|
||||
assert supportsARecords || supportsAAAARecords;
|
||||
this.supportsAAAARecords = supportsAAAARecords;
|
||||
this.supportsARecords = supportsARecords;
|
||||
resolveRecordTypes = recordTypes.toArray(new DnsRecordType[recordTypes.size()]);
|
||||
preferredAddressType = resolvedAddressTypes[0];
|
||||
|
||||
Bootstrap b = new Bootstrap();
|
||||
b.group(executor());
|
||||
@ -339,16 +315,15 @@ public class DnsNameResolver extends InetNameResolver {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of the protocol families of the address resolved by {@link #resolve(String)}
|
||||
* in the order of preference.
|
||||
* Returns the {@link ResolvedAddressTypes} resolved by {@link #resolve(String)}.
|
||||
* The default value depends on the value of the system property {@code "java.net.preferIPv6Addresses"}.
|
||||
*/
|
||||
public List<InternetProtocolFamily> resolvedAddressTypes() {
|
||||
return Arrays.asList(resolvedAddressTypes);
|
||||
public ResolvedAddressTypes resolvedAddressTypes() {
|
||||
return resolvedAddressTypes;
|
||||
}
|
||||
|
||||
InternetProtocolFamily[] resolveAddressTypesUnsafe() {
|
||||
return resolvedAddressTypes;
|
||||
InternetProtocolFamily[] resolvedInternetProtocolFamiliesUnsafe() {
|
||||
return resolvedInternetProtocolFamilies;
|
||||
}
|
||||
|
||||
final String[] searchDomains() {
|
||||
@ -447,7 +422,7 @@ public class DnsNameResolver extends InetNameResolver {
|
||||
if (hostsFileEntriesResolver == null) {
|
||||
return null;
|
||||
} else {
|
||||
InetAddress address = hostsFileEntriesResolver.address(hostname);
|
||||
InetAddress address = hostsFileEntriesResolver.address(hostname, resolvedAddressTypes);
|
||||
if (address == null && PlatformDependent.isWindows() && LOCALHOST.equalsIgnoreCase(hostname)) {
|
||||
// If we tried to resolve localhost we need workaround that windows removed localhost from its
|
||||
// hostfile in later versions.
|
||||
@ -526,7 +501,7 @@ public class DnsNameResolver extends InetNameResolver {
|
||||
|
||||
@Override
|
||||
protected void doResolve(String inetHost, Promise<InetAddress> promise) throws Exception {
|
||||
doResolve(inetHost, EMTPY_ADDITIONALS, promise, resolveCache);
|
||||
doResolve(inetHost, EMPTY_ADDITIONALS, promise, resolveCache);
|
||||
}
|
||||
|
||||
private static DnsRecord[] toArray(Iterable<DnsRecord> additionals, boolean validateType) {
|
||||
@ -541,7 +516,7 @@ public class DnsNameResolver extends InetNameResolver {
|
||||
|
||||
Iterator<DnsRecord> additionalsIt = additionals.iterator();
|
||||
if (!additionalsIt.hasNext()) {
|
||||
return EMTPY_ADDITIONALS;
|
||||
return EMPTY_ADDITIONALS;
|
||||
}
|
||||
List<DnsRecord> records = new ArrayList<DnsRecord>();
|
||||
do {
|
||||
@ -617,7 +592,7 @@ public class DnsNameResolver extends InetNameResolver {
|
||||
cause = cachedEntries.get(0).cause();
|
||||
} else {
|
||||
// Find the first entry with the preferred address type.
|
||||
for (InternetProtocolFamily f : resolvedAddressTypes) {
|
||||
for (InternetProtocolFamily f : resolvedInternetProtocolFamilies) {
|
||||
for (int i = 0; i < numEntries; i++) {
|
||||
final DnsCacheEntry e = cachedEntries.get(i);
|
||||
if (f.addressType().isInstance(e.address())) {
|
||||
@ -692,7 +667,7 @@ public class DnsNameResolver extends InetNameResolver {
|
||||
|
||||
@Override
|
||||
protected void doResolveAll(String inetHost, Promise<List<InetAddress>> promise) throws Exception {
|
||||
doResolveAll(inetHost, EMTPY_ADDITIONALS, promise, resolveCache);
|
||||
doResolveAll(inetHost, EMPTY_ADDITIONALS, promise, resolveCache);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -746,7 +721,7 @@ public class DnsNameResolver extends InetNameResolver {
|
||||
if (cachedEntries.get(0).cause() != null) {
|
||||
cause = cachedEntries.get(0).cause();
|
||||
} else {
|
||||
for (InternetProtocolFamily f : resolvedAddressTypes) {
|
||||
for (InternetProtocolFamily f : resolvedInternetProtocolFamilies) {
|
||||
for (int i = 0; i < numEntries; i++) {
|
||||
final DnsCacheEntry e = cachedEntries.get(i);
|
||||
if (f.addressType().isInstance(e.address())) {
|
||||
@ -859,7 +834,7 @@ public class DnsNameResolver extends InetNameResolver {
|
||||
public Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> query(
|
||||
InetSocketAddress nameServerAddr, DnsQuestion question) {
|
||||
|
||||
return query0(nameServerAddr, question, EMTPY_ADDITIONALS,
|
||||
return query0(nameServerAddr, question, EMPTY_ADDITIONALS,
|
||||
ch.eventLoop().<AddressedEnvelope<? extends DnsResponse, InetSocketAddress>>newPromise());
|
||||
}
|
||||
|
||||
@ -880,7 +855,7 @@ public class DnsNameResolver extends InetNameResolver {
|
||||
InetSocketAddress nameServerAddr, DnsQuestion question,
|
||||
Promise<AddressedEnvelope<? extends DnsResponse, InetSocketAddress>> promise) {
|
||||
|
||||
return query0(nameServerAddr, question, EMTPY_ADDITIONALS, promise);
|
||||
return query0(nameServerAddr, question, EMPTY_ADDITIONALS, promise);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,6 +23,7 @@ import io.netty.channel.ReflectiveChannelFactory;
|
||||
import io.netty.channel.socket.DatagramChannel;
|
||||
import io.netty.channel.socket.InternetProtocolFamily;
|
||||
import io.netty.resolver.HostsFileEntriesResolver;
|
||||
import io.netty.resolver.ResolvedAddressTypes;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -45,14 +46,14 @@ public final class DnsNameResolverBuilder {
|
||||
private Integer maxTtl;
|
||||
private Integer negativeTtl;
|
||||
private long queryTimeoutMillis = 5000;
|
||||
private InternetProtocolFamily[] resolvedAddressTypes = DnsNameResolver.DEFAULT_RESOLVE_ADDRESS_TYPES;
|
||||
private ResolvedAddressTypes resolvedAddressTypes = DnsNameResolver.DEFAULT_RESOLVE_ADDRESS_TYPES;
|
||||
private boolean recursionDesired = true;
|
||||
private int maxQueriesPerResolve = 16;
|
||||
private boolean traceEnabled;
|
||||
private int maxPayloadSize = 4096;
|
||||
private boolean optResourceEnabled = true;
|
||||
private HostsFileEntriesResolver hostsFileEntriesResolver = HostsFileEntriesResolver.DEFAULT;
|
||||
private String[] searchDomains = DnsNameResolver.DEFAULT_SEACH_DOMAINS;
|
||||
private String[] searchDomains = DnsNameResolver.DEFAULT_SEARCH_DOMAINS;
|
||||
private int ndots = 1;
|
||||
private boolean decodeIdn = true;
|
||||
|
||||
@ -162,76 +163,45 @@ public final class DnsNameResolverBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}
|
||||
* Compute a {@link ResolvedAddressTypes} from some {@link InternetProtocolFamily}s.
|
||||
* An empty input will return the default value, based on "java.net" System properties.
|
||||
* Valid inputs are (), (IPv4), (IPv6), (Ipv4, IPv6) and (IPv6, IPv4).
|
||||
* @param internetProtocolFamilies a valid sequence of {@link InternetProtocolFamily}s
|
||||
* @return a {@link ResolvedAddressTypes}
|
||||
*/
|
||||
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;
|
||||
public static ResolvedAddressTypes computeResolvedAddressTypes(InternetProtocolFamily... internetProtocolFamilies) {
|
||||
if (internetProtocolFamilies == null || internetProtocolFamilies.length == 0) {
|
||||
return DnsNameResolver.DEFAULT_RESOLVE_ADDRESS_TYPES;
|
||||
}
|
||||
if (internetProtocolFamilies.length > 2) {
|
||||
throw new IllegalArgumentException("No more than 2 InternetProtocolFamilies");
|
||||
}
|
||||
|
||||
// Avoid duplicate entries.
|
||||
if (list.contains(f)) {
|
||||
continue;
|
||||
switch(internetProtocolFamilies[0]) {
|
||||
case IPv4:
|
||||
return (internetProtocolFamilies.length >= 2
|
||||
&& internetProtocolFamilies[1] == InternetProtocolFamily.IPv6) ?
|
||||
ResolvedAddressTypes.IPV4_PREFERRED: ResolvedAddressTypes.IPV4_ONLY;
|
||||
case IPv6:
|
||||
return (internetProtocolFamilies.length >= 2
|
||||
&& internetProtocolFamilies[1] == InternetProtocolFamily.IPv4) ?
|
||||
ResolvedAddressTypes.IPV6_PREFERRED: ResolvedAddressTypes.IPV6_ONLY;
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
"Couldn't resolve ResolvedAddressTypes from InternetProtocolFamily array");
|
||||
}
|
||||
|
||||
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}.
|
||||
* You can use {@link DnsNameResolverBuilder#computeResolvedAddressTypes(InternetProtocolFamily...)}
|
||||
* to get a {@link ResolvedAddressTypes} out of some {@link InternetProtocolFamily}s.
|
||||
*
|
||||
* @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()]);
|
||||
|
||||
public DnsNameResolverBuilder resolvedAddressTypes(ResolvedAddressTypes resolvedAddressTypes) {
|
||||
this.resolvedAddressTypes = resolvedAddressTypes;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ abstract class DnsNameResolverContext<T> {
|
||||
private final DnsCache resolveCache;
|
||||
private final boolean traceEnabled;
|
||||
private final int maxAllowedQueries;
|
||||
private final InternetProtocolFamily[] resolveAddressTypes;
|
||||
private final InternetProtocolFamily[] resolvedInternetProtocolFamilies;
|
||||
private final DnsRecord[] additionals;
|
||||
|
||||
private final Set<Future<AddressedEnvelope<DnsResponse, InetSocketAddress>>> queriesInProgress =
|
||||
@ -97,7 +97,7 @@ abstract class DnsNameResolverContext<T> {
|
||||
|
||||
nameServerAddrs = parent.nameServerAddresses.stream();
|
||||
maxAllowedQueries = parent.maxQueriesPerResolve();
|
||||
resolveAddressTypes = parent.resolveAddressTypesUnsafe();
|
||||
resolvedInternetProtocolFamilies = parent.resolvedInternetProtocolFamiliesUnsafe();
|
||||
traceEnabled = parent.isTraceEnabled();
|
||||
allowedQueries = maxAllowedQueries;
|
||||
}
|
||||
@ -611,7 +611,7 @@ abstract class DnsNameResolverContext<T> {
|
||||
|
||||
if (resolvedEntries != null) {
|
||||
// Found at least one resolved address.
|
||||
for (InternetProtocolFamily f: resolveAddressTypes) {
|
||||
for (InternetProtocolFamily f: resolvedInternetProtocolFamilies) {
|
||||
if (finishResolve(f.addressType(), resolvedEntries, promise)) {
|
||||
return;
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ import io.netty.handler.codec.dns.DnsRecordType;
|
||||
import io.netty.handler.codec.dns.DnsResponse;
|
||||
import io.netty.handler.codec.dns.DnsResponseCode;
|
||||
import io.netty.handler.codec.dns.DnsSection;
|
||||
import io.netty.resolver.ResolvedAddressTypes;
|
||||
import io.netty.util.NetUtil;
|
||||
import io.netty.resolver.HostsFileEntriesResolver;
|
||||
import io.netty.util.concurrent.Future;
|
||||
@ -65,7 +66,6 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.hasToString;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@ -283,12 +283,12 @@ public class DnsNameResolverTest {
|
||||
return newResolver(true);
|
||||
}
|
||||
|
||||
private static DnsNameResolverBuilder newResolver(InternetProtocolFamily... resolvedAddressTypes) {
|
||||
private static DnsNameResolverBuilder newResolver(ResolvedAddressTypes resolvedAddressTypes) {
|
||||
return newResolver()
|
||||
.resolvedAddressTypes(resolvedAddressTypes);
|
||||
}
|
||||
|
||||
private static DnsNameResolverBuilder newNonCachedResolver(InternetProtocolFamily... resolvedAddressTypes) {
|
||||
private static DnsNameResolverBuilder newNonCachedResolver(ResolvedAddressTypes resolvedAddressTypes) {
|
||||
return newResolver()
|
||||
.resolveCache(NoopDnsCache.INSTANCE)
|
||||
.resolvedAddressTypes(resolvedAddressTypes);
|
||||
@ -306,7 +306,7 @@ public class DnsNameResolverTest {
|
||||
|
||||
@Test
|
||||
public void testResolveAorAAAA() throws Exception {
|
||||
DnsNameResolver resolver = newResolver(InternetProtocolFamily.IPv4, InternetProtocolFamily.IPv6).build();
|
||||
DnsNameResolver resolver = newResolver(ResolvedAddressTypes.IPV4_PREFERRED).build();
|
||||
try {
|
||||
testResolve0(resolver, EXCLUSIONS_RESOLVE_A);
|
||||
} finally {
|
||||
@ -316,7 +316,7 @@ public class DnsNameResolverTest {
|
||||
|
||||
@Test
|
||||
public void testResolveAAAAorA() throws Exception {
|
||||
DnsNameResolver resolver = newResolver(InternetProtocolFamily.IPv6, InternetProtocolFamily.IPv4).build();
|
||||
DnsNameResolver resolver = newResolver(ResolvedAddressTypes.IPV6_PREFERRED).build();
|
||||
try {
|
||||
testResolve0(resolver, EXCLUSIONS_RESOLVE_A);
|
||||
} finally {
|
||||
@ -326,7 +326,7 @@ public class DnsNameResolverTest {
|
||||
|
||||
@Test
|
||||
public void testResolveA() throws Exception {
|
||||
DnsNameResolver resolver = newResolver(InternetProtocolFamily.IPv4)
|
||||
DnsNameResolver resolver = newResolver(ResolvedAddressTypes.IPV4_ONLY)
|
||||
// Cache for eternity
|
||||
.ttl(Integer.MAX_VALUE, Integer.MAX_VALUE)
|
||||
.build();
|
||||
@ -357,7 +357,7 @@ public class DnsNameResolverTest {
|
||||
|
||||
@Test
|
||||
public void testResolveAAAA() throws Exception {
|
||||
DnsNameResolver resolver = newResolver(InternetProtocolFamily.IPv6).build();
|
||||
DnsNameResolver resolver = newResolver(ResolvedAddressTypes.IPV6_ONLY).build();
|
||||
try {
|
||||
testResolve0(resolver, EXCLUSIONS_RESOLVE_AAAA);
|
||||
} finally {
|
||||
@ -367,7 +367,7 @@ public class DnsNameResolverTest {
|
||||
|
||||
@Test
|
||||
public void testNonCachedResolve() throws Exception {
|
||||
DnsNameResolver resolver = newNonCachedResolver(InternetProtocolFamily.IPv4).build();
|
||||
DnsNameResolver resolver = newNonCachedResolver(ResolvedAddressTypes.IPV4_ONLY).build();
|
||||
try {
|
||||
testResolve0(resolver, EXCLUSIONS_RESOLVE_A);
|
||||
} finally {
|
||||
@ -442,7 +442,7 @@ public class DnsNameResolverTest {
|
||||
assertThat(resolved.getHostName(), is(unresolved));
|
||||
|
||||
boolean typeMatches = false;
|
||||
for (InternetProtocolFamily f: resolver.resolvedAddressTypes()) {
|
||||
for (InternetProtocolFamily f: resolver.resolvedInternetProtocolFamiliesUnsafe()) {
|
||||
Class<?> resolvedType = resolved.getClass();
|
||||
if (f.addressType().isAssignableFrom(resolvedType)) {
|
||||
typeMatches = true;
|
||||
@ -574,26 +574,26 @@ public class DnsNameResolverTest {
|
||||
|
||||
@Test
|
||||
public void testResolveEmptyIpv4() {
|
||||
testResolve0(InternetProtocolFamily.IPv4, NetUtil.LOCALHOST4, StringUtil.EMPTY_STRING);
|
||||
testResolve0(ResolvedAddressTypes.IPV4_ONLY, NetUtil.LOCALHOST4, StringUtil.EMPTY_STRING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveEmptyIpv6() {
|
||||
testResolve0(InternetProtocolFamily.IPv6, NetUtil.LOCALHOST6, StringUtil.EMPTY_STRING);
|
||||
testResolve0(ResolvedAddressTypes.IPV6_ONLY, NetUtil.LOCALHOST6, StringUtil.EMPTY_STRING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveNullIpv4() {
|
||||
testResolve0(InternetProtocolFamily.IPv4, NetUtil.LOCALHOST4, null);
|
||||
testResolve0(ResolvedAddressTypes.IPV4_ONLY, NetUtil.LOCALHOST4, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveNullIpv6() {
|
||||
testResolve0(InternetProtocolFamily.IPv6, NetUtil.LOCALHOST6, null);
|
||||
testResolve0(ResolvedAddressTypes.IPV6_ONLY, NetUtil.LOCALHOST6, null);
|
||||
}
|
||||
|
||||
private static void testResolve0(InternetProtocolFamily family, InetAddress expectedAddr, String name) {
|
||||
DnsNameResolver resolver = newResolver(family).build();
|
||||
private static void testResolve0(ResolvedAddressTypes addressTypes, InetAddress expectedAddr, String name) {
|
||||
DnsNameResolver resolver = newResolver(addressTypes).build();
|
||||
try {
|
||||
InetAddress address = resolver.resolve(name).syncUninterruptibly().getNow();
|
||||
assertEquals(expectedAddr, address);
|
||||
@ -604,26 +604,26 @@ public class DnsNameResolverTest {
|
||||
|
||||
@Test
|
||||
public void testResolveAllEmptyIpv4() {
|
||||
testResolveAll0(InternetProtocolFamily.IPv4, NetUtil.LOCALHOST4, StringUtil.EMPTY_STRING);
|
||||
testResolveAll0(ResolvedAddressTypes.IPV4_ONLY, NetUtil.LOCALHOST4, StringUtil.EMPTY_STRING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveAllEmptyIpv6() {
|
||||
testResolveAll0(InternetProtocolFamily.IPv6, NetUtil.LOCALHOST6, StringUtil.EMPTY_STRING);
|
||||
testResolveAll0(ResolvedAddressTypes.IPV6_ONLY, NetUtil.LOCALHOST6, StringUtil.EMPTY_STRING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveAllNullIpv4() {
|
||||
testResolveAll0(InternetProtocolFamily.IPv4, NetUtil.LOCALHOST4, null);
|
||||
testResolveAll0(ResolvedAddressTypes.IPV4_ONLY, NetUtil.LOCALHOST4, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveAllNullIpv6() {
|
||||
testResolveAll0(InternetProtocolFamily.IPv6, NetUtil.LOCALHOST6, null);
|
||||
testResolveAll0(ResolvedAddressTypes.IPV6_ONLY, NetUtil.LOCALHOST6, null);
|
||||
}
|
||||
|
||||
private static void testResolveAll0(InternetProtocolFamily family, InetAddress expectedAddr, String name) {
|
||||
DnsNameResolver resolver = newResolver(family).build();
|
||||
private static void testResolveAll0(ResolvedAddressTypes addressTypes, InetAddress expectedAddr, String name) {
|
||||
DnsNameResolver resolver = newResolver(addressTypes).build();
|
||||
try {
|
||||
List<InetAddress> addresses = resolver.resolveAll(name).syncUninterruptibly().getNow();
|
||||
assertEquals(1, addresses.size());
|
||||
@ -682,9 +682,9 @@ public class DnsNameResolverTest {
|
||||
DnsNameResolver resolver = new DnsNameResolver(
|
||||
group.next(), new ReflectiveChannelFactory<DatagramChannel>(NioDatagramChannel.class),
|
||||
DnsServerAddresses.singleton(dnsServer.localAddress()), NoopDnsCache.INSTANCE, nsCache,
|
||||
3000, new InternetProtocolFamily[] { InternetProtocolFamily.IPv4 }, true, 10,
|
||||
3000, ResolvedAddressTypes.IPV4_ONLY, true, 10,
|
||||
true, 4096, false, HostsFileEntriesResolver.DEFAULT,
|
||||
DnsNameResolver.DEFAULT_SEACH_DOMAINS, 0, true) {
|
||||
DnsNameResolver.DEFAULT_SEARCH_DOMAINS, 0, true) {
|
||||
@Override
|
||||
int dnsRedirectPort(InetAddress server) {
|
||||
return server.equals(dnsServerAuthority.localAddress().getAddress()) ?
|
||||
|
@ -15,6 +15,10 @@
|
||||
*/
|
||||
package io.netty.resolver;
|
||||
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@ -22,13 +26,39 @@ import java.util.Map;
|
||||
/**
|
||||
* Default {@link HostsFileEntriesResolver} that resolves hosts file entries only once.
|
||||
*/
|
||||
@UnstableApi
|
||||
public final class DefaultHostsFileEntriesResolver implements HostsFileEntriesResolver {
|
||||
|
||||
private final Map<String, InetAddress> entries = HostsFileParser.parseSilently();
|
||||
private final Map<String, Inet4Address> inet4Entries;
|
||||
private final Map<String, Inet6Address> inet6Entries;
|
||||
|
||||
public DefaultHostsFileEntriesResolver() {
|
||||
this(HostsFileParser.parseSilently());
|
||||
}
|
||||
|
||||
// for testing purpose only
|
||||
DefaultHostsFileEntriesResolver(HostsFileEntries entries) {
|
||||
inet4Entries = entries.inet4Entries();
|
||||
inet6Entries = entries.inet6Entries();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetAddress address(String inetHost) {
|
||||
return entries.get(normalize(inetHost));
|
||||
public InetAddress address(String inetHost, ResolvedAddressTypes resolvedAddressTypes) {
|
||||
String normalized = normalize(inetHost);
|
||||
switch (resolvedAddressTypes) {
|
||||
case IPV4_ONLY:
|
||||
return inet4Entries.get(normalized);
|
||||
case IPV6_ONLY:
|
||||
return inet6Entries.get(normalized);
|
||||
case IPV4_PREFERRED:
|
||||
Inet4Address inet4Address = inet4Entries.get(normalized);
|
||||
return inet4Address != null? inet4Address : inet6Entries.get(normalized);
|
||||
case IPV6_PREFERRED:
|
||||
Inet6Address inet6Address = inet6Entries.get(normalized);
|
||||
return inet6Address != null? inet6Address : inet4Entries.get(normalized);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown ResolvedAddressTypes " + resolvedAddressTypes);
|
||||
}
|
||||
}
|
||||
|
||||
// package-private for testing purposes
|
||||
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2017 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.internal.UnstableApi;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A container of hosts file entries
|
||||
*/
|
||||
@UnstableApi
|
||||
public final class HostsFileEntries {
|
||||
|
||||
/**
|
||||
* Empty entries
|
||||
*/
|
||||
static final HostsFileEntries EMPTY =
|
||||
new HostsFileEntries(
|
||||
Collections.<String, Inet4Address>emptyMap(),
|
||||
Collections.<String, Inet6Address>emptyMap());
|
||||
|
||||
private final Map<String, Inet4Address> inet4Entries;
|
||||
private final Map<String, Inet6Address> inet6Entries;
|
||||
|
||||
public HostsFileEntries(Map<String, Inet4Address> inet4Entries, Map<String, Inet6Address> inet6Entries) {
|
||||
this.inet4Entries = Collections.unmodifiableMap(new HashMap<String, Inet4Address>(inet4Entries));
|
||||
this.inet6Entries = Collections.unmodifiableMap(new HashMap<String, Inet6Address>(inet6Entries));
|
||||
}
|
||||
|
||||
/**
|
||||
* The IPv4 entries
|
||||
* @return the IPv4 entries
|
||||
*/
|
||||
public Map<String, Inet4Address> inet4Entries() {
|
||||
return inet4Entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* The IPv6 entries
|
||||
* @return the IPv6 entries
|
||||
*/
|
||||
public Map<String, Inet6Address> inet6Entries() {
|
||||
return inet6Entries;
|
||||
}
|
||||
}
|
@ -15,11 +15,14 @@
|
||||
*/
|
||||
package io.netty.resolver;
|
||||
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
/**
|
||||
* Resolves a hostname against the hosts file entries.
|
||||
*/
|
||||
@UnstableApi
|
||||
public interface HostsFileEntriesResolver {
|
||||
|
||||
/**
|
||||
@ -27,5 +30,11 @@ public interface HostsFileEntriesResolver {
|
||||
*/
|
||||
HostsFileEntriesResolver DEFAULT = new DefaultHostsFileEntriesResolver();
|
||||
|
||||
InetAddress address(String inetHost);
|
||||
/**
|
||||
* Resolve the address of a hostname against the entries in a hosts file, depending on some address types.
|
||||
* @param inetHost the hostname to resolve
|
||||
* @param resolvedAddressTypes the address types to resolve
|
||||
* @return the first matching address
|
||||
*/
|
||||
InetAddress address(String inetHost, ResolvedAddressTypes resolvedAddressTypes);
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ package io.netty.resolver;
|
||||
|
||||
import io.netty.util.NetUtil;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import io.netty.util.internal.UnstableApi;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
|
||||
@ -25,9 +26,10 @@ import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.HashMap;
|
||||
@ -39,6 +41,7 @@ import static io.netty.util.internal.ObjectUtil.*;
|
||||
/**
|
||||
* A parser for hosts files.
|
||||
*/
|
||||
@UnstableApi
|
||||
public final class HostsFileParser {
|
||||
|
||||
private static final String WINDOWS_DEFAULT_SYSTEM_ROOT = "C:\\Windows";
|
||||
@ -65,25 +68,25 @@ public final class HostsFileParser {
|
||||
/**
|
||||
* Parse hosts file at standard OS location.
|
||||
*
|
||||
* @return a map of hostname or alias to {@link InetAddress}
|
||||
* @return a {@link HostsFileEntries}
|
||||
*/
|
||||
public static Map<String, InetAddress> parseSilently() {
|
||||
public static HostsFileEntries 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();
|
||||
return HostsFileEntries.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse hosts file at standard OS location.
|
||||
*
|
||||
* @return a map of hostname or alias to {@link InetAddress}
|
||||
* @return a {@link HostsFileEntries}
|
||||
* @throws IOException file could not be read
|
||||
*/
|
||||
public static Map<String, InetAddress> parse() throws IOException {
|
||||
public static HostsFileEntries parse() throws IOException {
|
||||
return parse(locateHostsFile());
|
||||
}
|
||||
|
||||
@ -91,15 +94,15 @@ public final class HostsFileParser {
|
||||
* Parse a hosts file.
|
||||
*
|
||||
* @param file the file to be parsed
|
||||
* @return a map of hostname or alias to {@link InetAddress}
|
||||
* @return a {@link HostsFileEntries}
|
||||
* @throws IOException file could not be read
|
||||
*/
|
||||
public static Map<String, InetAddress> parse(File file) throws IOException {
|
||||
public static HostsFileEntries parse(File file) throws IOException {
|
||||
checkNotNull(file, "file");
|
||||
if (file.exists() && file.isFile()) {
|
||||
return parse(new BufferedReader(new FileReader(file)));
|
||||
} else {
|
||||
return Collections.emptyMap();
|
||||
return HostsFileEntries.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,14 +110,15 @@ public final class HostsFileParser {
|
||||
* Parse a reader of hosts file format.
|
||||
*
|
||||
* @param reader the file to be parsed
|
||||
* @return a map of hostname or alias to {@link InetAddress}
|
||||
* @return a {@link HostsFileEntries}
|
||||
* @throws IOException file could not be read
|
||||
*/
|
||||
public static Map<String, InetAddress> parse(Reader reader) throws IOException {
|
||||
public static HostsFileEntries parse(Reader reader) throws IOException {
|
||||
checkNotNull(reader, "reader");
|
||||
BufferedReader buff = new BufferedReader(reader);
|
||||
try {
|
||||
Map<String, InetAddress> entries = new HashMap<String, InetAddress>();
|
||||
Map<String, Inet4Address> ipv4Entries = new HashMap<String, Inet4Address>();
|
||||
Map<String, Inet6Address> ipv6Entries = new HashMap<String, Inet6Address>();
|
||||
String line;
|
||||
while ((line = buff.readLine()) != null) {
|
||||
// remove comment
|
||||
@ -153,14 +157,25 @@ public final class HostsFileParser {
|
||||
for (int i = 1; i < lineParts.size(); i ++) {
|
||||
String hostname = lineParts.get(i);
|
||||
String hostnameLower = hostname.toLowerCase(Locale.ENGLISH);
|
||||
if (!entries.containsKey(hostnameLower)) {
|
||||
// trying to map a host to multiple IPs is wrong
|
||||
// only the first entry is honored
|
||||
entries.put(hostnameLower, InetAddress.getByAddress(hostname, ipBytes));
|
||||
InetAddress address = InetAddress.getByAddress(hostname, ipBytes);
|
||||
if (address instanceof Inet4Address) {
|
||||
Inet4Address previous = ipv4Entries.put(hostnameLower, (Inet4Address) address);
|
||||
if (previous != null) {
|
||||
// restore, we want to keep the first entry
|
||||
ipv4Entries.put(hostnameLower, previous);
|
||||
}
|
||||
} else {
|
||||
Inet6Address previous = ipv6Entries.put(hostnameLower, (Inet6Address) address);
|
||||
if (previous != null) {
|
||||
// restore, we want to keep the first entry
|
||||
ipv6Entries.put(hostnameLower, previous);
|
||||
}
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
return ipv4Entries.isEmpty() && ipv6Entries.isEmpty() ?
|
||||
HostsFileEntries.EMPTY :
|
||||
new HostsFileEntries(ipv4Entries, ipv6Entries);
|
||||
} finally {
|
||||
try {
|
||||
buff.close();
|
||||
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2017 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.internal.UnstableApi;
|
||||
|
||||
/**
|
||||
* Defined resolved address types.
|
||||
*/
|
||||
@UnstableApi
|
||||
public enum ResolvedAddressTypes {
|
||||
/**
|
||||
* Only resolve IPv4 addresses
|
||||
*/
|
||||
IPV4_ONLY,
|
||||
/**
|
||||
* Only resolve IPv6 addresses
|
||||
*/
|
||||
IPV6_ONLY,
|
||||
/**
|
||||
* Prefer IPv4 addresses over IPv6 ones
|
||||
*/
|
||||
IPV4_PREFERRED,
|
||||
/**
|
||||
* Prefer IPv6 addresses over IPv4 ones
|
||||
*/
|
||||
IPV6_PREFERRED
|
||||
}
|
@ -13,21 +13,72 @@
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
/**
|
||||
* show issue https://github.com/netty/netty/issues/5182
|
||||
* HostsFileParser tries to resolve hostnames as case-sensitive
|
||||
*/
|
||||
package io.netty.resolver;
|
||||
|
||||
import io.netty.util.NetUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class DefaultHostsFileEntriesResolverTest {
|
||||
|
||||
/**
|
||||
* show issue https://github.com/netty/netty/issues/5182
|
||||
* HostsFileParser tries to resolve hostnames as case-sensitive
|
||||
*/
|
||||
@Test
|
||||
public void testCaseInsensitivity() throws Exception {
|
||||
DefaultHostsFileEntriesResolver resolver = new DefaultHostsFileEntriesResolver();
|
||||
//normalized somehow
|
||||
Assert.assertEquals(resolver.normalize("localhost"), resolver.normalize("LOCALHOST"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldntFindWhenAddressTypeDoesntMatch() {
|
||||
Map<String, Inet4Address> inet4Entries = new HashMap<String, Inet4Address>();
|
||||
Map<String, Inet6Address> inet6Entries = new HashMap<String, Inet6Address>();
|
||||
|
||||
inet4Entries.put("localhost", NetUtil.LOCALHOST4);
|
||||
|
||||
DefaultHostsFileEntriesResolver resolver =
|
||||
new DefaultHostsFileEntriesResolver(new HostsFileEntries(inet4Entries, inet6Entries));
|
||||
|
||||
InetAddress address = resolver.address("localhost", ResolvedAddressTypes.IPV6_ONLY);
|
||||
Assert.assertNull("Should pick an IPv6 address", address);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPickIpv4WhenBothAreDefinedButIpv4IsPreferred() {
|
||||
Map<String, Inet4Address> inet4Entries = new HashMap<String, Inet4Address>();
|
||||
Map<String, Inet6Address> inet6Entries = new HashMap<String, Inet6Address>();
|
||||
|
||||
inet4Entries.put("localhost", NetUtil.LOCALHOST4);
|
||||
inet6Entries.put("localhost", NetUtil.LOCALHOST6);
|
||||
|
||||
DefaultHostsFileEntriesResolver resolver =
|
||||
new DefaultHostsFileEntriesResolver(new HostsFileEntries(inet4Entries, inet6Entries));
|
||||
|
||||
InetAddress address = resolver.address("localhost", ResolvedAddressTypes.IPV4_PREFERRED);
|
||||
Assert.assertTrue("Should pick an IPv4 address", address instanceof Inet4Address);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPickIpv6WhenBothAreDefinedButIpv6IsPreferred() {
|
||||
Map<String, Inet4Address> inet4Entries = new HashMap<String, Inet4Address>();
|
||||
Map<String, Inet6Address> inet6Entries = new HashMap<String, Inet6Address>();
|
||||
|
||||
inet4Entries.put("localhost", NetUtil.LOCALHOST4);
|
||||
inet6Entries.put("localhost", NetUtil.LOCALHOST6);
|
||||
|
||||
DefaultHostsFileEntriesResolver resolver =
|
||||
new DefaultHostsFileEntriesResolver(new HostsFileEntries(inet4Entries, inet6Entries));
|
||||
|
||||
InetAddress address = resolver.address("localhost", ResolvedAddressTypes.IPV6_PREFERRED);
|
||||
Assert.assertTrue("Should pick an IPv6 address", address instanceof Inet6Address);
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,8 @@ import org.junit.Test;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
@ -31,6 +32,7 @@ public class HostsFileParserTest {
|
||||
public void testParse() throws IOException {
|
||||
String hostsString = new StringBuilder()
|
||||
.append("127.0.0.1 host1").append("\n") // single hostname, separated with blanks
|
||||
.append("::1 host1").append("\n") // same as above, but IPv6
|
||||
.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
|
||||
@ -42,16 +44,20 @@ public class HostsFileParserTest {
|
||||
.append("192.168.0.6 host7").append("\n") // should be ignored since we have the uppercase host already
|
||||
.toString();
|
||||
|
||||
Map<String, InetAddress> entries = HostsFileParser.parse(new BufferedReader(new StringReader(hostsString)));
|
||||
HostsFileEntries entries = HostsFileParser.parse(new BufferedReader(new StringReader(hostsString)));
|
||||
Map<String, Inet4Address> inet4Entries = entries.inet4Entries();
|
||||
Map<String, Inet6Address> inet6Entries = entries.inet6Entries();
|
||||
|
||||
assertEquals("Expected 7 entries", 7, 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());
|
||||
assertNotNull("uppercase host doesn't resolve", entries.get("host7"));
|
||||
assertEquals("192.168.0.5", entries.get("host7").getHostAddress());
|
||||
assertEquals("Expected 7 IPv4 entries", 7, inet4Entries.size());
|
||||
assertEquals("Expected 1 IPv6 entries", 1, inet6Entries.size());
|
||||
assertEquals("127.0.0.1", inet4Entries.get("host1").getHostAddress());
|
||||
assertEquals("192.168.0.1", inet4Entries.get("host2").getHostAddress());
|
||||
assertEquals("192.168.0.2", inet4Entries.get("host3").getHostAddress());
|
||||
assertEquals("192.168.0.3", inet4Entries.get("host4").getHostAddress());
|
||||
assertEquals("192.168.0.3", inet4Entries.get("host5").getHostAddress());
|
||||
assertEquals("192.168.0.3", inet4Entries.get("host6").getHostAddress());
|
||||
assertNotNull("uppercase host doesn't resolve", inet4Entries.get("host7"));
|
||||
assertEquals("192.168.0.5", inet4Entries.get("host7").getHostAddress());
|
||||
assertEquals("0:0:0:0:0:0:0:1", inet6Entries.get("host1").getHostAddress());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user