• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

c-ares / c-ares / #55

27 Sep 2023 06:00PM UTC coverage: 88.217% (+0.04%) from 88.181%
#55

push

travis-ci

bradh352
#532.  Don't requeue any queries for getaddrinfo() during destruction.

19 of 19 new or added lines in 1 file covered. (100.0%)

5001 of 5669 relevant lines covered (88.22%)

11668.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.51
/src/lib/ares_getaddrinfo.c
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
#include "ares_dns.h"
55

56
#ifdef WATT32
57
#undef WIN32
58
#endif
59
#ifdef WIN32
60
#  include "ares_platform.h"
61
#endif
62

63
struct host_query
64
{
65
  ares_channel channel;
66
  char *name;
67
  unsigned short port; /* in host order */
68
  ares_addrinfo_callback callback;
69
  void *arg;
70
  struct ares_addrinfo_hints hints;
71
  int sent_family; /* this family is what was is being used */
72
  int timeouts;    /* number of timeouts we saw for this request */
73
  const char *remaining_lookups; /* types of lookup we need to perform ("fb" by
74
                                    default, file and dns respectively) */
75
  struct ares_addrinfo *ai;      /* store results between lookups */
76
  unsigned short qid_a;    /* qid for A request */
77
  unsigned short qid_aaaa; /* qid for AAAA request */
78
  int remaining;   /* number of DNS answers waiting for */
79
  int next_domain; /* next search domain to try */
80
  int nodata_cnt; /* Track nodata responses to possibly override final result */
81

82
};
83

84
static const struct ares_addrinfo_hints default_hints = {
85
  0,         /* ai_flags */
86
  AF_UNSPEC, /* ai_family */
87
  0,         /* ai_socktype */
88
  0,         /* ai_protocol */
89
};
90

91
static const struct ares_addrinfo_cname empty_addrinfo_cname = {
92
  INT_MAX, /* ttl */
93
  NULL,    /* alias */
94
  NULL,    /* name */
95
  NULL,    /* next */
96
};
97

98
static const struct ares_addrinfo_node empty_addrinfo_node = {
99
  0,    /* ai_ttl */
100
  0,    /* ai_flags */
101
  0,    /* ai_family */
102
  0,    /* ai_socktype */
103
  0,    /* ai_protocol */
104
  0,    /* ai_addrlen */
105
  NULL, /* ai_addr */
106
  NULL  /* ai_next */
107
};
108

109
static const struct ares_addrinfo empty_addrinfo = {
110
  NULL, /* cnames */
111
  NULL, /* nodes */
112
  NULL  /* name */
113
};
114

115
/* forward declarations */
116
static void host_callback(void *arg, int status, int timeouts,
117
                          unsigned char *abuf, int alen);
118
static int as_is_first(const struct host_query *hquery);
119
static int as_is_only(const struct host_query* hquery);
120
static int next_dns_lookup(struct host_query *hquery);
121

122
static struct ares_addrinfo_cname *ares__malloc_addrinfo_cname(void)
68✔
123
{
124
  struct ares_addrinfo_cname *cname = ares_malloc(sizeof(struct ares_addrinfo_cname));
68✔
125
  if (!cname)
68✔
126
    return NULL;
2✔
127

128
  *cname = empty_addrinfo_cname;
66✔
129
  return cname;
66✔
130
}
131

132
struct ares_addrinfo_cname *ares__append_addrinfo_cname(struct ares_addrinfo_cname **head)
68✔
133
{
134
  struct ares_addrinfo_cname *tail = ares__malloc_addrinfo_cname();
68✔
135
  struct ares_addrinfo_cname *last = *head;
68✔
136
  if (!last)
68✔
137
    {
138
      *head = tail;
52✔
139
      return tail;
52✔
140
    }
141

142
  while (last->next)
23✔
143
    {
144
      last = last->next;
7✔
145
    }
146

147
  last->next = tail;
16✔
148
  return tail;
16✔
149
}
150

