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

c-ares / c-ares / 12675075779

08 Jan 2025 04:43PM UTC coverage: 91.497% (-0.03%) from 91.526%
12675075779

push

github

web-flow
CI: GitHub actions ubuntu-latest now points to 24.04 causing some minor issues (#957)

Containerized tests now need to be run with sudo to gain access to
chroot(). Also, `gcov` in ubuntu 24.04 is broken with googletest so
revert back to 22.04 for coverage.

Finally, codespell is updated in 24.04 to detect more issues and there
were some typos that needed to be fixed.

Signed-off-by: Brad House (@bradh352)

22404 of 24486 relevant lines covered (91.5%)

11927.82 hits per line

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

91.04
/src/lib/ares_search.c
1
/* MIT License
2
 *
3
 * Copyright (c) 1998 Massachusetts Institute of Technology
4
 * Copyright (c) The c-ares project and its contributors
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice (including the next
14
 * paragraph) shall be included in all copies or substantial portions of the
15
 * Software.
16
 *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
 * SOFTWARE.
24
 *
25
 * SPDX-License-Identifier: MIT
26
 */
27

28
#include "ares_private.h"
29

30
#ifdef HAVE_STRINGS_H
31
#  include <strings.h>
32
#endif
33

34
struct search_query {
35
  /* Arguments passed to ares_search_dnsrec() */
36
  ares_channel_t      *channel;
37
  ares_callback_dnsrec callback;
38
  void                *arg;
39

40
  /* Duplicate of DNS record passed to ares_search_dnsrec() */
41
  ares_dns_record_t   *dnsrec;
42

43
  /* Search order for names */
44
  char               **names;
45
  size_t               names_cnt;
46

47
  /* State tracking progress through the search query */
48
  size_t               next_name_idx; /* next name index being attempted */
49
  size_t      timeouts;        /* number of timeouts we saw for this request */
50
  ares_bool_t ever_got_nodata; /* did we ever get ARES_ENODATA along the way? */
51
};
52

53
static void squery_free(struct search_query *squery)
36✔
54
{
55
  if (squery == NULL) {
36✔
56
    return; /* LCOV_EXCL_LINE: DefensiveCoding */
57
  }
58
  ares_strsplit_free(squery->names, squery->names_cnt);
36✔
59
  ares_dns_record_destroy(squery->dnsrec);
36✔
60
  ares_free(squery);
36✔
61
}
62

63
/* End a search query by invoking the user callback and freeing the
64
 * search_query structure.
65
 */
66
static void end_squery(struct search_query *squery, ares_status_t status,
36✔
67
                       const ares_dns_record_t *dnsrec)
68
{
69
  squery->callback(squery->arg, status, squery->timeouts, dnsrec);
36✔
70
  squery_free(squery);
36✔
71
}
36✔
72

73
static void search_callback(void *arg, ares_status_t status, size_t timeouts,
74
                            const ares_dns_record_t *dnsrec);
75

76
static ares_status_t ares_search_next(ares_channel_t      *channel,
66✔
77
                                      struct search_query *squery,
78
                                      ares_bool_t         *skip_cleanup)
79
{
80
  ares_status_t status;
81

82
  *skip_cleanup = ARES_FALSE;
66✔
83

84
  /* Misuse check */
85
  if (squery->next_name_idx >= squery->names_cnt) {
66✔
86
    return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
87
  }
88

89
  status = ares_dns_record_query_set_name(
66✔
90
    squery->dnsrec, 0, squery->names[squery->next_name_idx++]);
66✔
91
  if (status != ARES_SUCCESS) {
66✔
92
    return status;
×
93
  }
94

95
  status = ares_send_nolock(channel, NULL, 0, squery->dnsrec, search_callback,
66✔
96
                            squery, NULL);
97

98
  if (status != ARES_EFORMERR) {
66✔
99
    *skip_cleanup = ARES_TRUE;
66✔
100
  }
101

102
  return status;
66✔
103
}
104

105
static void search_callback(void *arg, ares_status_t status, size_t timeouts,
66✔
106
                            const ares_dns_record_t *dnsrec)
107
{
108
  struct search_query *squery  = (struct search_query *)arg;
66✔
109
  ares_channel_t      *channel = squery->channel;
66✔
110

111
  ares_status_t        mystatus;
112
  ares_bool_t          skip_cleanup = ARES_FALSE;
66✔
113

114
  squery->timeouts += timeouts;
66✔
115

116
  if (dnsrec) {
66✔
117
    ares_dns_rcode_t rcode = ares_dns_record_get_rcode(dnsrec);
64✔
118
    size_t ancount = ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER);
64✔
119
    mystatus       = ares_dns_query_reply_tostatus(rcode, ancount);
64✔
120
  } else {
121
    mystatus = status;
2✔
122
  }
123

124
  switch (mystatus) {
66✔
125
    case ARES_ENODATA:
22✔
126
    case ARES_ENOTFOUND:
127
      break;
22✔
128
    case ARES_ESERVFAIL:
8✔
129
    case ARES_EREFUSED:
130
      /* Issue #852, systemd-resolved may return SERVFAIL or REFUSED on a
131
       * single label domain name. */
132
      if (ares_name_label_cnt(squery->names[squery->next_name_idx - 1]) != 1) {
8✔
133
        end_squery(squery, mystatus, dnsrec);
×
134
        return;
66✔
135
      }
136
      break;
8✔
137
    default:
36✔
138
      end_squery(squery, mystatus, dnsrec);
36✔
139
      return;
36✔
140
  }
141

142
  /* If we ever get ARES_ENODATA along the way, record that; if the search
143
   * should run to the very end and we got at least one ARES_ENODATA,
144
   * then callers like ares_gethostbyname() may want to try a T_A search
145
   * even if the last domain we queried for T_AAAA resource records
146
   * returned ARES_ENOTFOUND.
147
   */
148
  if (mystatus == ARES_ENODATA) {
30✔
149
    squery->ever_got_nodata = ARES_TRUE;
×
150
  }
151

152
  if (squery->next_name_idx < squery->names_cnt) {
30✔
153
    mystatus = ares_search_next(channel, squery, &skip_cleanup);
30✔
154
    if (mystatus != ARES_SUCCESS && !skip_cleanup) {
30✔
155
      end_squery(squery, mystatus, NULL);
×
156
    }
157
    return;
30✔
158
  }
159

160
  /* We have no more domains to search, return an appropriate response. */
161
  if (mystatus == ARES_ENOTFOUND && squery->ever_got_nodata) {
×
162
    end_squery(squery, ARES_ENODATA, NULL);
×
163
    return;
×
164
  }
165

166
  end_squery(squery, mystatus, NULL);
×
167
}
168

