Coverage Report

Created: 2026-05-30 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/c-ares/src/lib/ares_getaddrinfo.c
Line
Count
Source
1
/* MIT License
2
 *
3
 * Copyright (c) 1998, 2011, 2013 Massachusetts Institute of Technology
4
 * Copyright (c) 2017 Christian Ammer
5
 * Copyright (c) 2019 Andrew Selivanov
6
 *
7
 * Permission is hereby granted, free of charge, to any person obtaining a copy
8
 * of this software and associated documentation files (the "Software"), to deal
9
 * in the Software without restriction, including without limitation the rights
10
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
 * copies of the Software, and to permit persons to whom the Software is
12
 * furnished to do so, subject to the following conditions:
13
 *
14
 * The above copyright notice and this permission notice (including the next
15
 * paragraph) shall be included in all copies or substantial portions of the
16
 * Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
 * SOFTWARE.
25
 *
26
 * SPDX-License-Identifier: MIT
27
 */
28
29
#include "ares_private.h"
30
31
#ifdef HAVE_GETSERVBYNAME_R
32
#  if !defined(GETSERVBYNAME_R_ARGS) || (GETSERVBYNAME_R_ARGS < 4) || \
33
    (GETSERVBYNAME_R_ARGS > 6)
34
#    error "you MUST specify a valid number of arguments for getservbyname_r"
35
#  endif
36
#endif
37
38
#ifdef HAVE_NETINET_IN_H
39
#  include <netinet/in.h>
40
#endif
41
#ifdef HAVE_NETDB_H
42
#  include <netdb.h>
43
#endif
44
#ifdef HAVE_ARPA_INET_H
45
#  include <arpa/inet.h>
46
#endif
47
48
#include "ares_nameser.h"
49
50
#ifdef HAVE_STRINGS_H
51
#  include <strings.h>
52
#endif
53
#include <assert.h>
54
55
#ifdef HAVE_LIMITS_H
56
#  include <limits.h>
57
#endif
58
59
#include "ares_dns.h"
60
61
struct host_query {
62
  ares_channel_t            *channel;
63
  char                      *name;
64
  unsigned short             port; /* in host order */
65
  ares_addrinfo_callback     callback;
66
  void                      *arg;
67
  struct ares_addrinfo_hints hints;
68
  int    sent_family; /* this family is what was is being used */
69
  size_t timeouts;    /* number of timeouts we saw for this request */
70
  char  *lookups; /* Duplicate memory from channel because of ares_reinit() */
71
  const char *remaining_lookups; /* types of lookup we need to perform ("fb" by
72
                                    default, file and dns respectively) */
73
74
  /* Search order for names */
75
  char      **names;
76
  size_t      names_cnt;
77
  size_t      next_name_idx;       /* next name index being attempted */
78
79
  struct ares_addrinfo *ai;        /* store results between lookups */
80
  unsigned short        qid_a;     /* qid for A request */
81
  unsigned short        qid_aaaa;  /* qid for AAAA request */
82
83
  size_t                remaining; /* number of DNS answers waiting for */
84
85
  /* Track nodata responses to possibly override final result */
86
  size_t                nodata_cnt;
87
};
88
89
static const struct ares_addrinfo_hints default_hints = {
90
  0,         /* ai_flags */
91
  AF_UNSPEC, /* ai_family */
92
  0,         /* ai_socktype */
93
  0,         /* ai_protocol */
94
};
95
96
/* forward declarations */
97
static ares_bool_t next_dns_lookup(struct host_query *hquery);
98
99
struct ares_addrinfo_cname *
100
  ares_append_addrinfo_cname(struct ares_addrinfo_cname **head)
101
0
{
102
0
  struct ares_addrinfo_cname *tail = ares_malloc_zero(sizeof(*tail));
103
0
  struct ares_addrinfo_cname *last = *head;
104
105
0
  if (tail == NULL) {
106
0
    return NULL; /* LCOV_EXCL_LINE: OutOfMemory */
107
0
  }
108
109
0
  if (!last) {
110
0
    *head = tail;
111
0
    return tail;
112
0
  }
113
114
0
  while (last->next) {
115
0
    last = last->next;
116
0
  }
117
118
0
  last->next = tail;
119
0
  return tail;
120
0
}
121
122
void ares_addrinfo_cat_cnames(struct ares_addrinfo_cname **head,
123
                              struct ares_addrinfo_cname  *tail)
