diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProvider.java b/resolver-dns/src/main/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProvider.java index d12c63749b..c22f3ae6c4 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProvider.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProvider.java @@ -27,11 +27,13 @@ import java.io.FileReader; import java.io.IOException; import java.net.InetSocketAddress; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import static io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.DNS_PORT; +import static io.netty.util.internal.ObjectUtil.checkNotNull; import static io.netty.util.internal.StringUtil.indexOfNonWhiteSpace; /** @@ -54,7 +56,7 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ * Attempt to parse {@code /etc/resolv.conf} and files in the {@code /etc/resolver} directory by default. * A failure to parse will return {@link DefaultDnsServerAddressStreamProvider}. */ - public static DnsServerAddressStreamProvider parseSilently() { + static DnsServerAddressStreamProvider parseSilently() { try { UnixResolverDnsServerAddressStreamProvider nameServerCache = new UnixResolverDnsServerAddressStreamProvider("/etc/resolv.conf", "/etc/resolver"); @@ -80,21 +82,25 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ * @throws IOException If an error occurs while parsing the input files. */ public UnixResolverDnsServerAddressStreamProvider(File etcResolvConf, File... etcResolverFiles) throws IOException { - if (etcResolvConf == null && (etcResolverFiles == null || etcResolverFiles.length == 0)) { - throw new IllegalArgumentException("no files to parse"); + if (etcResolverFiles != null && etcResolverFiles.length == 0) { + throw new IllegalArgumentException("etcResolverFiles must either be null or non-empty"); } - if (etcResolverFiles != null) { - domainToNameServerStreamMap = parse(etcResolverFiles); - if (etcResolvConf != null) { - Map etcResolvConfMap = parse(etcResolvConf); - defaultNameServerAddresses = etcResolvConfMap.remove(etcResolvConf.getName()); - domainToNameServerStreamMap.putAll(etcResolvConfMap); - } else { - defaultNameServerAddresses = null; + Map etcResolvConfMap = parse(checkNotNull(etcResolvConf, "etcResolvConf")); + domainToNameServerStreamMap = etcResolverFiles != null ? parse(etcResolverFiles) : etcResolvConfMap; + + DnsServerAddresses defaultNameServerAddresses = etcResolvConfMap.get(etcResolvConf.getName()); + if (defaultNameServerAddresses == null) { + Collection values = etcResolvConfMap.values(); + if (values.isEmpty()) { + throw new IllegalArgumentException(etcResolvConf + " didn't provide any name servers"); } + this.defaultNameServerAddresses = values.iterator().next(); } else { - domainToNameServerStreamMap = parse(etcResolvConf); - defaultNameServerAddresses = domainToNameServerStreamMap.remove(etcResolvConf.getName()); + this.defaultNameServerAddresses = defaultNameServerAddresses; + } + + if (etcResolverFiles != null) { + domainToNameServerStreamMap.putAll(etcResolvConfMap); } } @@ -113,7 +119,7 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ */ public UnixResolverDnsServerAddressStreamProvider(String etcResolvConf, String etcResolverDir) throws IOException { this(etcResolvConf == null ? null : new File(etcResolvConf), - etcResolverDir == null ? null :new File(etcResolverDir).listFiles()); + etcResolverDir == null ? null : new File(etcResolverDir).listFiles()); } @Override @@ -121,7 +127,7 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ for (;;) { int i = hostname.indexOf('.', 1); if (i < 0 || i == hostname.length() - 1) { - return defaultNameServerAddresses != null ? defaultNameServerAddresses.stream() : null; + return defaultNameServerAddresses.stream(); } DnsServerAddresses addresses = domainToNameServerStreamMap.get(hostname); @@ -133,9 +139,8 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ } } - boolean mayOverrideNameServers() { - return !domainToNameServerStreamMap.isEmpty() || - defaultNameServerAddresses != null && defaultNameServerAddresses.stream().next() != null; + private boolean mayOverrideNameServers() { + return !domainToNameServerStreamMap.isEmpty() || defaultNameServerAddresses.stream().next() != null; } private static Map parse(File... etcResolverFiles) throws IOException { @@ -176,7 +181,7 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ port = Integer.parseInt(maybeIP.substring(i + 1)); maybeIP = maybeIP.substring(0, i); } - addresses.add(new InetSocketAddress(SocketUtils.addressByName(maybeIP), port)); + addresses.add(SocketUtils.socketAddress(maybeIP, port)); } else if (line.startsWith(DOMAIN_ROW_LABEL)) { int i = indexOfNonWhiteSpace(line, DOMAIN_ROW_LABEL.length()); if (i < 0) { @@ -184,7 +189,7 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ " in file " + etcResolverFile + " value: " + line); } domainName = line.substring(i); - if (addresses != null && !addresses.isEmpty()) { + if (!addresses.isEmpty()) { putIfAbsent(domainToNameServerStreamMap, domainName, addresses); } addresses = new ArrayList(2); @@ -199,7 +204,7 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ logger.info("row type {} not supported. ignoring line: {}", SORTLIST_ROW_LABEL, line); } } - if (addresses != null && !addresses.isEmpty()) { + if (!addresses.isEmpty()) { putIfAbsent(domainToNameServerStreamMap, domainName, addresses); } } finally { @@ -217,7 +222,7 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ String domainName, List addresses) { // TODO(scott): sortlist is being ignored. - putIfAbsent(domainToNameServerStreamMap, domainName, DnsServerAddresses.shuffled(addresses)); + putIfAbsent(domainToNameServerStreamMap, domainName, DnsServerAddresses.sequential(addresses)); } private static void putIfAbsent(Map domainToNameServerStreamMap, diff --git a/resolver-dns/src/test/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProviderTest.java b/resolver-dns/src/test/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProviderTest.java new file mode 100644 index 0000000000..bcb61e78d1 --- /dev/null +++ b/resolver-dns/src/test/java/io/netty/resolver/dns/UnixResolverDnsServerAddressStreamProviderTest.java @@ -0,0 +1,94 @@ +/* + * 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.dns; + +import io.netty.util.CharsetUtil; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; + +import static org.junit.Assert.assertEquals; + +public class UnixResolverDnsServerAddressStreamProviderTest { + @Rule + public final TemporaryFolder folder = new TemporaryFolder(); + + @Test + public void defaultLookupShouldReturnResultsIfOnlySingleFileSpecified() throws Exception { + File f = buildFile("domain linecorp.local\n" + + "nameserver 127.0.0.2\n" + + "nameserver 127.0.0.3\n"); + UnixResolverDnsServerAddressStreamProvider p = + new UnixResolverDnsServerAddressStreamProvider(f, null); + + DnsServerAddressStream stream = p.nameServerAddressStream("somehost"); + assertHostNameEquals("127.0.0.2", stream.next()); + assertHostNameEquals("127.0.0.3", stream.next()); + } + + @Test + public void defaultReturnedWhenNoBetterMatch() throws Exception { + File f = buildFile("domain linecorp.local\n" + + "nameserver 127.0.0.2\n" + + "nameserver 127.0.0.3\n"); + File f2 = buildFile("domain squarecorp.local\n" + + "nameserver 127.0.0.4\n" + + "nameserver 127.0.0.5\n"); + UnixResolverDnsServerAddressStreamProvider p = + new UnixResolverDnsServerAddressStreamProvider(f, f2); + + DnsServerAddressStream stream = p.nameServerAddressStream("somehost"); + assertHostNameEquals("127.0.0.2", stream.next()); + assertHostNameEquals("127.0.0.3", stream.next()); + } + + @Test + public void moreRefinedSelectionReturnedWhenMatch() throws Exception { + File f = buildFile("domain linecorp.local\n" + + "nameserver 127.0.0.2\n" + + "nameserver 127.0.0.3\n"); + File f2 = buildFile("domain dc1.linecorp.local\n" + + "nameserver 127.0.0.4\n" + + "nameserver 127.0.0.5\n"); + UnixResolverDnsServerAddressStreamProvider p = + new UnixResolverDnsServerAddressStreamProvider(f, f2); + + DnsServerAddressStream stream = p.nameServerAddressStream("myhost.dc1.linecorp.local"); + assertHostNameEquals("127.0.0.4", stream.next()); + assertHostNameEquals("127.0.0.5", stream.next()); + } + + private File buildFile(String contents) throws IOException { + File f = folder.newFile(); + OutputStream out = new FileOutputStream(f); + try { + out.write(contents.getBytes(CharsetUtil.UTF_8)); + } finally { + out.close(); + } + return f; + } + + private static void assertHostNameEquals(String expectedHostname, InetSocketAddress next) { + assertEquals("unexpected hostname: " + next, expectedHostname, next.getHostName()); + } +}