Coverage Report

Created: 2026-08-15 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/c-ares/src/lib/ares_init.c
Line
Count
Source
1
/* MIT License
2
 *
3
 * Copyright (c) 1998 Massachusetts Institute of Technology
4
 * Copyright (c) 2007 Daniel Stenberg
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_SYS_PARAM_H
31
#  include <sys/param.h>
32
#endif
33
34
#ifdef HAVE_NETINET_IN_H
35
#  include <netinet/in.h>
36
#endif
37
38
#ifdef HAVE_NETDB_H
39
#  include <netdb.h>
40
#endif
41
42
#ifdef HAVE_ARPA_INET_H
43
#  include <arpa/inet.h>
44
#endif
45
46
#include "ares_nameser.h"
47
48
#if defined(ANDROID) || defined(__ANDROID__)
49
#  include <sys/system_properties.h>
50
#  include "ares_android.h"
51
/* From the Bionic sources */
52
#  define DNS_PROP_NAME_PREFIX "net.dns"
53
#  define MAX_DNS_PROPERTIES   8
54
#endif
55
56
#if defined(CARES_USE_LIBRESOLV)
57
#  include <resolv.h>
58
#endif
59
60
#if defined(USE_WINSOCK) && defined(HAVE_IPHLPAPI_H)
61
#  include <iphlpapi.h>
62
#endif
63
64
#include "ares_inet_net_pton.h"
65
#include "event/ares_event.h"
66
67
int ares_init(ares_channel_t **channelptr)
68
4.39k
{
69
4.39k
  return ares_init_options(channelptr, NULL, 0);
70
4.39k
}
71
72
static int ares_query_timeout_cmp_cb(const void *arg1, const void *arg2)
73
0
{
74
0
  const ares_query_t *q1 = arg1;
75
0
  const ares_query_t *q2 = arg2;
76
77
0
  if (q1->timeout.sec > q2->timeout.sec) {
78
0
    return 1;
79
0
  }
80
0
  if (q1->timeout.sec < q2->timeout.sec) {
81
0
    return -1;
82
0
  }
83
84
0
  if (q1->timeout.usec > q2->timeout.usec) {
85
0
    return 1;
86
0
  }
87
0
  if (q1->timeout.usec < q2->timeout.usec) {
88
0
    return -1;
89
0
  }
90
91
0
  return 0;
92
0
}
93
94
static int server_sort_cb(const void *data1, const void *data2)
95
137k
{
96
137k
  const ares_server_t *s1 = data1;
97
137k
  const ares_server_t *s2 = data2;
98
99
137k
  if (s1->consec_failures < s2->consec_failures) {
100
0
    return -1;
101
0
  }
102
137k
  if (s1->consec_failures > s2->consec_failures) {
103
0
    return 1;
104
0
  }
105
106
  /* Among servers with the same failure count, prefer the one that failed
107
   * least recently so that a server that went down long ago (and may have
108
   * since recovered) is tried before one that just failed.  This also keeps
109
   * retries rotating across servers once the failure counts saturate at
110
   * SERVER_CONSEC_FAILURES_CAP.  Healthy servers all have a zeroed retry
111
   * time and fall through to configuration order. */
112
137k
  if (s1->next_retry_time.sec != s2->next_retry_time.sec) {
113
0
    return s1->next_retry_time.sec < s2->next_retry_time.sec ? -1 : 1;
114
0
  }
115
137k
  if (s1->next_retry_time.usec != s2->next_retry_time.usec) {
116
0
    return s1->next_retry_time.usec < s2->next_retry_time.usec ? -1 : 1;
117
0
  }
118
119
137k
  if (s1->idx < s2->idx) {
120
0
    return -1;
121
0
  }
122
137k
  if (s1->idx > s2->idx) {
123
136k
    return 1;
124
136k
  }
125
1.01k
  return 0;
126
137k
}
127
128
static void server_destroy_cb(void *data)
129
1.17k
{
130
1.17k
  if (data == NULL) {
131
0
    return; /* LCOV_EXCL_LINE: DefensiveCoding */
132
0
  }
133
1.17k
  ares_destroy_server(data);
134
1.17k
}
135
136
static ares_status_t init_by_defaults(ares_channel_t *channel)
137
4.39k
{
138
4.39k
  char         *hostname = NULL;
139
4.39k
  ares_status_t rc       = ARES_SUCCESS;
140
4.39k
#ifdef HAVE_GETHOSTNAME
141
4.39k
  const char *dot;
142
4.39k
#endif
143
4.39k
  struct ares_addr addr;
144
4.39k
  ares_llist_t    *sconfig = NULL;
145
146
  /* Enable EDNS by default */
147
4.39k
  if (!(channel->optmask & ARES_OPT_FLAGS)) {
148
4.39k
    channel->flags = ARES_FLAG_EDNS;
149
4.39k
  }
150
4.39k
  if (channel->ednspsz == 0) {
151
4.39k
    channel->ednspsz = EDNSPACKETSZ;
152
4.39k
  }
153
154
4.39k
  if (channel->timeout == 0) {
155
4.39k
    channel->timeout = DEFAULT_TIMEOUT;
156
4.39k
  }
157
158
4.39k
  if (channel->tries == 0) {
159
4.39k
    channel->tries = DEFAULT_TRIES;
160
4.39k
  }
161
162
4.39k
  if (ares_slist_len(channel->servers) == 0) {
163
    /* Add a default local named server to the channel unless configured not
164
     * to (in which case return an error).
165
     */
166
0
    if (channel->flags & ARES_FLAG_NO_DFLT_SVR) {
167
0
      rc = ARES_ENOSERVER;
168
0
      goto error;
169
0
    }
170
171
0
    addr.family            = AF_INET;
172
0
    addr.addr.addr4.s_addr = htonl(INADDR_LOOPBACK);
173
174
0
    rc = ares_sconfig_append(channel, &sconfig, &addr, 0, 0, NULL);
175
0
    if (rc != ARES_SUCCESS) {
176
0
      goto error; /* LCOV_EXCL_LINE: OutOfMemory */
177
0
    }
178
179
0
    rc = ares_servers_update(channel, sconfig, ARES_FALSE);
180
0
    ares_llist_destroy(sconfig);
181
182
0
    if (rc != ARES_SUCCESS) {
183
0
      goto error;
184
0
    }
185
0
  }
186
187
4.39k
  if (channel->ndomains == 0) {
188
    /* Derive a default domain search list from the kernel hostname,
189
     * or set it to empty if the hostname isn't helpful.
190
     */
191
#ifndef HAVE_GETHOSTNAME
192
    channel->ndomains = 0; /* default to none */
193
#else
194
0
    size_t len        = 256;
195
0
    channel->ndomains = 0; /* default to none */
196
197
0
    hostname = ares_malloc(len);
198
0
    if (!hostname) {
199
0
      rc = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
200
0
      goto error;       /* LCOV_EXCL_LINE: OutOfMemory */
201
0
    }
202
203
0
    if (gethostname(hostname, (GETHOSTNAME_TYPE_ARG2)len) != 0) {
204
      /* Lets not treat a gethostname failure as critical, since we
205
       * are ok if gethostname doesn't even exist */
206
0
      *hostname = '\0';
207
0
    }
208
209
0
    dot = strchr(hostname, '.');
210
0
    if (dot) {
211
      /* a dot was found */
212
0
      channel->domains = ares_malloc(sizeof(char *));
213
0
      if (!channel->domains) {
214
0
        rc = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
215
0
        goto error;       /* LCOV_EXCL_LINE: OutOfMemory */
216
0
      }
217
0
      channel->domains[0] = ares_strdup(dot + 1);
218
0
      if (!channel->domains[0]) {
219
0
        rc = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
220
0
        goto error;       /* LCOV_EXCL_LINE: OutOfMemory */
221
0
      }
222
0
      channel->ndomains = 1;
223
0
    }
224
0
#endif
225
0
  }
226
227
4.39k
  if (channel->nsort == 0) {
228
4.39k
    channel->sortlist = NULL;
229
4.39k
  }
230
231
4.39k
  if (!channel->lookups) {
232
0
    channel->lookups = ares_strdup("fb");
233
0
    if (!channel->lookups) {
234
0
      rc = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
235
0
    }
236
0
  }
237
238
  /* Set default fields for server failover behavior */
239
4.39k
  if (!(channel->optmask & ARES_OPT_SERVER_FAILOVER)) {
240
4.39k
    channel->server_retry_chance = DEFAULT_SERVER_RETRY_CHANCE;
241
4.39k
    channel->server_retry_delay  = DEFAULT_SERVER_RETRY_DELAY;
242
4.39k
  }
243
244
4.39k
error:
245
4.39k
  if (hostname) {
246
0
    ares_free(hostname);
247
0
  }
248
249
4.39k
  return rc;
250
4.39k
}
251
252
int ares_init_options(ares_channel_t           **channelptr,
253
                      const struct ares_options *options, int optmask)
