Coverage Report

Created: 2024-10-29 06:06

/src/c-ares/src/lib/ares_getaddrinfo.c
Line
Count
Source (jump to first uncovered line)
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
          callback(arg, (int)status, 0, NULL); /* LCOV_EXCL_LINE: OutOfMemory */
272
0
          return ARES_TRUE;                    /* LCOV_EXCL_LINE: OutOfMemory */
273
0
        }
274
0
      }
275
0
    }
276
0
  }
277
278
0
  if (!result && (family == AF_INET6 || family == AF_UNSPEC)) {
279
0
    struct ares_in6_addr addr6;
280
0
    result =
281
0
      ares_inet_pton(AF_INET6, name, &addr6) < 1 ? ARES_FALSE : ARES_TRUE;
282
0
    if (result) {
283
0
      status = ares_append_ai_node(AF_INET6, port, 0, &addr6, &ai->nodes);
284
0
      if (status != ARES_SUCCESS) {
285
0
        callback(arg, (int)status, 0, NULL); /* LCOV_EXCL_LINE: OutOfMemory */
286
0
        return ARES_TRUE;                    /* LCOV_EXCL_LINE: OutOfMemory */
287
0
      }
288
0
    }
289
0
  }
290
291
0
  if (!result) {
292
0
    return ARES_FALSE;
293
0
  }
294
295
0
  if (hints->ai_flags & ARES_AI_CANONNAME) {
296
0
    cname = ares_append_addrinfo_cname(&ai->cnames);
297
0
    if (!cname) {
298
      /* LCOV_EXCL_START: OutOfMemory */
299
0
      ares_freeaddrinfo(ai);
300
0
      callback(arg, ARES_ENOMEM, 0, NULL);
301
0
      return ARES_TRUE;
302
      /* LCOV_EXCL_STOP */
303
0
    }
304
305
    /* Duplicate the name, to avoid a constness violation. */
306
0
    cname->name = ares_strdup(name);
307
0
    if (!cname->name) {
308
0
      ares_freeaddrinfo(ai);
309
0
      callback(arg, ARES_ENOMEM, 0, NULL);
310
0
      return ARES_TRUE;
311
0
    }
312
0
  }
313
314
0
  ai->nodes->ai_socktype = hints->ai_socktype;
315
0
  ai->nodes->ai_protocol = hints->ai_protocol;
316
317
0
  callback(arg, ARES_SUCCESS, 0, ai);
318
0
  return ARES_TRUE;