151
void ares__addrinfo_cat_cnames(struct ares_addrinfo_cname **head,
24✔
152
                               struct ares_addrinfo_cname *tail)
153
{
154
  struct ares_addrinfo_cname *last = *head;
24✔
155
  if (!last)
24✔
156
    {
157
      *head = tail;
24✔
158
      return;
24✔
159
    }
160

161
  while (last->next)
×
162
    {
163
      last = last->next;
×
164
    }
165

166
  last->next = tail;
×
167
}
168

169
static struct ares_addrinfo *ares__malloc_addrinfo(void)
472✔
170
{
171
  struct ares_addrinfo *ai = ares_malloc(sizeof(struct ares_addrinfo));
472✔
172
  if (!ai)
472✔
173
    return NULL;
1✔
174

175
  *ai = empty_addrinfo;
471✔
176
  return ai;
471✔
177
}
178

179
static struct ares_addrinfo_node *ares__malloc_addrinfo_node(void)
733✔
180
{
181
  struct ares_addrinfo_node *node =
182
      ares_malloc(sizeof(*node));
733✔
183
  if (!node)
733✔
184
    return NULL;
3✔
185

186
  *node = empty_addrinfo_node;
730✔
187
  return node;
730✔
188
}
189

190
/* Allocate new addrinfo and append to the tail. */
191
struct ares_addrinfo_node *ares__append_addrinfo_node(struct ares_addrinfo_node **head)
733✔
192
{
193
  struct ares_addrinfo_node *tail = ares__malloc_addrinfo_node();
733✔
194
  struct ares_addrinfo_node *last = *head;
733✔
195
  if (!last)
733✔
196
    {
197
      *head = tail;
487✔
198
      return tail;
487✔
199
    }
200

201
  while (last->ai_next)
1,314✔
202
    {
203
      last = last->ai_next;
1,068✔
204
    }
205

206
  last->ai_next = tail;
246✔
207
  return tail;
246✔
208
}
209

210
void ares__addrinfo_cat_nodes(struct ares_addrinfo_node **head,
468✔
211
                              struct ares_addrinfo_node *tail)
212
{
213
  struct ares_addrinfo_node *last = *head;
468✔
214
  if (!last)
468✔
215
    {
216
      *head = tail;
466✔
217
      return;
466✔
218
    }
219

220
  while (last->ai_next)
2✔
221
    {
222
      last = last->ai_next;
×
223
    }
224

225
  last->ai_next = tail;
2✔
226
}
227

228
/* Resolve service name into port number given in host byte order.
229
 * If not resolved, return 0.
230
 */
231
static unsigned short lookup_service(const char *service, int flags)
2✔
232
{
233
  const char *proto;
234
  struct servent *sep;
235
#ifdef HAVE_GETSERVBYNAME_R
236
  struct servent se;
237
  char tmpbuf[4096];
238
#endif
239

240
  if (service)
2✔
241
    {
242
      if (flags & ARES_NI_UDP)
2✔
243
        proto = "udp";
×
244
      else if (flags & ARES_NI_SCTP)
2✔
245
        proto = "sctp";
×
246
      else if (flags & ARES_NI_DCCP)
2✔
247
        proto = "dccp";
×
248
      else
249
        proto = "tcp";
2✔
250
#ifdef HAVE_GETSERVBYNAME_R
251
      memset(&se, 0, sizeof(se));
252
      sep = &se;
253
      memset(tmpbuf, 0, sizeof(tmpbuf));
254
#if GETSERVBYNAME_R_ARGS == 6
255
      if (getservbyname_r(service, proto, &se, (void *)tmpbuf, sizeof(tmpbuf),
256
                          &sep) != 0)
257
        sep = NULL; /* LCOV_EXCL_LINE: buffer large so this never fails */
258
#elif GETSERVBYNAME_R_ARGS == 5
259
      sep =
260
          getservbyname_r(service, proto, &se, (void *)tmpbuf, sizeof(tmpbuf));
261
#elif GETSERVBYNAME_R_ARGS == 4
262
      if (getservbyname_r(service, proto, &se, (void *)tmpbuf) != 0)
263
        sep = NULL;
264
#else
265
      /* Lets just hope the OS uses TLS! */
266
      sep = getservbyname(service, proto);
267
#endif
268
#else
269
        /* Lets just hope the OS uses TLS! */
270
#if (defined(NETWARE) && !defined(__NOVELL_LIBC__))
271
      sep = getservbyname(service, (char *)proto);
272
#else
273
      sep = getservbyname(service, proto);
2✔
274
#endif
275
#endif
276
      return (sep ? ntohs((unsigned short)sep->s_port) : 0);
2✔
277
    }
278
  return 0;
×
279
}
280