254
4.39k
{
255
4.39k
  ares_channel_t *channel;
256
4.39k
  ares_status_t   status = ARES_SUCCESS;
257
258
4.39k
  if (ares_library_initialized() != ARES_SUCCESS) {
259
0
    return ARES_ENOTINITIALIZED; /* LCOV_EXCL_LINE: n/a on non-WinSock */
260
0
  }
261
262
4.39k
  channel = ares_malloc_zero(sizeof(*channel));
263
4.39k
  if (!channel) {
264
0
    *channelptr = NULL;
265
0
    return ARES_ENOMEM;
266
0
  }
267
268
  /* We are in a good state */
269
4.39k
  channel->sys_up = ARES_TRUE;
270
271
  /* One option where zero is valid, so set default value here */
272
4.39k
  channel->ndots = 1;
273
274
4.39k
  status = ares_channel_threading_init(channel);
275
4.39k
  if (status != ARES_SUCCESS) {
276
0
    goto done;
277
0
  }
278
279
  /* Generate random key */
280
4.39k
  channel->rand_state = ares_init_rand_state();
281
4.39k
  if (channel->rand_state == NULL) {
282
0
    status = ARES_ENOMEM;
283
0
    DEBUGF(fprintf(stderr, "Error: init_id_key failed: %s\n",
284
0
                   ares_strerror(status)));
285
0
    goto done;
286
0
  }
287
288
4.39k
  ares_set_socket_functions_def(channel);
289
290
  /* Initialize Server List */
291
4.39k
  channel->servers =
292
4.39k
    ares_slist_create(channel->rand_state, server_sort_cb, server_destroy_cb);
293
4.39k
  if (channel->servers == NULL) {
294
0
    status = ARES_ENOMEM;
295
0
    goto done;
296
0
  }
297
298
  /* Initialize our lists of queries */
299
4.39k
  channel->all_queries = ares_llist_create(NULL);
300
4.39k
  if (channel->all_queries == NULL) {
301
0
    status = ARES_ENOMEM;
302
0
    goto done;
303
0
  }
304
305
4.39k
  channel->queries_by_qid = ares_htable_szvp_create(NULL);
306
4.39k
  if (channel->queries_by_qid == NULL) {
307
0
    status = ARES_ENOMEM;
308
0
    goto done;
309
0
  }
310
311
4.39k
  channel->queries_by_timeout =
312
4.39k
    ares_slist_create(channel->rand_state, ares_query_timeout_cmp_cb, NULL);
313
4.39k
  if (channel->queries_by_timeout == NULL) {
314
0
    status = ARES_ENOMEM;
315
0
    goto done;
316
0
  }
317
318
4.39k
  channel->connnode_by_socket = ares_htable_asvp_create(NULL);
319
4.39k
  if (channel->connnode_by_socket == NULL) {
320
0
    status = ARES_ENOMEM;
321
0
    goto done;
322
0
  }
323
324
  /* Initialize configuration by each of the four sources, from highest
325
   * precedence to lowest.
326
   */
327
328
4.39k
  status = ares_init_by_options(channel, options, optmask);
329
4.39k
  if (status != ARES_SUCCESS) {
330
0
    DEBUGF(fprintf(stderr, "Error: init_by_options failed: %s\n",
331
0
                   ares_strerror(status)));
332
    /* If we fail to apply user-specified options, fail the whole init process
333
     */
334
0
    goto done;
335
0
  }
336
337
  /* Go ahead and let it initialize the query cache even if the ttl is 0 and
338
   * completely unused.  This reduces the number of different code paths that
339
   * might be followed even if there is a minor performance hit. */
340
4.39k
  status = ares_qcache_create(channel->rand_state, channel->qcache_max_ttl,
341
4.39k
                              &channel->qcache);
342
4.39k
  if (status != ARES_SUCCESS) {
343
0
    goto done; /* LCOV_EXCL_LINE: OutOfMemory */
344
0
  }
345
346
4.39k
  if (status == ARES_SUCCESS) {
347
4.39k
    status = ares_init_by_sysconfig(channel);
348
4.39k
    if (status != ARES_SUCCESS) {
349
0
      DEBUGF(fprintf(stderr, "Error: init_by_sysconfig failed: %s\n",
350
0
                     ares_strerror(status)));
351
0
    }
352
4.39k
  }
353
354
  /*
355
   * No matter what failed or succeeded, seed defaults to provide
356
   * useful behavior for things that we missed.
357
   */
358
4.39k
  status = init_by_defaults(channel);
359
4.39k
  if (status != ARES_SUCCESS) {
360
0
    DEBUGF(fprintf(stderr, "Error: init_by_defaults failed: %s\n",
361
0
                   ares_strerror(status)));
362
0
    goto done;
363
0
  }
364
365
  /* Initialize the event thread */
366
4.39k
  if (channel->optmask & ARES_OPT_EVENT_THREAD) {
367
0
    ares_event_thread_t *e = NULL;
368
369
0
    status = ares_event_thread_init(channel);
370
0
    if (status != ARES_SUCCESS) {
371
0
      goto done; /* LCOV_EXCL_LINE: UntestablePath */
372
0
    }
373
374
    /* Initialize monitor for configuration changes.  In some rare cases,
375
     * ARES_ENOTIMP may occur (OpenWatcom), ignore this. */
376
0
    e      = channel->sock_state_cb_data;
377
0
    status = ares_event_configchg_init(&e->configchg, e);
378
0
    if (status != ARES_SUCCESS && status != ARES_ENOTIMP) {
379
0
      DEBUGF(fprintf(stderr, "Error: ares_event_configchg_init failed: %s\n",
380
0
                     ares_strerror(status)));
381
0
    }
382
0
    status = ARES_SUCCESS;
383
0
  }
384
385
4.39k
done:
386
4.39k
  if (status != ARES_SUCCESS) {
387
0
    ares_destroy(channel);
388
0
    return (int)status;
389
0
  }
390
391
4.39k
  *channelptr = channel;
392
4.39k
  return ARES_SUCCESS;
393
4.39k
}
394
395
static void *ares_reinit_thread(void *arg)
396
0
{
397
0
  ares_channel_t *channel = arg;
398
0
  ares_status_t   status;
399
400
  /* ares_init_by_sysconfig() will lock when applying the config, but not
401
   * when retrieving. */
402
0
  status = ares_init_by_sysconfig(channel);
403
0
  if (status != ARES_SUCCESS) {
404
0
    DEBUGF(fprintf(stderr, "Error: init_by_sysconfig failed: %s\n",
405
0
                   ares_strerror(status)));
406
0
  }
407
408
0
  ares_channel_lock(channel);
409
410
  /* Flush cached queries on reinit */
411
0
  if (status == ARES_SUCCESS && channel->qcache) {
412
0
    ares_qcache_flush(channel->qcache);
413
0
  }
414
415
0
  channel->reinit_pending = ARES_FALSE;
416
0
  ares_channel_unlock(channel);
417
418
0
  return NULL;
419
0
}
420
421
ares_status_t ares_reinit(ares_channel_t *channel)
422
0
{
423
0
  ares_status_t status = ARES_SUCCESS;
424
425
0
  if (channel == NULL) {
426
0
    return ARES_EFORMERR;
427
0
  }
428
429
0
  ares_channel_lock(channel);
430
431
  /* If a reinit is already in process, lets not do it again. Or if we are
432
   * shutting down, skip. */
433
0
  if (!channel->sys_up || channel->reinit_pending) {
434
0
    ares_channel_unlock(channel);
435
0
    return ARES_SUCCESS;
436
0
  }
437
0
  channel->reinit_pending = ARES_TRUE;
438
0
  ares_channel_unlock(channel);
439
440
0
  if (ares_threadsafety()) {
441
    /* clean up the prior reinit process's thread.  We know the thread isn't
442
     * running since reinit_pending was false */
443
0
    if (channel->reinit_thread != NULL) {
444
0
      void *rv;
445
0
      ares_thread_join(channel->reinit_thread, &rv);
446
0
      channel->reinit_thread = NULL;
447
0
    }
448
449
    /* Spawn a new thread */
450
0
    status =
451
0
      ares_thread_create(&channel->reinit_thread, ares_reinit_thread, channel);
452
0
    if (status != ARES_SUCCESS) {
453
      /* LCOV_EXCL_START: UntestablePath */
454
0
      ares_channel_lock(channel);
455
0
      channel->reinit_pending = ARES_FALSE;
456
0
      ares_channel_unlock(channel);
457
      /* LCOV_EXCL_STOP */
458
0
    }
459
0
  } else {
460
    /* Threading support not available, call directly */
461
0
    ares_reinit_thread(channel);
462
0
  }
463
464
0
  return status;
465
0
}
466
467
/* ares_dup() duplicates a channel handle with all its options and returns a
468
   new channel handle */
