resolv: Remove EDNS fallback [BZ #21369]

EDNS is disabled by default (so there is interoperability issue), and
the fallback code is problematic because it prevents an application
from obtaining DNSSEC data after a FORMERR response.
This commit is contained in:
Florian Weimer 2017-04-13 13:22:51 +02:00
parent e14a27723c
commit 44500cbb25
5 changed files with 75 additions and 62 deletions

View File

@ -1,3 +1,15 @@
2017-04-13 Florian Weimer <fweimer@redhat.com>
[BZ #21369]
Remove EDNS fallback.
* resolv/res_query.c (__libc_res_nquery): Remove RES_F_EDNS0ERR
handling.
* resolv/res_send.c (send_dg): Likewise.
* resolv/tst-resolv-edns.c (response): Handle "formerr." and
"tcp." prefixes.
(do_test): Send a "formerr."-prefixed query in an attempt to
trigger EDNS fallback.
2017-04-13 Florian Weimer <fweimer@redhat.com>
[BZ #21361]

5
NEWS
View File

@ -41,6 +41,11 @@ Version 2.26
"The Rules of Hungarian Orthography, 12th edition" and the work of
Egmont Koblinger (Bug 18934).
* The DNS stub resolver no longer performs EDNS fallback. If EDNS or DNSSEC
support is enabled, the configured recursive resolver must support EDNS.
(Responding to EDNS-enabled queries with responses which are not
EDNS-enabled is fine, but FORMERR responses are not.)
* res_mkquery and res_nmkquery no longer support the IQUERY opcode. DNS
servers have not supported this opcode for a long time.

View File

@ -122,7 +122,6 @@ __libc_res_nquery(res_state statp,
HEADER *hp = (HEADER *) answer;
HEADER *hp2;
int n, use_malloc = 0;
u_int oflags = statp->_flags;
size_t bufsize = (type == T_QUERY_A_AND_AAAA ? 2 : 1) * QUERYSIZE;
u_char *buf = alloca (bufsize);
@ -145,8 +144,7 @@ __libc_res_nquery(res_state statp,
query1, bufsize);
if (n > 0)
{
if ((oflags & RES_F_EDNS0ERR) == 0
&& (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
if ((statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
{
/* Use RESOLV_EDNS_BUFFER_SIZE because the receive
buffer can be reallocated. */
@ -170,7 +168,6 @@ __libc_res_nquery(res_state statp,
n = res_nmkquery(statp, QUERY, name, class, T_AAAA, NULL, 0,
NULL, query2, bufsize - nused);
if (n > 0
&& (oflags & RES_F_EDNS0ERR) == 0
&& (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
/* Use RESOLV_EDNS_BUFFER_SIZE because the receive
buffer can be reallocated. */
@ -187,7 +184,6 @@ __libc_res_nquery(res_state statp,
query1, bufsize);
if (n > 0
&& (oflags & RES_F_EDNS0ERR) == 0
&& (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
{
/* Use RESOLV_EDNS_BUFFER_SIZE if the receive buffer
@ -215,16 +211,6 @@ __libc_res_nquery(res_state statp,
}
}
if (__glibc_unlikely (n <= 0)) {
/* If the query choked with EDNS0, retry without EDNS0. */
if ((statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0
&& ((oflags ^ statp->_flags) & RES_F_EDNS0ERR) != 0) {
statp->_flags |= RES_F_EDNS0ERR;
#ifdef DEBUG
if (statp->options & RES_DEBUG)
printf(";; res_nquery: retry without EDNS0\n");
#endif
goto again;
}
#ifdef DEBUG
if (statp->options & RES_DEBUG)
printf(";; res_query: mkquery failed\n");

View File

@ -1321,26 +1321,6 @@ send_dg(res_state statp,
? *thisanssizp : *thisresplenp);
goto wait;
}
#ifdef RES_USE_EDNS0
if (anhp->rcode == FORMERR
&& (statp->options & RES_USE_EDNS0) != 0U) {
/*
* Do not retry if the server does not understand
* EDNS0. The case has to be captured here, as
* FORMERR packet do not carry query section, hence
* res_queriesmatch() returns 0.
*/
DprintQ(statp->options & RES_DEBUG,
(stdout,
"server rejected query with EDNS0:\n"),
*thisansp,
(*thisresplenp > *thisanssizp)
? *thisanssizp : *thisresplenp);
/* record the error */
statp->_flags |= RES_F_EDNS0ERR;
return close_and_return_error (statp, resplen2);
}
#endif
if (!(statp->options & RES_INSECURE2)
&& (recvresp1 || !res_queriesmatch(buf, buf + buflen,
*thisansp,

View File

@ -115,8 +115,23 @@ response (const struct resolv_response_context *ctx,
{
TEST_VERIFY_EXIT (qname != NULL);
/* The "tcp." prefix can be used to request TCP fallback. */
const char *qname_compare = qname;
/* The "formerr." prefix can be used to request a FORMERR response on the
first server. */
bool send_formerr;
if (strncmp ("formerr.", qname, strlen ("formerr.")) == 0)
{
send_formerr = true;
qname_compare = qname + strlen ("formerr.");
}
else
{
send_formerr = false;
qname_compare = qname;
}
/* The "tcp." prefix can be used to request TCP fallback. */
bool force_tcp;
if (strncmp ("tcp.", qname_compare, strlen ("tcp.")) == 0)
{
@ -132,14 +147,20 @@ response (const struct resolv_response_context *ctx,
else
{
support_record_failure ();
printf ("error: unexpected QNAME: %s\n", qname);
printf ("error: unexpected QNAME: %s (reduced: %s)\n",
qname, qname_compare);
return;
}
TEST_VERIFY_EXIT (qclass == C_IN);
struct resolv_response_flags flags = {.tc = force_tcp && !ctx->tcp};
struct resolv_response_flags flags = { };
flags.tc = force_tcp && !ctx->tcp;
if (!flags.tc && send_formerr && ctx->server_index == 0)
/* Send a FORMERR for the first full response from the first
server. */
flags.rcode = 1; /* FORMERR */
resolv_response_init (b, flags);
resolv_response_add_question (b, qname, qclass, qtype);
if (flags.tc)
if (flags.tc || flags.rcode != 0)
return;
if (test_verbose)
@ -466,33 +487,42 @@ do_test (void)
for (int do_edns = 0; do_edns < 2; ++do_edns)
for (int do_dnssec = 0; do_dnssec < 2; ++do_dnssec)
for (int do_tcp = 0; do_tcp < 2; ++do_tcp)
{
struct resolv_test *aux = resolv_test_start
((struct resolv_redirect_config)
{
.response_callback = response,
});
for (int do_formerr = 0; do_formerr < 2; ++do_formerr)
{
struct resolv_test *aux = resolv_test_start
((struct resolv_redirect_config)
{
.response_callback = response,
});
use_edns = do_edns;
if (do_edns)
_res.options |= RES_USE_EDNS0;
use_dnssec = do_dnssec;
if (do_dnssec)
_res.options |= RES_USE_DNSSEC;
use_edns = do_edns;
if (do_edns)
_res.options |= RES_USE_EDNS0;
use_dnssec = do_dnssec;
if (do_dnssec)
_res.options |= RES_USE_DNSSEC;
char *probe_name = xstrdup (EDNS_PROBE_EXAMPLE);
if (do_tcp)
{
char *n = xasprintf ("tcp.%s", probe_name);
free (probe_name);
probe_name = n;
}
char *probe_name = xstrdup (EDNS_PROBE_EXAMPLE);
if (do_tcp)
{
char *n = xasprintf ("tcp.%s", probe_name);
free (probe_name);
probe_name = n;
}
if (do_formerr)
{
/* Send a garbage query in an attempt to trigger EDNS
fallback. */
char *n = xasprintf ("formerr.%s", probe_name);
gethostbyname (n);
free (n);
}
run_test (probe_name);
run_test (probe_name);
free (probe_name);
resolv_test_end (aux);
}
free (probe_name);
resolv_test_end (aux);
}
free_response_data ();
return 0;