From b4bdb6d4d7915bf5e1a89b6ae19f7ffbcd2e8144 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Thu, 15 Nov 2007 08:36:25 +0000 Subject: [PATCH] Return TTL data from ares_parse_{a,aaaa}_reply, if the user is so inclined. Patch from the Google tree. --- ares.h | 23 ++++++- ares_gethostbyname.c | 4 +- ares_parse_a_reply.3 | 21 ++++++- ares_parse_a_reply.c | 134 +++++++++++++++++++++++++++++----------- ares_parse_aaaa_reply.3 | 21 ++++++- ares_parse_aaaa_reply.c | 128 ++++++++++++++++++++++++++++---------- 6 files changed, 253 insertions(+), 78 deletions(-) diff --git a/ares.h b/ares.h index af7df703..5e343101 100644 --- a/ares.h +++ b/ares.h @@ -240,10 +240,29 @@ int ares_expand_name(const unsigned char *encoded, const unsigned char *abuf, int alen, char **s, long *enclen); int ares_expand_string(const unsigned char *encoded, const unsigned char *abuf, int alen, unsigned char **s, long *enclen); + +struct addrttl { + struct in_addr ipaddr; + int ttl; +}; +struct addr6ttl { + struct in6_addr ip6addr; + int ttl; +}; + +/* +** Parse the buffer, starting at *abuf and of length alen bytes, previously +** obtained from an ares_search call. Put the results in *host, if nonnull. +** Also, if addrttls is nonnull, put up to *naddrttls IPv4 addresses along with +** their TTLs in that array, and set *naddrttls to the number of addresses +** so written. +*/ int ares_parse_a_reply(const unsigned char *abuf, int alen, - struct hostent **host); + struct hostent **host, + struct addrttl *addrttls, int *naddrttls); int ares_parse_aaaa_reply(const unsigned char *abuf, int alen, - struct hostent **host); + struct hostent **host, + struct addr6ttl *addrttls, int *naddrttls); int ares_parse_ptr_reply(const unsigned char *abuf, int alen, const void *addr, int addrlen, int family, struct hostent **host); int ares_parse_ns_reply(const unsigned char *abuf, int alen, diff --git a/ares_gethostbyname.c b/ares_gethostbyname.c index bdc87919..b96ac6bf 100644 --- a/ares_gethostbyname.c +++ b/ares_gethostbyname.c @@ -160,13 +160,13 @@ static void host_callback(void *arg, int status, int timeouts, { if (hquery->family == AF_INET) { - status = ares_parse_a_reply(abuf, alen, &host); + status = ares_parse_a_reply(abuf, alen, &host, NULL, NULL); if (host && channel->nsort) sort_addresses(host, channel->sortlist, channel->nsort); } else if (hquery->family == AF_INET6) { - status = ares_parse_aaaa_reply(abuf, alen, &host); + status = ares_parse_aaaa_reply(abuf, alen, &host, NULL, NULL); if (host && channel->nsort) sort6_addresses(host, channel->sortlist, channel->nsort); } diff --git a/ares_parse_a_reply.3 b/ares_parse_a_reply.3 index 7acd70fe..fff8e232 100644 --- a/ares_parse_a_reply.3 +++ b/ares_parse_a_reply.3 @@ -22,24 +22,39 @@ ares_parse_a_reply \- Parse a reply to a DNS query of type A into a hostent .B #include .PP .B int ares_parse_a_reply(const unsigned char *\fIabuf\fP, int \fIalen\fP, -.B struct hostent **\fIhost\fP); +.B struct hostent **\fIhost\fP, +.B struct addrttl *\fIaddrttls\fB, int *\fInaddrttls\fB); .fi .SH DESCRIPTION The .B ares_parse_a_reply function parses the response to a query of type A into a -.BR "struct hostent" . +.BR "struct hostent" +and/or an array of +.BR "struct addrttls" . The parameters .I abuf and .I alen give the contents of the response. The result is stored in allocated memory and a pointer to it stored into the variable pointed to by -.IR host . +.IR host , +if host is nonnull. It is the caller's responsibility to free the resulting host structure using .BR ares_free_hostent (3) when it is no longer needed. +.PP +If +.IR addrttls +and +.IR naddrttls +are both nonnull, +then up to *naddrttls +.BR "struct addrttl" +records are stored in the array pointed to by addrttls, +and then *naddrttls is set to the number of records so stored. +Note that the memory for these records is supplied by the caller. .SH RETURN VALUES .B ares_parse_a_reply can return any of the following values: diff --git a/ares_parse_a_reply.c b/ares_parse_a_reply.c index 6f427502..4dea80ea 100644 --- a/ares_parse_a_reply.c +++ b/ares_parse_a_reply.c @@ -37,19 +37,26 @@ #include "ares_private.h" int ares_parse_a_reply(const unsigned char *abuf, int alen, - struct hostent **host) + struct hostent **host, + struct addrttl *addrttls, int *naddrttls) { unsigned int qdcount, ancount; - int status, i, rr_type, rr_class, rr_len, naddrs; + int status, i, rr_type, rr_class, rr_len, rr_ttl, naddrs; + int cname_ttl = INT_MAX; /* the TTL imposed by the CNAME chain */ int naliases; long len; const unsigned char *aptr; char *hostname, *rr_name, *rr_data, **aliases; struct in_addr *addrs; struct hostent *hostent; + const int max_addr_ttls = (addrttls && naddrttls) ? *naddrttls : 0; /* Set *host to NULL for all failure cases. */ - *host = NULL; + if (host) + *host = NULL; + /* Same with *naddrttls. */ + if (naddrttls) + *naddrttls = 0; /* Give up if abuf doesn't have room for a header. */ if (alen < HFIXEDSZ) @@ -73,20 +80,29 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen, } aptr += len + QFIXEDSZ; - /* Allocate addresses and aliases; ancount gives an upper bound for both. */ - addrs = malloc(ancount * sizeof(struct in_addr)); - if (!addrs) + if (host) { - free(hostname); - return ARES_ENOMEM; + /* Allocate addresses and aliases; ancount gives an upper bound for both. */ + addrs = malloc(ancount * sizeof(struct in_addr)); + if (!addrs) + { + free(hostname); + return ARES_ENOMEM; + } + aliases = malloc((ancount + 1) * sizeof(char *)); + if (!aliases) + { + free(hostname); + free(addrs); + return ARES_ENOMEM; + } } - aliases = malloc((ancount + 1) * sizeof(char *)); - if (!aliases) + else { - free(hostname); - free(addrs); - return ARES_ENOMEM; + addrs = NULL; + aliases = NULL; } + naddrs = 0; naliases = 0; @@ -106,13 +122,33 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen, rr_type = DNS_RR_TYPE(aptr); rr_class = DNS_RR_CLASS(aptr); rr_len = DNS_RR_LEN(aptr); + rr_ttl = DNS_RR_TTL(aptr); aptr += RRFIXEDSZ; if (rr_class == C_IN && rr_type == T_A && rr_len == sizeof(struct in_addr) && strcasecmp(rr_name, hostname) == 0) { - memcpy(&addrs[naddrs], aptr, sizeof(struct in_addr)); + if (addrs) + { + if (aptr + sizeof(struct in_addr) > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + memcpy(&addrs[naddrs], aptr, sizeof(struct in_addr)); + } + if (naddrs < max_addr_ttls) + { + struct addrttl * const at = &addrttls[naddrs]; + if (aptr + sizeof(struct in_addr) > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + memcpy(&at->ipaddr, aptr, sizeof(struct in_addr)); + at->ttl = rr_ttl; + } naddrs++; status = ARES_SUCCESS; } @@ -120,7 +156,10 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen, if (rr_class == C_IN && rr_type == T_CNAME) { /* Record the RR name as an alias. */ - aliases[naliases] = rr_name; + if (aliases) + aliases[naliases] = rr_name; + else + free(rr_name); naliases++; /* Decode the RR data and replace the hostname with it. */ @@ -129,6 +168,10 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen, break; free(hostname); hostname = rr_data; + + /* Take the min of the TTLs we see in the CNAME chain. */ + if (cname_ttl > rr_ttl) + cname_ttl = rr_ttl; } else free(rr_name); @@ -145,32 +188,51 @@ int ares_parse_a_reply(const unsigned char *abuf, int alen, status = ARES_ENODATA; if (status == ARES_SUCCESS) { - /* We got our answer. Allocate memory to build the host entry. */ - aliases[naliases] = NULL; - hostent = malloc(sizeof(struct hostent)); - if (hostent) + /* We got our answer. */ + if (naddrttls) { - hostent->h_addr_list = malloc((naddrs + 1) * sizeof(char *)); - if (hostent->h_addr_list) + const int n = naddrs < max_addr_ttls ? naddrs : max_addr_ttls; + for (i = 0; i < n; i++) { - /* Fill in the hostent and return successfully. */ - hostent->h_name = hostname; - hostent->h_aliases = aliases; - hostent->h_addrtype = AF_INET; - hostent->h_length = sizeof(struct in_addr); - for (i = 0; i < naddrs; i++) - hostent->h_addr_list[i] = (char *) &addrs[i]; - hostent->h_addr_list[naddrs] = NULL; - *host = hostent; - return ARES_SUCCESS; + /* Ensure that each A TTL is no larger than the CNAME TTL. */ + if (addrttls[i].ttl > cname_ttl) + addrttls[i].ttl = cname_ttl; } - free(hostent); + *naddrttls = n; } - status = ARES_ENOMEM; + if (aliases) + aliases[naliases] = NULL; + if (host) + { + /* Allocate memory to build the host entry. */ + hostent = malloc(sizeof(struct hostent)); + if (hostent) + { + hostent->h_addr_list = malloc((naddrs + 1) * sizeof(char *)); + if (hostent->h_addr_list) + { + /* Fill in the hostent and return successfully. */ + hostent->h_name = hostname; + hostent->h_aliases = aliases; + hostent->h_addrtype = AF_INET; + hostent->h_length = sizeof(struct in_addr); + for (i = 0; i < naddrs; i++) + hostent->h_addr_list[i] = (char *) &addrs[i]; + hostent->h_addr_list[naddrs] = NULL; + *host = hostent; + return ARES_SUCCESS; + } + free(hostent); + } + status = ARES_ENOMEM; + } + } + if (aliases) + { + for (i = 0; i < naliases; i++) + free(aliases[i]); + free(aliases); } - for (i = 0; i < naliases; i++) - free(aliases[i]); - free(aliases); free(addrs); free(hostname); return status; diff --git a/ares_parse_aaaa_reply.3 b/ares_parse_aaaa_reply.3 index 15ecd5e9..3b80295f 100644 --- a/ares_parse_aaaa_reply.3 +++ b/ares_parse_aaaa_reply.3 @@ -22,24 +22,39 @@ ares_parse_aaaa_reply \- Parse a reply to a DNS query of type AAAA into a hosten .B #include .PP .B int ares_parse_aaaa_reply(const unsigned char *\fIabuf\fP, int \fIalen\fP, -.B struct hostent **\fIhost\fP); +.B struct hostent **\fIhost\fP, +.B struct addrttl *\fIaddrttls\fB, int *\fInaddrttls\fB); .fi .SH DESCRIPTION The .B ares_parse_aaaa_reply function parses the response to a query of type AAAA into a -.BR "struct hostent" . +.BR "struct hostent" +and/or an array of +.BR "struct addrttls" . The parameters .I abuf and .I alen give the contents of the response. The result is stored in allocated memory and a pointer to it stored into the variable pointed to by -.IR host . +.IR host , +if host is nonnull. It is the caller's responsibility to free the resulting host structure using .BR ares_free_hostent (3) when it is no longer needed. +.PP +If +.IR addrttls +and +.IR naddrttls +are both nonnull, +then up to *naddrttls +.BR "struct addr6ttl" +records are stored in the array pointed to by addrttls, +and then *naddrttls is set to the number of records so stored. +Note that the memory for these records is supplied by the caller. .SH RETURN VALUES .B ares_parse_aaaa_reply can return any of the following values: diff --git a/ares_parse_aaaa_reply.c b/ares_parse_aaaa_reply.c index 535d40ee..3b578f15 100644 --- a/ares_parse_aaaa_reply.c +++ b/ares_parse_aaaa_reply.c @@ -40,19 +40,26 @@ #include "ares_private.h" int ares_parse_aaaa_reply(const unsigned char *abuf, int alen, - struct hostent **host) + struct hostent **host, struct addr6ttl *addrttls, + int *naddrttls) { unsigned int qdcount, ancount; - int status, i, rr_type, rr_class, rr_len, naddrs; + int status, i, rr_type, rr_class, rr_len, rr_ttl, naddrs; + int cname_ttl = INT_MAX; /* the TTL imposed by the CNAME chain */ int naliases; long len; const unsigned char *aptr; char *hostname, *rr_name, *rr_data, **aliases; struct in6_addr *addrs; struct hostent *hostent; + const int max_addr_ttls = (addrttls && naddrttls) ? *naddrttls : 0; /* Set *host to NULL for all failure cases. */ - *host = NULL; + if (host) + *host = NULL; + /* Same with *naddrttls. */ + if (naddrttls) + *naddrttls = 0; /* Give up if abuf doesn't have room for a header. */ if (alen < HFIXEDSZ) @@ -77,18 +84,26 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen, aptr += len + QFIXEDSZ; /* Allocate addresses and aliases; ancount gives an upper bound for both. */ - addrs = malloc(ancount * sizeof(struct in6_addr)); - if (!addrs) + if (host) { - free(hostname); - return ARES_ENOMEM; + addrs = malloc(ancount * sizeof(struct in6_addr)); + if (!addrs) + { + free(hostname); + return ARES_ENOMEM; + } + aliases = malloc((ancount + 1) * sizeof(char *)); + if (!aliases) + { + free(hostname); + free(addrs); + return ARES_ENOMEM; + } } - aliases = malloc((ancount + 1) * sizeof(char *)); - if (!aliases) + else { - free(hostname); - free(addrs); - return ARES_ENOMEM; + addrs = NULL; + aliases = NULL; } naddrs = 0; naliases = 0; @@ -109,13 +124,33 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen, rr_type = DNS_RR_TYPE(aptr); rr_class = DNS_RR_CLASS(aptr); rr_len = DNS_RR_LEN(aptr); + rr_ttl = DNS_RR_TTL(aptr); aptr += RRFIXEDSZ; if (rr_class == C_IN && rr_type == T_AAAA && rr_len == sizeof(struct in6_addr) && strcasecmp(rr_name, hostname) == 0) { - memcpy(&addrs[naddrs], aptr, sizeof(struct in6_addr)); + if (addrs) + { + if (aptr + sizeof(struct in6_addr) > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + memcpy(&addrs[naddrs], aptr, sizeof(struct in6_addr)); + } + if (naddrs < max_addr_ttls) + { + struct addr6ttl * const at = &addrttls[naddrs]; + if (aptr + sizeof(struct in6_addr) > abuf + alen) + { + status = ARES_EBADRESP; + break; + } + memcpy(&at->ip6addr, aptr, sizeof(struct in6_addr)); + at->ttl = rr_ttl; + } naddrs++; status = ARES_SUCCESS; } @@ -123,7 +158,10 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen, if (rr_class == C_IN && rr_type == T_CNAME) { /* Record the RR name as an alias. */ - aliases[naliases] = rr_name; + if (aliases) + aliases[naliases] = rr_name; + else + free(rr_name); naliases++; /* Decode the RR data and replace the hostname with it. */ @@ -132,6 +170,10 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen, break; free(hostname); hostname = rr_data; + + /* Take the min of the TTLs we see in the CNAME chain. */ + if (cname_ttl > rr_ttl) + cname_ttl = rr_ttl; } else free(rr_name); @@ -148,28 +190,50 @@ int ares_parse_aaaa_reply(const unsigned char *abuf, int alen, status = ARES_ENODATA; if (status == ARES_SUCCESS) { - /* We got our answer. Allocate memory to build the host entry. */ - aliases[naliases] = NULL; - hostent = malloc(sizeof(struct hostent)); - if (hostent) + /* We got our answer. */ + if (naddrttls) { - hostent->h_addr_list = malloc((naddrs + 1) * sizeof(char *)); - if (hostent->h_addr_list) + const int n = naddrs < max_addr_ttls ? naddrs : max_addr_ttls; + for (i = 0; i < n; i++) { - /* Fill in the hostent and return successfully. */ - hostent->h_name = hostname; - hostent->h_aliases = aliases; - hostent->h_addrtype = AF_INET6; - hostent->h_length = sizeof(struct in6_addr); - for (i = 0; i < naddrs; i++) - hostent->h_addr_list[i] = (char *) &addrs[i]; - hostent->h_addr_list[naddrs] = NULL; - *host = hostent; - return ARES_SUCCESS; + /* Ensure that each A TTL is no larger than the CNAME TTL. */ + if (addrttls[i].ttl > cname_ttl) + addrttls[i].ttl = cname_ttl; } - free(hostent); + *naddrttls = n; } - status = ARES_ENOMEM; + if (aliases) + aliases[naliases] = NULL; + if (host) + { + /* Allocate memory to build the host entry. */ + hostent = malloc(sizeof(struct hostent)); + if (hostent) + { + hostent->h_addr_list = malloc((naddrs + 1) * sizeof(char *)); + if (hostent->h_addr_list) + { + /* Fill in the hostent and return successfully. */ + hostent->h_name = hostname; + hostent->h_aliases = aliases; + hostent->h_addrtype = AF_INET6; + hostent->h_length = sizeof(struct in6_addr); + for (i = 0; i < naddrs; i++) + hostent->h_addr_list[i] = (char *) &addrs[i]; + hostent->h_addr_list[naddrs] = NULL; + *host = hostent; + return ARES_SUCCESS; + } + free(hostent); + } + status = ARES_ENOMEM; + } + } + if (aliases) + { + for (i = 0; i < naliases; i++) + free(aliases[i]); + free(aliases); } for (i = 0; i < naliases; i++) free(aliases[i]);