2 * Copyright (C) 2004-2008, 2011 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000-2003 Internet Software Consortium.
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
18 /* $Id: lwconfig.c,v 1.48.436.2 2011-03-12 04:59:19 tbox Exp $ */
23 * Module for parsing resolv.conf files.
25 * lwres_conf_init() creates an empty lwres_conf_t structure for
26 * lightweight resolver context ctx.
28 * lwres_conf_clear() frees up all the internal memory used by that
29 * lwres_conf_t structure in resolver context ctx.
31 * lwres_conf_parse() opens the file filename and parses it to initialise
32 * the resolver context ctx's lwres_conf_t structure.
34 * lwres_conf_print() prints the lwres_conf_t structure for resolver
35 * context ctx to the FILE fp.
37 * \section lwconfig_return Return Values
39 * lwres_conf_parse() returns #LWRES_R_SUCCESS if it successfully read and
40 * parsed filename. It returns #LWRES_R_FAILURE if filename could not be
41 * opened or contained incorrect resolver statements.
43 * lwres_conf_print() returns #LWRES_R_SUCCESS unless an error occurred
44 * when converting the network addresses to a numeric host address
45 * string. If this happens, the function returns #LWRES_R_FAILURE.
47 * \section lwconfig_see See Also
49 * stdio(3), \link resolver resolver \endlink
51 * \section files Files
66 #include <lwres/lwbuffer.h>
67 #include <lwres/lwres.h>
68 #include <lwres/net.h>
69 #include <lwres/result.h>
72 #include "context_p.h"
75 #if ! defined(NS_INADDRSZ)
79 #if ! defined(NS_IN6ADDRSZ)
80 #define NS_IN6ADDRSZ 16
84 lwres_conf_parsenameserver(lwres_context_t *ctx, FILE *fp);
87 lwres_conf_parselwserver(lwres_context_t *ctx, FILE *fp);
90 lwres_conf_parsedomain(lwres_context_t *ctx, FILE *fp);
93 lwres_conf_parsesearch(lwres_context_t *ctx, FILE *fp);
96 lwres_conf_parsesortlist(lwres_context_t *ctx, FILE *fp);
99 lwres_conf_parseoption(lwres_context_t *ctx, FILE *fp);
102 lwres_resetaddr(lwres_addr_t *addr);
104 static lwres_result_t
105 lwres_create_addr(const char *buff, lwres_addr_t *addr, int convert_zero);
107 static int lwresaddr2af(int lwresaddrtype);
111 lwresaddr2af(int lwresaddrtype)
115 switch (lwresaddrtype) {
116 case LWRES_ADDRTYPE_V4:
120 case LWRES_ADDRTYPE_V6:
130 * Eat characters from FP until EOL or EOF. Returns EOF or '\n'
137 while (ch != '\n' && ch != EOF)
145 * Eats white space up to next newline or non-whitespace character (of
146 * EOF). Returns the last character read. Comments are considered white
154 while (ch != '\n' && ch != EOF && isspace((unsigned char)ch))
157 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 lwres_resetaddr(lwres_addr_t *addr) {
202 REQUIRE(addr != NULL);
204 memset(addr->address, 0, LWRES_ADDR_MAXLEN);
210 lwres_strdup(lwres_context_t *ctx, const char *str) {
213 REQUIRE(str != NULL);
214 REQUIRE(strlen(str) > 0U);
216 p = CTXMALLOC(strlen(str) + 1);
223 /*% intializes data structure for subsequent config parsing. */
225 lwres_conf_init(lwres_context_t *ctx) {
227 lwres_conf_t *confdata;
229 REQUIRE(ctx != NULL);
230 confdata = &ctx->confdata;
232 confdata->nsnext = 0;
233 confdata->lwnext = 0;
234 confdata->domainname = NULL;
235 confdata->searchnxt = 0;
236 confdata->sortlistnxt = 0;
237 confdata->resdebug = 0;
239 confdata->no_tld_query = 0;
241 for (i = 0; i < LWRES_CONFMAXNAMESERVERS; i++)
242 lwres_resetaddr(&confdata->nameservers[i]);
244 for (i = 0; i < LWRES_CONFMAXSEARCH; i++)
245 confdata->search[i] = NULL;
247 for (i = 0; i < LWRES_CONFMAXSORTLIST; i++) {
248 lwres_resetaddr(&confdata->sortlist[i].addr);
249 lwres_resetaddr(&confdata->sortlist[i].mask);
253 /*% Frees up all the internal memory used by the config data structure, returning it to the lwres_context_t. */
255 lwres_conf_clear(lwres_context_t *ctx) {
257 lwres_conf_t *confdata;
259 REQUIRE(ctx != NULL);
260 confdata = &ctx->confdata;
262 for (i = 0; i < confdata->nsnext; i++)
263 lwres_resetaddr(&confdata->nameservers[i]);
265 if (confdata->domainname != NULL) {
266 CTXFREE(confdata->domainname,
267 strlen(confdata->domainname) + 1);
268 confdata->domainname = NULL;
271 for (i = 0; i < confdata->searchnxt; i++) {
272 if (confdata->search[i] != NULL) {
273 CTXFREE(confdata->search[i],
274 strlen(confdata->search[i]) + 1);
275 confdata->search[i] = NULL;
279 for (i = 0; i < LWRES_CONFMAXSORTLIST; i++) {
280 lwres_resetaddr(&confdata->sortlist[i].addr);
281 lwres_resetaddr(&confdata->sortlist[i].mask);
284 confdata->nsnext = 0;
285 confdata->lwnext = 0;
286 confdata->domainname = NULL;
287 confdata->searchnxt = 0;
288 confdata->sortlistnxt = 0;
289 confdata->resdebug = 0;
291 confdata->no_tld_query = 0;
294 static lwres_result_t
295 lwres_conf_parsenameserver(lwres_context_t *ctx, FILE *fp) {
296 char word[LWRES_CONFMAXLINELEN];
298 lwres_conf_t *confdata;
299 lwres_addr_t address;
301 confdata = &ctx->confdata;
303 if (confdata->nsnext == LWRES_CONFMAXNAMESERVERS)
304 return (LWRES_R_SUCCESS);
306 res = getword(fp, word, sizeof(word));
307 if (strlen(word) == 0U)
308 return (LWRES_R_FAILURE); /* Nothing on line. */
309 else if (res == ' ' || res == '\t')
312 if (res != EOF && res != '\n')
313 return (LWRES_R_FAILURE); /* Extra junk on line. */
315 res = lwres_create_addr(word, &address, 1);
316 if (res == LWRES_R_SUCCESS &&
317 ((address.family == LWRES_ADDRTYPE_V4 && ctx->use_ipv4 == 1) ||
318 (address.family == LWRES_ADDRTYPE_V6 && ctx->use_ipv6 == 1))) {
319 confdata->nameservers[confdata->nsnext++] = address;
322 return (LWRES_R_SUCCESS);
325 static lwres_result_t
326 lwres_conf_parselwserver(lwres_context_t *ctx, FILE *fp) {
327 char word[LWRES_CONFMAXLINELEN];
329 lwres_conf_t *confdata;
331 confdata = &ctx->confdata;
333 if (confdata->lwnext == LWRES_CONFMAXLWSERVERS)
334 return (LWRES_R_SUCCESS);
336 res = getword(fp, word, sizeof(word));
337 if (strlen(word) == 0U)
338 return (LWRES_R_FAILURE); /* Nothing on line. */
339 else if (res == ' ' || res == '\t')
342 if (res != EOF && res != '\n')
343 return (LWRES_R_FAILURE); /* Extra junk on line. */
345 res = lwres_create_addr(word,
346 &confdata->lwservers[confdata->lwnext++], 1);
347 if (res != LWRES_R_SUCCESS)
350 return (LWRES_R_SUCCESS);
353 static lwres_result_t
354 lwres_conf_parsedomain(lwres_context_t *ctx, FILE *fp) {
355 char word[LWRES_CONFMAXLINELEN];
357 lwres_conf_t *confdata;
359 confdata = &ctx->confdata;
361 res = getword(fp, word, sizeof(word));
362 if (strlen(word) == 0U)
363 return (LWRES_R_FAILURE); /* Nothing else on line. */
364 else if (res == ' ' || res == '\t')
367 if (res != EOF && res != '\n')
368 return (LWRES_R_FAILURE); /* Extra junk on line. */
370 if (confdata->domainname != NULL)
371 CTXFREE(confdata->domainname,
372 strlen(confdata->domainname) + 1); /* */
375 * Search and domain are mutually exclusive.
377 for (i = 0; i < LWRES_CONFMAXSEARCH; i++) {
378 if (confdata->search[i] != NULL) {
379 CTXFREE(confdata->search[i],
380 strlen(confdata->search[i])+1);
381 confdata->search[i] = NULL;
384 confdata->searchnxt = 0;
386 confdata->domainname = lwres_strdup(ctx, word);
388 if (confdata->domainname == NULL)
389 return (LWRES_R_FAILURE);
391 return (LWRES_R_SUCCESS);
394 static lwres_result_t
395 lwres_conf_parsesearch(lwres_context_t *ctx, FILE *fp) {
397 char word[LWRES_CONFMAXLINELEN];
398 lwres_conf_t *confdata;
400 confdata = &ctx->confdata;
402 if (confdata->domainname != NULL) {
404 * Search and domain are mutually exclusive.
406 CTXFREE(confdata->domainname,
407 strlen(confdata->domainname) + 1);
408 confdata->domainname = NULL;
412 * Remove any previous search definitions.
414 for (idx = 0; idx < LWRES_CONFMAXSEARCH; idx++) {
415 if (confdata->search[idx] != NULL) {
416 CTXFREE(confdata->search[idx],
417 strlen(confdata->search[idx])+1);
418 confdata->search[idx] = NULL;
421 confdata->searchnxt = 0;
423 delim = getword(fp, word, sizeof(word));
424 if (strlen(word) == 0U)
425 return (LWRES_R_FAILURE); /* Nothing else on line. */
428 while (strlen(word) > 0U) {
429 if (confdata->searchnxt == LWRES_CONFMAXSEARCH)
430 goto ignore; /* Too many domains. */
432 confdata->search[idx] = lwres_strdup(ctx, word);
433 if (confdata->search[idx] == NULL)
434 return (LWRES_R_FAILURE);
436 confdata->searchnxt++;
439 if (delim == EOF || delim == '\n')
442 delim = getword(fp, word, sizeof(word));
445 return (LWRES_R_SUCCESS);
448 static lwres_result_t
449 lwres_create_addr(const char *buffer, lwres_addr_t *addr, int convert_zero) {
453 if (lwres_net_aton(buffer, &v4) == 1) {
455 unsigned char zeroaddress[] = {0, 0, 0, 0};
456 unsigned char loopaddress[] = {127, 0, 0, 1};
457 if (memcmp(&v4, zeroaddress, 4) == 0)
458 memcpy(&v4, loopaddress, 4);
460 addr->family = LWRES_ADDRTYPE_V4;
461 addr->length = NS_INADDRSZ;
462 memcpy((void *)addr->address, &v4, NS_INADDRSZ);
464 } else if (lwres_net_pton(AF_INET6, buffer, &v6) == 1) {
465 addr->family = LWRES_ADDRTYPE_V6;
466 addr->length = NS_IN6ADDRSZ;
467 memcpy((void *)addr->address, &v6, NS_IN6ADDRSZ);
469 return (LWRES_R_FAILURE); /* Unrecognised format. */
472 return (LWRES_R_SUCCESS);
475 static lwres_result_t
476 lwres_conf_parsesortlist(lwres_context_t *ctx, FILE *fp) {
478 char word[LWRES_CONFMAXLINELEN];
480 lwres_conf_t *confdata;
482 confdata = &ctx->confdata;
484 delim = getword(fp, word, sizeof(word));
485 if (strlen(word) == 0U)
486 return (LWRES_R_FAILURE); /* Empty line after keyword. */
488 while (strlen(word) > 0U) {
489 if (confdata->sortlistnxt == LWRES_CONFMAXSORTLIST)
490 return (LWRES_R_FAILURE); /* Too many values. */
492 p = strchr(word, '/');
496 idx = confdata->sortlistnxt;
497 res = lwres_create_addr(word, &confdata->sortlist[idx].addr, 1);
498 if (res != LWRES_R_SUCCESS)
502 res = lwres_create_addr(p,
503 &confdata->sortlist[idx].mask,
505 if (res != LWRES_R_SUCCESS)
511 confdata->sortlist[idx].mask =
512 confdata->sortlist[idx].addr;
514 memset(&confdata->sortlist[idx].mask.address, 0xff,
515 confdata->sortlist[idx].addr.length);
518 confdata->sortlistnxt++;
520 if (delim == EOF || delim == '\n')
523 delim = getword(fp, word, sizeof(word));
526 return (LWRES_R_SUCCESS);
529 static lwres_result_t
530 lwres_conf_parseoption(lwres_context_t *ctx, FILE *fp) {
534 char word[LWRES_CONFMAXLINELEN];
535 lwres_conf_t *confdata;
537 REQUIRE(ctx != NULL);
538 confdata = &ctx->confdata;
540 delim = getword(fp, word, sizeof(word));
541 if (strlen(word) == 0U)
542 return (LWRES_R_FAILURE); /* Empty line after keyword. */
544 while (strlen(word) > 0U) {
545 if (strcmp("debug", word) == 0) {
546 confdata->resdebug = 1;
547 } else if (strcmp("no_tld_query", word) == 0) {
548 confdata->no_tld_query = 1;
549 } else if (strncmp("ndots:", word, 6) == 0) {
550 ndots = strtol(word + 6, &p, 10);
551 if (*p != '\0') /* Bad string. */
552 return (LWRES_R_FAILURE);
553 if (ndots < 0 || ndots > 0xff) /* Out of range. */
554 return (LWRES_R_FAILURE);
555 confdata->ndots = (lwres_uint8_t)ndots;
558 if (delim == EOF || delim == '\n')
561 delim = getword(fp, word, sizeof(word));
564 return (LWRES_R_SUCCESS);
567 /*% parses a file and fills in the data structure. */
569 lwres_conf_parse(lwres_context_t *ctx, const char *filename) {
572 lwres_result_t rval, ret;
573 lwres_conf_t *confdata;
576 REQUIRE(ctx != NULL);
577 confdata = &ctx->confdata;
579 REQUIRE(filename != NULL);
580 REQUIRE(strlen(filename) > 0U);
581 REQUIRE(confdata != NULL);
584 if ((fp = fopen(filename, "r")) == NULL)
585 return (LWRES_R_NOTFOUND);
587 ret = LWRES_R_SUCCESS;
589 stopchar = getword(fp, word, sizeof(word));
590 if (stopchar == EOF) {
591 rval = LWRES_R_SUCCESS;
596 if (strlen(word) == 0U)
597 rval = LWRES_R_SUCCESS;
598 else if (strcmp(word, "nameserver") == 0)
599 rval = lwres_conf_parsenameserver(ctx, fp);
600 else if (strcmp(word, "lwserver") == 0)
601 rval = lwres_conf_parselwserver(ctx, fp);
602 else if (strcmp(word, "domain") == 0)
603 rval = lwres_conf_parsedomain(ctx, fp);
604 else if (strcmp(word, "search") == 0)
605 rval = lwres_conf_parsesearch(ctx, fp);
606 else if (strcmp(word, "sortlist") == 0)
607 rval = lwres_conf_parsesortlist(ctx, fp);
608 else if (strcmp(word, "options") == 0)
609 rval = lwres_conf_parseoption(ctx, fp);
611 /* unrecognised word. Ignore entire line */
612 rval = LWRES_R_SUCCESS;
613 stopchar = eatline(fp);
614 if (stopchar == EOF) {
618 if (ret == LWRES_R_SUCCESS && rval != LWRES_R_SUCCESS)
627 /*% Prints the config data structure to the FILE. */
629 lwres_conf_print(lwres_context_t *ctx, FILE *fp) {
632 char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
634 lwres_conf_t *confdata;
635 lwres_addr_t tmpaddr;
637 REQUIRE(ctx != NULL);
638 confdata = &ctx->confdata;
640 REQUIRE(confdata->nsnext <= LWRES_CONFMAXNAMESERVERS);
642 for (i = 0; i < confdata->nsnext; i++) {
643 af = lwresaddr2af(confdata->nameservers[i].family);
645 p = lwres_net_ntop(af, confdata->nameservers[i].address,
648 return (LWRES_R_FAILURE);
650 fprintf(fp, "nameserver %s\n", tmp);
653 for (i = 0; i < confdata->lwnext; i++) {
654 af = lwresaddr2af(confdata->lwservers[i].family);
656 p = lwres_net_ntop(af, confdata->lwservers[i].address,
659 return (LWRES_R_FAILURE);
661 fprintf(fp, "lwserver %s\n", tmp);
664 if (confdata->domainname != NULL) {
665 fprintf(fp, "domain %s\n", confdata->domainname);
666 } else if (confdata->searchnxt > 0) {
667 REQUIRE(confdata->searchnxt <= LWRES_CONFMAXSEARCH);
669 fprintf(fp, "search");
670 for (i = 0; i < confdata->searchnxt; i++)
671 fprintf(fp, " %s", confdata->search[i]);
675 REQUIRE(confdata->sortlistnxt <= LWRES_CONFMAXSORTLIST);
677 if (confdata->sortlistnxt > 0) {
678 fputs("sortlist", fp);
679 for (i = 0; i < confdata->sortlistnxt; i++) {
680 af = lwresaddr2af(confdata->sortlist[i].addr.family);
682 p = lwres_net_ntop(af,
683 confdata->sortlist[i].addr.address,
686 return (LWRES_R_FAILURE);
688 fprintf(fp, " %s", tmp);
690 tmpaddr = confdata->sortlist[i].mask;
691 memset(&tmpaddr.address, 0xff, tmpaddr.length);
693 if (memcmp(&tmpaddr.address,
694 confdata->sortlist[i].mask.address,
695 confdata->sortlist[i].mask.length) != 0) {
697 confdata->sortlist[i].mask.family);
700 confdata->sortlist[i].mask.address,
703 return (LWRES_R_FAILURE);
705 fprintf(fp, "/%s", tmp);
711 if (confdata->resdebug)
712 fprintf(fp, "options debug\n");
714 if (confdata->ndots > 0)
715 fprintf(fp, "options ndots:%d\n", confdata->ndots);
717 if (confdata->no_tld_query)
718 fprintf(fp, "options no_tld_query\n");
720 return (LWRES_R_SUCCESS);
723 /*% Returns a pointer to the current config structure. */
725 lwres_conf_get(lwres_context_t *ctx) {
726 REQUIRE(ctx != NULL);
728 return (&ctx->confdata);