ares_sysconfig_files.c (24284B)
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 #if defined(ANDROID) || defined(__ANDROID__) 47 # include <sys/system_properties.h> 48 # include "ares_android.h" 49 /* From the Bionic sources */ 50 # define DNS_PROP_NAME_PREFIX "net.dns" 51 # define MAX_DNS_PROPERTIES 8 52 #endif 53 54 #if defined(CARES_USE_LIBRESOLV) 55 # include <resolv.h> 56 #endif 57 58 #if defined(USE_WINSOCK) && defined(HAVE_IPHLPAPI_H) 59 # include <iphlpapi.h> 60 #endif 61 62 #include "ares_inet_net_pton.h" 63 64 static unsigned char ip_natural_mask(const struct ares_addr *addr) 65 { 66 const unsigned char *ptr = NULL; 67 /* This is an odd one. If a raw ipv4 address is specified, then we take 68 * what is called a natural mask, which means we look at the first octet 69 * of the ip address and for values 0-127 we assume it is a class A (/8), 70 * for values 128-191 we assume it is a class B (/16), and for 192-223 71 * we assume it is a class C (/24). 223-239 is Class D which and 240-255 is 72 * Class E, however, there is no pre-defined mask for this, so we'll use 73 * /24 as well as that's what the old code did. 74 * 75 * For IPv6, we'll use /64. 76 */ 77 78 if (addr->family == AF_INET6) { 79 return 64; 80 } 81 82 ptr = (const unsigned char *)&addr->addr.addr4; 83 if (*ptr < 128) { 84 return 8; 85 } 86 87 if (*ptr < 192) { 88 return 16; 89 } 90 91 return 24; 92 } 93 94 static ares_bool_t sortlist_append(struct apattern **sortlist, size_t *nsort, 95 const struct apattern *pat) 96 { 97 struct apattern *newsort; 98 99 newsort = ares_realloc(*sortlist, (*nsort + 1) * sizeof(*newsort)); 100 if (newsort == NULL) { 101 return ARES_FALSE; /* LCOV_EXCL_LINE: OutOfMemory */ 102 } 103 104 *sortlist = newsort; 105 106 memcpy(&(*sortlist)[*nsort], pat, sizeof(**sortlist)); 107 (*nsort)++; 108 109 return ARES_TRUE; 110 } 111 112 static ares_status_t parse_sort(ares_buf_t *buf, struct apattern *pat) 113 { 114 ares_status_t status; 115 const unsigned char ip_charset[] = "ABCDEFabcdef0123456789.:"; 116 char ipaddr[INET6_ADDRSTRLEN] = ""; 117 size_t addrlen; 118 119 memset(pat, 0, sizeof(*pat)); 120 121 /* Consume any leading whitespace */ 122 ares_buf_consume_whitespace(buf, ARES_TRUE); 123 124 /* If no length, just ignore, return ENOTFOUND as an indicator */ 125 if (ares_buf_len(buf) == 0) { 126 return ARES_ENOTFOUND; 127 } 128 129 ares_buf_tag(buf); 130 131 /* Consume ip address */ 132 if (ares_buf_consume_charset(buf, ip_charset, sizeof(ip_charset) - 1) == 0) { 133 return ARES_EBADSTR; 134 } 135 136 /* Fetch ip address */ 137 status = ares_buf_tag_fetch_string(buf, ipaddr, sizeof(ipaddr)); 138 if (status != ARES_SUCCESS) { 139 return status; 140 } 141 142 /* Parse it to make sure its valid */ 143 pat->addr.family = AF_UNSPEC; 144 if (ares_dns_pton(ipaddr, &pat->addr, &addrlen) == NULL) { 145 return ARES_EBADSTR; 146 } 147 148 /* See if there is a subnet mask */ 149 if (ares_buf_begins_with(buf, (const unsigned char *)"/", 1)) { 150 char maskstr[16]; 151 const unsigned char ipv4_charset[] = "0123456789."; 152 153 154 /* Consume / */ 155 ares_buf_consume(buf, 1); 156 157 ares_buf_tag(buf); 158 159 /* Consume mask */ 160 if (ares_buf_consume_charset(buf, ipv4_charset, sizeof(ipv4_charset) - 1) == 161 0) { 162 return ARES_EBADSTR; 163 } 164 165 /* Fetch mask */ 166 status = ares_buf_tag_fetch_string(buf, maskstr, sizeof(maskstr)); 167 if (status != ARES_SUCCESS) { 168 return status; 169 } 170 171 if (ares_str_isnum(maskstr)) { 172 /* Numeric mask */ 173 int mask = atoi(maskstr); 174 if (mask < 0 || mask > 128) { 175 return ARES_EBADSTR; 176 } 177 if (pat->addr.family == AF_INET && mask > 32) { 178 return ARES_EBADSTR; 179 } 180 pat->mask = (unsigned char)mask; 181 } else { 182 /* Ipv4 subnet style mask */ 183 struct ares_addr maskaddr; 184 const unsigned char *ptr; 185 186 memset(&maskaddr, 0, sizeof(maskaddr)); 187 maskaddr.family = AF_INET; 188 if (ares_dns_pton(maskstr, &maskaddr, &addrlen) == NULL) { 189 return ARES_EBADSTR; 190 } 191 ptr = (const unsigned char *)&maskaddr.addr.addr4; 192 pat->mask = (unsigned char)(ares_count_bits_u8(ptr[0]) + 193 ares_count_bits_u8(ptr[1]) + 194 ares_count_bits_u8(ptr[2]) + 195 ares_count_bits_u8(ptr[3])); 196 } 197 } else { 198 pat->mask = ip_natural_mask(&pat->addr); 199 } 200 201 /* Consume any trailing whitespace */ 202 ares_buf_consume_whitespace(buf, ARES_TRUE); 203 204 /* If we have any trailing bytes other than whitespace, its a parse failure */ 205 if (ares_buf_len(buf) != 0) { 206 return ARES_EBADSTR; 207 } 208 209 return ARES_SUCCESS; 210 } 211 212 ares_status_t ares_parse_sortlist(struct apattern **sortlist, size_t *nsort, 213 const char *str) 214 { 215 ares_buf_t *buf = NULL; 216 ares_status_t status = ARES_SUCCESS; 217 ares_array_t *arr = NULL; 218 size_t num = 0; 219 size_t i; 220 221 if (sortlist == NULL || nsort == NULL || str == NULL) { 222 return ARES_EFORMERR; /* LCOV_EXCL_LINE: DefensiveCoding */ 223 } 224 225 if (*sortlist != NULL) { 226 ares_free(*sortlist); 227 } 228 229 *sortlist = NULL; 230 *nsort = 0; 231 232 buf = ares_buf_create_const((const unsigned char *)str, ares_strlen(str)); 233 if (buf == NULL) { 234 status = ARES_ENOMEM; 235 goto done; 236 } 237 238 /* Split on space or semicolon */ 239 status = ares_buf_split(buf, (const unsigned char *)" ;", 2, 240 ARES_BUF_SPLIT_NONE, 0, &arr); 241 if (status != ARES_SUCCESS) { 242 goto done; 243 } 244 245 num = ares_array_len(arr); 246 for (i = 0; i < num; i++) { 247 ares_buf_t **bufptr = ares_array_at(arr, i); 248 ares_buf_t *entry = *bufptr; 249 250 struct apattern pat; 251 252 status = parse_sort(entry, &pat); 253 if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) { 254 goto done; 255 } 256 257 if (status != ARES_SUCCESS) { 258 continue; 259 } 260 261 if (!sortlist_append(sortlist, nsort, &pat)) { 262 status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ 263 goto done; /* LCOV_EXCL_LINE: OutOfMemory */ 264 } 265 } 266 267 status = ARES_SUCCESS; 268 269 done: 270 ares_buf_destroy(buf); 271 ares_array_destroy(arr); 272 273 if (status != ARES_SUCCESS) { 274 ares_free(*sortlist); 275 *sortlist = NULL; 276 *nsort = 0; 277 } 278 279 return status; 280 } 281 282 static ares_status_t config_search(ares_sysconfig_t *sysconfig, const char *str, 283 size_t max_domains) 284 { 285 if (sysconfig->domains && sysconfig->ndomains > 0) { 286 /* if we already have some domains present, free them first */ 287 ares_strsplit_free(sysconfig->domains, sysconfig->ndomains); 288 sysconfig->domains = NULL; 289 sysconfig->ndomains = 0; 290 } 291 292 sysconfig->domains = ares_strsplit(str, ", ", &sysconfig->ndomains); 293 if (sysconfig->domains == NULL) { 294 return ARES_ENOMEM; 295 } 296 297 /* Truncate if necessary */ 298 if (max_domains && sysconfig->ndomains > max_domains) { 299 size_t i; 300 for (i = max_domains; i < sysconfig->ndomains; i++) { 301 ares_free(sysconfig->domains[i]); 302 sysconfig->domains[i] = NULL; 303 } 304 sysconfig->ndomains = max_domains; 305 } 306 307 return ARES_SUCCESS; 308 } 309 310 static ares_status_t buf_fetch_string(ares_buf_t *buf, char *str, 311 size_t str_len) 312 { 313 ares_status_t status; 314 ares_buf_tag(buf); 315 ares_buf_consume(buf, ares_buf_len(buf)); 316 317 status = ares_buf_tag_fetch_string(buf, str, str_len); 318 return status; 319 } 320 321 static ares_status_t config_lookup(ares_sysconfig_t *sysconfig, ares_buf_t *buf, 322 const char *separators) 323 { 324 ares_status_t status; 325 char lookupstr[32]; 326 size_t lookupstr_cnt = 0; 327 char **lookups = NULL; 328 size_t num = 0; 329 size_t i; 330 size_t separators_len = ares_strlen(separators); 331 332 status = 333 ares_buf_split_str(buf, (const unsigned char *)separators, separators_len, 334 ARES_BUF_SPLIT_TRIM, 0, &lookups, &num); 335 if (status != ARES_SUCCESS) { 336 goto done; 337 } 338 339 for (i = 0; i < num; i++) { 340 const char *value = lookups[i]; 341 char ch; 342 343 if (ares_strcaseeq(value, "dns") || ares_strcaseeq(value, "bind") || 344 ares_strcaseeq(value, "resolv") || ares_strcaseeq(value, "resolve")) { 345 ch = 'b'; 346 } else if (ares_strcaseeq(value, "files") || 347 ares_strcaseeq(value, "file") || 348 ares_strcaseeq(value, "local")) { 349 ch = 'f'; 350 } else { 351 continue; 352 } 353 354 /* Look for a duplicate and ignore */ 355 if (memchr(lookupstr, ch, lookupstr_cnt) == NULL) { 356 lookupstr[lookupstr_cnt++] = ch; 357 } 358 } 359 360 if (lookupstr_cnt) { 361 lookupstr[lookupstr_cnt] = 0; 362 ares_free(sysconfig->lookups); 363 sysconfig->lookups = ares_strdup(lookupstr); 364 if (sysconfig->lookups == NULL) { 365 status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ 366 goto done; /* LCOV_EXCL_LINE: OutOfMemory */ 367 } 368 } 369 370 status = ARES_SUCCESS; 371 372 done: 373 if (status != ARES_ENOMEM) { 374 status = ARES_SUCCESS; 375 } 376 ares_free_array(lookups, num, ares_free); 377 return status; 378 } 379 380 static ares_status_t process_option(ares_sysconfig_t *sysconfig, 381 ares_buf_t *option) 382 { 383 char **kv = NULL; 384 size_t num = 0; 385 const char *key; 386 const char *val; 387 unsigned int valint = 0; 388 ares_status_t status; 389 390 /* Split on : */ 391 status = ares_buf_split_str(option, (const unsigned char *)":", 1, 392 ARES_BUF_SPLIT_TRIM, 2, &kv, &num); 393 if (status != ARES_SUCCESS) { 394 goto done; 395 } 396 397 if (num < 1) { 398 status = ARES_EBADSTR; 399 goto done; 400 } 401 402 key = kv[0]; 403 if (num == 2) { 404 val = kv[1]; 405 valint = (unsigned int)strtoul(val, NULL, 10); 406 } 407 408 if (ares_streq(key, "ndots")) { 409 sysconfig->ndots = valint; 410 } else if (ares_streq(key, "retrans") || ares_streq(key, "timeout")) { 411 if (valint == 0) { 412 return ARES_EFORMERR; 413 } 414 sysconfig->timeout_ms = valint * 1000; 415 } else if (ares_streq(key, "retry") || ares_streq(key, "attempts")) { 416 if (valint == 0) { 417 return ARES_EFORMERR; 418 } 419 sysconfig->tries = valint; 420 } else if (ares_streq(key, "rotate")) { 421 sysconfig->rotate = ARES_TRUE; 422 } else if (ares_streq(key, "use-vc") || ares_streq(key, "usevc")) { 423 sysconfig->usevc = ARES_TRUE; 424 } 425 426 done: 427 ares_free_array(kv, num, ares_free); 428 return status; 429 } 430 431 ares_status_t ares_sysconfig_set_options(ares_sysconfig_t *sysconfig, 432 const char *str) 433 { 434 ares_buf_t *buf = NULL; 435 ares_array_t *options = NULL; 436 size_t num; 437 size_t i; 438 ares_status_t status; 439 440 buf = ares_buf_create_const((const unsigned char *)str, ares_strlen(str)); 441 if (buf == NULL) { 442 return ARES_ENOMEM; 443 } 444 445 status = ares_buf_split(buf, (const unsigned char *)" \t", 2, 446 ARES_BUF_SPLIT_TRIM, 0, &options); 447 if (status != ARES_SUCCESS) { 448 goto done; 449 } 450 451 num = ares_array_len(options); 452 for (i = 0; i < num; i++) { 453 ares_buf_t **bufptr = ares_array_at(options, i); 454 ares_buf_t *valbuf = *bufptr; 455 456 status = process_option(sysconfig, valbuf); 457 /* Out of memory is the only fatal condition */ 458 if (status == ARES_ENOMEM) { 459 goto done; /* LCOV_EXCL_LINE: OutOfMemory */ 460 } 461 } 462 463 status = ARES_SUCCESS; 464 465 done: 466 ares_array_destroy(options); 467 ares_buf_destroy(buf); 468 return status; 469 } 470 471 ares_status_t ares_init_by_environment(ares_sysconfig_t *sysconfig) 472 { 473 const char *localdomain; 474 const char *res_options; 475 ares_status_t status; 476 477 localdomain = getenv("LOCALDOMAIN"); 478 if (localdomain) { 479 char *temp = ares_strdup(localdomain); 480 if (temp == NULL) { 481 return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ 482 } 483 status = config_search(sysconfig, temp, 1); 484 ares_free(temp); 485 if (status != ARES_SUCCESS) { 486 return status; 487 } 488 } 489 490 res_options = getenv("RES_OPTIONS"); 491 if (res_options) { 492 status = ares_sysconfig_set_options(sysconfig, res_options); 493 if (status != ARES_SUCCESS) { 494 return status; 495 } 496 } 497 498 return ARES_SUCCESS; 499 } 500 501 /* Configuration Files: 502 * /etc/resolv.conf 503 * - All Unix-like systems 504 * - Comments start with ; or # 505 * - Lines have a keyword followed by a value that is interpreted specific 506 * to the keyword: 507 * - Keywords: 508 * - nameserver - IP address of nameserver with optional port (using a : 509 * prefix). If using an ipv6 address and specifying a port, the ipv6 510 * address must be encapsulated in brackets. For link-local ipv6 511 * addresses, the interface can also be specified with a % prefix. e.g.: 512 * "nameserver [fe80::1]:1234%iface" 513 * This keyword may be specified multiple times. 514 * - search - whitespace separated list of domains 515 * - domain - obsolete, same as search except only a single domain 516 * - lookup / hostresorder - local, bind, file, files 517 * - sortlist - whitespace separated ip-address/netmask pairs 518 * - options - options controlling resolver variables 519 * - ndots:n - set ndots option 520 * - timeout:n (retrans:n) - timeout per query attempt in seconds 521 * - attempts:n (retry:n) - number of times resolver will send query 522 * - rotate - round-robin selection of name servers 523 * - use-vc / usevc - force tcp 524 * /etc/nsswitch.conf 525 * - Modern Linux, FreeBSD, HP-UX, Solaris 526 * - Search order set via: 527 * "hosts: files dns mdns4_minimal mdns4" 528 * - files is /etc/hosts 529 * - dns is dns 530 * - mdns4_minimal does mdns only if ending in .local 531 * - mdns4 does not limit to domains ending in .local 532 * /etc/netsvc.conf 533 * - AIX 534 * - Search order set via: 535 * "hosts = local , bind" 536 * - bind is dns 537 * - local is /etc/hosts 538 * /etc/svc.conf 539 * - Tru64 540 * - Same format as /etc/netsvc.conf 541 * /etc/host.conf 542 * - Early FreeBSD, Early Linux 543 * - Not worth supporting, format varied based on system, FreeBSD used 544 * just a line per search order, Linux used "order " and a comma 545 * delimited list of "bind" and "hosts" 546 */ 547 548 549 /* This function will only return ARES_SUCCESS or ARES_ENOMEM. Any other 550 * conditions are ignored. Users may mess up config files, but we want to 551 * process anything we can. */ 552 ares_status_t ares_sysconfig_parse_resolv_line(const ares_channel_t *channel, 553 ares_sysconfig_t *sysconfig, 554 ares_buf_t *line) 555 { 556 char option[32]; 557 char value[512]; 558 ares_status_t status = ARES_SUCCESS; 559 560 /* Ignore lines beginning with a comment */ 561 if (ares_buf_begins_with(line, (const unsigned char *)"#", 1) || 562 ares_buf_begins_with(line, (const unsigned char *)";", 1)) { 563 return ARES_SUCCESS; 564 } 565 566 ares_buf_tag(line); 567 568 /* Shouldn't be possible, but if it happens, ignore the line. */ 569 if (ares_buf_consume_nonwhitespace(line) == 0) { 570 return ARES_SUCCESS; 571 } 572 573 status = ares_buf_tag_fetch_string(line, option, sizeof(option)); 574 if (status != ARES_SUCCESS) { 575 return ARES_SUCCESS; 576 } 577 578 ares_buf_consume_whitespace(line, ARES_TRUE); 579 580 status = buf_fetch_string(line, value, sizeof(value)); 581 if (status != ARES_SUCCESS) { 582 return ARES_SUCCESS; 583 } 584 585 ares_str_trim(value); 586 if (*value == 0) { 587 return ARES_SUCCESS; 588 } 589 590 /* At this point we have a string option and a string value, both trimmed 591 * of leading and trailing whitespace. Lets try to evaluate them */ 592 if (ares_streq(option, "domain")) { 593 /* Domain is legacy, don't overwrite an existing config set by search */ 594 if (sysconfig->domains == NULL) { 595 status = config_search(sysconfig, value, 1); 596 } 597 } else if (ares_streq(option, "lookup") || 598 ares_streq(option, "hostresorder")) { 599 ares_buf_tag_rollback(line); 600 status = config_lookup(sysconfig, line, " \t"); 601 } else if (ares_streq(option, "search")) { 602 status = config_search(sysconfig, value, 0); 603 } else if (ares_streq(option, "nameserver")) { 604 status = ares_sconfig_append_fromstr(channel, &sysconfig->sconfig, value, 605 ARES_TRUE); 606 } else if (ares_streq(option, "sortlist")) { 607 /* Ignore all failures except ENOMEM. If the sysadmin set a bad 608 * sortlist, just ignore the sortlist, don't cause an inoperable 609 * channel */ 610 status = 611 ares_parse_sortlist(&sysconfig->sortlist, &sysconfig->nsortlist, value); 612 if (status != ARES_ENOMEM) { 613 status = ARES_SUCCESS; 614 } 615 } else if (ares_streq(option, "options")) { 616 status = ares_sysconfig_set_options(sysconfig, value); 617 } 618 619 return status; 620 } 621 622 /* This function will only return ARES_SUCCESS or ARES_ENOMEM. Any other 623 * conditions are ignored. Users may mess up config files, but we want to 624 * process anything we can. */ 625 static ares_status_t parse_nsswitch_line(const ares_channel_t *channel, 626 ares_sysconfig_t *sysconfig, 627 ares_buf_t *line) 628 { 629 char option[32]; 630 ares_status_t status = ARES_SUCCESS; 631 ares_array_t *sects = NULL; 632 ares_buf_t **bufptr; 633 ares_buf_t *buf; 634 635 (void)channel; 636 637 /* Ignore lines beginning with a comment */ 638 if (ares_buf_begins_with(line, (const unsigned char *)"#", 1)) { 639 return ARES_SUCCESS; 640 } 641 642 /* database : values (space delimited) */ 643 status = ares_buf_split(line, (const unsigned char *)":", 1, 644 ARES_BUF_SPLIT_TRIM, 2, §s); 645 646 if (status != ARES_SUCCESS || ares_array_len(sects) != 2) { 647 goto done; 648 } 649 650 bufptr = ares_array_at(sects, 0); 651 buf = *bufptr; 652 653 status = buf_fetch_string(buf, option, sizeof(option)); 654 if (status != ARES_SUCCESS) { 655 goto done; 656 } 657 658 /* Only support "hosts:" */ 659 if (!ares_streq(option, "hosts")) { 660 goto done; 661 } 662 663 /* Values are space separated */ 664 bufptr = ares_array_at(sects, 1); 665 buf = *bufptr; 666 status = config_lookup(sysconfig, buf, " \t"); 667 668 done: 669 ares_array_destroy(sects); 670 if (status != ARES_ENOMEM) { 671 status = ARES_SUCCESS; 672 } 673 return status; 674 } 675 676 /* This function will only return ARES_SUCCESS or ARES_ENOMEM. Any other 677 * conditions are ignored. Users may mess up config files, but we want to 678 * process anything we can. */ 679 static ares_status_t parse_svcconf_line(const ares_channel_t *channel, 680 ares_sysconfig_t *sysconfig, 681 ares_buf_t *line) 682 { 683 char option[32]; 684 ares_buf_t **bufptr; 685 ares_buf_t *buf; 686 ares_status_t status = ARES_SUCCESS; 687 ares_array_t *sects = NULL; 688 689 (void)channel; 690 691 /* Ignore lines beginning with a comment */ 692 if (ares_buf_begins_with(line, (const unsigned char *)"#", 1)) { 693 return ARES_SUCCESS; 694 } 695 696 /* database = values (comma delimited)*/ 697 status = ares_buf_split(line, (const unsigned char *)"=", 1, 698 ARES_BUF_SPLIT_TRIM, 2, §s); 699 700 if (status != ARES_SUCCESS || ares_array_len(sects) != 2) { 701 goto done; 702 } 703 704 bufptr = ares_array_at(sects, 0); 705 buf = *bufptr; 706 status = buf_fetch_string(buf, option, sizeof(option)); 707 if (status != ARES_SUCCESS) { 708 goto done; 709 } 710 711 /* Only support "hosts=" */ 712 if (!ares_streq(option, "hosts")) { 713 goto done; 714 } 715 716 /* Values are comma separated */ 717 bufptr = ares_array_at(sects, 1); 718 buf = *bufptr; 719 status = config_lookup(sysconfig, buf, ","); 720 721 done: 722 ares_array_destroy(sects); 723 if (status != ARES_ENOMEM) { 724 status = ARES_SUCCESS; 725 } 726 return status; 727 } 728 729 730 ares_status_t ares_sysconfig_process_buf(const ares_channel_t *channel, 731 ares_sysconfig_t *sysconfig, 732 ares_buf_t *buf, 733 ares_sysconfig_line_cb_t cb) 734 { 735 ares_array_t *lines = NULL; 736 size_t num; 737 size_t i; 738 ares_status_t status; 739 740 status = ares_buf_split(buf, (const unsigned char *)"\n", 1, 741 ARES_BUF_SPLIT_TRIM, 0, &lines); 742 if (status != ARES_SUCCESS) { 743 goto done; 744 } 745 746 num = ares_array_len(lines); 747 for (i = 0; i < num; i++) { 748 ares_buf_t **bufptr = ares_array_at(lines, i); 749 ares_buf_t *line = *bufptr; 750 751 status = cb(channel, sysconfig, line); 752 if (status != ARES_SUCCESS) { 753 goto done; 754 } 755 } 756 757 done: 758 ares_array_destroy(lines); 759 return status; 760 } 761 762 /* Should only return: 763 * ARES_ENOTFOUND - file not found 764 * ARES_EFILE - error reading file (perms) 765 * ARES_ENOMEM - out of memory 766 * ARES_SUCCESS - file processed, doesn't necessarily mean it was a good 767 * file, but we're not erroring out if we can't parse 768 * something (or anything at all) */ 769 static ares_status_t process_config_lines(const ares_channel_t *channel, 770 const char *filename, 771 ares_sysconfig_t *sysconfig, 772 ares_sysconfig_line_cb_t cb) 773 { 774 ares_status_t status = ARES_SUCCESS; 775 ares_buf_t *buf = NULL; 776 777 buf = ares_buf_create(); 778 if (buf == NULL) { 779 status = ARES_ENOMEM; 780 goto done; 781 } 782 783 status = ares_buf_load_file(filename, buf); 784 if (status != ARES_SUCCESS) { 785 goto done; 786 } 787 788 status = ares_sysconfig_process_buf(channel, sysconfig, buf, cb); 789 790 done: 791 ares_buf_destroy(buf); 792 793 return status; 794 } 795 796 ares_status_t ares_init_sysconfig_files(const ares_channel_t *channel, 797 ares_sysconfig_t *sysconfig, 798 ares_bool_t process_resolvconf) 799 { 800 ares_status_t status = ARES_SUCCESS; 801 802 /* Resolv.conf */ 803 if (process_resolvconf) { 804 status = process_config_lines(channel, 805 (channel->resolvconf_path != NULL) 806 ? channel->resolvconf_path 807 : PATH_RESOLV_CONF, 808 sysconfig, ares_sysconfig_parse_resolv_line); 809 if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) { 810 goto done; 811 } 812 } 813 814 /* Nsswitch.conf */ 815 status = process_config_lines(channel, "/etc/nsswitch.conf", sysconfig, 816 parse_nsswitch_line); 817 if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) { 818 goto done; 819 } 820 821 /* netsvc.conf */ 822 status = process_config_lines(channel, "/etc/netsvc.conf", sysconfig, 823 parse_svcconf_line); 824 if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) { 825 goto done; 826 } 827 828 /* svc.conf */ 829 status = process_config_lines(channel, "/etc/svc.conf", sysconfig, 830 parse_svcconf_line); 831 if (status != ARES_SUCCESS && status != ARES_ENOTFOUND) { 832 goto done; 833 } 834 835 status = ARES_SUCCESS; 836 837 done: 838 return status; 839 }