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
26 #include "ares_nameser.h"
27
28 #ifdef HAVE_STRINGS_H
29 # include <strings.h>
30 #endif
31
32 #include "ares.h"
33 #include "ares_dns.h"
34 #include "ares_nowarn.h"
35 #include "ares_private.h"
36
ares_parse_ptr_reply(const unsigned char * abuf,int alen,const void * addr,int addrlen,int family,struct hostent ** host)37 int ares_parse_ptr_reply(const unsigned char *abuf, int alen, const void *addr,
38 int addrlen, int family, struct hostent **host)
39 {
40 unsigned int qdcount, ancount;
41 int status, i, rr_type, rr_class, rr_len;
42 long len;
43 const unsigned char *aptr;
44 char *ptrname, *hostname, *rr_name, *rr_data;
45 struct hostent *hostent = NULL;
46 int aliascnt = 0;
47 int alias_alloc = 8;
48 char ** aliases;
49 size_t rr_data_len;
50
51 /* Set *host to NULL for all failure cases. */
52 *host = NULL;
53
54 /* Give up if abuf doesn't have room for a header. */
55 if (alen < HFIXEDSZ)
56 return ARES_EBADRESP;
57
58 /* Fetch the question and answer count from the header. */
59 qdcount = DNS_HEADER_QDCOUNT(abuf);
60 ancount = DNS_HEADER_ANCOUNT(abuf);
61 if (qdcount != 1)
62 return ARES_EBADRESP;
63
64 /* Expand the name from the question, and skip past the question. */
65 aptr = abuf + HFIXEDSZ;
66 status = ares__expand_name_for_response(aptr, abuf, alen, &ptrname, &len, 0);
67 if (status != ARES_SUCCESS)
68 return status;
69 if (aptr + len + QFIXEDSZ > abuf + alen)
70 {
71 ares_free(ptrname);
72 return ARES_EBADRESP;
73 }
74 aptr += len + QFIXEDSZ;
75
76 /* Examine each answer resource record (RR) in turn. */
77 hostname = NULL;
78 aliases = ares_malloc(alias_alloc * sizeof(char *));
79 if (!aliases)
80 {
81 ares_free(ptrname);
82 return ARES_ENOMEM;
83 }
84 for (i = 0; i < (int)ancount; i++)
85 {
86 /* Decode the RR up to the data field. */
87 status = ares__expand_name_for_response(aptr, abuf, alen, &rr_name, &len, 0);
88 if (status != ARES_SUCCESS)
89 break;
90 aptr += len;
91 if (aptr + RRFIXEDSZ > abuf + alen)
92 {
93 ares_free(rr_name);
94 status = ARES_EBADRESP;
95 break;
96 }
97 rr_type = DNS_RR_TYPE(aptr);
98 rr_class = DNS_RR_CLASS(aptr);
99 rr_len = DNS_RR_LEN(aptr);
100 aptr += RRFIXEDSZ;
101 if (aptr + rr_len > abuf + alen)
102 {
103 ares_free(rr_name);
104 status = ARES_EBADRESP;
105 break;
106 }
107
108 if (rr_class == C_IN && rr_type == T_PTR
109 && strcasecmp(rr_name, ptrname) == 0)
110 {
111 /* Decode the RR data and set hostname to it. */
112 status = ares__expand_name_for_response(aptr, abuf, alen, &rr_data,
113 &len, 1);
114 if (status != ARES_SUCCESS)
115 {
116 ares_free(rr_name);
117 break;
118 }
119 if (hostname)
120 ares_free(hostname);
121 hostname = rr_data;
122 rr_data_len = strlen(rr_data)+1;
123 aliases[aliascnt] = ares_malloc(rr_data_len * sizeof(char));
124 if (!aliases[aliascnt])
125 {
126 ares_free(rr_name);
127 status = ARES_ENOMEM;
128 break;
129 }
130 strncpy(aliases[aliascnt], rr_data, rr_data_len);
131 aliascnt++;
132 if (aliascnt >= alias_alloc) {
133 char **ptr;
134 alias_alloc *= 2;
135 ptr = ares_realloc(aliases, alias_alloc * sizeof(char *));
136 if(!ptr) {
137 ares_free(rr_name);
138 status = ARES_ENOMEM;
139 break;
140 }
141 aliases = ptr;
142 }
143 }
144
145 if (rr_class == C_IN && rr_type == T_CNAME)
146 {
147 /* Decode the RR data and replace ptrname with it. */
148 status = ares__expand_name_for_response(aptr, abuf, alen, &rr_data,
149 &len, 1);
150 if (status != ARES_SUCCESS)
151 {
152 ares_free(rr_name);
153 break;
154 }
155 ares_free(ptrname);
156 ptrname = rr_data;
157 }
158
159 ares_free(rr_name);
160 aptr += rr_len;
161 if (aptr > abuf + alen)
162 { /* LCOV_EXCL_START: already checked above */
163 status = ARES_EBADRESP;
164 break;
165 } /* LCOV_EXCL_STOP */
166 }
167
168 if (status == ARES_SUCCESS && !hostname)
169 status = ARES_ENODATA;
170 if (status == ARES_SUCCESS)
171 {
172 /* If we don't reach the end, we must have failed due to out of memory */
173 status = ARES_ENOMEM;
174
175 /* We got our answer. Allocate memory to build the host entry. */
176 hostent = ares_malloc(sizeof(*hostent));
177 if (!hostent)
178 goto fail;
179
180 /* If we don't memset here, cleanups may fail */
181 memset(hostent, 0, sizeof(*hostent));
182
183 hostent->h_addr_list = ares_malloc(2 * sizeof(char *));
184 if (!hostent->h_addr_list)
185 goto fail;
186
187
188 if (addr && addrlen) {
189 hostent->h_addr_list[0] = ares_malloc(addrlen);
190 if (!hostent->h_addr_list[0])
191 goto fail;
192 } else {
193 hostent->h_addr_list[0] = NULL;
194 }
195
196 hostent->h_aliases = ares_malloc((aliascnt+1) * sizeof (char *));
197 if (!hostent->h_aliases)
198 goto fail;
199
200 /* Fill in the hostent and return successfully. */
201 hostent->h_name = hostname;
202 for (i=0 ; i<aliascnt ; i++)
203 hostent->h_aliases[i] = aliases[i];
204 hostent->h_aliases[aliascnt] = NULL;
205 hostent->h_addrtype = aresx_sitoss(family);
206 hostent->h_length = aresx_sitoss(addrlen);
207 if (addr && addrlen)
208 memcpy(hostent->h_addr_list[0], addr, addrlen);
209 hostent->h_addr_list[1] = NULL;
210 *host = hostent;
211 ares_free(aliases);
212 ares_free(ptrname);
213
214 return ARES_SUCCESS;
215 }
216
217 fail:
218 ares_free_hostent(hostent);
219
220 for (i=0 ; i<aliascnt ; i++)
221 if (aliases[i])
222 ares_free(aliases[i]);
223 ares_free(aliases);
224 if (hostname)
225 ares_free(hostname);
226 ares_free(ptrname);
227 return status;
228 }
229