30706328b3b2796f94545536b5c88e02c621183b
[platform/upstream/c-ares.git] / ares_parse_aaaa_reply.c
1 /* $Id$ */
2
3 /* Copyright 2005 Dominick Meglio
4  *
5  * Permission to use, copy, modify, and distribute this
6  * software and its documentation for any purpose and without
7  * fee is hereby granted, provided that the above copyright
8  * notice appear in all copies and that both that copyright
9  * notice and this permission notice appear in supporting
10  * documentation, and that the name of M.I.T. not be used in
11  * advertising or publicity pertaining to distribution of the
12  * software without specific, written prior permission.
13  * M.I.T. makes no representations about the suitability of
14  * this software for any purpose.  It is provided "as is"
15  * without express or implied warranty.
16  */
17
18 #include "setup.h"
19
20 #if defined(WIN32) && !defined(WATT32)
21 #include "nameser.h"
22 #else
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 #include <netdb.h>
27 #ifdef HAVE_ARPA_NAMESER_H
28 #include <arpa/nameser.h>
29 #endif
30 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
31 #include <arpa/nameser_compat.h>
32 #endif
33 #endif
34 #ifdef HAVE_STRINGS_H
35 #include <strings.h>
36 #endif
37
38 #include <stdlib.h>
39 #include <string.h>
40 #include <limits.h>
41 #include "ares.h"
42 #include "ares_dns.h"
43 #include "inet_net_pton.h"
44 #include "ares_private.h"
45
46 int ares_parse_aaaa_reply(const unsigned char *abuf, int alen,
47                           struct hostent **host, struct addr6ttl *addrttls,
48                           int *naddrttls)
49 {
50   unsigned int qdcount, ancount;
51   int status, i, rr_type, rr_class, rr_len, rr_ttl, naddrs;
52   int cname_ttl = INT_MAX;  /* the TTL imposed by the CNAME chain */
53   int naliases;
54   long len;
55   const unsigned char *aptr;
56   char *hostname, *rr_name, *rr_data, **aliases;
57   struct in6_addr *addrs;
58   struct hostent *hostent;
59   const int max_addr_ttls = (addrttls && naddrttls) ? *naddrttls : 0;
60
61   /* Set *host to NULL for all failure cases. */
62   if (host)
63     *host = NULL;
64   /* Same with *naddrttls. */
65   if (naddrttls)
66     *naddrttls = 0;
67
68   /* Give up if abuf doesn't have room for a header. */
69   if (alen < HFIXEDSZ)
70     return ARES_EBADRESP;
71
72   /* Fetch the question and answer count from the header. */
73   qdcount = DNS_HEADER_QDCOUNT(abuf);
74   ancount = DNS_HEADER_ANCOUNT(abuf);
75   if (qdcount != 1)
76     return ARES_EBADRESP;
77
78   /* Expand the name from the question, and skip past the question. */
79   aptr = abuf + HFIXEDSZ;
80   status = ares_expand_name(aptr, abuf, alen, &hostname, &len);
81   if (status != ARES_SUCCESS)
82     return status;
83   if (aptr + len + QFIXEDSZ > abuf + alen)
84     {
85       free(hostname);
86       return ARES_EBADRESP;
87     }
88   aptr += len + QFIXEDSZ;
89
90   /* Allocate addresses and aliases; ancount gives an upper bound for both. */
91   if (host)
92     {
93       addrs = malloc(ancount * sizeof(struct in6_addr));
94       if (!addrs)
95         {
96           free(hostname);
97           return ARES_ENOMEM;
98         }
99       aliases = malloc((ancount + 1) * sizeof(char *));
100       if (!aliases)
101         {
102           free(hostname);
103           free(addrs);
104           return ARES_ENOMEM;
105         }
106     }
107   else
108     {
109       addrs = NULL;
110       aliases = NULL;
111     }
112   naddrs = 0;
113   naliases = 0;
114
115   /* Examine each answer resource record (RR) in turn. */
116   for (i = 0; i < (int)ancount; i++)
117     {
118       /* Decode the RR up to the data field. */
119       status = ares_expand_name(aptr, abuf, alen, &rr_name, &len);
120       if (status != ARES_SUCCESS)
121         break;
122       aptr += len;
123       if (aptr + RRFIXEDSZ > abuf + alen)
124         {
125           status = ARES_EBADRESP;
126           break;
127         }
128       rr_type = DNS_RR_TYPE(aptr);
129       rr_class = DNS_RR_CLASS(aptr);
130       rr_len = DNS_RR_LEN(aptr);
131       rr_ttl = DNS_RR_TTL(aptr);
132       aptr += RRFIXEDSZ;
133
134       if (rr_class == C_IN && rr_type == T_AAAA
135           && rr_len == sizeof(struct in6_addr)
136           && strcasecmp(rr_name, hostname) == 0)
137         {
138           if (addrs)
139             {
140               if (aptr + sizeof(struct in6_addr) > abuf + alen)
141               {
142                 status = ARES_EBADRESP;
143                 break;
144               }
145               memcpy(&addrs[naddrs], aptr, sizeof(struct in6_addr));
146             }
147           if (naddrs < max_addr_ttls)
148             {
149               struct addr6ttl * const at = &addrttls[naddrs];
150               if (aptr + sizeof(struct in6_addr) > abuf + alen)
151               {
152                 status = ARES_EBADRESP;
153                 break;
154               }
155               memcpy(&at->ip6addr, aptr,  sizeof(struct in6_addr));
156               at->ttl = rr_ttl;
157             }
158           naddrs++;
159           status = ARES_SUCCESS;
160         }
161
162       if (rr_class == C_IN && rr_type == T_CNAME)
163         {
164           /* Record the RR name as an alias. */
165           if (aliases)
166             aliases[naliases] = rr_name;
167           else
168             free(rr_name);
169           naliases++;
170
171           /* Decode the RR data and replace the hostname with it. */
172           status = ares_expand_name(aptr, abuf, alen, &rr_data, &len);
173           if (status != ARES_SUCCESS)
174             break;
175           free(hostname);
176           hostname = rr_data;
177
178           /* Take the min of the TTLs we see in the CNAME chain. */
179           if (cname_ttl > rr_ttl)
180             cname_ttl = rr_ttl;
181         }
182       else
183         free(rr_name);
184
185       aptr += rr_len;
186       if (aptr > abuf + alen)
187         {
188           status = ARES_EBADRESP;
189           break;
190         }
191     }
192
193   if (status == ARES_SUCCESS && naddrs == 0)
194     status = ARES_ENODATA;
195   if (status == ARES_SUCCESS)
196     {
197       /* We got our answer. */
198       if (naddrttls)
199         {
200           const int n = naddrs < max_addr_ttls ? naddrs : max_addr_ttls;
201           for (i = 0; i < n; i++)
202             {
203               /* Ensure that each A TTL is no larger than the CNAME TTL. */
204               if (addrttls[i].ttl > cname_ttl)
205                 addrttls[i].ttl = cname_ttl;
206             }
207           *naddrttls = n;
208         }
209       if (aliases)
210         aliases[naliases] = NULL;
211       if (host)
212         {
213           /* Allocate memory to build the host entry. */
214           hostent = malloc(sizeof(struct hostent));
215           if (hostent)
216             {
217               hostent->h_addr_list = malloc((naddrs + 1) * sizeof(char *));
218               if (hostent->h_addr_list)
219                 {
220                   /* Fill in the hostent and return successfully. */
221                   hostent->h_name = hostname;
222                   hostent->h_aliases = aliases;
223                   hostent->h_addrtype = AF_INET6;
224                   hostent->h_length = sizeof(struct in6_addr);
225                   for (i = 0; i < naddrs; i++)
226                     hostent->h_addr_list[i] = (char *) &addrs[i];
227                   hostent->h_addr_list[naddrs] = NULL;
228                   *host = hostent;
229                   return ARES_SUCCESS;
230                 }
231               free(hostent);
232             }
233           status = ARES_ENOMEM;
234         }
235     }
236   if (aliases)
237     {
238       for (i = 0; i < naliases; i++)
239         free(aliases[i]);
240       free(aliases);
241     }
242   free(addrs);
243   free(hostname);
244   return status;
245 }