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