124
0
{
125
0
  struct ares_addrinfo_cname *last = *head;
126
0
  if (!last) {
127
0
    *head = tail;
128
0
    return;
129
0
  }
130
131
0
  while (last->next) {
132
0
    last = last->next;
133
0
  }
134
135
0
  last->next = tail;
136
0
}
137
138
/* Allocate new addrinfo and append to the tail. */
139
struct ares_addrinfo_node *
140
  ares_append_addrinfo_node(struct ares_addrinfo_node **head)
141
0
{
142
0
  struct ares_addrinfo_node *tail = ares_malloc_zero(sizeof(*tail));
143
0
  struct ares_addrinfo_node *last = *head;
144
145
0
  if (tail == NULL) {
146
0
    return NULL; /* LCOV_EXCL_LINE: OutOfMemory */
147
0
  }
148
149
0
  if (!last) {
150
0
    *head = tail;
151
0
    return tail;
152
0
  }
153
154
0
  while (last->ai_next) {
155
0
    last = last->ai_next;
156
0
  }
157
158
0
  last->ai_next = tail;
159
0
  return tail;
160
0
}
161
162
void ares_addrinfo_cat_nodes(struct ares_addrinfo_node **head,
163
                             struct ares_addrinfo_node  *tail)
164
0
{
165
0
  struct ares_addrinfo_node *last = *head;
166
0
  if (!last) {
167
0
    *head = tail;
168
0
    return;
169
0
  }
170
171
0
  while (last->ai_next) {
172
0
    last = last->ai_next;
173
0
  }
174
175
0
  last->ai_next = tail;
176
0
}
177
178
/* Resolve service name into port number given in host byte order.
179
 * If not resolved, return 0.
180
 */
181
static unsigned short lookup_service(const char *service, int flags)
182
0
{
183
0
  const char     *proto;
184
0
  struct servent *sep;
185
#ifdef HAVE_GETSERVBYNAME_R
186
  struct servent se;
187
  char           tmpbuf[4096];
188
#endif
189
190
0
  if (service) {
191
0
    if (flags & ARES_NI_UDP) {
192
0
      proto = "udp";
193
0
    } else if (flags & ARES_NI_SCTP) {
194
0
      proto = "sctp";
195
0
    } else if (flags & ARES_NI_DCCP) {
196
0
      proto = "dccp";
197
0
    } else {
198
0
      proto = "tcp";
199
0
    }
200
#ifdef HAVE_GETSERVBYNAME_R
201
    memset(&se, 0, sizeof(se));
202
    sep = &se;
203
    memset(tmpbuf, 0, sizeof(tmpbuf));
204
#  if GETSERVBYNAME_R_ARGS == 6
205
    if (getservbyname_r(service, proto, &se, (void *)tmpbuf, sizeof(tmpbuf),
206
                        &sep) != 0) {
207
      sep = NULL; /* LCOV_EXCL_LINE: buffer large so this never fails */
208
    }
209
#  elif GETSERVBYNAME_R_ARGS == 5
210
    sep = getservbyname_r(service, proto, &se, (void *)tmpbuf, sizeof(tmpbuf));
211
#  elif GETSERVBYNAME_R_ARGS == 4
212
    if (getservbyname_r(service, proto, &se, (void *)tmpbuf) != 0) {
213
      sep = NULL;
214
    }
215
#  else
216
    /* Lets just hope the OS uses TLS! */
217
    sep = getservbyname(service, proto);
218
#  endif
219
#else
220
    /* Lets just hope the OS uses TLS! */
221
#  if (defined(NETWARE) && !defined(__NOVELL_LIBC__))
222
    sep = getservbyname(service, (char *)proto);
223
#  else
224
0
    sep = getservbyname(service, proto);
225
0
#  endif
226
0
#endif
227
0
    return (sep ? ntohs((unsigned short)sep->s_port) : 0);
228
0
  }
229
0
  return 0;
230
0
}
231
232
/* If the name looks like an IP address or an error occurred,
233
 * fake up a host entry, end the query immediately, and return true.
234
 * Otherwise return false.
235
 */
236
static ares_bool_t fake_addrinfo(const char *name, unsigned short port,
237
                                 const struct ares_addrinfo_hints *hints,
238
                                 struct ares_addrinfo             *ai,
239
                                 ares_addrinfo_callback callback, void *arg)
