xref: /minix/external/bsd/bind/dist/lib/lwres/lwconfig.c (revision 00b67f09)
1 /*	$NetBSD: lwconfig.c,v 1.6 2014/12/10 04:38:02 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2004-2008, 2011, 2012, 2014  Internet Systems Consortium, Inc. ("ISC")
5  * Copyright (C) 2000-2003  Internet Software Consortium.
6  *
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.
10  *
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.
18  */
19 
20 /*! \file */
21 
22 /**
23  * Module for parsing resolv.conf files.
24  *
25  *    lwres_conf_init() creates an empty lwres_conf_t structure for
26  *    lightweight resolver context ctx.
27  *
28  *    lwres_conf_clear() frees up all the internal memory used by that
29  *    lwres_conf_t structure in resolver context ctx.
30  *
31  *    lwres_conf_parse() opens the file filename and parses it to initialise
32  *    the resolver context ctx's lwres_conf_t structure.
33  *
34  *    lwres_conf_print() prints the lwres_conf_t structure for resolver
35  *    context ctx to the FILE fp.
36  *
37  * \section lwconfig_return Return Values
38  *
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.
42  *
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.
46  *
47  * \section lwconfig_see See Also
48  *
49  *    stdio(3), \link resolver resolver \endlink
50  *
51  * \section files Files
52  *
53  *    /etc/resolv.conf
54  */
55 
56 #include <config.h>
57 
58 #include <assert.h>
59 #include <ctype.h>
60 #include <errno.h>
61 #include <stdlib.h>
62 #include <stdio.h>
63 #include <string.h>
64 #include <unistd.h>
65 
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>
72 
73 #include "assert_p.h"
74 #include "context_p.h"
75 #include "print_p.h"
76 
77 
78 #if ! defined(NS_INADDRSZ)
79 #define NS_INADDRSZ	 4
80 #endif
81 
82 #if ! defined(NS_IN6ADDRSZ)
83 #define NS_IN6ADDRSZ	16
84 #endif
85 
86 static lwres_result_t
87 lwres_conf_parsenameserver(lwres_context_t *ctx,  FILE *fp);
88 
89 static lwres_result_t
90 lwres_conf_parselwserver(lwres_context_t *ctx,  FILE *fp);
91 
92 static lwres_result_t
93 lwres_conf_parsedomain(lwres_context_t *ctx, FILE *fp);
94 
95 static lwres_result_t
96 lwres_conf_parsesearch(lwres_context_t *ctx,  FILE *fp);
97 
98 static lwres_result_t
99 lwres_conf_parsesortlist(lwres_context_t *ctx,  FILE *fp);
100 
101 static lwres_result_t
102 lwres_conf_parseoption(lwres_context_t *ctx,  FILE *fp);
103 
104 static void
105 lwres_resetaddr(lwres_addr_t *addr);
106 
107 static lwres_result_t
108 lwres_create_addr(const char *buff, lwres_addr_t *addr, int convert_zero);
109 
110 static int lwresaddr2af(int lwresaddrtype);
111 
112 
113 static int
114 lwresaddr2af(int lwresaddrtype)
115 {
116 	int af = 0;
117 
118 	switch (lwresaddrtype) {
119 	case LWRES_ADDRTYPE_V4:
120 		af = AF_INET;
121 		break;
122 
123 	case LWRES_ADDRTYPE_V6:
124 		af = AF_INET6;
125 		break;
126 	}
127 
128 	return (af);
129 }
130 
131 
132 /*!
133  * Eat characters from FP until EOL or EOF. Returns EOF or '\n'
134  */
135 static int
136 eatline(FILE *fp) {
137 	int ch;
138 
139 	ch = fgetc(fp);
140 	while (ch != '\n' && ch != EOF)
141 		ch = fgetc(fp);
142 
143 	return (ch);
144 }
145 
146 
147 /*!
148  * Eats white space up to next newline or non-whitespace character (of
149  * EOF). Returns the last character read. Comments are considered white
150  * space.
151  */
152 static int
153 eatwhite(FILE *fp) {
154 	int ch;
155 
156 	ch = fgetc(fp);
157 	while (ch != '\n' && ch != EOF && isspace((unsigned char)ch))
158 		ch = fgetc(fp);
159 
160 	if (ch == ';' || ch == '#')
161 		ch = eatline(fp);
162 
163 	return (ch);
164 }
165 
166 
167 /*!
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.
172  */
173 static int
174 getword(FILE *fp, char *buffer, size_t size) {
175 	int ch;
176 	char *p = buffer;
177 
178 	REQUIRE(buffer != NULL);
179 	REQUIRE(size > 0U);
180 
181 	*p = '\0';
182 
183 	ch = eatwhite(fp);
184 
185 	if (ch == EOF)
186 		return (EOF);
187 
188 	for (;;) {
189 		*p = '\0';
190 
191 		if (ch == EOF || isspace((unsigned char)ch))
192 			break;
193 		else if ((size_t) (p - buffer) == size - 1)
194 			return (EOF);	/* Not enough space. */
195 
196 		*p++ = (char)ch;
197 		ch = fgetc(fp);
198 	}
199 
200 	return (ch);
201 }
202 
203 static void
204 lwres_resetaddr(lwres_addr_t *addr) {
205 	REQUIRE(addr != NULL);
206 
207 	memset(addr->address, 0, LWRES_ADDR_MAXLEN);
208 	addr->family = 0;
209 	addr->length = 0;
210 	addr->zone = 0;
211 }
212 
213 static char *
214 lwres_strdup(lwres_context_t *ctx, const char *str) {
215 	char *p;
216 
217 	REQUIRE(str != NULL);
218 	REQUIRE(strlen(str) > 0U);
219 
220 	p = CTXMALLOC(strlen(str) + 1);
221 	if (p != NULL)
222 		strcpy(p, str);
223 
224 	return (p);
225 }
226 
227 /*% intializes data structure for subsequent config parsing. */
228 void
229 lwres_conf_init(lwres_context_t *ctx) {
230 	int i;
231 	lwres_conf_t *confdata;
232 
233 	REQUIRE(ctx != NULL);
234 	confdata = &ctx->confdata;
235 
236 	confdata->nsnext = 0;
237 	confdata->lwnext = 0;
238 	confdata->domainname = NULL;
239 	confdata->searchnxt = 0;
240 	confdata->sortlistnxt = 0;
241 	confdata->resdebug = 0;
242 	confdata->ndots = 1;
243 	confdata->no_tld_query = 0;
244 
245 	for (i = 0; i < LWRES_CONFMAXNAMESERVERS; i++)
246 		lwres_resetaddr(&confdata->nameservers[i]);
247 
248 	for (i = 0; i < LWRES_CONFMAXSEARCH; i++)
249 		confdata->search[i] = NULL;
250 
251 	for (i = 0; i < LWRES_CONFMAXSORTLIST; i++) {
252 		lwres_resetaddr(&confdata->sortlist[i].addr);
253 		lwres_resetaddr(&confdata->sortlist[i].mask);
254 	}
255 }
256 
257 /*% Frees up all the internal memory used by the config data structure, returning it to the lwres_context_t. */
258 void
259 lwres_conf_clear(lwres_context_t *ctx) {
260 	int i;
261 	lwres_conf_t *confdata;
262 
263 	REQUIRE(ctx != NULL);
264 	confdata = &ctx->confdata;
265 
266 	for (i = 0; i < confdata->nsnext; i++)
267 		lwres_resetaddr(&confdata->nameservers[i]);
268 
269 	if (confdata->domainname != NULL) {
270 		CTXFREE(confdata->domainname,
271 			strlen(confdata->domainname) + 1);
272 		confdata->domainname = NULL;
273 	}
274 
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;
280 		}
281 	}
282 
283 	for (i = 0; i < LWRES_CONFMAXSORTLIST; i++) {
284 		lwres_resetaddr(&confdata->sortlist[i].addr);
285 		lwres_resetaddr(&confdata->sortlist[i].mask);
286 	}
287 
288 	confdata->nsnext = 0;
289 	confdata->lwnext = 0;
290 	confdata->domainname = NULL;
291 	confdata->searchnxt = 0;
292 	confdata->sortlistnxt = 0;
293 	confdata->resdebug = 0;
294 	confdata->ndots = 1;
295 	confdata->no_tld_query = 0;
296 }
297 
298 static lwres_result_t
299 lwres_conf_parsenameserver(lwres_context_t *ctx,  FILE *fp) {
300 	char word[LWRES_CONFMAXLINELEN];
301 	int res;
302 	lwres_conf_t *confdata;
303 	lwres_addr_t address;
304 
305 	confdata = &ctx->confdata;
306 
307 	if (confdata->nsnext == LWRES_CONFMAXNAMESERVERS)
308 		return (LWRES_R_SUCCESS);
309 
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')
314 		res = eatwhite(fp);
315 
316 	if (res != EOF && res != '\n')
317 		return (LWRES_R_FAILURE); /* Extra junk on line. */
318 
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;
324 	}
325 
326 	return (LWRES_R_SUCCESS);
327 }
328 
329 static lwres_result_t
330 lwres_conf_parselwserver(lwres_context_t *ctx,  FILE *fp) {
331 	char word[LWRES_CONFMAXLINELEN];
332 	int res;
333 	lwres_conf_t *confdata;
334 
335 	confdata = &ctx->confdata;
336 
337 	if (confdata->lwnext == LWRES_CONFMAXLWSERVERS)
338 		return (LWRES_R_SUCCESS);
339 
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')
344 		res = eatwhite(fp);
345 
346 	if (res != EOF && res != '\n')
347 		return (LWRES_R_FAILURE); /* Extra junk on line. */
348 
349 	res = lwres_create_addr(word,
350 				&confdata->lwservers[confdata->lwnext++], 1);
351 	if (res != LWRES_R_SUCCESS)
352 		return (res);
353 
354 	return (LWRES_R_SUCCESS);
355 }
356 
357 static lwres_result_t
358 lwres_conf_parsedomain(lwres_context_t *ctx,  FILE *fp) {
359 	char word[LWRES_CONFMAXLINELEN];
360 	int res, i;
361 	lwres_conf_t *confdata;
362 
363 	confdata = &ctx->confdata;
364 
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')
369 		res = eatwhite(fp);
370 
371 	if (res != EOF && res != '\n')
372 		return (LWRES_R_FAILURE); /* Extra junk on line. */
373 
374 	if (confdata->domainname != NULL)
375 		CTXFREE(confdata->domainname,
376 			strlen(confdata->domainname) + 1); /*  */
377 
378 	/*
379 	 * Search and domain are mutually exclusive.
380 	 */
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;
386 		}
387 	}
388 	confdata->searchnxt = 0;
389 
390 	confdata->domainname = lwres_strdup(ctx, word);
391 
392 	if (confdata->domainname == NULL)
393 		return (LWRES_R_FAILURE);
394 
395 	return (LWRES_R_SUCCESS);
396 }
397 
398 static lwres_result_t
399 lwres_conf_parsesearch(lwres_context_t *ctx,  FILE *fp) {
400 	int idx, delim;
401 	char word[LWRES_CONFMAXLINELEN];
402 	lwres_conf_t *confdata;
403 
404 	confdata = &ctx->confdata;
405 
406 	if (confdata->domainname != NULL) {
407 		/*
408 		 * Search and domain are mutually exclusive.
409 		 */
410 		CTXFREE(confdata->domainname,
411 			strlen(confdata->domainname) + 1);
412 		confdata->domainname = NULL;
413 	}
414 
415 	/*
416 	 * Remove any previous search definitions.
417 	 */
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;
423 		}
424 	}
425 	confdata->searchnxt = 0;
426 
427 	delim = getword(fp, word, sizeof(word));
428 	if (strlen(word) == 0U)
429 		return (LWRES_R_FAILURE); /* Nothing else on line. */
430 
431 	idx = 0;
432 	while (strlen(word) > 0U) {
433 		if (confdata->searchnxt == LWRES_CONFMAXSEARCH)
434 			goto ignore; /* Too many domains. */
435 
436 		confdata->search[idx] = lwres_strdup(ctx, word);
437 		if (confdata->search[idx] == NULL)
438 			return (LWRES_R_FAILURE);
439 		idx++;
440 		confdata->searchnxt++;
441 
442 	ignore:
443 		if (delim == EOF || delim == '\n')
444 			break;
445 		else
446 			delim = getword(fp, word, sizeof(word));
447 	}
448 
449 	return (LWRES_R_SUCCESS);
450 }
451 
452 static lwres_result_t
453 lwres_create_addr(const char *buffer, lwres_addr_t *addr, int convert_zero) {
454 	struct in_addr v4;
455 	struct in6_addr v6;
456 	char buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") +
457 		 sizeof("%4294967295")];
458 	char *percent;
459 	size_t n;
460 
461 	n = strlcpy(buf, buffer, sizeof(buf));
462 	if (n >= sizeof(buf))
463 		return (LWRES_R_FAILURE);
464 
465 	percent = strchr(buf, '%');
466 	if (percent != NULL)
467 		*percent = 0;
468 
469 	if (lwres_net_aton(buffer, &v4) == 1) {
470 		if (convert_zero) {
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);
475 		}
476 		addr->family = LWRES_ADDRTYPE_V4;
477 		addr->length = NS_INADDRSZ;
478 		addr->zone = 0;
479 		memmove((void *)addr->address, &v4, NS_INADDRSZ);
480 
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) {
486 			unsigned long zone;
487 			char *ep;
488 
489 			percent++;
490 
491 #ifdef HAVE_IF_NAMETOINDEX
492 			zone = if_nametoindex(percent);
493 			if (zone != 0U) {
494 				addr->zone = zone;
495 				return (LWRES_R_SUCCESS);
496 			}
497 #endif
498 			zone = strtoul(percent, &ep, 10);
499 			if (ep != percent && *ep == 0)
500 				addr->zone = zone;
501 			else
502 				return (LWRES_R_FAILURE);
503 		} else
504 			addr->zone = 0;
505 	} else
506 		return (LWRES_R_FAILURE); /* Unrecognised format. */
507 
508 	return (LWRES_R_SUCCESS);
509 }
510 
511 static lwres_result_t
512 lwres_conf_parsesortlist(lwres_context_t *ctx,  FILE *fp) {
513 	int delim, res, idx;
514 	char word[LWRES_CONFMAXLINELEN];
515 	char *p;
516 	lwres_conf_t *confdata;
517 
518 	confdata = &ctx->confdata;
519 
520 	delim = getword(fp, word, sizeof(word));
521 	if (strlen(word) == 0U)
522 		return (LWRES_R_FAILURE); /* Empty line after keyword. */
523 
524 	while (strlen(word) > 0U) {
525 		if (confdata->sortlistnxt == LWRES_CONFMAXSORTLIST)
526 			return (LWRES_R_FAILURE); /* Too many values. */
527 
528 		p = strchr(word, '/');
529 		if (p != NULL)
530 			*p++ = '\0';
531 
532 		idx = confdata->sortlistnxt;
533 		res = lwres_create_addr(word, &confdata->sortlist[idx].addr, 1);
534 		if (res != LWRES_R_SUCCESS)
535 			return (res);
536 
537 		if (p != NULL) {
538 			res = lwres_create_addr(p,
539 						&confdata->sortlist[idx].mask,
540 						0);
541 			if (res != LWRES_R_SUCCESS)
542 				return (res);
543 		} else {
544 			/*
545 			 * Make up a mask.
546 			 */
547 			confdata->sortlist[idx].mask =
548 				confdata->sortlist[idx].addr;
549 
550 			memset(&confdata->sortlist[idx].mask.address, 0xff,
551 			       confdata->sortlist[idx].addr.length);
552 		}
553 
554 		confdata->sortlistnxt++;
555 
556 		if (delim == EOF || delim == '\n')
557 			break;
558 		else
559 			delim = getword(fp, word, sizeof(word));
560 	}
561 
562 	return (LWRES_R_SUCCESS);
563 }
564 
565 static lwres_result_t
566 lwres_conf_parseoption(lwres_context_t *ctx,  FILE *fp) {
567 	int delim;
568 	long ndots;
569 	char *p;
570 	char word[LWRES_CONFMAXLINELEN];
571 	lwres_conf_t *confdata;
572 
573 	REQUIRE(ctx != NULL);
574 	confdata = &ctx->confdata;
575 
576 	delim = getword(fp, word, sizeof(word));
577 	if (strlen(word) == 0U)
578 		return (LWRES_R_FAILURE); /* Empty line after keyword. */
579 
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;
592 		}
593 
594 		if (delim == EOF || delim == '\n')
595 			break;
596 		else
597 			delim = getword(fp, word, sizeof(word));
598 	}
599 
600 	return (LWRES_R_SUCCESS);
601 }
602 
603 /*% parses a file and fills in the data structure. */
604 lwres_result_t
605 lwres_conf_parse(lwres_context_t *ctx, const char *filename) {
606 	FILE *fp = NULL;
607 	char word[256];
608 	lwres_result_t rval, ret;
609 	lwres_conf_t *confdata;
610 	int stopchar;
611 
612 	REQUIRE(ctx != NULL);
613 	confdata = &ctx->confdata;
614 
615 	REQUIRE(filename != NULL);
616 	REQUIRE(strlen(filename) > 0U);
617 	REQUIRE(confdata != NULL);
618 
619 	errno = 0;
620 	if ((fp = fopen(filename, "r")) == NULL)
621 		return (LWRES_R_NOTFOUND);
622 
623 	ret = LWRES_R_SUCCESS;
624 	for (;;) {
625 		stopchar = getword(fp, word, sizeof(word));
626 		if (stopchar == EOF) {
627 			rval = LWRES_R_SUCCESS;
628 			POST(rval);
629 			break;
630 		}
631 
632 		if (strlen(word) == 0U)
633 			rval = LWRES_R_SUCCESS;
634 		else if (strcmp(word, "nameserver") == 0)
635 			rval = lwres_conf_parsenameserver(ctx, fp);
636 		else if (strcmp(word, "lwserver") == 0)
637 			rval = lwres_conf_parselwserver(ctx, fp);
638 		else if (strcmp(word, "domain") == 0)
639 			rval = lwres_conf_parsedomain(ctx, fp);
640 		else if (strcmp(word, "search") == 0)
641 			rval = lwres_conf_parsesearch(ctx, fp);
642 		else if (strcmp(word, "sortlist") == 0)
643 			rval = lwres_conf_parsesortlist(ctx, fp);
644 		else if (strcmp(word, "options") == 0)
645 			rval = lwres_conf_parseoption(ctx, fp);
646 		else {
647 			/* unrecognised word. Ignore entire line */
648 			rval = LWRES_R_SUCCESS;
649 			stopchar = eatline(fp);
650 			if (stopchar == EOF) {
651 				break;
652 			}
653 		}
654 		if (ret == LWRES_R_SUCCESS && rval != LWRES_R_SUCCESS)
655 			ret = rval;
656 	}
657 
658 	fclose(fp);
659 
660 	return (ret);
661 }
662 
663 /*% Prints the config data structure to the FILE. */
664 lwres_result_t
665 lwres_conf_print(lwres_context_t *ctx, FILE *fp) {
666 	int i;
667 	int af;
668 	char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
669 	char buf[sizeof("%4000000000")];
670 	const char *p;
671 	lwres_conf_t *confdata;
672 	lwres_addr_t tmpaddr;
673 
674 	REQUIRE(ctx != NULL);
675 	confdata = &ctx->confdata;
676 
677 	REQUIRE(confdata->nsnext <= LWRES_CONFMAXNAMESERVERS);
678 
679 	for (i = 0; i < confdata->nsnext; i++) {
680 		af = lwresaddr2af(confdata->nameservers[i].family);
681 
682 		p = lwres_net_ntop(af, confdata->nameservers[i].address,
683 				   tmp, sizeof(tmp));
684 		if (p != tmp)
685 			return (LWRES_R_FAILURE);
686 
687 		if (af == AF_INET6 && confdata->lwservers[i].zone != 0) {
688 			snprintf(buf, sizeof(buf), "%%%u",
689 				confdata->nameservers[i].zone);
690 		} else
691 			buf[0] = 0;
692 
693 		fprintf(fp, "nameserver %s%s\n", tmp, buf);
694 	}
695 
696 	for (i = 0; i < confdata->lwnext; i++) {
697 		af = lwresaddr2af(confdata->lwservers[i].family);
698 
699 		p = lwres_net_ntop(af, confdata->lwservers[i].address,
700 				   tmp, sizeof(tmp));
701 		if (p != tmp)
702 			return (LWRES_R_FAILURE);
703 
704 		if (af == AF_INET6 && confdata->lwservers[i].zone != 0) {
705 			snprintf(buf, sizeof(buf), "%%%u",
706 				confdata->nameservers[i].zone);
707 		} else
708 			buf[0] = 0;
709 
710 		fprintf(fp, "lwserver %s%s\n", tmp, buf);
711 	}
712 
713 	if (confdata->domainname != NULL) {
714 		fprintf(fp, "domain %s\n", confdata->domainname);
715 	} else if (confdata->searchnxt > 0) {
716 		REQUIRE(confdata->searchnxt <= LWRES_CONFMAXSEARCH);
717 
718 		fprintf(fp, "search");
719 		for (i = 0; i < confdata->searchnxt; i++)
720 			fprintf(fp, " %s", confdata->search[i]);
721 		fputc('\n', fp);
722 	}
723 
724 	REQUIRE(confdata->sortlistnxt <= LWRES_CONFMAXSORTLIST);
725 
726 	if (confdata->sortlistnxt > 0) {
727 		fputs("sortlist", fp);
728 		for (i = 0; i < confdata->sortlistnxt; i++) {
729 			af = lwresaddr2af(confdata->sortlist[i].addr.family);
730 
731 			p = lwres_net_ntop(af,
732 					   confdata->sortlist[i].addr.address,
733 					   tmp, sizeof(tmp));
734 			if (p != tmp)
735 				return (LWRES_R_FAILURE);
736 
737 			fprintf(fp, " %s", tmp);
738 
739 			tmpaddr = confdata->sortlist[i].mask;
740 			memset(&tmpaddr.address, 0xff, tmpaddr.length);
741 
742 			if (memcmp(&tmpaddr.address,
743 				   confdata->sortlist[i].mask.address,
744 				   confdata->sortlist[i].mask.length) != 0) {
745 				af = lwresaddr2af(
746 					    confdata->sortlist[i].mask.family);
747 				p = lwres_net_ntop
748 					(af,
749 					 confdata->sortlist[i].mask.address,
750 					 tmp, sizeof(tmp));
751 				if (p != tmp)
752 					return (LWRES_R_FAILURE);
753 
754 				fprintf(fp, "/%s", tmp);
755 			}
756 		}
757 		fputc('\n', fp);
758 	}
759 
760 	if (confdata->resdebug)
761 		fprintf(fp, "options debug\n");
762 
763 	if (confdata->ndots > 0)
764 		fprintf(fp, "options ndots:%d\n", confdata->ndots);
765 
766 	if (confdata->no_tld_query)
767 		fprintf(fp, "options no_tld_query\n");
768 
769 	return (LWRES_R_SUCCESS);
770 }
771 
772 /*% Returns a pointer to the current config structure. */
773 lwres_conf_t *
774 lwres_conf_get(lwres_context_t *ctx) {
775 	REQUIRE(ctx != NULL);
776 
777 	return (&ctx->confdata);
778 }
779