281
/* If the name looks like an IP address or an error occured,
282
 * fake up a host entry, end the query immediately, and return true.
283
 * Otherwise return false.
284
 */
285
static int fake_addrinfo(const char *name,
471✔
286
                         unsigned short port,
287
                         const struct ares_addrinfo_hints *hints,
288
                         struct ares_addrinfo *ai,
289
                         ares_addrinfo_callback callback,
290
                         void *arg)
291
{
292
  struct ares_addrinfo_cname *cname;
293
  int status = ARES_SUCCESS;
471✔
294
  int result = 0;
471✔
295
  int family = hints->ai_family;
471✔
296
  if (family == AF_INET || family == AF_INET6 || family == AF_UNSPEC)
471✔
297
    {
298
      /* It only looks like an IP address if it's all numbers and dots. */
299
      int numdots = 0, valid = 1;
471✔
300
      const char *p;
301
      for (p = name; *p; p++)
550✔
302
        {
303
          if (!ISDIGIT(*p) && *p != '.')
542✔
304
            {
305
              valid = 0;
463✔
306
              break;
463✔
307
            }
308
          else if (*p == '.')
79✔
309
            {
310
              numdots++;
28✔
311
            }
312
        }
313

314
      /* if we don't have 3 dots, it is illegal
315
       * (although inet_pton doesn't think so).
316
       */
317
      if (numdots != 3 || !valid)
471✔
318
        result = 0;
463✔
319
      else
320
        {
321
          struct in_addr addr4;
322
          result = ares_inet_pton(AF_INET, name, &addr4) < 1 ? 0 : 1;
8✔
323
          if (result)
8✔
324
            {
325
              status = ares_append_ai_node(AF_INET, port, 0, &addr4, &ai->nodes);
8✔
326
              if (status != ARES_SUCCESS)
8✔
327
                {
328
                  callback(arg, status, 0, NULL);
×
329
                  return 1;
×
330
                }
331
            }
332
        }
333
    }
334

335
  if (!result && (family == AF_INET6 || family == AF_UNSPEC))
471✔
336
    {
337
      struct ares_in6_addr addr6;
338
      result = ares_inet_pton(AF_INET6, name, &addr6) < 1 ? 0 : 1;
34✔
339
      if (result)
34✔
340
        {
341
          status = ares_append_ai_node(AF_INET6, port, 0, &addr6, &ai->nodes);
4✔
342
          if (status != ARES_SUCCESS)
4✔
343
            {
344
              callback(arg, status, 0, NULL);
×
345
              return 1;
×
346
            }
347
        }
348
    }
349

350
  if (!result)
471✔
351
    return 0;
459✔
352

353
  if (hints->ai_flags & ARES_AI_CANONNAME)
12✔
354
    {
355
      cname = ares__append_addrinfo_cname(&ai->cnames);
12✔
356
      if (!cname)
12✔
357
        {
358
          ares_freeaddrinfo(ai);
×
359
          callback(arg, ARES_ENOMEM, 0, NULL);
×
360
          return 1;
×
361
        }
362

363
      /* Duplicate the name, to avoid a constness violation. */
364
      cname->name = ares_strdup(name);
12✔
365
      if (!cname->name)
12✔
366
        {
367
          ares_freeaddrinfo(ai);
2✔
368
          callback(arg, ARES_ENOMEM, 0, NULL);
2✔
369
          return 1;
2✔
370
        }
371
    }
372

373
  ai->nodes->ai_socktype = hints->ai_socktype;
10✔
374
  ai->nodes->ai_protocol = hints->ai_protocol;
10✔
375

376
  callback(arg, ARES_SUCCESS, 0, ai);
10✔
377
  return 1;
10✔
378
}
379

