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 */
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
68 #include <lwres/lwbuffer.h>
69 #include <lwres/lwres.h>
70 #include <lwres/net.h>
71 #include <lwres/result.h>
74 #include "context_p.h"
77 #if ! defined(NS_INADDRSZ)
81 #if ! defined(NS_IN6ADDRSZ)
82 #define NS_IN6ADDRSZ 16
86 lwres_conf_parsenameserver(lwres_context_t
*ctx
, FILE *fp
);
89 lwres_conf_parselwserver(lwres_context_t
*ctx
, FILE *fp
);
92 lwres_conf_parsedomain(lwres_context_t
*ctx
, FILE *fp
);
95 lwres_conf_parsesearch(lwres_context_t
*ctx
, FILE *fp
);
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
);
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
);
113 lwresaddr2af(int lwresaddrtype
)
117 switch (lwresaddrtype
) {
118 case LWRES_ADDRTYPE_V4
:
122 case LWRES_ADDRTYPE_V6
:
132 * Eat characters from FP until EOL or EOF. Returns EOF or '\n'
139 while (ch
!= '\n' && ch
!= EOF
)
147 * Eats white space up to next newline or non-whitespace character (of
148 * EOF). Returns the last character read. Comments are considered white
156 while (ch
!= '\n' && ch
!= EOF
&& isspace((unsigned char)ch
))
159 if (ch
== ';' || 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.
173 getword(FILE *fp
, char *buffer
, size_t size
) {
177 REQUIRE(buffer
!= NULL
);
190 if (ch
== EOF
|| isspace((unsigned char)ch
))
192 else if ((size_t) (p
- buffer
) == size
- 1)
193 return (EOF
); /* Not enough space. */
203 lwres_resetaddr(lwres_addr_t
*addr
) {
204 REQUIRE(addr
!= NULL
);
206 memset(addr
->address
, 0, LWRES_ADDR_MAXLEN
);
212 lwres_strdup(lwres_context_t
*ctx
, const char *str
) {
215 REQUIRE(str
!= NULL
);
216 REQUIRE(strlen(str
) > 0U);
218 p
= CTXMALLOC(strlen(str
) + 1);
225 /*% intializes data structure for subsequent config parsing. */
227 lwres_conf_init(lwres_context_t
*ctx
) {
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;
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. */
257 lwres_conf_clear(lwres_context_t
*ctx
) {
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;
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
];
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')
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
];
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')
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
)
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
];
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')
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
) {
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. */
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
);
438 confdata
->searchnxt
++;
441 if (delim
== EOF
|| delim
== '\n')
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
) {
455 if (lwres_net_aton(buffer
, &v4
) == 1) {
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
);
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
) {
480 char word
[LWRES_CONFMAXLINELEN
];
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
, '/');
498 idx
= confdata
->sortlistnxt
;
499 res
= lwres_create_addr(word
, &confdata
->sortlist
[idx
].addr
, 1);
500 if (res
!= LWRES_R_SUCCESS
)
504 res
= lwres_create_addr(p
,
505 &confdata
->sortlist
[idx
].mask
,
507 if (res
!= LWRES_R_SUCCESS
)
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')
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
) {
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')
563 delim
= getword(fp
, word
, sizeof(word
));
566 return (LWRES_R_SUCCESS
);
569 /*% parses a file and fills in the data structure. */
571 lwres_conf_parse(lwres_context_t
*ctx
, const char *filename
) {
574 lwres_result_t rval
, ret
;
575 lwres_conf_t
*confdata
;
578 REQUIRE(ctx
!= NULL
);
579 confdata
= &ctx
->confdata
;
581 REQUIRE(filename
!= NULL
);
582 REQUIRE(strlen(filename
) > 0U);
583 REQUIRE(confdata
!= NULL
);
586 if ((fp
= fopen(filename
, "r")) == NULL
)
587 return (LWRES_R_NOTFOUND
);
589 ret
= LWRES_R_SUCCESS
;
591 stopchar
= getword(fp
, word
, sizeof(word
));
592 if (stopchar
== EOF
) {
593 rval
= LWRES_R_SUCCESS
;
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
);
612 /* unrecognised word. Ignore entire line */
613 rval
= LWRES_R_SUCCESS
;
614 stopchar
= eatline(fp
);
615 if (stopchar
== EOF
) {
619 if (ret
== LWRES_R_SUCCESS
&& rval
!= LWRES_R_SUCCESS
)
628 /*% Prints the config data structure to the FILE. */
630 lwres_conf_print(lwres_context_t
*ctx
, FILE *fp
) {
633 char tmp
[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
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
,
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
,
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
]);
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
,
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) {
698 confdata
->sortlist
[i
].mask
.family
);
701 confdata
->sortlist
[i
].mask
.address
,
704 return (LWRES_R_FAILURE
);
706 fprintf(fp
, "/%s", tmp
);
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. */
726 lwres_conf_get(lwres_context_t
*ctx
) {
727 REQUIRE(ctx
!= NULL
);
729 return (&ctx
->confdata
);