• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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