ares_options.c (14719B)
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 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 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 */ 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 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 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 }