From 2bbb7d5b3c517956e95e5827037a76cbc39a20c8 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Fri, 9 Feb 2007 23:46:29 +0000 Subject: * resolv/res_init.c (res_setoptions): Recognize edns0 option. * resolv/res_mkquery.c: Define __res_nopt. * resolv/res_query.c (__libc_res_nquery): If RES_USE_EDNS0 is set try adding EDNS0 record. * resolv/res_send.c (send_dg): If request failed with FORMERR and EDNS0 record was send make sure we don't try it again. * resolv/resolv.h: Define RES_F_EDNS0ERR and RES_USE_EDNS0. * include/resolv.h: Declare __res_nopt. --- ChangeLog | 11 +++++++++++ include/resolv.h | 3 +++ resolv/res_init.c | 2 ++ resolv/res_mkquery.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ resolv/res_query.c | 29 ++++++++++++++++++++++------- resolv/res_send.c | 18 ++++++++++++++++++ resolv/resolv.h | 2 ++ 7 files changed, 104 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index 070da56092..f1b6d3d022 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2007-02-09 Ulrich Drepper + + * resolv/res_init.c (res_setoptions): Recognize edns0 option. + * resolv/res_mkquery.c: Define __res_nopt. + * resolv/res_query.c (__libc_res_nquery): If RES_USE_EDNS0 is set + try adding EDNS0 record. + * resolv/res_send.c (send_dg): If request failed with FORMERR and + EDNS0 record was send make sure we don't try it again. + * resolv/resolv.h: Define RES_F_EDNS0ERR and RES_USE_EDNS0. + * include/resolv.h: Declare __res_nopt. + 2007-02-08 Jakub Jelinek [BZ #3944] diff --git a/include/resolv.h b/include/resolv.h index 189e4fb58b..c1c89f0ada 100644 --- a/include/resolv.h +++ b/include/resolv.h @@ -48,6 +48,8 @@ extern void res_send_setrhook (res_send_rhook __hook); extern int res_ourserver_p (const res_state __statp, const struct sockaddr_in6 *__inp); extern void __res_iclose (res_state statp, bool free_addr); +extern int __res_nopt(res_state statp, int n0, u_char *buf, int buflen, + int anslen); libc_hidden_proto (__res_ninit) libc_hidden_proto (__res_maybe_init) libc_hidden_proto (__res_nclose) @@ -100,6 +102,7 @@ libresolv_hidden_proto (__ns_name_ntop) libresolv_hidden_proto (__ns_name_unpack) libresolv_hidden_proto (__ns_get16) libresolv_hidden_proto (__ns_get32) +libresolv_hidden_proto (__res_nopt) extern const char *_res_opcodes[]; libresolv_hidden_proto (_res_opcodes) diff --git a/resolv/res_init.c b/resolv/res_init.c index b5a03d1883..640e087920 100644 --- a/resolv/res_init.c +++ b/resolv/res_init.c @@ -510,6 +510,8 @@ res_setoptions(res_state statp, const char *options, const char *source) { } else if (!strncmp(cp, "no-check-names", sizeof("no-check-names") - 1)) { statp->options |= RES_NOCHECKNAME; + } else if (!strncmp(cp, "edns0", sizeof("edns0") - 1)) { + statp->options |= RES_USE_EDNS0; } else { /* XXX - print a warning here? */ } diff --git a/resolv/res_mkquery.c b/resolv/res_mkquery.c index fd80569fe2..3fa597fecd 100644 --- a/resolv/res_mkquery.c +++ b/resolv/res_mkquery.c @@ -208,3 +208,49 @@ res_nmkquery(res_state statp, return (cp - buf); } libresolv_hidden_def (res_nmkquery) + + +/* attach OPT pseudo-RR, as documented in RFC2671 (EDNS0). */ +#ifndef T_OPT +#define T_OPT 41 +#endif + +int +__res_nopt(res_state statp, + int n0, /* current offset in buffer */ + u_char *buf, /* buffer to put query */ + int buflen, /* size of buffer */ + int anslen) /* UDP answer buffer size */ +{ + u_int16_t flags = 0; + +#ifdef DEBUG + if ((statp->options & RES_DEBUG) != 0U) + printf(";; res_nopt()\n"); +#endif + + HEADER *hp = (HEADER *) buf; + u_char *cp = buf + n0; + u_char *ep = buf + buflen; + + if ((ep - cp) < 1 + RRFIXEDSZ) + return -1; + + *cp++ = 0; /* "." */ + + ns_put16(T_OPT, cp); /* TYPE */ + cp += INT16SZ; + ns_put16(anslen & 0xffff, cp); /* CLASS = UDP payload size */ + cp += INT16SZ; + *cp++ = NOERROR; /* extended RCODE */ + *cp++ = 0; /* EDNS version */ + /* XXX Once we support DNSSEC we change the flag value here. */ + ns_put16(flags, cp); + cp += INT16SZ; + ns_put16(0, cp); /* RDLEN */ + cp += INT16SZ; + hp->arcount = htons(ntohs(hp->arcount) + 1); + + return cp - buf; +} +libresolv_hidden_def (__res_nopt) diff --git a/resolv/res_query.c b/resolv/res_query.c index 85bad97d2d..4371af5b05 100644 --- a/resolv/res_query.c +++ b/resolv/res_query.c @@ -120,10 +120,13 @@ __libc_res_nquery(res_state statp, u_char *buf; HEADER *hp = (HEADER *) answer; int n, use_malloc = 0; + u_int oflags = statp->_flags; - hp->rcode = NOERROR; /* default */ + size_t bufsize = QUERYSIZE; + buf = alloca (bufsize); - buf = alloca (QUERYSIZE); + again: + hp->rcode = NOERROR; /* default */ #ifdef DEBUG if (statp->options & RES_DEBUG) @@ -131,18 +134,30 @@ __libc_res_nquery(res_state statp, #endif n = res_nmkquery(statp, QUERY, name, class, type, NULL, 0, NULL, - buf, QUERYSIZE); - if (__builtin_expect (n <= 0, 0)) { + buf, bufsize); + if (n > 0 + && (oflags & RES_F_EDNS0ERR) == 0 + && (statp->options & RES_USE_EDNS0) != 0) + n = __res_nopt(statp, n, buf, bufsize, anslen); + if (__builtin_expect (n <= 0, 0) && !use_malloc) { /* Retry just in case res_nmkquery failed because of too short buffer. Shouldn't happen. */ - buf = malloc (MAXPACKET); + bufsize = MAXPACKET; + buf = malloc (bufsize); if (buf != NULL) { use_malloc = 1; - n = res_nmkquery(statp, QUERY, name, class, type, NULL, - 0, NULL, buf, MAXPACKET); + goto again; } } if (__builtin_expect (n <= 0, 0)) { + /* If the query choked with EDNS0, retry without EDNS0. */ + if ((statp->options & RES_USE_EDNS0) != 0 + && ((oflags ^ statp->_flags) & RES_F_EDNS0ERR) != 0) { + statp->_flags |= RES_F_EDNS0ERR; + if (statp->options & RES_DEBUG) + printf(";; res_nquery: retry without EDNS0\n"); + goto again; + } #ifdef DEBUG if (statp->options & RES_DEBUG) printf(";; res_query: mkquery failed\n"); diff --git a/resolv/res_send.c b/resolv/res_send.c index 887d048e19..f38c399ffd 100644 --- a/resolv/res_send.c +++ b/resolv/res_send.c @@ -986,6 +986,24 @@ send_dg(res_state statp, ans, (resplen > anssiz) ? anssiz : resplen); goto wait; } +#ifdef RES_USE_EDNS0 + if (anhp->rcode == FORMERR + && (statp->options & RES_USE_EDNS0) != 0U) { + /* + * Do not retry if the server do 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"), + ans, (resplen > anssiz) ? anssiz : resplen); + /* record the error */ + statp->_flags |= RES_F_EDNS0ERR; + goto err_out; + } +#endif if (!(statp->options & RES_INSECURE2) && !res_queriesmatch(buf, buf + buflen, ans, ans + anssiz)) { diff --git a/resolv/resolv.h b/resolv/resolv.h index d7cda117fd..9aa09b8701 100644 --- a/resolv/resolv.h +++ b/resolv/resolv.h @@ -180,6 +180,7 @@ struct res_sym { */ #define RES_F_VC 0x00000001 /* socket is TCP */ #define RES_F_CONN 0x00000002 /* socket is connected */ +#define RES_F_EDNS0ERR 0x00000004 /* EDNS0 caused errors */ /* res_findzonecut() options */ #define RES_EXHAUSTIVE 0x00000001 /* always do all queries */ @@ -209,6 +210,7 @@ struct res_sym { strings */ #define RES_NOIP6DOTINT 0x00080000 /* Do not use .ip6.int in IPv6 reverse lookup */ +#define RES_USE_EDNS0 0x00100000 /* Use EDNS0. */ #define RES_DEFAULT (RES_RECURSE|RES_DEFNAMES|RES_DNSRCH|RES_NOIP6DOTINT) -- cgit v1.2.1