1*bcda20f6Schristos /* $NetBSD: resconf.c,v 1.2 2025/01/26 16:25:24 christos Exp $ */ 29689912eSchristos 39689912eSchristos /* 49689912eSchristos * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 59689912eSchristos * 69689912eSchristos * SPDX-License-Identifier: MPL-2.0 79689912eSchristos * 89689912eSchristos * This Source Code Form is subject to the terms of the Mozilla Public 99689912eSchristos * License, v. 2.0. If a copy of the MPL was not distributed with this 109689912eSchristos * file, you can obtain one at https://mozilla.org/MPL/2.0/. 119689912eSchristos * 129689912eSchristos * See the COPYRIGHT file distributed with this work for additional 139689912eSchristos * information regarding copyright ownership. 149689912eSchristos */ 159689912eSchristos 169689912eSchristos /*! \file resconf.c */ 179689912eSchristos 189689912eSchristos /** 199689912eSchristos * Module for parsing resolv.conf files (largely derived from lwconfig.c). 209689912eSchristos * 219689912eSchristos * irs_resconf_load() opens the file filename and parses it to initialize 229689912eSchristos * the configuration structure. 239689912eSchristos * 249689912eSchristos * \section lwconfig_return Return Values 259689912eSchristos * 269689912eSchristos * irs_resconf_load() returns #IRS_R_SUCCESS if it successfully read and 279689912eSchristos * parsed filename. It returns a non-0 error code if filename could not be 289689912eSchristos * opened or contained incorrect resolver statements. 299689912eSchristos * 309689912eSchristos * \section lwconfig_see See Also 319689912eSchristos * 329689912eSchristos * stdio(3), \link resolver resolver \endlink 339689912eSchristos * 349689912eSchristos * \section files Files 359689912eSchristos * 369689912eSchristos * /etc/resolv.conf 379689912eSchristos */ 389689912eSchristos 399689912eSchristos #include <ctype.h> 409689912eSchristos #include <errno.h> 419689912eSchristos #include <inttypes.h> 429689912eSchristos #include <netdb.h> 439689912eSchristos #include <stdio.h> 449689912eSchristos #include <stdlib.h> 459689912eSchristos #include <string.h> 469689912eSchristos #include <sys/socket.h> 479689912eSchristos #include <sys/types.h> 489689912eSchristos 499689912eSchristos #include <isc/magic.h> 509689912eSchristos #include <isc/mem.h> 519689912eSchristos #include <isc/netaddr.h> 529689912eSchristos #include <isc/sockaddr.h> 539689912eSchristos #include <isc/util.h> 549689912eSchristos 559689912eSchristos #include <irs/resconf.h> 569689912eSchristos 579689912eSchristos #define IRS_RESCONF_MAGIC ISC_MAGIC('R', 'E', 'S', 'c') 589689912eSchristos #define IRS_RESCONF_VALID(c) ISC_MAGIC_VALID(c, IRS_RESCONF_MAGIC) 599689912eSchristos 609689912eSchristos /*! 619689912eSchristos * protocol constants 629689912eSchristos */ 639689912eSchristos 649689912eSchristos #if !defined(NS_INADDRSZ) 659689912eSchristos #define NS_INADDRSZ 4 669689912eSchristos #endif /* if !defined(NS_INADDRSZ) */ 679689912eSchristos 689689912eSchristos #if !defined(NS_IN6ADDRSZ) 699689912eSchristos #define NS_IN6ADDRSZ 16 709689912eSchristos #endif /* if !defined(NS_IN6ADDRSZ) */ 719689912eSchristos 729689912eSchristos /*! 739689912eSchristos * resolv.conf parameters 749689912eSchristos */ 759689912eSchristos 769689912eSchristos #define RESCONFMAXNAMESERVERS 3U /*%< max 3 "nameserver" entries */ 779689912eSchristos #define RESCONFMAXSEARCH 8U /*%< max 8 domains in "search" entry */ 789689912eSchristos #define RESCONFMAXLINELEN 256U /*%< max size of a line */ 799689912eSchristos #define RESCONFMAXSORTLIST 10U /*%< max 10 */ 809689912eSchristos 819689912eSchristos #define CHECK(op) \ 829689912eSchristos do { \ 839689912eSchristos result = (op); \ 849689912eSchristos if (result != ISC_R_SUCCESS) \ 859689912eSchristos goto cleanup; \ 869689912eSchristos } while (0) 879689912eSchristos 889689912eSchristos /*! 899689912eSchristos * configuration data structure 909689912eSchristos */ 919689912eSchristos 929689912eSchristos struct irs_resconf { 939689912eSchristos /* 949689912eSchristos * The configuration data is a thread-specific object, and does not 959689912eSchristos * need to be locked. 969689912eSchristos */ 979689912eSchristos unsigned int magic; 989689912eSchristos isc_mem_t *mctx; 999689912eSchristos 1009689912eSchristos isc_sockaddrlist_t nameservers; 1019689912eSchristos unsigned int numns; /*%< number of configured servers */ 1029689912eSchristos 1039689912eSchristos char *domainname; 1049689912eSchristos char *search[RESCONFMAXSEARCH]; 1059689912eSchristos uint8_t searchnxt; /*%< index for next free slot */ 1069689912eSchristos 1079689912eSchristos irs_resconf_searchlist_t searchlist; 1089689912eSchristos 1099689912eSchristos struct { 1109689912eSchristos isc_netaddr_t addr; 1119689912eSchristos /*% mask has a non-zero 'family' if set */ 1129689912eSchristos isc_netaddr_t mask; 1139689912eSchristos } sortlist[RESCONFMAXSORTLIST]; 1149689912eSchristos uint8_t sortlistnxt; 1159689912eSchristos 1169689912eSchristos /*%< non-zero if 'options debug' set */ 1179689912eSchristos uint8_t resdebug; 1189689912eSchristos /*%< set to n in 'options ndots:n' */ 1199689912eSchristos uint8_t ndots; 1209689912eSchristos /*%< set to n in 'options attempts:n' */ 1219689912eSchristos uint8_t attempts; 1229689912eSchristos /*%< set to n in 'options timeout:n' */ 1239689912eSchristos uint8_t timeout; 1249689912eSchristos }; 1259689912eSchristos 1269689912eSchristos static isc_result_t 1279689912eSchristos resconf_parsenameserver(irs_resconf_t *conf, FILE *fp); 1289689912eSchristos static isc_result_t 1299689912eSchristos resconf_parsedomain(irs_resconf_t *conf, FILE *fp); 1309689912eSchristos static isc_result_t 1319689912eSchristos resconf_parsesearch(irs_resconf_t *conf, FILE *fp); 1329689912eSchristos static isc_result_t 1339689912eSchristos resconf_parsesortlist(irs_resconf_t *conf, FILE *fp); 1349689912eSchristos static isc_result_t 1359689912eSchristos resconf_parseoption(irs_resconf_t *ctx, FILE *fp); 1369689912eSchristos 1379689912eSchristos /*! 1389689912eSchristos * Eat characters from FP until EOL or EOF. Returns EOF or '\n' 1399689912eSchristos */ 1409689912eSchristos static int 1419689912eSchristos eatline(FILE *fp) { 1429689912eSchristos int ch; 1439689912eSchristos 1449689912eSchristos ch = fgetc(fp); 1459689912eSchristos while (ch != '\n' && ch != EOF) { 1469689912eSchristos ch = fgetc(fp); 1479689912eSchristos } 1489689912eSchristos 1499689912eSchristos return ch; 1509689912eSchristos } 1519689912eSchristos 1529689912eSchristos /*! 1539689912eSchristos * Eats white space up to next newline or non-whitespace character (of 1549689912eSchristos * EOF). Returns the last character read. Comments are considered white 1559689912eSchristos * space. 1569689912eSchristos */ 1579689912eSchristos static int 1589689912eSchristos eatwhite(FILE *fp) { 1599689912eSchristos int ch; 1609689912eSchristos 1619689912eSchristos ch = fgetc(fp); 1629689912eSchristos while (ch != '\n' && ch != EOF && isspace((unsigned char)ch)) { 1639689912eSchristos ch = fgetc(fp); 1649689912eSchristos } 1659689912eSchristos 1669689912eSchristos if (ch == ';' || ch == '#') { 1679689912eSchristos ch = eatline(fp); 1689689912eSchristos } 1699689912eSchristos 1709689912eSchristos return ch; 1719689912eSchristos } 1729689912eSchristos 1739689912eSchristos /*! 1749689912eSchristos * Skip over any leading whitespace and then read in the next sequence of 1759689912eSchristos * non-whitespace characters. In this context newline is not considered 1769689912eSchristos * whitespace. Returns EOF on end-of-file, or the character 1779689912eSchristos * that caused the reading to stop. 1789689912eSchristos */ 1799689912eSchristos static int 1809689912eSchristos getword(FILE *fp, char *buffer, size_t size) { 1819689912eSchristos char *p = NULL; 1829689912eSchristos int ch; 1839689912eSchristos 1849689912eSchristos REQUIRE(buffer != NULL); 1859689912eSchristos REQUIRE(size > 0U); 1869689912eSchristos 1879689912eSchristos p = buffer; 1889689912eSchristos *p = '\0'; 1899689912eSchristos 1909689912eSchristos ch = eatwhite(fp); 1919689912eSchristos 1929689912eSchristos if (ch == EOF) { 1939689912eSchristos return EOF; 1949689912eSchristos } 1959689912eSchristos 1969689912eSchristos do { 1979689912eSchristos *p = '\0'; 1989689912eSchristos 1999689912eSchristos if (ch == EOF || isspace((unsigned char)ch)) { 2009689912eSchristos break; 2019689912eSchristos } else if ((size_t)(p - buffer) == size - 1) { 2029689912eSchristos return EOF; /* Not enough space. */ 2039689912eSchristos } 2049689912eSchristos 2059689912eSchristos *p++ = (char)ch; 2069689912eSchristos ch = fgetc(fp); 2079689912eSchristos } while (1); 2089689912eSchristos 2099689912eSchristos return ch; 2109689912eSchristos } 2119689912eSchristos 2129689912eSchristos static isc_result_t 2139689912eSchristos add_server(isc_mem_t *mctx, const char *address_str, 2149689912eSchristos isc_sockaddrlist_t *nameservers) { 2159689912eSchristos int error; 2169689912eSchristos isc_sockaddr_t *address = NULL; 2179689912eSchristos struct addrinfo hints, *res; 2189689912eSchristos isc_result_t result = ISC_R_SUCCESS; 2199689912eSchristos 2209689912eSchristos res = NULL; 2219689912eSchristos memset(&hints, 0, sizeof(hints)); 2229689912eSchristos hints.ai_family = AF_UNSPEC; 2239689912eSchristos hints.ai_socktype = SOCK_DGRAM; 2249689912eSchristos hints.ai_protocol = IPPROTO_UDP; 2259689912eSchristos hints.ai_flags = AI_NUMERICHOST; 2269689912eSchristos error = getaddrinfo(address_str, "53", &hints, &res); 2279689912eSchristos if (error != 0) { 2289689912eSchristos return ISC_R_BADADDRESSFORM; 2299689912eSchristos } 2309689912eSchristos 2319689912eSchristos address = isc_mem_get(mctx, sizeof(*address)); 2329689912eSchristos if (res->ai_addrlen > sizeof(address->type)) { 2339689912eSchristos isc_mem_put(mctx, address, sizeof(*address)); 2349689912eSchristos result = ISC_R_RANGE; 2359689912eSchristos goto cleanup; 2369689912eSchristos } 2379689912eSchristos 2389689912eSchristos if (res->ai_family == AF_INET) { 2399689912eSchristos struct in_addr *v4; 2409689912eSchristos unsigned char zeroaddress[] = { 0, 0, 0, 0 }; 2419689912eSchristos unsigned char loopaddress[] = { 127, 0, 0, 1 }; 2429689912eSchristos 2439689912eSchristos /* XXX: special case: treat all-0 IPv4 address as loopback */ 2449689912eSchristos v4 = &((struct sockaddr_in *)res->ai_addr)->sin_addr; 2459689912eSchristos if (memcmp(v4, zeroaddress, 4) == 0) { 2469689912eSchristos memmove(v4, loopaddress, 4); 2479689912eSchristos } 2489689912eSchristos memmove(&address->type.sin, res->ai_addr, res->ai_addrlen); 2499689912eSchristos } else if (res->ai_family == AF_INET6) { 2509689912eSchristos memmove(&address->type.sin6, res->ai_addr, res->ai_addrlen); 2519689912eSchristos } else { 2529689912eSchristos isc_mem_put(mctx, address, sizeof(*address)); 2539689912eSchristos UNEXPECTED_ERROR("ai_family (%d) not INET nor INET6", 2549689912eSchristos res->ai_family); 2559689912eSchristos result = ISC_R_UNEXPECTED; 2569689912eSchristos goto cleanup; 2579689912eSchristos } 2589689912eSchristos address->length = (unsigned int)res->ai_addrlen; 2599689912eSchristos 2609689912eSchristos ISC_LINK_INIT(address, link); 2619689912eSchristos ISC_LIST_APPEND(*nameservers, address, link); 2629689912eSchristos 2639689912eSchristos cleanup: 2649689912eSchristos freeaddrinfo(res); 2659689912eSchristos 2669689912eSchristos return result; 2679689912eSchristos } 2689689912eSchristos 2699689912eSchristos static isc_result_t 2709689912eSchristos create_addr(const char *buffer, isc_netaddr_t *addr, int convert_zero) { 2719689912eSchristos struct in_addr v4; 2729689912eSchristos struct in6_addr v6; 2739689912eSchristos 2749689912eSchristos if (inet_pton(AF_INET, buffer, &v4) == 1) { 2759689912eSchristos if (convert_zero) { 2769689912eSchristos unsigned char zeroaddress[] = { 0, 0, 0, 0 }; 2779689912eSchristos unsigned char loopaddress[] = { 127, 0, 0, 1 }; 2789689912eSchristos if (memcmp(&v4, zeroaddress, 4) == 0) { 2799689912eSchristos memmove(&v4, loopaddress, 4); 2809689912eSchristos } 2819689912eSchristos } 2829689912eSchristos addr->family = AF_INET; 2839689912eSchristos memmove(&addr->type.in, &v4, NS_INADDRSZ); 2849689912eSchristos addr->zone = 0; 2859689912eSchristos } else if (inet_pton(AF_INET6, buffer, &v6) == 1) { 2869689912eSchristos addr->family = AF_INET6; 2879689912eSchristos memmove(&addr->type.in6, &v6, NS_IN6ADDRSZ); 2889689912eSchristos addr->zone = 0; 2899689912eSchristos } else { 2909689912eSchristos return ISC_R_BADADDRESSFORM; /* Unrecognised format. */ 2919689912eSchristos } 2929689912eSchristos 2939689912eSchristos return ISC_R_SUCCESS; 2949689912eSchristos } 2959689912eSchristos 2969689912eSchristos static isc_result_t 2979689912eSchristos resconf_parsenameserver(irs_resconf_t *conf, FILE *fp) { 2989689912eSchristos char word[RESCONFMAXLINELEN]; 2999689912eSchristos int cp; 3009689912eSchristos isc_result_t result; 3019689912eSchristos 3029689912eSchristos cp = getword(fp, word, sizeof(word)); 3039689912eSchristos if (strlen(word) == 0U) { 3049689912eSchristos return ISC_R_UNEXPECTEDEND; /* Nothing on line. */ 3059689912eSchristos } else if (cp == ' ' || cp == '\t') { 3069689912eSchristos cp = eatwhite(fp); 3079689912eSchristos } 3089689912eSchristos 3099689912eSchristos if (cp != EOF && cp != '\n') { 3109689912eSchristos return ISC_R_UNEXPECTEDTOKEN; /* Extra junk on line. */ 3119689912eSchristos } 3129689912eSchristos 3139689912eSchristos if (conf->numns == RESCONFMAXNAMESERVERS) { 3149689912eSchristos return ISC_R_SUCCESS; 3159689912eSchristos } 3169689912eSchristos 3179689912eSchristos result = add_server(conf->mctx, word, &conf->nameservers); 3189689912eSchristos if (result != ISC_R_SUCCESS) { 3199689912eSchristos return result; 3209689912eSchristos } 3219689912eSchristos conf->numns++; 3229689912eSchristos 3239689912eSchristos return ISC_R_SUCCESS; 3249689912eSchristos } 3259689912eSchristos 3269689912eSchristos static isc_result_t 3279689912eSchristos resconf_parsedomain(irs_resconf_t *conf, FILE *fp) { 3289689912eSchristos char word[RESCONFMAXLINELEN]; 3299689912eSchristos int res; 3309689912eSchristos unsigned int i; 3319689912eSchristos 3329689912eSchristos res = getword(fp, word, sizeof(word)); 3339689912eSchristos if (strlen(word) == 0U) { 3349689912eSchristos return ISC_R_UNEXPECTEDEND; /* Nothing else on line. */ 3359689912eSchristos } else if (res == ' ' || res == '\t') { 3369689912eSchristos res = eatwhite(fp); 3379689912eSchristos } 3389689912eSchristos 3399689912eSchristos if (res != EOF && res != '\n') { 3409689912eSchristos return ISC_R_UNEXPECTEDTOKEN; /* Extra junk on line. */ 3419689912eSchristos } 3429689912eSchristos 3439689912eSchristos if (conf->domainname != NULL) { 3449689912eSchristos isc_mem_free(conf->mctx, conf->domainname); 3459689912eSchristos } 3469689912eSchristos 3479689912eSchristos /* 3489689912eSchristos * Search and domain are mutually exclusive. 3499689912eSchristos */ 3509689912eSchristos for (i = 0; i < RESCONFMAXSEARCH; i++) { 3519689912eSchristos if (conf->search[i] != NULL) { 3529689912eSchristos isc_mem_free(conf->mctx, conf->search[i]); 3539689912eSchristos conf->search[i] = NULL; 3549689912eSchristos } 3559689912eSchristos } 3569689912eSchristos conf->searchnxt = 0; 3579689912eSchristos 3589689912eSchristos conf->domainname = isc_mem_strdup(conf->mctx, word); 3599689912eSchristos 3609689912eSchristos return ISC_R_SUCCESS; 3619689912eSchristos } 3629689912eSchristos 3639689912eSchristos static isc_result_t 3649689912eSchristos resconf_parsesearch(irs_resconf_t *conf, FILE *fp) { 3659689912eSchristos int delim; 3669689912eSchristos unsigned int idx; 3679689912eSchristos char word[RESCONFMAXLINELEN]; 3689689912eSchristos 3699689912eSchristos if (conf->domainname != NULL) { 3709689912eSchristos /* 3719689912eSchristos * Search and domain are mutually exclusive. 3729689912eSchristos */ 3739689912eSchristos isc_mem_free(conf->mctx, conf->domainname); 3749689912eSchristos conf->domainname = NULL; 3759689912eSchristos } 3769689912eSchristos 3779689912eSchristos /* 3789689912eSchristos * Remove any previous search definitions. 3799689912eSchristos */ 3809689912eSchristos for (idx = 0; idx < RESCONFMAXSEARCH; idx++) { 3819689912eSchristos if (conf->search[idx] != NULL) { 3829689912eSchristos isc_mem_free(conf->mctx, conf->search[idx]); 3839689912eSchristos conf->search[idx] = NULL; 3849689912eSchristos } 3859689912eSchristos } 3869689912eSchristos conf->searchnxt = 0; 3879689912eSchristos 3889689912eSchristos delim = getword(fp, word, sizeof(word)); 3899689912eSchristos if (strlen(word) == 0U) { 3909689912eSchristos return ISC_R_UNEXPECTEDEND; /* Nothing else on line. */ 3919689912eSchristos } 3929689912eSchristos 3939689912eSchristos idx = 0; 3949689912eSchristos while (strlen(word) > 0U) { 3959689912eSchristos if (conf->searchnxt == RESCONFMAXSEARCH) { 3969689912eSchristos goto ignore; /* Too many domains. */ 3979689912eSchristos } 3989689912eSchristos 3999689912eSchristos INSIST(idx < sizeof(conf->search) / sizeof(conf->search[0])); 4009689912eSchristos conf->search[idx] = isc_mem_strdup(conf->mctx, word); 4019689912eSchristos idx++; 4029689912eSchristos conf->searchnxt++; 4039689912eSchristos 4049689912eSchristos ignore: 4059689912eSchristos if (delim == EOF || delim == '\n') { 4069689912eSchristos break; 4079689912eSchristos } else { 4089689912eSchristos delim = getword(fp, word, sizeof(word)); 4099689912eSchristos } 4109689912eSchristos } 4119689912eSchristos 4129689912eSchristos return ISC_R_SUCCESS; 4139689912eSchristos } 4149689912eSchristos 4159689912eSchristos static isc_result_t 4169689912eSchristos resconf_parsesortlist(irs_resconf_t *conf, FILE *fp) { 4179689912eSchristos int delim, res; 4189689912eSchristos unsigned int idx; 4199689912eSchristos char word[RESCONFMAXLINELEN]; 4209689912eSchristos char *p; 4219689912eSchristos 4229689912eSchristos delim = getword(fp, word, sizeof(word)); 4239689912eSchristos if (strlen(word) == 0U) { 4249689912eSchristos return ISC_R_UNEXPECTEDEND; /* Empty line after keyword. */ 4259689912eSchristos } 4269689912eSchristos 4279689912eSchristos while (strlen(word) > 0U) { 4289689912eSchristos if (conf->sortlistnxt == RESCONFMAXSORTLIST) { 4299689912eSchristos return ISC_R_QUOTA; /* Too many values. */ 4309689912eSchristos } 4319689912eSchristos 4329689912eSchristos p = strchr(word, '/'); 4339689912eSchristos if (p != NULL) { 4349689912eSchristos *p++ = '\0'; 4359689912eSchristos } 4369689912eSchristos 4379689912eSchristos idx = conf->sortlistnxt; 4389689912eSchristos INSIST(idx < 4399689912eSchristos sizeof(conf->sortlist) / sizeof(conf->sortlist[0])); 4409689912eSchristos res = create_addr(word, &conf->sortlist[idx].addr, 1); 4419689912eSchristos if (res != ISC_R_SUCCESS) { 4429689912eSchristos return res; 4439689912eSchristos } 4449689912eSchristos 4459689912eSchristos if (p != NULL) { 4469689912eSchristos res = create_addr(p, &conf->sortlist[idx].mask, 0); 4479689912eSchristos if (res != ISC_R_SUCCESS) { 4489689912eSchristos return res; 4499689912eSchristos } 4509689912eSchristos } else { 4519689912eSchristos /* 4529689912eSchristos * Make up a mask. (XXX: is this correct?) 4539689912eSchristos */ 4549689912eSchristos conf->sortlist[idx].mask = conf->sortlist[idx].addr; 4559689912eSchristos memset(&conf->sortlist[idx].mask.type, 0xff, 4569689912eSchristos sizeof(conf->sortlist[idx].mask.type)); 4579689912eSchristos } 4589689912eSchristos 4599689912eSchristos conf->sortlistnxt++; 4609689912eSchristos 4619689912eSchristos if (delim == EOF || delim == '\n') { 4629689912eSchristos break; 4639689912eSchristos } else { 4649689912eSchristos delim = getword(fp, word, sizeof(word)); 4659689912eSchristos } 4669689912eSchristos } 4679689912eSchristos 4689689912eSchristos return ISC_R_SUCCESS; 4699689912eSchristos } 4709689912eSchristos 4719689912eSchristos static isc_result_t 4729689912eSchristos resconf_optionnumber(const char *word, uint8_t *number) { 4739689912eSchristos char *p; 4749689912eSchristos long n; 4759689912eSchristos 4769689912eSchristos n = strtol(word, &p, 10); 4779689912eSchristos if (*p != '\0') { /* Bad string. */ 4789689912eSchristos return ISC_R_UNEXPECTEDTOKEN; 4799689912eSchristos } 4809689912eSchristos if (n < 0 || n > 0xff) { /* Out of range. */ 4819689912eSchristos return ISC_R_RANGE; 4829689912eSchristos } 4839689912eSchristos *number = n; 4849689912eSchristos return ISC_R_SUCCESS; 4859689912eSchristos } 4869689912eSchristos 4879689912eSchristos static isc_result_t 4889689912eSchristos resconf_parseoption(irs_resconf_t *conf, FILE *fp) { 4899689912eSchristos int delim; 4909689912eSchristos isc_result_t result = ISC_R_SUCCESS; 4919689912eSchristos char word[RESCONFMAXLINELEN]; 4929689912eSchristos 4939689912eSchristos delim = getword(fp, word, sizeof(word)); 4949689912eSchristos if (strlen(word) == 0U) { 4959689912eSchristos return ISC_R_UNEXPECTEDEND; /* Empty line after keyword. */ 4969689912eSchristos } 4979689912eSchristos 4989689912eSchristos while (strlen(word) > 0U) { 4999689912eSchristos if (strcmp("debug", word) == 0) { 5009689912eSchristos conf->resdebug = 1; 5019689912eSchristos } else if (strncmp("ndots:", word, 6) == 0) { 5029689912eSchristos CHECK(resconf_optionnumber(word + 6, &conf->ndots)); 5039689912eSchristos } else if (strncmp("attempts:", word, 9) == 0) { 5049689912eSchristos CHECK(resconf_optionnumber(word + 9, &conf->attempts)); 5059689912eSchristos } else if (strncmp("timeout:", word, 8) == 0) { 5069689912eSchristos CHECK(resconf_optionnumber(word + 8, &conf->timeout)); 5079689912eSchristos } 5089689912eSchristos 5099689912eSchristos if (delim == EOF || delim == '\n') { 5109689912eSchristos break; 5119689912eSchristos } else { 5129689912eSchristos delim = getword(fp, word, sizeof(word)); 5139689912eSchristos } 5149689912eSchristos } 5159689912eSchristos 5169689912eSchristos cleanup: 5179689912eSchristos return result; 5189689912eSchristos } 5199689912eSchristos 5209689912eSchristos static isc_result_t 5219689912eSchristos add_search(irs_resconf_t *conf, char *domain) { 5229689912eSchristos irs_resconf_search_t *entry; 5239689912eSchristos 5249689912eSchristos entry = isc_mem_get(conf->mctx, sizeof(*entry)); 5259689912eSchristos 5269689912eSchristos entry->domain = domain; 5279689912eSchristos ISC_LINK_INIT(entry, link); 5289689912eSchristos ISC_LIST_APPEND(conf->searchlist, entry, link); 5299689912eSchristos 5309689912eSchristos return ISC_R_SUCCESS; 5319689912eSchristos } 5329689912eSchristos 5339689912eSchristos /*% parses a file and fills in the data structure. */ 5349689912eSchristos isc_result_t 5359689912eSchristos irs_resconf_load(isc_mem_t *mctx, const char *filename, irs_resconf_t **confp) { 5369689912eSchristos FILE *fp = NULL; 5379689912eSchristos char word[256]; 5389689912eSchristos isc_result_t rval, ret = ISC_R_SUCCESS; 5399689912eSchristos irs_resconf_t *conf; 5409689912eSchristos unsigned int i; 5419689912eSchristos int stopchar; 5429689912eSchristos 5439689912eSchristos REQUIRE(mctx != NULL); 5449689912eSchristos REQUIRE(filename != NULL); 5459689912eSchristos REQUIRE(strlen(filename) > 0U); 5469689912eSchristos REQUIRE(confp != NULL && *confp == NULL); 5479689912eSchristos 5489689912eSchristos conf = isc_mem_get(mctx, sizeof(*conf)); 5499689912eSchristos 5509689912eSchristos conf->mctx = mctx; 5519689912eSchristos ISC_LIST_INIT(conf->nameservers); 5529689912eSchristos ISC_LIST_INIT(conf->searchlist); 5539689912eSchristos conf->numns = 0; 5549689912eSchristos conf->domainname = NULL; 5559689912eSchristos conf->searchnxt = 0; 5569689912eSchristos conf->sortlistnxt = 0; 5579689912eSchristos conf->resdebug = 0; 5589689912eSchristos conf->ndots = 1; 5599689912eSchristos conf->attempts = 3; 5609689912eSchristos conf->timeout = 0; 5619689912eSchristos for (i = 0; i < RESCONFMAXSEARCH; i++) { 5629689912eSchristos conf->search[i] = NULL; 5639689912eSchristos } 5649689912eSchristos 5659689912eSchristos errno = 0; 5669689912eSchristos if ((fp = fopen(filename, "r")) != NULL) { 5679689912eSchristos do { 5689689912eSchristos stopchar = getword(fp, word, sizeof(word)); 5699689912eSchristos if (stopchar == EOF) { 5709689912eSchristos rval = ISC_R_SUCCESS; 5719689912eSchristos POST(rval); 5729689912eSchristos break; 5739689912eSchristos } 5749689912eSchristos 5759689912eSchristos if (strlen(word) == 0U) { 5769689912eSchristos rval = ISC_R_SUCCESS; 5779689912eSchristos } else if (strcmp(word, "nameserver") == 0) { 5789689912eSchristos rval = resconf_parsenameserver(conf, fp); 5799689912eSchristos } else if (strcmp(word, "domain") == 0) { 5809689912eSchristos rval = resconf_parsedomain(conf, fp); 5819689912eSchristos } else if (strcmp(word, "search") == 0) { 5829689912eSchristos rval = resconf_parsesearch(conf, fp); 5839689912eSchristos } else if (strcmp(word, "sortlist") == 0) { 5849689912eSchristos rval = resconf_parsesortlist(conf, fp); 5859689912eSchristos } else if (strcmp(word, "options") == 0) { 5869689912eSchristos rval = resconf_parseoption(conf, fp); 5879689912eSchristos } else { 5889689912eSchristos /* unrecognised word. Ignore entire line */ 5899689912eSchristos rval = ISC_R_SUCCESS; 5909689912eSchristos stopchar = eatline(fp); 5919689912eSchristos if (stopchar == EOF) { 5929689912eSchristos break; 5939689912eSchristos } 5949689912eSchristos } 5959689912eSchristos if (ret == ISC_R_SUCCESS && rval != ISC_R_SUCCESS) { 5969689912eSchristos ret = rval; 5979689912eSchristos } 5989689912eSchristos } while (1); 5999689912eSchristos 6009689912eSchristos fclose(fp); 6019689912eSchristos } else { 6029689912eSchristos switch (errno) { 6039689912eSchristos case ENOENT: 6049689912eSchristos break; 6059689912eSchristos default: 6069689912eSchristos isc_mem_put(mctx, conf, sizeof(*conf)); 6079689912eSchristos return ISC_R_INVALIDFILE; 6089689912eSchristos } 6099689912eSchristos } 6109689912eSchristos 6119689912eSchristos if (ret != ISC_R_SUCCESS) { 6129689912eSchristos goto error; 6139689912eSchristos } 6149689912eSchristos 6159689912eSchristos /* 6169689912eSchristos * Construct unified search list from domain or configured 6179689912eSchristos * search list 6189689912eSchristos */ 6199689912eSchristos if (conf->domainname != NULL) { 6209689912eSchristos ret = add_search(conf, conf->domainname); 6219689912eSchristos } else if (conf->searchnxt > 0) { 6229689912eSchristos for (i = 0; i < conf->searchnxt; i++) { 6239689912eSchristos ret = add_search(conf, conf->search[i]); 6249689912eSchristos if (ret != ISC_R_SUCCESS) { 6259689912eSchristos break; 6269689912eSchristos } 6279689912eSchristos } 6289689912eSchristos } 6299689912eSchristos 6309689912eSchristos /* If we don't find a nameserver fall back to localhost */ 6319689912eSchristos if (conf->numns == 0U) { 6329689912eSchristos INSIST(ISC_LIST_EMPTY(conf->nameservers)); 6339689912eSchristos 6349689912eSchristos /* XXX: should we catch errors? */ 6359689912eSchristos (void)add_server(conf->mctx, "::1", &conf->nameservers); 6369689912eSchristos (void)add_server(conf->mctx, "127.0.0.1", &conf->nameservers); 6379689912eSchristos } 6389689912eSchristos 6399689912eSchristos error: 6409689912eSchristos conf->magic = IRS_RESCONF_MAGIC; 6419689912eSchristos 6429689912eSchristos if (ret != ISC_R_SUCCESS) { 6439689912eSchristos irs_resconf_destroy(&conf); 6449689912eSchristos } else { 6459689912eSchristos if (fp == NULL) { 6469689912eSchristos ret = ISC_R_FILENOTFOUND; 6479689912eSchristos } 6489689912eSchristos *confp = conf; 6499689912eSchristos } 6509689912eSchristos 6519689912eSchristos return ret; 6529689912eSchristos } 6539689912eSchristos 6549689912eSchristos void 6559689912eSchristos irs_resconf_destroy(irs_resconf_t **confp) { 6569689912eSchristos irs_resconf_t *conf; 6579689912eSchristos isc_sockaddr_t *address; 6589689912eSchristos irs_resconf_search_t *searchentry; 6599689912eSchristos unsigned int i; 6609689912eSchristos 6619689912eSchristos REQUIRE(confp != NULL); 6629689912eSchristos conf = *confp; 6639689912eSchristos *confp = NULL; 6649689912eSchristos REQUIRE(IRS_RESCONF_VALID(conf)); 6659689912eSchristos 6669689912eSchristos while ((searchentry = ISC_LIST_HEAD(conf->searchlist)) != NULL) { 6679689912eSchristos ISC_LIST_UNLINK(conf->searchlist, searchentry, link); 6689689912eSchristos isc_mem_put(conf->mctx, searchentry, sizeof(*searchentry)); 6699689912eSchristos } 6709689912eSchristos 6719689912eSchristos while ((address = ISC_LIST_HEAD(conf->nameservers)) != NULL) { 6729689912eSchristos ISC_LIST_UNLINK(conf->nameservers, address, link); 6739689912eSchristos isc_mem_put(conf->mctx, address, sizeof(*address)); 6749689912eSchristos } 6759689912eSchristos 6769689912eSchristos if (conf->domainname != NULL) { 6779689912eSchristos isc_mem_free(conf->mctx, conf->domainname); 6789689912eSchristos } 6799689912eSchristos 6809689912eSchristos for (i = 0; i < RESCONFMAXSEARCH; i++) { 6819689912eSchristos if (conf->search[i] != NULL) { 6829689912eSchristos isc_mem_free(conf->mctx, conf->search[i]); 6839689912eSchristos } 6849689912eSchristos } 6859689912eSchristos 6869689912eSchristos isc_mem_put(conf->mctx, conf, sizeof(*conf)); 6879689912eSchristos } 6889689912eSchristos 6899689912eSchristos isc_sockaddrlist_t * 6909689912eSchristos irs_resconf_getnameservers(irs_resconf_t *conf) { 6919689912eSchristos REQUIRE(IRS_RESCONF_VALID(conf)); 6929689912eSchristos 6939689912eSchristos return &conf->nameservers; 6949689912eSchristos } 6959689912eSchristos 6969689912eSchristos irs_resconf_searchlist_t * 6979689912eSchristos irs_resconf_getsearchlist(irs_resconf_t *conf) { 6989689912eSchristos REQUIRE(IRS_RESCONF_VALID(conf)); 6999689912eSchristos 7009689912eSchristos return &conf->searchlist; 7019689912eSchristos } 7029689912eSchristos 7039689912eSchristos unsigned int 7049689912eSchristos irs_resconf_getndots(irs_resconf_t *conf) { 7059689912eSchristos REQUIRE(IRS_RESCONF_VALID(conf)); 7069689912eSchristos 7079689912eSchristos return (unsigned int)conf->ndots; 7089689912eSchristos } 7099689912eSchristos 7109689912eSchristos unsigned int 7119689912eSchristos irs_resconf_getattempts(irs_resconf_t *conf) { 7129689912eSchristos REQUIRE(IRS_RESCONF_VALID(conf)); 7139689912eSchristos 7149689912eSchristos return (unsigned int)conf->attempts; 7159689912eSchristos } 7169689912eSchristos 7179689912eSchristos unsigned int 7189689912eSchristos irs_resconf_gettimeout(irs_resconf_t *conf) { 7199689912eSchristos REQUIRE(IRS_RESCONF_VALID(conf)); 7209689912eSchristos 7219689912eSchristos return (unsigned int)conf->timeout; 7229689912eSchristos } 723