169
/* Determine if the domain should be looked up as-is, or if it is eligible
170
 * for search by appending domains */
171
static ares_bool_t ares_search_eligible(const ares_channel_t *channel,
4,723✔
172
                                        const char           *name)
173
{
174
  size_t len = ares_strlen(name);
4,723✔
175

176
  /* Name ends in '.', cannot search */
177
  if (len && name[len - 1] == '.') {
4,723✔
178
    return ARES_FALSE;
4,385✔
179
  }
180

181
  if (channel->flags & ARES_FLAG_NOSEARCH) {
338✔
182
    return ARES_FALSE;
×
183
  }
184

185
  return ARES_TRUE;
338✔
186
}
187

188
size_t ares_name_label_cnt(const char *name)
418✔
189
{
190
  const char *p;
191
  size_t      ndots = 0;
418✔
192

193
  if (name == NULL) {
418✔
194
    return 0;
×
195
  }
196

197
  for (p = name; p != NULL && *p != 0; p++) {
3,440✔
198
    if (*p == '.') {
3,022✔
199
      ndots++;
325✔
200
    }
201
  }
202

203
  /* Label count is 1 greater than ndots */
204
  return ndots + 1;
418✔
205
}
206

207
ares_status_t ares_search_name_list(const ares_channel_t *channel,
4,739✔
208
                                    const char *name, char ***names,
209
                                    size_t *names_len)
