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