1 /* $NetBSD: lwconfig.c,v 1.6 2014/12/10 04:38:02 christos Exp $ */
4 * Copyright (C) 2004-2008, 2011, 2012, 2014 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.
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>
70 #include <lwres/stdlib.h>
71 #include <lwres/string.h>
74 #include "context_p.h"
78 #if ! defined(NS_INADDRSZ)
82 #if ! defined(NS_IN6ADDRSZ)
83 #define NS_IN6ADDRSZ 16
87 lwres_conf_parsenameserver(lwres_context_t
*ctx
, FILE *fp
);
90 lwres_conf_parselwserver(lwres_context_t
*ctx
, FILE *fp
);
93 lwres_conf_parsedomain(lwres_context_t
*ctx
, FILE *fp
);
96 lwres_conf_parsesearch(lwres_context_t
*ctx
, FILE *fp
);
99 lwres_conf_parsesortlist(lwres_context_t
*ctx
, FILE *fp
);
101 static lwres_result_t
102 lwres_conf_parseoption(lwres_context_t
*ctx
, FILE *fp
);
105 lwres_resetaddr(lwres_addr_t
*addr
);
107 static lwres_result_t
108 lwres_create_addr(const char *buff
, lwres_addr_t
*addr
, int convert_zero
);
110 static int lwresaddr2af(int lwresaddrtype
);
114 lwresaddr2af(int lwresaddrtype
)
118 switch (lwresaddrtype
) {
119 case LWRES_ADDRTYPE_V4
:
123 case LWRES_ADDRTYPE_V6
:
133 * Eat characters from FP until EOL or EOF. Returns EOF or '\n'
140 while (ch
!= '\n' && ch
!= EOF
)
148 * Eats white space up to next newline or non-whitespace character (of
149 * EOF). Returns the last character read. Comments are considered white
157 while (ch
!= '\n' && ch
!= EOF
&& isspace((unsigned char)ch
))
160 if (ch
== ';' || ch
== '#')
168 * Skip over any leading whitespace and then read in the next sequence of
169 * non-whitespace characters. In this context newline is not considered
170 * whitespace. Returns EOF on end-of-file, or the character
171 * that caused the reading to stop.
174 getword(FILE *fp
, char *buffer
, size_t size
) {
178 REQUIRE(buffer
!= NULL
);
191 if (ch
== EOF
|| isspace((unsigned char)ch
))
193 else if ((size_t) (p
- buffer
) == size
- 1)
194 return (EOF
); /* Not enough space. */
204 lwres_resetaddr(lwres_addr_t
*addr
) {
205 REQUIRE(addr
!= NULL
);
207 memset(addr
->address
, 0, LWRES_ADDR_MAXLEN
);
214 lwres_strdup(lwres_context_t
*ctx
, const char *str
) {
217 REQUIRE(str
!= NULL
);
218 REQUIRE(strlen(str
) > 0U);
220 p
= CTXMALLOC(strlen(str
) + 1);
227 /*% intializes data structure for subsequent config parsing. */
229 lwres_conf_init(lwres_context_t
*ctx
) {
231 lwres_conf_t
*confdata
;
233 REQUIRE(ctx
!= NULL
);
234 confdata
= &ctx
->confdata
;
236 confdata
->nsnext
= 0;
237 confdata
->lwnext
= 0;
238 confdata
->domainname
= NULL
;
239 confdata
->searchnxt
= 0;
240 confdata
->sortlistnxt
= 0;
241 confdata
->resdebug
= 0;
243 confdata
->no_tld_query
= 0;
245 for (i
= 0; i
< LWRES_CONFMAXNAMESERVERS
; i
++)
246 lwres_resetaddr(&confdata
->nameservers
[i
]);
248 for (i
= 0; i
< LWRES_CONFMAXSEARCH
; i
++)
249 confdata
->search
[i
] = NULL
;
251 for (i
= 0; i
< LWRES_CONFMAXSORTLIST
; i
++) {
252 lwres_resetaddr(&confdata
->sortlist
[i
].addr
);
253 lwres_resetaddr(&confdata
->sortlist
[i
].mask
);
257 /*% Frees up all the internal memory used by the config data structure, returning it to the lwres_context_t. */
259 lwres_conf_clear(lwres_context_t
*ctx
) {
261 lwres_conf_t
*confdata
;
263 REQUIRE(ctx
!= NULL
);
264 confdata
= &ctx
->confdata
;
266 for (i
= 0; i
< confdata
->nsnext
; i
++)
267 lwres_resetaddr(&confdata
->nameservers
[i
]);
269 if (confdata
->domainname
!= NULL
) {
270 CTXFREE(confdata
->domainname
,
271 strlen(confdata
->domainname
) + 1);
272 confdata
->domainname
= NULL
;
275 for (i
= 0; i
< confdata
->searchnxt
; i
++) {
276 if (confdata
->search
[i
] != NULL
) {
277 CTXFREE(confdata
->search
[i
],
278 strlen(confdata
->search
[i
]) + 1);
279 confdata
->search
[i
] = NULL
;
283 for (i
= 0; i
< LWRES_CONFMAXSORTLIST
; i
++) {
284 lwres_resetaddr(&confdata
->sortlist
[i
].addr
);
285 lwres_resetaddr(&confdata
->sortlist
[i
].mask
);
288 confdata
->nsnext
= 0;
289 confdata
->lwnext
= 0;
290 confdata
->domainname
= NULL
;
291 confdata
->searchnxt
= 0;
292 confdata
->sortlistnxt
= 0;
293 confdata
->resdebug
= 0;
295 confdata
->no_tld_query
= 0;
298 static lwres_result_t
299 lwres_conf_parsenameserver(lwres_context_t
*ctx
, FILE *fp
) {
300 char word
[LWRES_CONFMAXLINELEN
];
302 lwres_conf_t
*confdata
;
303 lwres_addr_t address
;
305 confdata
= &ctx
->confdata
;
307 if (confdata
->nsnext
== LWRES_CONFMAXNAMESERVERS
)
308 return (LWRES_R_SUCCESS
);
310 res
= getword(fp
, word
, sizeof(word
));
311 if (strlen(word
) == 0U)
312 return (LWRES_R_FAILURE
); /* Nothing on line. */
313 else if (res
== ' ' || res
== '\t')
316 if (res
!= EOF
&& res
!= '\n')
317 return (LWRES_R_FAILURE
); /* Extra junk on line. */
319 res
= lwres_create_addr(word
, &address
, 1);
320 if (res
== LWRES_R_SUCCESS
&&
321 ((address
.family
== LWRES_ADDRTYPE_V4
&& ctx
->use_ipv4
== 1) ||
322 (address
.family
== LWRES_ADDRTYPE_V6
&& ctx
->use_ipv6
== 1))) {
323 confdata
->nameservers
[confdata
->nsnext
++] = address
;
326 return (LWRES_R_SUCCESS
);
329 static lwres_result_t
330 lwres_conf_parselwserver(lwres_context_t
*ctx
, FILE *fp
) {
331 char word
[LWRES_CONFMAXLINELEN
];
333 lwres_conf_t
*confdata
;
335 confdata
= &ctx
->confdata
;
337 if (confdata
->lwnext
== LWRES_CONFMAXLWSERVERS
)
338 return (LWRES_R_SUCCESS
);
340 res
= getword(fp
, word
, sizeof(word
));
341 if (strlen(word
) == 0U)
342 return (LWRES_R_FAILURE
); /* Nothing on line. */
343 else if (res
== ' ' || res
== '\t')
346 if (res
!= EOF
&& res
!= '\n')
347 return (LWRES_R_FAILURE
); /* Extra junk on line. */
349 res
= lwres_create_addr(word
,
350 &confdata
->lwservers
[confdata
->lwnext
++], 1);
351 if (res
!= LWRES_R_SUCCESS
)
354 return (LWRES_R_SUCCESS
);
357 static lwres_result_t
358 lwres_conf_parsedomain(lwres_context_t
*ctx
, FILE *fp
) {
359 char word
[LWRES_CONFMAXLINELEN
];
361 lwres_conf_t
*confdata
;
363 confdata
= &ctx
->confdata
;
365 res
= getword(fp
, word
, sizeof(word
));
366 if (strlen(word
) == 0U)
367 return (LWRES_R_FAILURE
); /* Nothing else on line. */
368 else if (res
== ' ' || res
== '\t')
371 if (res
!= EOF
&& res
!= '\n')
372 return (LWRES_R_FAILURE
); /* Extra junk on line. */
374 if (confdata
->domainname
!= NULL
)
375 CTXFREE(confdata
->domainname
,
376 strlen(confdata
->domainname
) + 1); /* */
379 * Search and domain are mutually exclusive.
381 for (i
= 0; i
< LWRES_CONFMAXSEARCH
; i
++) {
382 if (confdata
->search
[i
] != NULL
) {
383 CTXFREE(confdata
->search
[i
],
384 strlen(confdata
->search
[i
])+1);
385 confdata
->search
[i
] = NULL
;
388 confdata
->searchnxt
= 0;
390 confdata
->domainname
= lwres_strdup(ctx
, word
);
392 if (confdata
->domainname
== NULL
)
393 return (LWRES_R_FAILURE
);
395 return (LWRES_R_SUCCESS
);
398 static lwres_result_t
399 lwres_conf_parsesearch(lwres_context_t
*ctx
, FILE *fp
) {
401 char word
[LWRES_CONFMAXLINELEN
];
402 lwres_conf_t
*confdata
;
404 confdata
= &ctx
->confdata
;
406 if (confdata
->domainname
!= NULL
) {
408 * Search and domain are mutually exclusive.
410 CTXFREE(confdata
->domainname
,
411 strlen(confdata
->domainname
) + 1);
412 confdata
->domainname
= NULL
;
416 * Remove any previous search definitions.
418 for (idx
= 0; idx
< LWRES_CONFMAXSEARCH
; idx
++) {
419 if (confdata
->search
[idx
] != NULL
) {
420 CTXFREE(confdata
->search
[idx
],
421 strlen(confdata
->search
[idx
])+1);
422 confdata
->search
[idx
] = NULL
;
425 confdata
->searchnxt
= 0;
427 delim
= getword(fp
, word
, sizeof(word
));
428 if (strlen(word
) == 0U)
429 return (LWRES_R_FAILURE
); /* Nothing else on line. */
432 while (strlen(word
) > 0U) {
433 if (confdata
->searchnxt
== LWRES_CONFMAXSEARCH
)
434 goto ignore
; /* Too many domains. */
436 confdata
->search
[idx
] = lwres_strdup(ctx
, word
);
437 if (confdata
->search
[idx
] == NULL
)
438 return (LWRES_R_FAILURE
);
440 confdata
->searchnxt
++;
443 if (delim
== EOF
|| delim
== '\n')
446 delim
= getword(fp
, word
, sizeof(word
));
449 return (LWRES_R_SUCCESS
);
452 static lwres_result_t
453 lwres_create_addr(const char *buffer
, lwres_addr_t
*addr
, int convert_zero
) {
456 char buf
[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") +
457 sizeof("%4294967295")];
461 n
= strlcpy(buf
, buffer
, sizeof(buf
));
462 if (n
>= sizeof(buf
))
463 return (LWRES_R_FAILURE
);
465 percent
= strchr(buf
, '%');
469 if (lwres_net_aton(buffer
, &v4
) == 1) {
471 unsigned char zeroaddress
[] = {0, 0, 0, 0};
472 unsigned char loopaddress
[] = {127, 0, 0, 1};
473 if (memcmp(&v4
, zeroaddress
, 4) == 0)
474 memmove(&v4
, loopaddress
, 4);
476 addr
->family
= LWRES_ADDRTYPE_V4
;
477 addr
->length
= NS_INADDRSZ
;
479 memmove((void *)addr
->address
, &v4
, NS_INADDRSZ
);
481 } else if (lwres_net_pton(AF_INET6
, buf
, &v6
) == 1) {
482 addr
->family
= LWRES_ADDRTYPE_V6
;
483 addr
->length
= NS_IN6ADDRSZ
;
484 memmove((void *)addr
->address
, &v6
, NS_IN6ADDRSZ
);
485 if (percent
!= NULL
) {
491 #ifdef HAVE_IF_NAMETOINDEX
492 zone
= if_nametoindex(percent
);
495 return (LWRES_R_SUCCESS
);
498 zone
= strtoul(percent
, &ep
, 10);
499 if (ep
!= percent
&& *ep
== 0)
502 return (LWRES_R_FAILURE
);
506 return (LWRES_R_FAILURE
); /* Unrecognised format. */
508 return (LWRES_R_SUCCESS
);
511 static lwres_result_t
512 lwres_conf_parsesortlist(lwres_context_t
*ctx
, FILE *fp
) {
514 char word
[LWRES_CONFMAXLINELEN
];
516 lwres_conf_t
*confdata
;
518 confdata
= &ctx
->confdata
;
520 delim
= getword(fp
, word
, sizeof(word
));
521 if (strlen(word
) == 0U)
522 return (LWRES_R_FAILURE
); /* Empty line after keyword. */
524 while (strlen(word
) > 0U) {
525 if (confdata
->sortlistnxt
== LWRES_CONFMAXSORTLIST
)
526 return (LWRES_R_FAILURE
); /* Too many values. */
528 p
= strchr(word
, '/');
532 idx
= confdata
->sortlistnxt
;
533 res
= lwres_create_addr(word
, &confdata
->sortlist
[idx
].addr
, 1);
534 if (res
!= LWRES_R_SUCCESS
)
538 res
= lwres_create_addr(p
,
539 &confdata
->sortlist
[idx
].mask
,
541 if (res
!= LWRES_R_SUCCESS
)
547 confdata
->sortlist
[idx
].mask
=
548 confdata
->sortlist
[idx
].addr
;
550 memset(&confdata
->sortlist
[idx
].mask
.address
, 0xff,
551 confdata
->sortlist
[idx
].addr
.length
);
554 confdata
->sortlistnxt
++;
556 if (delim
== EOF
|| delim
== '\n')
559 delim
= getword(fp
, word
, sizeof(word
));
562 return (LWRES_R_SUCCESS
);
565 static lwres_result_t
566 lwres_conf_parseoption(lwres_context_t
*ctx
, FILE *fp
) {
570 char word
[LWRES_CONFMAXLINELEN
];
571 lwres_conf_t
*confdata
;
573 REQUIRE(ctx
!= NULL
);
574 confdata
= &ctx
->confdata
;
576 delim
= getword(fp
, word
, sizeof(word
));
577 if (strlen(word
) == 0U)
578 return (LWRES_R_FAILURE
); /* Empty line after keyword. */
580 while (strlen(word
) > 0U) {
581 if (strcmp("debug", word
) == 0) {
582 confdata
->resdebug
= 1;
583 } else if (strcmp("no_tld_query", word
) == 0) {
584 confdata
->no_tld_query
= 1;
585 } else if (strncmp("ndots:", word
, 6) == 0) {
586 ndots
= strtol(word
+ 6, &p
, 10);
587 if (*p
!= '\0') /* Bad string. */
588 return (LWRES_R_FAILURE
);
589 if (ndots
< 0 || ndots
> 0xff) /* Out of range. */
590 return (LWRES_R_FAILURE
);
591 confdata
->ndots
= (lwres_uint8_t
)ndots
;
594 if (delim
== EOF
|| delim
== '\n')
597 delim
= getword(fp
, word
, sizeof(word
));
600 return (LWRES_R_SUCCESS
);
603 /*% parses a file and fills in the data structure. */
605 lwres_conf_parse(lwres_context_t
*ctx
, const char *filename
) {
608 lwres_result_t rval
, ret
;
609 #if !defined(NDEBUG) && defined(__minix)
610 lwres_conf_t
*confdata
;
611 #endif /* !defined(NDEBUG) && defined(__minix) */
614 REQUIRE(ctx
!= NULL
);
615 #if !defined(NDEBUG) && defined(__minix)
616 confdata
= &ctx
->confdata
;
617 #endif /* !defined(NDEBUG) && defined(__minix) */
619 REQUIRE(filename
!= NULL
);
620 REQUIRE(strlen(filename
) > 0U);
621 #if !defined(NDEBUG) && defined(__minix)
622 REQUIRE(confdata
!= NULL
);
623 #endif /* !defined(NDEBUG) && defined(__minix) */
626 if ((fp
= fopen(filename
, "r")) == NULL
)
627 return (LWRES_R_NOTFOUND
);
629 ret
= LWRES_R_SUCCESS
;
631 stopchar
= getword(fp
, word
, sizeof(word
));
632 if (stopchar
== EOF
) {
633 rval
= LWRES_R_SUCCESS
;
638 if (strlen(word
) == 0U)
639 rval
= LWRES_R_SUCCESS
;
640 else if (strcmp(word
, "nameserver") == 0)
641 rval
= lwres_conf_parsenameserver(ctx
, fp
);
642 else if (strcmp(word
, "lwserver") == 0)
643 rval
= lwres_conf_parselwserver(ctx
, fp
);
644 else if (strcmp(word
, "domain") == 0)
645 rval
= lwres_conf_parsedomain(ctx
, fp
);
646 else if (strcmp(word
, "search") == 0)
647 rval
= lwres_conf_parsesearch(ctx
, fp
);
648 else if (strcmp(word
, "sortlist") == 0)
649 rval
= lwres_conf_parsesortlist(ctx
, fp
);
650 else if (strcmp(word
, "options") == 0)
651 rval
= lwres_conf_parseoption(ctx
, fp
);
653 /* unrecognised word. Ignore entire line */
654 rval
= LWRES_R_SUCCESS
;
655 stopchar
= eatline(fp
);
656 if (stopchar
== EOF
) {
660 if (ret
== LWRES_R_SUCCESS
&& rval
!= LWRES_R_SUCCESS
)
669 /*% Prints the config data structure to the FILE. */
671 lwres_conf_print(lwres_context_t
*ctx
, FILE *fp
) {
674 char tmp
[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
675 char buf
[sizeof("%4000000000")];
677 lwres_conf_t
*confdata
;
678 lwres_addr_t tmpaddr
;
680 REQUIRE(ctx
!= NULL
);
681 confdata
= &ctx
->confdata
;
683 REQUIRE(confdata
->nsnext
<= LWRES_CONFMAXNAMESERVERS
);
685 for (i
= 0; i
< confdata
->nsnext
; i
++) {
686 af
= lwresaddr2af(confdata
->nameservers
[i
].family
);
688 p
= lwres_net_ntop(af
, confdata
->nameservers
[i
].address
,
691 return (LWRES_R_FAILURE
);
693 if (af
== AF_INET6
&& confdata
->lwservers
[i
].zone
!= 0) {
694 snprintf(buf
, sizeof(buf
), "%%%u",
695 confdata
->nameservers
[i
].zone
);
699 fprintf(fp
, "nameserver %s%s\n", tmp
, buf
);
702 for (i
= 0; i
< confdata
->lwnext
; i
++) {
703 af
= lwresaddr2af(confdata
->lwservers
[i
].family
);
705 p
= lwres_net_ntop(af
, confdata
->lwservers
[i
].address
,
708 return (LWRES_R_FAILURE
);
710 if (af
== AF_INET6
&& confdata
->lwservers
[i
].zone
!= 0) {
711 snprintf(buf
, sizeof(buf
), "%%%u",
712 confdata
->nameservers
[i
].zone
);
716 fprintf(fp
, "lwserver %s%s\n", tmp
, buf
);
719 if (confdata
->domainname
!= NULL
) {
720 fprintf(fp
, "domain %s\n", confdata
->domainname
);
721 } else if (confdata
->searchnxt
> 0) {
722 REQUIRE(confdata
->searchnxt
<= LWRES_CONFMAXSEARCH
);
724 fprintf(fp
, "search");
725 for (i
= 0; i
< confdata
->searchnxt
; i
++)
726 fprintf(fp
, " %s", confdata
->search
[i
]);
730 REQUIRE(confdata
->sortlistnxt
<= LWRES_CONFMAXSORTLIST
);
732 if (confdata
->sortlistnxt
> 0) {
733 fputs("sortlist", fp
);
734 for (i
= 0; i
< confdata
->sortlistnxt
; i
++) {
735 af
= lwresaddr2af(confdata
->sortlist
[i
].addr
.family
);
737 p
= lwres_net_ntop(af
,
738 confdata
->sortlist
[i
].addr
.address
,
741 return (LWRES_R_FAILURE
);
743 fprintf(fp
, " %s", tmp
);
745 tmpaddr
= confdata
->sortlist
[i
].mask
;
746 memset(&tmpaddr
.address
, 0xff, tmpaddr
.length
);
748 if (memcmp(&tmpaddr
.address
,
749 confdata
->sortlist
[i
].mask
.address
,
750 confdata
->sortlist
[i
].mask
.length
) != 0) {
752 confdata
->sortlist
[i
].mask
.family
);
755 confdata
->sortlist
[i
].mask
.address
,
758 return (LWRES_R_FAILURE
);
760 fprintf(fp
, "/%s", tmp
);
766 if (confdata
->resdebug
)
767 fprintf(fp
, "options debug\n");
769 if (confdata
->ndots
> 0)
770 fprintf(fp
, "options ndots:%d\n", confdata
->ndots
);
772 if (confdata
->no_tld_query
)
773 fprintf(fp
, "options no_tld_query\n");
775 return (LWRES_R_SUCCESS
);
778 /*% Returns a pointer to the current config structure. */
780 lwres_conf_get(lwres_context_t
*ctx
) {
781 REQUIRE(ctx
!= NULL
);
783 return (&ctx
->confdata
);