[1/2] resolv: change search domain addition logic

Submitted by Mathias Kresin on Oct. 18, 2014, 3:16 p.m.

Details

Message ID 1413645366-28119-1-git-send-email-openwrt@kresin.me
State New
Headers show

Commit Message

Mathias Kresin Oct. 18, 2014, 3:16 p.m.
RFC1034 3.1 suggests that

  a character string that represents the starting labels of a
  domain name which is incomplete, and should be completed by
  local software using knowledge of the local domain (often
  called "relative").

Following this advice, the patch eliminates an incompatibility with
dnsmasq > 2.57. dnsmasq always returns NOERROR when it receives a hostname for
resolving, because it is handled like a TLD by dnsmasq. The dnsmasq authors
explains it as the following:

  Consider doing a query for "com". If dnsmasq returns NXDOMAIN then
  whatever gets that answer can assume the nothing that ends on ".com"
  exists, which is clearly wrong, and breaks a lot of stuff. Simple
  resolvers won't break, but anything more complex will, so reverting the
  behaviour is no possible.

To be compliant with RFC1034 on one side and still fast on the other side, its
implemented using the following logic:

a) The supplied name is a fqdn (ends with a dot):
    1. send the name
b) The supplied name contains (but doesn't end with) a dot:
    1. send the name, as it could be a fqdn with a missing trailing dot
    2. if the name couldn't be found, append the search domains and send
c) The supplied name doesn't contain any dots:
    1. append the search domains and send
    2. if the name couldn't be found, send the original name

Signed-off-by: Mathias Kresin <openwrt@kresin.me>
---
 libc/inet/resolv.c | 38 +++++++++++++++++++++++++++-----------
 1 file changed, 27 insertions(+), 11 deletions(-)

Patch hide | download patch | download mbox

diff --git a/libc/inet/resolv.c b/libc/inet/resolv.c
index cfc1eee..3d3a540 100644
--- a/libc/inet/resolv.c
+++ b/libc/inet/resolv.c
@@ -1258,7 +1258,8 @@  int __dns_lookup(const char *name,
 	int local_ns_num = -1; /* Nth server to use */
 	int local_id = local_id; /* for compiler */
 	int sdomains;
-	bool ends_with_dot;
+	bool is_fqdn;
+	bool looks_like_fqdn;
 	sockaddr46_t sa;
 
 	fd = -1;
@@ -1269,11 +1270,13 @@  int __dns_lookup(const char *name,
 	lookup = malloc(name_len + 1/*for '.'*/ + MAXLEN_searchdomain + 1);
 	if (!packet || !lookup || !name[0])
 		goto fail;
-	ends_with_dot = (name[name_len - 1] == '.');
+	is_fqdn = (name[name_len - 1] == '.');
+	looks_like_fqdn = (!is_fqdn && strchr(name, '.') != NULL) ? 1 : 0;
 	/* no strcpy! paranoia, user might change name[] under us */
 	memcpy(lookup, name, name_len);
 
 	DPRINTF("Looking up type %d answer for '%s'\n", type, name);
+	DPRINTF("is_fqdn:%d looks_like_fqdn:%d\n", is_fqdn, looks_like_fqdn);
 	retries_left = 0; /* for compiler */
 	do {
 		int pos;
@@ -1297,11 +1300,24 @@  int __dns_lookup(const char *name,
 		__open_nameservers();
 		sdomains = __searchdomains;
 		lookup[name_len] = '\0';
-		if ((unsigned)variant < sdomains) {
-			/* lookup is name_len + 1 + MAXLEN_searchdomain + 1 long */
-			/* __searchdomain[] is not bigger than MAXLEN_searchdomain */
-			lookup[name_len] = '.';
-			strcpy(&lookup[name_len + 1], __searchdomain[variant]);
+
+		/* add the first search domain if the name isn't a FQDN and doesn't look
+		 * like a one (contains a dot). If we're here again and the name looked
+		 * like a FQDN add the first search domain. If all search domains where
+		 * added, and we are here again, send the name without any searchdomains
+		 * as last effort */
+		if (!is_fqdn) {
+			if (!looks_like_fqdn || variant != -1) {
+				if (variant == -1)
+					variant = 0;
+
+				if (variant < sdomains) {
+					/* lookup is name_len + 1 + MAXLEN_searchdomain + 1 long */
+					/* __searchdomain[] is not bigger than MAXLEN_searchdomain */
+					lookup[name_len] = '.';
+					strcpy(&lookup[name_len + 1], __searchdomain[variant]);
+				}
+			}
 		}
 		/* first time? pick starting server etc */
 		if (local_ns_num < 0) {
@@ -1461,11 +1477,11 @@  int __dns_lookup(const char *name,
 		 * and retry, which is, eh, an error. :)
 		 * We were incurring long delays because of this. */
 		if (h.rcode == NXDOMAIN || h.rcode == SERVFAIL) {
-			/* if possible, try next search domain */
-			if (!ends_with_dot) {
+			/* if possible, try first/next search domain */
+			if (!is_fqdn) {
 				DPRINTF("variant:%d sdomains:%d\n", variant, sdomains);
-				if (variant < sdomains - 1) {
-					/* next search domain */
+				if (variant < sdomains - 1 || (variant == sdomains -1 && !looks_like_fqdn)) {
+					/* first/next search domain */
 					variant++;
 					continue;
 				}