210
{
211
  ares_status_t status;
212
  char        **list     = NULL;
4,739✔
213
  size_t        list_len = 0;
4,739✔
214
  char         *alias    = NULL;
4,739✔
215
  size_t        ndots    = 0;
4,739✔
216
  size_t        idx      = 0;
4,739✔
217
  size_t        i;
218

219
  /* Perform HOSTALIASES resolution */
220
  status = ares_lookup_hostaliases(channel, name, &alias);
4,739✔
221
  if (status == ARES_SUCCESS) {
4,739✔
222
    /* If hostalias succeeds, there is no searching, it is used as-is */
223
    list_len = 1;
16✔
224
    list     = ares_malloc_zero(sizeof(*list) * list_len);
16✔
225
    if (list == NULL) {
16✔
226
      status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
227
      goto done;            /* LCOV_EXCL_LINE: OutOfMemory */
228
    }
229
    list[0] = alias;
16✔
230
    alias   = NULL;
16✔
231
    goto done;
16✔
232
  } else if (status != ARES_ENOTFOUND) {
4,723✔
233
    goto done;
×
234
  }
235

236
  /* See if searching is eligible at all, if not, look up as-is only */
237
  if (!ares_search_eligible(channel, name)) {
4,723✔
238
    list_len = 1;
4,385✔
239
    list     = ares_malloc_zero(sizeof(*list) * list_len);
4,385✔
240
    if (list == NULL) {
4,385✔
241
      status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
242
      goto done;            /* LCOV_EXCL_LINE: OutOfMemory */
243
    }
244
    list[0] = ares_strdup(name);
4,385✔
245
    if (list[0] == NULL) {
4,385✔
246
      status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
247
    } else {
248
      status = ARES_SUCCESS;
4,385✔
249
    }
250
    goto done;
4,385✔
251
  }
252

253
  /* Count the number of dots in name, 1 less than label count */
254
  ndots = ares_name_label_cnt(name);
338✔
255
  if (ndots > 0) {
338✔
256
    ndots--;
338✔
257
  }
258

259
  /* Allocate an entry for each search domain, plus one for as-is */
260
  list_len = channel->ndomains + 1;
338✔
261
  list     = ares_malloc_zero(sizeof(*list) * list_len);
338✔
262
  if (list == NULL) {
338✔
263
    status = ARES_ENOMEM;
3✔
264
    goto done;
3✔
265
  }
266

267
  /* Set status here, its possible there are no search domains at all, so
268
   * status may be ARES_ENOTFOUND from ares_lookup_hostaliases(). */
269
  status = ARES_SUCCESS;
335✔
270

271
  /* Try as-is first */
272
  if (ndots >= channel->ndots) {
335✔
273
    list[idx] = ares_strdup(name);
89✔
274
    if (list[idx] == NULL) {
89✔
275
      status = ARES_ENOMEM;
1✔
276
      goto done;
1✔
277
    }
278
    idx++;
88✔
279
  }
280

281
  /* Append each search suffix to the name */
282
  for (i = 0; i < channel->ndomains; i++) {
1,176✔
283
    status = ares_cat_domain(name, channel->domains[i], &list[idx]);
849✔
284
    if (status != ARES_SUCCESS) {
849✔
285
      goto done;
7✔
286
    }
287
    idx++;
842✔
288
  }
289

290
  /* Try as-is last */
291
  if (ndots < channel->ndots) {
327✔
292
    list[idx] = ares_strdup(name);
240✔
293
    if (list[idx] == NULL) {
240✔
294
      status = ARES_ENOMEM;
2✔
295
      goto done;
2✔
296
    }
297
    idx++;
238✔
298
  }
299

300

301
done:
87✔
302
  if (status == ARES_SUCCESS) {
4,739✔
303
    *names     = list;
4,726✔
304
    *names_len = list_len;
4,726✔
305
  } else {
306
    ares_strsplit_free(list, list_len);
13✔
307
  }
308

309
  ares_free(alias);
4,739✔
310
  return status;
4,739✔
311
}
312

313
static ares_status_t ares_search_int(ares_channel_t          *channel,
36✔
314
                                     const ares_dns_record_t *dnsrec,
315
                                     ares_callback_dnsrec callback, void *arg)