240
0
{
241
0
  struct ares_addrinfo_cname *cname;
242
0
  ares_status_t               status = ARES_SUCCESS;
243
0
  ares_bool_t                 result = ARES_FALSE;
244
0
  int                         family = hints->ai_family;
245
0
  if (family == AF_INET || family == AF_INET6 || family == AF_UNSPEC) {
246
    /* It only looks like an IP address if it's all numbers and dots. */
247
0
    size_t      numdots = 0;
248
0
    ares_bool_t valid   = ARES_TRUE;
249
0
    const char *p;
250
0
    for (p = name; *p; p++) {
251
0
      if (!ares_isdigit(*p) && *p != '.') {
252
0
        valid = ARES_FALSE;
253
0
        break;
254
0
      } else if (*p == '.') {
255
0
        numdots++;
256
0
      }
257
0
    }
258
259
    /* if we don't have 3 dots, it is illegal
260
     * (although inet_pton doesn't think so).
261
     */
262
0
    if (numdots != 3 || !valid) {
263
0
      result = ARES_FALSE;
264
0
    } else {
265
0
      struct in_addr addr4;
266
0
      result =
267
0
        ares_inet_pton(AF_INET, name, &addr4) < 1 ? ARES_FALSE : ARES_TRUE;
268
0
      if (result) {
269
0
        status = ares_append_ai_node(AF_INET, port, 0, &addr4, &ai->nodes);
270
0
        if (status != ARES_SUCCESS) {
271
0
          ares_freeaddrinfo(ai);
272
0
          callback(arg, (int)status, 0, NULL); /* LCOV_EXCL_LINE: OutOfMemory */
273
0
          return ARES_TRUE;                    /* LCOV_EXCL_LINE: OutOfMemory */
274
0
        }
275
0
      }
276
0
    }
277
0
  }
278
279
0
  if (!result && (family == AF_INET6 || family == AF_UNSPEC)) {
280
0
    struct ares_in6_addr addr6;
281
0
    result =
282
0
      ares_inet_pton(AF_INET6, name, &addr6) < 1 ? ARES_FALSE : ARES_TRUE;
283
0
    if (result) {
284
0
      status = ares_append_ai_node(AF_INET6, port, 0, &addr6, &ai->nodes);
285
0
      if (status != ARES_SUCCESS) {
286
0
        ares_freeaddrinfo(ai);
287
0
        callback(arg, (int)status, 0, NULL); /* LCOV_EXCL_LINE: OutOfMemory */
288
0
        return ARES_TRUE;                    /* LCOV_EXCL_LINE: OutOfMemory */
289
0
      }
290
0
    }
291
0
  }
292
293
0
  if (!result) {
294
0
    return ARES_FALSE;
295
0
  }
296
297
0
  if (hints->ai_flags & ARES_AI_CANONNAME) {
298
0
    cname = ares_append_addrinfo_cname(&ai->cnames);
299
0
    if (!cname) {
300
      /* LCOV_EXCL_START: OutOfMemory */
301
0
      ares_freeaddrinfo(ai);
302
0
      callback(arg, ARES_ENOMEM, 0, NULL);
303
0
      return ARES_TRUE;
304
      /* LCOV_EXCL_STOP */
305
0
    }
306
307
    /* Duplicate the name, to avoid a constness violation. */
308
0
    cname->name = ares_strdup(name);
309
0
    if (!cname->name) {
310
0
      ares_freeaddrinfo(ai);
311
0
      callback(arg, ARES_ENOMEM, 0, NULL);
312
0
      return ARES_TRUE;
313
0
    }
314
0
  }
315
316
0
  ai->nodes->ai_socktype = hints->ai_socktype;
317
0
  ai->nodes->ai_protocol = hints->ai_protocol;
318
319
0
  callback(arg, ARES_SUCCESS, 0, ai);
320
0
  return ARES_TRUE;
321
0
}
322
323
static void hquery_free(struct host_query *hquery, ares_bool_t cleanup_ai)
324
0
{
325
0
  if (cleanup_ai) {
326
0
    ares_freeaddrinfo(hquery->ai);
327
0
  }
328
0
  ares_strsplit_free(hquery->names, hquery->names_cnt);
329
0
  ares_free(hquery->name);
330
0
  ares_free(hquery->lookups);
331
0
  ares_free(hquery);
332
0
}
333
334
static void end_hquery(struct host_query *hquery, ares_status_t status)
335
0
{
336
0
  struct ares_addrinfo_node  sentinel;
337
0
  struct ares_addrinfo_node *next;
338
339
0
  if (status == ARES_SUCCESS) {
340
0
    if (!(hquery->hints.ai_flags & ARES_AI_NOSORT) && hquery->ai->nodes) {
341
0
      sentinel.ai_next = hquery->ai->nodes;
342
0
      ares_sortaddrinfo(hquery->channel, &sentinel);
343
0
      hquery->ai->nodes = sentinel.ai_next;
344
0
    }
345
0
    next = hquery->ai->nodes;
346
347
0
    while (next) {
348
0
      next->ai_socktype = hquery->hints.ai_socktype;
349
0
      next->ai_protocol = hquery->hints.ai_protocol;
350
0
      next              = next->ai_next;
351
0
    }
352
0
  } else {
353
    /* Clean up what we have collected by so far. */
354
0
    ares_freeaddrinfo(hquery->ai);
355
0
    hquery->ai = NULL;
356
0
  }
357
358
0
  hquery->callback(hquery->arg, (int)status, (int)hquery->timeouts, hquery->ai);
359
0
  hquery_free(hquery, ARES_FALSE);
360
0
}
361
362
ares_bool_t ares_is_localhost(const char *name)
363
0
{
364
  /* RFC6761 6.3 says : The domain "localhost." and any names falling within
365
   * ".localhost." */
366
0
  size_t len;
367
368
0
  if (name == NULL) {
369
0
    return ARES_FALSE; /* LCOV_EXCL_LINE: DefensiveCoding */
370
0
  }
371
372
0
  if (ares_strcaseeq(name, "localhost")) {
373
0
    return ARES_TRUE;
374
0
  }
375
376
0
  len = ares_strlen(name);
377
0
  if (len < 10 /* strlen(".localhost") */) {
378
0
    return ARES_FALSE;
379
0
  }
380
381
0
  if (ares_strcaseeq(name + (len - 10 /* strlen(".localhost") */),
382
0
                     ".localhost")) {
383
0
    return ARES_TRUE;
384
0
  }
385
386
0
  return ARES_FALSE;
387
0
}
388
389
static ares_status_t file_lookup(struct host_query *hquery)
390
0
{
391
0
  const ares_hosts_entry_t *entry;
392
0
  ares_status_t             status;
393
394
  /* Per RFC 7686, reject queries for ".onion" domain names with NXDOMAIN. */
395
0
  if (ares_is_onion_domain(hquery->name)) {
396
0
    return ARES_ENOTFOUND;
397
0
  }
398
399
0
  status = ares_hosts_search_host(
400
0
    hquery->channel,
401
0
    (hquery->hints.ai_flags & ARES_AI_ENVHOSTS) ? ARES_TRUE : ARES_FALSE,
402
0
    hquery->name, &entry);
403
404
0
  if (status != ARES_SUCCESS) {
405
0
    goto done;
406
0
  }
407
408
0
  status = ares_hosts_entry_to_addrinfo(
409
0
    entry, hquery->name, hquery->hints.ai_family, hquery->port,
410
0
    (hquery->hints.ai_flags & ARES_AI_CANONNAME) ? ARES_TRUE : ARES_FALSE,
411
0
    hquery->ai);
412
413
0
  if (status != ARES_SUCCESS) {
414
0
    goto done; /* LCOV_EXCL_LINE: OutOfMemory */
415
0
  }
416
417
418
0
done:
419
  /* RFC6761 section 6.3 #3 states that "Name resolution APIs and libraries
420
   * SHOULD recognize localhost names as special and SHOULD always return the
421
   * IP loopback address for address queries".
422
   * We will also ignore ALL errors when trying to resolve localhost, such
423
   * as permissions errors reading /etc/hosts or a malformed /etc/hosts.
424
   *
425
   * Also, just because the query itself returned success from /etc/hosts
426
   * lookup doesn't mean it returned everything it needed to for all requested
427
   * address families. As long as we're not on a critical out of memory
428
   * condition pass it through to fill in any other address classes. */
429
0
  if (status != ARES_ENOMEM && ares_is_localhost(hquery->name)) {
430
0
    return ares_addrinfo_localhost(hquery->name, hquery->port, &hquery->hints,
431
0
                                   hquery->ai);
432
0
  }
433
434
0
  return status;
435
0
}
436
437
static void next_lookup(struct host_query *hquery, ares_status_t status)
438
0
{
439
0
  switch (*hquery->remaining_lookups) {
440
0
    case 'b':
441
      /* RFC6761 section 6.3 #3 says "Name resolution APIs SHOULD NOT send
442
       * queries for localhost names to their configured caching DNS
443
       * server(s)."
444
       * Otherwise, DNS lookup. */
445
0
      if (!ares_is_localhost(hquery->name) && next_dns_lookup(hquery)) {
446
0
        break;
447
0
      }
448
449
0
      hquery->remaining_lookups++;
450
0
      next_lookup(hquery, status);
451
0
      break;
452
453
0
    case 'f':
454
      /* Host file lookup */
455
0
      if (file_lookup(hquery) == ARES_SUCCESS) {
456
0
        end_hquery(hquery, ARES_SUCCESS);
457
0
        break;
458
0
      }
459
0
      hquery->remaining_lookups++;
460
0
      next_lookup(hquery, status);
461
0
      break;
462
0
    default:
463
      /* No lookup left */
464
0
      end_hquery(hquery, status);
465
0
      break;
466
0
  }
467
0
}
468
469
static void terminate_retries(const struct host_query *hquery,
470
                              unsigned short           qid)
