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> 2017-04-13 Florian Weimer <fweimer@redhat.com>
[BZ #21361] [BZ #21361]

5
NEWS
View File

@ -41,6 +41,11 @@ Version 2.26
"The Rules of Hungarian Orthography, 12th edition" and the work of "The Rules of Hungarian Orthography, 12th edition" and the work of
Egmont Koblinger (Bug 18934). 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 * res_mkquery and res_nmkquery no longer support the IQUERY opcode. DNS
servers have not supported this opcode for a long time. 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 *hp = (HEADER *) answer;
HEADER *hp2; HEADER *hp2;
int n, use_malloc = 0; int n, use_malloc = 0;
u_int oflags = statp->_flags;
size_t bufsize = (type == T_QUERY_A_AND_AAAA ? 2 : 1) * QUERYSIZE; size_t bufsize = (type == T_QUERY_A_AND_AAAA ? 2 : 1) * QUERYSIZE;
u_char *buf = alloca (bufsize); u_char *buf = alloca (bufsize);
@ -145,8 +144,7 @@ __libc_res_nquery(res_state statp,
query1, bufsize); query1, bufsize);
if (n > 0) if (n > 0)
{ {
if ((oflags & RES_F_EDNS0ERR) == 0 if ((statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
&& (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
{ {
/* Use RESOLV_EDNS_BUFFER_SIZE because the receive /* Use RESOLV_EDNS_BUFFER_SIZE because the receive
buffer can be reallocated. */ 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, n = res_nmkquery(statp, QUERY, name, class, T_AAAA, NULL, 0,
NULL, query2, bufsize - nused); NULL, query2, bufsize - nused);
if (n > 0 if (n > 0
&& (oflags & RES_F_EDNS0ERR) == 0
&& (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0) && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
/* Use RESOLV_EDNS_BUFFER_SIZE because the receive /* Use RESOLV_EDNS_BUFFER_SIZE because the receive
buffer can be reallocated. */ buffer can be reallocated. */
@ -187,7 +184,6 @@ __libc_res_nquery(res_state statp,
query1, bufsize); query1, bufsize);
if (n > 0 if (n > 0
&& (oflags & RES_F_EDNS0ERR) == 0
&& (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0) && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
{ {
/* Use RESOLV_EDNS_BUFFER_SIZE if the receive buffer /* 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 (__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 #ifdef DEBUG
if (statp->options & RES_DEBUG) if (statp->options & RES_DEBUG)
printf(";; res_query: mkquery failed\n"); printf(";; res_query: mkquery failed\n");

View File

@ -1321,26 +1321,6 @@ send_dg(res_state statp,
? *thisanssizp : *thisresplenp); ? *thisanssizp : *thisresplenp);
goto wait; 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) if (!(statp->options & RES_INSECURE2)
&& (recvresp1 || !res_queriesmatch(buf, buf + buflen, && (recvresp1 || !res_queriesmatch(buf, buf + buflen,
*thisansp, *thisansp,

View File

@ -115,8 +115,23 @@ response (const struct resolv_response_context *ctx,
{ {
TEST_VERIFY_EXIT (qname != NULL); TEST_VERIFY_EXIT (qname != NULL);
/* The "tcp." prefix can be used to request TCP fallback. */
const char *qname_compare = qname; 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; bool force_tcp;
if (strncmp ("tcp.", qname_compare, strlen ("tcp.")) == 0) if (strncmp ("tcp.", qname_compare, strlen ("tcp.")) == 0)
{ {
@ -132,14 +147,20 @@ response (const struct resolv_response_context *ctx,
else else
{ {
support_record_failure (); support_record_failure ();
printf ("error: unexpected QNAME: %s\n", qname); printf ("error: unexpected QNAME: %s (reduced: %s)\n",
qname, qname_compare);
return; return;
} }
TEST_VERIFY_EXIT (qclass == C_IN); 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_init (b, flags);
resolv_response_add_question (b, qname, qclass, qtype); resolv_response_add_question (b, qname, qclass, qtype);
if (flags.tc) if (flags.tc || flags.rcode != 0)
return; return;
if (test_verbose) if (test_verbose)
@ -466,33 +487,42 @@ do_test (void)
for (int do_edns = 0; do_edns < 2; ++do_edns) for (int do_edns = 0; do_edns < 2; ++do_edns)
for (int do_dnssec = 0; do_dnssec < 2; ++do_dnssec) for (int do_dnssec = 0; do_dnssec < 2; ++do_dnssec)
for (int do_tcp = 0; do_tcp < 2; ++do_tcp) for (int do_tcp = 0; do_tcp < 2; ++do_tcp)
{ for (int do_formerr = 0; do_formerr < 2; ++do_formerr)
struct resolv_test *aux = resolv_test_start {
((struct resolv_redirect_config) struct resolv_test *aux = resolv_test_start
{ ((struct resolv_redirect_config)
.response_callback = response, {
}); .response_callback = response,
});
use_edns = do_edns; use_edns = do_edns;
if (do_edns) if (do_edns)
_res.options |= RES_USE_EDNS0; _res.options |= RES_USE_EDNS0;
use_dnssec = do_dnssec; use_dnssec = do_dnssec;
if (do_dnssec) if (do_dnssec)
_res.options |= RES_USE_DNSSEC; _res.options |= RES_USE_DNSSEC;
char *probe_name = xstrdup (EDNS_PROBE_EXAMPLE); char *probe_name = xstrdup (EDNS_PROBE_EXAMPLE);
if (do_tcp) if (do_tcp)
{ {
char *n = xasprintf ("tcp.%s", probe_name); char *n = xasprintf ("tcp.%s", probe_name);
free (probe_name); free (probe_name);
probe_name = n; 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); free (probe_name);
resolv_test_end (aux); resolv_test_end (aux);
} }
free_response_data (); free_response_data ();
return 0; return 0;