316
{
317
  struct search_query *squery = NULL;
36✔
318
  const char          *name;
319
  ares_status_t        status       = ARES_SUCCESS;
36✔
320
  ares_bool_t          skip_cleanup = ARES_FALSE;
36✔
321

322
  /* Extract the name for the search. Note that searches are only supported for
323
   * DNS records containing a single query.
324
   */
325
  if (ares_dns_record_query_cnt(dnsrec) != 1) {
36✔
326
    status = ARES_EBADQUERY;
×
327
    goto fail;
×
328
  }
329

330
  status = ares_dns_record_query_get(dnsrec, 0, &name, NULL, NULL);
36✔
331
  if (status != ARES_SUCCESS) {
36✔
332
    goto fail;
×
333
  }
334

335
  /* Per RFC 7686, reject queries for ".onion" domain names with NXDOMAIN. */
336
  if (ares_is_onion_domain(name)) {
36✔
337
    status = ARES_ENOTFOUND;
×
338
    goto fail;
×
339
  }
340

341
  /* Allocate a search_query structure to hold the state necessary for
342
   * doing multiple lookups.
343
   */
344
  squery = ares_malloc_zero(sizeof(*squery));
36✔
345
  if (squery == NULL) {
36✔
346
    status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
347
    goto fail;            /* LCOV_EXCL_LINE: OutOfMemory */
348
  }
349

350
  squery->channel = channel;
36✔
351

352
  /* Duplicate DNS record since, name will need to be rewritten */
353
  squery->dnsrec = ares_dns_record_duplicate(dnsrec);
36✔
354
  if (squery->dnsrec == NULL) {
36✔
355
    status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
356
    goto fail;            /* LCOV_EXCL_LINE: OutOfMemory */
357
  }
358

359
  squery->callback        = callback;
36✔
360
  squery->arg             = arg;
36✔
361
  squery->timeouts        = 0;
36✔
362
  squery->ever_got_nodata = ARES_FALSE;
36✔
363

364
  status =
365
    ares_search_name_list(channel, name, &squery->names, &squery->names_cnt);
36✔
366
  if (status != ARES_SUCCESS) {
36✔
367
    goto fail;
×
368
  }
369

370
  status = ares_search_next(channel, squery, &skip_cleanup);
36✔
371
  if (status != ARES_SUCCESS) {
36✔
372
    goto fail;
2✔
373
  }
374

375
  return status;
34✔
376

377
fail:
2✔
378
  if (!skip_cleanup) {
2✔
379
    squery_free(squery);
×
380
    callback(arg, status, 0, NULL);
×
381
  }
382
  return status;
2✔
383
}
384

385
/* Callback argument structure passed to ares_dnsrec_convert_cb(). */
386
typedef struct {
387
  ares_callback callback;
388
  void         *arg;
389
} dnsrec_convert_arg_t;
390

391
/*! Function to create callback arg for converting from ares_callback_dnsrec
392
 *  to ares_calback */
393
void *ares_dnsrec_convert_arg(ares_callback callback, void *arg)
31✔
394
{
395
  dnsrec_convert_arg_t *carg = ares_malloc_zero(sizeof(*carg));
31✔
396
  if (carg == NULL) {
31✔
397
    return NULL;
4✔
398
  }
399
  carg->callback = callback;
27✔
400
  carg->arg      = arg;
27✔
401
  return carg;
27✔
402
}
403

404
/*! Callback function used to convert from the ares_callback_dnsrec prototype to
405
 *  the ares_callback prototype, by writing the result and passing that to
406
 *  the inner callback.
407
 */
408
void ares_dnsrec_convert_cb(void *arg, ares_status_t status, size_t timeouts,
26✔
409
                            const ares_dns_record_t *dnsrec)
410
{
411
  dnsrec_convert_arg_t *carg = arg;
26✔
412
  unsigned char        *abuf = NULL;
26✔
413
  size_t                alen = 0;
26✔
414

415
  if (dnsrec != NULL) {
26✔
416
    ares_status_t mystatus = ares_dns_write(dnsrec, &abuf, &alen);
24✔
417
    if (mystatus != ARES_SUCCESS) {
24✔
418
      status = mystatus;
×
419
    }
420
  }
421

422
  carg->callback(carg->arg, (int)status, (int)timeouts, abuf, (int)alen);
26✔
423

424
  ares_free(abuf);
26✔
425
  ares_free(carg);
26✔
426
}
26✔
427