380
static void end_hquery(struct host_query *hquery, int status)
457✔
381
{
382
  struct ares_addrinfo_node sentinel;
383
  struct ares_addrinfo_node *next;
384

385
  if (status == ARES_SUCCESS)
457✔
386
    {
387
      if (!(hquery->hints.ai_flags & ARES_AI_NOSORT) && hquery->ai->nodes)
387✔
388
        {
389
          sentinel.ai_next = hquery->ai->nodes;
347✔
390
          ares__sortaddrinfo(hquery->channel, &sentinel);
347✔
391
          hquery->ai->nodes = sentinel.ai_next;
347✔
392
        }
393
      next = hquery->ai->nodes;
387✔
394

395
      while (next)
848✔
396
        {
397
          next->ai_socktype = hquery->hints.ai_socktype;
461✔
398
          next->ai_protocol = hquery->hints.ai_protocol;
461✔
399
          next = next->ai_next;
461✔
400
        }
401
    }
402
  else
403
    {
404
      /* Clean up what we have collected by so far. */
405
      ares_freeaddrinfo(hquery->ai);
70✔
406
      hquery->ai = NULL;
70✔
407
    }
408

409
  hquery->callback(hquery->arg, status, hquery->timeouts, hquery->ai);
457✔
410
  ares_free(hquery->name);
457✔
411
  ares_free(hquery);
457✔
412
}
457✔
413

414
static int is_localhost(const char *name)
953✔
415
{
416
  /* RFC6761 6.3 says : The domain "localhost." and any names falling within ".localhost." */
417
  size_t len;
418

419
  if (name == NULL)
953✔
420
    return 0;
×
421

422
  if (strcmp(name, "localhost") == 0)
953✔
423
    return 1;
4✔
424

425
  len = strlen(name);
949✔
426
  if (len < 10 /* strlen(".localhost") */)
949✔
427
    return 0;
163✔
428

429
  if (strcmp(name + (len - 10 /* strlen(".localhost") */), ".localhost") == 0)
786✔
430
    return 1;
10✔
431

432
  return 0;
776✔
433
}
434

435
static int file_lookup(struct host_query *hquery)
453✔
436
{
437
  FILE *fp;
438
  int error;
439
  int status;
440
  char *path_hosts = NULL;
453✔
441

442
  if (hquery->hints.ai_flags & ARES_AI_ENVHOSTS)
453✔
443
    {
444
      path_hosts = ares_strdup(getenv("CARES_HOSTS"));
4✔
445
      if (!path_hosts)
4✔
446
        return ARES_ENOMEM;
×
447
    }
448

449
  if (hquery->channel->hosts_path)
453✔
450
    {
451
      path_hosts = ares_strdup(hquery->channel->hosts_path);
×
452
      if (!path_hosts)
×
453
        return ARES_ENOMEM;
×
454
    }
455

456
  if (!path_hosts)
453✔
457
    {
458
#ifdef WIN32
459
      char PATH_HOSTS[MAX_PATH];
460
      win_platform platform;
461

462
      PATH_HOSTS[0] = '\0';
463

464
      platform = ares__getplatform();
465

466
      if (platform == WIN_NT)
467
        {
468
          char tmp[MAX_PATH];
469
          HKEY hkeyHosts;
470

471
          if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ,
472
                           &hkeyHosts) == ERROR_SUCCESS)
473
            {
474
              DWORD dwLength = MAX_PATH;
475
              RegQueryValueExA(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp,
476
                              &dwLength);
477
              ExpandEnvironmentStringsA(tmp, PATH_HOSTS, MAX_PATH);
478
              RegCloseKey(hkeyHosts);
479
            }
480
        }
