2 * Copyright (C) 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
19 /*! \file resconf.c */
22 * Module for parsing resolv.conf files (largely derived from lwconfig.c).
24 * irs_resconf_load() opens the file filename and parses it to initialize
25 * the configuration structure.
27 * \section lwconfig_return Return Values
29 * irs_resconf_load() returns #IRS_R_SUCCESS if it successfully read and
30 * parsed filename. It returns a non-0 error code if filename could not be
31 * opened or contained incorrect resolver statements.
33 * \section lwconfig_see See Also
35 * stdio(3), \link resolver resolver \endlink
37 * \section files Files
44 #include <sys/types.h>
45 #include <sys/socket.h>
54 #include <isc/magic.h>
56 #include <isc/netaddr.h>
57 #include <isc/sockaddr.h>
60 #include <irs/resconf.h>
62 #define IRS_RESCONF_MAGIC ISC_MAGIC('R', 'E', 'S', 'c')
63 #define IRS_RESCONF_VALID(c) ISC_MAGIC_VALID(c, IRS_RESCONF_MAGIC)
69 #if ! defined(NS_INADDRSZ)
73 #if ! defined(NS_IN6ADDRSZ)
74 #define NS_IN6ADDRSZ 16
78 * resolv.conf parameters
81 #define RESCONFMAXNAMESERVERS 3 /*%< max 3 "nameserver" entries */
82 #define RESCONFMAXSEARCH 8 /*%< max 8 domains in "search" entry */
83 #define RESCONFMAXLINELEN 256 /*%< max size of a line */
84 #define RESCONFMAXSORTLIST 10 /*%< max 10 */
87 * configuration data structure
92 * The configuration data is a thread-specific object, and does not
98 isc_sockaddrlist_t nameservers;
99 unsigned int numns; /*%< number of configured servers */
102 char *search[RESCONFMAXSEARCH];
103 isc_uint8_t searchnxt; /*%< index for next free slot */
105 irs_resconf_searchlist_t searchlist;
109 /*% mask has a non-zero 'family' if set */
111 } sortlist[RESCONFMAXSORTLIST];
112 isc_uint8_t sortlistnxt;
114 /*%< non-zero if 'options debug' set */
115 isc_uint8_t resdebug;
116 /*%< set to n in 'options ndots:n' */
121 resconf_parsenameserver(irs_resconf_t *conf, FILE *fp);
123 resconf_parsedomain(irs_resconf_t *conf, FILE *fp);
125 resconf_parsesearch(irs_resconf_t *conf, FILE *fp);
127 resconf_parsesortlist(irs_resconf_t *conf, FILE *fp);
129 resconf_parseoption(irs_resconf_t *ctx, FILE *fp);
132 * Eat characters from FP until EOL or EOF. Returns EOF or '\n'
139 while (ch != '\n' && ch != EOF)
146 * Eats white space up to next newline or non-whitespace character (of
147 * EOF). Returns the last character read. Comments are considered white
155 while (ch != '\n' && ch != EOF && isspace((unsigned char)ch))
158 if (ch == ';' || ch == '#')
165 * Skip over any leading whitespace and then read in the next sequence of
166 * non-whitespace characters. In this context newline is not considered
167 * whitespace. Returns EOF on end-of-file, or the character
168 * that caused the reading to stop.
171 getword(FILE *fp, char *buffer, size_t size) {
175 REQUIRE(buffer != NULL);
188 if (ch == EOF || isspace((unsigned char)ch))
190 else if ((size_t) (p - buffer) == size - 1)
191 return (EOF); /* Not enough space. */
201 add_server(isc_mem_t *mctx, const char *address_str,
202 isc_sockaddrlist_t *nameservers)
205 isc_sockaddr_t *address = NULL;
206 struct addrinfo hints, *res;
207 isc_result_t result = ISC_R_SUCCESS;
210 memset(&hints, 0, sizeof(hints));
211 hints.ai_family = AF_UNSPEC;
212 hints.ai_socktype = SOCK_DGRAM;
213 hints.ai_protocol = IPPROTO_UDP;
214 hints.ai_flags = AI_NUMERICHOST;
215 error = getaddrinfo(address_str, "53", &hints, &res);
217 return (ISC_R_BADADDRESSFORM);
219 /* XXX: special case: treat all-0 IPv4 address as loopback */
220 if (res->ai_family == AF_INET) {
222 unsigned char zeroaddress[] = {0, 0, 0, 0};
223 unsigned char loopaddress[] = {127, 0, 0, 1};
225 v4 = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
226 if (memcmp(v4, zeroaddress, 4) == 0)
227 memcpy(v4, loopaddress, 4);
230 address = isc_mem_get(mctx, sizeof(*address));
231 if (address == NULL) {
232 result = ISC_R_NOMEMORY;
235 if (res->ai_addrlen > sizeof(address->type)) {
236 isc_mem_put(mctx, address, sizeof(*address));
237 result = ISC_R_RANGE;
240 address->length = res->ai_addrlen;
241 memcpy(&address->type.sa, res->ai_addr, res->ai_addrlen);
242 ISC_LINK_INIT(address, link);
243 ISC_LIST_APPEND(*nameservers, address, link);
252 create_addr(const char *buffer, isc_netaddr_t *addr, int convert_zero) {
256 if (inet_aton(buffer, &v4) == 1) {
258 unsigned char zeroaddress[] = {0, 0, 0, 0};
259 unsigned char loopaddress[] = {127, 0, 0, 1};
260 if (memcmp(&v4, zeroaddress, 4) == 0)
261 memcpy(&v4, loopaddress, 4);
263 addr->family = AF_INET;
264 memcpy(&addr->type.in, &v4, NS_INADDRSZ);
266 } else if (inet_pton(AF_INET6, buffer, &v6) == 1) {
267 addr->family = AF_INET6;
268 memcpy(&addr->type.in6, &v6, NS_IN6ADDRSZ);
271 return (ISC_R_BADADDRESSFORM); /* Unrecognised format. */
273 return (ISC_R_SUCCESS);
277 resconf_parsenameserver(irs_resconf_t *conf, FILE *fp) {
278 char word[RESCONFMAXLINELEN];
282 if (conf->numns == RESCONFMAXNAMESERVERS)
283 return (ISC_R_SUCCESS);
285 cp = getword(fp, word, sizeof(word));
286 if (strlen(word) == 0U)
287 return (ISC_R_UNEXPECTEDEND); /* Nothing on line. */
288 else if (cp == ' ' || cp == '\t')
291 if (cp != EOF && cp != '\n')
292 return (ISC_R_UNEXPECTEDTOKEN); /* Extra junk on line. */
294 result = add_server(conf->mctx, word, &conf->nameservers);
295 if (result != ISC_R_SUCCESS)
299 return (ISC_R_SUCCESS);
303 resconf_parsedomain(irs_resconf_t *conf, FILE *fp) {
304 char word[RESCONFMAXLINELEN];
307 res = getword(fp, word, sizeof(word));
308 if (strlen(word) == 0U)
309 return (ISC_R_UNEXPECTEDEND); /* Nothing else on line. */
310 else if (res == ' ' || res == '\t')
313 if (res != EOF && res != '\n')
314 return (ISC_R_UNEXPECTEDTOKEN); /* Extra junk on line. */
316 if (conf->domainname != NULL)
317 isc_mem_free(conf->mctx, conf->domainname);
320 * Search and domain are mutually exclusive.
322 for (i = 0; i < RESCONFMAXSEARCH; i++) {
323 if (conf->search[i] != NULL) {
324 isc_mem_free(conf->mctx, conf->search[i]);
325 conf->search[i] = NULL;
330 conf->domainname = isc_mem_strdup(conf->mctx, word);
331 if (conf->domainname == NULL)
332 return (ISC_R_NOMEMORY);
334 return (ISC_R_SUCCESS);
338 resconf_parsesearch(irs_resconf_t *conf, FILE *fp) {
340 char word[RESCONFMAXLINELEN];
342 if (conf->domainname != NULL) {
344 * Search and domain are mutually exclusive.
346 isc_mem_free(conf->mctx, conf->domainname);
347 conf->domainname = NULL;
351 * Remove any previous search definitions.
353 for (idx = 0; idx < RESCONFMAXSEARCH; idx++) {
354 if (conf->search[idx] != NULL) {
355 isc_mem_free(conf->mctx, conf->search[idx]);
356 conf->search[idx] = NULL;
361 delim = getword(fp, word, sizeof(word));
362 if (strlen(word) == 0U)
363 return (ISC_R_UNEXPECTEDEND); /* Nothing else on line. */
366 while (strlen(word) > 0U) {
367 if (conf->searchnxt == RESCONFMAXSEARCH)
368 goto ignore; /* Too many domains. */
370 conf->search[idx] = isc_mem_strdup(conf->mctx, word);
371 if (conf->search[idx] == NULL)
372 return (ISC_R_NOMEMORY);
377 if (delim == EOF || delim == '\n')
380 delim = getword(fp, word, sizeof(word));
383 return (ISC_R_SUCCESS);
387 resconf_parsesortlist(irs_resconf_t *conf, FILE *fp) {
389 char word[RESCONFMAXLINELEN];
392 delim = getword(fp, word, sizeof(word));
393 if (strlen(word) == 0U)
394 return (ISC_R_UNEXPECTEDEND); /* Empty line after keyword. */
396 while (strlen(word) > 0U) {
397 if (conf->sortlistnxt == RESCONFMAXSORTLIST)
398 return (ISC_R_QUOTA); /* Too many values. */
400 p = strchr(word, '/');
404 idx = conf->sortlistnxt;
405 res = create_addr(word, &conf->sortlist[idx].addr, 1);
406 if (res != ISC_R_SUCCESS)
410 res = create_addr(p, &conf->sortlist[idx].mask, 0);
411 if (res != ISC_R_SUCCESS)
415 * Make up a mask. (XXX: is this correct?)
417 conf->sortlist[idx].mask = conf->sortlist[idx].addr;
418 memset(&conf->sortlist[idx].mask.type, 0xff,
419 sizeof(conf->sortlist[idx].mask.type));
424 if (delim == EOF || delim == '\n')
427 delim = getword(fp, word, sizeof(word));
430 return (ISC_R_SUCCESS);
434 resconf_parseoption(irs_resconf_t *conf, FILE *fp) {
438 char word[RESCONFMAXLINELEN];
440 delim = getword(fp, word, sizeof(word));
441 if (strlen(word) == 0U)
442 return (ISC_R_UNEXPECTEDEND); /* Empty line after keyword. */
444 while (strlen(word) > 0U) {
445 if (strcmp("debug", word) == 0) {
447 } else if (strncmp("ndots:", word, 6) == 0) {
448 ndots = strtol(word + 6, &p, 10);
449 if (*p != '\0') /* Bad string. */
450 return (ISC_R_UNEXPECTEDTOKEN);
451 if (ndots < 0 || ndots > 0xff) /* Out of range. */
452 return (ISC_R_RANGE);
453 conf->ndots = (isc_uint8_t)ndots;
456 if (delim == EOF || delim == '\n')
459 delim = getword(fp, word, sizeof(word));
462 return (ISC_R_SUCCESS);
466 add_search(irs_resconf_t *conf, char *domain) {
467 irs_resconf_search_t *entry;
469 entry = isc_mem_get(conf->mctx, sizeof(*entry));
471 return (ISC_R_NOMEMORY);
473 entry->domain = domain;
474 ISC_LINK_INIT(entry, link);
475 ISC_LIST_APPEND(conf->searchlist, entry, link);
477 return (ISC_R_SUCCESS);
480 /*% parses a file and fills in the data structure. */
482 irs_resconf_load(isc_mem_t *mctx, const char *filename, irs_resconf_t **confp)
486 isc_result_t rval, ret;
490 REQUIRE(mctx != NULL);
491 REQUIRE(filename != NULL);
492 REQUIRE(strlen(filename) > 0U);
493 REQUIRE(confp != NULL && *confp == NULL);
495 conf = isc_mem_get(mctx, sizeof(*conf));
497 return (ISC_R_NOMEMORY);
500 ISC_LIST_INIT(conf->nameservers);
502 conf->domainname = NULL;
506 for (i = 0; i < RESCONFMAXSEARCH; i++)
507 conf->search[i] = NULL;
510 if ((fp = fopen(filename, "r")) == NULL) {
511 isc_mem_put(mctx, conf, sizeof(*conf));
512 return (ISC_R_INVALIDFILE);
517 stopchar = getword(fp, word, sizeof(word));
518 if (stopchar == EOF) {
519 rval = ISC_R_SUCCESS;
524 if (strlen(word) == 0U)
525 rval = ISC_R_SUCCESS;
526 else if (strcmp(word, "nameserver") == 0)
527 rval = resconf_parsenameserver(conf, fp);
528 else if (strcmp(word, "domain") == 0)
529 rval = resconf_parsedomain(conf, fp);
530 else if (strcmp(word, "search") == 0)
531 rval = resconf_parsesearch(conf, fp);
532 else if (strcmp(word, "sortlist") == 0)
533 rval = resconf_parsesortlist(conf, fp);
534 else if (strcmp(word, "options") == 0)
535 rval = resconf_parseoption(conf, fp);
537 /* unrecognised word. Ignore entire line */
538 rval = ISC_R_SUCCESS;
539 stopchar = eatline(fp);
540 if (stopchar == EOF) {
544 if (ret == ISC_R_SUCCESS && rval != ISC_R_SUCCESS)
550 /* If we don't find a nameserver fall back to localhost */
551 if (conf->numns == 0) {
552 INSIST(ISC_LIST_EMPTY(conf->nameservers));
554 /* XXX: should we catch errors? */
555 (void)add_server(conf->mctx, "127.0.0.1", &conf->nameservers);
556 (void)add_server(conf->mctx, "::1", &conf->nameservers);
560 * Construct unified search list from domain or configured
563 ISC_LIST_INIT(conf->searchlist);
564 if (conf->domainname != NULL) {
565 ret = add_search(conf, conf->domainname);
566 } else if (conf->searchnxt > 0) {
567 for (i = 0; i < conf->searchnxt; i++) {
568 ret = add_search(conf, conf->search[i]);
569 if (ret != ISC_R_SUCCESS)
574 conf->magic = IRS_RESCONF_MAGIC;
576 if (ret != ISC_R_SUCCESS)
577 irs_resconf_destroy(&conf);
585 irs_resconf_destroy(irs_resconf_t **confp) {
587 isc_sockaddr_t *address;
588 irs_resconf_search_t *searchentry;
591 REQUIRE(confp != NULL);
593 REQUIRE(IRS_RESCONF_VALID(conf));
595 while ((searchentry = ISC_LIST_HEAD(conf->searchlist)) != NULL) {
596 ISC_LIST_UNLINK(conf->searchlist, searchentry, link);
597 isc_mem_put(conf->mctx, searchentry, sizeof(*searchentry));
600 while ((address = ISC_LIST_HEAD(conf->nameservers)) != NULL) {
601 ISC_LIST_UNLINK(conf->nameservers, address, link);
602 isc_mem_put(conf->mctx, address, sizeof(*address));
605 if (conf->domainname != NULL)
606 isc_mem_free(conf->mctx, conf->domainname);
608 for (i = 0; i < RESCONFMAXSEARCH; i++) {
609 if (conf->search[i] != NULL)
610 isc_mem_free(conf->mctx, conf->search[i]);
613 isc_mem_put(conf->mctx, conf, sizeof(*conf));
619 irs_resconf_getnameservers(irs_resconf_t *conf) {
620 REQUIRE(IRS_RESCONF_VALID(conf));
622 return (&conf->nameservers);
625 irs_resconf_searchlist_t *
626 irs_resconf_getsearchlist(irs_resconf_t *conf) {
627 REQUIRE(IRS_RESCONF_VALID(conf));
629 return (&conf->searchlist);
633 irs_resconf_getndots(irs_resconf_t *conf) {
634 REQUIRE(IRS_RESCONF_VALID(conf));
636 return ((unsigned int)conf->ndots);