469
int ares_dup(ares_channel_t **dest, const ares_channel_t *src)
470
0
{
471
0
  struct ares_options opts;
472
0
  ares_status_t       rc;
473
0
  int                 optmask;
474
475
0
  if (dest == NULL || src == NULL) {
476
0
    return ARES_EFORMERR;
477
0
  }
478
479
0
  *dest = NULL; /* in case of failure return NULL explicitly */
480
481
  /* First get the options supported by the old ares_save_options() function,
482
     which is most of them */
483
0
  rc = (ares_status_t)ares_save_options(src, &opts, &optmask);
484
0
  if (rc != ARES_SUCCESS) {
485
0
    ares_destroy_options(&opts);
486
0
    goto done;
487
0
  }
488
489
  /* Then create the new channel with those options */
490
0
  rc = (ares_status_t)ares_init_options(dest, &opts, optmask);
491
492
  /* destroy the options copy to not leak any memory */
493
0
  ares_destroy_options(&opts);
494
495
0
  if (rc != ARES_SUCCESS) {
496
0
    goto done;
497
0
  }
498
499
0
  ares_channel_lock(src);
500
  /* Now clone the options that ares_save_options() doesn't support, but are
501
   * user-provided */
502
0
  (*dest)->sock_create_cb      = src->sock_create_cb;
503
0
  (*dest)->sock_create_cb_data = src->sock_create_cb_data;
504
0
  (*dest)->sock_config_cb      = src->sock_config_cb;
505
0
  (*dest)->sock_config_cb_data = src->sock_config_cb_data;
506
0
  memcpy(&(*dest)->sock_funcs, &(src->sock_funcs), sizeof((*dest)->sock_funcs));
507
0
  (*dest)->sock_func_cb_data            = src->sock_func_cb_data;
508
0
  (*dest)->legacy_sock_funcs            = src->legacy_sock_funcs;
509
0
  (*dest)->legacy_sock_funcs_cb_data    = src->legacy_sock_funcs_cb_data;
510
0
  (*dest)->server_state_cb              = src->server_state_cb;
511
0
  (*dest)->server_state_cb_data         = src->server_state_cb_data;
512
0
  (*dest)->notify_pending_write_cb      = src->notify_pending_write_cb;
513
0
  (*dest)->notify_pending_write_cb_data = src->notify_pending_write_cb_data;
514
0
  (*dest)->query_enqueue_cb             = src->query_enqueue_cb;
515
0
  (*dest)->query_enqueue_cb_data        = src->query_enqueue_cb_data;
516
517
0
  ares_strcpy((*dest)->local_dev_name, src->local_dev_name,
518
0
              sizeof((*dest)->local_dev_name));
519
0
  (*dest)->local_ip4 = src->local_ip4;
520
0
  memcpy((*dest)->local_ip6, src->local_ip6, sizeof(src->local_ip6));
521
0
  ares_channel_unlock(src);
522
523
  /* Servers are a bit unique as ares_init_options() only allows ipv4 servers
524
   * and not a port per server, but there are other user specified ways, that
525
   * too will toggle the optmask ARES_OPT_SERVERS to let us know.  If that's
526
   * the case, pull them in.
527
   *
528
   * We don't want to clone system-configuration servers though.
529
   *
530
   * We must use the "csv" format to get things like link-local address support
531
   */
532
533
0
  if (optmask & ARES_OPT_SERVERS) {
534
0
    char *csv = ares_get_servers_csv(src);
535
0
    if (csv == NULL) {
536
      /* LCOV_EXCL_START: OutOfMemory */
537
0
      ares_destroy(*dest);
538
0
      *dest = NULL;
539
0
      rc    = ARES_ENOMEM;
540
0
      goto done;
541
      /* LCOV_EXCL_STOP */
542
0
    }
543
544
0
    rc = (ares_status_t)ares_set_servers_ports_csv(*dest, csv);
545
0
    ares_free_string(csv);
546
0
    if (rc != ARES_SUCCESS) {
547
      /* LCOV_EXCL_START: OutOfMemory */
548
0
      ares_destroy(*dest);
549
0
      *dest = NULL;
550
0
      goto done;
551
      /* LCOV_EXCL_STOP */
552
0
    }
553
0
  }
554
555
0
  rc = ARES_SUCCESS;
556
0
done:
557
0
  return (int)rc; /* everything went fine */
558
0
}
559
560
void ares_set_local_ip4(ares_channel_t *channel, unsigned int local_ip)
561
0
{
562
0
  if (channel == NULL) {
563
0
    return;
564
0
  }
565
0
  ares_channel_lock(channel);
566
0
  channel->local_ip4 = local_ip;
567
0
  ares_channel_unlock(channel);
568
0
}
569
570
/* local_ip6 should be 16 bytes in length */
571
void ares_set_local_ip6(ares_channel_t *channel, const unsigned char *local_ip6)
572
0
{
573
0
  if (channel == NULL) {
574
0
    return;
575
0
  }
576
0
  ares_channel_lock(channel);
577
0
  memcpy(&channel->local_ip6, local_ip6, sizeof(channel->local_ip6));
578
0
  ares_channel_unlock(channel);
579
0
}
580
581
/* local_dev_name should be null terminated. */
582
void ares_set_local_dev(ares_channel_t *channel, const char *local_dev_name)
583
0
{
584
0
  if (channel == NULL) {
585
0
    return;
586
0
  }
587
588
0
  ares_channel_lock(channel);
589
0
  ares_strcpy(channel->local_dev_name, local_dev_name,
590
0
              sizeof(channel->local_dev_name));
591
0
  channel->local_dev_name[sizeof(channel->local_dev_name) - 1] = 0;
592
0
  ares_channel_unlock(channel);
593
0
}
594
595
int ares_set_sortlist(ares_channel_t *channel, const char *sortstr)
596
0
{
597
0
  size_t           nsort    = 0;
598
0
  struct apattern *sortlist = NULL;
599
0
  ares_status_t    status;
600
601
0
  if (!channel) {
602
0
    return ARES_ENODATA;
603
0
  }
604
0
  ares_channel_lock(channel);
605
606
0
  status = ares_parse_sortlist(&sortlist, &nsort, sortstr);
607
0
  if (status == ARES_SUCCESS && sortlist) {
608
0
    if (channel->sortlist) {
609
0
      ares_free(channel->sortlist);
610
0
    }
611
0
    channel->sortlist = sortlist;
612
0
    channel->nsort    = nsort;
613
614
    /* Save sortlist as if it was passed in as an option */
615
0
    channel->optmask |= ARES_OPT_SORTLIST;
616
0
  }
617
0
  ares_channel_unlock(channel);
618
0
  return (int)status;
619
0
}
620
621
void ares_set_query_enqueue_cb(ares_channel_t       *channel,
622
                               ares_query_enqueue_cb callback, void *user_data)
623
0
{
624
0
  if (channel == NULL) {
625
0
    return;
626
0
  }
627
628
0
  channel->query_enqueue_cb      = callback;
629
0
  channel->query_enqueue_cb_data = user_data;
630
0
}