Skip to content

Commit

Permalink
Fix DN resolution when ndots is greater than 1
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
shyiko authored and normanmaurer committed Nov 20, 2017
1 parent a8bb9dc commit 844d804
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -125,33 +125,43 @@ public void operationComplete(Future<AddressedEnvelope<DnsResponse, InetSocketAd
}

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);
} else {
int dots = 0;
for (int idx = hostname.length() - 1; idx >= 0; idx--) {
if (hostname.charAt(idx) == '.' && ++dots >= parent.ndots()) {
internalResolve(promise);
return;
}
}
final boolean startWithoutSearchDomain = hasNDots();
final String initialHostname = startWithoutSearchDomain ? hostname : hostname + '.' + searchDomains[0];
final int initialSearchDomainIdx = startWithoutSearchDomain ? 0 : 1;

doSearchDomainQuery(0, new FutureListener<T>() {
private int count = 1;
doSearchDomainQuery(initialHostname, new FutureListener<T>() {
private int searchDomainIdx = initialSearchDomainIdx;
@Override
public void operationComplete(Future<T> future) throws Exception {
if (future.isSuccess()) {
promise.trySuccess(future.getNow());
} else if (count < parent.searchDomains().length) {
doSearchDomainQuery(count++, this);
} else if (searchDomainIdx < searchDomains.length) {
doSearchDomainQuery(hostname + '.' + searchDomains[searchDomainIdx++], this);
} 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 long serialVersionUID = -8573510133644997085L;

Expand All @@ -166,12 +176,9 @@ public Throwable fillInStackTrace() {
}
}

private void doSearchDomainQuery(int count, FutureListener<T> listener) {
DnsNameResolverContext<T> nextContext = newResolverContext(parent,
hostname + '.' + parent.searchDomains()[count],
additionals,
resolveCache,
nameServerAddrs);
private void doSearchDomainQuery(String hostname, FutureListener<T> listener) {
DnsNameResolverContext<T> nextContext = newResolverContext(parent, hostname, additionals, resolveCache,
nameServerAddrs);
Promise<T> nextPromise = parent.executor().newPromise();
nextContext.internalResolve(nextPromise);
nextPromise.addListener(listener);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,10 @@ public void testResolve() throws Exception {
// "host2" not resolved
assertNotResolve(resolver, "host2");

// "host3" does not contain a dot or is not absolute
assertNotResolve(resolver, "host3");
// "host3" does not contain a dot nor it's absolute but it should still be resolved after search list have
// been checked
resolved = assertResolve(resolver, "host3");
assertEquals(store.getAddress("host3"), resolved);

// "host3." does not contain a dot but is absolute
resolved = assertResolve(resolver, "host3.");
Expand Down Expand Up @@ -152,8 +154,10 @@ public void testResolveAll() throws Exception {
// "host2" not resolved
assertNotResolveAll(resolver, "host2");

// "host3" does not contain a dot or is not absolute
assertNotResolveAll(resolver, "host3");
// "host3" does not contain a dot nor it's absolute but it should still be resolved after search list have
// been checked
resolved = assertResolveAll(resolver, "host3");
assertEquals(store.getAddresses("host3"), resolved);

// "host3." does not contain a dot but is absolute
resolved = assertResolveAll(resolver, "host3.");
Expand Down Expand Up @@ -281,7 +285,7 @@ public void testExceptionMsgContainsSearchDomain() throws Exception {
dnsServer = new TestDnsServer(store);
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");
assertTrue(fut.await(10, TimeUnit.SECONDS));
Expand All @@ -293,12 +297,12 @@ public void testExceptionMsgContainsSearchDomain() throws Exception {
}

@Test
public void testExceptionMsgDoesNotContainSearchDomainIfNdotsNotHighEnough() throws Exception {
public void testExceptionMsgDoesNotContainSearchDomainIfNdotsIsNotReached() throws Exception {
TestDnsServer.MapRecordStoreA store = new TestDnsServer.MapRecordStoreA(Collections.<String>emptySet());
dnsServer = new TestDnsServer(store);
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");
assertTrue(fut.await(10, TimeUnit.SECONDS));
Expand Down

0 comments on commit 844d804

Please sign in to comment.