Import bind 9.5.2 vendor sources.
[dragonfly.git] / contrib / bind-9.5.2 / lib / lwres / lwconfig.c
1 /*
2  * Copyright (C) 2004-2008  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2000-2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15  * PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 /* $Id: lwconfig.c,v 1.46.128.2 2008/12/17 23:46:34 tbox Exp $ */
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
71 #include "assert_p.h"
72 #include "context_p.h"
73
74
75 #if ! defined(NS_INADDRSZ)
76 #define NS_INADDRSZ      4
77 #endif
78
79 #if ! defined(NS_IN6ADDRSZ)
80 #define NS_IN6ADDRSZ    16
81 #endif
82
83 static lwres_result_t
84 lwres_conf_parsenameserver(lwres_context_t *ctx,  FILE *fp);
85
86 static lwres_result_t
87 lwres_conf_parselwserver(lwres_context_t *ctx,  FILE *fp);
88
89 static lwres_result_t
90 lwres_conf_parsedomain(lwres_context_t *ctx, FILE *fp);
91
92 static lwres_result_t
93 lwres_conf_parsesearch(lwres_context_t *ctx,  FILE *fp);
94
95 static lwres_result_t
96 lwres_conf_parsesortlist(lwres_context_t *ctx,  FILE *fp);
97
98 static lwres_result_t
99 lwres_conf_parseoption(lwres_context_t *ctx,  FILE *fp);
100
101 static void
102 lwres_resetaddr(lwres_addr_t *addr);
103
104 static lwres_result_t
105 lwres_create_addr(const char *buff, lwres_addr_t *addr, int convert_zero);
106
107 static int lwresaddr2af(int lwresaddrtype);
108
109
110 static int
111 lwresaddr2af(int lwresaddrtype)
112 {
113         int af = 0;
114
115         switch (lwresaddrtype) {
116         case LWRES_ADDRTYPE_V4:
117                 af = AF_INET;
118                 break;
119
120         case LWRES_ADDRTYPE_V6:
121                 af = AF_INET6;
122                 break;
123         }
124
125         return (af);
126 }
127
128
129 /*!
130  * Eat characters from FP until EOL or EOF. Returns EOF or '\n'
131  */
132 static int
133 eatline(FILE *fp) {
134         int ch;
135
136         ch = fgetc(fp);
137         while (ch != '\n' && ch != EOF)
138                 ch = fgetc(fp);
139
140         return (ch);
141 }
142
143
144 /*!
145  * Eats white space up to next newline or non-whitespace character (of
146  * EOF). Returns the last character read. Comments are considered white
147  * space.
148  */
149 static int
150 eatwhite(FILE *fp) {
151         int ch;
152
153         ch = fgetc(fp);
154         while (ch != '\n' && ch != EOF && isspace((unsigned char)ch))
155                 ch = fgetc(fp);
156
157         if (ch == ';' || ch == '#')
158                 ch = eatline(fp);
159
160         return (ch);
161 }
162
163
164 /*!
165  * Skip over any leading whitespace and then read in the next sequence of
166  * non-whitespace characters. In this context newline is not considered
167  * whitespace. Returns EOF on end-of-file, or the character
168  * that caused the reading to stop.
169  */
170 static int
171 getword(FILE *fp, char *buffer, size_t size) {
172         int ch;
173         char *p = buffer;
174
175         REQUIRE(buffer != NULL);
176         REQUIRE(size > 0U);
177
178         *p = '\0';
179
180         ch = eatwhite(fp);
181
182         if (ch == EOF)
183                 return (EOF);
184
185         do {
186                 *p = '\0';
187
188                 if (ch == EOF || isspace((unsigned char)ch))
189                         break;
190                 else if ((size_t) (p - buffer) == size - 1)
191                         return (EOF);   /* Not enough space. */
192
193                 *p++ = (char)ch;
194                 ch = fgetc(fp);
195         } while (1);
196
197         return (ch);
198 }
199
200 static void
201 lwres_resetaddr(lwres_addr_t *addr) {
202         REQUIRE(addr != NULL);
203
204         memset(addr->address, 0, LWRES_ADDR_MAXLEN);
205         addr->family = 0;
206         addr->length = 0;
207 }
208
209 static char *
210 lwres_strdup(lwres_context_t *ctx, const char *str) {
211         char *p;
212
213         REQUIRE(str != NULL);
214         REQUIRE(strlen(str) > 0U);
215
216         p = CTXMALLOC(strlen(str) + 1);
217         if (p != NULL)
218                 strcpy(p, str);
219
220         return (p);
221 }
222
223 /*% intializes data structure for subsequent config parsing. */
224 void
225 lwres_conf_init(lwres_context_t *ctx) {
226         int i;
227         lwres_conf_t *confdata;
228
229         REQUIRE(ctx != NULL);
230         confdata = &ctx->confdata;
231
232         confdata->nsnext = 0;
233         confdata->lwnext = 0;
234         confdata->domainname = NULL;
235         confdata->searchnxt = 0;
236         confdata->sortlistnxt = 0;
237         confdata->resdebug = 0;
238         confdata->ndots = 1;
239         confdata->no_tld_query = 0;
240
241         for (i = 0; i < LWRES_CONFMAXNAMESERVERS; i++)
242                 lwres_resetaddr(&confdata->nameservers[i]);
243
244         for (i = 0; i < LWRES_CONFMAXSEARCH; i++)
245                 confdata->search[i] = NULL;
246
247         for (i = 0; i < LWRES_CONFMAXSORTLIST; i++) {
248                 lwres_resetaddr(&confdata->sortlist[i].addr);
249                 lwres_resetaddr(&confdata->sortlist[i].mask);
250         }
251 }
252
253 /*% Frees up all the internal memory used by the config data structure, returning it to the lwres_context_t. */
254 void
255 lwres_conf_clear(lwres_context_t *ctx) {
256         int i;
257         lwres_conf_t *confdata;
258
259         REQUIRE(ctx != NULL);
260         confdata = &ctx->confdata;
261
262         for (i = 0; i < confdata->nsnext; i++)
263                 lwres_resetaddr(&confdata->nameservers[i]);
264
265         if (confdata->domainname != NULL) {
266                 CTXFREE(confdata->domainname,
267                         strlen(confdata->domainname) + 1);
268                 confdata->domainname = NULL;
269         }
270
271         for (i = 0; i < confdata->searchnxt; i++) {
272                 if (confdata->search[i] != NULL) {
273                         CTXFREE(confdata->search[i],
274                                 strlen(confdata->search[i]) + 1);
275                         confdata->search[i] = NULL;
276                 }
277         }
278
279         for (i = 0; i < LWRES_CONFMAXSORTLIST; i++) {
280                 lwres_resetaddr(&confdata->sortlist[i].addr);
281                 lwres_resetaddr(&confdata->sortlist[i].mask);
282         }
283
284         confdata->nsnext = 0;
285         confdata->lwnext = 0;
286         confdata->domainname = NULL;
287         confdata->searchnxt = 0;
288         confdata->sortlistnxt = 0;
289         confdata->resdebug = 0;
290         confdata->ndots = 1;
291         confdata->no_tld_query = 0;
292 }
293
294 static lwres_result_t
295 lwres_conf_parsenameserver(lwres_context_t *ctx,  FILE *fp) {
296         char word[LWRES_CONFMAXLINELEN];
297         int res;
298         lwres_conf_t *confdata;
299         lwres_addr_t address;
300
301         confdata = &ctx->confdata;
302
303         if (confdata->nsnext == LWRES_CONFMAXNAMESERVERS)
304                 return (LWRES_R_SUCCESS);
305
306         res = getword(fp, word, sizeof(word));
307         if (strlen(word) == 0U)
308                 return (LWRES_R_FAILURE); /* Nothing on line. */
309         else if (res == ' ' || res == '\t')
310                 res = eatwhite(fp);
311
312         if (res != EOF && res != '\n')
313                 return (LWRES_R_FAILURE); /* Extra junk on line. */
314
315         res = lwres_create_addr(word, &address, 1);
316         if (res == LWRES_R_SUCCESS &&
317             ((address.family == LWRES_ADDRTYPE_V4 && ctx->use_ipv4 == 1) ||
318              (address.family == LWRES_ADDRTYPE_V6 && ctx->use_ipv6 == 1))) {
319                 confdata->nameservers[confdata->nsnext++] = address;
320         }
321
322         return (LWRES_R_SUCCESS);
323 }
324
325 static lwres_result_t
326 lwres_conf_parselwserver(lwres_context_t *ctx,  FILE *fp) {
327         char word[LWRES_CONFMAXLINELEN];
328         int res;
329         lwres_conf_t *confdata;
330
331         confdata = &ctx->confdata;
332
333         if (confdata->lwnext == LWRES_CONFMAXLWSERVERS)
334                 return (LWRES_R_SUCCESS);
335
336         res = getword(fp, word, sizeof(word));
337         if (strlen(word) == 0U)
338                 return (LWRES_R_FAILURE); /* Nothing on line. */
339         else if (res == ' ' || res == '\t')
340                 res = eatwhite(fp);
341
342         if (res != EOF && res != '\n')
343                 return (LWRES_R_FAILURE); /* Extra junk on line. */
344
345         res = lwres_create_addr(word,
346                                 &confdata->lwservers[confdata->lwnext++], 1);
347         if (res != LWRES_R_SUCCESS)
348                 return (res);
349
350         return (LWRES_R_SUCCESS);
351 }
352
353 static lwres_result_t
354 lwres_conf_parsedomain(lwres_context_t *ctx,  FILE *fp) {
355         char word[LWRES_CONFMAXLINELEN];
356         int res, i;
357         lwres_conf_t *confdata;
358
359         confdata = &ctx->confdata;
360
361         res = getword(fp, word, sizeof(word));
362         if (strlen(word) == 0U)
363                 return (LWRES_R_FAILURE); /* Nothing else on line. */
364         else if (res == ' ' || res == '\t')
365                 res = eatwhite(fp);
366
367         if (res != EOF && res != '\n')
368                 return (LWRES_R_FAILURE); /* Extra junk on line. */
369
370         if (confdata->domainname != NULL)
371                 CTXFREE(confdata->domainname,
372                         strlen(confdata->domainname) + 1); /*  */
373
374         /*
375          * Search and domain are mutually exclusive.
376          */
377         for (i = 0; i < LWRES_CONFMAXSEARCH; i++) {
378                 if (confdata->search[i] != NULL) {
379                         CTXFREE(confdata->search[i],
380                                 strlen(confdata->search[i])+1);
381                         confdata->search[i] = NULL;
382                 }
383         }
384         confdata->searchnxt = 0;
385
386         confdata->domainname = lwres_strdup(ctx, word);
387
388         if (confdata->domainname == NULL)
389                 return (LWRES_R_FAILURE);
390
391         return (LWRES_R_SUCCESS);
392 }
393
394 static lwres_result_t
395 lwres_conf_parsesearch(lwres_context_t *ctx,  FILE *fp) {
396         int idx, delim;
397         char word[LWRES_CONFMAXLINELEN];
398         lwres_conf_t *confdata;
399
400         confdata = &ctx->confdata;
401
402         if (confdata->domainname != NULL) {
403                 /*
404                  * Search and domain are mutually exclusive.
405                  */
406                 CTXFREE(confdata->domainname,
407                         strlen(confdata->domainname) + 1);
408                 confdata->domainname = NULL;
409         }
410
411         /*
412          * Remove any previous search definitions.
413          */
414         for (idx = 0; idx < LWRES_CONFMAXSEARCH; idx++) {
415                 if (confdata->search[idx] != NULL) {
416                         CTXFREE(confdata->search[idx],
417                                 strlen(confdata->search[idx])+1);
418                         confdata->search[idx] = NULL;
419                 }
420         }
421         confdata->searchnxt = 0;
422
423         delim = getword(fp, word, sizeof(word));
424         if (strlen(word) == 0U)
425                 return (LWRES_R_FAILURE); /* Nothing else on line. */
426
427         idx = 0;
428         while (strlen(word) > 0U) {
429                 if (confdata->searchnxt == LWRES_CONFMAXSEARCH)
430                         goto ignore; /* Too many domains. */
431
432                 confdata->search[idx] = lwres_strdup(ctx, word);
433                 if (confdata->search[idx] == NULL)
434                         return (LWRES_R_FAILURE);
435                 idx++;
436                 confdata->searchnxt++;
437
438         ignore:
439                 if (delim == EOF || delim == '\n')
440                         break;
441                 else
442                         delim = getword(fp, word, sizeof(word));
443         }
444
445         return (LWRES_R_SUCCESS);
446 }
447
448 static lwres_result_t
449 lwres_create_addr(const char *buffer, lwres_addr_t *addr, int convert_zero) {
450         struct in_addr v4;
451         struct in6_addr v6;
452
453         if (lwres_net_aton(buffer, &v4) == 1) {
454                 if (convert_zero) {
455                         unsigned char zeroaddress[] = {0, 0, 0, 0};
456                         unsigned char loopaddress[] = {127, 0, 0, 1};
457                         if (memcmp(&v4, zeroaddress, 4) == 0)
458                                 memcpy(&v4, loopaddress, 4);
459                 }
460                 addr->family = LWRES_ADDRTYPE_V4;
461                 addr->length = NS_INADDRSZ;
462                 memcpy((void *)addr->address, &v4, NS_INADDRSZ);
463
464         } else if (lwres_net_pton(AF_INET6, buffer, &v6) == 1) {
465                 addr->family = LWRES_ADDRTYPE_V6;
466                 addr->length = NS_IN6ADDRSZ;
467                 memcpy((void *)addr->address, &v6, NS_IN6ADDRSZ);
468         } else {
469                 return (LWRES_R_FAILURE); /* Unrecognised format. */
470         }
471
472         return (LWRES_R_SUCCESS);
473 }
474
475 static lwres_result_t
476 lwres_conf_parsesortlist(lwres_context_t *ctx,  FILE *fp) {
477         int delim, res, idx;
478         char word[LWRES_CONFMAXLINELEN];
479         char *p;
480         lwres_conf_t *confdata;
481
482         confdata = &ctx->confdata;
483
484         delim = getword(fp, word, sizeof(word));
485         if (strlen(word) == 0U)
486                 return (LWRES_R_FAILURE); /* Empty line after keyword. */
487
488         while (strlen(word) > 0U) {
489                 if (confdata->sortlistnxt == LWRES_CONFMAXSORTLIST)
490                         return (LWRES_R_FAILURE); /* Too many values. */
491
492                 p = strchr(word, '/');
493                 if (p != NULL)
494                         *p++ = '\0';
495
496                 idx = confdata->sortlistnxt;
497                 res = lwres_create_addr(word, &confdata->sortlist[idx].addr, 1);
498                 if (res != LWRES_R_SUCCESS)
499                         return (res);
500
501                 if (p != NULL) {
502                         res = lwres_create_addr(p,
503                                                 &confdata->sortlist[idx].mask,
504                                                 0);
505                         if (res != LWRES_R_SUCCESS)
506                                 return (res);
507                 } else {
508                         /*
509                          * Make up a mask.
510                          */
511                         confdata->sortlist[idx].mask =
512                                 confdata->sortlist[idx].addr;
513
514                         memset(&confdata->sortlist[idx].mask.address, 0xff,
515                                confdata->sortlist[idx].addr.length);
516                 }
517
518                 confdata->sortlistnxt++;
519
520                 if (delim == EOF || delim == '\n')
521                         break;
522                 else
523                         delim = getword(fp, word, sizeof(word));
524         }
525
526         return (LWRES_R_SUCCESS);
527 }
528
529 static lwres_result_t
530 lwres_conf_parseoption(lwres_context_t *ctx,  FILE *fp) {
531         int delim;
532         long ndots;
533         char *p;
534         char word[LWRES_CONFMAXLINELEN];
535         lwres_conf_t *confdata;
536
537         REQUIRE(ctx != NULL);
538         confdata = &ctx->confdata;
539
540         delim = getword(fp, word, sizeof(word));
541         if (strlen(word) == 0U)
542                 return (LWRES_R_FAILURE); /* Empty line after keyword. */
543
544         while (strlen(word) > 0U) {
545                 if (strcmp("debug", word) == 0) {
546                         confdata->resdebug = 1;
547                 } else if (strcmp("no_tld_query", word) == 0) {
548                         confdata->no_tld_query = 1;
549                 } else if (strncmp("ndots:", word, 6) == 0) {
550                         ndots = strtol(word + 6, &p, 10);
551                         if (*p != '\0') /* Bad string. */
552                                 return (LWRES_R_FAILURE);
553                         if (ndots < 0 || ndots > 0xff) /* Out of range. */
554                                 return (LWRES_R_FAILURE);
555                         confdata->ndots = (lwres_uint8_t)ndots;
556                 }
557
558                 if (delim == EOF || delim == '\n')
559                         break;
560                 else
561                         delim = getword(fp, word, sizeof(word));
562         }
563
564         return (LWRES_R_SUCCESS);
565 }
566
567 /*% parses a file and fills in the data structure. */
568 lwres_result_t
569 lwres_conf_parse(lwres_context_t *ctx, const char *filename) {
570         FILE *fp = NULL;
571         char word[256];
572         lwres_result_t rval, ret;
573         lwres_conf_t *confdata;
574         int stopchar;
575
576         REQUIRE(ctx != NULL);
577         confdata = &ctx->confdata;
578
579         REQUIRE(filename != NULL);
580         REQUIRE(strlen(filename) > 0U);
581         REQUIRE(confdata != NULL);
582
583         errno = 0;
584         if ((fp = fopen(filename, "r")) == NULL)
585                 return (LWRES_R_NOTFOUND);
586
587         ret = LWRES_R_SUCCESS;
588         do {
589                 stopchar = getword(fp, word, sizeof(word));
590                 if (stopchar == EOF) {
591                         rval = LWRES_R_SUCCESS;
592                         break;
593                 }
594
595                 if (strlen(word) == 0U)
596                         rval = LWRES_R_SUCCESS;
597                 else if (strcmp(word, "nameserver") == 0)
598                         rval = lwres_conf_parsenameserver(ctx, fp);
599                 else if (strcmp(word, "lwserver") == 0)
600                         rval = lwres_conf_parselwserver(ctx, fp);
601                 else if (strcmp(word, "domain") == 0)
602                         rval = lwres_conf_parsedomain(ctx, fp);
603                 else if (strcmp(word, "search") == 0)
604                         rval = lwres_conf_parsesearch(ctx, fp);
605                 else if (strcmp(word, "sortlist") == 0)
606                         rval = lwres_conf_parsesortlist(ctx, fp);
607                 else if (strcmp(word, "options") == 0)
608                         rval = lwres_conf_parseoption(ctx, fp);
609                 else {
610                         /* unrecognised word. Ignore entire line */
611                         rval = LWRES_R_SUCCESS;
612                         stopchar = eatline(fp);
613                         if (stopchar == EOF) {
614                                 break;
615                         }
616                 }
617                 if (ret == LWRES_R_SUCCESS && rval != LWRES_R_SUCCESS)
618                         ret = rval;
619         } while (1);
620
621         fclose(fp);
622
623         return (ret);
624 }
625
626 /*% Prints the config data structure to the FILE. */
627 lwres_result_t
628 lwres_conf_print(lwres_context_t *ctx, FILE *fp) {
629         int i;
630         int af;
631         char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
632         const char *p;
633         lwres_conf_t *confdata;
634         lwres_addr_t tmpaddr;
635
636         REQUIRE(ctx != NULL);
637         confdata = &ctx->confdata;
638
639         REQUIRE(confdata->nsnext <= LWRES_CONFMAXNAMESERVERS);
640
641         for (i = 0; i < confdata->nsnext; i++) {
642                 af = lwresaddr2af(confdata->nameservers[i].family);
643
644                 p = lwres_net_ntop(af, confdata->nameservers[i].address,
645                                    tmp, sizeof(tmp));
646                 if (p != tmp)
647                         return (LWRES_R_FAILURE);
648
649                 fprintf(fp, "nameserver %s\n", tmp);
650         }
651
652         for (i = 0; i < confdata->lwnext; i++) {
653                 af = lwresaddr2af(confdata->lwservers[i].family);
654
655                 p = lwres_net_ntop(af, confdata->lwservers[i].address,
656                                    tmp, sizeof(tmp));
657                 if (p != tmp)
658                         return (LWRES_R_FAILURE);
659
660                 fprintf(fp, "lwserver %s\n", tmp);
661         }
662
663         if (confdata->domainname != NULL) {
664                 fprintf(fp, "domain %s\n", confdata->domainname);
665         } else if (confdata->searchnxt > 0) {
666                 REQUIRE(confdata->searchnxt <= LWRES_CONFMAXSEARCH);
667
668                 fprintf(fp, "search");
669                 for (i = 0; i < confdata->searchnxt; i++)
670                         fprintf(fp, " %s", confdata->search[i]);
671                 fputc('\n', fp);
672         }
673
674         REQUIRE(confdata->sortlistnxt <= LWRES_CONFMAXSORTLIST);
675
676         if (confdata->sortlistnxt > 0) {
677                 fputs("sortlist", fp);
678                 for (i = 0; i < confdata->sortlistnxt; i++) {
679                         af = lwresaddr2af(confdata->sortlist[i].addr.family);
680
681                         p = lwres_net_ntop(af,
682                                            confdata->sortlist[i].addr.address,
683                                            tmp, sizeof(tmp));
684                         if (p != tmp)
685                                 return (LWRES_R_FAILURE);
686
687                         fprintf(fp, " %s", tmp);
688
689                         tmpaddr = confdata->sortlist[i].mask;
690                         memset(&tmpaddr.address, 0xff, tmpaddr.length);
691
692                         if (memcmp(&tmpaddr.address,
693                                    confdata->sortlist[i].mask.address,
694                                    confdata->sortlist[i].mask.length) != 0) {
695                                 af = lwresaddr2af(
696                                             confdata->sortlist[i].mask.family);
697                                 p = lwres_net_ntop
698                                         (af,
699                                          confdata->sortlist[i].mask.address,
700                                          tmp, sizeof(tmp));
701                                 if (p != tmp)
702                                         return (LWRES_R_FAILURE);
703
704                                 fprintf(fp, "/%s", tmp);
705                         }
706                 }
707                 fputc('\n', fp);
708         }
709
710         if (confdata->resdebug)
711                 fprintf(fp, "options debug\n");
712
713         if (confdata->ndots > 0)
714                 fprintf(fp, "options ndots:%d\n", confdata->ndots);
715
716         if (confdata->no_tld_query)
717                 fprintf(fp, "options no_tld_query\n");
718
719         return (LWRES_R_SUCCESS);
720 }
721
722 /*% Returns a pointer to the current config structure. */
723 lwres_conf_t *
724 lwres_conf_get(lwres_context_t *ctx) {
725         REQUIRE(ctx != NULL);
726
727         return (&ctx->confdata);
728 }