DnsNameResolverContext#followCname only uses first name server
Motivation: When following a CNAME it is possible there are multiple name servers to query against. However DnsNameResolverContext#followCname explicitly only uses the first name server address when attempting the query. This may lead to resolution failures because we didn't try all the available name servers. Modifications: DnsNameResolverContext#followCname should not just try the first name server, but it should try all name servers Result: More complete CNAME resolution.
This commit is contained in:
parent
95b9b0af5c
commit
fe5c69bdd9
@ -755,7 +755,7 @@ abstract class DnsNameResolverContext<T> {
|
|||||||
|
|
||||||
private void followCname(String cname, final DnsQueryLifecycleObserver queryLifecycleObserver, Promise<T> promise) {
|
private void followCname(String cname, final DnsQueryLifecycleObserver queryLifecycleObserver, Promise<T> promise) {
|
||||||
// Use the same server for both CNAME queries
|
// Use the same server for both CNAME queries
|
||||||
DnsServerAddressStream stream = DnsServerAddresses.singleton(getNameServers(cname).next()).stream();
|
DnsServerAddressStream stream = getNameServers(cname);
|
||||||
|
|
||||||
DnsQuestion cnameQuestion = null;
|
DnsQuestion cnameQuestion = null;
|
||||||
if (parent.supportsARecords()) {
|
if (parent.supportsARecords()) {
|
||||||
|
@ -49,6 +49,7 @@ import org.apache.directory.server.dns.messages.RecordClass;
|
|||||||
import org.apache.directory.server.dns.messages.RecordType;
|
import org.apache.directory.server.dns.messages.RecordType;
|
||||||
import org.apache.directory.server.dns.messages.ResourceRecord;
|
import org.apache.directory.server.dns.messages.ResourceRecord;
|
||||||
import org.apache.directory.server.dns.messages.ResourceRecordModifier;
|
import org.apache.directory.server.dns.messages.ResourceRecordModifier;
|
||||||
|
import org.apache.directory.server.dns.messages.ResponseCode;
|
||||||
import org.apache.directory.server.dns.store.DnsAttribute;
|
import org.apache.directory.server.dns.store.DnsAttribute;
|
||||||
import org.apache.directory.server.dns.store.RecordStore;
|
import org.apache.directory.server.dns.store.RecordStore;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
@ -73,6 +74,7 @@ import java.util.Queue;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import static io.netty.handler.codec.dns.DnsRecordType.A;
|
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.AAAA;
|
||||||
@ -817,6 +819,108 @@ public class DnsNameResolverTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCNAMERecursiveResolveMultipleNameServersIPv4() throws IOException {
|
||||||
|
testCNAMERecursiveResolveMultipleNameServers(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCNAMERecursiveResolveMultipleNameServersIPv6() throws IOException {
|
||||||
|
testCNAMERecursiveResolveMultipleNameServers(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testCNAMERecursiveResolveMultipleNameServers(boolean ipv4Preferred) throws IOException {
|
||||||
|
final String firstName = "firstname.nettyfoo.com";
|
||||||
|
final String lastName = "lastname.nettybar.com";
|
||||||
|
final String ipv4Addr = "1.2.3.4";
|
||||||
|
final String ipv6Addr = "::1";
|
||||||
|
final AtomicBoolean hitServer2 = new AtomicBoolean();
|
||||||
|
final TestDnsServer dnsServer2 = new TestDnsServer(new RecordStore() {
|
||||||
|
@Override
|
||||||
|
public Set<ResourceRecord> getRecords(QuestionRecord question) throws DnsException {
|
||||||
|
hitServer2.set(true);
|
||||||
|
if (question.getDomainName().equals(firstName)) {
|
||||||
|
ResourceRecordModifier rm = new ResourceRecordModifier();
|
||||||
|
rm.setDnsClass(RecordClass.IN);
|
||||||
|
rm.setDnsName(question.getDomainName());
|
||||||
|
rm.setDnsTtl(100);
|
||||||
|
rm.setDnsType(RecordType.CNAME);
|
||||||
|
rm.put(DnsAttribute.DOMAIN_NAME, lastName);
|
||||||
|
return Collections.singleton(rm.getEntry());
|
||||||
|
} else {
|
||||||
|
throw new DnsException(ResponseCode.REFUSED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
final TestDnsServer dnsServer3 = new TestDnsServer(new RecordStore() {
|
||||||
|
@Override
|
||||||
|
public Set<ResourceRecord> getRecords(QuestionRecord question) throws DnsException {
|
||||||
|
if (question.getDomainName().equals(lastName)) {
|
||||||
|
ResourceRecordModifier rm = new ResourceRecordModifier();
|
||||||
|
rm.setDnsClass(RecordClass.IN);
|
||||||
|
rm.setDnsName(question.getDomainName());
|
||||||
|
rm.setDnsTtl(100);
|
||||||
|
rm.setDnsType(question.getRecordType());
|
||||||
|
switch (question.getRecordType()) {
|
||||||
|
case A:
|
||||||
|
rm.put(DnsAttribute.IP_ADDRESS, ipv4Addr);
|
||||||
|
break;
|
||||||
|
case AAAA:
|
||||||
|
rm.put(DnsAttribute.IP_ADDRESS, ipv6Addr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.singleton(rm.getEntry());
|
||||||
|
} else {
|
||||||
|
throw new DnsException(ResponseCode.REFUSED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dnsServer2.start();
|
||||||
|
dnsServer3.start();
|
||||||
|
DnsNameResolver resolver = null;
|
||||||
|
try {
|
||||||
|
DnsCache nsCache = new DefaultDnsCache();
|
||||||
|
// What we want to test is the following:
|
||||||
|
// 1. Do a DNS query.
|
||||||
|
// 2. CNAME is returned, we want to lookup that CNAME on multiple DNS servers
|
||||||
|
// 3. The first DNS server should fail
|
||||||
|
// 4. The second DNS server should succeed
|
||||||
|
// This verifies that we do in fact follow multiple DNS servers in the CNAME resolution.
|
||||||
|
// The DnsCache is used for the name server cache, but doesn't provide a InetSocketAddress (only InetAddress
|
||||||
|
// so no port), so we only specify the name server in the cache, and then specify both name servers in the
|
||||||
|
// fallback name server provider.
|
||||||
|
nsCache.cache("nettyfoo.com.", null, dnsServer2.localAddress().getAddress(), 10000, group.next());
|
||||||
|
resolver = new DnsNameResolver(
|
||||||
|
group.next(), new ReflectiveChannelFactory<DatagramChannel>(NioDatagramChannel.class),
|
||||||
|
NoopDnsCache.INSTANCE, nsCache, NoopDnsQueryLifecycleObserverFactory.INSTANCE, 3000,
|
||||||
|
ipv4Preferred ? ResolvedAddressTypes.IPV4_ONLY : ResolvedAddressTypes.IPV6_ONLY, true,
|
||||||
|
10, true, 4096, false, HostsFileEntriesResolver.DEFAULT,
|
||||||
|
new SequentialDnsServerAddressStreamProvider(dnsServer2.localAddress(), dnsServer3.localAddress()),
|
||||||
|
DnsNameResolver.DEFAULT_SEARCH_DOMAINS, 0, true) {
|
||||||
|
@Override
|
||||||
|
int dnsRedirectPort(InetAddress server) {
|
||||||
|
return hitServer2.get() ? dnsServer3.localAddress().getPort() : dnsServer2.localAddress().getPort();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
InetAddress resolvedAddress = resolver.resolve(firstName).syncUninterruptibly().getNow();
|
||||||
|
if (ipv4Preferred) {
|
||||||
|
assertEquals(ipv4Addr, resolvedAddress.getHostAddress());
|
||||||
|
} else {
|
||||||
|
assertEquals(ipv6Addr, NetUtil.toAddressString(resolvedAddress));
|
||||||
|
}
|
||||||
|
assertEquals(firstName, resolvedAddress.getHostName());
|
||||||
|
} finally {
|
||||||
|
dnsServer2.stop();
|
||||||
|
dnsServer3.stop();
|
||||||
|
if (resolver != null) {
|
||||||
|
resolver.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResolveAllNullIpv4() {
|
public void testResolveAllNullIpv4() {
|
||||||
testResolveAll0(ResolvedAddressTypes.IPV4_ONLY, NetUtil.LOCALHOST4, null);
|
testResolveAll0(ResolvedAddressTypes.IPV4_ONLY, NetUtil.LOCALHOST4, null);
|
||||||
|
Loading…
Reference in New Issue
Block a user