removed -fpack-struct because gcc4 seems to know its obsolete and warns...
[platform/upstream/c-ares.git] / ares_parse_aaaa_reply.c
1 /* Copyright 2005 Dominick Meglio
2  *
3  * Permission to use, copy, modify, and distribute this
4  * software and its documentation for any purpose and without
5  * fee is hereby granted, provided that the above copyright
6  * notice appear in all copies and that both that copyright
7  * notice and this permission notice appear in supporting
8  * documentation, and that the name of M.I.T. not be used in
9  * advertising or publicity pertaining to distribution of the
10  * software without specific, written prior permission.
11  * M.I.T. makes no representations about the suitability of
12  * this software for any purpose.  It is provided "as is"
13  * without express or implied warranty.
14  */
15
16 #include "setup.h"
17 #include <sys/types.h>
18
19 #if defined(WIN32) && !defined(WATT32)
20 #include "nameser.h"
21 #else
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
25 #include <netdb.h>
26 #ifdef HAVE_ARPA_NAMESER_H
27 #include <arpa/nameser.h>
28 #endif
29 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
30 #include <arpa/nameser_compat.h>
31 #endif
32 #endif
33
34 #include <stdlib.h>
35 #include <string.h>
36 #include "ares.h"
37 #include "ares_dns.h"
38 #include "inet_net_pton.h"
39 #include "ares_private.h"
40
41 int ares_parse_aaaa_reply(const unsigned char *abuf, int alen,
42                        struct hostent **host)
43 {
44   unsigned int qdcount, ancount;
45   int status, i, rr_type, rr_class, rr_len, naddrs;
46   int naliases;
47   long len;
48   const unsigned char *aptr;
49   char *hostname, *rr_name, *rr_data, **aliases;
50   struct in6_addr *addrs;
51   struct hostent *hostent;
52
53   /* Set *host to NULL for all failure cases. */
54   *host = NULL;
55
56   /* Give up if abuf doesn't have room for a header. */
57   if (alen < HFIXEDSZ)
58     return ARES_EBADRESP;
59
60   /* Fetch the question and answer count from the header. */
61   qdcount = DNS_HEADER_QDCOUNT(abuf);
62   ancount = DNS_HEADER_ANCOUNT(abuf);
63   if (qdcount != 1)
64     return ARES_EBADRESP;
65
66   /* Expand the name from the question, and skip past the question. */
67   aptr = abuf + HFIXEDSZ;
68   status = ares_expand_name(aptr, abuf, alen, &hostname, &len);
69   if (status != ARES_SUCCESS)
70     return status;
71   if (aptr + len + QFIXEDSZ > abuf + alen)
72     {
73       free(hostname);
74       return ARES_EBADRESP;
75     }
76   aptr += len + QFIXEDSZ;
77
78   /* Allocate addresses and aliases; ancount gives an upper bound for both. */
79   addrs = malloc(ancount * sizeof(struct in6_addr));
80   if (!addrs)
81     {
82       free(hostname);
83       return ARES_ENOMEM;
84     }
85   aliases = malloc((ancount + 1) * sizeof(char *));
86   if (!aliases)
87     {
88       free(hostname);
89       free(addrs);
90       return ARES_ENOMEM;
91     }
92   naddrs = 0;
93   naliases = 0;
94
95   /* Examine each answer resource record (RR) in turn. */
96   for (i = 0; i < (int)ancount; i++)
97     {
98       /* Decode the RR up to the data field. */
99       status = ares_expand_name(aptr, abuf, alen, &rr_name, &len);
100       if (status != ARES_SUCCESS)
101         break;
102       aptr += len;
103       if (aptr + RRFIXEDSZ > abuf + alen)
104         {
105           status = ARES_EBADRESP;
106           break;
107         }
108       rr_type = DNS_RR_TYPE(aptr);
109       rr_class = DNS_RR_CLASS(aptr);
110       rr_len = DNS_RR_LEN(aptr);
111       aptr += RRFIXEDSZ;
112
113       if (rr_class == C_IN && rr_type == T_AAAA
114           && rr_len == sizeof(struct in6_addr)
115           && strcasecmp(rr_name, hostname) == 0)
116         {
117           memcpy(&addrs[naddrs], aptr, sizeof(struct in6_addr));
118           naddrs++;
119           status = ARES_SUCCESS;
120         }
121
122       if (rr_class == C_IN && rr_type == T_CNAME)
123         {
124           /* Record the RR name as an alias. */
125           aliases[naliases] = rr_name;
126           naliases++;
127
128           /* Decode the RR data and replace the hostname with it. */
129           status = ares_expand_name(aptr, abuf, alen, &rr_data, &len);
130           if (status != ARES_SUCCESS)
131             break;
132           free(hostname);
133           hostname = rr_data;
134         }
135       else
136         free(rr_name);
137
138       aptr += rr_len;
139       if (aptr > abuf + alen)
140         {
141           status = ARES_EBADRESP;
142           break;
143         }
144     }
145
146   if (status == ARES_SUCCESS && naddrs == 0)
147     status = ARES_ENODATA;
148   if (status == ARES_SUCCESS)
149     {
150       /* We got our answer.  Allocate memory to build the host entry. */
151       aliases[naliases] = NULL;
152       hostent = malloc(sizeof(struct hostent));
153       if (hostent)
154         {
155           hostent->h_addr_list = malloc((naddrs + 1) * sizeof(char *));
156           if (hostent->h_addr_list)
157             {
158               /* Fill in the hostent and return successfully. */
159               hostent->h_name = hostname;
160               hostent->h_aliases = aliases;
161               hostent->h_addrtype = AF_INET6;
162               hostent->h_length = sizeof(struct in6_addr);
163               for (i = 0; i < naddrs; i++)
164                 hostent->h_addr_list[i] = (char *) &addrs[i];
165               hostent->h_addr_list[naddrs] = NULL;
166               *host = hostent;
167               return ARES_SUCCESS;
168             }
169           free(hostent);
170         }
171       status = ARES_ENOMEM;
172     }
173   for (i = 0; i < naliases; i++)
174     free(aliases[i]);
175   free(aliases);
176   free(addrs);
177   free(hostname);
178   return status;
179 }