Modified license using SPDX license identifier
[platform/upstream/c-ares.git] / ares_parse_ptr_reply.c
1
2 /* Copyright 1998 by the Massachusetts Institute of Technology.
3  *
4  * Permission to use, copy, modify, and distribute this
5  * software and its documentation for any purpose and without
6  * fee is hereby granted, provided that the above copyright
7  * notice appear in all copies and that both that copyright
8  * notice and this permission notice appear in supporting
9  * documentation, and that the name of M.I.T. not be used in
10  * advertising or publicity pertaining to distribution of the
11  * software without specific, written prior permission.
12  * M.I.T. makes no representations about the suitability of
13  * this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  */
16
17 #include "ares_setup.h"
18
19 #ifdef HAVE_NETINET_IN_H
20 #  include <netinet/in.h>
21 #endif
22 #ifdef HAVE_NETDB_H
23 #  include <netdb.h>
24 #endif
25 #ifdef HAVE_ARPA_NAMESER_H
26 #  include <arpa/nameser.h>
27 #else
28 #  include "nameser.h"
29 #endif
30 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
31 #  include <arpa/nameser_compat.h>
32 #endif
33
34 #ifdef HAVE_STRINGS_H
35 #  include <strings.h>
36 #endif
37
38 #include "ares.h"
39 #include "ares_dns.h"
40 #include "ares_nowarn.h"
41 #include "ares_private.h"
42
43 int ares_parse_ptr_reply(const unsigned char *abuf, int alen, const void *addr,
44                          int addrlen, int family, struct hostent **host)
45 {
46   unsigned int qdcount, ancount;
47   int status, i, rr_type, rr_class, rr_len;
48   long len;
49   const unsigned char *aptr;
50   char *ptrname, *hostname, *rr_name, *rr_data;
51   struct hostent *hostent;
52   int aliascnt = 0;
53   int alias_alloc = 8;
54   char ** aliases;
55
56   /* Set *host to NULL for all failure cases. */
57   *host = NULL;
58
59   /* Give up if abuf doesn't have room for a header. */
60   if (alen < HFIXEDSZ)
61     return ARES_EBADRESP;
62
63   /* Fetch the question and answer count from the header. */
64   qdcount = DNS_HEADER_QDCOUNT(abuf);
65   ancount = DNS_HEADER_ANCOUNT(abuf);
66   if (qdcount != 1)
67     return ARES_EBADRESP;
68
69   /* Expand the name from the question, and skip past the question. */
70   aptr = abuf + HFIXEDSZ;
71   status = ares__expand_name_for_response(aptr, abuf, alen, &ptrname, &len);
72   if (status != ARES_SUCCESS)
73     return status;
74   if (aptr + len + QFIXEDSZ > abuf + alen)
75     {
76       free(ptrname);
77       return ARES_EBADRESP;
78     }
79   aptr += len + QFIXEDSZ;
80
81   /* Examine each answer resource record (RR) in turn. */
82   hostname = NULL;
83   aliases = malloc(alias_alloc * sizeof(char *));
84   if (!aliases)
85     {
86       free(ptrname);
87       return ARES_ENOMEM;
88     }
89   for (i = 0; i < (int)ancount; i++)
90     {
91       /* Decode the RR up to the data field. */
92       status = ares__expand_name_for_response(aptr, abuf, alen, &rr_name, &len);
93       if (status != ARES_SUCCESS)
94         break;
95       aptr += len;
96       if (aptr + RRFIXEDSZ > abuf + alen)
97         {
98           free(rr_name);
99           status = ARES_EBADRESP;
100           break;
101         }
102       rr_type = DNS_RR_TYPE(aptr);
103       rr_class = DNS_RR_CLASS(aptr);
104       rr_len = DNS_RR_LEN(aptr);
105       aptr += RRFIXEDSZ;
106       if (aptr + rr_len > abuf + alen)
107         {
108           free(rr_name);
109           status = ARES_EBADRESP;
110           break;
111         }
112
113       if (rr_class == C_IN && rr_type == T_PTR
114           && strcasecmp(rr_name, ptrname) == 0)
115         {
116           /* Decode the RR data and set hostname to it. */
117           status = ares__expand_name_for_response(aptr, abuf, alen, &rr_data,
118                                                   &len);
119           if (status != ARES_SUCCESS)
120             {
121               free(rr_name);
122               break;
123             }
124           if (hostname)
125             free(hostname);
126           hostname = rr_data;
127           aliases[aliascnt] = malloc((strlen(rr_data)+1) * sizeof(char));
128           if (!aliases[aliascnt])
129             {
130               free(rr_name);
131               status = ARES_ENOMEM;
132               break;
133             }
134           strncpy(aliases[aliascnt], rr_data, strlen(rr_data)+1);
135           aliascnt++;
136           if (aliascnt >= alias_alloc) {
137             char **ptr;
138             alias_alloc *= 2;
139             ptr = realloc(aliases, alias_alloc * sizeof(char *));
140             if(!ptr) {
141               free(rr_name);
142               status = ARES_ENOMEM;
143               break;
144             }
145             aliases = ptr;
146           }
147         }
148
149       if (rr_class == C_IN && rr_type == T_CNAME)
150         {
151           /* Decode the RR data and replace ptrname with it. */
152           status = ares__expand_name_for_response(aptr, abuf, alen, &rr_data,
153                                                   &len);
154           if (status != ARES_SUCCESS)
155             {
156               free(rr_name);
157               break;
158             }
159           free(ptrname);
160           ptrname = rr_data;
161         }
162
163       free(rr_name);
164       aptr += rr_len;
165       if (aptr > abuf + alen)
166         {
167           status = ARES_EBADRESP;
168           break;
169         }
170     }
171
172   if (status == ARES_SUCCESS && !hostname)
173     status = ARES_ENODATA;
174   if (status == ARES_SUCCESS)
175     {
176       /* We got our answer.  Allocate memory to build the host entry. */
177       hostent = malloc(sizeof(struct hostent));
178       if (hostent)
179         {
180           hostent->h_addr_list = malloc(2 * sizeof(char *));
181           if (hostent->h_addr_list)
182             {
183               hostent->h_addr_list[0] = malloc(addrlen);
184               if (hostent->h_addr_list[0])
185                 {
186                   hostent->h_aliases = malloc((aliascnt+1) * sizeof (char *));
187                   if (hostent->h_aliases)
188                     {
189                       /* Fill in the hostent and return successfully. */
190                       hostent->h_name = hostname;
191                       for (i=0 ; i<aliascnt ; i++)
192                         hostent->h_aliases[i] = aliases[i];
193                       hostent->h_aliases[aliascnt] = NULL;
194                       hostent->h_addrtype = aresx_sitoss(family);
195                       hostent->h_length = aresx_sitoss(addrlen);
196                       memcpy(hostent->h_addr_list[0], addr, addrlen);
197                       hostent->h_addr_list[1] = NULL;
198                       *host = hostent;
199                       free(aliases);
200                       free(ptrname);
201                       return ARES_SUCCESS;
202                     }
203                   free(hostent->h_addr_list[0]);
204                 }
205               free(hostent->h_addr_list);
206             }
207           free(hostent);
208         }
209       status = ARES_ENOMEM;
210     }
211   for (i=0 ; i<aliascnt ; i++)
212     if (aliases[i])
213       free(aliases[i]);
214   free(aliases);
215   if (hostname)
216     free(hostname);
217   free(ptrname);
218   return status;
219 }