471
0
{
472
0
  unsigned short term_qid =
473
0
    (qid == hquery->qid_a) ? hquery->qid_aaaa : hquery->qid_a;
474
0
  const ares_channel_t *channel = hquery->channel;
475
0
  ares_query_t         *query   = NULL;
476
477
  /* No other outstanding queries, nothing to do */
478
0
  if (!hquery->remaining) {
479
0
    return;
480
0
  }
481
482
0
  query = ares_htable_szvp_get_direct(channel->queries_by_qid, term_qid);
483
0
  if (query == NULL) {
484
0
    return;
485
0
  }
486
487
0
  query->no_retries = ARES_TRUE;
488
0
}
489
490
static ares_bool_t ai_has_ipv4(struct ares_addrinfo *ai)
491
0
{
492
0
  struct ares_addrinfo_node *node;
493
494
0
  for (node = ai->nodes; node != NULL; node = node->ai_next) {
495
0
    if (node->ai_family == AF_INET) {
496
0
      return ARES_TRUE;
497
0
    }
498
0
  }
499
0
  return ARES_FALSE;
500
0
}
501
502
static void host_callback(void *arg, ares_status_t status, size_t timeouts,
503
                          const ares_dns_record_t *dnsrec)
504
0
{
505
0
  struct host_query *hquery         = (struct host_query *)arg;
506
0
  ares_status_t      addinfostatus  = ARES_SUCCESS;
507
0
  hquery->timeouts                 += timeouts;
508
0
  hquery->remaining--;
509
510
0
  if (status == ARES_SUCCESS) {
511
0
    if (dnsrec == NULL) {
512
0
      addinfostatus = ARES_EBADRESP; /* LCOV_EXCL_LINE: DefensiveCoding */
513
0
    } else {
514
0
      addinfostatus =
515
0
        ares_parse_into_addrinfo(dnsrec, ARES_TRUE, hquery->port, hquery->ai);
516
0
    }
517
518
    /* We sent out ipv4 and ipv6 requests simultaneously.  If we got a
519
     * successful ipv4 response, we want to go ahead and tell the ipv6 request
520
     * that if it fails or times out to not try again since we have the data
521
     * we need.
522
     *
523
     * Our initial implementation of this would terminate retries if we got any
524
     * successful response (ipv4 _or_ ipv6).  But we did get some user-reported
525
     * issues with this that had bad system configs and odd behavior:
526
     *  https://github.com/alpinelinux/docker-alpine/issues/366
527
     *
528
     * Essentially the ipv6 query succeeded but the ipv4 query failed or timed
529
     * out, and so we only returned the ipv6 address, but the host couldn't
530
     * use ipv6.  If we continued to allow ipv4 retries it would have found a
531
     * server that worked and returned both address classes (this is clearly
532
     * unexpected behavior).
533
     *
534
     * At some point down the road if ipv6 actually becomes required and
535
     * reliable we can drop this ipv4 check.
536
     */
537
0
    if (addinfostatus == ARES_SUCCESS && ai_has_ipv4(hquery->ai)) {
538
0
      terminate_retries(hquery, ares_dns_record_get_id(dnsrec));
539
0
    }
540
0
  }
541
542
0
  if (!hquery->remaining) {
543
0
    if (status == ARES_EDESTRUCTION || status == ARES_ECANCELLED) {
544
      /* must make sure we don't do next_lookup() on destroy or cancel,
545
       * and return the appropriate status.  We won't return a partial
546
       * result in this case. */
547
0
      end_hquery(hquery, status);
548
0
    } else if (addinfostatus != ARES_SUCCESS && addinfostatus != ARES_ENODATA) {
549
      /* error in parsing result e.g. no memory */
550
0
      if (addinfostatus == ARES_EBADRESP && hquery->ai->nodes) {
551
        /* We got a bad response from server, but at least one query
552
         * ended with ARES_SUCCESS */
553
0
        end_hquery(hquery, ARES_SUCCESS);
554
0
      } else {
555
0
        end_hquery(hquery, addinfostatus);
556
0
      }
557
0
    } else if (hquery->ai->nodes) {
558
      /* at least one query ended with ARES_SUCCESS */
559
0
      end_hquery(hquery, ARES_SUCCESS);
560
0
    } else if (status == ARES_ENOTFOUND || status == ARES_ENODATA ||
561
0
               addinfostatus == ARES_ENODATA) {
562
0
      if (status == ARES_ENODATA || addinfostatus == ARES_ENODATA) {
563
0
        hquery->nodata_cnt++;
564
0
      }
565
0
      next_lookup(hquery, hquery->nodata_cnt ? ARES_ENODATA : status);
566
0
    } else if ((status == ARES_ESERVFAIL || status == ARES_EREFUSED) &&
567
0
               ares_name_label_cnt(hquery->names[hquery->next_name_idx - 1]) ==
568
0
                 1) {
569
      /* Issue #852, systemd-resolved may return SERVFAIL or REFUSED on a
570
       * single label domain name. */
571
0
      next_lookup(hquery, hquery->nodata_cnt ? ARES_ENODATA : status);
572
0
    } else {
573
0
      end_hquery(hquery, status);
574
0
    }
575
0
  }
576
577
  /* at this point we keep on waiting for the next query to finish */
578
0
}
579
580
static ares_bool_t numeric_service_to_port(const char *service,
581
                                           unsigned short *port)
