Fix DN resolution when ndots is greater than 1

Motivation:

DN resolution does not fall back to the "original name" lookup after search list is checked. This results in a failure to resolve any name (outside of search list) that has number of dots less than resolv.conf's ndots value (which, for example, is often the case in the context of Kubernetes where kubelet passes on resolv.conf containing "options ndots:5").

It also does not go through the search list in a situation described in resolv.conf man:
"The default for n[dots] is 1, meaning that if there are any dots in a name, the name will be tried first as an absolute name before any search list elements are appended to it."

Modifications:

DnsNameResolverContext::resolve was updated to match Go's https://github.com/golang/go/blob/release-branch.go1.9/src/net/dnsclient_unix.go#L338 logic.

Result:
DnsNameResolverContext::resolve will now try to resolve "original name" if search list yields no results when number of dots in the original name is less than resolv.conf's ndots value. It will also go through the search list in case "origin name" resolution fails and number of dots is equal or larger than resolv.conf's ndots value.
This commit is contained in:
Stanley Shyiko 2017-11-16 23:15:30 -08:00 committed by Norman Maurer
parent a8bb9dc180
commit 844d804aba
2 changed files with 37 additions and 26 deletions

View File

@ -125,33 +125,43 @@ abstract class DnsNameResolverContext<T> {
} }
void resolve(final Promise<T> promise) { void resolve(final Promise<T> promise) {
if (parent.searchDomains().length == 0 || parent.ndots() == 0 || StringUtil.endsWith(hostname, '.')) { final String[] searchDomains = parent.searchDomains();
if (searchDomains.length == 0 || parent.ndots() == 0 || StringUtil.endsWith(hostname, '.')) {
internalResolve(promise); internalResolve(promise);
} else { } else {
int dots = 0; final boolean startWithoutSearchDomain = hasNDots();
for (int idx = hostname.length() - 1; idx >= 0; idx--) { final String initialHostname = startWithoutSearchDomain ? hostname : hostname + '.' + searchDomains[0];
if (hostname.charAt(idx) == '.' && ++dots >= parent.ndots()) { final int initialSearchDomainIdx = startWithoutSearchDomain ? 0 : 1;
internalResolve(promise);
return;
}
}
doSearchDomainQuery(0, new FutureListener<T>() { doSearchDomainQuery(initialHostname, new FutureListener<T>() {
private int count = 1; private int searchDomainIdx = initialSearchDomainIdx;
@Override @Override
public void operationComplete(Future<T> future) throws Exception { public void operationComplete(Future<T> future) throws Exception {
if (future.isSuccess()) { if (future.isSuccess()) {
promise.trySuccess(future.getNow()); promise.trySuccess(future.getNow());
} else if (count < parent.searchDomains().length) { } else if (searchDomainIdx < searchDomains.length) {
doSearchDomainQuery(count++, this); doSearchDomainQuery(hostname + '.' + searchDomains[searchDomainIdx++], this);
} else { } else {
promise.tryFailure(new SearchDomainUnknownHostException(future.cause(), hostname)); if (!startWithoutSearchDomain) {
internalResolve(promise);
} else {
promise.tryFailure(new SearchDomainUnknownHostException(future.cause(), hostname));
}
} }
} }
}); });
} }
} }
private boolean hasNDots() {
for (int idx = hostname.length() - 1, dots = 0; idx >= 0; idx--) {
if (hostname.charAt(idx) == '.' && ++dots >= parent.ndots()) {
return true;
}
}
return false;
}
private static final class SearchDomainUnknownHostException extends UnknownHostException { private static final class SearchDomainUnknownHostException extends UnknownHostException {
private static final long serialVersionUID = -8573510133644997085L; private static final long serialVersionUID = -8573510133644997085L;
@ -166,12 +176,9 @@ abstract class DnsNameResolverContext<T> {
} }
} }
private void doSearchDomainQuery(int count, FutureListener<T> listener) { private void doSearchDomainQuery(String hostname, FutureListener<T> listener) {
DnsNameResolverContext<T> nextContext = newResolverContext(parent, DnsNameResolverContext<T> nextContext = newResolverContext(parent, hostname, additionals, resolveCache,
hostname + '.' + parent.searchDomains()[count], nameServerAddrs);
additionals,
resolveCache,
nameServerAddrs);
Promise<T> nextPromise = parent.executor().newPromise(); Promise<T> nextPromise = parent.executor().newPromise();
nextContext.internalResolve(nextPromise); nextContext.internalResolve(nextPromise);
nextPromise.addListener(listener); nextPromise.addListener(listener);

View File

@ -104,8 +104,10 @@ public class SearchDomainTest {
// "host2" not resolved // "host2" not resolved
assertNotResolve(resolver, "host2"); assertNotResolve(resolver, "host2");
// "host3" does not contain a dot or is not absolute // "host3" does not contain a dot nor it's absolute but it should still be resolved after search list have
assertNotResolve(resolver, "host3"); // been checked
resolved = assertResolve(resolver, "host3");
assertEquals(store.getAddress("host3"), resolved);
// "host3." does not contain a dot but is absolute // "host3." does not contain a dot but is absolute
resolved = assertResolve(resolver, "host3."); resolved = assertResolve(resolver, "host3.");
@ -152,8 +154,10 @@ public class SearchDomainTest {
// "host2" not resolved // "host2" not resolved
assertNotResolveAll(resolver, "host2"); assertNotResolveAll(resolver, "host2");
// "host3" does not contain a dot or is not absolute // "host3" does not contain a dot nor it's absolute but it should still be resolved after search list have
assertNotResolveAll(resolver, "host3"); // been checked
resolved = assertResolveAll(resolver, "host3");
assertEquals(store.getAddresses("host3"), resolved);
// "host3." does not contain a dot but is absolute // "host3." does not contain a dot but is absolute
resolved = assertResolveAll(resolver, "host3."); resolved = assertResolveAll(resolver, "host3.");
@ -281,7 +285,7 @@ public class SearchDomainTest {
dnsServer = new TestDnsServer(store); dnsServer = new TestDnsServer(store);
dnsServer.start(); dnsServer.start();
resolver = newResolver().searchDomains(Collections.singletonList("foo.com")).ndots(2).build(); resolver = newResolver().searchDomains(Collections.singletonList("foo.com")).ndots(1).build();
Future<InetAddress> fut = resolver.resolve("unknown.hostname"); Future<InetAddress> fut = resolver.resolve("unknown.hostname");
assertTrue(fut.await(10, TimeUnit.SECONDS)); assertTrue(fut.await(10, TimeUnit.SECONDS));
@ -293,12 +297,12 @@ public class SearchDomainTest {
} }
@Test @Test
public void testExceptionMsgDoesNotContainSearchDomainIfNdotsNotHighEnough() throws Exception { public void testExceptionMsgDoesNotContainSearchDomainIfNdotsIsNotReached() throws Exception {
TestDnsServer.MapRecordStoreA store = new TestDnsServer.MapRecordStoreA(Collections.<String>emptySet()); TestDnsServer.MapRecordStoreA store = new TestDnsServer.MapRecordStoreA(Collections.<String>emptySet());
dnsServer = new TestDnsServer(store); dnsServer = new TestDnsServer(store);
dnsServer.start(); dnsServer.start();
resolver = newResolver().searchDomains(Collections.singletonList("foo.com")).ndots(1).build(); resolver = newResolver().searchDomains(Collections.singletonList("foo.com")).ndots(2).build();
Future<InetAddress> fut = resolver.resolve("unknown.hostname"); Future<InetAddress> fut = resolver.resolve("unknown.hostname");
assertTrue(fut.await(10, TimeUnit.SECONDS)); assertTrue(fut.await(10, TimeUnit.SECONDS));