Dns resolver: honor resolv.conf timeout, rotate and attempts options (#10207)
Motivations ----------- DnsNameResolverBuilder and DnsNameResolver do not auto-configure themselves uing default options define in /etc/resolv.conf. In particular, rotate, timeout and attempts options are ignored. Modifications ------------- - Modified UnixResolverDnsServerAddressStreamProvider to parse ndots, attempts and timeout options all at once and use these defaults to configure DnsNameResolver when values are not provided by the DnsNameResolverBuilder. - When rotate option is specified, the DnsServerAddresses returned by UnixResolverDnsServerAddressStreamProvider is rotational. - Amend resolv.conf options with the RES_OPTIONS environment variable when present. Result: Fixes https://github.com/netty/netty/issues/10202
This commit is contained in:
parent
5fa5ce34e1
commit
4f72cdf233
@ -77,9 +77,9 @@ import java.util.Comparator;
|
|||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.DNS_PORT;
|
import static io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.DNS_PORT;
|
||||||
import static io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider.parseEtcResolverFirstNdots;
|
|
||||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||||
import static io.netty.util.internal.ObjectUtil.checkPositive;
|
import static io.netty.util.internal.ObjectUtil.checkPositive;
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ public class DnsNameResolver extends InetNameResolver {
|
|||||||
|
|
||||||
static final ResolvedAddressTypes DEFAULT_RESOLVE_ADDRESS_TYPES;
|
static final ResolvedAddressTypes DEFAULT_RESOLVE_ADDRESS_TYPES;
|
||||||
static final String[] DEFAULT_SEARCH_DOMAINS;
|
static final String[] DEFAULT_SEARCH_DOMAINS;
|
||||||
private static final int DEFAULT_NDOTS;
|
private static final UnixResolverOptions DEFAULT_OPTIONS;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
if (NetUtil.isIpV4StackPreferred() || !anyInterfaceSupportsIpV6()) {
|
if (NetUtil.isIpV4StackPreferred() || !anyInterfaceSupportsIpV6()) {
|
||||||
@ -141,13 +141,13 @@ public class DnsNameResolver extends InetNameResolver {
|
|||||||
}
|
}
|
||||||
DEFAULT_SEARCH_DOMAINS = searchDomains;
|
DEFAULT_SEARCH_DOMAINS = searchDomains;
|
||||||
|
|
||||||
int ndots;
|
UnixResolverOptions options;
|
||||||
try {
|
try {
|
||||||
ndots = parseEtcResolverFirstNdots();
|
options = UnixResolverDnsServerAddressStreamProvider.parseEtcResolverOptions();
|
||||||
} catch (Exception ignore) {
|
} catch (Exception ignore) {
|
||||||
ndots = UnixResolverDnsServerAddressStreamProvider.DEFAULT_NDOTS;
|
options = UnixResolverOptions.newBuilder().build();
|
||||||
}
|
}
|
||||||
DEFAULT_NDOTS = ndots;
|
DEFAULT_OPTIONS = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -383,10 +383,12 @@ public class DnsNameResolver extends InetNameResolver {
|
|||||||
boolean decodeIdn,
|
boolean decodeIdn,
|
||||||
boolean completeOncePreferredResolved) {
|
boolean completeOncePreferredResolved) {
|
||||||
super(eventLoop);
|
super(eventLoop);
|
||||||
this.queryTimeoutMillis = checkPositive(queryTimeoutMillis, "queryTimeoutMillis");
|
this.queryTimeoutMillis = queryTimeoutMillis > 0
|
||||||
|
? queryTimeoutMillis
|
||||||
|
: TimeUnit.SECONDS.toMillis(DEFAULT_OPTIONS.timeout());
|
||||||
this.resolvedAddressTypes = resolvedAddressTypes != null ? resolvedAddressTypes : DEFAULT_RESOLVE_ADDRESS_TYPES;
|
this.resolvedAddressTypes = resolvedAddressTypes != null ? resolvedAddressTypes : DEFAULT_RESOLVE_ADDRESS_TYPES;
|
||||||
this.recursionDesired = recursionDesired;
|
this.recursionDesired = recursionDesired;
|
||||||
this.maxQueriesPerResolve = checkPositive(maxQueriesPerResolve, "maxQueriesPerResolve");
|
this.maxQueriesPerResolve = maxQueriesPerResolve > 0 ? maxQueriesPerResolve : DEFAULT_OPTIONS.attempts();
|
||||||
this.maxPayloadSize = checkPositive(maxPayloadSize, "maxPayloadSize");
|
this.maxPayloadSize = checkPositive(maxPayloadSize, "maxPayloadSize");
|
||||||
this.optResourceEnabled = optResourceEnabled;
|
this.optResourceEnabled = optResourceEnabled;
|
||||||
this.hostsFileEntriesResolver = checkNotNull(hostsFileEntriesResolver, "hostsFileEntriesResolver");
|
this.hostsFileEntriesResolver = checkNotNull(hostsFileEntriesResolver, "hostsFileEntriesResolver");
|
||||||
@ -401,7 +403,7 @@ public class DnsNameResolver extends InetNameResolver {
|
|||||||
dnsQueryLifecycleObserverFactory) :
|
dnsQueryLifecycleObserverFactory) :
|
||||||
checkNotNull(dnsQueryLifecycleObserverFactory, "dnsQueryLifecycleObserverFactory");
|
checkNotNull(dnsQueryLifecycleObserverFactory, "dnsQueryLifecycleObserverFactory");
|
||||||
this.searchDomains = searchDomains != null ? searchDomains.clone() : DEFAULT_SEARCH_DOMAINS;
|
this.searchDomains = searchDomains != null ? searchDomains.clone() : DEFAULT_SEARCH_DOMAINS;
|
||||||
this.ndots = ndots >= 0 ? ndots : DEFAULT_NDOTS;
|
this.ndots = ndots >= 0 ? ndots : DEFAULT_OPTIONS.ndots();
|
||||||
this.decodeIdn = decodeIdn;
|
this.decodeIdn = decodeIdn;
|
||||||
this.completeOncePreferredResolved = completeOncePreferredResolved;
|
this.completeOncePreferredResolved = completeOncePreferredResolved;
|
||||||
this.socketChannelFactory = socketChannelFactory;
|
this.socketChannelFactory = socketChannelFactory;
|
||||||
|
@ -29,7 +29,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static io.netty.resolver.dns.DnsServerAddressStreamProviders.platformDefault;
|
|
||||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||||
import static io.netty.util.internal.ObjectUtil.intValue;
|
import static io.netty.util.internal.ObjectUtil.intValue;
|
||||||
|
|
||||||
@ -46,16 +45,17 @@ public final class DnsNameResolverBuilder {
|
|||||||
private Integer minTtl;
|
private Integer minTtl;
|
||||||
private Integer maxTtl;
|
private Integer maxTtl;
|
||||||
private Integer negativeTtl;
|
private Integer negativeTtl;
|
||||||
private long queryTimeoutMillis = 5000;
|
private long queryTimeoutMillis = -1;
|
||||||
private ResolvedAddressTypes resolvedAddressTypes = DnsNameResolver.DEFAULT_RESOLVE_ADDRESS_TYPES;
|
private ResolvedAddressTypes resolvedAddressTypes = DnsNameResolver.DEFAULT_RESOLVE_ADDRESS_TYPES;
|
||||||
private boolean completeOncePreferredResolved;
|
private boolean completeOncePreferredResolved;
|
||||||
private boolean recursionDesired = true;
|
private boolean recursionDesired = true;
|
||||||
private int maxQueriesPerResolve = 16;
|
private int maxQueriesPerResolve = -1;
|
||||||
private boolean traceEnabled;
|
private boolean traceEnabled;
|
||||||
private int maxPayloadSize = 4096;
|
private int maxPayloadSize = 4096;
|
||||||
private boolean optResourceEnabled = true;
|
private boolean optResourceEnabled = true;
|
||||||
private HostsFileEntriesResolver hostsFileEntriesResolver = HostsFileEntriesResolver.DEFAULT;
|
private HostsFileEntriesResolver hostsFileEntriesResolver = HostsFileEntriesResolver.DEFAULT;
|
||||||
private DnsServerAddressStreamProvider dnsServerAddressStreamProvider = platformDefault();
|
private DnsServerAddressStreamProvider dnsServerAddressStreamProvider =
|
||||||
|
DnsServerAddressStreamProviders.platformDefault();
|
||||||
private DnsQueryLifecycleObserverFactory dnsQueryLifecycleObserverFactory =
|
private DnsQueryLifecycleObserverFactory dnsQueryLifecycleObserverFactory =
|
||||||
NoopDnsQueryLifecycleObserverFactory.INSTANCE;
|
NoopDnsQueryLifecycleObserverFactory.INSTANCE;
|
||||||
private String[] searchDomains;
|
private String[] searchDomains;
|
||||||
|
@ -46,19 +46,22 @@ import static io.netty.util.internal.StringUtil.indexOfWhiteSpace;
|
|||||||
public final class UnixResolverDnsServerAddressStreamProvider implements DnsServerAddressStreamProvider {
|
public final class UnixResolverDnsServerAddressStreamProvider implements DnsServerAddressStreamProvider {
|
||||||
private static final InternalLogger logger =
|
private static final InternalLogger logger =
|
||||||
InternalLoggerFactory.getInstance(UnixResolverDnsServerAddressStreamProvider.class);
|
InternalLoggerFactory.getInstance(UnixResolverDnsServerAddressStreamProvider.class);
|
||||||
|
|
||||||
|
private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\s+");
|
||||||
|
private static final String RES_OPTIONS = System.getenv("RES_OPTIONS");
|
||||||
|
|
||||||
private static final String ETC_RESOLV_CONF_FILE = "/etc/resolv.conf";
|
private static final String ETC_RESOLV_CONF_FILE = "/etc/resolv.conf";
|
||||||
private static final String ETC_RESOLVER_DIR = "/etc/resolver";
|
private static final String ETC_RESOLVER_DIR = "/etc/resolver";
|
||||||
private static final String NAMESERVER_ROW_LABEL = "nameserver";
|
private static final String NAMESERVER_ROW_LABEL = "nameserver";
|
||||||
private static final String SORTLIST_ROW_LABEL = "sortlist";
|
private static final String SORTLIST_ROW_LABEL = "sortlist";
|
||||||
private static final String OPTIONS_ROW_LABEL = "options";
|
private static final String OPTIONS_ROW_LABEL = "options ";
|
||||||
|
private static final String OPTIONS_ROTATE_FLAG = "rotate";
|
||||||
private static final String DOMAIN_ROW_LABEL = "domain";
|
private static final String DOMAIN_ROW_LABEL = "domain";
|
||||||
private static final String SEARCH_ROW_LABEL = "search";
|
private static final String SEARCH_ROW_LABEL = "search";
|
||||||
private static final String PORT_ROW_LABEL = "port";
|
private static final String PORT_ROW_LABEL = "port";
|
||||||
private static final String NDOTS_LABEL = "ndots:";
|
|
||||||
static final int DEFAULT_NDOTS = 1;
|
|
||||||
private final DnsServerAddresses defaultNameServerAddresses;
|
private final DnsServerAddresses defaultNameServerAddresses;
|
||||||
private final Map<String, DnsServerAddresses> domainToNameServerStreamMap;
|
private final Map<String, DnsServerAddresses> domainToNameServerStreamMap;
|
||||||
private static final Pattern SEARCH_DOMAIN_PATTERN = Pattern.compile("\\s+");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to parse {@code /etc/resolv.conf} and files in the {@code /etc/resolver} directory by default.
|
* Attempt to parse {@code /etc/resolv.conf} and files in the {@code /etc/resolver} directory by default.
|
||||||
@ -154,6 +157,7 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ
|
|||||||
private static Map<String, DnsServerAddresses> parse(File... etcResolverFiles) throws IOException {
|
private static Map<String, DnsServerAddresses> parse(File... etcResolverFiles) throws IOException {
|
||||||
Map<String, DnsServerAddresses> domainToNameServerStreamMap =
|
Map<String, DnsServerAddresses> domainToNameServerStreamMap =
|
||||||
new HashMap<String, DnsServerAddresses>(etcResolverFiles.length << 1);
|
new HashMap<String, DnsServerAddresses>(etcResolverFiles.length << 1);
|
||||||
|
boolean rotateGlobal = RES_OPTIONS != null && RES_OPTIONS.contains(OPTIONS_ROTATE_FLAG);
|
||||||
for (File etcResolverFile : etcResolverFiles) {
|
for (File etcResolverFile : etcResolverFiles) {
|
||||||
if (!etcResolverFile.isFile()) {
|
if (!etcResolverFile.isFile()) {
|
||||||
continue;
|
continue;
|
||||||
@ -164,6 +168,7 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ
|
|||||||
br = new BufferedReader(fr);
|
br = new BufferedReader(fr);
|
||||||
List<InetSocketAddress> addresses = new ArrayList<InetSocketAddress>(2);
|
List<InetSocketAddress> addresses = new ArrayList<InetSocketAddress>(2);
|
||||||
String domainName = etcResolverFile.getName();
|
String domainName = etcResolverFile.getName();
|
||||||
|
boolean rotate = rotateGlobal;
|
||||||
int port = DNS_PORT;
|
int port = DNS_PORT;
|
||||||
String line;
|
String line;
|
||||||
while ((line = br.readLine()) != null) {
|
while ((line = br.readLine()) != null) {
|
||||||
@ -173,7 +178,9 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ
|
|||||||
if (line.isEmpty() || (c = line.charAt(0)) == '#' || c == ';') {
|
if (line.isEmpty() || (c = line.charAt(0)) == '#' || c == ';') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (line.startsWith(NAMESERVER_ROW_LABEL)) {
|
if (!rotate && line.startsWith(OPTIONS_ROW_LABEL)) {
|
||||||
|
rotate = line.contains(OPTIONS_ROTATE_FLAG);
|
||||||
|
} else if (line.startsWith(NAMESERVER_ROW_LABEL)) {
|
||||||
int i = indexOfNonWhiteSpace(line, NAMESERVER_ROW_LABEL.length());
|
int i = indexOfNonWhiteSpace(line, NAMESERVER_ROW_LABEL.length());
|
||||||
if (i < 0) {
|
if (i < 0) {
|
||||||
throw new IllegalArgumentException("error parsing label " + NAMESERVER_ROW_LABEL +
|
throw new IllegalArgumentException("error parsing label " + NAMESERVER_ROW_LABEL +
|
||||||
@ -212,7 +219,7 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ
|
|||||||
}
|
}
|
||||||
domainName = line.substring(i);
|
domainName = line.substring(i);
|
||||||
if (!addresses.isEmpty()) {
|
if (!addresses.isEmpty()) {
|
||||||
putIfAbsent(domainToNameServerStreamMap, domainName, addresses);
|
putIfAbsent(domainToNameServerStreamMap, domainName, addresses, rotate);
|
||||||
}
|
}
|
||||||
addresses = new ArrayList<InetSocketAddress>(2);
|
addresses = new ArrayList<InetSocketAddress>(2);
|
||||||
} else if (line.startsWith(PORT_ROW_LABEL)) {
|
} else if (line.startsWith(PORT_ROW_LABEL)) {
|
||||||
@ -230,7 +237,7 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!addresses.isEmpty()) {
|
if (!addresses.isEmpty()) {
|
||||||
putIfAbsent(domainToNameServerStreamMap, domainName, addresses);
|
putIfAbsent(domainToNameServerStreamMap, domainName, addresses, rotate);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (br == null) {
|
if (br == null) {
|
||||||
@ -245,9 +252,13 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ
|
|||||||
|
|
||||||
private static void putIfAbsent(Map<String, DnsServerAddresses> domainToNameServerStreamMap,
|
private static void putIfAbsent(Map<String, DnsServerAddresses> domainToNameServerStreamMap,
|
||||||
String domainName,
|
String domainName,
|
||||||
List<InetSocketAddress> addresses) {
|
List<InetSocketAddress> addresses,
|
||||||
|
boolean rotate) {
|
||||||
// TODO(scott): sortlist is being ignored.
|
// TODO(scott): sortlist is being ignored.
|
||||||
putIfAbsent(domainToNameServerStreamMap, domainName, DnsServerAddresses.sequential(addresses));
|
DnsServerAddresses addrs = rotate
|
||||||
|
? DnsServerAddresses.rotational(addresses)
|
||||||
|
: DnsServerAddresses.sequential(addresses);
|
||||||
|
putIfAbsent(domainToNameServerStreamMap, domainName, addrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void putIfAbsent(Map<String, DnsServerAddresses> domainToNameServerStreamMap,
|
private static void putIfAbsent(Map<String, DnsServerAddresses> domainToNameServerStreamMap,
|
||||||
@ -264,25 +275,25 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a file of the format <a href="https://linux.die.net/man/5/resolver">/etc/resolv.conf</a> and return the
|
* Parse <a href="https://linux.die.net/man/5/resolver">/etc/resolv.conf</a> and return options of interest, namely:
|
||||||
* value corresponding to the first ndots in an options configuration.
|
* timeout, attempts and ndots.
|
||||||
* @return the value corresponding to the first ndots in an options configuration, or {@link #DEFAULT_NDOTS} if not
|
* @return The options values provided by /etc/resolve.conf.
|
||||||
* found.
|
|
||||||
* @throws IOException If a failure occurs parsing the file.
|
* @throws IOException If a failure occurs parsing the file.
|
||||||
*/
|
*/
|
||||||
static int parseEtcResolverFirstNdots() throws IOException {
|
static UnixResolverOptions parseEtcResolverOptions() throws IOException {
|
||||||
return parseEtcResolverFirstNdots(new File(ETC_RESOLV_CONF_FILE));
|
return parseEtcResolverOptions(new File(ETC_RESOLV_CONF_FILE));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a file of the format <a href="https://linux.die.net/man/5/resolver">/etc/resolv.conf</a> and return the
|
* Parse a file of the format <a href="https://linux.die.net/man/5/resolver">/etc/resolv.conf</a> and return options
|
||||||
* value corresponding to the first ndots in an options configuration.
|
* of interest, namely: timeout, attempts and ndots.
|
||||||
* @param etcResolvConf a file of the format <a href="https://linux.die.net/man/5/resolver">/etc/resolv.conf</a>.
|
* @param etcResolvConf a file of the format <a href="https://linux.die.net/man/5/resolver">/etc/resolv.conf</a>.
|
||||||
* @return the value corresponding to the first ndots in an options configuration, or {@link #DEFAULT_NDOTS} if not
|
* @return The options values provided by /etc/resolve.conf.
|
||||||
* found.
|
|
||||||
* @throws IOException If a failure occurs parsing the file.
|
* @throws IOException If a failure occurs parsing the file.
|
||||||
*/
|
*/
|
||||||
static int parseEtcResolverFirstNdots(File etcResolvConf) throws IOException {
|
static UnixResolverOptions parseEtcResolverOptions(File etcResolvConf) throws IOException {
|
||||||
|
UnixResolverOptions.Builder optionsBuilder = UnixResolverOptions.newBuilder();
|
||||||
|
|
||||||
FileReader fr = new FileReader(etcResolvConf);
|
FileReader fr = new FileReader(etcResolvConf);
|
||||||
BufferedReader br = null;
|
BufferedReader br = null;
|
||||||
try {
|
try {
|
||||||
@ -290,12 +301,7 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ
|
|||||||
String line;
|
String line;
|
||||||
while ((line = br.readLine()) != null) {
|
while ((line = br.readLine()) != null) {
|
||||||
if (line.startsWith(OPTIONS_ROW_LABEL)) {
|
if (line.startsWith(OPTIONS_ROW_LABEL)) {
|
||||||
int i = line.indexOf(NDOTS_LABEL);
|
parseResOptions(line.substring(OPTIONS_ROW_LABEL.length()), optionsBuilder);
|
||||||
if (i >= 0) {
|
|
||||||
i += NDOTS_LABEL.length();
|
|
||||||
final int j = line.indexOf(' ', i);
|
|
||||||
return Integer.parseInt(line.substring(i, j < 0 ? line.length() : j));
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -306,7 +312,35 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ
|
|||||||
br.close();
|
br.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return DEFAULT_NDOTS;
|
|
||||||
|
// amend options
|
||||||
|
if (RES_OPTIONS != null) {
|
||||||
|
parseResOptions(RES_OPTIONS, optionsBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
return optionsBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void parseResOptions(String line, UnixResolverOptions.Builder builder) {
|
||||||
|
String[] opts = WHITESPACE_PATTERN.split(line);
|
||||||
|
for (String opt : opts) {
|
||||||
|
try {
|
||||||
|
if (opt.startsWith("ndots:")) {
|
||||||
|
builder.setNdots(parseResIntOption(opt, "ndots:"));
|
||||||
|
} else if (opt.startsWith("attempts:")) {
|
||||||
|
builder.setAttempts(parseResIntOption(opt, "attempts:"));
|
||||||
|
} else if (opt.startsWith("timeout:")) {
|
||||||
|
builder.setTimeout(parseResIntOption(opt, "timeout:"));
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException ignore) {
|
||||||
|
// skip bad int values from resolv.conf to keep value already set in UnixResolverOptions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int parseResIntOption(String opt, String fullLabel) {
|
||||||
|
String optValue = opt.substring(fullLabel.length());
|
||||||
|
return Integer.parseInt(optValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -346,7 +380,7 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ
|
|||||||
if (i >= 0) {
|
if (i >= 0) {
|
||||||
// May contain more then one entry, either seperated by whitespace or tab.
|
// May contain more then one entry, either seperated by whitespace or tab.
|
||||||
// See https://linux.die.net/man/5/resolver
|
// See https://linux.die.net/man/5/resolver
|
||||||
String[] domains = SEARCH_DOMAIN_PATTERN.split(line.substring(i));
|
String[] domains = WHITESPACE_PATTERN.split(line.substring(i));
|
||||||
Collections.addAll(searchDomains, domains);
|
Collections.addAll(searchDomains, domains);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -364,4 +398,5 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ
|
|||||||
? Collections.singletonList(localDomain)
|
? Collections.singletonList(localDomain)
|
||||||
: searchDomains;
|
: searchDomains;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents options defined in a file of the format <a href=https://linux.die.net/man/5/resolver>etc/resolv.conf</a>.
|
||||||
|
*/
|
||||||
|
final class UnixResolverOptions {
|
||||||
|
|
||||||
|
private final int ndots;
|
||||||
|
private final int timeout;
|
||||||
|
private final int attempts;
|
||||||
|
|
||||||
|
UnixResolverOptions(int ndots, int timeout, int attempts) {
|
||||||
|
this.ndots = ndots;
|
||||||
|
this.timeout = timeout;
|
||||||
|
this.attempts = attempts;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UnixResolverOptions.Builder newBuilder() {
|
||||||
|
return new UnixResolverOptions.Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of dots which must appear in a name before an initial absolute query is made.
|
||||||
|
* The default value is {@code 1}.
|
||||||
|
*/
|
||||||
|
int ndots() {
|
||||||
|
return ndots;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The timeout of each DNS query performed by this resolver (in seconds).
|
||||||
|
* The default value is {@code 5}.
|
||||||
|
*/
|
||||||
|
int timeout() {
|
||||||
|
return timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum allowed number of DNS queries to send when resolving a host name.
|
||||||
|
* The default value is {@code 16}.
|
||||||
|
*/
|
||||||
|
int attempts() {
|
||||||
|
return attempts;
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class Builder {
|
||||||
|
|
||||||
|
private int ndots = 1;
|
||||||
|
private int timeout = 5;
|
||||||
|
private int attempts = 16;
|
||||||
|
|
||||||
|
private Builder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void setNdots(int ndots) {
|
||||||
|
this.ndots = ndots;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTimeout(int timeout) {
|
||||||
|
this.timeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAttempts(int attempts) {
|
||||||
|
this.attempts = attempts;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnixResolverOptions build() {
|
||||||
|
return new UnixResolverOptions(ndots, timeout, attempts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -29,8 +29,7 @@ import java.util.Arrays;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider.DEFAULT_NDOTS;
|
import static io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider.parseEtcResolverOptions;
|
||||||
import static io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider.parseEtcResolverFirstNdots;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
public class UnixResolverDnsServerAddressStreamProviderTest {
|
public class UnixResolverDnsServerAddressStreamProviderTest {
|
||||||
@ -50,6 +49,62 @@ public class UnixResolverDnsServerAddressStreamProviderTest {
|
|||||||
assertHostNameEquals("127.0.0.3", stream.next());
|
assertHostNameEquals("127.0.0.3", stream.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nameServerAddressStreamShouldBeRotationalWhenRotationOptionsIsPresent() throws Exception {
|
||||||
|
File f = buildFile("options rotate\n" +
|
||||||
|
"domain linecorp.local\n" +
|
||||||
|
"nameserver 127.0.0.2\n" +
|
||||||
|
"nameserver 127.0.0.3\n" +
|
||||||
|
"nameserver 127.0.0.4\n");
|
||||||
|
UnixResolverDnsServerAddressStreamProvider p =
|
||||||
|
new UnixResolverDnsServerAddressStreamProvider(f, null);
|
||||||
|
|
||||||
|
DnsServerAddressStream stream = p.nameServerAddressStream("");
|
||||||
|
assertHostNameEquals("127.0.0.2", stream.next());
|
||||||
|
assertHostNameEquals("127.0.0.3", stream.next());
|
||||||
|
assertHostNameEquals("127.0.0.4", stream.next());
|
||||||
|
|
||||||
|
stream = p.nameServerAddressStream("");
|
||||||
|
assertHostNameEquals("127.0.0.3", stream.next());
|
||||||
|
assertHostNameEquals("127.0.0.4", stream.next());
|
||||||
|
assertHostNameEquals("127.0.0.2", stream.next());
|
||||||
|
|
||||||
|
stream = p.nameServerAddressStream("");
|
||||||
|
assertHostNameEquals("127.0.0.4", stream.next());
|
||||||
|
assertHostNameEquals("127.0.0.2", stream.next());
|
||||||
|
assertHostNameEquals("127.0.0.3", stream.next());
|
||||||
|
|
||||||
|
stream = p.nameServerAddressStream("");
|
||||||
|
assertHostNameEquals("127.0.0.2", stream.next());
|
||||||
|
assertHostNameEquals("127.0.0.3", stream.next());
|
||||||
|
assertHostNameEquals("127.0.0.4", stream.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nameServerAddressStreamShouldAlwaysStartFromTheTopWhenRotationOptionsIsAbsent() throws Exception {
|
||||||
|
File f = buildFile("domain linecorp.local\n" +
|
||||||
|
"nameserver 127.0.0.2\n" +
|
||||||
|
"nameserver 127.0.0.3\n" +
|
||||||
|
"nameserver 127.0.0.4\n");
|
||||||
|
UnixResolverDnsServerAddressStreamProvider p =
|
||||||
|
new UnixResolverDnsServerAddressStreamProvider(f, null);
|
||||||
|
|
||||||
|
DnsServerAddressStream stream = p.nameServerAddressStream("");
|
||||||
|
assertHostNameEquals("127.0.0.2", stream.next());
|
||||||
|
assertHostNameEquals("127.0.0.3", stream.next());
|
||||||
|
assertHostNameEquals("127.0.0.4", stream.next());
|
||||||
|
|
||||||
|
stream = p.nameServerAddressStream("");
|
||||||
|
assertHostNameEquals("127.0.0.2", stream.next());
|
||||||
|
assertHostNameEquals("127.0.0.3", stream.next());
|
||||||
|
assertHostNameEquals("127.0.0.4", stream.next());
|
||||||
|
|
||||||
|
stream = p.nameServerAddressStream("");
|
||||||
|
assertHostNameEquals("127.0.0.2", stream.next());
|
||||||
|
assertHostNameEquals("127.0.0.3", stream.next());
|
||||||
|
assertHostNameEquals("127.0.0.4", stream.next());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void defaultReturnedWhenNoBetterMatch() throws Exception {
|
public void defaultReturnedWhenNoBetterMatch() throws Exception {
|
||||||
File f = buildFile("domain linecorp.local\n" +
|
File f = buildFile("domain linecorp.local\n" +
|
||||||
@ -83,23 +138,63 @@ public class UnixResolverDnsServerAddressStreamProviderTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void ndotsIsParsedIfPresent() throws IOException {
|
public void ndotsOptionIsParsedIfPresent() throws IOException {
|
||||||
File f = buildFile("search localdomain\n" +
|
File f = buildFile("search localdomain\n" +
|
||||||
"nameserver 127.0.0.11\n" +
|
"nameserver 127.0.0.11\n" +
|
||||||
"options ndots:0\n");
|
"options ndots:0\n");
|
||||||
assertEquals(0, parseEtcResolverFirstNdots(f));
|
assertEquals(0, parseEtcResolverOptions(f).ndots());
|
||||||
|
|
||||||
f = buildFile("search localdomain\n" +
|
f = buildFile("search localdomain\n" +
|
||||||
"nameserver 127.0.0.11\n" +
|
"nameserver 127.0.0.11\n" +
|
||||||
"options ndots:123 foo:goo\n");
|
"options ndots:123 foo:goo\n");
|
||||||
assertEquals(123, parseEtcResolverFirstNdots(f));
|
assertEquals(123, parseEtcResolverOptions(f).ndots());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void defaultValueReturnedIfNdotsNotPresent() throws IOException {
|
public void defaultValueReturnedIfNdotsOptionsNotPresent() throws IOException {
|
||||||
File f = buildFile("search localdomain\n" +
|
File f = buildFile("search localdomain\n" +
|
||||||
"nameserver 127.0.0.11\n");
|
"nameserver 127.0.0.11\n");
|
||||||
assertEquals(DEFAULT_NDOTS, parseEtcResolverFirstNdots(f));
|
assertEquals(1, parseEtcResolverOptions(f).ndots());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void timeoutOptionIsParsedIfPresent() throws IOException {
|
||||||
|
File f = buildFile("search localdomain\n" +
|
||||||
|
"nameserver 127.0.0.11\n" +
|
||||||
|
"options timeout:0\n");
|
||||||
|
assertEquals(0, parseEtcResolverOptions(f).timeout());
|
||||||
|
|
||||||
|
f = buildFile("search localdomain\n" +
|
||||||
|
"nameserver 127.0.0.11\n" +
|
||||||
|
"options foo:bar timeout:124\n");
|
||||||
|
assertEquals(124, parseEtcResolverOptions(f).timeout());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void defaultValueReturnedIfTimeoutOptionsIsNotPresent() throws IOException {
|
||||||
|
File f = buildFile("search localdomain\n" +
|
||||||
|
"nameserver 127.0.0.11\n");
|
||||||
|
assertEquals(5, parseEtcResolverOptions(f).timeout());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void attemptsOptionIsParsedIfPresent() throws IOException {
|
||||||
|
File f = buildFile("search localdomain\n" +
|
||||||
|
"nameserver 127.0.0.11\n" +
|
||||||
|
"options attempts:0\n");
|
||||||
|
assertEquals(0, parseEtcResolverOptions(f).attempts());
|
||||||
|
|
||||||
|
f = buildFile("search localdomain\n" +
|
||||||
|
"nameserver 127.0.0.11\n" +
|
||||||
|
"options foo:bar attempts:12\n");
|
||||||
|
assertEquals(12, parseEtcResolverOptions(f).attempts());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void defaultValueReturnedIfAttemptsOptionsIsNotPresent() throws IOException {
|
||||||
|
File f = buildFile("search localdomain\n" +
|
||||||
|
"nameserver 127.0.0.11\n");
|
||||||
|
assertEquals(16, parseEtcResolverOptions(f).attempts());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Loading…
Reference in New Issue
Block a user