319
0
}
320
321
static void hquery_free(struct host_query *hquery, ares_bool_t cleanup_ai)
322
0
{
323
0
  if (cleanup_ai) {
324
0
    ares_freeaddrinfo(hquery->ai);
325
0
  }
326
0
  ares_strsplit_free(hquery->names, hquery->names_cnt);
327
0
  ares_free(hquery->name);
328
0
  ares_free(hquery->lookups);
329
0
  ares_free(hquery);
330
0
}
331
332
static void end_hquery(struct host_query *hquery, ares_status_t status)
333
0
{
334
0
  struct ares_addrinfo_node  sentinel;
335
0
  struct ares_addrinfo_node *next;
336
337
0
  if (status == ARES_SUCCESS) {
338
0
    if (!(hquery->hints.ai_flags & ARES_AI_NOSORT) && hquery->ai->nodes) {
339
0
      sentinel.ai_next = hquery->ai->nodes;
340
0
      ares_sortaddrinfo(hquery->channel, &sentinel);
341
0
      hquery->ai->nodes = sentinel.ai_next;
342
0
    }
343
0
    next = hquery->ai->nodes;
344
345
0
    while (next) {
346
0
      next->ai_socktype = hquery->hints.ai_socktype;
347
0
      next->ai_protocol = hquery->hints.ai_protocol;
348
0
      next              = next->ai_next;
349
0
    }
350
0
  } else {
351
    /* Clean up what we have collected by so far. */
352
0
    ares_freeaddrinfo(hquery->ai);
353
0
    hquery->ai = NULL;
354
0
  }
355
356
0
  hquery->callback(hquery->arg, (int)status, (int)hquery->timeouts, hquery->ai);
357
0
  hquery_free(hquery, ARES_FALSE);
358
0
}
359
360
ares_bool_t ares_is_localhost(const char *name)
361
0
{
362
  /* RFC6761 6.3 says : The domain "localhost." and any names falling within
363
   * ".localhost." */
364
0
  size_t len;
365
366
0
  if (name == NULL) {
367
0
    return ARES_FALSE; /* LCOV_EXCL_LINE: DefensiveCoding */
368
0
  }
369
370
0
  if (ares_strcaseeq(name, "localhost")) {
371
0
    return ARES_TRUE;
372
0
  }
373
374
0
  len = ares_strlen(name);
375
0
  if (len < 10 /* strlen(".localhost") */) {
376
0
    return ARES_FALSE;
377
0
  }
378
379
0
  if (ares_strcaseeq(name + (len - 10 /* strlen(".localhost") */),
380
0
                     ".localhost")) {
381
0
    return ARES_TRUE;
382
0
  }
383
384
0
  return ARES_FALSE;
385
0
}
386
387
static ares_status_t file_lookup(struct host_query *hquery)
388
0
{
389
0
  const ares_hosts_entry_t *entry;
390
0
  ares_status_t             status;
391
392
  /* Per RFC 7686, reject queries for ".onion" domain names with NXDOMAIN. */
393
0
  if (ares_is_onion_domain(hquery->name)) {
394
0
    return ARES_ENOTFOUND;
395
0
  }
396
397
0
  status = ares_hosts_search_host(
398
0
    hquery->channel,
399
0
    (hquery->hints.ai_flags & ARES_AI_ENVHOSTS) ? ARES_TRUE : ARES_FALSE,
400
0
    hquery->name, &entry);
401
402
0
  if (status != ARES_SUCCESS) {
403
0
    goto done;
404
0
  }
405
406
0
  status = ares_hosts_entry_to_addrinfo(
407
0
    entry, hquery->name, hquery->hints.ai_family, hquery->port,
408
0
    (hquery->hints.ai_flags & ARES_AI_CANONNAME) ? ARES_TRUE : ARES_FALSE,
409
0
    hquery->ai);
410
411
0
  if (status != ARES_SUCCESS) {
412
0
    goto done; /* LCOV_EXCL_LINE: OutOfMemory */
413
0
  }
414
415
416
0
done:
417
  /* RFC6761 section 6.3 #3 states that "Name resolution APIs and libraries
418
   * SHOULD recognize localhost names as special and SHOULD always return the
419
   * IP loopback address for address queries".
420
   * We will also ignore ALL errors when trying to resolve localhost, such
421
   * as permissions errors reading /etc/hosts or a malformed /etc/hosts */
422
0
  if (status != ARES_SUCCESS && status != ARES_ENOMEM &&
423
0
      ares_is_localhost(hquery->name)) {
424
0
    return ares_addrinfo_localhost(hquery->name, hquery->port, &hquery->hints,
425
0
                                   hquery->ai);
426
0
  }
427
428
0
  return status;
429
0
}
430
431
static void next_lookup(struct host_query *hquery, ares_status_t status)
432
0
{
433
0
  switch (*hquery->remaining_lookups) {
434
0
    case 'b':
435
      /* RFC6761 section 6.3 #3 says "Name resolution APIs SHOULD NOT send
436
       * queries for localhost names to their configured caching DNS
437
       * server(s)."
438
       * Otherwise, DNS lookup. */
439
0
      if (!ares_is_localhost(hquery->name) && next_dns_lookup(hquery)) {
440
0
        break;
441
0
      }
442
443
0
      hquery->remaining_lookups++;
444
0
      next_lookup(hquery, status);
445
0
      break;
446
447
0
    case 'f':
448
      /* Host file lookup */
449
0
      if (file_lookup(hquery) == ARES_SUCCESS) {
450
0
        end_hquery(hquery, ARES_SUCCESS);
451
0
        break;
452
0
      }
453
0
      hquery->remaining_lookups++;
454
0
      next_lookup(hquery, status);
455
0
      break;
456
0
    default:
457
      /* No lookup left */
458
0
      end_hquery(hquery, status);
459
0
      break;
460
0
  }
461
0
}
462
463
static void terminate_retries(const struct host_query *hquery,
464
                              unsigned short           qid)
465
0
{
466
0
  unsigned short term_qid =
467
0
    (qid == hquery->qid_a) ? hquery->qid_aaaa : hquery->qid_a;
468
0
  const ares_channel_t *channel = hquery->channel;
469
0
  ares_query_t         *query   = NULL;
470
471
  /* No other outstanding queries, nothing to do */
472
0
  if (!hquery->remaining) {
473
0
    return;
474
0
  }
475
476
0
  query = ares_htable_szvp_get_direct(channel->queries_by_qid, term_qid);
477
0
  if (query == NULL) {
478
0
    return;
479
0
  }
480
481
0
  query->no_retries = ARES_TRUE;
482
0
}
483
484
static void host_callback(void *arg, ares_status_t status, size_t timeouts,
485
                          const ares_dns_record_t *dnsrec)