481
      else if (platform == WIN_9X)
482
        GetWindowsDirectoryA(PATH_HOSTS, MAX_PATH);
483
      else
484
        return ARES_ENOTFOUND;
485

486
      strcat(PATH_HOSTS, WIN_PATH_HOSTS);
487
#elif defined(WATT32)
488
      const char *PATH_HOSTS = _w32_GetHostsFile();
489

490
      if (!PATH_HOSTS)
491
        return ARES_ENOTFOUND;
492
#endif
493
      path_hosts = ares_strdup(PATH_HOSTS);
449✔
494
      if (!path_hosts)
449✔
495
        return ARES_ENOMEM;
1✔
496
    }
497

498
  fp = fopen(path_hosts, "r");
452✔
499
  if (!fp)
452✔
500
    {
501
      error = ERRNO;
×
502
      switch (error)
×
503
        {
504
        case ENOENT:
×
505
        case ESRCH:
506
          status = ARES_ENOTFOUND;
×
507
          break;
×
508
        default:
×
509
          DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", error,
510
                         strerror(error)));
511
          DEBUGF(fprintf(stderr, "Error opening file: %s\n", path_hosts));
512
          status = ARES_EFILE;
×
513
          break;
×
514
        }
515
    }
516
  else
517
    {
518
      status = ares__readaddrinfo(fp, hquery->name, hquery->port, &hquery->hints, hquery->ai);
452✔
519
      fclose(fp);
452✔
520
    }
521
  ares_free(path_hosts);
452✔
522

523
  /* RFC6761 section 6.3 #3 states that "Name resolution APIs and libraries
524
   * SHOULD recognize localhost names as special and SHOULD always return the
525
   * IP loopback address for address queries".
526
   * We will also ignore ALL errors when trying to resolve localhost, such
527
   * as permissions errors reading /etc/hosts or a malformed /etc/hosts */
528
  if (status != ARES_SUCCESS && is_localhost(hquery->name))
452✔
529
    {
530
      return ares__addrinfo_localhost(hquery->name, hquery->port,
6✔
531
                                      &hquery->hints, hquery->ai);
6✔
532
    }
533

534
  return status;
446✔
535
}
536

537
static void next_lookup(struct host_query *hquery, int status)
981✔
538
{
539
  switch (*hquery->remaining_lookups)
981✔
540
    {
541
      case 'b':
512✔
542
          /* RFC6761 section 6.3 #3 says "Name resolution APIs SHOULD NOT send
543
           * queries for localhost names to their configured caching DNS
544
           * server(s)." */
545
          if (!is_localhost(hquery->name))
512✔
546
            {
547
              /* DNS lookup */
548
              if (next_dns_lookup(hquery))
504✔
549
                break;
492✔
550
            }
551

552
          hquery->remaining_lookups++;
20✔
553
          next_lookup(hquery, status);
20✔
554
          break;
20✔
555

556
      case 'f':
453✔
557
          /* Host file lookup */
558
          if (file_lookup(hquery) == ARES_SUCCESS)
453✔
559
            {
560
              end_hquery(hquery, ARES_SUCCESS);
17✔
561
              break;
17✔
562
            }
563
          hquery->remaining_lookups++;
436✔
564
          next_lookup(hquery, status);
436✔
565
          break;
436✔
566
      default:
16✔
567
          /* No lookup left */
568
         end_hquery(hquery, status);
16✔
569
         break;
16✔
570
    }
571
}
981✔
572

573

