1224090Sdougb/* 2262706Serwin * Copyright (C) 2009, 2011, 2012, 2014 Internet Systems Consortium, Inc. ("ISC") 3224090Sdougb * 4224090Sdougb * Permission to use, copy, modify, and/or distribute this software for any 5224090Sdougb * purpose with or without fee is hereby granted, provided that the above 6224090Sdougb * copyright notice and this permission notice appear in all copies. 7224090Sdougb * 8224090Sdougb * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 9224090Sdougb * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10224090Sdougb * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 11224090Sdougb * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12224090Sdougb * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13224090Sdougb * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14224090Sdougb * PERFORMANCE OF THIS SOFTWARE. 15224090Sdougb */ 16224090Sdougb 17234010Sdougb/* $Id$ */ 18224090Sdougb 19224090Sdougb/*! \file resconf.c */ 20224090Sdougb 21224090Sdougb/** 22224090Sdougb * Module for parsing resolv.conf files (largely derived from lwconfig.c). 23224090Sdougb * 24224090Sdougb * irs_resconf_load() opens the file filename and parses it to initialize 25224090Sdougb * the configuration structure. 26224090Sdougb * 27224090Sdougb * \section lwconfig_return Return Values 28224090Sdougb * 29224090Sdougb * irs_resconf_load() returns #IRS_R_SUCCESS if it successfully read and 30224090Sdougb * parsed filename. It returns a non-0 error code if filename could not be 31224090Sdougb * opened or contained incorrect resolver statements. 32224090Sdougb * 33224090Sdougb * \section lwconfig_see See Also 34224090Sdougb * 35224090Sdougb * stdio(3), \link resolver resolver \endlink 36224090Sdougb * 37224090Sdougb * \section files Files 38224090Sdougb * 39224090Sdougb * /etc/resolv.conf 40224090Sdougb */ 41224090Sdougb 42224090Sdougb#include <config.h> 43224090Sdougb 44224090Sdougb#include <sys/types.h> 45224090Sdougb#include <sys/socket.h> 46224090Sdougb 47224090Sdougb#include <ctype.h> 48224090Sdougb#include <errno.h> 49224090Sdougb#include <netdb.h> 50224090Sdougb#include <stdlib.h> 51224090Sdougb#include <stdio.h> 52224090Sdougb#include <string.h> 53224090Sdougb 54224090Sdougb#include <isc/magic.h> 55224090Sdougb#include <isc/mem.h> 56224090Sdougb#include <isc/netaddr.h> 57224090Sdougb#include <isc/sockaddr.h> 58224090Sdougb#include <isc/util.h> 59224090Sdougb 60224090Sdougb#include <irs/resconf.h> 61224090Sdougb 62224090Sdougb#define IRS_RESCONF_MAGIC ISC_MAGIC('R', 'E', 'S', 'c') 63224090Sdougb#define IRS_RESCONF_VALID(c) ISC_MAGIC_VALID(c, IRS_RESCONF_MAGIC) 64224090Sdougb 65224090Sdougb/*! 66224090Sdougb * protocol constants 67224090Sdougb */ 68224090Sdougb 69224090Sdougb#if ! defined(NS_INADDRSZ) 70224090Sdougb#define NS_INADDRSZ 4 71224090Sdougb#endif 72224090Sdougb 73224090Sdougb#if ! defined(NS_IN6ADDRSZ) 74224090Sdougb#define NS_IN6ADDRSZ 16 75224090Sdougb#endif 76224090Sdougb 77224090Sdougb/*! 78224090Sdougb * resolv.conf parameters 79224090Sdougb */ 80224090Sdougb 81224090Sdougb#define RESCONFMAXNAMESERVERS 3 /*%< max 3 "nameserver" entries */ 82224090Sdougb#define RESCONFMAXSEARCH 8 /*%< max 8 domains in "search" entry */ 83224090Sdougb#define RESCONFMAXLINELEN 256 /*%< max size of a line */ 84224090Sdougb#define RESCONFMAXSORTLIST 10 /*%< max 10 */ 85224090Sdougb 86224090Sdougb/*! 87224090Sdougb * configuration data structure 88224090Sdougb */ 89224090Sdougb 90224090Sdougbstruct irs_resconf { 91224090Sdougb /* 92224090Sdougb * The configuration data is a thread-specific object, and does not 93224090Sdougb * need to be locked. 94224090Sdougb */ 95224090Sdougb unsigned int magic; 96224090Sdougb isc_mem_t *mctx; 97224090Sdougb 98224090Sdougb isc_sockaddrlist_t nameservers; 99224090Sdougb unsigned int numns; /*%< number of configured servers */ 100224090Sdougb 101224090Sdougb char *domainname; 102224090Sdougb char *search[RESCONFMAXSEARCH]; 103224090Sdougb isc_uint8_t searchnxt; /*%< index for next free slot */ 104224090Sdougb 105224090Sdougb irs_resconf_searchlist_t searchlist; 106224090Sdougb 107224090Sdougb struct { 108224090Sdougb isc_netaddr_t addr; 109224090Sdougb /*% mask has a non-zero 'family' if set */ 110224090Sdougb isc_netaddr_t mask; 111224090Sdougb } sortlist[RESCONFMAXSORTLIST]; 112224090Sdougb isc_uint8_t sortlistnxt; 113224090Sdougb 114224090Sdougb /*%< non-zero if 'options debug' set */ 115224090Sdougb isc_uint8_t resdebug; 116224090Sdougb /*%< set to n in 'options ndots:n' */ 117224090Sdougb isc_uint8_t ndots; 118224090Sdougb}; 119224090Sdougb 120224090Sdougbstatic isc_result_t 121224090Sdougbresconf_parsenameserver(irs_resconf_t *conf, FILE *fp); 122224090Sdougbstatic isc_result_t 123224090Sdougbresconf_parsedomain(irs_resconf_t *conf, FILE *fp); 124224090Sdougbstatic isc_result_t 125224090Sdougbresconf_parsesearch(irs_resconf_t *conf, FILE *fp); 126224090Sdougbstatic isc_result_t 127224090Sdougbresconf_parsesortlist(irs_resconf_t *conf, FILE *fp); 128224090Sdougbstatic isc_result_t 129224090Sdougbresconf_parseoption(irs_resconf_t *ctx, FILE *fp); 130224090Sdougb 131224090Sdougb/*! 132224090Sdougb * Eat characters from FP until EOL or EOF. Returns EOF or '\n' 133224090Sdougb */ 134224090Sdougbstatic int 135224090Sdougbeatline(FILE *fp) { 136224090Sdougb int ch; 137224090Sdougb 138224090Sdougb ch = fgetc(fp); 139224090Sdougb while (ch != '\n' && ch != EOF) 140224090Sdougb ch = fgetc(fp); 141224090Sdougb 142224090Sdougb return (ch); 143224090Sdougb} 144224090Sdougb 145224090Sdougb/*! 146224090Sdougb * Eats white space up to next newline or non-whitespace character (of 147224090Sdougb * EOF). Returns the last character read. Comments are considered white 148224090Sdougb * space. 149224090Sdougb */ 150224090Sdougbstatic int 151224090Sdougbeatwhite(FILE *fp) { 152224090Sdougb int ch; 153224090Sdougb 154224090Sdougb ch = fgetc(fp); 155224090Sdougb while (ch != '\n' && ch != EOF && isspace((unsigned char)ch)) 156224090Sdougb ch = fgetc(fp); 157224090Sdougb 158224090Sdougb if (ch == ';' || ch == '#') 159224090Sdougb ch = eatline(fp); 160224090Sdougb 161224090Sdougb return (ch); 162224090Sdougb} 163224090Sdougb 164224090Sdougb/*! 165224090Sdougb * Skip over any leading whitespace and then read in the next sequence of 166224090Sdougb * non-whitespace characters. In this context newline is not considered 167224090Sdougb * whitespace. Returns EOF on end-of-file, or the character 168224090Sdougb * that caused the reading to stop. 169224090Sdougb */ 170224090Sdougbstatic int 171224090Sdougbgetword(FILE *fp, char *buffer, size_t size) { 172224090Sdougb int ch; 173224090Sdougb char *p = buffer; 174224090Sdougb 175224090Sdougb REQUIRE(buffer != NULL); 176224090Sdougb REQUIRE(size > 0U); 177224090Sdougb 178224090Sdougb *p = '\0'; 179224090Sdougb 180224090Sdougb ch = eatwhite(fp); 181224090Sdougb 182224090Sdougb if (ch == EOF) 183224090Sdougb return (EOF); 184224090Sdougb 185224090Sdougb do { 186224090Sdougb *p = '\0'; 187224090Sdougb 188224090Sdougb if (ch == EOF || isspace((unsigned char)ch)) 189224090Sdougb break; 190224090Sdougb else if ((size_t) (p - buffer) == size - 1) 191224090Sdougb return (EOF); /* Not enough space. */ 192224090Sdougb 193224090Sdougb *p++ = (char)ch; 194224090Sdougb ch = fgetc(fp); 195224090Sdougb } while (1); 196224090Sdougb 197224090Sdougb return (ch); 198224090Sdougb} 199224090Sdougb 200224090Sdougbstatic isc_result_t 201224090Sdougbadd_server(isc_mem_t *mctx, const char *address_str, 202224090Sdougb isc_sockaddrlist_t *nameservers) 203224090Sdougb{ 204224090Sdougb int error; 205224090Sdougb isc_sockaddr_t *address = NULL; 206224090Sdougb struct addrinfo hints, *res; 207224090Sdougb isc_result_t result = ISC_R_SUCCESS; 208224090Sdougb 209224090Sdougb res = NULL; 210224090Sdougb memset(&hints, 0, sizeof(hints)); 211224090Sdougb hints.ai_family = AF_UNSPEC; 212224090Sdougb hints.ai_socktype = SOCK_DGRAM; 213224090Sdougb hints.ai_protocol = IPPROTO_UDP; 214224090Sdougb hints.ai_flags = AI_NUMERICHOST; 215224090Sdougb error = getaddrinfo(address_str, "53", &hints, &res); 216224090Sdougb if (error != 0) 217224090Sdougb return (ISC_R_BADADDRESSFORM); 218224090Sdougb 219224090Sdougb /* XXX: special case: treat all-0 IPv4 address as loopback */ 220224090Sdougb if (res->ai_family == AF_INET) { 221224090Sdougb struct in_addr *v4; 222224090Sdougb unsigned char zeroaddress[] = {0, 0, 0, 0}; 223224090Sdougb unsigned char loopaddress[] = {127, 0, 0, 1}; 224224090Sdougb 225224090Sdougb v4 = &((struct sockaddr_in *)res->ai_addr)->sin_addr; 226224090Sdougb if (memcmp(v4, zeroaddress, 4) == 0) 227262706Serwin memmove(v4, loopaddress, 4); 228224090Sdougb } 229224090Sdougb 230224090Sdougb address = isc_mem_get(mctx, sizeof(*address)); 231224090Sdougb if (address == NULL) { 232224090Sdougb result = ISC_R_NOMEMORY; 233224090Sdougb goto cleanup; 234224090Sdougb } 235224090Sdougb if (res->ai_addrlen > sizeof(address->type)) { 236224090Sdougb isc_mem_put(mctx, address, sizeof(*address)); 237224090Sdougb result = ISC_R_RANGE; 238224090Sdougb goto cleanup; 239224090Sdougb } 240224090Sdougb address->length = res->ai_addrlen; 241262706Serwin memmove(&address->type.ss, res->ai_addr, res->ai_addrlen); 242224090Sdougb ISC_LINK_INIT(address, link); 243224090Sdougb ISC_LIST_APPEND(*nameservers, address, link); 244224090Sdougb 245224090Sdougb cleanup: 246224090Sdougb freeaddrinfo(res); 247224090Sdougb 248224090Sdougb return (result); 249224090Sdougb} 250224090Sdougb 251224090Sdougbstatic isc_result_t 252224090Sdougbcreate_addr(const char *buffer, isc_netaddr_t *addr, int convert_zero) { 253224090Sdougb struct in_addr v4; 254224090Sdougb struct in6_addr v6; 255224090Sdougb 256224090Sdougb if (inet_aton(buffer, &v4) == 1) { 257224090Sdougb if (convert_zero) { 258224090Sdougb unsigned char zeroaddress[] = {0, 0, 0, 0}; 259224090Sdougb unsigned char loopaddress[] = {127, 0, 0, 1}; 260224090Sdougb if (memcmp(&v4, zeroaddress, 4) == 0) 261262706Serwin memmove(&v4, loopaddress, 4); 262224090Sdougb } 263224090Sdougb addr->family = AF_INET; 264262706Serwin memmove(&addr->type.in, &v4, NS_INADDRSZ); 265224090Sdougb addr->zone = 0; 266224090Sdougb } else if (inet_pton(AF_INET6, buffer, &v6) == 1) { 267224090Sdougb addr->family = AF_INET6; 268262706Serwin memmove(&addr->type.in6, &v6, NS_IN6ADDRSZ); 269224090Sdougb addr->zone = 0; 270224090Sdougb } else 271224090Sdougb return (ISC_R_BADADDRESSFORM); /* Unrecognised format. */ 272224090Sdougb 273224090Sdougb return (ISC_R_SUCCESS); 274224090Sdougb} 275224090Sdougb 276224090Sdougbstatic isc_result_t 277224090Sdougbresconf_parsenameserver(irs_resconf_t *conf, FILE *fp) { 278224090Sdougb char word[RESCONFMAXLINELEN]; 279224090Sdougb int cp; 280224090Sdougb isc_result_t result; 281224090Sdougb 282224090Sdougb if (conf->numns == RESCONFMAXNAMESERVERS) 283224090Sdougb return (ISC_R_SUCCESS); 284224090Sdougb 285224090Sdougb cp = getword(fp, word, sizeof(word)); 286224090Sdougb if (strlen(word) == 0U) 287224090Sdougb return (ISC_R_UNEXPECTEDEND); /* Nothing on line. */ 288224090Sdougb else if (cp == ' ' || cp == '\t') 289224090Sdougb cp = eatwhite(fp); 290224090Sdougb 291224090Sdougb if (cp != EOF && cp != '\n') 292224090Sdougb return (ISC_R_UNEXPECTEDTOKEN); /* Extra junk on line. */ 293224090Sdougb 294224090Sdougb result = add_server(conf->mctx, word, &conf->nameservers); 295224090Sdougb if (result != ISC_R_SUCCESS) 296224090Sdougb return (result); 297224090Sdougb conf->numns++; 298224090Sdougb 299224090Sdougb return (ISC_R_SUCCESS); 300224090Sdougb} 301224090Sdougb 302224090Sdougbstatic isc_result_t 303224090Sdougbresconf_parsedomain(irs_resconf_t *conf, FILE *fp) { 304224090Sdougb char word[RESCONFMAXLINELEN]; 305224090Sdougb int res, i; 306224090Sdougb 307224090Sdougb res = getword(fp, word, sizeof(word)); 308224090Sdougb if (strlen(word) == 0U) 309224090Sdougb return (ISC_R_UNEXPECTEDEND); /* Nothing else on line. */ 310224090Sdougb else if (res == ' ' || res == '\t') 311224090Sdougb res = eatwhite(fp); 312224090Sdougb 313224090Sdougb if (res != EOF && res != '\n') 314224090Sdougb return (ISC_R_UNEXPECTEDTOKEN); /* Extra junk on line. */ 315224090Sdougb 316224090Sdougb if (conf->domainname != NULL) 317224090Sdougb isc_mem_free(conf->mctx, conf->domainname); 318224090Sdougb 319224090Sdougb /* 320224090Sdougb * Search and domain are mutually exclusive. 321224090Sdougb */ 322224090Sdougb for (i = 0; i < RESCONFMAXSEARCH; i++) { 323224090Sdougb if (conf->search[i] != NULL) { 324224090Sdougb isc_mem_free(conf->mctx, conf->search[i]); 325224090Sdougb conf->search[i] = NULL; 326224090Sdougb } 327224090Sdougb } 328224090Sdougb conf->searchnxt = 0; 329224090Sdougb 330224090Sdougb conf->domainname = isc_mem_strdup(conf->mctx, word); 331224090Sdougb if (conf->domainname == NULL) 332224090Sdougb return (ISC_R_NOMEMORY); 333224090Sdougb 334224090Sdougb return (ISC_R_SUCCESS); 335224090Sdougb} 336224090Sdougb 337224090Sdougbstatic isc_result_t 338224090Sdougbresconf_parsesearch(irs_resconf_t *conf, FILE *fp) { 339224090Sdougb int idx, delim; 340224090Sdougb char word[RESCONFMAXLINELEN]; 341224090Sdougb 342224090Sdougb if (conf->domainname != NULL) { 343224090Sdougb /* 344224090Sdougb * Search and domain are mutually exclusive. 345224090Sdougb */ 346224090Sdougb isc_mem_free(conf->mctx, conf->domainname); 347224090Sdougb conf->domainname = NULL; 348224090Sdougb } 349224090Sdougb 350224090Sdougb /* 351224090Sdougb * Remove any previous search definitions. 352224090Sdougb */ 353224090Sdougb for (idx = 0; idx < RESCONFMAXSEARCH; idx++) { 354224090Sdougb if (conf->search[idx] != NULL) { 355224090Sdougb isc_mem_free(conf->mctx, conf->search[idx]); 356224090Sdougb conf->search[idx] = NULL; 357224090Sdougb } 358224090Sdougb } 359224090Sdougb conf->searchnxt = 0; 360224090Sdougb 361224090Sdougb delim = getword(fp, word, sizeof(word)); 362224090Sdougb if (strlen(word) == 0U) 363224090Sdougb return (ISC_R_UNEXPECTEDEND); /* Nothing else on line. */ 364224090Sdougb 365224090Sdougb idx = 0; 366224090Sdougb while (strlen(word) > 0U) { 367224090Sdougb if (conf->searchnxt == RESCONFMAXSEARCH) 368224090Sdougb goto ignore; /* Too many domains. */ 369224090Sdougb 370224090Sdougb conf->search[idx] = isc_mem_strdup(conf->mctx, word); 371224090Sdougb if (conf->search[idx] == NULL) 372224090Sdougb return (ISC_R_NOMEMORY); 373224090Sdougb idx++; 374224090Sdougb conf->searchnxt++; 375224090Sdougb 376224090Sdougb ignore: 377224090Sdougb if (delim == EOF || delim == '\n') 378224090Sdougb break; 379224090Sdougb else 380224090Sdougb delim = getword(fp, word, sizeof(word)); 381224090Sdougb } 382224090Sdougb 383224090Sdougb return (ISC_R_SUCCESS); 384224090Sdougb} 385224090Sdougb 386224090Sdougbstatic isc_result_t 387224090Sdougbresconf_parsesortlist(irs_resconf_t *conf, FILE *fp) { 388224090Sdougb int delim, res, idx; 389224090Sdougb char word[RESCONFMAXLINELEN]; 390224090Sdougb char *p; 391224090Sdougb 392224090Sdougb delim = getword(fp, word, sizeof(word)); 393224090Sdougb if (strlen(word) == 0U) 394224090Sdougb return (ISC_R_UNEXPECTEDEND); /* Empty line after keyword. */ 395224090Sdougb 396224090Sdougb while (strlen(word) > 0U) { 397224090Sdougb if (conf->sortlistnxt == RESCONFMAXSORTLIST) 398224090Sdougb return (ISC_R_QUOTA); /* Too many values. */ 399224090Sdougb 400224090Sdougb p = strchr(word, '/'); 401224090Sdougb if (p != NULL) 402224090Sdougb *p++ = '\0'; 403224090Sdougb 404224090Sdougb idx = conf->sortlistnxt; 405224090Sdougb res = create_addr(word, &conf->sortlist[idx].addr, 1); 406224090Sdougb if (res != ISC_R_SUCCESS) 407224090Sdougb return (res); 408224090Sdougb 409224090Sdougb if (p != NULL) { 410224090Sdougb res = create_addr(p, &conf->sortlist[idx].mask, 0); 411224090Sdougb if (res != ISC_R_SUCCESS) 412224090Sdougb return (res); 413224090Sdougb } else { 414224090Sdougb /* 415224090Sdougb * Make up a mask. (XXX: is this correct?) 416224090Sdougb */ 417224090Sdougb conf->sortlist[idx].mask = conf->sortlist[idx].addr; 418224090Sdougb memset(&conf->sortlist[idx].mask.type, 0xff, 419224090Sdougb sizeof(conf->sortlist[idx].mask.type)); 420224090Sdougb } 421224090Sdougb 422224090Sdougb conf->sortlistnxt++; 423224090Sdougb 424224090Sdougb if (delim == EOF || delim == '\n') 425224090Sdougb break; 426224090Sdougb else 427224090Sdougb delim = getword(fp, word, sizeof(word)); 428224090Sdougb } 429224090Sdougb 430224090Sdougb return (ISC_R_SUCCESS); 431224090Sdougb} 432224090Sdougb 433224090Sdougbstatic isc_result_t 434224090Sdougbresconf_parseoption(irs_resconf_t *conf, FILE *fp) { 435224090Sdougb int delim; 436224090Sdougb long ndots; 437224090Sdougb char *p; 438224090Sdougb char word[RESCONFMAXLINELEN]; 439224090Sdougb 440224090Sdougb delim = getword(fp, word, sizeof(word)); 441224090Sdougb if (strlen(word) == 0U) 442224090Sdougb return (ISC_R_UNEXPECTEDEND); /* Empty line after keyword. */ 443224090Sdougb 444224090Sdougb while (strlen(word) > 0U) { 445224090Sdougb if (strcmp("debug", word) == 0) { 446224090Sdougb conf->resdebug = 1; 447224090Sdougb } else if (strncmp("ndots:", word, 6) == 0) { 448224090Sdougb ndots = strtol(word + 6, &p, 10); 449224090Sdougb if (*p != '\0') /* Bad string. */ 450224090Sdougb return (ISC_R_UNEXPECTEDTOKEN); 451224090Sdougb if (ndots < 0 || ndots > 0xff) /* Out of range. */ 452224090Sdougb return (ISC_R_RANGE); 453224090Sdougb conf->ndots = (isc_uint8_t)ndots; 454224090Sdougb } 455224090Sdougb 456224090Sdougb if (delim == EOF || delim == '\n') 457224090Sdougb break; 458224090Sdougb else 459224090Sdougb delim = getword(fp, word, sizeof(word)); 460224090Sdougb } 461224090Sdougb 462224090Sdougb return (ISC_R_SUCCESS); 463224090Sdougb} 464224090Sdougb 465224090Sdougbstatic isc_result_t 466224090Sdougbadd_search(irs_resconf_t *conf, char *domain) { 467224090Sdougb irs_resconf_search_t *entry; 468224090Sdougb 469224090Sdougb entry = isc_mem_get(conf->mctx, sizeof(*entry)); 470224090Sdougb if (entry == NULL) 471224090Sdougb return (ISC_R_NOMEMORY); 472224090Sdougb 473224090Sdougb entry->domain = domain; 474224090Sdougb ISC_LINK_INIT(entry, link); 475224090Sdougb ISC_LIST_APPEND(conf->searchlist, entry, link); 476224090Sdougb 477224090Sdougb return (ISC_R_SUCCESS); 478224090Sdougb} 479224090Sdougb 480224090Sdougb/*% parses a file and fills in the data structure. */ 481224090Sdougbisc_result_t 482224090Sdougbirs_resconf_load(isc_mem_t *mctx, const char *filename, irs_resconf_t **confp) 483224090Sdougb{ 484224090Sdougb FILE *fp = NULL; 485224090Sdougb char word[256]; 486262706Serwin isc_result_t rval, ret = ISC_R_SUCCESS; 487224090Sdougb irs_resconf_t *conf; 488224090Sdougb int i, stopchar; 489224090Sdougb 490224090Sdougb REQUIRE(mctx != NULL); 491224090Sdougb REQUIRE(filename != NULL); 492224090Sdougb REQUIRE(strlen(filename) > 0U); 493224090Sdougb REQUIRE(confp != NULL && *confp == NULL); 494224090Sdougb 495224090Sdougb conf = isc_mem_get(mctx, sizeof(*conf)); 496224090Sdougb if (conf == NULL) 497224090Sdougb return (ISC_R_NOMEMORY); 498224090Sdougb 499224090Sdougb conf->mctx = mctx; 500224090Sdougb ISC_LIST_INIT(conf->nameservers); 501224090Sdougb conf->numns = 0; 502224090Sdougb conf->domainname = NULL; 503224090Sdougb conf->searchnxt = 0; 504224090Sdougb conf->resdebug = 0; 505224090Sdougb conf->ndots = 1; 506224090Sdougb for (i = 0; i < RESCONFMAXSEARCH; i++) 507224090Sdougb conf->search[i] = NULL; 508224090Sdougb 509224090Sdougb errno = 0; 510262706Serwin if ((fp = fopen(filename, "r")) != NULL) { 511262706Serwin do { 512262706Serwin stopchar = getword(fp, word, sizeof(word)); 513224090Sdougb if (stopchar == EOF) { 514262706Serwin rval = ISC_R_SUCCESS; 515262706Serwin POST(rval); 516224090Sdougb break; 517224090Sdougb } 518262706Serwin 519262706Serwin if (strlen(word) == 0U) 520262706Serwin rval = ISC_R_SUCCESS; 521262706Serwin else if (strcmp(word, "nameserver") == 0) 522262706Serwin rval = resconf_parsenameserver(conf, fp); 523262706Serwin else if (strcmp(word, "domain") == 0) 524262706Serwin rval = resconf_parsedomain(conf, fp); 525262706Serwin else if (strcmp(word, "search") == 0) 526262706Serwin rval = resconf_parsesearch(conf, fp); 527262706Serwin else if (strcmp(word, "sortlist") == 0) 528262706Serwin rval = resconf_parsesortlist(conf, fp); 529262706Serwin else if (strcmp(word, "options") == 0) 530262706Serwin rval = resconf_parseoption(conf, fp); 531262706Serwin else { 532262706Serwin /* unrecognised word. Ignore entire line */ 533262706Serwin rval = ISC_R_SUCCESS; 534262706Serwin stopchar = eatline(fp); 535262706Serwin if (stopchar == EOF) { 536262706Serwin break; 537262706Serwin } 538262706Serwin } 539262706Serwin if (ret == ISC_R_SUCCESS && rval != ISC_R_SUCCESS) 540262706Serwin ret = rval; 541262706Serwin } while (1); 542262706Serwin 543262706Serwin fclose(fp); 544262706Serwin } else { 545262706Serwin switch (errno) { 546262706Serwin case ENOENT: 547262706Serwin break; 548262706Serwin default: 549262706Serwin isc_mem_put(mctx, conf, sizeof(*conf)); 550262706Serwin return (ISC_R_INVALIDFILE); 551224090Sdougb } 552262706Serwin } 553224090Sdougb 554224090Sdougb /* If we don't find a nameserver fall back to localhost */ 555224090Sdougb if (conf->numns == 0) { 556224090Sdougb INSIST(ISC_LIST_EMPTY(conf->nameservers)); 557224090Sdougb 558224090Sdougb /* XXX: should we catch errors? */ 559224090Sdougb (void)add_server(conf->mctx, "127.0.0.1", &conf->nameservers); 560224090Sdougb (void)add_server(conf->mctx, "::1", &conf->nameservers); 561224090Sdougb } 562224090Sdougb 563224090Sdougb /* 564224090Sdougb * Construct unified search list from domain or configured 565224090Sdougb * search list 566224090Sdougb */ 567224090Sdougb ISC_LIST_INIT(conf->searchlist); 568224090Sdougb if (conf->domainname != NULL) { 569224090Sdougb ret = add_search(conf, conf->domainname); 570224090Sdougb } else if (conf->searchnxt > 0) { 571224090Sdougb for (i = 0; i < conf->searchnxt; i++) { 572224090Sdougb ret = add_search(conf, conf->search[i]); 573224090Sdougb if (ret != ISC_R_SUCCESS) 574224090Sdougb break; 575224090Sdougb } 576224090Sdougb } 577224090Sdougb 578224090Sdougb conf->magic = IRS_RESCONF_MAGIC; 579224090Sdougb 580224090Sdougb if (ret != ISC_R_SUCCESS) 581224090Sdougb irs_resconf_destroy(&conf); 582262706Serwin else { 583262706Serwin if (fp == NULL) 584262706Serwin ret = ISC_R_FILENOTFOUND; 585224090Sdougb *confp = conf; 586262706Serwin } 587224090Sdougb 588224090Sdougb return (ret); 589224090Sdougb} 590224090Sdougb 591224090Sdougbvoid 592224090Sdougbirs_resconf_destroy(irs_resconf_t **confp) { 593224090Sdougb irs_resconf_t *conf; 594224090Sdougb isc_sockaddr_t *address; 595224090Sdougb irs_resconf_search_t *searchentry; 596224090Sdougb int i; 597224090Sdougb 598224090Sdougb REQUIRE(confp != NULL); 599224090Sdougb conf = *confp; 600224090Sdougb REQUIRE(IRS_RESCONF_VALID(conf)); 601224090Sdougb 602224090Sdougb while ((searchentry = ISC_LIST_HEAD(conf->searchlist)) != NULL) { 603224090Sdougb ISC_LIST_UNLINK(conf->searchlist, searchentry, link); 604224090Sdougb isc_mem_put(conf->mctx, searchentry, sizeof(*searchentry)); 605224090Sdougb } 606224090Sdougb 607224090Sdougb while ((address = ISC_LIST_HEAD(conf->nameservers)) != NULL) { 608224090Sdougb ISC_LIST_UNLINK(conf->nameservers, address, link); 609224090Sdougb isc_mem_put(conf->mctx, address, sizeof(*address)); 610224090Sdougb } 611224090Sdougb 612224090Sdougb if (conf->domainname != NULL) 613224090Sdougb isc_mem_free(conf->mctx, conf->domainname); 614224090Sdougb 615224090Sdougb for (i = 0; i < RESCONFMAXSEARCH; i++) { 616224090Sdougb if (conf->search[i] != NULL) 617224090Sdougb isc_mem_free(conf->mctx, conf->search[i]); 618224090Sdougb } 619224090Sdougb 620224090Sdougb isc_mem_put(conf->mctx, conf, sizeof(*conf)); 621224090Sdougb 622224090Sdougb *confp = NULL; 623224090Sdougb} 624224090Sdougb 625224090Sdougbisc_sockaddrlist_t * 626224090Sdougbirs_resconf_getnameservers(irs_resconf_t *conf) { 627224090Sdougb REQUIRE(IRS_RESCONF_VALID(conf)); 628224090Sdougb 629224090Sdougb return (&conf->nameservers); 630224090Sdougb} 631224090Sdougb 632224090Sdougbirs_resconf_searchlist_t * 633224090Sdougbirs_resconf_getsearchlist(irs_resconf_t *conf) { 634224090Sdougb REQUIRE(IRS_RESCONF_VALID(conf)); 635224090Sdougb 636224090Sdougb return (&conf->searchlist); 637224090Sdougb} 638224090Sdougb 639224090Sdougbunsigned int 640224090Sdougbirs_resconf_getndots(irs_resconf_t *conf) { 641224090Sdougb REQUIRE(IRS_RESCONF_VALID(conf)); 642224090Sdougb 643224090Sdougb return ((unsigned int)conf->ndots); 644224090Sdougb} 645