486
0
{
487
0
  struct host_query *hquery         = (struct host_query *)arg;
488
0
  ares_status_t      addinfostatus  = ARES_SUCCESS;
489
0
  hquery->timeouts                 += timeouts;
490
0
  hquery->remaining--;
491
492
0
  if (status == ARES_SUCCESS) {
493
0
    if (dnsrec == NULL) {
494
0
      addinfostatus = ARES_EBADRESP; /* LCOV_EXCL_LINE: DefensiveCoding */
495
0
    } else {
496
0
      addinfostatus =
497
0
        ares_parse_into_addrinfo(dnsrec, ARES_TRUE, hquery->port, hquery->ai);
498
0
    }
499
0
    if (addinfostatus == ARES_SUCCESS) {
500
0
      terminate_retries(hquery, ares_dns_record_get_id(dnsrec));
501
0
    }
502
0
  }
503
504
0
  if (!hquery->remaining) {
505
0
    if (status == ARES_EDESTRUCTION || status == ARES_ECANCELLED) {
506
      /* must make sure we don't do next_lookup() on destroy or cancel,
507
       * and return the appropriate status.  We won't return a partial
508
       * result in this case. */
509
0
      end_hquery(hquery, status);
510
0
    } else if (addinfostatus != ARES_SUCCESS && addinfostatus != ARES_ENODATA) {
511
      /* error in parsing result e.g. no memory */
512
0
      if (addinfostatus == ARES_EBADRESP && hquery->ai->nodes) {
513
        /* We got a bad response from server, but at least one query
514
         * ended with ARES_SUCCESS */
515
0
        end_hquery(hquery, ARES_SUCCESS);
516
0
      } else {
517
0
        end_hquery(hquery, addinfostatus);
518
0
      }
519
0
    } else if (hquery->ai->nodes) {
520
      /* at least one query ended with ARES_SUCCESS */
521
0
      end_hquery(hquery, ARES_SUCCESS);
522
0
    } else if (status == ARES_ENOTFOUND || status == ARES_ENODATA ||
523
0
               addinfostatus == ARES_ENODATA) {
524
0
      if (status == ARES_ENODATA || addinfostatus == ARES_ENODATA) {
525
0
        hquery->nodata_cnt++;
526
0
      }
527
0
      next_lookup(hquery, hquery->nodata_cnt ? ARES_ENODATA : status);
528
0
    } else if ((status == ARES_ESERVFAIL || status == ARES_EREFUSED) &&
529
0
               ares_name_label_cnt(hquery->names[hquery->next_name_idx - 1]) ==
530
0
                 1) {
531
      /* Issue #852, systemd-resolved may return SERVFAIL or REFUSED on a
532
       * single label domain name. */
533
0
      next_lookup(hquery, hquery->nodata_cnt ? ARES_ENODATA : status);
534
0
    } else {
535
0
      end_hquery(hquery, status);
536
0
    }
537
0
  }
538
539
  /* at this point we keep on waiting for the next query to finish */
540
0
}
541
542
static void ares_getaddrinfo_int(ares_channel_t *channel, const char *name,
543
                                 const char                       *service,
544
                                 const struct ares_addrinfo_hints *hints,
545
                                 ares_addrinfo_callback callback, void *arg)