574
static void terminate_retries(struct host_query *hquery, unsigned short qid)
372✔
575
{
576
  unsigned short term_qid = (qid == hquery->qid_a)?hquery->qid_aaaa:hquery->qid_a;
372✔
577
  ares_channel   channel  = hquery->channel;
372✔
578
  struct query  *query    = NULL;
372✔
579

580
  /* No other outstanding queries, nothing to do */
581
  if (!hquery->remaining)
372✔
582
    return;
363✔
583

584
  query = ares__htable_stvp_get_direct(channel->queries_by_qid, term_qid);
9✔
585
  if (query == NULL)
9✔
586
    return;
×
587

588
  query->no_retries = 1;
9✔
589
}
590

591

592
static void host_callback(void *arg, int status, int timeouts,
507✔
593
                          unsigned char *abuf, int alen)
594
{
595
  struct host_query *hquery = (struct host_query*)arg;
507✔
596
  int addinfostatus = ARES_SUCCESS;
507✔
597
  unsigned short qid = 0;
507✔
598
  hquery->timeouts += timeouts;
507✔
599
  hquery->remaining--;
507✔
600

601
  if (status == ARES_SUCCESS) {
507✔
602
    addinfostatus = ares__parse_into_addrinfo(abuf, alen, 1, hquery->port,
380✔
603
                                              hquery->ai);
604
    if (addinfostatus == ARES_SUCCESS && alen >= HFIXEDSZ) {
380✔
605
      qid = DNS_HEADER_QID(abuf); /* Converts to host byte order */
372✔
606
      terminate_retries(hquery, qid);
372✔
607
    }
608
  }
609

610
  if (!hquery->remaining) {
507✔
611
    if (addinfostatus != ARES_SUCCESS && addinfostatus != ARES_ENODATA) {
492✔
612
      /* error in parsing result e.g. no memory */
613
      if (addinfostatus == ARES_EBADRESP && hquery->ai->nodes) {
×
614
        /* We got a bad response from server, but at least one query
615
         * ended with ARES_SUCCESS */
616
        end_hquery(hquery, ARES_SUCCESS);
×
617
      } else {
618
        end_hquery(hquery, addinfostatus);
×
619
      }
620
    } else if (hquery->ai->nodes) {
492✔
621
      /* at least one query ended with ARES_SUCCESS */
622
      end_hquery(hquery, ARES_SUCCESS);
370✔
623
    } else if (status == ARES_EDESTRUCTION || status == ARES_ECANCELLED) {
122✔
624
      /* must make sure we don't do next_lookup() on destroy or cancel */
625
      end_hquery(hquery, status);
10✔
626
    } else if (status == ARES_ENOTFOUND || status == ARES_ENODATA ||
112✔
627
               addinfostatus == ARES_ENODATA) {
628
      if (status == ARES_ENODATA || addinfostatus == ARES_ENODATA)
68✔
629
        hquery->nodata_cnt++;
24✔
630
      next_lookup(hquery, hquery->nodata_cnt?ARES_ENODATA:status);
68✔
631
    } else {
632
      end_hquery(hquery, status);
44✔
633
    }
634
  }
635

636
  /* at this point we keep on waiting for the next query to finish */
637
}
507✔
638

639
void ares_getaddrinfo(ares_channel channel,
478✔
640
                      const char* name, const char* service,
641
                      const struct ares_addrinfo_hints* hints,
642
                      ares_addrinfo_callback callback, void* arg)
