DnsNameResolver.resolveAll(...) should not include duplicates (#9021)

Motivation:

DnsNameResolver#resolveAll(String) may return duplicate results in the event that the original hostname DNS response includes an IP address X and a CNAME that ends up resolving the same IP address X. This behavior is inconsistent with the JDK’s resolver and is unexpected to retrun a List with duplicate entries from a resolveAll(..) call.

Modifications:

- Filter out duplicates
- Add unit test

Result:

More consistent and less suprising behavior
This commit is contained in:
Norman Maurer 2019-04-09 09:44:23 +02:00 committed by GitHub
parent 8f7ef1cabb
commit e63c596f24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 54 additions and 1 deletions

View File

@ -695,10 +695,17 @@ abstract class DnsResolveContext<T> {
continue;
}
// We want to ensure we do not have duplicates in finalResult as this may be unexpected.
//
// While using a LinkedHashSet or HashSet may sound like the perfect fit for this we will use an
// ArrayList here as duplicates should be found quite unfrequently in the wild and we dont want to pay
// for the extra memory copy and allocations in this cases later on.
if (finalResult == null) {
finalResult = new ArrayList<T>(8);
finalResult.add(converted);
} else if (!finalResult.contains(converted)) {
finalResult.add(converted);
}
finalResult.add(converted);
cache(hostname, additionals, r, converted);
found = true;

View File

@ -2539,4 +2539,50 @@ public class DnsNameResolverTest {
}
return list;
}
@Test
public void testNotIncludeDuplicates() throws IOException {
final String name = "netty.io";
final String ipv4Addr = "1.2.3.4";
TestDnsServer dnsServer2 = new TestDnsServer(new RecordStore() {
@Override
public Set<ResourceRecord> getRecords(QuestionRecord question) {
Set<ResourceRecord> records = new LinkedHashSet<ResourceRecord>(4);
String qName = question.getDomainName().toLowerCase();
if (qName.equals(name)) {
records.add(new TestDnsServer.TestResourceRecord(
qName, RecordType.CNAME,
Collections.<String, Object>singletonMap(
DnsAttribute.DOMAIN_NAME.toLowerCase(), "cname.netty.io")));
records.add(new TestDnsServer.TestResourceRecord(qName,
RecordType.A, Collections.<String, Object>singletonMap(
DnsAttribute.IP_ADDRESS.toLowerCase(), ipv4Addr)));
} else {
records.add(new TestDnsServer.TestResourceRecord(qName,
RecordType.A, Collections.<String, Object>singletonMap(
DnsAttribute.IP_ADDRESS.toLowerCase(), ipv4Addr)));
}
return records;
}
});
dnsServer2.start();
DnsNameResolver resolver = null;
try {
DnsNameResolverBuilder builder = newResolver()
.recursionDesired(true)
.maxQueriesPerResolve(16)
.nameServerProvider(new SingletonDnsServerAddressStreamProvider(dnsServer2.localAddress()));
builder.resolvedAddressTypes(ResolvedAddressTypes.IPV4_ONLY);
resolver = builder.build();
List<InetAddress> resolvedAddresses = resolver.resolveAll(name).syncUninterruptibly().getNow();
assertEquals(Collections.singletonList(InetAddress.getByAddress(name, new byte[] { 1, 2, 3, 4 })),
resolvedAddresses);
} finally {
dnsServer2.stop();
if (resolver != null) {
resolver.close();
}
}
}
}