546
0
{
547
0
  struct host_query    *hquery;
548
0
  unsigned short        port = 0;
549
0
  int                   family;
550
0
  struct ares_addrinfo *ai;
551
0
  ares_status_t         status;
552
553
0
  if (!hints) {
554
0
    hints = &default_hints;
555
0
  }
556
557
0
  family = hints->ai_family;
558
559
  /* Right now we only know how to look up Internet addresses
560
     and unspec means try both basically. */
561
0
  if (family != AF_INET && family != AF_INET6 && family != AF_UNSPEC) {
562
0
    callback(arg, ARES_ENOTIMP, 0, NULL);
563
0
    return;
564
0
  }
565
566
0
  if (ares_is_onion_domain(name)) {
567
0
    callback(arg, ARES_ENOTFOUND, 0, NULL);
568
0
    return;
569
0
  }
570
571
0
  if (service) {
572
0
    if (hints->ai_flags & ARES_AI_NUMERICSERV) {
573
0
      unsigned long val;
574
0
      errno = 0;
575
0
      val   = strtoul(service, NULL, 0);
576
0
      if ((val == 0 && errno != 0) || val > 65535) {
577
0
        callback(arg, ARES_ESERVICE, 0, NULL);
578
0
        return;
579
0
      }
580
0
      port = (unsigned short)val;
581
0
    } else {
582
0
      port = lookup_service(service, 0);
583
0
      if (!port) {
584
0
        unsigned long val;
585
0
        errno = 0;
586
0
        val   = strtoul(service, NULL, 0);
587
0
        if ((val == 0 && errno != 0) || val > 65535) {
588
0
          callback(arg, ARES_ESERVICE, 0, NULL);
589
0
          return;
590
0
        }
591
0
        port = (unsigned short)val;
592
0
      }
593
0
    }
594
0
  }
595
596
0
  ai = ares_malloc_zero(sizeof(*ai));
597
0
  if (!ai) {
598
0
    callback(arg, ARES_ENOMEM, 0, NULL);
599
0
    return;
600
0
  }
601
602
0
  if (fake_addrinfo(name, port, hints, ai, callback, arg)) {
603
0
    return;
604
0
  }
605
606
  /* Allocate and fill in the host query structure. */
607
0
  hquery = ares_malloc_zero(sizeof(*hquery));
608
0
  if (!hquery) {
609
0
    ares_freeaddrinfo(ai);
610
0
    callback(arg, ARES_ENOMEM, 0, NULL);
611
0
    return;
612
0
  }
613
614
0
  hquery->port        = port;
615
0
  hquery->channel     = channel;
616
0
  hquery->hints       = *hints;
617
0
  hquery->sent_family = -1; /* nothing is sent yet */
618
0
  hquery->callback    = callback;
619
0
  hquery->arg         = arg;
620
0
  hquery->ai          = ai;
621
0
  hquery->name        = ares_strdup(name);
622
0
  if (hquery->name == NULL) {
623
0
    hquery_free(hquery, ARES_TRUE);
624
0
    callback(arg, ARES_ENOMEM, 0, NULL);
625
0
    return;
626
0
  }
627
628
0
  status =
629
0
    ares_search_name_list(channel, name, &hquery->names, &hquery->names_cnt);
630
0
  if (status != ARES_SUCCESS) {
631
0
    hquery_free(hquery, ARES_TRUE);
632
0
    callback(arg, (int)status, 0, NULL);
633
0
    return;
634
0
  }
635
0
  hquery->next_name_idx = 0;
636
637
638
0
  hquery->lookups = ares_strdup(channel->lookups);
639
0
  if (hquery->lookups == NULL) {
640
0
    hquery_free(hquery, ARES_TRUE);
641
0
    callback(arg, ARES_ENOMEM, 0, NULL);
642
0
    return;
643
0
  }
644
0
  hquery->remaining_lookups = hquery->lookups;
645
646
  /* Start performing lookups according to channel->lookups. */
647
0
  next_lookup(hquery, ARES_ECONNREFUSED /* initial error code */);
648
0
}
649
650
void ares_getaddrinfo(ares_channel_t *channel, const char *name,
651
                      const char                       *service,
652
                      const struct ares_addrinfo_hints *hints,
653
                      ares_addrinfo_callback callback, void *arg)
654
0
{
655
0
  if (channel == NULL) {
656
0
    return;
657
0
  }
658
0
  ares_channel_lock(channel);
659
0
  ares_getaddrinfo_int(channel, name, service, hints, callback, arg);
660
0
  ares_channel_unlock(channel);
661
0
}
662
663
static ares_bool_t next_dns_lookup(struct host_query *hquery)
664
0
{
665
0
  const char *name = NULL;
666
667
0
  if (hquery->next_name_idx >= hquery->names_cnt) {
668
0
    return ARES_FALSE;
669
0
  }
670
671
0
  name = hquery->names[hquery->next_name_idx++];
672
673
  /* NOTE: hquery may be invalidated during the call to ares_query_qid(),
674
   *       so should not be referenced after this point */
675
0
  switch (hquery->hints.ai_family) {
676
0
    case AF_INET:
677
0
      hquery->remaining += 1;
678
0
      ares_query_nolock(hquery->channel, name, ARES_CLASS_IN, ARES_REC_TYPE_A,
679
0
                        host_callback, hquery, &hquery->qid_a);
680
0
      break;
681
0
    case AF_INET6:
682
0
      hquery->remaining += 1;
683
0
      ares_query_nolock(hquery->channel, name, ARES_CLASS_IN,
684
0
                        ARES_REC_TYPE_AAAA, host_callback, hquery,
685
0
                        &hquery->qid_aaaa);
686
0
      break;
687
0
    case AF_UNSPEC:
688
0
      hquery->remaining += 2;
689
0
      ares_query_nolock(hquery->channel, name, ARES_CLASS_IN, ARES_REC_TYPE_A,
690
0
                        host_callback, hquery, &hquery->qid_a);
691
0
      ares_query_nolock(hquery->channel, name, ARES_CLASS_IN,
692
0
                        ARES_REC_TYPE_AAAA, host_callback, hquery,
693
0
                        &hquery->qid_aaaa);
694
0
      break;
695
0
    default:
696
0
      break;
697
0
  }
698
699
0
  return ARES_TRUE;
700
0
}