643
{
644
  struct host_query *hquery;
645
  unsigned short port = 0;
478✔
646
  int family;
647
  struct ares_addrinfo *ai;
648
  char *alias_name = NULL;
478✔
649
  int status;
650

651
  if (!hints)
478✔
652
    {
653
      hints = &default_hints;
×
654
    }
655

656
  family = hints->ai_family;
478✔
657

658
  /* Right now we only know how to look up Internet addresses
659
     and unspec means try both basically. */
660
  if (family != AF_INET &&
478✔
661
      family != AF_INET6 &&
18✔
662
      family != AF_UNSPEC)
663
    {
664
      callback(arg, ARES_ENOTIMP, 0, NULL);
4✔
665
      return;
21✔
666
    }
667

668
  if (ares__is_onion_domain(name))
474✔
669
    {
670
      callback(arg, ARES_ENOTFOUND, 0, NULL);
2✔
671
      return;
2✔
672
    }
673

674
  /* perform HOSTALIAS resolution (technically this function does some other
675
   * things we are going to ignore) */
676
  status = ares__single_domain(channel, name, &alias_name);
472✔
677
  if (status != ARES_SUCCESS) {
472✔
678
    callback(arg, status, 0, NULL);
×
679
    return;
×
680
  }
681

682
  if (alias_name)
472✔
683
    name = alias_name;
380✔
684

685
  if (service)
472✔
686
    {
687
      if (hints->ai_flags & ARES_AI_NUMERICSERV)
2✔
688
        {
689
          unsigned long val;
690
          errno = 0;
×
691
          val = strtoul(service, NULL, 0);
×
692
          if ((val == 0 && errno != 0) || val > 65535)
×
693
            {
694
              ares_free(alias_name);
×
695
              callback(arg, ARES_ESERVICE, 0, NULL);
×
696
              return;
×
697
            }
698
          port = (unsigned short)val;
×
699
        }
700
      else
701
        {
702
          port = lookup_service(service, 0);
2✔
703
          if (!port)
2✔
704
            {
705
              unsigned long val;
706
              errno = 0;
×
707
              val = strtoul(service, NULL, 0);
×
708
              if ((val == 0 && errno != 0) || val > 65535)
×
709
                {
710
                  ares_free(alias_name);
×
711
                  callback(arg, ARES_ESERVICE, 0, NULL);
×
712
                  return;
×
713
                }
714
              port = (unsigned short)val;
×
715
            }
716
        }
717
    }
718

719
  ai = ares__malloc_addrinfo();
472✔
720
  if (!ai)
472✔
721
    {
722
      ares_free(alias_name);
1✔
723
      callback(arg, ARES_ENOMEM, 0, NULL);
1✔
724
      return;
1✔
725
    }
726

727
  if (fake_addrinfo(name, port, hints, ai, callback, arg))
471✔
728
    {
729
      ares_free(alias_name);
12✔
730
      return;
12✔
731
    }
732

733
  /* Allocate and fill in the host query structure. */
734
  hquery = ares_malloc(sizeof(*hquery));
459✔
735
  if (!hquery)
459✔
736
    {
737
      ares_free(alias_name);
1✔
738
      ares_freeaddrinfo(ai);
1✔
739
      callback(arg, ARES_ENOMEM, 0, NULL);
1✔
740
      return;
1✔
741
    }
742
  memset(hquery, 0, sizeof(*hquery));
458✔
743
  hquery->name = ares_strdup(name);
458✔
744
  ares_free(alias_name);
458✔
745
  if (!hquery->name)
458✔
746
    {
747
      ares_free(hquery);
1✔
748
      ares_freeaddrinfo(ai);
1✔
749
      callback(arg, ARES_ENOMEM, 0, NULL);
1✔
750
      return;
1✔
751
    }
752

753
  hquery->port = port;
457✔
754
  hquery->channel = channel;
457✔
755
  hquery->hints = *hints;
457✔
756
  hquery->sent_family = -1; /* nothing is sent yet */
457✔
757
  hquery->callback = callback;
457✔
758
  hquery->arg = arg;
457✔
759
  hquery->remaining_lookups = channel->lookups;
457✔
760
  hquery->ai = ai;
457✔
761
  hquery->next_domain = -1;
457✔
762

763
  /* Start performing lookups according to channel->lookups. */
764
  next_lookup(hquery, ARES_ECONNREFUSED /* initial error code */);
457✔
765
}
766

