/src/c-ares/src/lib/ares_getaddrinfo.c
Line | Count | Source (jump to first uncovered line) |
1 | | |
2 | | /* Copyright 1998, 2011, 2013 by the Massachusetts Institute of Technology. |
3 | | * Copyright (C) 2017 - 2018 by Christian Ammer |
4 | | * Copyright (C) 2019 by Andrew Selivanov |
5 | | * |
6 | | * Permission to use, copy, modify, and distribute this |
7 | | * software and its documentation for any purpose and without |
8 | | * fee is hereby granted, provided that the above copyright |
9 | | * notice appear in all copies and that both that copyright |
10 | | * notice and this permission notice appear in supporting |
11 | | * documentation, and that the name of M.I.T. not be used in |
12 | | * advertising or publicity pertaining to distribution of the |
13 | | * software without specific, written prior permission. |
14 | | * M.I.T. makes no representations about the suitability of |
15 | | * this software for any purpose. It is provided "as is" |
16 | | * without express or implied warranty. |
17 | | * |
18 | | * SPDX-License-Identifier: MIT |
19 | | */ |
20 | | |
21 | | #include "ares_setup.h" |
22 | | |
23 | | #ifdef HAVE_GETSERVBYNAME_R |
24 | | # if !defined(GETSERVBYNAME_R_ARGS) || \ |
25 | | (GETSERVBYNAME_R_ARGS < 4) || (GETSERVBYNAME_R_ARGS > 6) |
26 | | # error "you MUST specifiy a valid number of arguments for getservbyname_r" |
27 | | # endif |
28 | | #endif |
29 | | |
30 | | #ifdef HAVE_NETINET_IN_H |
31 | | # include <netinet/in.h> |
32 | | #endif |
33 | | #ifdef HAVE_NETDB_H |
34 | | # include <netdb.h> |
35 | | #endif |
36 | | #ifdef HAVE_ARPA_INET_H |
37 | | # include <arpa/inet.h> |
38 | | #endif |
39 | | |
40 | | #include "ares_nameser.h" |
41 | | |
42 | | #ifdef HAVE_STRINGS_H |
43 | | #include <strings.h> |
44 | | #endif |
45 | | #include <assert.h> |
46 | | |
47 | | #ifdef HAVE_LIMITS_H |
48 | | #include <limits.h> |
49 | | #endif |
50 | | |
51 | | #include "ares.h" |
52 | | #include "bitncmp.h" |
53 | | #include "ares_private.h" |
54 | | |
55 | | #ifdef WATT32 |
56 | | #undef WIN32 |
57 | | #endif |
58 | | #ifdef WIN32 |
59 | | # include "ares_platform.h" |
60 | | #endif |
61 | | |
62 | | struct host_query |
63 | | { |
64 | | ares_channel channel; |
65 | | char *name; |
66 | | unsigned short port; /* in host order */ |
67 | | ares_addrinfo_callback callback; |
68 | | void *arg; |
69 | | struct ares_addrinfo_hints hints; |
70 | | int sent_family; /* this family is what was is being used */ |
71 | | int timeouts; /* number of timeouts we saw for this request */ |
72 | | const char *remaining_lookups; /* types of lookup we need to perform ("fb" by |
73 | | default, file and dns respectively) */ |
74 | | struct ares_addrinfo *ai; /* store results between lookups */ |
75 | | int remaining; /* number of DNS answers waiting for */ |
76 | | int next_domain; /* next search domain to try */ |
77 | | int nodata_cnt; /* Track nodata responses to possibly override final result */ |
78 | | }; |
79 | | |
80 | | static const struct ares_addrinfo_hints default_hints = { |
81 | | 0, /* ai_flags */ |
82 | | AF_UNSPEC, /* ai_family */ |
83 | | 0, /* ai_socktype */ |
84 | | 0, /* ai_protocol */ |
85 | | }; |
86 | | |
87 | | static const struct ares_addrinfo_cname empty_addrinfo_cname = { |
88 | | INT_MAX, /* ttl */ |
89 | | NULL, /* alias */ |
90 | | NULL, /* name */ |
91 | | NULL, /* next */ |
92 | | }; |
93 | | |
94 | | static const struct ares_addrinfo_node empty_addrinfo_node = { |
95 | | 0, /* ai_ttl */ |
96 | | 0, /* ai_flags */ |
97 | | 0, /* ai_family */ |
98 | | 0, /* ai_socktype */ |
99 | | 0, /* ai_protocol */ |
100 | | 0, /* ai_addrlen */ |
101 | | NULL, /* ai_addr */ |
102 | | NULL /* ai_next */ |
103 | | }; |
104 | | |
105 | | static const struct ares_addrinfo empty_addrinfo = { |
106 | | NULL, /* cnames */ |
107 | | NULL, /* nodes */ |
108 | | NULL /* name */ |
109 | | }; |
110 | | |
111 | | /* forward declarations */ |
112 | | static void host_callback(void *arg, int status, int timeouts, |
113 | | unsigned char *abuf, int alen); |
114 | | static int as_is_first(const struct host_query *hquery); |
115 | | static int as_is_only(const struct host_query* hquery); |
116 | | static int next_dns_lookup(struct host_query *hquery); |
117 | | |
118 | | static struct ares_addrinfo_cname *ares__malloc_addrinfo_cname(void) |
119 | 2.93k | { |
120 | 2.93k | struct ares_addrinfo_cname *cname = ares_malloc(sizeof(struct ares_addrinfo_cname)); |
121 | 2.93k | if (!cname) |
122 | 0 | return NULL; |
123 | | |
124 | 2.93k | *cname = empty_addrinfo_cname; |
125 | 2.93k | return cname; |
126 | 2.93k | } |
127 | | |
128 | | struct ares_addrinfo_cname *ares__append_addrinfo_cname(struct ares_addrinfo_cname **head) |
129 | 2.93k | { |
130 | 2.93k | struct ares_addrinfo_cname *tail = ares__malloc_addrinfo_cname(); |
131 | 2.93k | struct ares_addrinfo_cname *last = *head; |
132 | 2.93k | if (!last) |
133 | 278 | { |
134 | 278 | *head = tail; |
135 | 278 | return tail; |
136 | 278 | } |
137 | | |
138 | 137k | while (last->next) |
139 | 134k | { |
140 | 134k | last = last->next; |
141 | 134k | } |
142 | | |
143 | 2.65k | last->next = tail; |
144 | 2.65k | return tail; |
145 | 2.93k | } |
146 | | |
147 | | void ares__addrinfo_cat_cnames(struct ares_addrinfo_cname **head, |
148 | | struct ares_addrinfo_cname *tail) |
149 | 226 | { |
150 | 226 | struct ares_addrinfo_cname *last = *head; |
151 | 226 | if (!last) |
152 | 226 | { |
153 | 226 | *head = tail; |
154 | 226 | return; |
155 | 226 | } |
156 | | |
157 | 0 | while (last->next) |
158 | 0 | { |
159 | 0 | last = last->next; |
160 | 0 | } |
161 | |
|
162 | 0 | last->next = tail; |
163 | 0 | } |
164 | | |
165 | | static struct ares_addrinfo *ares__malloc_addrinfo(void) |
166 | 0 | { |
167 | 0 | struct ares_addrinfo *ai = ares_malloc(sizeof(struct ares_addrinfo)); |
168 | 0 | if (!ai) |
169 | 0 | return NULL; |
170 | | |
171 | 0 | *ai = empty_addrinfo; |
172 | 0 | return ai; |
173 | 0 | } |
174 | | |
175 | | static struct ares_addrinfo_node *ares__malloc_addrinfo_node(void) |
176 | 68.7k | { |
177 | 68.7k | struct ares_addrinfo_node *node = |
178 | 68.7k | ares_malloc(sizeof(*node)); |
179 | 68.7k | if (!node) |
180 | 0 | return NULL; |
181 | | |
182 | 68.7k | *node = empty_addrinfo_node; |
183 | 68.7k | return node; |
184 | 68.7k | } |
185 | | |
186 | | /* Allocate new addrinfo and append to the tail. */ |
187 | | struct ares_addrinfo_node *ares__append_addrinfo_node(struct ares_addrinfo_node **head) |
188 | 68.7k | { |
189 | 68.7k | struct ares_addrinfo_node *tail = ares__malloc_addrinfo_node(); |
190 | 68.7k | struct ares_addrinfo_node *last = *head; |
191 | 68.7k | if (!last) |
192 | 368 | { |
193 | 368 | *head = tail; |
194 | 368 | return tail; |
195 | 368 | } |
196 | | |
197 | 358M | while (last->ai_next) |
198 | 358M | { |
199 | 358M | last = last->ai_next; |
200 | 358M | } |
201 | | |
202 | 68.4k | last->ai_next = tail; |
203 | 68.4k | return tail; |
204 | 68.7k | } |
205 | | |
206 | | void ares__addrinfo_cat_nodes(struct ares_addrinfo_node **head, |
207 | | struct ares_addrinfo_node *tail) |
208 | 354 | { |
209 | 354 | struct ares_addrinfo_node *last = *head; |
210 | 354 | if (!last) |
211 | 354 | { |
212 | 354 | *head = tail; |
213 | 354 | return; |
214 | 354 | } |
215 | | |
216 | 0 | while (last->ai_next) |
217 | 0 | { |
218 | 0 | last = last->ai_next; |
219 | 0 | } |
220 | |
|
221 | 0 | last->ai_next = tail; |
222 | 0 | } |
223 | | |
224 | | /* Resolve service name into port number given in host byte order. |
225 | | * If not resolved, return 0. |
226 | | */ |
227 | | static unsigned short lookup_service(const char *service, int flags) |
228 | 0 | { |
229 | 0 | const char *proto; |
230 | 0 | struct servent *sep; |
231 | | #ifdef HAVE_GETSERVBYNAME_R |
232 | | struct servent se; |
233 | | char tmpbuf[4096]; |
234 | | #endif |
235 | |
|
236 | 0 | if (service) |
237 | 0 | { |
238 | 0 | if (flags & ARES_NI_UDP) |
239 | 0 | proto = "udp"; |
240 | 0 | else if (flags & ARES_NI_SCTP) |
241 | 0 | proto = "sctp"; |
242 | 0 | else if (flags & ARES_NI_DCCP) |
243 | 0 | proto = "dccp"; |
244 | 0 | else |
245 | 0 | proto = "tcp"; |
246 | | #ifdef HAVE_GETSERVBYNAME_R |
247 | | memset(&se, 0, sizeof(se)); |
248 | | sep = &se; |
249 | | memset(tmpbuf, 0, sizeof(tmpbuf)); |
250 | | #if GETSERVBYNAME_R_ARGS == 6 |
251 | | if (getservbyname_r(service, proto, &se, (void *)tmpbuf, sizeof(tmpbuf), |
252 | | &sep) != 0) |
253 | | sep = NULL; /* LCOV_EXCL_LINE: buffer large so this never fails */ |
254 | | #elif GETSERVBYNAME_R_ARGS == 5 |
255 | | sep = |
256 | | getservbyname_r(service, proto, &se, (void *)tmpbuf, sizeof(tmpbuf)); |
257 | | #elif GETSERVBYNAME_R_ARGS == 4 |
258 | | if (getservbyname_r(service, proto, &se, (void *)tmpbuf) != 0) |
259 | | sep = NULL; |
260 | | #else |
261 | | /* Lets just hope the OS uses TLS! */ |
262 | | sep = getservbyname(service, proto); |
263 | | #endif |
264 | | #else |
265 | | /* Lets just hope the OS uses TLS! */ |
266 | | #if (defined(NETWARE) && !defined(__NOVELL_LIBC__)) |
267 | | sep = getservbyname(service, (char *)proto); |
268 | | #else |
269 | 0 | sep = getservbyname(service, proto); |
270 | 0 | #endif |
271 | 0 | #endif |
272 | 0 | return (sep ? ntohs((unsigned short)sep->s_port) : 0); |
273 | 0 | } |
274 | 0 | return 0; |
275 | 0 | } |
276 | | |
277 | | /* If the name looks like an IP address or an error occured, |
278 | | * fake up a host entry, end the query immediately, and return true. |
279 | | * Otherwise return false. |
280 | | */ |
281 | | static int fake_addrinfo(const char *name, |
282 | | unsigned short port, |
283 | | const struct ares_addrinfo_hints *hints, |
284 | | struct ares_addrinfo *ai, |
285 | | ares_addrinfo_callback callback, |
286 | | void *arg) |
287 | 0 | { |
288 | 0 | struct ares_addrinfo_cname *cname; |
289 | 0 | int status = ARES_SUCCESS; |
290 | 0 | int result = 0; |
291 | 0 | int family = hints->ai_family; |
292 | 0 | if (family == AF_INET || family == AF_INET6 || family == AF_UNSPEC) |
293 | 0 | { |
294 | | /* It only looks like an IP address if it's all numbers and dots. */ |
295 | 0 | int numdots = 0, valid = 1; |
296 | 0 | const char *p; |
297 | 0 | for (p = name; *p; p++) |
298 | 0 | { |
299 | 0 | if (!ISDIGIT(*p) && *p != '.') |
300 | 0 | { |
301 | 0 | valid = 0; |
302 | 0 | break; |
303 | 0 | } |
304 | 0 | else if (*p == '.') |
305 | 0 | { |
306 | 0 | numdots++; |
307 | 0 | } |
308 | 0 | } |
309 | | |
310 | | /* if we don't have 3 dots, it is illegal |
311 | | * (although inet_pton doesn't think so). |
312 | | */ |
313 | 0 | if (numdots != 3 || !valid) |
314 | 0 | result = 0; |
315 | 0 | else |
316 | 0 | { |
317 | 0 | struct in_addr addr4; |
318 | 0 | result = ares_inet_pton(AF_INET, name, &addr4) < 1 ? 0 : 1; |
319 | 0 | if (result) |
320 | 0 | { |
321 | 0 | status = ares_append_ai_node(AF_INET, port, 0, &addr4, &ai->nodes); |
322 | 0 | if (status != ARES_SUCCESS) |
323 | 0 | { |
324 | 0 | callback(arg, status, 0, NULL); |
325 | 0 | return 1; |
326 | 0 | } |
327 | 0 | } |
328 | 0 | } |
329 | 0 | } |
330 | | |
331 | 0 | if (!result && (family == AF_INET6 || family == AF_UNSPEC)) |
332 | 0 | { |
333 | 0 | struct ares_in6_addr addr6; |
334 | 0 | result = ares_inet_pton(AF_INET6, name, &addr6) < 1 ? 0 : 1; |
335 | 0 | if (result) |
336 | 0 | { |
337 | 0 | status = ares_append_ai_node(AF_INET6, port, 0, &addr6, &ai->nodes); |
338 | 0 | if (status != ARES_SUCCESS) |
339 | 0 | { |
340 | 0 | callback(arg, status, 0, NULL); |
341 | 0 | return 1; |
342 | 0 | } |
343 | 0 | } |
344 | 0 | } |
345 | | |
346 | 0 | if (!result) |
347 | 0 | return 0; |
348 | | |
349 | 0 | if (hints->ai_flags & ARES_AI_CANONNAME) |
350 | 0 | { |
351 | 0 | cname = ares__append_addrinfo_cname(&ai->cnames); |
352 | 0 | if (!cname) |
353 | 0 | { |
354 | 0 | ares_freeaddrinfo(ai); |
355 | 0 | callback(arg, ARES_ENOMEM, 0, NULL); |
356 | 0 | return 1; |
357 | 0 | } |
358 | | |
359 | | /* Duplicate the name, to avoid a constness violation. */ |
360 | 0 | cname->name = ares_strdup(name); |
361 | 0 | if (!cname->name) |
362 | 0 | { |
363 | 0 | ares_freeaddrinfo(ai); |
364 | 0 | callback(arg, ARES_ENOMEM, 0, NULL); |
365 | 0 | return 1; |
366 | 0 | } |
367 | 0 | } |
368 | | |
369 | 0 | ai->nodes->ai_socktype = hints->ai_socktype; |
370 | 0 | ai->nodes->ai_protocol = hints->ai_protocol; |
371 | |
|
372 | 0 | callback(arg, ARES_SUCCESS, 0, ai); |
373 | 0 | return 1; |
374 | 0 | } |
375 | | |
376 | | static void end_hquery(struct host_query *hquery, int status) |
377 | 0 | { |
378 | 0 | struct ares_addrinfo_node sentinel; |
379 | 0 | struct ares_addrinfo_node *next; |
380 | 0 | if (status == ARES_SUCCESS) |
381 | 0 | { |
382 | 0 | if (!(hquery->hints.ai_flags & ARES_AI_NOSORT) && hquery->ai->nodes) |
383 | 0 | { |
384 | 0 | sentinel.ai_next = hquery->ai->nodes; |
385 | 0 | ares__sortaddrinfo(hquery->channel, &sentinel); |
386 | 0 | hquery->ai->nodes = sentinel.ai_next; |
387 | 0 | } |
388 | 0 | next = hquery->ai->nodes; |
389 | |
|
390 | 0 | while (next) |
391 | 0 | { |
392 | 0 | next->ai_socktype = hquery->hints.ai_socktype; |
393 | 0 | next->ai_protocol = hquery->hints.ai_protocol; |
394 | 0 | next = next->ai_next; |
395 | 0 | } |
396 | 0 | } |
397 | 0 | else |
398 | 0 | { |
399 | | /* Clean up what we have collected by so far. */ |
400 | 0 | ares_freeaddrinfo(hquery->ai); |
401 | 0 | hquery->ai = NULL; |
402 | 0 | } |
403 | |
|
404 | 0 | hquery->callback(hquery->arg, status, hquery->timeouts, hquery->ai); |
405 | 0 | ares_free(hquery->name); |
406 | 0 | ares_free(hquery); |
407 | 0 | } |
408 | | |
409 | | static int is_localhost(const char *name) |
410 | 0 | { |
411 | | /* RFC6761 6.3 says : The domain "localhost." and any names falling within ".localhost." */ |
412 | 0 | size_t len; |
413 | |
|
414 | 0 | if (name == NULL) |
415 | 0 | return 0; |
416 | | |
417 | 0 | if (strcmp(name, "localhost") == 0) |
418 | 0 | return 1; |
419 | | |
420 | 0 | len = strlen(name); |
421 | 0 | if (len < 10 /* strlen(".localhost") */) |
422 | 0 | return 0; |
423 | | |
424 | 0 | if (strcmp(name + (len - 10 /* strlen(".localhost") */), ".localhost") == 0) |
425 | 0 | return 1; |
426 | | |
427 | 0 | return 0; |
428 | 0 | } |
429 | | |
430 | | static int file_lookup(struct host_query *hquery) |
431 | 0 | { |
432 | 0 | FILE *fp; |
433 | 0 | int error; |
434 | 0 | int status; |
435 | 0 | char *path_hosts = NULL; |
436 | |
|
437 | 0 | if (hquery->hints.ai_flags & ARES_AI_ENVHOSTS) |
438 | 0 | { |
439 | 0 | path_hosts = ares_strdup(getenv("CARES_HOSTS")); |
440 | 0 | if (!path_hosts) |
441 | 0 | return ARES_ENOMEM; |
442 | 0 | } |
443 | | |
444 | 0 | if (hquery->channel->hosts_path) |
445 | 0 | { |
446 | 0 | path_hosts = ares_strdup(hquery->channel->hosts_path); |
447 | 0 | if (!path_hosts) |
448 | 0 | return ARES_ENOMEM; |
449 | 0 | } |
450 | | |
451 | 0 | if (!path_hosts) |
452 | 0 | { |
453 | | #ifdef WIN32 |
454 | | char PATH_HOSTS[MAX_PATH]; |
455 | | win_platform platform; |
456 | | |
457 | | PATH_HOSTS[0] = '\0'; |
458 | | |
459 | | platform = ares__getplatform(); |
460 | | |
461 | | if (platform == WIN_NT) |
462 | | { |
463 | | char tmp[MAX_PATH]; |
464 | | HKEY hkeyHosts; |
465 | | |
466 | | if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ, |
467 | | &hkeyHosts) == ERROR_SUCCESS) |
468 | | { |
469 | | DWORD dwLength = MAX_PATH; |
470 | | RegQueryValueExA(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp, |
471 | | &dwLength); |
472 | | ExpandEnvironmentStringsA(tmp, PATH_HOSTS, MAX_PATH); |
473 | | RegCloseKey(hkeyHosts); |
474 | | } |
475 | | } |
476 | | else if (platform == WIN_9X) |
477 | | GetWindowsDirectoryA(PATH_HOSTS, MAX_PATH); |
478 | | else |
479 | | return ARES_ENOTFOUND; |
480 | | |
481 | | strcat(PATH_HOSTS, WIN_PATH_HOSTS); |
482 | | #elif defined(WATT32) |
483 | | const char *PATH_HOSTS = _w32_GetHostsFile(); |
484 | | |
485 | | if (!PATH_HOSTS) |
486 | | return ARES_ENOTFOUND; |
487 | | #endif |
488 | 0 | path_hosts = ares_strdup(PATH_HOSTS); |
489 | 0 | if (!path_hosts) |
490 | 0 | return ARES_ENOMEM; |
491 | 0 | } |
492 | | |
493 | 0 | fp = fopen(path_hosts, "r"); |
494 | 0 | if (!fp) |
495 | 0 | { |
496 | 0 | error = ERRNO; |
497 | 0 | switch (error) |
498 | 0 | { |
499 | 0 | case ENOENT: |
500 | 0 | case ESRCH: |
501 | 0 | status = ARES_ENOTFOUND; |
502 | 0 | break; |
503 | 0 | default: |
504 | 0 | DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", error, |
505 | 0 | strerror(error))); |
506 | 0 | DEBUGF(fprintf(stderr, "Error opening file: %s\n", path_hosts)); |
507 | 0 | status = ARES_EFILE; |
508 | 0 | break; |
509 | 0 | } |
510 | 0 | } |
511 | 0 | else |
512 | 0 | { |
513 | 0 | status = ares__readaddrinfo(fp, hquery->name, hquery->port, &hquery->hints, hquery->ai); |
514 | 0 | fclose(fp); |
515 | 0 | } |
516 | 0 | ares_free(path_hosts); |
517 | | |
518 | | /* RFC6761 section 6.3 #3 states that "Name resolution APIs and libraries |
519 | | * SHOULD recognize localhost names as special and SHOULD always return the |
520 | | * IP loopback address for address queries". |
521 | | * We will also ignore ALL errors when trying to resolve localhost, such |
522 | | * as permissions errors reading /etc/hosts or a malformed /etc/hosts */ |
523 | 0 | if (status != ARES_SUCCESS && is_localhost(hquery->name)) |
524 | 0 | { |
525 | 0 | return ares__addrinfo_localhost(hquery->name, hquery->port, |
526 | 0 | &hquery->hints, hquery->ai); |
527 | 0 | } |
528 | | |
529 | 0 | return status; |
530 | 0 | } |
531 | | |
532 | | static void next_lookup(struct host_query *hquery, int status) |
533 | 0 | { |
534 | 0 | switch (*hquery->remaining_lookups) |
535 | 0 | { |
536 | 0 | case 'b': |
537 | | /* RFC6761 section 6.3 #3 says "Name resolution APIs SHOULD NOT send |
538 | | * queries for localhost names to their configured caching DNS |
539 | | * server(s)." */ |
540 | 0 | if (!is_localhost(hquery->name)) |
541 | 0 | { |
542 | | /* DNS lookup */ |
543 | 0 | if (next_dns_lookup(hquery)) |
544 | 0 | break; |
545 | 0 | } |
546 | | |
547 | 0 | hquery->remaining_lookups++; |
548 | 0 | next_lookup(hquery, status); |
549 | 0 | break; |
550 | | |
551 | 0 | case 'f': |
552 | | /* Host file lookup */ |
553 | 0 | if (file_lookup(hquery) == ARES_SUCCESS) |
554 | 0 | { |
555 | 0 | end_hquery(hquery, ARES_SUCCESS); |
556 | 0 | break; |
557 | 0 | } |
558 | 0 | hquery->remaining_lookups++; |
559 | 0 | next_lookup(hquery, status); |
560 | 0 | break; |
561 | 0 | default: |
562 | | /* No lookup left */ |
563 | 0 | end_hquery(hquery, status); |
564 | 0 | break; |
565 | 0 | } |
566 | 0 | } |
567 | | |
568 | | static void host_callback(void *arg, int status, int timeouts, |
569 | | unsigned char *abuf, int alen) |
570 | 0 | { |
571 | 0 | struct host_query *hquery = (struct host_query*)arg; |
572 | 0 | int addinfostatus = ARES_SUCCESS; |
573 | 0 | hquery->timeouts += timeouts; |
574 | 0 | hquery->remaining--; |
575 | |
|
576 | 0 | if (status == ARES_SUCCESS) |
577 | 0 | { |
578 | 0 | addinfostatus = ares__parse_into_addrinfo(abuf, alen, 1, hquery->port, hquery->ai); |
579 | 0 | } |
580 | |
|
581 | 0 | if (!hquery->remaining) |
582 | 0 | { |
583 | 0 | if (addinfostatus != ARES_SUCCESS && addinfostatus != ARES_ENODATA) |
584 | 0 | { |
585 | | /* error in parsing result e.g. no memory */ |
586 | 0 | if (addinfostatus == ARES_EBADRESP && hquery->ai->nodes) |
587 | 0 | { |
588 | | /* We got a bad response from server, but at least one query |
589 | | * ended with ARES_SUCCESS */ |
590 | 0 | end_hquery(hquery, ARES_SUCCESS); |
591 | 0 | } |
592 | 0 | else |
593 | 0 | { |
594 | 0 | end_hquery(hquery, addinfostatus); |
595 | 0 | } |
596 | 0 | } |
597 | 0 | else if (hquery->ai->nodes) |
598 | 0 | { |
599 | | /* at least one query ended with ARES_SUCCESS */ |
600 | 0 | end_hquery(hquery, ARES_SUCCESS); |
601 | 0 | } |
602 | 0 | else if (status == ARES_ENOTFOUND || status == ARES_ENODATA || |
603 | 0 | addinfostatus == ARES_ENODATA) |
604 | 0 | { |
605 | 0 | if (status == ARES_ENODATA || addinfostatus == ARES_ENODATA) |
606 | 0 | hquery->nodata_cnt++; |
607 | 0 | next_lookup(hquery, hquery->nodata_cnt?ARES_ENODATA:status); |
608 | 0 | } |
609 | 0 | else if (status == ARES_EDESTRUCTION) |
610 | 0 | { |
611 | | /* NOTE: Could also be ARES_EDESTRUCTION. We need to only call this |
612 | | * once all queries (there can be multiple for getaddrinfo) are |
613 | | * terminated. */ |
614 | 0 | end_hquery(hquery, status); |
615 | 0 | } |
616 | 0 | else |
617 | 0 | { |
618 | 0 | end_hquery(hquery, status); |
619 | 0 | } |
620 | 0 | } |
621 | | |
622 | | /* at this point we keep on waiting for the next query to finish */ |
623 | 0 | } |
624 | | |
625 | | void ares_getaddrinfo(ares_channel channel, |
626 | | const char* name, const char* service, |
627 | | const struct ares_addrinfo_hints* hints, |
628 | | ares_addrinfo_callback callback, void* arg) |
629 | 0 | { |
630 | 0 | struct host_query *hquery; |
631 | 0 | unsigned short port = 0; |
632 | 0 | int family; |
633 | 0 | struct ares_addrinfo *ai; |
634 | 0 | char *alias_name = NULL; |
635 | 0 | int status; |
636 | |
|
637 | 0 | if (!hints) |
638 | 0 | { |
639 | 0 | hints = &default_hints; |
640 | 0 | } |
641 | |
|
642 | 0 | family = hints->ai_family; |
643 | | |
644 | | /* Right now we only know how to look up Internet addresses |
645 | | and unspec means try both basically. */ |
646 | 0 | if (family != AF_INET && |
647 | 0 | family != AF_INET6 && |
648 | 0 | family != AF_UNSPEC) |
649 | 0 | { |
650 | 0 | callback(arg, ARES_ENOTIMP, 0, NULL); |
651 | 0 | return; |
652 | 0 | } |
653 | | |
654 | 0 | if (ares__is_onion_domain(name)) |
655 | 0 | { |
656 | 0 | callback(arg, ARES_ENOTFOUND, 0, NULL); |
657 | 0 | return; |
658 | 0 | } |
659 | | |
660 | | /* perform HOSTALIAS resolution (technically this function does some other |
661 | | * things we are going to ignore) */ |
662 | 0 | status = ares__single_domain(channel, name, &alias_name); |
663 | 0 | if (status != ARES_SUCCESS) { |
664 | 0 | callback(arg, status, 0, NULL); |
665 | 0 | return; |
666 | 0 | } |
667 | | |
668 | 0 | if (alias_name) |
669 | 0 | name = alias_name; |
670 | |
|
671 | 0 | if (service) |
672 | 0 | { |
673 | 0 | if (hints->ai_flags & ARES_AI_NUMERICSERV) |
674 | 0 | { |
675 | 0 | unsigned long val; |
676 | 0 | errno = 0; |
677 | 0 | val = strtoul(service, NULL, 0); |
678 | 0 | if ((val == 0 && errno != 0) || val > 65535) |
679 | 0 | { |
680 | 0 | ares_free(alias_name); |
681 | 0 | callback(arg, ARES_ESERVICE, 0, NULL); |
682 | 0 | return; |
683 | 0 | } |
684 | 0 | port = (unsigned short)val; |
685 | 0 | } |
686 | 0 | else |
687 | 0 | { |
688 | 0 | port = lookup_service(service, 0); |
689 | 0 | if (!port) |
690 | 0 | { |
691 | 0 | unsigned long val; |
692 | 0 | errno = 0; |
693 | 0 | val = strtoul(service, NULL, 0); |
694 | 0 | if ((val == 0 && errno != 0) || val > 65535) |
695 | 0 | { |
696 | 0 | ares_free(alias_name); |
697 | 0 | callback(arg, ARES_ESERVICE, 0, NULL); |
698 | 0 | return; |
699 | 0 | } |
700 | 0 | port = (unsigned short)val; |
701 | 0 | } |
702 | 0 | } |
703 | 0 | } |
704 | | |
705 | 0 | ai = ares__malloc_addrinfo(); |
706 | 0 | if (!ai) |
707 | 0 | { |
708 | 0 | ares_free(alias_name); |
709 | 0 | callback(arg, ARES_ENOMEM, 0, NULL); |
710 | 0 | return; |
711 | 0 | } |
712 | | |
713 | 0 | if (fake_addrinfo(name, port, hints, ai, callback, arg)) |
714 | 0 | { |
715 | 0 | ares_free(alias_name); |
716 | 0 | return; |
717 | 0 | } |
718 | | |
719 | | /* Allocate and fill in the host query structure. */ |
720 | 0 | hquery = ares_malloc(sizeof(struct host_query)); |
721 | 0 | if (!hquery) |
722 | 0 | { |
723 | 0 | ares_free(alias_name); |
724 | 0 | ares_freeaddrinfo(ai); |
725 | 0 | callback(arg, ARES_ENOMEM, 0, NULL); |
726 | 0 | return; |
727 | 0 | } |
728 | | |
729 | 0 | hquery->name = ares_strdup(name); |
730 | 0 | ares_free(alias_name); |
731 | 0 | if (!hquery->name) |
732 | 0 | { |
733 | 0 | ares_free(hquery); |
734 | 0 | ares_freeaddrinfo(ai); |
735 | 0 | callback(arg, ARES_ENOMEM, 0, NULL); |
736 | 0 | return; |
737 | 0 | } |
738 | | |
739 | 0 | hquery->port = port; |
740 | 0 | hquery->channel = channel; |
741 | 0 | hquery->hints = *hints; |
742 | 0 | hquery->sent_family = -1; /* nothing is sent yet */ |
743 | 0 | hquery->callback = callback; |
744 | 0 | hquery->arg = arg; |
745 | 0 | hquery->remaining_lookups = channel->lookups; |
746 | 0 | hquery->timeouts = 0; |
747 | 0 | hquery->ai = ai; |
748 | 0 | hquery->next_domain = -1; |
749 | 0 | hquery->remaining = 0; |
750 | 0 | hquery->nodata_cnt = 0; |
751 | | |
752 | | /* Start performing lookups according to channel->lookups. */ |
753 | 0 | next_lookup(hquery, ARES_ECONNREFUSED /* initial error code */); |
754 | 0 | } |
755 | | |
756 | | static int next_dns_lookup(struct host_query *hquery) |
757 | 0 | { |
758 | 0 | char *s = NULL; |
759 | 0 | int is_s_allocated = 0; |
760 | 0 | int status; |
761 | | |
762 | | /* if next_domain == -1 and as_is_first is true, try hquery->name */ |
763 | 0 | if (hquery->next_domain == -1) |
764 | 0 | { |
765 | 0 | if (as_is_first(hquery)) |
766 | 0 | { |
767 | 0 | s = hquery->name; |
768 | 0 | } |
769 | 0 | hquery->next_domain = 0; |
770 | 0 | } |
771 | | |
772 | | /* if as_is_first is false, try hquery->name at last */ |
773 | 0 | if (!s && hquery->next_domain == hquery->channel->ndomains) { |
774 | 0 | if (!as_is_first(hquery)) |
775 | 0 | { |
776 | 0 | s = hquery->name; |
777 | 0 | } |
778 | 0 | hquery->next_domain++; |
779 | 0 | } |
780 | |
|
781 | 0 | if (!s && hquery->next_domain < hquery->channel->ndomains && !as_is_only(hquery)) |
782 | 0 | { |
783 | 0 | status = ares__cat_domain( |
784 | 0 | hquery->name, |
785 | 0 | hquery->channel->domains[hquery->next_domain++], |
786 | 0 | &s); |
787 | 0 | if (status == ARES_SUCCESS) |
788 | 0 | { |
789 | 0 | is_s_allocated = 1; |
790 | 0 | } |
791 | 0 | } |
792 | |
|
793 | 0 | if (s) |
794 | 0 | { |
795 | 0 | switch (hquery->hints.ai_family) |
796 | 0 | { |
797 | 0 | case AF_INET: |
798 | 0 | hquery->remaining += 1; |
799 | 0 | ares_query(hquery->channel, s, C_IN, T_A, host_callback, hquery); |
800 | 0 | break; |
801 | 0 | case AF_INET6: |
802 | 0 | hquery->remaining += 1; |
803 | 0 | ares_query(hquery->channel, s, C_IN, T_AAAA, host_callback, hquery); |
804 | 0 | break; |
805 | 0 | case AF_UNSPEC: |
806 | 0 | hquery->remaining += 2; |
807 | 0 | ares_query(hquery->channel, s, C_IN, T_A, host_callback, hquery); |
808 | 0 | ares_query(hquery->channel, s, C_IN, T_AAAA, host_callback, hquery); |
809 | 0 | break; |
810 | 0 | default: break; |
811 | 0 | } |
812 | 0 | if (is_s_allocated) |
813 | 0 | { |
814 | 0 | ares_free(s); |
815 | 0 | } |
816 | 0 | return 1; |
817 | 0 | } |
818 | 0 | else |
819 | 0 | { |
820 | 0 | assert(!hquery->ai->nodes); |
821 | 0 | return 0; |
822 | 0 | } |
823 | 0 | } |
824 | | |
825 | | static int as_is_first(const struct host_query* hquery) |
826 | 0 | { |
827 | 0 | char* p; |
828 | 0 | int ndots = 0; |
829 | 0 | size_t nname = hquery->name?strlen(hquery->name):0; |
830 | 0 | for (p = hquery->name; p && *p; p++) |
831 | 0 | { |
832 | 0 | if (*p == '.') |
833 | 0 | { |
834 | 0 | ndots++; |
835 | 0 | } |
836 | 0 | } |
837 | 0 | if (nname && hquery->name[nname-1] == '.') |
838 | 0 | { |
839 | | /* prevent ARES_EBADNAME for valid FQDN, where ndots < channel->ndots */ |
840 | 0 | return 1; |
841 | 0 | } |
842 | 0 | return ndots >= hquery->channel->ndots; |
843 | 0 | } |
844 | | |
845 | | static int as_is_only(const struct host_query* hquery) |
846 | 0 | { |
847 | 0 | size_t nname = hquery->name?strlen(hquery->name):0; |
848 | 0 | if (nname && hquery->name[nname-1] == '.') |
849 | 0 | return 1; |
850 | 0 | return 0; |
851 | 0 | } |
852 | | |