/src/c-ares/src/lib/ares__hosts_file.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* MIT License |
2 | | * |
3 | | * Copyright (c) 2023 Brad House |
4 | | * |
5 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
6 | | * of this software and associated documentation files (the "Software"), to deal |
7 | | * in the Software without restriction, including without limitation the rights |
8 | | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
9 | | * copies of the Software, and to permit persons to whom the Software is |
10 | | * furnished to do so, subject to the following conditions: |
11 | | * |
12 | | * The above copyright notice and this permission notice (including the next |
13 | | * paragraph) shall be included in all copies or substantial portions of the |
14 | | * Software. |
15 | | * |
16 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
19 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
20 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
21 | | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
22 | | * SOFTWARE. |
23 | | * |
24 | | * SPDX-License-Identifier: MIT |
25 | | */ |
26 | | #include "ares_setup.h" |
27 | | #include "ares.h" |
28 | | #include "ares_private.h" |
29 | | #ifdef HAVE_SYS_TYPES_H |
30 | | # include <sys/types.h> |
31 | | #endif |
32 | | #ifdef HAVE_SYS_STAT_H |
33 | | # include <sys/stat.h> |
34 | | #endif |
35 | | #ifdef HAVE_NETINET_IN_H |
36 | | # include <netinet/in.h> |
37 | | #endif |
38 | | #ifdef HAVE_NETDB_H |
39 | | # include <netdb.h> |
40 | | #endif |
41 | | #ifdef HAVE_ARPA_INET_H |
42 | | # include <arpa/inet.h> |
43 | | #endif |
44 | | #include <time.h> |
45 | | #include "ares_platform.h" |
46 | | |
47 | | /* HOSTS FILE PROCESSING OVERVIEW |
48 | | * ============================== |
49 | | * The hosts file on the system contains static entries to be processed locally |
50 | | * rather than querying the nameserver. Each row is an IP address followed by |
51 | | * a list of space delimited hostnames that match the ip address. This is used |
52 | | * for both forward and reverse lookups. |
53 | | * |
54 | | * We are caching the entire parsed hosts file for performance reasons. Some |
55 | | * files may be quite sizable and as per Issue #458 can approach 1/2MB in size, |
56 | | * and the parse overhead on a rapid succession of queries can be quite large. |
57 | | * The entries are stored in forwards and backwards hashtables so we can get |
58 | | * O(1) performance on lookup. The file is cached until the file modification |
59 | | * timestamp changes. |
60 | | * |
61 | | * The hosts file processing is quite unique. It has to merge all related hosts |
62 | | * and ips into a single entry due to file formatting requirements. For |
63 | | * instance take the below: |
64 | | * |
65 | | * 127.0.0.1 localhost.localdomain localhost |
66 | | * ::1 localhost.localdomain localhost |
67 | | * 192.168.1.1 host.example.com host |
68 | | * 192.168.1.5 host.example.com host |
69 | | * 2620:1234::1 host.example.com host6.example.com host6 host |
70 | | * |
71 | | * This will yield 2 entries. |
72 | | * 1) ips: 127.0.0.1,::1 |
73 | | * hosts: localhost.localdomain,localhost |
74 | | * 2) ips: 192.168.1.1,192.168.1.5,2620:1234::1 |
75 | | * hosts: host.example.com,host,host6.example.com,host6 |
76 | | * |
77 | | * It could be argued that if searching for 192.168.1.1 that the 'host6' |
78 | | * hostnames should not be returned, but this implementation will return them |
79 | | * since they are related. It is unlikely this will matter in the real world. |
80 | | */ |
81 | | |
82 | | struct ares_hosts_file { |
83 | | time_t ts; |
84 | | /*! cache the filename so we know if the filename changes it automatically |
85 | | * invalidates the cache */ |
86 | | char *filename; |
87 | | /*! iphash is the owner of the 'entry' object as there is only ever a single |
88 | | * match to the object. */ |
89 | | ares__htable_strvp_t *iphash; |
90 | | /*! hosthash does not own the entry so won't free on destruction */ |
91 | | ares__htable_strvp_t *hosthash; |
92 | | }; |
93 | | |
94 | | struct ares_hosts_entry { |
95 | | size_t refcnt; /*! If the entry is stored multiple times in the |
96 | | * ip address hash, we have to reference count it */ |
97 | | ares__llist_t *ips; |
98 | | ares__llist_t *hosts; |
99 | | }; |
100 | | |
101 | | static ares_status_t ares__read_file_into_buf(const char *filename, |
102 | | ares__buf_t *buf) |
103 | 0 | { |
104 | 0 | FILE *fp = NULL; |
105 | 0 | unsigned char *ptr = NULL; |
106 | 0 | size_t len = 0; |
107 | 0 | size_t ptr_len = 0; |
108 | 0 | long ftell_len = 0; |
109 | 0 | ares_status_t status; |
110 | |
|
111 | 0 | if (filename == NULL || buf == NULL) { |
112 | 0 | return ARES_EFORMERR; |
113 | 0 | } |
114 | | |
115 | 0 | fp = fopen(filename, "rb"); |
116 | 0 | if (fp == NULL) { |
117 | 0 | int error = ERRNO; |
118 | 0 | switch (error) { |
119 | 0 | case ENOENT: |
120 | 0 | case ESRCH: |
121 | 0 | status = ARES_ENOTFOUND; |
122 | 0 | goto done; |
123 | 0 | default: |
124 | 0 | DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", error, |
125 | 0 | strerror(error))); |
126 | 0 | DEBUGF(fprintf(stderr, "Error opening file: %s\n", filename)); |
127 | 0 | status = ARES_EFILE; |
128 | 0 | goto done; |
129 | 0 | } |
130 | 0 | } |
131 | | |
132 | | /* Get length portably, fstat() is POSIX, not C */ |
133 | 0 | if (fseek(fp, 0, SEEK_END) != 0) { |
134 | 0 | status = ARES_EFILE; |
135 | 0 | goto done; |
136 | 0 | } |
137 | | |
138 | 0 | ftell_len = ftell(fp); |
139 | 0 | if (ftell_len < 0) { |
140 | 0 | status = ARES_EFILE; |
141 | 0 | goto done; |
142 | 0 | } |
143 | 0 | len = (size_t)ftell_len; |
144 | |
|
145 | 0 | if (fseek(fp, 0, SEEK_SET) != 0) { |
146 | 0 | status = ARES_EFILE; |
147 | 0 | goto done; |
148 | 0 | } |
149 | | |
150 | 0 | if (len == 0) { |
151 | 0 | status = ARES_SUCCESS; |
152 | 0 | goto done; |
153 | 0 | } |
154 | | |
155 | | /* Read entire data into buffer */ |
156 | 0 | ptr_len = len; |
157 | 0 | ptr = ares__buf_append_start(buf, &ptr_len); |
158 | 0 | if (ptr == NULL) { |
159 | 0 | status = ARES_ENOMEM; |
160 | 0 | goto done; |
161 | 0 | } |
162 | | |
163 | 0 | ptr_len = fread(ptr, 1, len, fp); |
164 | 0 | if (ptr_len != len) { |
165 | 0 | status = ARES_EFILE; |
166 | 0 | goto done; |
167 | 0 | } |
168 | | |
169 | 0 | ares__buf_append_finish(buf, len); |
170 | 0 | status = ARES_SUCCESS; |
171 | |
|
172 | 0 | done: |
173 | 0 | if (fp != NULL) { |
174 | 0 | fclose(fp); |
175 | 0 | } |
176 | 0 | return status; |
177 | 0 | } |
178 | | |
179 | | static ares_bool_t ares__is_hostname(const char *str) |
180 | 0 | { |
181 | 0 | size_t i; |
182 | 0 | for (i = 0; str[i] != 0; i++) { |
183 | 0 | if (!ares__is_hostnamech(str[i])) { |
184 | 0 | return ARES_FALSE; |
185 | 0 | } |
186 | 0 | } |
187 | 0 | return ARES_TRUE; |
188 | 0 | } |
189 | | |
190 | | const void *ares_dns_pton(const char *ipaddr, struct ares_addr *addr, |
191 | | size_t *out_len) |
192 | 0 | { |
193 | 0 | const void *ptr = NULL; |
194 | 0 | size_t ptr_len = 0; |
195 | |
|
196 | 0 | if (ipaddr == NULL || addr == NULL || out_len == NULL) { |
197 | 0 | return NULL; |
198 | 0 | } |
199 | | |
200 | 0 | *out_len = 0; |
201 | |
|
202 | 0 | if (addr->family == AF_INET && |
203 | 0 | ares_inet_pton(AF_INET, ipaddr, &addr->addr.addr4) > 0) { |
204 | 0 | ptr = &addr->addr.addr4; |
205 | 0 | ptr_len = sizeof(addr->addr.addr4); |
206 | 0 | } else if (addr->family == AF_INET6 && |
207 | 0 | ares_inet_pton(AF_INET6, ipaddr, &addr->addr.addr6) > 0) { |
208 | 0 | ptr = &addr->addr.addr6; |
209 | 0 | ptr_len = sizeof(addr->addr.addr6); |
210 | 0 | } else if (addr->family == AF_UNSPEC) { |
211 | 0 | if (ares_inet_pton(AF_INET, ipaddr, &addr->addr.addr4) > 0) { |
212 | 0 | addr->family = AF_INET; |
213 | 0 | ptr = &addr->addr.addr4; |
214 | 0 | ptr_len = sizeof(addr->addr.addr4); |
215 | 0 | } else if (ares_inet_pton(AF_INET6, ipaddr, &addr->addr.addr6) > 0) { |
216 | 0 | addr->family = AF_INET6; |
217 | 0 | ptr = &addr->addr.addr6; |
218 | 0 | ptr_len = sizeof(addr->addr.addr6); |
219 | 0 | } |
220 | 0 | } |
221 | |
|
222 | 0 | *out_len = ptr_len; |
223 | 0 | return ptr; |
224 | 0 | } |
225 | | |
226 | | static ares_bool_t ares__normalize_ipaddr(const char *ipaddr, char *out, |
227 | | size_t out_len) |
228 | 0 | { |
229 | 0 | struct ares_addr data; |
230 | 0 | const void *addr; |
231 | 0 | size_t addr_len = 0; |
232 | |
|
233 | 0 | memset(&data, 0, sizeof(data)); |
234 | 0 | data.family = AF_UNSPEC; |
235 | |
|
236 | 0 | addr = ares_dns_pton(ipaddr, &data, &addr_len); |
237 | 0 | if (addr == NULL) { |
238 | 0 | return ARES_FALSE; |
239 | 0 | } |
240 | | |
241 | 0 | if (!ares_inet_ntop(data.family, addr, out, (ares_socklen_t)out_len)) { |
242 | 0 | return ARES_FALSE; |
243 | 0 | } |
244 | | |
245 | 0 | return ARES_TRUE; |
246 | 0 | } |
247 | | |
248 | | static void ares__hosts_entry_destroy(ares_hosts_entry_t *entry) |
249 | 0 | { |
250 | 0 | if (entry == NULL) { |
251 | 0 | return; |
252 | 0 | } |
253 | | |
254 | | /* Honor reference counting */ |
255 | 0 | if (entry->refcnt != 0) { |
256 | 0 | entry->refcnt--; |
257 | 0 | } |
258 | |
|
259 | 0 | if (entry->refcnt > 0) { |
260 | 0 | return; |
261 | 0 | } |
262 | | |
263 | 0 | ares__llist_destroy(entry->hosts); |
264 | 0 | ares__llist_destroy(entry->ips); |
265 | 0 | ares_free(entry); |
266 | 0 | } |
267 | | |
268 | | static void ares__hosts_entry_destroy_cb(void *entry) |
269 | 0 | { |
270 | 0 | ares__hosts_entry_destroy(entry); |
271 | 0 | } |
272 | | |
273 | | void ares__hosts_file_destroy(ares_hosts_file_t *hf) |
274 | 0 | { |
275 | 0 | if (hf == NULL) { |
276 | 0 | return; |
277 | 0 | } |
278 | | |
279 | 0 | ares_free(hf->filename); |
280 | 0 | ares__htable_strvp_destroy(hf->hosthash); |
281 | 0 | ares__htable_strvp_destroy(hf->iphash); |
282 | 0 | ares_free(hf); |
283 | 0 | } |
284 | | |
285 | | static ares_hosts_file_t *ares__hosts_file_create(const char *filename) |
286 | 0 | { |
287 | 0 | ares_hosts_file_t *hf = ares_malloc_zero(sizeof(*hf)); |
288 | 0 | if (hf == NULL) { |
289 | 0 | goto fail; |
290 | 0 | } |
291 | | |
292 | 0 | hf->ts = time(NULL); |
293 | |
|
294 | 0 | hf->filename = ares_strdup(filename); |
295 | 0 | if (hf->filename == NULL) { |
296 | 0 | goto fail; |
297 | 0 | } |
298 | | |
299 | 0 | hf->iphash = ares__htable_strvp_create(ares__hosts_entry_destroy_cb); |
300 | 0 | if (hf->iphash == NULL) { |
301 | 0 | goto fail; |
302 | 0 | } |
303 | | |
304 | 0 | hf->hosthash = ares__htable_strvp_create(NULL); |
305 | 0 | if (hf->hosthash == NULL) { |
306 | 0 | goto fail; |
307 | 0 | } |
308 | | |
309 | 0 | return hf; |
310 | | |
311 | 0 | fail: |
312 | 0 | ares__hosts_file_destroy(hf); |
313 | 0 | return NULL; |
314 | 0 | } |
315 | | |
316 | | typedef enum { |
317 | | ARES_MATCH_NONE = 0, |
318 | | ARES_MATCH_IPADDR = 1, |
319 | | ARES_MATCH_HOST = 2 |
320 | | } ares_hosts_file_match_t; |
321 | | |
322 | | static ares_status_t ares__hosts_file_merge_entry( |
323 | | const ares_hosts_file_t *hf, ares_hosts_entry_t *existing, |
324 | | ares_hosts_entry_t *entry, ares_hosts_file_match_t matchtype) |
325 | 0 | { |
326 | 0 | ares__llist_node_t *node; |
327 | | |
328 | | /* If we matched on IP address, we know there can only be 1, so there's no |
329 | | * reason to do anything */ |
330 | 0 | if (matchtype != ARES_MATCH_IPADDR) { |
331 | 0 | while ((node = ares__llist_node_first(entry->ips)) != NULL) { |
332 | 0 | const char *ipaddr = ares__llist_node_val(node); |
333 | |
|
334 | 0 | if (ares__htable_strvp_get_direct(hf->iphash, ipaddr) != NULL) { |
335 | 0 | ares__llist_node_destroy(node); |
336 | 0 | continue; |
337 | 0 | } |
338 | | |
339 | 0 | ares__llist_node_move_parent_last(node, existing->ips); |
340 | 0 | } |
341 | 0 | } |
342 | | |
343 | |
|
344 | 0 | while ((node = ares__llist_node_first(entry->hosts)) != NULL) { |
345 | 0 | const char *hostname = ares__llist_node_val(node); |
346 | |
|
347 | 0 | if (ares__htable_strvp_get_direct(hf->hosthash, hostname) != NULL) { |
348 | 0 | ares__llist_node_destroy(node); |
349 | 0 | continue; |
350 | 0 | } |
351 | | |
352 | 0 | ares__llist_node_move_parent_last(node, existing->hosts); |
353 | 0 | } |
354 | |
|
355 | 0 | ares__hosts_entry_destroy(entry); |
356 | 0 | return ARES_SUCCESS; |
357 | 0 | } |
358 | | |
359 | | static ares_hosts_file_match_t |
360 | | ares__hosts_file_match(const ares_hosts_file_t *hf, ares_hosts_entry_t *entry, |
361 | | ares_hosts_entry_t **match) |
362 | 0 | { |
363 | 0 | ares__llist_node_t *node; |
364 | 0 | *match = NULL; |
365 | |
|
366 | 0 | for (node = ares__llist_node_first(entry->ips); node != NULL; |
367 | 0 | node = ares__llist_node_next(node)) { |
368 | 0 | const char *ipaddr = ares__llist_node_val(node); |
369 | 0 | *match = ares__htable_strvp_get_direct(hf->iphash, ipaddr); |
370 | 0 | if (*match != NULL) { |
371 | 0 | return ARES_MATCH_IPADDR; |
372 | 0 | } |
373 | 0 | } |
374 | | |
375 | 0 | for (node = ares__llist_node_first(entry->hosts); node != NULL; |
376 | 0 | node = ares__llist_node_next(node)) { |
377 | 0 | const char *host = ares__llist_node_val(node); |
378 | 0 | *match = ares__htable_strvp_get_direct(hf->hosthash, host); |
379 | 0 | if (*match != NULL) { |
380 | 0 | return ARES_MATCH_HOST; |
381 | 0 | } |
382 | 0 | } |
383 | | |
384 | 0 | return ARES_MATCH_NONE; |
385 | 0 | } |
386 | | |
387 | | /*! entry is invalidated upon calling this function, always, even on error */ |
388 | | static ares_status_t ares__hosts_file_add(ares_hosts_file_t *hosts, |
389 | | ares_hosts_entry_t *entry) |
390 | 0 | { |
391 | 0 | ares_hosts_entry_t *match = NULL; |
392 | 0 | ares_status_t status = ARES_SUCCESS; |
393 | 0 | ares__llist_node_t *node; |
394 | 0 | ares_hosts_file_match_t matchtype; |
395 | 0 | size_t num_hostnames; |
396 | | |
397 | | /* Record the number of hostnames in this entry file. If we merge into an |
398 | | * existing record, these will be *appended* to the entry, so we'll count |
399 | | * backwards when adding to the hosts hashtable */ |
400 | 0 | num_hostnames = ares__llist_len(entry->hosts); |
401 | |
|
402 | 0 | matchtype = ares__hosts_file_match(hosts, entry, &match); |
403 | |
|
404 | 0 | if (matchtype != ARES_MATCH_NONE) { |
405 | 0 | status = ares__hosts_file_merge_entry(hosts, match, entry, matchtype); |
406 | 0 | if (status != ARES_SUCCESS) { |
407 | 0 | ares__hosts_entry_destroy(entry); |
408 | 0 | return status; |
409 | 0 | } |
410 | | /* entry was invalidated above by merging */ |
411 | 0 | entry = match; |
412 | 0 | } |
413 | | |
414 | 0 | if (matchtype != ARES_MATCH_IPADDR) { |
415 | 0 | const char *ipaddr = ares__llist_last_val(entry->ips); |
416 | |
|
417 | 0 | if (!ares__htable_strvp_get(hosts->iphash, ipaddr, NULL)) { |
418 | 0 | if (!ares__htable_strvp_insert(hosts->iphash, ipaddr, entry)) { |
419 | 0 | ares__hosts_entry_destroy(entry); |
420 | 0 | return ARES_ENOMEM; |
421 | 0 | } |
422 | 0 | entry->refcnt++; |
423 | 0 | } |
424 | 0 | } |
425 | | |
426 | | /* Go backwards, on a merge, hostnames are appended. Breakout once we've |
427 | | * consumed all the hosts that we appended */ |
428 | 0 | for (node = ares__llist_node_last(entry->hosts); node != NULL; |
429 | 0 | node = ares__llist_node_prev(node)) { |
430 | 0 | const char *val = ares__llist_node_val(node); |
431 | |
|
432 | 0 | if (num_hostnames == 0) { |
433 | 0 | break; |
434 | 0 | } |
435 | | |
436 | 0 | num_hostnames--; |
437 | | |
438 | | /* first hostname match wins. If we detect a duplicate hostname for another |
439 | | * ip it will automatically be added to the same entry */ |
440 | 0 | if (ares__htable_strvp_get(hosts->hosthash, val, NULL)) { |
441 | 0 | continue; |
442 | 0 | } |
443 | | |
444 | 0 | if (!ares__htable_strvp_insert(hosts->hosthash, val, entry)) { |
445 | 0 | return ARES_ENOMEM; |
446 | 0 | } |
447 | 0 | } |
448 | | |
449 | 0 | return ARES_SUCCESS; |
450 | 0 | } |
451 | | |
452 | | static ares_bool_t ares__hosts_entry_isdup(ares_hosts_entry_t *entry, |
453 | | const char *host) |
454 | 0 | { |
455 | 0 | ares__llist_node_t *node; |
456 | |
|
457 | 0 | for (node = ares__llist_node_first(entry->ips); node != NULL; |
458 | 0 | node = ares__llist_node_next(node)) { |
459 | 0 | const char *myhost = ares__llist_node_val(node); |
460 | 0 | if (strcasecmp(myhost, host) == 0) { |
461 | 0 | return ARES_TRUE; |
462 | 0 | } |
463 | 0 | } |
464 | | |
465 | 0 | return ARES_FALSE; |
466 | 0 | } |
467 | | |
468 | | static ares_status_t ares__parse_hosts_hostnames(ares__buf_t *buf, |
469 | | ares_hosts_entry_t *entry) |
470 | 0 | { |
471 | 0 | entry->hosts = ares__llist_create(ares_free); |
472 | 0 | if (entry->hosts == NULL) { |
473 | 0 | return ARES_ENOMEM; |
474 | 0 | } |
475 | | |
476 | | /* Parse hostnames and aliases */ |
477 | 0 | while (ares__buf_len(buf)) { |
478 | 0 | char hostname[256]; |
479 | 0 | char *temp; |
480 | 0 | ares_status_t status; |
481 | 0 | unsigned char comment = '#'; |
482 | |
|
483 | 0 | ares__buf_consume_whitespace(buf, ARES_FALSE); |
484 | |
|
485 | 0 | if (ares__buf_len(buf) == 0) { |
486 | 0 | break; |
487 | 0 | } |
488 | | |
489 | | /* See if it is a comment, if so stop processing */ |
490 | 0 | if (ares__buf_begins_with(buf, &comment, 1)) { |
491 | 0 | break; |
492 | 0 | } |
493 | | |
494 | 0 | ares__buf_tag(buf); |
495 | | |
496 | | /* Must be at end of line */ |
497 | 0 | if (ares__buf_consume_nonwhitespace(buf) == 0) { |
498 | 0 | break; |
499 | 0 | } |
500 | | |
501 | 0 | status = ares__buf_tag_fetch_string(buf, hostname, sizeof(hostname)); |
502 | 0 | if (status != ARES_SUCCESS) { |
503 | | /* Bad entry, just ignore as long as its not the first. If its the first, |
504 | | * it must be valid */ |
505 | 0 | if (ares__llist_len(entry->hosts) == 0) { |
506 | 0 | return ARES_EBADSTR; |
507 | 0 | } |
508 | | |
509 | 0 | continue; |
510 | 0 | } |
511 | | |
512 | | /* Validate it is a valid hostname characterset */ |
513 | 0 | if (!ares__is_hostname(hostname)) { |
514 | 0 | continue; |
515 | 0 | } |
516 | | |
517 | | /* Don't add a duplicate to the same entry */ |
518 | 0 | if (ares__hosts_entry_isdup(entry, hostname)) { |
519 | 0 | continue; |
520 | 0 | } |
521 | | |
522 | | /* Add to list */ |
523 | 0 | temp = ares_strdup(hostname); |
524 | 0 | if (temp == NULL) { |
525 | 0 | return ARES_ENOMEM; |
526 | 0 | } |
527 | | |
528 | 0 | if (ares__llist_insert_last(entry->hosts, temp) == NULL) { |
529 | 0 | ares_free(temp); |
530 | 0 | return ARES_ENOMEM; |
531 | 0 | } |
532 | 0 | } |
533 | | |
534 | | /* Must have at least 1 entry */ |
535 | 0 | if (ares__llist_len(entry->hosts) == 0) { |
536 | 0 | return ARES_EBADSTR; |
537 | 0 | } |
538 | | |
539 | 0 | return ARES_SUCCESS; |
540 | 0 | } |
541 | | |
542 | | static ares_status_t ares__parse_hosts_ipaddr(ares__buf_t *buf, |
543 | | ares_hosts_entry_t **entry_out) |
544 | 0 | { |
545 | 0 | char addr[INET6_ADDRSTRLEN]; |
546 | 0 | char *temp; |
547 | 0 | ares_hosts_entry_t *entry = NULL; |
548 | 0 | ares_status_t status; |
549 | |
|
550 | 0 | *entry_out = NULL; |
551 | |
|
552 | 0 | ares__buf_tag(buf); |
553 | 0 | ares__buf_consume_nonwhitespace(buf); |
554 | 0 | status = ares__buf_tag_fetch_string(buf, addr, sizeof(addr)); |
555 | 0 | if (status != ARES_SUCCESS) { |
556 | 0 | return status; |
557 | 0 | } |
558 | | |
559 | | /* Validate and normalize the ip address format */ |
560 | 0 | if (!ares__normalize_ipaddr(addr, addr, sizeof(addr))) { |
561 | 0 | return ARES_EBADSTR; |
562 | 0 | } |
563 | | |
564 | 0 | entry = ares_malloc_zero(sizeof(*entry)); |
565 | 0 | if (entry == NULL) { |
566 | 0 | return ARES_ENOMEM; |
567 | 0 | } |
568 | | |
569 | 0 | entry->ips = ares__llist_create(ares_free); |
570 | 0 | if (entry->ips == NULL) { |
571 | 0 | ares__hosts_entry_destroy(entry); |
572 | 0 | return ARES_ENOMEM; |
573 | 0 | } |
574 | | |
575 | 0 | temp = ares_strdup(addr); |
576 | 0 | if (temp == NULL) { |
577 | 0 | ares__hosts_entry_destroy(entry); |
578 | 0 | return ARES_ENOMEM; |
579 | 0 | } |
580 | | |
581 | 0 | if (ares__llist_insert_first(entry->ips, temp) == NULL) { |
582 | 0 | ares_free(temp); |
583 | 0 | ares__hosts_entry_destroy(entry); |
584 | 0 | return ARES_ENOMEM; |
585 | 0 | } |
586 | | |
587 | 0 | *entry_out = entry; |
588 | |
|
589 | 0 | return ARES_SUCCESS; |
590 | 0 | } |
591 | | |
592 | | static ares_status_t ares__parse_hosts(const char *filename, |
593 | | ares_hosts_file_t **out) |
594 | 0 | { |
595 | 0 | ares__buf_t *buf = NULL; |
596 | 0 | ares_status_t status = ARES_EBADRESP; |
597 | 0 | ares_hosts_file_t *hf = NULL; |
598 | 0 | ares_hosts_entry_t *entry = NULL; |
599 | |
|
600 | 0 | *out = NULL; |
601 | |
|
602 | 0 | buf = ares__buf_create(); |
603 | 0 | if (buf == NULL) { |
604 | 0 | status = ARES_ENOMEM; |
605 | 0 | goto done; |
606 | 0 | } |
607 | | |
608 | 0 | status = ares__read_file_into_buf(filename, buf); |
609 | 0 | if (status != ARES_SUCCESS) { |
610 | 0 | goto done; |
611 | 0 | } |
612 | | |
613 | 0 | hf = ares__hosts_file_create(filename); |
614 | 0 | if (hf == NULL) { |
615 | 0 | status = ARES_ENOMEM; |
616 | 0 | goto done; |
617 | 0 | } |
618 | | |
619 | 0 | while (ares__buf_len(buf)) { |
620 | 0 | unsigned char comment = '#'; |
621 | | |
622 | | /* -- Start of new line here -- */ |
623 | | |
624 | | /* Consume any leading whitespace */ |
625 | 0 | ares__buf_consume_whitespace(buf, ARES_FALSE); |
626 | |
|
627 | 0 | if (ares__buf_len(buf) == 0) { |
628 | 0 | break; |
629 | 0 | } |
630 | | |
631 | | /* See if it is a comment, if so, consume remaining line */ |
632 | 0 | if (ares__buf_begins_with(buf, &comment, 1)) { |
633 | 0 | ares__buf_consume_line(buf, ARES_TRUE); |
634 | 0 | continue; |
635 | 0 | } |
636 | | |
637 | | /* Pull off ip address */ |
638 | 0 | status = ares__parse_hosts_ipaddr(buf, &entry); |
639 | 0 | if (status == ARES_ENOMEM) { |
640 | 0 | goto done; |
641 | 0 | } |
642 | 0 | if (status != ARES_SUCCESS) { |
643 | | /* Bad line, consume and go onto next */ |
644 | 0 | ares__buf_consume_line(buf, ARES_TRUE); |
645 | 0 | continue; |
646 | 0 | } |
647 | | |
648 | | /* Parse of the hostnames */ |
649 | 0 | status = ares__parse_hosts_hostnames(buf, entry); |
650 | 0 | if (status == ARES_ENOMEM) { |
651 | 0 | goto done; |
652 | 0 | } else if (status != ARES_SUCCESS) { |
653 | | /* Bad line, consume and go onto next */ |
654 | 0 | ares__hosts_entry_destroy(entry); |
655 | 0 | entry = NULL; |
656 | 0 | ares__buf_consume_line(buf, ARES_TRUE); |
657 | 0 | continue; |
658 | 0 | } |
659 | | |
660 | | /* Append the successful entry to the hosts file */ |
661 | 0 | status = ares__hosts_file_add(hf, entry); |
662 | 0 | entry = NULL; /* is always invalidated by this function, even on error */ |
663 | 0 | if (status != ARES_SUCCESS) { |
664 | 0 | goto done; |
665 | 0 | } |
666 | | |
667 | | /* Go to next line */ |
668 | 0 | ares__buf_consume_line(buf, ARES_TRUE); |
669 | 0 | } |
670 | | |
671 | 0 | status = ARES_SUCCESS; |
672 | |
|
673 | 0 | done: |
674 | 0 | ares__hosts_entry_destroy(entry); |
675 | 0 | ares__buf_destroy(buf); |
676 | 0 | if (status != ARES_SUCCESS) { |
677 | 0 | ares__hosts_file_destroy(hf); |
678 | 0 | } else { |
679 | 0 | *out = hf; |
680 | 0 | } |
681 | 0 | return status; |
682 | 0 | } |
683 | | |
684 | | static ares_bool_t ares__hosts_expired(const char *filename, |
685 | | const ares_hosts_file_t *hf) |
686 | 0 | { |
687 | 0 | time_t mod_ts = 0; |
688 | |
|
689 | 0 | #ifdef HAVE_STAT |
690 | 0 | struct stat st; |
691 | 0 | if (stat(filename, &st) == 0) { |
692 | 0 | mod_ts = st.st_mtime; |
693 | 0 | } |
694 | | #elif defined(_WIN32) |
695 | | struct _stat st; |
696 | | if (_stat(filename, &st) == 0) { |
697 | | mod_ts = st.st_mtime; |
698 | | } |
699 | | #else |
700 | | (void)filename; |
701 | | #endif |
702 | |
|
703 | 0 | if (hf == NULL) { |
704 | 0 | return ARES_TRUE; |
705 | 0 | } |
706 | | |
707 | | /* Expire every 60s if we can't get a time */ |
708 | 0 | if (mod_ts == 0) { |
709 | 0 | mod_ts = time(NULL) - 60; |
710 | 0 | } |
711 | | |
712 | | /* If filenames are different, its expired */ |
713 | 0 | if (strcasecmp(hf->filename, filename) != 0) { |
714 | 0 | return ARES_TRUE; |
715 | 0 | } |
716 | | |
717 | 0 | if (hf->ts <= mod_ts) { |
718 | 0 | return ARES_TRUE; |
719 | 0 | } |
720 | | |
721 | 0 | return ARES_FALSE; |
722 | 0 | } |
723 | | |
724 | | static ares_status_t ares__hosts_path(const ares_channel_t *channel, |
725 | | ares_bool_t use_env, char **path) |
726 | 0 | { |
727 | 0 | char *path_hosts = NULL; |
728 | |
|
729 | 0 | *path = NULL; |
730 | |
|
731 | 0 | if (channel->hosts_path) { |
732 | 0 | path_hosts = ares_strdup(channel->hosts_path); |
733 | 0 | if (!path_hosts) { |
734 | 0 | return ARES_ENOMEM; |
735 | 0 | } |
736 | 0 | } |
737 | | |
738 | 0 | if (use_env) { |
739 | 0 | if (path_hosts) { |
740 | 0 | ares_free(path_hosts); |
741 | 0 | } |
742 | |
|
743 | 0 | path_hosts = ares_strdup(getenv("CARES_HOSTS")); |
744 | 0 | if (!path_hosts) { |
745 | 0 | return ARES_ENOMEM; |
746 | 0 | } |
747 | 0 | } |
748 | | |
749 | 0 | if (!path_hosts) { |
750 | | #ifdef WIN32 |
751 | | char PATH_HOSTS[MAX_PATH] = ""; |
752 | | char tmp[MAX_PATH]; |
753 | | HKEY hkeyHosts; |
754 | | DWORD dwLength = sizeof(tmp); |
755 | | if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ, |
756 | | &hkeyHosts) != ERROR_SUCCESS) { |
757 | | return ARES_ENOTFOUND; |
758 | | } |
759 | | RegQueryValueExA(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp, |
760 | | &dwLength); |
761 | | ExpandEnvironmentStringsA(tmp, PATH_HOSTS, MAX_PATH); |
762 | | RegCloseKey(hkeyHosts); |
763 | | strcat(PATH_HOSTS, WIN_PATH_HOSTS); |
764 | | #elif defined(WATT32) |
765 | | const char *PATH_HOSTS = _w32_GetHostsFile(); |
766 | | |
767 | | if (!PATH_HOSTS) { |
768 | | return ARES_ENOTFOUND; |
769 | | } |
770 | | #endif |
771 | 0 | path_hosts = ares_strdup(PATH_HOSTS); |
772 | 0 | if (!path_hosts) { |
773 | 0 | return ARES_ENOMEM; |
774 | 0 | } |
775 | 0 | } |
776 | | |
777 | 0 | *path = path_hosts; |
778 | 0 | return ARES_SUCCESS; |
779 | 0 | } |
780 | | |
781 | | static ares_status_t ares__hosts_update(ares_channel_t *channel, |
782 | | ares_bool_t use_env) |
783 | 0 | { |
784 | 0 | ares_status_t status; |
785 | 0 | char *filename = NULL; |
786 | |
|
787 | 0 | status = ares__hosts_path(channel, use_env, &filename); |
788 | 0 | if (status != ARES_SUCCESS) { |
789 | 0 | return status; |
790 | 0 | } |
791 | | |
792 | 0 | if (!ares__hosts_expired(filename, channel->hf)) { |
793 | 0 | ares_free(filename); |
794 | 0 | return ARES_SUCCESS; |
795 | 0 | } |
796 | | |
797 | 0 | ares__hosts_file_destroy(channel->hf); |
798 | 0 | channel->hf = NULL; |
799 | |
|
800 | 0 | status = ares__parse_hosts(filename, &channel->hf); |
801 | 0 | ares_free(filename); |
802 | 0 | return status; |
803 | 0 | } |
804 | | |
805 | | ares_status_t ares__hosts_search_ipaddr(ares_channel_t *channel, |
806 | | ares_bool_t use_env, const char *ipaddr, |
807 | | const ares_hosts_entry_t **entry) |
808 | 0 | { |
809 | 0 | ares_status_t status; |
810 | 0 | char addr[INET6_ADDRSTRLEN]; |
811 | |
|
812 | 0 | *entry = NULL; |
813 | |
|
814 | 0 | status = ares__hosts_update(channel, use_env); |
815 | 0 | if (status != ARES_SUCCESS) { |
816 | 0 | return status; |
817 | 0 | } |
818 | | |
819 | 0 | if (channel->hf == NULL) { |
820 | 0 | return ARES_ENOTFOUND; |
821 | 0 | } |
822 | | |
823 | 0 | if (!ares__normalize_ipaddr(ipaddr, addr, sizeof(addr))) { |
824 | 0 | return ARES_EBADNAME; |
825 | 0 | } |
826 | | |
827 | 0 | *entry = ares__htable_strvp_get_direct(channel->hf->iphash, addr); |
828 | 0 | if (*entry == NULL) { |
829 | 0 | return ARES_ENOTFOUND; |
830 | 0 | } |
831 | | |
832 | 0 | return ARES_SUCCESS; |
833 | 0 | } |
834 | | |
835 | | ares_status_t ares__hosts_search_host(ares_channel_t *channel, |
836 | | ares_bool_t use_env, const char *host, |
837 | | const ares_hosts_entry_t **entry) |
838 | 0 | { |
839 | 0 | ares_status_t status; |
840 | |
|
841 | 0 | *entry = NULL; |
842 | |
|
843 | 0 | status = ares__hosts_update(channel, use_env); |
844 | 0 | if (status != ARES_SUCCESS) { |
845 | 0 | return status; |
846 | 0 | } |
847 | | |
848 | 0 | if (channel->hf == NULL) { |
849 | 0 | return ARES_ENOTFOUND; |
850 | 0 | } |
851 | | |
852 | 0 | *entry = ares__htable_strvp_get_direct(channel->hf->hosthash, host); |
853 | 0 | if (*entry == NULL) { |
854 | 0 | return ARES_ENOTFOUND; |
855 | 0 | } |
856 | | |
857 | 0 | return ARES_SUCCESS; |
858 | 0 | } |
859 | | |
860 | | ares_status_t ares__hosts_entry_to_hostent(const ares_hosts_entry_t *entry, |
861 | | int family, struct hostent **hostent) |
862 | 0 | { |
863 | 0 | ares_status_t status; |
864 | 0 | size_t naliases; |
865 | 0 | ares__llist_node_t *node; |
866 | 0 | size_t idx; |
867 | |
|
868 | 0 | *hostent = ares_malloc_zero(sizeof(**hostent)); |
869 | 0 | if (*hostent == NULL) { |
870 | 0 | status = ARES_ENOMEM; |
871 | 0 | goto fail; |
872 | 0 | } |
873 | | |
874 | 0 | (*hostent)->h_addrtype = (HOSTENT_ADDRTYPE_TYPE)family; |
875 | | |
876 | | /* Copy IP addresses that match the address family */ |
877 | 0 | idx = 0; |
878 | 0 | for (node = ares__llist_node_first(entry->ips); node != NULL; |
879 | 0 | node = ares__llist_node_next(node)) { |
880 | 0 | struct ares_addr addr; |
881 | 0 | const void *ptr = NULL; |
882 | 0 | size_t ptr_len = 0; |
883 | 0 | const char *ipaddr = ares__llist_node_val(node); |
884 | 0 | char **temp = NULL; |
885 | |
|
886 | 0 | memset(&addr, 0, sizeof(addr)); |
887 | |
|
888 | 0 | addr.family = family; |
889 | 0 | ptr = ares_dns_pton(ipaddr, &addr, &ptr_len); |
890 | 0 | if (ptr == NULL) { |
891 | 0 | continue; |
892 | 0 | } |
893 | | |
894 | | /* If family == AF_UNSPEC, then we want to inherit this for future |
895 | | * conversions as we can only support a single address class */ |
896 | 0 | if (family == AF_UNSPEC) { |
897 | 0 | family = addr.family; |
898 | 0 | (*hostent)->h_addrtype = (HOSTENT_ADDRTYPE_TYPE)addr.family; |
899 | 0 | } |
900 | |
|
901 | 0 | temp = ares_realloc_zero((*hostent)->h_addr_list, |
902 | 0 | (idx + 1) * sizeof(*(*hostent)->h_addr_list), |
903 | 0 | (idx + 2) * sizeof(*(*hostent)->h_addr_list)); |
904 | 0 | if (temp == NULL) { |
905 | 0 | status = ARES_ENOMEM; |
906 | 0 | goto fail; |
907 | 0 | } |
908 | | |
909 | 0 | (*hostent)->h_addr_list = temp; |
910 | |
|
911 | 0 | (*hostent)->h_addr_list[idx] = ares_malloc(ptr_len); |
912 | 0 | if ((*hostent)->h_addr_list[idx] == NULL) { |
913 | 0 | status = ARES_ENOMEM; |
914 | 0 | goto fail; |
915 | 0 | } |
916 | | |
917 | 0 | memcpy((*hostent)->h_addr_list[idx], ptr, ptr_len); |
918 | 0 | idx++; |
919 | 0 | (*hostent)->h_length = (HOSTENT_LENGTH_TYPE)ptr_len; |
920 | 0 | } |
921 | | |
922 | | /* entry didn't match address class */ |
923 | 0 | if (idx == 0) { |
924 | 0 | status = ARES_ENOTFOUND; |
925 | 0 | goto fail; |
926 | 0 | } |
927 | | |
928 | | /* Copy main hostname */ |
929 | 0 | (*hostent)->h_name = ares_strdup(ares__llist_first_val(entry->hosts)); |
930 | 0 | if ((*hostent)->h_name == NULL) { |
931 | 0 | status = ARES_ENOMEM; |
932 | 0 | goto fail; |
933 | 0 | } |
934 | | |
935 | | /* Copy aliases */ |
936 | 0 | naliases = ares__llist_len(entry->hosts) - 1; |
937 | | |
938 | | /* Cap at 100, some people use https://github.com/StevenBlack/hosts and we |
939 | | * don't need 200k+ aliases */ |
940 | 0 | if (naliases > 100) { |
941 | 0 | naliases = 100; |
942 | 0 | } |
943 | |
|
944 | 0 | (*hostent)->h_aliases = |
945 | 0 | ares_malloc_zero((naliases + 1) * sizeof(*(*hostent)->h_aliases)); |
946 | 0 | if ((*hostent)->h_aliases == NULL) { |
947 | 0 | status = ARES_ENOMEM; |
948 | 0 | goto fail; |
949 | 0 | } |
950 | | |
951 | | /* Copy all entries to the alias except the first */ |
952 | 0 | idx = 0; |
953 | 0 | node = ares__llist_node_first(entry->hosts); |
954 | 0 | node = ares__llist_node_next(node); |
955 | 0 | while (node != NULL) { |
956 | 0 | (*hostent)->h_aliases[idx] = ares_strdup(ares__llist_node_val(node)); |
957 | 0 | if ((*hostent)->h_aliases[idx] == NULL) { |
958 | 0 | status = ARES_ENOMEM; |
959 | 0 | goto fail; |
960 | 0 | } |
961 | 0 | idx++; |
962 | | |
963 | | /* Break out if artificially capped */ |
964 | 0 | if (idx == naliases) { |
965 | 0 | break; |
966 | 0 | } |
967 | 0 | node = ares__llist_node_next(node); |
968 | 0 | } |
969 | | |
970 | 0 | return ARES_SUCCESS; |
971 | | |
972 | 0 | fail: |
973 | 0 | ares_free_hostent(*hostent); |
974 | 0 | *hostent = NULL; |
975 | 0 | return status; |
976 | 0 | } |
977 | | |
978 | | static ares_status_t |
979 | | ares__hosts_ai_append_cnames(const ares_hosts_entry_t *entry, |
980 | | struct ares_addrinfo_cname **cnames_out) |
981 | 0 | { |
982 | 0 | struct ares_addrinfo_cname *cname = NULL; |
983 | 0 | struct ares_addrinfo_cname *cnames = NULL; |
984 | 0 | const char *primaryhost; |
985 | 0 | ares__llist_node_t *node; |
986 | 0 | ares_status_t status; |
987 | 0 | size_t cnt = 0; |
988 | |
|
989 | 0 | node = ares__llist_node_first(entry->hosts); |
990 | 0 | primaryhost = ares__llist_node_val(node); |
991 | | /* Skip to next node to start with aliases */ |
992 | 0 | node = ares__llist_node_next(node); |
993 | |
|
994 | 0 | while (node != NULL) { |
995 | 0 | const char *host = ares__llist_node_val(node); |
996 | | |
997 | | /* Cap at 100 entries. , some people use |
998 | | * https://github.com/StevenBlack/hosts and we don't need 200k+ aliases */ |
999 | 0 | cnt++; |
1000 | 0 | if (cnt > 100) { |
1001 | 0 | break; |
1002 | 0 | } |
1003 | | |
1004 | 0 | cname = ares__append_addrinfo_cname(&cnames); |
1005 | 0 | if (cname == NULL) { |
1006 | 0 | status = ARES_ENOMEM; |
1007 | 0 | goto done; |
1008 | 0 | } |
1009 | | |
1010 | 0 | cname->alias = ares_strdup(host); |
1011 | 0 | if (cname->alias == NULL) { |
1012 | 0 | status = ARES_ENOMEM; |
1013 | 0 | goto done; |
1014 | 0 | } |
1015 | | |
1016 | 0 | cname->name = ares_strdup(primaryhost); |
1017 | 0 | if (cname->name == NULL) { |
1018 | 0 | status = ARES_ENOMEM; |
1019 | 0 | goto done; |
1020 | 0 | } |
1021 | | |
1022 | 0 | node = ares__llist_node_next(node); |
1023 | 0 | } |
1024 | | |
1025 | | /* No entries, add only primary */ |
1026 | 0 | if (cnames == NULL) { |
1027 | 0 | cname = ares__append_addrinfo_cname(&cnames); |
1028 | 0 | if (cname == NULL) { |
1029 | 0 | status = ARES_ENOMEM; |
1030 | 0 | goto done; |
1031 | 0 | } |
1032 | | |
1033 | 0 | cname->name = ares_strdup(primaryhost); |
1034 | 0 | if (cname->name == NULL) { |
1035 | 0 | status = ARES_ENOMEM; |
1036 | 0 | goto done; |
1037 | 0 | } |
1038 | 0 | } |
1039 | 0 | status = ARES_SUCCESS; |
1040 | |
|
1041 | 0 | done: |
1042 | 0 | if (status != ARES_SUCCESS) { |
1043 | 0 | ares__freeaddrinfo_cnames(cnames); |
1044 | 0 | return status; |
1045 | 0 | } |
1046 | | |
1047 | 0 | *cnames_out = cnames; |
1048 | 0 | return ARES_SUCCESS; |
1049 | 0 | } |
1050 | | |
1051 | | ares_status_t ares__hosts_entry_to_addrinfo(const ares_hosts_entry_t *entry, |
1052 | | const char *name, int family, |
1053 | | unsigned short port, |
1054 | | ares_bool_t want_cnames, |
1055 | | struct ares_addrinfo *ai) |
1056 | 0 | { |
1057 | 0 | ares_status_t status; |
1058 | 0 | struct ares_addrinfo_cname *cnames = NULL; |
1059 | 0 | struct ares_addrinfo_node *ainodes = NULL; |
1060 | 0 | ares__llist_node_t *node; |
1061 | |
|
1062 | 0 | switch (family) { |
1063 | 0 | case AF_INET: |
1064 | 0 | case AF_INET6: |
1065 | 0 | case AF_UNSPEC: |
1066 | 0 | break; |
1067 | 0 | default: |
1068 | 0 | return ARES_EBADFAMILY; |
1069 | 0 | } |
1070 | | |
1071 | 0 | ai->name = ares_strdup(name); |
1072 | 0 | if (ai->name == NULL) { |
1073 | 0 | status = ARES_ENOMEM; |
1074 | 0 | goto done; |
1075 | 0 | } |
1076 | | |
1077 | 0 | for (node = ares__llist_node_first(entry->ips); node != NULL; |
1078 | 0 | node = ares__llist_node_next(node)) { |
1079 | 0 | struct ares_addr addr; |
1080 | 0 | const void *ptr = NULL; |
1081 | 0 | size_t ptr_len = 0; |
1082 | 0 | const char *ipaddr = ares__llist_node_val(node); |
1083 | |
|
1084 | 0 | memset(&addr, 0, sizeof(addr)); |
1085 | 0 | addr.family = family; |
1086 | 0 | ptr = ares_dns_pton(ipaddr, &addr, &ptr_len); |
1087 | |
|
1088 | 0 | if (ptr == NULL) { |
1089 | 0 | continue; |
1090 | 0 | } |
1091 | | |
1092 | 0 | status = ares_append_ai_node(addr.family, port, 0, ptr, &ainodes); |
1093 | 0 | if (status != ARES_SUCCESS) { |
1094 | 0 | goto done; |
1095 | 0 | } |
1096 | 0 | } |
1097 | | |
1098 | 0 | if (want_cnames) { |
1099 | 0 | status = ares__hosts_ai_append_cnames(entry, &cnames); |
1100 | 0 | if (status != ARES_SUCCESS) { |
1101 | 0 | goto done; |
1102 | 0 | } |
1103 | 0 | } |
1104 | | |
1105 | 0 | status = ARES_SUCCESS; |
1106 | |
|
1107 | 0 | done: |
1108 | 0 | if (status != ARES_SUCCESS) { |
1109 | 0 | ares__freeaddrinfo_cnames(cnames); |
1110 | 0 | ares__freeaddrinfo_nodes(ainodes); |
1111 | 0 | ares_free(ai->name); |
1112 | 0 | ai->name = NULL; |
1113 | 0 | return status; |
1114 | 0 | } |
1115 | 0 | ares__addrinfo_cat_cnames(&ai->cnames, cnames); |
1116 | 0 | ares__addrinfo_cat_nodes(&ai->nodes, ainodes); |
1117 | |
|
1118 | 0 | return status; |
1119 | 0 | } |