767
static int next_dns_lookup(struct host_query *hquery)
504✔
768
{
769
  char *s = NULL;
504✔
770
  int is_s_allocated = 0;
504✔
771
  int status;
772

773
  /* if next_domain == -1 and as_is_first is true, try hquery->name */
774
  if (hquery->next_domain == -1)
504✔
775
    {
776
      if (as_is_first(hquery))
436✔
777
        {
778
          s = hquery->name;
385✔
779
        }
780
      hquery->next_domain = 0;
436✔
781
    }
782

783
  /* if as_is_first is false, try hquery->name at last */
784
  if (!s && hquery->next_domain == hquery->channel->ndomains) {
504✔
785
    if (!as_is_first(hquery))
6✔
786
      {
787
        s = hquery->name;
6✔
788
      }
789
    hquery->next_domain++;
6✔
790
  }
791

792
  if (!s && hquery->next_domain < hquery->channel->ndomains && !as_is_only(hquery))
504✔
793
    {
794
      status = ares__cat_domain(
103✔
795
          hquery->name,
103✔
796
          hquery->channel->domains[hquery->next_domain++],
103✔
797
          &s);
798
      if (status == ARES_SUCCESS)
103✔
799
        {
800
          is_s_allocated = 1;
101✔
801
        }
802
    }
803

804
  if (s)
504✔
805
    {
806
      /* NOTE: hquery may be invalidated during the call to ares_query_qid(),
807
       *       so should not be referenced after this point */
808
      switch (hquery->hints.ai_family)
492✔
809
        {
810
          case AF_INET:
469✔
811
            hquery->remaining += 1;
469✔
812
            ares_query_qid(hquery->channel, s, C_IN, T_A, host_callback, hquery,
469✔
813
                           &hquery->qid_a);
814
            break;
469✔
815
          case AF_INET6:
8✔
816
            hquery->remaining += 1;
8✔
817
            ares_query_qid(hquery->channel, s, C_IN, T_AAAA, host_callback,
8✔
818
                           hquery, &hquery->qid_aaaa);
819
            break;
8✔
820
          case AF_UNSPEC:
15✔
821
            hquery->remaining += 2;
15✔
822
            ares_query_qid(hquery->channel, s, C_IN, T_A, host_callback,
15✔
823
                           hquery, &hquery->qid_a);
824
            ares_query_qid(hquery->channel, s, C_IN, T_AAAA, host_callback,
15✔
825
                           hquery, &hquery->qid_aaaa);
826
            break;
15✔
827
          default: break;
×
828
        }
829
      if (is_s_allocated)
492✔
830
        {
831
          ares_free(s);
101✔
832
        }
833
      return 1;
492✔
834
    }
835
  else
836
    {
837
      assert(!hquery->ai->nodes);
838
      return 0;
12✔
839
    }
840
}
841

842
static int as_is_first(const struct host_query* hquery)
442✔
843
{
844
  char* p;
845
  int ndots = 0;
442✔
846
  size_t nname = hquery->name?strlen(hquery->name):0;
442✔
847
  for (p = hquery->name; p && *p; p++)
6,388✔
848
    {
849
      if (*p == '.')
5,946✔
850
        {
851
          ndots++;
1,129✔
852
        }
853
    }
854
  if (nname && hquery->name[nname-1] == '.')
442✔
855
    {
856
      /* prevent ARES_EBADNAME for valid FQDN, where ndots < channel->ndots  */
857
      return 1;
378✔
858
    }
859
  return ndots >= hquery->channel->ndots;
64✔
860
}
861

862
static int as_is_only(const struct host_query* hquery)
109✔
863
{
864
  size_t nname = hquery->name?strlen(hquery->name):0;
109✔
865
  if (nname && hquery->name[nname-1] == '.')
109✔
866
    return 1;
6✔
867
  return 0;
103✔
868
}
869

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc