Sync usage with man page.
[netbsd-mini2440.git] / external / bsd / bind / dist / lib / lwres / lwconfig.c
blobe1a0e973cfb97d0f130abc1a09182b612d0fce78
1 /* $NetBSD$ */
3 /*
4 * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 2000-2003 Internet Software Consortium.
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
20 /* Id: lwconfig.c,v 1.48 2008/12/17 23:47:58 tbox Exp */
22 /*! \file */
24 /**
25 * Module for parsing resolv.conf files.
27 * lwres_conf_init() creates an empty lwres_conf_t structure for
28 * lightweight resolver context ctx.
30 * lwres_conf_clear() frees up all the internal memory used by that
31 * lwres_conf_t structure in resolver context ctx.
33 * lwres_conf_parse() opens the file filename and parses it to initialise
34 * the resolver context ctx's lwres_conf_t structure.
36 * lwres_conf_print() prints the lwres_conf_t structure for resolver
37 * context ctx to the FILE fp.
39 * \section lwconfig_return Return Values
41 * lwres_conf_parse() returns #LWRES_R_SUCCESS if it successfully read and
42 * parsed filename. It returns #LWRES_R_FAILURE if filename could not be
43 * opened or contained incorrect resolver statements.
45 * lwres_conf_print() returns #LWRES_R_SUCCESS unless an error occurred
46 * when converting the network addresses to a numeric host address
47 * string. If this happens, the function returns #LWRES_R_FAILURE.
49 * \section lwconfig_see See Also
51 * stdio(3), \link resolver resolver \endlink
53 * \section files Files
55 * /etc/resolv.conf
58 #include <config.h>
60 #include <assert.h>
61 #include <ctype.h>
62 #include <errno.h>
63 #include <stdlib.h>
64 #include <stdio.h>
65 #include <string.h>
66 #include <unistd.h>
68 #include <lwres/lwbuffer.h>
69 #include <lwres/lwres.h>
70 #include <lwres/net.h>
71 #include <lwres/result.h>
73 #include "assert_p.h"
74 #include "context_p.h"
77 #if ! defined(NS_INADDRSZ)
78 #define NS_INADDRSZ 4
79 #endif
81 #if ! defined(NS_IN6ADDRSZ)
82 #define NS_IN6ADDRSZ 16
83 #endif
85 static lwres_result_t
86 lwres_conf_parsenameserver(lwres_context_t *ctx, FILE *fp);
88 static lwres_result_t
89 lwres_conf_parselwserver(lwres_context_t *ctx, FILE *fp);
91 static lwres_result_t
92 lwres_conf_parsedomain(lwres_context_t *ctx, FILE *fp);
94 static lwres_result_t
95 lwres_conf_parsesearch(lwres_context_t *ctx, FILE *fp);
97 static lwres_result_t
98 lwres_conf_parsesortlist(lwres_context_t *ctx, FILE *fp);
100 static lwres_result_t
101 lwres_conf_parseoption(lwres_context_t *ctx, FILE *fp);
103 static void
104 lwres_resetaddr(lwres_addr_t *addr);
106 static lwres_result_t
107 lwres_create_addr(const char *buff, lwres_addr_t *addr, int convert_zero);
109 static int lwresaddr2af(int lwresaddrtype);
112 static int
113 lwresaddr2af(int lwresaddrtype)
115 int af = 0;
117 switch (lwresaddrtype) {
118 case LWRES_ADDRTYPE_V4:
119 af = AF_INET;
120 break;
122 case LWRES_ADDRTYPE_V6:
123 af = AF_INET6;
124 break;
127 return (af);
132 * Eat characters from FP until EOL or EOF. Returns EOF or '\n'
134 static int
135 eatline(FILE *fp) {
136 int ch;
138 ch = fgetc(fp);
139 while (ch != '\n' && ch != EOF)
140 ch = fgetc(fp);
142 return (ch);
147 * Eats white space up to next newline or non-whitespace character (of
148 * EOF). Returns the last character read. Comments are considered white
149 * space.
151 static int
152 eatwhite(FILE *fp) {
153 int ch;
155 ch = fgetc(fp);
156 while (ch != '\n' && ch != EOF && isspace((unsigned char)ch))
157 ch = fgetc(fp);
159 if (ch == ';' || ch == '#')
160 ch = eatline(fp);
162 return (ch);
167 * Skip over any leading whitespace and then read in the next sequence of
168 * non-whitespace characters. In this context newline is not considered
169 * whitespace. Returns EOF on end-of-file, or the character
170 * that caused the reading to stop.
172 static int
173 getword(FILE *fp, char *buffer, size_t size) {
174 int ch;
175 char *p = buffer;
177 REQUIRE(buffer != NULL);
178 REQUIRE(size > 0U);
180 *p = '\0';
182 ch = eatwhite(fp);
184 if (ch == EOF)
185 return (EOF);
187 do {
188 *p = '\0';
190 if (ch == EOF || isspace((unsigned char)ch))
191 break;
192 else if ((size_t) (p - buffer) == size - 1)
193 return (EOF); /* Not enough space. */
195 *p++ = (char)ch;
196 ch = fgetc(fp);
197 } while (1);
199 return (ch);
202 static void
203 lwres_resetaddr(lwres_addr_t *addr) {
204 REQUIRE(addr != NULL);
206 memset(addr->address, 0, LWRES_ADDR_MAXLEN);
207 addr->family = 0;
208 addr->length = 0;
211 static char *
212 lwres_strdup(lwres_context_t *ctx, const char *str) {
213 char *p;
215 REQUIRE(str != NULL);
216 REQUIRE(strlen(str) > 0U);
218 p = CTXMALLOC(strlen(str) + 1);
219 if (p != NULL)
220 strcpy(p, str);
222 return (p);
225 /*% intializes data structure for subsequent config parsing. */
226 void
227 lwres_conf_init(lwres_context_t *ctx) {
228 int i;
229 lwres_conf_t *confdata;
231 REQUIRE(ctx != NULL);
232 confdata = &ctx->confdata;
234 confdata->nsnext = 0;
235 confdata->lwnext = 0;
236 confdata->domainname = NULL;
237 confdata->searchnxt = 0;
238 confdata->sortlistnxt = 0;
239 confdata->resdebug = 0;
240 confdata->ndots = 1;
241 confdata->no_tld_query = 0;
243 for (i = 0; i < LWRES_CONFMAXNAMESERVERS; i++)
244 lwres_resetaddr(&confdata->nameservers[i]);
246 for (i = 0; i < LWRES_CONFMAXSEARCH; i++)
247 confdata->search[i] = NULL;
249 for (i = 0; i < LWRES_CONFMAXSORTLIST; i++) {
250 lwres_resetaddr(&confdata->sortlist[i].addr);
251 lwres_resetaddr(&confdata->sortlist[i].mask);
255 /*% Frees up all the internal memory used by the config data structure, returning it to the lwres_context_t. */
256 void
257 lwres_conf_clear(lwres_context_t *ctx) {
258 int i;
259 lwres_conf_t *confdata;
261 REQUIRE(ctx != NULL);
262 confdata = &ctx->confdata;
264 for (i = 0; i < confdata->nsnext; i++)
265 lwres_resetaddr(&confdata->nameservers[i]);
267 if (confdata->domainname != NULL) {
268 CTXFREE(confdata->domainname,
269 strlen(confdata->domainname) + 1);
270 confdata->domainname = NULL;
273 for (i = 0; i < confdata->searchnxt; i++) {
274 if (confdata->search[i] != NULL) {
275 CTXFREE(confdata->search[i],
276 strlen(confdata->search[i]) + 1);
277 confdata->search[i] = NULL;
281 for (i = 0; i < LWRES_CONFMAXSORTLIST; i++) {
282 lwres_resetaddr(&confdata->sortlist[i].addr);
283 lwres_resetaddr(&confdata->sortlist[i].mask);
286 confdata->nsnext = 0;
287 confdata->lwnext = 0;
288 confdata->domainname = NULL;
289 confdata->searchnxt = 0;
290 confdata->sortlistnxt = 0;
291 confdata->resdebug = 0;
292 confdata->ndots = 1;
293 confdata->no_tld_query = 0;
296 static lwres_result_t
297 lwres_conf_parsenameserver(lwres_context_t *ctx, FILE *fp) {
298 char word[LWRES_CONFMAXLINELEN];
299 int res;
300 lwres_conf_t *confdata;
301 lwres_addr_t address;
303 confdata = &ctx->confdata;
305 if (confdata->nsnext == LWRES_CONFMAXNAMESERVERS)
306 return (LWRES_R_SUCCESS);
308 res = getword(fp, word, sizeof(word));
309 if (strlen(word) == 0U)
310 return (LWRES_R_FAILURE); /* Nothing on line. */
311 else if (res == ' ' || res == '\t')
312 res = eatwhite(fp);
314 if (res != EOF && res != '\n')
315 return (LWRES_R_FAILURE); /* Extra junk on line. */
317 res = lwres_create_addr(word, &address, 1);
318 if (res == LWRES_R_SUCCESS &&
319 ((address.family == LWRES_ADDRTYPE_V4 && ctx->use_ipv4 == 1) ||
320 (address.family == LWRES_ADDRTYPE_V6 && ctx->use_ipv6 == 1))) {
321 confdata->nameservers[confdata->nsnext++] = address;
324 return (LWRES_R_SUCCESS);
327 static lwres_result_t
328 lwres_conf_parselwserver(lwres_context_t *ctx, FILE *fp) {
329 char word[LWRES_CONFMAXLINELEN];
330 int res;
331 lwres_conf_t *confdata;
333 confdata = &ctx->confdata;
335 if (confdata->lwnext == LWRES_CONFMAXLWSERVERS)
336 return (LWRES_R_SUCCESS);
338 res = getword(fp, word, sizeof(word));
339 if (strlen(word) == 0U)
340 return (LWRES_R_FAILURE); /* Nothing on line. */
341 else if (res == ' ' || res == '\t')
342 res = eatwhite(fp);
344 if (res != EOF && res != '\n')
345 return (LWRES_R_FAILURE); /* Extra junk on line. */
347 res = lwres_create_addr(word,
348 &confdata->lwservers[confdata->lwnext++], 1);
349 if (res != LWRES_R_SUCCESS)
350 return (res);
352 return (LWRES_R_SUCCESS);
355 static lwres_result_t
356 lwres_conf_parsedomain(lwres_context_t *ctx, FILE *fp) {
357 char word[LWRES_CONFMAXLINELEN];
358 int res, i;
359 lwres_conf_t *confdata;
361 confdata = &ctx->confdata;
363 res = getword(fp, word, sizeof(word));
364 if (strlen(word) == 0U)
365 return (LWRES_R_FAILURE); /* Nothing else on line. */
366 else if (res == ' ' || res == '\t')
367 res = eatwhite(fp);
369 if (res != EOF && res != '\n')
370 return (LWRES_R_FAILURE); /* Extra junk on line. */
372 if (confdata->domainname != NULL)
373 CTXFREE(confdata->domainname,
374 strlen(confdata->domainname) + 1); /* */
377 * Search and domain are mutually exclusive.
379 for (i = 0; i < LWRES_CONFMAXSEARCH; i++) {
380 if (confdata->search[i] != NULL) {
381 CTXFREE(confdata->search[i],
382 strlen(confdata->search[i])+1);
383 confdata->search[i] = NULL;
386 confdata->searchnxt = 0;
388 confdata->domainname = lwres_strdup(ctx, word);
390 if (confdata->domainname == NULL)
391 return (LWRES_R_FAILURE);
393 return (LWRES_R_SUCCESS);
396 static lwres_result_t
397 lwres_conf_parsesearch(lwres_context_t *ctx, FILE *fp) {
398 int idx, delim;
399 char word[LWRES_CONFMAXLINELEN];
400 lwres_conf_t *confdata;
402 confdata = &ctx->confdata;
404 if (confdata->domainname != NULL) {
406 * Search and domain are mutually exclusive.
408 CTXFREE(confdata->domainname,
409 strlen(confdata->domainname) + 1);
410 confdata->domainname = NULL;
414 * Remove any previous search definitions.
416 for (idx = 0; idx < LWRES_CONFMAXSEARCH; idx++) {
417 if (confdata->search[idx] != NULL) {
418 CTXFREE(confdata->search[idx],
419 strlen(confdata->search[idx])+1);
420 confdata->search[idx] = NULL;
423 confdata->searchnxt = 0;
425 delim = getword(fp, word, sizeof(word));
426 if (strlen(word) == 0U)
427 return (LWRES_R_FAILURE); /* Nothing else on line. */
429 idx = 0;
430 while (strlen(word) > 0U) {
431 if (confdata->searchnxt == LWRES_CONFMAXSEARCH)
432 goto ignore; /* Too many domains. */
434 confdata->search[idx] = lwres_strdup(ctx, word);
435 if (confdata->search[idx] == NULL)
436 return (LWRES_R_FAILURE);
437 idx++;
438 confdata->searchnxt++;
440 ignore:
441 if (delim == EOF || delim == '\n')
442 break;
443 else
444 delim = getword(fp, word, sizeof(word));
447 return (LWRES_R_SUCCESS);
450 static lwres_result_t
451 lwres_create_addr(const char *buffer, lwres_addr_t *addr, int convert_zero) {
452 struct in_addr v4;
453 struct in6_addr v6;
455 if (lwres_net_aton(buffer, &v4) == 1) {
456 if (convert_zero) {
457 unsigned char zeroaddress[] = {0, 0, 0, 0};
458 unsigned char loopaddress[] = {127, 0, 0, 1};
459 if (memcmp(&v4, zeroaddress, 4) == 0)
460 memcpy(&v4, loopaddress, 4);
462 addr->family = LWRES_ADDRTYPE_V4;
463 addr->length = NS_INADDRSZ;
464 memcpy((void *)addr->address, &v4, NS_INADDRSZ);
466 } else if (lwres_net_pton(AF_INET6, buffer, &v6) == 1) {
467 addr->family = LWRES_ADDRTYPE_V6;
468 addr->length = NS_IN6ADDRSZ;
469 memcpy((void *)addr->address, &v6, NS_IN6ADDRSZ);
470 } else {
471 return (LWRES_R_FAILURE); /* Unrecognised format. */
474 return (LWRES_R_SUCCESS);
477 static lwres_result_t
478 lwres_conf_parsesortlist(lwres_context_t *ctx, FILE *fp) {
479 int delim, res, idx;
480 char word[LWRES_CONFMAXLINELEN];
481 char *p;
482 lwres_conf_t *confdata;
484 confdata = &ctx->confdata;
486 delim = getword(fp, word, sizeof(word));
487 if (strlen(word) == 0U)
488 return (LWRES_R_FAILURE); /* Empty line after keyword. */
490 while (strlen(word) > 0U) {
491 if (confdata->sortlistnxt == LWRES_CONFMAXSORTLIST)
492 return (LWRES_R_FAILURE); /* Too many values. */
494 p = strchr(word, '/');
495 if (p != NULL)
496 *p++ = '\0';
498 idx = confdata->sortlistnxt;
499 res = lwres_create_addr(word, &confdata->sortlist[idx].addr, 1);
500 if (res != LWRES_R_SUCCESS)
501 return (res);
503 if (p != NULL) {
504 res = lwres_create_addr(p,
505 &confdata->sortlist[idx].mask,
507 if (res != LWRES_R_SUCCESS)
508 return (res);
509 } else {
511 * Make up a mask.
513 confdata->sortlist[idx].mask =
514 confdata->sortlist[idx].addr;
516 memset(&confdata->sortlist[idx].mask.address, 0xff,
517 confdata->sortlist[idx].addr.length);
520 confdata->sortlistnxt++;
522 if (delim == EOF || delim == '\n')
523 break;
524 else
525 delim = getword(fp, word, sizeof(word));
528 return (LWRES_R_SUCCESS);
531 static lwres_result_t
532 lwres_conf_parseoption(lwres_context_t *ctx, FILE *fp) {
533 int delim;
534 long ndots;
535 char *p;
536 char word[LWRES_CONFMAXLINELEN];
537 lwres_conf_t *confdata;
539 REQUIRE(ctx != NULL);
540 confdata = &ctx->confdata;
542 delim = getword(fp, word, sizeof(word));
543 if (strlen(word) == 0U)
544 return (LWRES_R_FAILURE); /* Empty line after keyword. */
546 while (strlen(word) > 0U) {
547 if (strcmp("debug", word) == 0) {
548 confdata->resdebug = 1;
549 } else if (strcmp("no_tld_query", word) == 0) {
550 confdata->no_tld_query = 1;
551 } else if (strncmp("ndots:", word, 6) == 0) {
552 ndots = strtol(word + 6, &p, 10);
553 if (*p != '\0') /* Bad string. */
554 return (LWRES_R_FAILURE);
555 if (ndots < 0 || ndots > 0xff) /* Out of range. */
556 return (LWRES_R_FAILURE);
557 confdata->ndots = (lwres_uint8_t)ndots;
560 if (delim == EOF || delim == '\n')
561 break;
562 else
563 delim = getword(fp, word, sizeof(word));
566 return (LWRES_R_SUCCESS);
569 /*% parses a file and fills in the data structure. */
570 lwres_result_t
571 lwres_conf_parse(lwres_context_t *ctx, const char *filename) {
572 FILE *fp = NULL;
573 char word[256];
574 lwres_result_t rval, ret;
575 lwres_conf_t *confdata;
576 int stopchar;
578 REQUIRE(ctx != NULL);
579 confdata = &ctx->confdata;
581 REQUIRE(filename != NULL);
582 REQUIRE(strlen(filename) > 0U);
583 REQUIRE(confdata != NULL);
585 errno = 0;
586 if ((fp = fopen(filename, "r")) == NULL)
587 return (LWRES_R_NOTFOUND);
589 ret = LWRES_R_SUCCESS;
590 do {
591 stopchar = getword(fp, word, sizeof(word));
592 if (stopchar == EOF) {
593 rval = LWRES_R_SUCCESS;
594 break;
597 if (strlen(word) == 0U)
598 rval = LWRES_R_SUCCESS;
599 else if (strcmp(word, "nameserver") == 0)
600 rval = lwres_conf_parsenameserver(ctx, fp);
601 else if (strcmp(word, "lwserver") == 0)
602 rval = lwres_conf_parselwserver(ctx, fp);
603 else if (strcmp(word, "domain") == 0)
604 rval = lwres_conf_parsedomain(ctx, fp);
605 else if (strcmp(word, "search") == 0)
606 rval = lwres_conf_parsesearch(ctx, fp);
607 else if (strcmp(word, "sortlist") == 0)
608 rval = lwres_conf_parsesortlist(ctx, fp);
609 else if (strcmp(word, "options") == 0)
610 rval = lwres_conf_parseoption(ctx, fp);
611 else {
612 /* unrecognised word. Ignore entire line */
613 rval = LWRES_R_SUCCESS;
614 stopchar = eatline(fp);
615 if (stopchar == EOF) {
616 break;
619 if (ret == LWRES_R_SUCCESS && rval != LWRES_R_SUCCESS)
620 ret = rval;
621 } while (1);
623 fclose(fp);
625 return (ret);
628 /*% Prints the config data structure to the FILE. */
629 lwres_result_t
630 lwres_conf_print(lwres_context_t *ctx, FILE *fp) {
631 int i;
632 int af;
633 char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
634 const char *p;
635 lwres_conf_t *confdata;
636 lwres_addr_t tmpaddr;
638 REQUIRE(ctx != NULL);
639 confdata = &ctx->confdata;
641 REQUIRE(confdata->nsnext <= LWRES_CONFMAXNAMESERVERS);
643 for (i = 0; i < confdata->nsnext; i++) {
644 af = lwresaddr2af(confdata->nameservers[i].family);
646 p = lwres_net_ntop(af, confdata->nameservers[i].address,
647 tmp, sizeof(tmp));
648 if (p != tmp)
649 return (LWRES_R_FAILURE);
651 fprintf(fp, "nameserver %s\n", tmp);
654 for (i = 0; i < confdata->lwnext; i++) {
655 af = lwresaddr2af(confdata->lwservers[i].family);
657 p = lwres_net_ntop(af, confdata->lwservers[i].address,
658 tmp, sizeof(tmp));
659 if (p != tmp)
660 return (LWRES_R_FAILURE);
662 fprintf(fp, "lwserver %s\n", tmp);
665 if (confdata->domainname != NULL) {
666 fprintf(fp, "domain %s\n", confdata->domainname);
667 } else if (confdata->searchnxt > 0) {
668 REQUIRE(confdata->searchnxt <= LWRES_CONFMAXSEARCH);
670 fprintf(fp, "search");
671 for (i = 0; i < confdata->searchnxt; i++)
672 fprintf(fp, " %s", confdata->search[i]);
673 fputc('\n', fp);
676 REQUIRE(confdata->sortlistnxt <= LWRES_CONFMAXSORTLIST);
678 if (confdata->sortlistnxt > 0) {
679 fputs("sortlist", fp);
680 for (i = 0; i < confdata->sortlistnxt; i++) {
681 af = lwresaddr2af(confdata->sortlist[i].addr.family);
683 p = lwres_net_ntop(af,
684 confdata->sortlist[i].addr.address,
685 tmp, sizeof(tmp));
686 if (p != tmp)
687 return (LWRES_R_FAILURE);
689 fprintf(fp, " %s", tmp);
691 tmpaddr = confdata->sortlist[i].mask;
692 memset(&tmpaddr.address, 0xff, tmpaddr.length);
694 if (memcmp(&tmpaddr.address,
695 confdata->sortlist[i].mask.address,
696 confdata->sortlist[i].mask.length) != 0) {
697 af = lwresaddr2af(
698 confdata->sortlist[i].mask.family);
699 p = lwres_net_ntop
700 (af,
701 confdata->sortlist[i].mask.address,
702 tmp, sizeof(tmp));
703 if (p != tmp)
704 return (LWRES_R_FAILURE);
706 fprintf(fp, "/%s", tmp);
709 fputc('\n', fp);
712 if (confdata->resdebug)
713 fprintf(fp, "options debug\n");
715 if (confdata->ndots > 0)
716 fprintf(fp, "options ndots:%d\n", confdata->ndots);
718 if (confdata->no_tld_query)
719 fprintf(fp, "options no_tld_query\n");
721 return (LWRES_R_SUCCESS);
724 /*% Returns a pointer to the current config structure. */
725 lwres_conf_t *
726 lwres_conf_get(lwres_context_t *ctx) {
727 REQUIRE(ctx != NULL);
729 return (&ctx->confdata);