diff options
author | Dan Goodliffe <dan.goodliffe@octal.co.uk> | 2022-10-03 15:26:27 +0100 |
---|---|---|
committer | Dan Goodliffe <dan.goodliffe@octal.co.uk> | 2022-10-03 15:26:27 +0100 |
commit | 8140e5ab627796383abc93292d6bd66a09c5aac8 (patch) | |
tree | 666cf28058a57b3b9eb303192934a32511c0d085 /sys-libs/glibc | |
parent | Remove patch now upstream (diff) | |
download | patches-8140e5ab627796383abc93292d6bd66a09c5aac8.tar.bz2 patches-8140e5ab627796383abc93292d6bd66a09c5aac8.tar.xz patches-8140e5ab627796383abc93292d6bd66a09c5aac8.zip |
Revert rewrite resolver
Diffstat (limited to 'sys-libs/glibc')
-rw-r--r-- | sys-libs/glibc/revert-rewrite-resolver.patch | 576 |
1 files changed, 576 insertions, 0 deletions
diff --git a/sys-libs/glibc/revert-rewrite-resolver.patch b/sys-libs/glibc/revert-rewrite-resolver.patch new file mode 100644 index 0000000..05fcb55 --- /dev/null +++ b/sys-libs/glibc/revert-rewrite-resolver.patch @@ -0,0 +1,576 @@ +reverted: +--- b/resolv/nss_dns/dns-host.c ++++ a/resolv/nss_dns/dns-host.c +@@ -100,6 +100,13 @@ + #endif + #define MAXHOSTNAMELEN 256 + ++/* We need this time later. */ ++typedef union querybuf ++{ ++ HEADER hdr; ++ u_char buf[MAXPACKET]; ++} querybuf; ++ + /* For historic reasons, pointers to IP addresses are char *, so use a + single list type for addresses and host names. */ + #define DYNARRAY_STRUCT ptrlist +@@ -118,18 +125,18 @@ + char **hnamep, int *errnop, + int *h_errnop, int32_t *ttlp); + ++static enum nss_status gaih_getanswer (const querybuf *answer1, int anslen1, ++ const querybuf *answer2, int anslen2, ++ const char *qname, +-static enum nss_status gaih_getanswer (unsigned char *packet1, +- size_t packet1len, +- unsigned char *packet2, +- size_t packet2len, +- struct alloc_buffer *abuf, + struct gaih_addrtuple **pat, ++ char *buffer, size_t buflen, + int *errnop, int *h_errnop, + int32_t *ttlp); ++static enum nss_status gaih_getanswer_noaaaa (const querybuf *answer1, ++ int anslen1, ++ const char *qname, +-static enum nss_status gaih_getanswer_noaaaa (unsigned char *packet, +- size_t packetlen, +- struct alloc_buffer *abuf, + struct gaih_addrtuple **pat, ++ char *buffer, size_t buflen, + int *errnop, int *h_errnop, + int32_t *ttlp); + +@@ -401,13 +408,17 @@ + name = cp; + } + ++ union ++ { ++ querybuf *buf; ++ u_char *ptr; ++ } host_buffer; ++ querybuf *orig_host_buffer; ++ host_buffer.buf = orig_host_buffer = (querybuf *) alloca (2048); +- unsigned char dns_packet_buffer[2048]; +- unsigned char *alt_dns_packet_buffer = dns_packet_buffer; + u_char *ans2p = NULL; + int nans2p = 0; + int resplen2 = 0; + int ans2p_malloced = 0; +- struct alloc_buffer abuf = alloc_buffer_create (buffer, buflen); + + + int olderr = errno; +@@ -416,21 +427,22 @@ + if ((ctx->resp->options & RES_NOAAAA) == 0) + { + n = __res_context_search (ctx, name, C_IN, T_QUERY_A_AND_AAAA, ++ host_buffer.buf->buf, 2048, &host_buffer.ptr, ++ &ans2p, &nans2p, &resplen2, &ans2p_malloced); +- dns_packet_buffer, sizeof (dns_packet_buffer), +- &alt_dns_packet_buffer, &ans2p, &nans2p, +- &resplen2, &ans2p_malloced); + if (n >= 0) ++ status = gaih_getanswer (host_buffer.buf, n, (const querybuf *) ans2p, ++ resplen2, name, pat, buffer, buflen, ++ errnop, herrnop, ttlp); +- status = gaih_getanswer (alt_dns_packet_buffer, n, ans2p, resplen2, +- &abuf, pat, errnop, herrnop, ttlp); + } + else + { + n = __res_context_search (ctx, name, C_IN, T_A, ++ host_buffer.buf->buf, 2048, NULL, ++ NULL, NULL, NULL, NULL); +- dns_packet_buffer, sizeof (dns_packet_buffer), +- NULL, NULL, NULL, NULL, NULL); + if (n >= 0) ++ status = gaih_getanswer_noaaaa (host_buffer.buf, n, ++ name, pat, buffer, buflen, ++ errnop, herrnop, ttlp); +- status = gaih_getanswer_noaaaa (alt_dns_packet_buffer, n, +- &abuf, pat, errnop, herrnop, ttlp); + } + if (n < 0) + { +@@ -461,20 +473,12 @@ + __set_errno (olderr); + } + +- /* Implement the buffer resizing protocol. */ +- if (alloc_buffer_has_failed (&abuf)) +- { +- *errnop = ERANGE; +- *herrnop = NETDB_INTERNAL; +- status = NSS_STATUS_TRYAGAIN; +- } +- + /* Check whether ans2p was separately allocated. */ + if (ans2p_malloced) + free (ans2p); + ++ if (host_buffer.buf != orig_host_buffer) ++ free (host_buffer.buf); +- if (alt_dns_packet_buffer != dns_packet_buffer) +- free (alt_dns_packet_buffer); + + __resolv_context_put (ctx); + return status; +@@ -888,152 +892,259 @@ + return NSS_STATUS_TRYAGAIN; + } + +-/* Parses DNS data found in PACKETLEN bytes at PACKET in struct +- gaih_addrtuple address tuples. The new address tuples are linked +- from **TAILP, with backing store allocated from ABUF, and *TAILP is +- updated to point where the next tuple pointer should be stored. If +- TTLP is not null, *TTLP is updated to reflect the minimum TTL. If +- STORE_CANON is true, the canonical name is stored as part of the +- first address tuple being written. */ + static enum nss_status ++gaih_getanswer_slice (const querybuf *answer, int anslen, const char *qname, ++ struct gaih_addrtuple ***patp, ++ char **bufferp, size_t *buflenp, ++ int *errnop, int *h_errnop, int32_t *ttlp, int *firstp) +-gaih_getanswer_slice (unsigned char *packet, size_t packetlen, +- struct alloc_buffer *abuf, +- struct gaih_addrtuple ***tailp, +- int *errnop, int *h_errnop, int32_t *ttlp, +- bool store_canon) + { ++ char *buffer = *bufferp; ++ size_t buflen = *buflenp; ++ ++ struct gaih_addrtuple **pat = *patp; ++ const HEADER *hp = &answer->hdr; ++ int ancount = ntohs (hp->ancount); ++ int qdcount = ntohs (hp->qdcount); ++ const u_char *cp = answer->buf + HFIXEDSZ; ++ const u_char *end_of_message = answer->buf + anslen; ++ if (__glibc_unlikely (qdcount != 1)) ++ { ++ *h_errnop = NO_RECOVERY; ++ return NSS_STATUS_UNAVAIL; ++ } ++ ++ u_char packtmp[NS_MAXCDNAME]; ++ int n = __ns_name_unpack (answer->buf, end_of_message, cp, ++ packtmp, sizeof packtmp); ++ /* We unpack the name to check it for validity. But we do not need ++ it later. */ ++ if (n != -1 && __ns_name_ntop (packtmp, buffer, buflen) == -1) ++ { ++ if (__glibc_unlikely (errno == EMSGSIZE)) ++ { ++ too_small: ++ *errnop = ERANGE; ++ *h_errnop = NETDB_INTERNAL; ++ return NSS_STATUS_TRYAGAIN; ++ } ++ ++ n = -1; ++ } ++ ++ if (__glibc_unlikely (n < 0)) ++ { ++ *errnop = errno; ++ *h_errnop = NO_RECOVERY; ++ return NSS_STATUS_UNAVAIL; ++ } ++ if (__glibc_unlikely (__libc_res_hnok (buffer) == 0)) +- struct ns_rr_cursor c; +- if (!__ns_rr_cursor_init (&c, packet, packetlen)) + { ++ errno = EBADMSG; ++ *errnop = EBADMSG; +- /* This should not happen because __res_context_query already +- perfroms response validation. */ + *h_errnop = NO_RECOVERY; + return NSS_STATUS_UNAVAIL; + } ++ cp += n + QFIXEDSZ; +- bool haveanswer = false; /* Set to true if at least one address. */ +- uint16_t qtype = ns_rr_cursor_qtype (&c); +- int ancount = ns_rr_cursor_ancount (&c); +- const unsigned char *expected_name = ns_rr_cursor_qname (&c); +- /* expected_name may be updated to point into this buffer. */ +- unsigned char name_buffer[NS_MAXCDNAME]; + ++ int haveanswer = 0; ++ int had_error = 0; ++ char *canon = NULL; ++ char *h_name = NULL; ++ int h_namelen = 0; +- /* This is a pointer to a possibly-compressed name in the packet. +- Eventually it is equivalent to the canonical name. If needed, it +- is uncompressed and translated to text form when the first +- address tuple is encountered. */ +- const unsigned char *compressed_alias_name = expected_name; + ++ if (ancount == 0) +- if (ancount == 0 || !__res_binary_hnok (compressed_alias_name)) + { + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } + ++ while (ancount-- > 0 && cp < end_of_message && had_error == 0) +- for (; ancount > -0; --ancount) + { ++ n = __ns_name_unpack (answer->buf, end_of_message, cp, ++ packtmp, sizeof packtmp); ++ if (n != -1 && ++ (h_namelen = __ns_name_ntop (packtmp, buffer, buflen)) == -1) ++ { ++ if (__glibc_unlikely (errno == EMSGSIZE)) ++ goto too_small; ++ ++ n = -1; ++ } ++ if (__glibc_unlikely (n < 0)) ++ { ++ ++had_error; ++ continue; ++ } ++ if (*firstp && canon == NULL && __libc_res_hnok (buffer)) ++ { ++ h_name = buffer; ++ buffer += h_namelen; ++ buflen -= h_namelen; ++ } ++ ++ cp += n; /* name */ ++ ++ if (__glibc_unlikely (cp + 10 > end_of_message)) ++ { ++ ++had_error; ++ continue; ++ } ++ ++ uint16_t type; ++ NS_GET16 (type, cp); ++ uint16_t class; ++ NS_GET16 (class, cp); ++ int32_t ttl; ++ NS_GET32 (ttl, cp); ++ NS_GET16 (n, cp); /* RDATA length. */ ++ ++ if (end_of_message - cp < n) +- struct ns_rr_wire rr; +- if (!__ns_rr_cursor_next (&c, &rr)) + { ++ /* RDATA extends beyond the end of the packet. */ ++ ++had_error; ++ continue; +- *h_errnop = NO_RECOVERY; +- return NSS_STATUS_UNAVAIL; + } + ++ if (class != C_IN) ++ { ++ cp += n; ++ continue; ++ } +- /* Update TTL for known record types. */ +- if ((rr.rtype == T_CNAME || rr.rtype == qtype) +- && ttlp != NULL && *ttlp > rr.ttl) +- *ttlp = rr.ttl; + ++ if (type == T_CNAME) +- if (rr.rtype == T_CNAME) + { ++ char tbuf[MAXDNAME]; ++ ++ /* A CNAME could also have a TTL entry. */ ++ if (ttlp != NULL && ttl < *ttlp) ++ *ttlp = ttl; ++ ++ n = __libc_dn_expand (answer->buf, end_of_message, cp, ++ tbuf, sizeof tbuf); ++ if (__glibc_unlikely (n < 0)) ++ { ++ ++had_error; ++ continue; ++ } ++ cp += n; ++ ++ if (*firstp && __libc_res_hnok (tbuf)) +- /* NB: No check for owner name match, based on historic +- precedent. Record the CNAME target as the new expected +- name. */ +- int n = __ns_name_unpack (c.begin, c.end, rr.rdata, +- name_buffer, sizeof (name_buffer)); +- if (n < 0) + { ++ /* Reclaim buffer space. */ ++ if (h_name + h_namelen == buffer) ++ { ++ buffer = h_name; ++ buflen += h_namelen; ++ } ++ ++ n = strlen (tbuf) + 1; ++ if (__glibc_unlikely (n > buflen)) ++ goto too_small; ++ if (__glibc_unlikely (n >= MAXHOSTNAMELEN)) ++ { ++ ++had_error; ++ continue; ++ } ++ ++ canon = buffer; ++ buffer = __mempcpy (buffer, tbuf, n); ++ buflen -= n; ++ h_namelen = 0; +- *h_errnop = NO_RECOVERY; +- return NSS_STATUS_UNAVAIL; + } ++ continue; +- expected_name = name_buffer; +- if (store_canon && __res_binary_hnok (name_buffer)) +- /* This name can be used as a canonical name. Do not +- translate to text form here to conserve buffer space. +- Point to the compressed name because name_buffer can be +- overwritten with an unusable name later. */ +- compressed_alias_name = rr.rdata; + } ++ ++ /* Stop parsing if we encounter a record with incorrect RDATA ++ length. */ ++ if (type == T_A || type == T_AAAA) +- else if (rr.rtype == qtype +- && __ns_samebinaryname (rr.rname, expected_name) +- && rr.rdlength == rrtype_to_rdata_length (qtype)) + { ++ if (n != rrtype_to_rdata_length (type)) +- struct gaih_addrtuple *ntup +- = alloc_buffer_alloc (abuf, struct gaih_addrtuple); +- /* Delay error reporting to the callers (they implement the +- ERANGE buffer resizing handshake). */ +- if (ntup != NULL) + { ++ ++had_error; ++ continue; +- ntup->next = NULL; +- if (store_canon && compressed_alias_name != NULL) +- { +- /* This assumes that all the CNAME records come +- first. Use MAXHOSTNAMELEN instead of +- NS_MAXCDNAME for additional length checking. +- However, these checks are not expected to fail +- because all size NS_MAXCDNAME names should into +- the hname buffer because no escaping is +- needed. */ +- char unsigned nbuf[NS_MAXCDNAME]; +- char hname[MAXHOSTNAMELEN + 1]; +- if (__ns_name_unpack (c.begin, c.end, +- compressed_alias_name, +- nbuf, sizeof (nbuf)) >= 0 +- && __ns_name_ntop (nbuf, hname, sizeof (hname)) >= 0) +- /* Space checking is performed by the callers. */ +- ntup->name = alloc_buffer_copy_string (abuf, hname); +- store_canon = false; +- } +- else +- ntup->name = NULL; +- if (rr.rdlength == 4) +- ntup->family = AF_INET; +- else +- ntup->family = AF_INET6; +- memcpy (ntup->addr, rr.rdata, rr.rdlength); +- ntup->scopeid = 0; +- +- /* Link in the new tuple, and update the tail pointer to +- point to its next field. */ +- **tailp = ntup; +- *tailp = &ntup->next; +- +- haveanswer = true; + } + } ++ else ++ { ++ /* Skip unknown records. */ ++ cp += n; ++ continue; ++ } ++ ++ assert (type == T_A || type == T_AAAA); ++ if (*pat == NULL) ++ { ++ uintptr_t pad = (-(uintptr_t) buffer ++ % __alignof__ (struct gaih_addrtuple)); ++ buffer += pad; ++ buflen = buflen > pad ? buflen - pad : 0; ++ ++ if (__glibc_unlikely (buflen < sizeof (struct gaih_addrtuple))) ++ goto too_small; ++ ++ *pat = (struct gaih_addrtuple *) buffer; ++ buffer += sizeof (struct gaih_addrtuple); ++ buflen -= sizeof (struct gaih_addrtuple); ++ } ++ ++ (*pat)->name = NULL; ++ (*pat)->next = NULL; ++ ++ if (*firstp) ++ { ++ /* We compose a single hostent out of the entire chain of ++ entries, so the TTL of the hostent is essentially the lowest ++ TTL in the chain. */ ++ if (ttlp != NULL && ttl < *ttlp) ++ *ttlp = ttl; ++ ++ (*pat)->name = canon ?: h_name; ++ ++ *firstp = 0; ++ } ++ ++ (*pat)->family = type == T_A ? AF_INET : AF_INET6; ++ memcpy ((*pat)->addr, cp, n); ++ cp += n; ++ (*pat)->scopeid = 0; ++ ++ pat = &((*pat)->next); ++ ++ haveanswer = 1; + } + + if (haveanswer) + { ++ *patp = pat; ++ *bufferp = buffer; ++ *buflenp = buflen; ++ + *h_errnop = NETDB_SUCCESS; + return NSS_STATUS_SUCCESS; + } ++ ++ /* Special case here: if the resolver sent a result but it only ++ contains a CNAME while we are looking for a T_A or T_AAAA record, ++ we fail with NOTFOUND instead of TRYAGAIN. */ ++ if (canon != NULL) +- else + { +- /* Special case here: if the resolver sent a result but it only +- contains a CNAME while we are looking for a T_A or T_AAAA +- record, we fail with NOTFOUND. */ + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } ++ ++ *h_errnop = NETDB_INTERNAL; ++ return NSS_STATUS_TRYAGAIN; + } + + + static enum nss_status ++gaih_getanswer (const querybuf *answer1, int anslen1, const querybuf *answer2, ++ int anslen2, const char *qname, ++ struct gaih_addrtuple **pat, char *buffer, size_t buflen, +-gaih_getanswer (unsigned char *packet1, size_t packet1len, +- unsigned char *packet2, size_t packet2len, +- struct alloc_buffer *abuf, struct gaih_addrtuple **pat, + int *errnop, int *h_errnop, int32_t *ttlp) + { ++ int first = 1; ++ + enum nss_status status = NSS_STATUS_NOTFOUND; + + /* Combining the NSS status of two distinct queries requires some +@@ -1045,10 +1156,7 @@ + between TRYAGAIN (recoverable) and TRYAGAIN' (not-recoverable). + A recoverable TRYAGAIN is almost always due to buffer size issues + and returns ERANGE in errno and the caller is expected to retry ++ with a larger buffer. +- with a larger buffer. (The caller, _nss_dns_gethostbyname4_r, +- ignores the return status if it detects that the result buffer +- has been exhausted and generates a TRYAGAIN failure with an +- ERANGE code.) + + Lastly, you may be tempted to make significant changes to the + conditions in this code to bring about symmetry between responses. +@@ -1128,30 +1236,36 @@ + is a recoverable error we now return TRYAGIN even if the first + response was SUCCESS. */ + ++ if (anslen1 > 0) ++ status = gaih_getanswer_slice(answer1, anslen1, qname, ++ &pat, &buffer, &buflen, ++ errnop, h_errnop, ttlp, ++ &first); ++ ++ if ((status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND ++ || (status == NSS_STATUS_TRYAGAIN ++ /* We want to look at the second answer in case of an ++ NSS_STATUS_TRYAGAIN only if the error is non-recoverable, i.e. ++ *h_errnop is NO_RECOVERY. If not, and if the failure was due to ++ an insufficient buffer (ERANGE), then we need to drop the results ++ and pass on the NSS_STATUS_TRYAGAIN to the caller so that it can ++ repeat the query with a larger buffer. */ ++ && (*errnop != ERANGE || *h_errnop == NO_RECOVERY))) ++ && answer2 != NULL && anslen2 > 0) +- if (packet1len > 0) + { ++ enum nss_status status2 = gaih_getanswer_slice(answer2, anslen2, qname, ++ &pat, &buffer, &buflen, ++ errnop, h_errnop, ttlp, ++ &first); +- status = gaih_getanswer_slice (packet1, packet1len, +- abuf, &pat, errnop, h_errnop, ttlp, true); +- if (alloc_buffer_has_failed (abuf)) +- /* Do not try parsing the second packet if a larger result +- buffer is needed. The caller implements the resizing +- protocol because *abuf has been exhausted. */ +- return NSS_STATUS_TRYAGAIN; /* Ignored by the caller. */ +- } +- +- if ((status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND) +- && packet2 != NULL && packet2len > 0) +- { +- enum nss_status status2 +- = gaih_getanswer_slice (packet2, packet2len, +- abuf, &pat, errnop, h_errnop, ttlp, +- /* Success means that data with a +- canonical name has already been +- stored. Do not store the name again. */ +- status != NSS_STATUS_SUCCESS); + /* Use the second response status in some cases. */ + if (status != NSS_STATUS_SUCCESS && status2 != NSS_STATUS_NOTFOUND) + status = status2; ++ /* Do not return a truncated second response (unless it was ++ unavoidable e.g. unrecoverable TRYAGAIN). */ ++ if (status == NSS_STATUS_SUCCESS ++ && (status2 == NSS_STATUS_TRYAGAIN ++ && *errnop == ERANGE && *h_errnop != NO_RECOVERY)) ++ status = NSS_STATUS_TRYAGAIN; + } + + return status; +@@ -1159,13 +1273,18 @@ + + /* Variant of gaih_getanswer without a second (AAAA) response. */ + static enum nss_status ++gaih_getanswer_noaaaa (const querybuf *answer1, int anslen1, const char *qname, ++ struct gaih_addrtuple **pat, ++ char *buffer, size_t buflen, +-gaih_getanswer_noaaaa (unsigned char *packet, size_t packetlen, +- struct alloc_buffer *abuf, struct gaih_addrtuple **pat, + int *errnop, int *h_errnop, int32_t *ttlp) + { ++ int first = 1; ++ + enum nss_status status = NSS_STATUS_NOTFOUND; ++ if (anslen1 > 0) ++ status = gaih_getanswer_slice (answer1, anslen1, qname, ++ &pat, &buffer, &buflen, ++ errnop, h_errnop, ttlp, ++ &first); +- if (packetlen > 0) +- status = gaih_getanswer_slice (packet, packetlen, +- abuf, &pat, errnop, h_errnop, ttlp, true); + return status; + } |