1 /* MIT License
2 *
3 * Copyright (c) 1998 Massachusetts Institute of Technology
4 * Copyright (c) 2008 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_ARPA_INET_H
31 # include <arpa/inet.h>
32 #endif
33
34 #include "ares_data.h"
35 #include "ares_inet_net_pton.h"
36
ares_destroy_options(struct ares_options * options)37 void ares_destroy_options(struct ares_options *options)
38 {
39 int i;
40
41 ares_free(options->servers);
42
43 for (i = 0; options->domains && i < options->ndomains; i++) {
44 ares_free(options->domains[i]);
45 }
46
47 ares_free(options->domains);
48 ares_free(options->sortlist);
49 ares_free(options->lookups);
50 ares_free(options->resolvconf_path);
51 ares_free(options->hosts_path);
52 }
53
ares_save_opt_servers(const ares_channel_t * channel,int * nservers)54 static struct in_addr *ares_save_opt_servers(const ares_channel_t *channel,
55 int *nservers)
56 {
57 ares_slist_node_t *snode;
58 struct in_addr *out =
59 ares_malloc_zero(ares_slist_len(channel->servers) * sizeof(*out));
60
61 *nservers = 0;
62
63 if (out == NULL) {
64 return NULL;
65 }
66
67 for (snode = ares_slist_node_first(channel->servers); snode != NULL;
68 snode = ares_slist_node_next(snode)) {
69 const ares_server_t *server = ares_slist_node_val(snode);
70
71 if (server->addr.family != AF_INET) {
72 continue;
73 }
74
75 memcpy(&out[*nservers], &server->addr.addr.addr4, sizeof(*out));
76 (*nservers)++;
77 }
78
79 return out;
80 }
81
82 /* Save options from initialized channel */
ares_save_options(const ares_channel_t * channel,struct ares_options * options,int * optmask)83 int ares_save_options(const ares_channel_t *channel,
84 struct ares_options *options, int *optmask)
85 {
86 size_t i;
87
88 /* NOTE: We can't zero the whole thing out, this is because the size of the
89 * struct ares_options changes over time, so if someone compiled
90 * with an older version, their struct size might be smaller and
91 * we might overwrite their memory! So using the optmask is critical
92 * here, as they could have only set options they knew about.
93 *
94 * Unfortunately ares_destroy_options() doesn't take an optmask, so
95 * there are a few pointers we *must* zero out otherwise we won't
96 * know if they were allocated or not
97 */
98 options->servers = NULL;
99 options->domains = NULL;
100 options->sortlist = NULL;
101 options->lookups = NULL;
102 options->resolvconf_path = NULL;
103 options->hosts_path = NULL;
104
105 if (!ARES_CONFIG_CHECK(channel)) {
106 return ARES_ENODATA;
107 }
108
109 if (channel->optmask & ARES_OPT_FLAGS) {
110 options->flags = (int)channel->flags;
111 }
112
113 /* We convert ARES_OPT_TIMEOUT to ARES_OPT_TIMEOUTMS in
114 * ares_init_by_options() */
115 if (channel->optmask & ARES_OPT_TIMEOUTMS) {
116 options->timeout = (int)channel->timeout;
117 }
118
119 if (channel->optmask & ARES_OPT_TRIES) {
120 options->tries = (int)channel->tries;
121 }
122
123 if (channel->optmask & ARES_OPT_NDOTS) {
124 options->ndots = (int)channel->ndots;
125 }
126
127 if (channel->optmask & ARES_OPT_MAXTIMEOUTMS) {
128 options->maxtimeout = (int)channel->maxtimeout;
129 }
130
131 if (channel->optmask & ARES_OPT_UDP_PORT) {
132 options->udp_port = channel->udp_port;
133 }
134 if (channel->optmask & ARES_OPT_TCP_PORT) {
135 options->tcp_port = channel->tcp_port;
136 }
137
138 if (channel->optmask & ARES_OPT_SOCK_STATE_CB) {
139 options->sock_state_cb = channel->sock_state_cb;
140 options->sock_state_cb_data = channel->sock_state_cb_data;
141 }
142
143 if (channel->optmask & ARES_OPT_SERVERS) {
144 options->servers = ares_save_opt_servers(channel, &options->nservers);
145 if (options->servers == NULL) {
146 return ARES_ENOMEM;
147 }
148 }
149
150 if (channel->optmask & ARES_OPT_DOMAINS) {
151 options->domains = NULL;
152 if (channel->ndomains) {
153 options->domains = ares_malloc(channel->ndomains * sizeof(char *));
154 if (!options->domains) {
155 return ARES_ENOMEM;
156 }
157
158 for (i = 0; i < channel->ndomains; i++) {
159 options->domains[i] = ares_strdup(channel->domains[i]);
160 if (!options->domains[i]) {
161 options->ndomains = (int)i;
162 return ARES_ENOMEM;
163 }
164 }
165 }
166 options->ndomains = (int)channel->ndomains;
167 }
168
169 if (channel->optmask & ARES_OPT_LOOKUPS) {
170 options->lookups = ares_strdup(channel->lookups);
171 if (!options->lookups && channel->lookups) {
172 return ARES_ENOMEM;
173 }
174 }
175
176 if (channel->optmask & ARES_OPT_SORTLIST) {
177 options->sortlist = NULL;
178 if (channel->nsort) {
179 options->sortlist = ares_malloc(channel->nsort * sizeof(struct apattern));
180 if (!options->sortlist) {
181 return ARES_ENOMEM;
182 }
183 for (i = 0; i < channel->nsort; i++) {
184 options->sortlist[i] = channel->sortlist[i];
185 }
186 }
187 options->nsort = (int)channel->nsort;
188 }
189
190 if (channel->optmask & ARES_OPT_RESOLVCONF) {
191 options->resolvconf_path = ares_strdup(channel->resolvconf_path);
192 if (!options->resolvconf_path) {
193 return ARES_ENOMEM;
194 }
195 }
196
197 if (channel->optmask & ARES_OPT_HOSTS_FILE) {
198 options->hosts_path = ares_strdup(channel->hosts_path);
199 if (!options->hosts_path) {
200 return ARES_ENOMEM;
201 }
202 }
203
204 if (channel->optmask & ARES_OPT_SOCK_SNDBUF &&
205 channel->socket_send_buffer_size > 0) {
206 options->socket_send_buffer_size = channel->socket_send_buffer_size;
207 }
208
209 if (channel->optmask & ARES_OPT_SOCK_RCVBUF &&
210 channel->socket_receive_buffer_size > 0) {
211 options->socket_receive_buffer_size = channel->socket_receive_buffer_size;
212 }
213
214 if (channel->optmask & ARES_OPT_EDNSPSZ) {
215 options->ednspsz = (int)channel->ednspsz;
216 }
217
218 if (channel->optmask & ARES_OPT_UDP_MAX_QUERIES) {
219 options->udp_max_queries = (int)channel->udp_max_queries;
220 }
221
222 if (channel->optmask & ARES_OPT_QUERY_CACHE) {
223 options->qcache_max_ttl = channel->qcache_max_ttl;
224 }
225
226 if (channel->optmask & ARES_OPT_EVENT_THREAD) {
227 options->evsys = channel->evsys;
228 }
229
230 /* Set options for server failover behavior */
231 if (channel->optmask & ARES_OPT_SERVER_FAILOVER) {
232 options->server_failover_opts.retry_chance = channel->server_retry_chance;
233 options->server_failover_opts.retry_delay = channel->server_retry_delay;
234 }
235
236 *optmask = (int)channel->optmask;
237
238 return ARES_SUCCESS;
239 }
240
ares_init_options_servers(ares_channel_t * channel,const struct in_addr * servers,size_t nservers)241 static ares_status_t ares_init_options_servers(ares_channel_t *channel,
242 const struct in_addr *servers,
243 size_t nservers)
244 {
245 ares_llist_t *slist = NULL;
246 ares_status_t status;
247
248 status = ares_in_addr_to_sconfig_llist(servers, nservers, &slist);
249 if (status != ARES_SUCCESS) {
250 return status; /* LCOV_EXCL_LINE: OutOfMemory */
251 }
252
253 status = ares_servers_update(channel, slist, ARES_TRUE);
254
255 ares_llist_destroy(slist);
256
257 return status;
258 }
259
ares_init_by_options(ares_channel_t * channel,const struct ares_options * options,int optmask)260 ares_status_t ares_init_by_options(ares_channel_t *channel,
261 const struct ares_options *options,
262 int optmask)
263 {
264 size_t i;
265
266 if (channel == NULL) {
267 return ARES_ENODATA; /* LCOV_EXCL_LINE: DefensiveCoding */
268 }
269
270 if (options == NULL) {
271 if (optmask != 0) {
272 return ARES_ENODATA; /* LCOV_EXCL_LINE: DefensiveCoding */
273 }
274 return ARES_SUCCESS;
275 }
276
277 /* Easy stuff. */
278
279 /* Event Thread requires threading support and is incompatible with socket
280 * state callbacks */
281 if (optmask & ARES_OPT_EVENT_THREAD) {
282 if (!ares_threadsafety()) {
283 return ARES_ENOTIMP;
284 }
285 if (optmask & ARES_OPT_SOCK_STATE_CB) {
286 return ARES_EFORMERR;
287 }
288 channel->evsys = options->evsys;
289 }
290
291 if (optmask & ARES_OPT_FLAGS) {
292 channel->flags = (unsigned int)options->flags;
293 }
294
295 if (optmask & ARES_OPT_TIMEOUTMS) {
296 /* Apparently some integrations were passing -1 to tell c-ares to use
297 * the default instead of just omitting the optmask */
298 if (options->timeout <= 0) {
299 optmask &= ~(ARES_OPT_TIMEOUTMS);
300 } else {
301 channel->timeout = (unsigned int)options->timeout;
302 }
303 } else if (optmask & ARES_OPT_TIMEOUT) {
304 optmask &= ~(ARES_OPT_TIMEOUT);
305 /* Apparently some integrations were passing -1 to tell c-ares to use
306 * the default instead of just omitting the optmask */
307 if (options->timeout > 0) {
308 /* Convert to milliseconds */
309 optmask |= ARES_OPT_TIMEOUTMS;
310 channel->timeout = (unsigned int)options->timeout * 1000;
311 }
312 }
313
314 if (optmask & ARES_OPT_TRIES) {
315 if (options->tries <= 0) {
316 optmask &= ~(ARES_OPT_TRIES);
317 } else {
318 channel->tries = (size_t)options->tries;
319 }
320 }
321
322 if (optmask & ARES_OPT_NDOTS) {
323 if (options->ndots < 0) {
324 optmask &= ~(ARES_OPT_NDOTS);
325 } else {
326 channel->ndots = (size_t)options->ndots;
327 }
328 }
329
330 if (optmask & ARES_OPT_MAXTIMEOUTMS) {
331 if (options->maxtimeout <= 0) {
332 optmask &= ~(ARES_OPT_MAXTIMEOUTMS);
333 } else {
334 channel->maxtimeout = (size_t)options->maxtimeout;
335 }
336 }
337
338 if (optmask & ARES_OPT_ROTATE) {
339 channel->rotate = ARES_TRUE;
340 }
341
342 if (optmask & ARES_OPT_NOROTATE) {
343 channel->rotate = ARES_FALSE;
344 }
345
346 if (optmask & ARES_OPT_UDP_PORT) {
347 channel->udp_port = options->udp_port;
348 }
349
350 if (optmask & ARES_OPT_TCP_PORT) {
351 channel->tcp_port = options->tcp_port;
352 }
353
354 if (optmask & ARES_OPT_SOCK_STATE_CB) {
355 channel->sock_state_cb = options->sock_state_cb;
356 channel->sock_state_cb_data = options->sock_state_cb_data;
357 }
358
359 if (optmask & ARES_OPT_SOCK_SNDBUF) {
360 if (options->socket_send_buffer_size <= 0) {
361 optmask &= ~(ARES_OPT_SOCK_SNDBUF);
362 } else {
363 channel->socket_send_buffer_size = options->socket_send_buffer_size;
364 }
365 }
366
367 if (optmask & ARES_OPT_SOCK_RCVBUF) {
368 if (options->socket_receive_buffer_size <= 0) {
369 optmask &= ~(ARES_OPT_SOCK_RCVBUF);
370 } else {
371 channel->socket_receive_buffer_size = options->socket_receive_buffer_size;
372 }
373 }
374
375 if (optmask & ARES_OPT_EDNSPSZ) {
376 if (options->ednspsz <= 0) {
377 optmask &= ~(ARES_OPT_EDNSPSZ);
378 } else {
379 channel->ednspsz = (size_t)options->ednspsz;
380 }
381 }
382
383 /* Copy the domains, if given. Keep channel->ndomains consistent so
384 * we can clean up in case of error.
385 */
386 if (optmask & ARES_OPT_DOMAINS && options->ndomains > 0) {
387 channel->domains =
388 ares_malloc_zero((size_t)options->ndomains * sizeof(char *));
389 if (!channel->domains) {
390 return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
391 }
392 channel->ndomains = (size_t)options->ndomains;
393 for (i = 0; i < (size_t)options->ndomains; i++) {
394 channel->domains[i] = ares_strdup(options->domains[i]);
395 if (!channel->domains[i]) {
396 return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
397 }
398 }
399 }
400
401 /* Set lookups, if given. */
402 if (optmask & ARES_OPT_LOOKUPS) {
403 if (options->lookups == NULL) {
404 optmask &= ~(ARES_OPT_LOOKUPS);
405 } else {
406 channel->lookups = ares_strdup(options->lookups);
407 if (!channel->lookups) {
408 return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
409 }
410 }
411 }
412
413 /* copy sortlist */
414 if (optmask & ARES_OPT_SORTLIST && options->nsort > 0) {
415 channel->nsort = (size_t)options->nsort;
416 channel->sortlist =
417 ares_malloc((size_t)options->nsort * sizeof(struct apattern));
418 if (!channel->sortlist) {
419 return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
420 }
421 for (i = 0; i < (size_t)options->nsort; i++) {
422 channel->sortlist[i] = options->sortlist[i];
423 }
424 }
425
426 /* Set path for resolv.conf file, if given. */
427 if (optmask & ARES_OPT_RESOLVCONF) {
428 if (options->resolvconf_path == NULL) {
429 optmask &= ~(ARES_OPT_RESOLVCONF);
430 } else {
431 channel->resolvconf_path = ares_strdup(options->resolvconf_path);
432 if (channel->resolvconf_path == NULL) {
433 return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
434 }
435 }
436 }
437
438 /* Set path for hosts file, if given. */
439 if (optmask & ARES_OPT_HOSTS_FILE) {
440 if (options->hosts_path == NULL) {
441 optmask &= ~(ARES_OPT_HOSTS_FILE);
442 } else {
443 channel->hosts_path = ares_strdup(options->hosts_path);
444 if (channel->hosts_path == NULL) {
445 return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */
446 }
447 }
448 }
449
450 if (optmask & ARES_OPT_UDP_MAX_QUERIES) {
451 if (options->udp_max_queries <= 0) {
452 optmask &= ~(ARES_OPT_UDP_MAX_QUERIES);
453 } else {
454 channel->udp_max_queries = (size_t)options->udp_max_queries;
455 }
456 }
457
458 /* As of c-ares 1.31.0, the Query Cache is on by default. The only way to
459 * disable it is to set options->qcache_max_ttl = 0 while specifying the
460 * ARES_OPT_QUERY_CACHE which will actually disable it completely. */
461 if (optmask & ARES_OPT_QUERY_CACHE) {
462 /* qcache_max_ttl is unsigned unlike the others */
463 channel->qcache_max_ttl = options->qcache_max_ttl;
464 } else {
465 optmask |= ARES_OPT_QUERY_CACHE;
466 channel->qcache_max_ttl = 3600;
467 }
468
469 /* Initialize the ipv4 servers if provided */
470 if (optmask & ARES_OPT_SERVERS) {
471 if (options->nservers <= 0) {
472 optmask &= ~(ARES_OPT_SERVERS);
473 } else {
474 ares_status_t status;
475 status = ares_init_options_servers(channel, options->servers,
476 (size_t)options->nservers);
477 if (status != ARES_SUCCESS) {
478 return status; /* LCOV_EXCL_LINE: OutOfMemory */
479 }
480 }
481 }
482
483 /* Set fields for server failover behavior */
484 if (optmask & ARES_OPT_SERVER_FAILOVER) {
485 channel->server_retry_chance = options->server_failover_opts.retry_chance;
486 channel->server_retry_delay = options->server_failover_opts.retry_delay;
487 }
488
489 channel->optmask = (unsigned int)optmask;
490
491 return ARES_SUCCESS;
492 }
493