8597553
commit 44500cbb25bc6e76723304b9ff39f875c04309f9
8597553
Author: Florian Weimer <fweimer@redhat.com>
8597553
Date:   Thu Apr 13 13:22:51 2017 +0200
8597553
8597553
    resolv: Remove EDNS fallback [BZ #21369]
8597553
    
8597553
    EDNS is disabled by default (so there is interoperability issue), and
8597553
    the fallback code is problematic because it prevents an application
8597553
    from obtaining DNSSEC data after a FORMERR response.
8597553
8597553
diff --git a/resolv/res_query.c b/resolv/res_query.c
8597553
index c3ebcbf6b50fcf4b..ec65bab04153c2ff 100644
8597553
--- a/resolv/res_query.c
8597553
+++ b/resolv/res_query.c
8597553
@@ -122,7 +122,6 @@ __libc_res_nquery(res_state statp,
8597553
 	HEADER *hp = (HEADER *) answer;
8597553
 	HEADER *hp2;
8597553
 	int n, use_malloc = 0;
8597553
-	u_int oflags = statp->_flags;
8597553
 
8597553
 	size_t bufsize = (type == T_QUERY_A_AND_AAAA ? 2 : 1) * QUERYSIZE;
8597553
 	u_char *buf = alloca (bufsize);
8597553
@@ -145,8 +144,7 @@ __libc_res_nquery(res_state statp,
8597553
 			     query1, bufsize);
8597553
 	    if (n > 0)
8597553
 	      {
8597553
-		if ((oflags & RES_F_EDNS0ERR) == 0
8597553
-		    && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
8597553
+		if ((statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
8597553
 		  {
8597553
 		    /* Use RESOLV_EDNS_BUFFER_SIZE because the receive
8597553
 		       buffer can be reallocated.  */
8597553
@@ -170,7 +168,6 @@ __libc_res_nquery(res_state statp,
8597553
 		n = res_nmkquery(statp, QUERY, name, class, T_AAAA, NULL, 0,
8597553
 				 NULL, query2, bufsize - nused);
8597553
 		if (n > 0
8597553
-		    && (oflags & RES_F_EDNS0ERR) == 0
8597553
 		    && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
8597553
 		  /* Use RESOLV_EDNS_BUFFER_SIZE because the receive
8597553
 		     buffer can be reallocated.  */
8597553
@@ -187,7 +184,6 @@ __libc_res_nquery(res_state statp,
8597553
 			     query1, bufsize);
8597553
 
8597553
 	    if (n > 0
8597553
-		&& (oflags & RES_F_EDNS0ERR) == 0
8597553
 		&& (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
8597553
 	      {
8597553
 		/* Use RESOLV_EDNS_BUFFER_SIZE if the receive buffer
8597553
@@ -215,16 +211,6 @@ __libc_res_nquery(res_state statp,
8597553
 		}
8597553
 	}
8597553
 	if (__glibc_unlikely (n <= 0))       {
8597553
-		/* If the query choked with EDNS0, retry without EDNS0.  */
8597553
-		if ((statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0
8597553
-		    && ((oflags ^ statp->_flags) & RES_F_EDNS0ERR) != 0) {
8597553
-			statp->_flags |= RES_F_EDNS0ERR;
8597553
-#ifdef DEBUG
8597553
-			if (statp->options & RES_DEBUG)
8597553
-				printf(";; res_nquery: retry without EDNS0\n");
8597553
-#endif
8597553
-			goto again;
8597553
-		}
8597553
 #ifdef DEBUG
8597553
 		if (statp->options & RES_DEBUG)
8597553
 			printf(";; res_query: mkquery failed\n");
8597553
diff --git a/resolv/res_send.c b/resolv/res_send.c
8597553
index 77d59dcc4e3b8d23..98968d6239d0c8f7 100644
8597553
--- a/resolv/res_send.c
8597553
+++ b/resolv/res_send.c
8597553
@@ -1321,26 +1321,6 @@ send_dg(res_state statp,
8597553
 				? *thisanssizp : *thisresplenp);
8597553
 			goto wait;
8597553
 		}
8597553
-#ifdef RES_USE_EDNS0
8597553
-		if (anhp->rcode == FORMERR
8597553
-		    && (statp->options & RES_USE_EDNS0) != 0U) {
8597553
-			/*
8597553
-			 * Do not retry if the server does not understand
8597553
-			 * EDNS0.  The case has to be captured here, as
8597553
-			 * FORMERR packet do not carry query section, hence
8597553
-			 * res_queriesmatch() returns 0.
8597553
-			 */
8597553
-			DprintQ(statp->options & RES_DEBUG,
8597553
-				(stdout,
8597553
-				 "server rejected query with EDNS0:\n"),
8597553
-				*thisansp,
8597553
-				(*thisresplenp > *thisanssizp)
8597553
-				? *thisanssizp : *thisresplenp);
8597553
-			/* record the error */
8597553
-			statp->_flags |= RES_F_EDNS0ERR;
8597553
-			return close_and_return_error (statp, resplen2);
8597553
-	}
8597553
-#endif
8597553
 		if (!(statp->options & RES_INSECURE2)
8597553
 		    && (recvresp1 || !res_queriesmatch(buf, buf + buflen,
8597553
 						       *thisansp,
8597553
diff --git a/resolv/tst-resolv-edns.c b/resolv/tst-resolv-edns.c
8597553
index f17dbc3450f52b85..093a4f5f22aaaa51 100644
8597553
--- a/resolv/tst-resolv-edns.c
8597553
+++ b/resolv/tst-resolv-edns.c
8597553
@@ -115,8 +115,23 @@ response (const struct resolv_response_context *ctx,
8597553
 {
8597553
   TEST_VERIFY_EXIT (qname != NULL);
8597553
 
8597553
-  /* The "tcp." prefix can be used to request TCP fallback.  */
8597553
   const char *qname_compare = qname;
8597553
+
8597553
+  /* The "formerr." prefix can be used to request a FORMERR response on the
8597553
+     first server.  */
8597553
+  bool send_formerr;
8597553
+  if (strncmp ("formerr.", qname, strlen ("formerr.")) == 0)
8597553
+    {
8597553
+      send_formerr = true;
8597553
+      qname_compare = qname + strlen ("formerr.");
8597553
+    }
8597553
+  else
8597553
+    {
8597553
+      send_formerr = false;
8597553
+      qname_compare = qname;
8597553
+    }
8597553
+
8597553
+  /* The "tcp." prefix can be used to request TCP fallback.  */
8597553
   bool force_tcp;
8597553
   if (strncmp ("tcp.", qname_compare, strlen ("tcp.")) == 0)
8597553
     {
8597553
@@ -132,14 +147,20 @@ response (const struct resolv_response_context *ctx,
8597553
   else
8597553
     {
8597553
       support_record_failure ();
8597553
-      printf ("error: unexpected QNAME: %s\n", qname);
8597553
+      printf ("error: unexpected QNAME: %s (reduced: %s)\n",
8597553
+              qname, qname_compare);
8597553
       return;
8597553
     }
8597553
   TEST_VERIFY_EXIT (qclass == C_IN);
8597553
-  struct resolv_response_flags flags = {.tc = force_tcp && !ctx->tcp};
8597553
+  struct resolv_response_flags flags = { };
8597553
+  flags.tc = force_tcp && !ctx->tcp;
8597553
+  if (!flags.tc && send_formerr && ctx->server_index == 0)
8597553
+    /* Send a FORMERR for the first full response from the first
8597553
+       server.  */
8597553
+    flags.rcode = 1;          /* FORMERR */
8597553
   resolv_response_init (b, flags);
8597553
   resolv_response_add_question (b, qname, qclass, qtype);
8597553
-  if (flags.tc)
8597553
+  if (flags.tc || flags.rcode != 0)
8597553
     return;
8597553
 
8597553
   if (test_verbose)
8597553
@@ -466,33 +487,42 @@ do_test (void)
8597553
   for (int do_edns = 0; do_edns < 2; ++do_edns)
8597553
     for (int do_dnssec = 0; do_dnssec < 2; ++do_dnssec)
8597553
       for (int do_tcp = 0; do_tcp < 2; ++do_tcp)
8597553
-        {
8597553
-          struct resolv_test *aux = resolv_test_start
8597553
-            ((struct resolv_redirect_config)
8597553
-             {
8597553
-               .response_callback = response,
8597553
-                 });
8597553
-
8597553
-          use_edns = do_edns;
8597553
-          if (do_edns)
8597553
-            _res.options |= RES_USE_EDNS0;
8597553
-          use_dnssec = do_dnssec;
8597553
-          if (do_dnssec)
8597553
-            _res.options |= RES_USE_DNSSEC;
8597553
-
8597553
-          char *probe_name = xstrdup (EDNS_PROBE_EXAMPLE);
8597553
-          if (do_tcp)
8597553
-            {
8597553
-              char *n = xasprintf ("tcp.%s", probe_name);
8597553
-              free (probe_name);
8597553
-              probe_name = n;
8597553
-            }
8597553
+        for (int do_formerr = 0; do_formerr < 2; ++do_formerr)
8597553
+          {
8597553
+            struct resolv_test *aux = resolv_test_start
8597553
+              ((struct resolv_redirect_config)
8597553
+               {
8597553
+                 .response_callback = response,
8597553
+               });
8597553
+
8597553
+            use_edns = do_edns;
8597553
+            if (do_edns)
8597553
+              _res.options |= RES_USE_EDNS0;
8597553
+            use_dnssec = do_dnssec;
8597553
+            if (do_dnssec)
8597553
+              _res.options |= RES_USE_DNSSEC;
8597553
+
8597553
+            char *probe_name = xstrdup (EDNS_PROBE_EXAMPLE);
8597553
+            if (do_tcp)
8597553
+              {
8597553
+                char *n = xasprintf ("tcp.%s", probe_name);
8597553
+                free (probe_name);
8597553
+                probe_name = n;
8597553
+              }
8597553
+            if (do_formerr)
8597553
+              {
8597553
+                /* Send a garbage query in an attempt to trigger EDNS
8597553
+                   fallback.  */
8597553
+                char *n = xasprintf ("formerr.%s", probe_name);
8597553
+                gethostbyname (n);
8597553
+                free (n);
8597553
+              }
8597553
 
8597553
-          run_test (probe_name);
8597553
+            run_test (probe_name);
8597553
 
8597553
-          free (probe_name);
8597553
-          resolv_test_end (aux);
8597553
-        }
8597553
+            free (probe_name);
8597553
+            resolv_test_end (aux);
8597553
+          }
8597553
 
8597553
   free_response_data ();
8597553
   return 0;