diff options
Diffstat (limited to 'resolv')
-rw-r--r-- | resolv/res_init.c | 2 | ||||
-rw-r--r-- | resolv/res_mkquery.c | 46 | ||||
-rw-r--r-- | resolv/res_query.c | 29 | ||||
-rw-r--r-- | resolv/res_send.c | 18 | ||||
-rw-r--r-- | resolv/resolv.h | 2 |
5 files changed, 90 insertions, 7 deletions
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) |