You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
447 lines
12 KiB
447 lines
12 KiB
commit d9c979abf9307ef3e27dbe65317430977bb322c7 |
|
Author: Florian Weimer <fweimer@redhat.com> |
|
Date: Tue Aug 30 10:02:49 2022 +0200 |
|
|
|
nss_dns: Split getanswer_ptr from getanswer_r |
|
|
|
And expand the use of name_ok and qtype in getanswer_ptr (the |
|
former also in getanswer_r). |
|
|
|
After further cleanups, not much code will be shared between the |
|
two functions. |
|
|
|
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org> |
|
(cherry picked from commit 0dcc43e9981005540bf39dc7bf33fbab62cf9e84) |
|
|
|
diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c |
|
index 6e83fca1c5b1f98c..a6bf73a091968358 100644 |
|
--- a/resolv/nss_dns/dns-host.c |
|
+++ b/resolv/nss_dns/dns-host.c |
|
@@ -117,6 +117,11 @@ static enum nss_status getanswer_r (struct resolv_context *ctx, |
|
struct hostent *result, char *buffer, |
|
size_t buflen, int *errnop, int *h_errnop, |
|
int map, int32_t *ttlp, char **canonp); |
|
+static enum nss_status getanswer_ptr (const querybuf *answer, int anslen, |
|
+ const char *qname, |
|
+ struct hostent *result, char *buffer, |
|
+ size_t buflen, int *errnop, |
|
+ int *h_errnop, int32_t *ttlp); |
|
|
|
static enum nss_status gaih_getanswer (const querybuf *answer1, int anslen1, |
|
const querybuf *answer2, int anslen2, |
|
@@ -562,9 +567,8 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af, |
|
return errno == ECONNREFUSED ? NSS_STATUS_UNAVAIL : NSS_STATUS_NOTFOUND; |
|
} |
|
|
|
- status = getanswer_r |
|
- (ctx, host_buffer.buf, n, qbuf, T_PTR, result, buffer, buflen, |
|
- errnop, h_errnop, 0 /* XXX */, ttlp, NULL); |
|
+ status = getanswer_ptr (host_buffer.buf, n, qbuf, result, |
|
+ buffer, buflen, errnop, h_errnop, ttlp); |
|
if (host_buffer.buf != orig_host_buffer) |
|
free (host_buffer.buf); |
|
if (status != NSS_STATUS_SUCCESS) |
|
@@ -660,8 +664,6 @@ getanswer_r (struct resolv_context *ctx, |
|
int haveanswer, had_error; |
|
char *bp, **ap, **hap; |
|
char tbuf[MAXDNAME]; |
|
- const char *tname; |
|
- int (*name_ok) (const char *); |
|
u_char packtmp[NS_MAXCDNAME]; |
|
int have_to_map = 0; |
|
uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct host_data); |
|
@@ -680,22 +682,8 @@ getanswer_r (struct resolv_context *ctx, |
|
if (buflen - sizeof (struct host_data) != linebuflen) |
|
linebuflen = INT_MAX; |
|
|
|
- tname = qname; |
|
result->h_name = NULL; |
|
end_of_message = answer->buf + anslen; |
|
- switch (qtype) |
|
- { |
|
- case T_A: |
|
- case T_AAAA: |
|
- name_ok = __libc_res_hnok; |
|
- break; |
|
- case T_PTR: |
|
- name_ok = __libc_res_dnok; |
|
- break; |
|
- default: |
|
- *errnop = ENOENT; |
|
- return NSS_STATUS_UNAVAIL; /* XXX should be abort(); */ |
|
- } |
|
|
|
/* |
|
* find first satisfactory answer |
|
@@ -730,7 +718,7 @@ getanswer_r (struct resolv_context *ctx, |
|
*h_errnop = NO_RECOVERY; |
|
return NSS_STATUS_UNAVAIL; |
|
} |
|
- if (__glibc_unlikely (name_ok (bp) == 0)) |
|
+ if (__glibc_unlikely (__libc_res_hnok (bp) == 0)) |
|
{ |
|
errno = EBADMSG; |
|
*errnop = EBADMSG; |
|
@@ -784,7 +772,7 @@ getanswer_r (struct resolv_context *ctx, |
|
n = -1; |
|
} |
|
|
|
- if (__glibc_unlikely (n < 0 || (*name_ok) (bp) == 0)) |
|
+ if (__glibc_unlikely (n < 0 || __libc_res_hnok (bp) == 0)) |
|
{ |
|
++had_error; |
|
continue; |
|
@@ -817,7 +805,7 @@ getanswer_r (struct resolv_context *ctx, |
|
continue; /* XXX - had_error++ ? */ |
|
} |
|
|
|
- if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) |
|
+ if (type == T_CNAME) |
|
{ |
|
/* A CNAME could also have a TTL entry. */ |
|
if (ttlp != NULL && ttl < *ttlp) |
|
@@ -827,7 +815,7 @@ getanswer_r (struct resolv_context *ctx, |
|
continue; |
|
n = __libc_dn_expand (answer->buf, end_of_message, cp, |
|
tbuf, sizeof tbuf); |
|
- if (__glibc_unlikely (n < 0 || (*name_ok) (tbuf) == 0)) |
|
+ if (__glibc_unlikely (n < 0 || __libc_res_hnok (tbuf) == 0)) |
|
{ |
|
++had_error; |
|
continue; |
|
@@ -858,7 +846,260 @@ getanswer_r (struct resolv_context *ctx, |
|
continue; |
|
} |
|
|
|
- if (qtype == T_PTR && type == T_CNAME) |
|
+ if (type == T_A && qtype == T_AAAA && map) |
|
+ have_to_map = 1; |
|
+ else if (__glibc_unlikely (type != qtype)) |
|
+ { |
|
+ cp += n; |
|
+ continue; /* XXX - had_error++ ? */ |
|
+ } |
|
+ |
|
+ switch (type) |
|
+ { |
|
+ case T_A: |
|
+ case T_AAAA: |
|
+ if (__glibc_unlikely (__strcasecmp (result->h_name, bp) != 0)) |
|
+ { |
|
+ cp += n; |
|
+ continue; /* XXX - had_error++ ? */ |
|
+ } |
|
+ |
|
+ /* Stop parsing at a record whose length is incorrect. */ |
|
+ if (n != rrtype_to_rdata_length (type)) |
|
+ { |
|
+ ++had_error; |
|
+ break; |
|
+ } |
|
+ |
|
+ /* Skip records of the wrong type. */ |
|
+ if (n != result->h_length) |
|
+ { |
|
+ cp += n; |
|
+ continue; |
|
+ } |
|
+ if (!haveanswer) |
|
+ { |
|
+ int nn; |
|
+ |
|
+ /* 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; |
|
+ if (canonp != NULL) |
|
+ *canonp = bp; |
|
+ result->h_name = bp; |
|
+ nn = strlen (bp) + 1; /* for the \0 */ |
|
+ bp += nn; |
|
+ linebuflen -= nn; |
|
+ } |
|
+ |
|
+ /* Provide sufficient alignment for both address |
|
+ families. */ |
|
+ enum { align = 4 }; |
|
+ _Static_assert ((align % __alignof__ (struct in_addr)) == 0, |
|
+ "struct in_addr alignment"); |
|
+ _Static_assert ((align % __alignof__ (struct in6_addr)) == 0, |
|
+ "struct in6_addr alignment"); |
|
+ { |
|
+ char *new_bp = PTR_ALIGN_UP (bp, align); |
|
+ linebuflen -= new_bp - bp; |
|
+ bp = new_bp; |
|
+ } |
|
+ |
|
+ if (__glibc_unlikely (n > linebuflen)) |
|
+ goto too_small; |
|
+ bp = __mempcpy (*hap++ = bp, cp, n); |
|
+ cp += n; |
|
+ linebuflen -= n; |
|
+ break; |
|
+ default: |
|
+ abort (); |
|
+ } |
|
+ if (had_error == 0) |
|
+ ++haveanswer; |
|
+ } |
|
+ |
|
+ if (haveanswer > 0) |
|
+ { |
|
+ *ap = NULL; |
|
+ *hap = NULL; |
|
+ /* |
|
+ * Note: we sort even if host can take only one address |
|
+ * in its return structures - should give it the "best" |
|
+ * address in that case, not some random one |
|
+ */ |
|
+ if (haveanswer > 1 && qtype == T_A |
|
+ && __resolv_context_sort_count (ctx) > 0) |
|
+ addrsort (ctx, host_data->h_addr_ptrs, haveanswer); |
|
+ |
|
+ if (result->h_name == NULL) |
|
+ { |
|
+ n = strlen (qname) + 1; /* For the \0. */ |
|
+ if (n > linebuflen) |
|
+ goto too_small; |
|
+ if (n >= MAXHOSTNAMELEN) |
|
+ goto no_recovery; |
|
+ result->h_name = bp; |
|
+ bp = __mempcpy (bp, qname, n); /* Cannot overflow. */ |
|
+ linebuflen -= n; |
|
+ } |
|
+ |
|
+ if (have_to_map) |
|
+ if (map_v4v6_hostent (result, &bp, &linebuflen)) |
|
+ goto too_small; |
|
+ *h_errnop = NETDB_SUCCESS; |
|
+ return NSS_STATUS_SUCCESS; |
|
+ } |
|
+ no_recovery: |
|
+ *h_errnop = NO_RECOVERY; |
|
+ *errnop = ENOENT; |
|
+ /* 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. */ |
|
+ return ((qtype == T_A || qtype == T_AAAA) && ap != host_data->aliases |
|
+ ? NSS_STATUS_NOTFOUND : NSS_STATUS_TRYAGAIN); |
|
+} |
|
+ |
|
+static enum nss_status |
|
+getanswer_ptr (const querybuf *answer, int anslen, const char *qname, |
|
+ struct hostent *result, char *buffer, size_t buflen, |
|
+ int *errnop, int *h_errnop, int32_t *ttlp) |
|
+{ |
|
+ struct host_data |
|
+ { |
|
+ char *aliases[MAX_NR_ALIASES]; |
|
+ unsigned char host_addr[16]; /* IPv4 or IPv6 */ |
|
+ char *h_addr_ptrs[0]; |
|
+ } *host_data; |
|
+ int linebuflen; |
|
+ const HEADER *hp; |
|
+ const u_char *end_of_message, *cp; |
|
+ int n, ancount, qdcount; |
|
+ int haveanswer, had_error; |
|
+ char *bp, **ap, **hap; |
|
+ char tbuf[MAXDNAME]; |
|
+ const char *tname; |
|
+ u_char packtmp[NS_MAXCDNAME]; |
|
+ uintptr_t pad = -(uintptr_t) buffer % __alignof__ (struct host_data); |
|
+ buffer += pad; |
|
+ buflen = buflen > pad ? buflen - pad : 0; |
|
+ if (__glibc_unlikely (buflen < sizeof (struct host_data))) |
|
+ { |
|
+ /* The buffer is too small. */ |
|
+ too_small: |
|
+ *errnop = ERANGE; |
|
+ *h_errnop = NETDB_INTERNAL; |
|
+ return NSS_STATUS_TRYAGAIN; |
|
+ } |
|
+ host_data = (struct host_data *) buffer; |
|
+ linebuflen = buflen - sizeof (struct host_data); |
|
+ if (buflen - sizeof (struct host_data) != linebuflen) |
|
+ linebuflen = INT_MAX; |
|
+ |
|
+ tname = qname; |
|
+ result->h_name = NULL; |
|
+ end_of_message = answer->buf + anslen; |
|
+ |
|
+ /* |
|
+ * find first satisfactory answer |
|
+ */ |
|
+ hp = &answer->hdr; |
|
+ ancount = ntohs (hp->ancount); |
|
+ qdcount = ntohs (hp->qdcount); |
|
+ cp = answer->buf + HFIXEDSZ; |
|
+ if (__glibc_unlikely (qdcount != 1)) |
|
+ { |
|
+ *h_errnop = NO_RECOVERY; |
|
+ return NSS_STATUS_UNAVAIL; |
|
+ } |
|
+ if (sizeof (struct host_data) + (ancount + 1) * sizeof (char *) >= buflen) |
|
+ goto too_small; |
|
+ bp = (char *) &host_data->h_addr_ptrs[ancount + 1]; |
|
+ linebuflen -= (ancount + 1) * sizeof (char *); |
|
+ |
|
+ n = __ns_name_unpack (answer->buf, end_of_message, cp, |
|
+ packtmp, sizeof packtmp); |
|
+ if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1) |
|
+ { |
|
+ if (__glibc_unlikely (errno == EMSGSIZE)) |
|
+ goto too_small; |
|
+ |
|
+ n = -1; |
|
+ } |
|
+ |
|
+ if (__glibc_unlikely (n < 0)) |
|
+ { |
|
+ *errnop = errno; |
|
+ *h_errnop = NO_RECOVERY; |
|
+ return NSS_STATUS_UNAVAIL; |
|
+ } |
|
+ if (__glibc_unlikely (__libc_res_dnok (bp) == 0)) |
|
+ { |
|
+ errno = EBADMSG; |
|
+ *errnop = EBADMSG; |
|
+ *h_errnop = NO_RECOVERY; |
|
+ return NSS_STATUS_UNAVAIL; |
|
+ } |
|
+ cp += n + QFIXEDSZ; |
|
+ |
|
+ ap = host_data->aliases; |
|
+ *ap = NULL; |
|
+ result->h_aliases = host_data->aliases; |
|
+ hap = host_data->h_addr_ptrs; |
|
+ *hap = NULL; |
|
+ result->h_addr_list = host_data->h_addr_ptrs; |
|
+ haveanswer = 0; |
|
+ had_error = 0; |
|
+ |
|
+ while (ancount-- > 0 && cp < end_of_message && had_error == 0) |
|
+ { |
|
+ int type, class; |
|
+ |
|
+ n = __ns_name_unpack (answer->buf, end_of_message, cp, |
|
+ packtmp, sizeof packtmp); |
|
+ if (n != -1 && __ns_name_ntop (packtmp, bp, linebuflen) == -1) |
|
+ { |
|
+ if (__glibc_unlikely (errno == EMSGSIZE)) |
|
+ goto too_small; |
|
+ |
|
+ n = -1; |
|
+ } |
|
+ |
|
+ if (__glibc_unlikely (n < 0 || __libc_res_dnok (bp) == 0)) |
|
+ { |
|
+ ++had_error; |
|
+ continue; |
|
+ } |
|
+ cp += n; /* name */ |
|
+ |
|
+ if (__glibc_unlikely (cp + 10 > end_of_message)) |
|
+ { |
|
+ ++had_error; |
|
+ continue; |
|
+ } |
|
+ |
|
+ NS_GET16 (type, cp); |
|
+ NS_GET16 (class, cp); |
|
+ int32_t ttl; |
|
+ NS_GET32 (ttl, cp); |
|
+ NS_GET16 (n, cp); /* RDATA length. */ |
|
+ |
|
+ if (end_of_message - cp < n) |
|
+ { |
|
+ /* RDATA extends beyond the end of the packet. */ |
|
+ ++had_error; |
|
+ continue; |
|
+ } |
|
+ |
|
+ if (__glibc_unlikely (class != C_IN)) |
|
+ { |
|
+ /* XXX - debug? syslog? */ |
|
+ cp += n; |
|
+ continue; /* XXX - had_error++ ? */ |
|
+ } |
|
+ |
|
+ if (type == T_CNAME) |
|
{ |
|
/* A CNAME could also have a TTL entry. */ |
|
if (ttlp != NULL && ttl < *ttlp) |
|
@@ -887,14 +1128,6 @@ getanswer_r (struct resolv_context *ctx, |
|
continue; |
|
} |
|
|
|
- if (type == T_A && qtype == T_AAAA && map) |
|
- have_to_map = 1; |
|
- else if (__glibc_unlikely (type != qtype)) |
|
- { |
|
- cp += n; |
|
- continue; /* XXX - had_error++ ? */ |
|
- } |
|
- |
|
switch (type) |
|
{ |
|
case T_PTR: |
|
@@ -956,8 +1189,6 @@ getanswer_r (struct resolv_context *ctx, |
|
TTL in the chain. */ |
|
if (ttlp != NULL && ttl < *ttlp) |
|
*ttlp = ttl; |
|
- if (canonp != NULL) |
|
- *canonp = bp; |
|
result->h_name = bp; |
|
nn = strlen (bp) + 1; /* for the \0 */ |
|
bp += nn; |
|
@@ -984,7 +1215,8 @@ getanswer_r (struct resolv_context *ctx, |
|
linebuflen -= n; |
|
break; |
|
default: |
|
- abort (); |
|
+ cp += n; |
|
+ continue; /* XXX - had_error++ ? */ |
|
} |
|
if (had_error == 0) |
|
++haveanswer; |
|
@@ -994,14 +1226,6 @@ getanswer_r (struct resolv_context *ctx, |
|
{ |
|
*ap = NULL; |
|
*hap = NULL; |
|
- /* |
|
- * Note: we sort even if host can take only one address |
|
- * in its return structures - should give it the "best" |
|
- * address in that case, not some random one |
|
- */ |
|
- if (haveanswer > 1 && qtype == T_A |
|
- && __resolv_context_sort_count (ctx) > 0) |
|
- addrsort (ctx, host_data->h_addr_ptrs, haveanswer); |
|
|
|
if (result->h_name == NULL) |
|
{ |
|
@@ -1015,23 +1239,15 @@ getanswer_r (struct resolv_context *ctx, |
|
linebuflen -= n; |
|
} |
|
|
|
- if (have_to_map) |
|
- if (map_v4v6_hostent (result, &bp, &linebuflen)) |
|
- goto too_small; |
|
*h_errnop = NETDB_SUCCESS; |
|
return NSS_STATUS_SUCCESS; |
|
} |
|
no_recovery: |
|
*h_errnop = NO_RECOVERY; |
|
*errnop = ENOENT; |
|
- /* 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. */ |
|
- return ((qtype == T_A || qtype == T_AAAA) && ap != host_data->aliases |
|
- ? NSS_STATUS_NOTFOUND : NSS_STATUS_TRYAGAIN); |
|
+ return NSS_STATUS_TRYAGAIN; |
|
} |
|
|
|
- |
|
static enum nss_status |
|
gaih_getanswer_slice (const querybuf *answer, int anslen, const char *qname, |
|
struct gaih_addrtuple ***patp,
|
|
|