582
0
{
583
0
  char *end;
584
0
  unsigned long val;
585
586
0
  errno = 0;
587
0
  val   = strtoul(service, &end, 10);
588
589
0
  if (errno == 0 && *end == '\0' && val <= 65535) {
590
0
    *port = (unsigned short)val;
591
0
    return ARES_TRUE;
592
0
  }
593
594
0
  return ARES_FALSE;
595
0
}
596
597
static void ares_getaddrinfo_int(ares_channel_t *channel, const char *name,
598
                                 const char                       *service,
599
                                 const struct ares_addrinfo_hints *hints,
600
                                 ares_addrinfo_callback callback, void *arg)
601
0
{
602
0
  struct host_query    *hquery;
603
0
  unsigned short        port = 0;
604
0
  int                   family;
605
0
  struct ares_addrinfo *ai;
606
0
  ares_status_t         status;
607
608
0
  if (!hints) {
609
0
    hints = &default_hints;
610
0
  }
611
612
0
  family = hints->ai_family;
613
614
  /* Right now we only know how to look up Internet addresses
615
     and unspec means try both basically. */
616
0
  if (family != AF_INET && family != AF_INET6 && family != AF_UNSPEC) {
617
0
    callback(arg, ARES_ENOTIMP, 0, NULL);
618
0
    return;
619
0
  }
620
621
0
  if (ares_is_onion_domain(name)) {
622
0
    callback(arg, ARES_ENOTFOUND, 0, NULL);
623
0
    return;
624
0
  }
625
626
0
  if (service) {
627
0
    if (hints->ai_flags & ARES_AI_NUMERICSERV) {
628
0
      if (!numeric_service_to_port(service, &port)) {
629
0
        callback(arg, ARES_ESERVICE, 0, NULL);
630
0
        return;
631
0
      }
632
0
    } else {
633
0
      port = lookup_service(service, 0);
634
0
      if (!port) {
635
0
        if (!numeric_service_to_port(service, &port)) {
636
0
          callback(arg, ARES_ESERVICE, 0, NULL);
637
0
          return;
638
0
        }
639
0
      }
640
0
    }
641
0
  }
642
643
0
  ai = ares_malloc_zero(sizeof(*ai));
644
0
  if (!ai) {
645
0
    callback(arg, ARES_ENOMEM, 0, NULL);
646
0
    return;
647
0
  }
648
649
0
  if (fake_addrinfo(name, port, hints, ai, callback, arg)) {
650
0
    return;
651
0
  }
652
653
  /* Allocate and fill in the host query structure. */
654
0
  hquery = ares_malloc_zero(sizeof(*hquery));
655
0
  if (!hquery) {
656
0
    ares_freeaddrinfo(ai);
657
0
    callback(arg, ARES_ENOMEM, 0, NULL);
658
0
    return;
659
0
  }
660
661
0
  hquery->port        = port;
662
0
  hquery->channel     = channel;
663
0
  hquery->hints       = *hints;
664
0
  hquery->sent_family = -1; /* nothing is sent yet */
665
0
  hquery->callback    = callback;
666
0
  hquery->arg         = arg;
667
0
  hquery->ai          = ai;
668
0
  hquery->name        = ares_strdup(name);
669
0
  if (hquery->name == NULL) {
670
0
    hquery_free(hquery, ARES_TRUE);
671
0
    callback(arg, ARES_ENOMEM, 0, NULL);
672
0
    return;
673
0
  }
674
675
0
  status =
676
0
    ares_search_name_list(channel, name, &hquery->names, &hquery->names_cnt);
677
0
  if (status != ARES_SUCCESS) {
678
0
    hquery_free(hquery, ARES_TRUE);
679
0
    callback(arg, (int)status, 0, NULL);
680
0
    return;
681
0
  }
682
0
  hquery->next_name_idx = 0;
683
684
685
0
  hquery->lookups = ares_strdup(channel->lookups);
686
0
  if (hquery->lookups == NULL) {
687
0
    hquery_free(hquery, ARES_TRUE);
688
0
    callback(arg, ARES_ENOMEM, 0, NULL);
689
0
    return;
690
0
  }
691
0
  hquery->remaining_lookups = hquery->lookups;
692
693
  /* Start performing lookups according to channel->lookups. */
694
0
  next_lookup(hquery, ARES_ECONNREFUSED /* initial error code */);
695
0
}
696
697
void ares_getaddrinfo(ares_channel_t *channel, const char *name,
698
                      const char                       *service,
699
                      const struct ares_addrinfo_hints *hints,
700
                      ares_addrinfo_callback callback, void *arg)