428
/* Search for a DNS name with given class and type. Wrapper around
429
 * ares_search_int() where the DNS record to search is first constructed.
430
 */
431
void ares_search(ares_channel_t *channel, const char *name, int dnsclass,
31✔
432
                 int type, ares_callback callback, void *arg)
433
{
434
  ares_status_t      status;
435
  ares_dns_record_t *dnsrec = NULL;
31✔
436
  size_t             max_udp_size;
437
  ares_dns_flags_t   rd_flag;
438
  void              *carg = NULL;
31✔
439
  if (channel == NULL || name == NULL) {
31✔
440
    return;
5✔
441
  }
442

443
  /* For now, ares_search_int() uses the ares_callback prototype. We need to
444
   * wrap the callback passed to this function in ares_dnsrec_convert_cb, to
445
   * convert from ares_callback_dnsrec to ares_callback. Allocate the convert
446
   * arg structure here.
447
   */
448
  carg = ares_dnsrec_convert_arg(callback, arg);
31✔
449
  if (carg == NULL) {
31✔
450
    callback(arg, ARES_ENOMEM, 0, NULL, 0);
4✔
451
    return;
4✔
452
  }
453

454
  rd_flag      = !(channel->flags & ARES_FLAG_NORECURSE) ? ARES_FLAG_RD : 0;
27✔
455
  max_udp_size = (channel->flags & ARES_FLAG_EDNS) ? channel->ednspsz : 0;
27✔
456
  status       = ares_dns_record_create_query(
27✔
457
    &dnsrec, name, (ares_dns_class_t)dnsclass, (ares_dns_rec_type_t)type, 0,
458
    rd_flag, max_udp_size);
459
  if (status != ARES_SUCCESS) {
27✔
460
    callback(arg, (int)status, 0, NULL, 0);
1✔
461
    ares_free(carg);
1✔
462
    return;
1✔
463
  }
464

465
  ares_channel_lock(channel);
26✔
466
  ares_search_int(channel, dnsrec, ares_dnsrec_convert_cb, carg);
26✔
467
  ares_channel_unlock(channel);
26✔
468

469
  ares_dns_record_destroy(dnsrec);
26✔
470
}
471

472
/* Search for a DNS record. Wrapper around ares_search_int(). */
473
ares_status_t ares_search_dnsrec(ares_channel_t          *channel,
10✔
474
                                 const ares_dns_record_t *dnsrec,
475
                                 ares_callback_dnsrec callback, void *arg)
476
{
477
  ares_status_t status;
478

479
  if (channel == NULL || dnsrec == NULL || callback == NULL) {
10✔
480
    return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
481
  }
482

483
  ares_channel_lock(channel);
10✔
484
  status = ares_search_int(channel, dnsrec, callback, arg);
10✔
485
  ares_channel_unlock(channel);
10✔
486

487
  return status;
10✔
488
}
489

490
/* Concatenate two domains. */
491
ares_status_t ares_cat_domain(const char *name, const char *domain, char **s)
852✔
492
{
493
  size_t nlen = ares_strlen(name);
852✔
494
  size_t dlen = ares_strlen(domain);
852✔
495

496
  *s = ares_malloc(nlen + 1 + dlen + 1);
852✔
497
  if (!*s) {
852✔
498
    return ARES_ENOMEM;
7✔
499
  }
500
  memcpy(*s, name, nlen);
845✔
501
  (*s)[nlen] = '.';
845✔
502
  if (ares_streq(domain, ".")) {
845✔
503
    /* Avoid appending the root domain to the separator, which would set *s to
504
       an ill-formed value (ending in two consecutive dots). */
505
    dlen = 0;
1✔
506
  }
507
  memcpy(*s + nlen + 1, domain, dlen);
845✔
508
  (*s)[nlen + 1 + dlen] = 0;
845✔
509
  return ARES_SUCCESS;
845✔
510
}
511

512
ares_status_t ares_lookup_hostaliases(const ares_channel_t *channel,
4,739✔
513
                                      const char *name, char **alias)
