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