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:
Fabien Renaud 2020-04-28 00:28:05 -07:00 committed by GitHub
parent 5fa5ce34e1
commit 4f72cdf233
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 271 additions and 53 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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;
} }
} }

View File

@ -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);
}
}
}

View File

@ -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