514
{
515
  ares_status_t status      = ARES_SUCCESS;
4,739✔
516
  const char   *hostaliases = NULL;
4,739✔
517
  ares_buf_t   *buf         = NULL;
4,739✔
518
  ares_array_t *lines       = NULL;
4,739✔
519
  size_t        num;
520
  size_t        i;
521

522
  if (channel == NULL || name == NULL || alias == NULL) {
4,739✔
523
    return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */
524
  }
525

526
  *alias = NULL;
4,739✔
527

528
  /* Configuration says to not perform alias lookup */
529
  if (channel->flags & ARES_FLAG_NOALIASES) {
4,739✔
530
    return ARES_ENOTFOUND;
×
531
  }
532

533
  /* If a domain has a '.', its not allowed to perform an alias lookup */
534
  if (strchr(name, '.') != NULL) {
4,739✔
535
    return ARES_ENOTFOUND;
4,451✔
536
  }
537

538
  hostaliases = getenv("HOSTALIASES");
288✔
539
  if (hostaliases == NULL) {
288✔
540
    status = ARES_ENOTFOUND;
240✔
541
    goto done;
240✔
542
  }
543

544
  buf = ares_buf_create();
48✔
545
  if (buf == NULL) {
48✔
546
    status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
547
    goto done;            /* LCOV_EXCL_LINE: OutOfMemory */
548
  }
549

550
  status = ares_buf_load_file(hostaliases, buf);
48✔
551
  if (status != ARES_SUCCESS) {
48✔
552
    goto done;
16✔
553
  }
554

555
  /* The HOSTALIASES file is structured as one alias per line.  The first
556
   * field in the line is the simple hostname with no periods, followed by
557
   * whitespace, then the full domain name, e.g.:
558
   *
559
   * c-ares  www.c-ares.org
560
   * curl    www.curl.se
561
   */
562

563
  status = ares_buf_split(buf, (const unsigned char *)"\n", 1,
32✔
564
                          ARES_BUF_SPLIT_TRIM, 0, &lines);
565
  if (status != ARES_SUCCESS) {
32✔
566
    goto done;
×
567
  }
568

569
  num = ares_array_len(lines);
32✔
570
  for (i = 0; i < num; i++) {
80✔
571
    ares_buf_t **bufptr       = ares_array_at(lines, i);
64✔
572
    ares_buf_t  *line         = *bufptr;
64✔
573
    char         hostname[64] = "";
64✔
574
    char         fqdn[256]    = "";
64✔
575

576
    /* Pull off hostname */
577
    ares_buf_tag(line);
64✔
578
    ares_buf_consume_nonwhitespace(line);
64✔
579
    if (ares_buf_tag_fetch_string(line, hostname, sizeof(hostname)) !=
64✔
580
        ARES_SUCCESS) {
581
      continue;
48✔
582
    }
583

584
    /* Match hostname */
585
    if (!ares_strcaseeq(hostname, name)) {
64✔
586
      continue;
48✔
587
    }
588

589
    /* consume whitespace */
590
    ares_buf_consume_whitespace(line, ARES_TRUE);
16✔
591

592
    /* pull off fqdn */
593
    ares_buf_tag(line);
16✔
594
    ares_buf_consume_nonwhitespace(line);
16✔
595
    if (ares_buf_tag_fetch_string(line, fqdn, sizeof(fqdn)) != ARES_SUCCESS ||
32✔
596
        ares_strlen(fqdn) == 0) {
16✔
597
      continue;
×
598
    }
599

600
    /* Validate characterset */
601
    if (!ares_is_hostname(fqdn)) {
16✔
602
      continue;
×
603
    }
604

605
    *alias = ares_strdup(fqdn);
16✔
606
    if (*alias == NULL) {
16✔
607
      status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
608
      goto done;            /* LCOV_EXCL_LINE: OutOfMemory */
609
    }
610

611
    /* Good! */
612
    status = ARES_SUCCESS;
16✔
613
    goto done;
16✔
614
  }
615

616
  status = ARES_ENOTFOUND;
16✔
617

618
done:
288✔
619
  ares_buf_destroy(buf);
288✔
620
  ares_array_destroy(lines);
288✔
621

622
  return status;
288✔
623
}
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