701
0
{
702
0
  if (channel == NULL) {
703
0
    return;
704
0
  }
705
0
  ares_channel_lock(channel);
706
0
  ares_getaddrinfo_int(channel, name, service, hints, callback, arg);
707
0
  ares_channel_unlock(channel);
708
0
}
709
710
static ares_bool_t next_dns_lookup(struct host_query *hquery)
711
0
{
712
0
  const char *name = NULL;
713
714
0
  if (hquery->next_name_idx >= hquery->names_cnt) {
715
0
    return ARES_FALSE;
716
0
  }
717
718
0
  name = hquery->names[hquery->next_name_idx++];
719
720
  /* NOTE: hquery may be invalidated during the call to ares_query_qid(),
721
   *       so should not be referenced after this point */
722
0
  switch (hquery->hints.ai_family) {
723
0
    case AF_INET:
724
0
      hquery->remaining += 1;
725
0
      ares_query_nolock(hquery->channel, name, ARES_CLASS_IN, ARES_REC_TYPE_A,
726
0
                        host_callback, hquery, &hquery->qid_a);
727
0
      break;
728
0
    case AF_INET6:
729
0
      hquery->remaining += 1;
730
0
      ares_query_nolock(hquery->channel, name, ARES_CLASS_IN,
731
0
                        ARES_REC_TYPE_AAAA, host_callback, hquery,
732
0
                        &hquery->qid_aaaa);
733
0
      break;
734
0
    case AF_UNSPEC:
735
0
      hquery->remaining += 2;
736
0
      ares_query_nolock(hquery->channel, name, ARES_CLASS_IN, ARES_REC_TYPE_A,
737
0
                        host_callback, hquery, &hquery->qid_a);
738
0
      ares_query_nolock(hquery->channel, name, ARES_CLASS_IN,
739
0
                        ARES_REC_TYPE_AAAA, host_callback, hquery,
740
0
                        &hquery->qid_aaaa);
741
0
      break;
742
0
    default:
743
0
      break;
744
0
  }
745
746
0
  return ARES_TRUE;
747
0
}