Don't use the cname cache when using DnsRecordResolveContext (#10808)

Motivation:

The DnsNameResolver internally follows CNAME indirects for all records types, and supports caching for CNAME resolution and A* records. For DNS record types that are not cached (e.g. SRV records) the caching of CNAME records may result in failures at incorrect times. For example if a CNAME record has a larger TTL than the entries it resolves this may result in failures which don't occur if the CNAME cache is disabled.

Modifications:

- Don't cache CNAME and also dont use the cache for CNAME when using DnsRecordResolveContext
- Add unit test

Result:

More correct resolving and also not possible to have failures due CNAME still be in the cache while the queried record experied
This commit is contained in:
Norman Maurer 2020-11-26 15:34:56 +01:00 committed by GitHub
parent 221c1a1ed7
commit 567b46fa01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 94 additions and 0 deletions

View File

@ -84,4 +84,10 @@ final class DnsRecordResolveContext extends DnsResolveContext<DnsRecord> {
// Do not cache.
// XXX: When we implement cache, we would need to retain the reference count of the result record.
}
@Override
DnsCnameCache cnameCache() {
// We don't use a cache here at all as we also don't cache if we end up using the DnsRecordResolverContext.
return NoopDnsCnameCache.INSTANCE;
}
}

View File

@ -109,6 +109,7 @@ import java.util.concurrent.atomic.AtomicReference;
import static io.netty.handler.codec.dns.DnsRecordType.A;
import static io.netty.handler.codec.dns.DnsRecordType.AAAA;
import static io.netty.handler.codec.dns.DnsRecordType.CNAME;
import static io.netty.handler.codec.dns.DnsRecordType.SRV;
import static io.netty.resolver.dns.DnsServerAddresses.sequential;
import static java.util.Collections.singletonList;
import static org.hamcrest.MatcherAssert.assertThat;
@ -3178,4 +3179,91 @@ public class DnsNameResolverTest {
}
}
}
@Test(timeout = 2000)
public void testSrvWithCnameNotCached() throws Exception {
final AtomicBoolean alias = new AtomicBoolean();
TestDnsServer dnsServer2 = new TestDnsServer(new RecordStore() {
@Override
public Set<ResourceRecord> getRecords(QuestionRecord question) {
String name = question.getDomainName();
if (name.equals("service.netty.io")) {
Set<ResourceRecord> records = new HashSet<ResourceRecord>(2);
ResourceRecordModifier rm = new ResourceRecordModifier();
rm.setDnsClass(RecordClass.IN);
rm.setDnsName(name);
rm.setDnsTtl(10);
rm.setDnsType(RecordType.CNAME);
rm.put(DnsAttribute.DOMAIN_NAME, "alias.service.netty.io");
records.add(rm.getEntry());
rm = new ResourceRecordModifier();
rm.setDnsClass(RecordClass.IN);
rm.setDnsName(name);
rm.setDnsTtl(10);
rm.setDnsType(RecordType.SRV);
rm.put(DnsAttribute.DOMAIN_NAME, "foo.service.netty.io");
rm.put(DnsAttribute.SERVICE_PORT, "8080");
rm.put(DnsAttribute.SERVICE_PRIORITY, "10");
rm.put(DnsAttribute.SERVICE_WEIGHT, "1");
records.add(rm.getEntry());
return records;
}
if (name.equals("foo.service.netty.io")) {
ResourceRecordModifier rm = new ResourceRecordModifier();
rm.setDnsClass(RecordClass.IN);
rm.setDnsName(name);
rm.setDnsTtl(10);
rm.setDnsType(RecordType.A);
rm.put(DnsAttribute.IP_ADDRESS, "10.0.0.1");
return Collections.singleton(rm.getEntry());
}
if (alias.get()) {
ResourceRecordModifier rm = new ResourceRecordModifier();
rm.setDnsClass(RecordClass.IN);
rm.setDnsName(name);
rm.setDnsTtl(10);
rm.setDnsType(RecordType.SRV);
rm.put(DnsAttribute.DOMAIN_NAME, "foo.service.netty.io");
rm.put(DnsAttribute.SERVICE_PORT, "8080");
rm.put(DnsAttribute.SERVICE_PRIORITY, "10");
rm.put(DnsAttribute.SERVICE_WEIGHT, "1");
return Collections.singleton(rm.getEntry());
}
return null;
}
});
dnsServer2.start();
DnsNameResolver resolver = null;
try {
DnsNameResolverBuilder builder = newResolver()
.recursionDesired(false)
.queryTimeoutMillis(10000)
.resolvedAddressTypes(ResolvedAddressTypes.IPV4_PREFERRED)
.completeOncePreferredResolved(true)
.maxQueriesPerResolve(16)
.nameServerProvider(new SingletonDnsServerAddressStreamProvider(dnsServer2.localAddress()));
resolver = builder.build();
assertNotEmptyAndRelease(resolver.resolveAll(new DefaultDnsQuestion("service.netty.io", SRV)));
alias.set(true);
assertNotEmptyAndRelease(resolver.resolveAll(new DefaultDnsQuestion("service.netty.io", SRV)));
alias.set(false);
assertNotEmptyAndRelease(resolver.resolveAll(new DefaultDnsQuestion("service.netty.io", SRV)));
} finally {
dnsServer2.stop();
if (resolver != null) {
resolver.close();
}
}
}
private static void assertNotEmptyAndRelease(Future<List<DnsRecord>> recordsFuture) throws Exception {
List<DnsRecord> records = recordsFuture.get();
assertFalse(records.isEmpty());
for (DnsRecord record : records) {
ReferenceCountUtil.release(record);
}
}
}