]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - contrib/bind9/lib/irs/resconf.c
MFV r306384:
[FreeBSD/stable/9.git] / contrib / bind9 / lib / irs / resconf.c
1 /*
2  * Copyright (C) 2009, 2011, 2012, 2014, 2015  Internet Systems Consortium, Inc. ("ISC")
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14  * PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 /* $Id$ */
18
19 /*! \file resconf.c */
20
21 /**
22  * Module for parsing resolv.conf files (largely derived from lwconfig.c).
23  *
24  *    irs_resconf_load() opens the file filename and parses it to initialize
25  *    the configuration structure.
26  *
27  * \section lwconfig_return Return Values
28  *
29  *    irs_resconf_load() returns #IRS_R_SUCCESS if it successfully read and
30  *    parsed filename. It returns a non-0 error code if filename could not be
31  *    opened or contained incorrect resolver statements.
32  *
33  * \section lwconfig_see See Also
34  *
35  *    stdio(3), \link resolver resolver \endlink
36  *
37  * \section files Files
38  *
39  *    /etc/resolv.conf
40  */
41
42 #include <config.h>
43
44 #include <sys/types.h>
45 #include <sys/socket.h>
46
47 #include <ctype.h>
48 #include <errno.h>
49 #include <netdb.h>
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <string.h>
53
54 #include <isc/magic.h>
55 #include <isc/mem.h>
56 #include <isc/netaddr.h>
57 #include <isc/sockaddr.h>
58 #include <isc/util.h>
59
60 #include <irs/resconf.h>
61
62 #define IRS_RESCONF_MAGIC               ISC_MAGIC('R', 'E', 'S', 'c')
63 #define IRS_RESCONF_VALID(c)            ISC_MAGIC_VALID(c, IRS_RESCONF_MAGIC)
64
65 /*!
66  * protocol constants
67  */
68
69 #if ! defined(NS_INADDRSZ)
70 #define NS_INADDRSZ      4
71 #endif
72
73 #if ! defined(NS_IN6ADDRSZ)
74 #define NS_IN6ADDRSZ    16
75 #endif
76
77 /*!
78  * resolv.conf parameters
79  */
80
81 #define RESCONFMAXNAMESERVERS 3         /*%< max 3 "nameserver" entries */
82 #define RESCONFMAXSEARCH 8              /*%< max 8 domains in "search" entry */
83 #define RESCONFMAXLINELEN 256           /*%< max size of a line */
84 #define RESCONFMAXSORTLIST 10           /*%< max 10 */
85
86 /*!
87  * configuration data structure
88  */
89
90 struct irs_resconf {
91         /*
92          * The configuration data is a thread-specific object, and does not
93          * need to be locked.
94          */
95         unsigned int            magic;
96         isc_mem_t               *mctx;
97
98         isc_sockaddrlist_t      nameservers;
99         unsigned int            numns; /*%< number of configured servers */
100
101         char                    *domainname;
102         char                    *search[RESCONFMAXSEARCH];
103         isc_uint8_t             searchnxt; /*%< index for next free slot */
104
105         irs_resconf_searchlist_t searchlist;
106
107         struct {
108                 isc_netaddr_t   addr;
109                 /*% mask has a non-zero 'family' if set */
110                 isc_netaddr_t   mask;
111         } sortlist[RESCONFMAXSORTLIST];
112         isc_uint8_t             sortlistnxt;
113
114         /*%< non-zero if 'options debug' set */
115         isc_uint8_t             resdebug;
116         /*%< set to n in 'options ndots:n' */
117         isc_uint8_t             ndots;
118 };
119
120 static isc_result_t
121 resconf_parsenameserver(irs_resconf_t *conf,  FILE *fp);
122 static isc_result_t
123 resconf_parsedomain(irs_resconf_t *conf,  FILE *fp);
124 static isc_result_t
125 resconf_parsesearch(irs_resconf_t *conf,  FILE *fp);
126 static isc_result_t
127 resconf_parsesortlist(irs_resconf_t *conf,  FILE *fp);
128 static isc_result_t
129 resconf_parseoption(irs_resconf_t *ctx,  FILE *fp);
130
131 /*!
132  * Eat characters from FP until EOL or EOF. Returns EOF or '\n'
133  */
134 static int
135 eatline(FILE *fp) {
136         int ch;
137
138         ch = fgetc(fp);
139         while (ch != '\n' && ch != EOF)
140                 ch = fgetc(fp);
141
142         return (ch);
143 }
144
145 /*!
146  * Eats white space up to next newline or non-whitespace character (of
147  * EOF). Returns the last character read. Comments are considered white
148  * space.
149  */
150 static int
151 eatwhite(FILE *fp) {
152         int ch;
153
154         ch = fgetc(fp);
155         while (ch != '\n' && ch != EOF && isspace((unsigned char)ch))
156                 ch = fgetc(fp);
157
158         if (ch == ';' || ch == '#')
159                 ch = eatline(fp);
160
161         return (ch);
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;
174
175         REQUIRE(buffer != NULL);
176         REQUIRE(size > 0U);
177
178         p = buffer;
179         *p = '\0';
180
181         ch = eatwhite(fp);
182
183         if (ch == EOF)
184                 return (EOF);
185
186         do {
187                 *p = '\0';
188
189                 if (ch == EOF || isspace((unsigned char)ch))
190                         break;
191                 else if ((size_t) (p - buffer) == size - 1)
192                         return (EOF);   /* Not enough space. */
193
194                 *p++ = (char)ch;
195                 ch = fgetc(fp);
196         } while (1);
197
198         return (ch);
199 }
200
201 static isc_result_t
202 add_server(isc_mem_t *mctx, const char *address_str,
203            isc_sockaddrlist_t *nameservers)
204 {
205         int error;
206         isc_sockaddr_t *address = NULL;
207         struct addrinfo hints, *res;
208         isc_result_t result = ISC_R_SUCCESS;
209
210         res = NULL;
211         memset(&hints, 0, sizeof(hints));
212         hints.ai_family = AF_UNSPEC;
213         hints.ai_socktype = SOCK_DGRAM;
214         hints.ai_protocol = IPPROTO_UDP;
215         hints.ai_flags = AI_NUMERICHOST;
216         error = getaddrinfo(address_str, "53", &hints, &res);
217         if (error != 0)
218                 return (ISC_R_BADADDRESSFORM);
219
220         /* XXX: special case: treat all-0 IPv4 address as loopback */
221         if (res->ai_family == AF_INET) {
222                 struct in_addr *v4;
223                 unsigned char zeroaddress[] = {0, 0, 0, 0};
224                 unsigned char loopaddress[] = {127, 0, 0, 1};
225
226                 v4 = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
227                 if (memcmp(v4, zeroaddress, 4) == 0)
228                         memmove(v4, loopaddress, 4);
229         }
230
231         address = isc_mem_get(mctx, sizeof(*address));
232         if (address == NULL) {
233                 result = ISC_R_NOMEMORY;
234                 goto cleanup;
235         }
236         if (res->ai_addrlen > sizeof(address->type)) {
237                 isc_mem_put(mctx, address, sizeof(*address));
238                 result = ISC_R_RANGE;
239                 goto cleanup;
240         }
241         address->length = res->ai_addrlen;
242         memmove(&address->type.ss, res->ai_addr, res->ai_addrlen);
243         ISC_LINK_INIT(address, link);
244         ISC_LIST_APPEND(*nameservers, address, link);
245
246   cleanup:
247         freeaddrinfo(res);
248
249         return (result);
250 }
251
252 static isc_result_t
253 create_addr(const char *buffer, isc_netaddr_t *addr, int convert_zero) {
254         struct in_addr v4;
255         struct in6_addr v6;
256
257         if (inet_aton(buffer, &v4) == 1) {
258                 if (convert_zero) {
259                         unsigned char zeroaddress[] = {0, 0, 0, 0};
260                         unsigned char loopaddress[] = {127, 0, 0, 1};
261                         if (memcmp(&v4, zeroaddress, 4) == 0)
262                                 memmove(&v4, loopaddress, 4);
263                 }
264                 addr->family = AF_INET;
265                 memmove(&addr->type.in, &v4, NS_INADDRSZ);
266                 addr->zone = 0;
267         } else if (inet_pton(AF_INET6, buffer, &v6) == 1) {
268                 addr->family = AF_INET6;
269                 memmove(&addr->type.in6, &v6, NS_IN6ADDRSZ);
270                 addr->zone = 0;
271         } else
272                 return (ISC_R_BADADDRESSFORM); /* Unrecognised format. */
273
274         return (ISC_R_SUCCESS);
275 }
276
277 static isc_result_t
278 resconf_parsenameserver(irs_resconf_t *conf,  FILE *fp) {
279         char word[RESCONFMAXLINELEN];
280         int cp;
281         isc_result_t result;
282
283         if (conf->numns == RESCONFMAXNAMESERVERS)
284                 return (ISC_R_SUCCESS);
285
286         cp = getword(fp, word, sizeof(word));
287         if (strlen(word) == 0U)
288                 return (ISC_R_UNEXPECTEDEND); /* Nothing on line. */
289         else if (cp == ' ' || cp == '\t')
290                 cp = eatwhite(fp);
291
292         if (cp != EOF && cp != '\n')
293                 return (ISC_R_UNEXPECTEDTOKEN); /* Extra junk on line. */
294
295         result = add_server(conf->mctx, word, &conf->nameservers);
296         if (result != ISC_R_SUCCESS)
297                 return (result);
298         conf->numns++;
299
300         return (ISC_R_SUCCESS);
301 }
302
303 static isc_result_t
304 resconf_parsedomain(irs_resconf_t *conf,  FILE *fp) {
305         char word[RESCONFMAXLINELEN];
306         int res, i;
307
308         res = getword(fp, word, sizeof(word));
309         if (strlen(word) == 0U)
310                 return (ISC_R_UNEXPECTEDEND); /* Nothing else on line. */
311         else if (res == ' ' || res == '\t')
312                 res = eatwhite(fp);
313
314         if (res != EOF && res != '\n')
315                 return (ISC_R_UNEXPECTEDTOKEN); /* Extra junk on line. */
316
317         if (conf->domainname != NULL)
318                 isc_mem_free(conf->mctx, conf->domainname);
319
320         /*
321          * Search and domain are mutually exclusive.
322          */
323         for (i = 0; i < RESCONFMAXSEARCH; i++) {
324                 if (conf->search[i] != NULL) {
325                         isc_mem_free(conf->mctx, conf->search[i]);
326                         conf->search[i] = NULL;
327                 }
328         }
329         conf->searchnxt = 0;
330
331         conf->domainname = isc_mem_strdup(conf->mctx, word);
332         if (conf->domainname == NULL)
333                 return (ISC_R_NOMEMORY);
334
335         return (ISC_R_SUCCESS);
336 }
337
338 static isc_result_t
339 resconf_parsesearch(irs_resconf_t *conf,  FILE *fp) {
340         int idx, delim;
341         char word[RESCONFMAXLINELEN];
342
343         if (conf->domainname != NULL) {
344                 /*
345                  * Search and domain are mutually exclusive.
346                  */
347                 isc_mem_free(conf->mctx, conf->domainname);
348                 conf->domainname = NULL;
349         }
350
351         /*
352          * Remove any previous search definitions.
353          */
354         for (idx = 0; idx < RESCONFMAXSEARCH; idx++) {
355                 if (conf->search[idx] != NULL) {
356                         isc_mem_free(conf->mctx, conf->search[idx]);
357                         conf->search[idx] = NULL;
358                 }
359         }
360         conf->searchnxt = 0;
361
362         delim = getword(fp, word, sizeof(word));
363         if (strlen(word) == 0U)
364                 return (ISC_R_UNEXPECTEDEND); /* Nothing else on line. */
365
366         idx = 0;
367         while (strlen(word) > 0U) {
368                 if (conf->searchnxt == RESCONFMAXSEARCH)
369                         goto ignore; /* Too many domains. */
370
371                 conf->search[idx] = isc_mem_strdup(conf->mctx, word);
372                 if (conf->search[idx] == NULL)
373                         return (ISC_R_NOMEMORY);
374                 idx++;
375                 conf->searchnxt++;
376
377         ignore:
378                 if (delim == EOF || delim == '\n')
379                         break;
380                 else
381                         delim = getword(fp, word, sizeof(word));
382         }
383
384         return (ISC_R_SUCCESS);
385 }
386
387 static isc_result_t
388 resconf_parsesortlist(irs_resconf_t *conf,  FILE *fp) {
389         int delim, res, idx;
390         char word[RESCONFMAXLINELEN];
391         char *p;
392
393         delim = getword(fp, word, sizeof(word));
394         if (strlen(word) == 0U)
395                 return (ISC_R_UNEXPECTEDEND); /* Empty line after keyword. */
396
397         while (strlen(word) > 0U) {
398                 if (conf->sortlistnxt == RESCONFMAXSORTLIST)
399                         return (ISC_R_QUOTA); /* Too many values. */
400
401                 p = strchr(word, '/');
402                 if (p != NULL)
403                         *p++ = '\0';
404
405                 idx = conf->sortlistnxt;
406                 res = create_addr(word, &conf->sortlist[idx].addr, 1);
407                 if (res != ISC_R_SUCCESS)
408                         return (res);
409
410                 if (p != NULL) {
411                         res = create_addr(p, &conf->sortlist[idx].mask, 0);
412                         if (res != ISC_R_SUCCESS)
413                                 return (res);
414                 } else {
415                         /*
416                          * Make up a mask. (XXX: is this correct?)
417                          */
418                         conf->sortlist[idx].mask = conf->sortlist[idx].addr;
419                         memset(&conf->sortlist[idx].mask.type, 0xff,
420                                sizeof(conf->sortlist[idx].mask.type));
421                 }
422
423                 conf->sortlistnxt++;
424
425                 if (delim == EOF || delim == '\n')
426                         break;
427                 else
428                         delim = getword(fp, word, sizeof(word));
429         }
430
431         return (ISC_R_SUCCESS);
432 }
433
434 static isc_result_t
435 resconf_parseoption(irs_resconf_t *conf,  FILE *fp) {
436         int delim;
437         long ndots;
438         char *p;
439         char word[RESCONFMAXLINELEN];
440
441         delim = getword(fp, word, sizeof(word));
442         if (strlen(word) == 0U)
443                 return (ISC_R_UNEXPECTEDEND); /* Empty line after keyword. */
444
445         while (strlen(word) > 0U) {
446                 if (strcmp("debug", word) == 0) {
447                         conf->resdebug = 1;
448                 } else if (strncmp("ndots:", word, 6) == 0) {
449                         ndots = strtol(word + 6, &p, 10);
450                         if (*p != '\0') /* Bad string. */
451                                 return (ISC_R_UNEXPECTEDTOKEN);
452                         if (ndots < 0 || ndots > 0xff) /* Out of range. */
453                                 return (ISC_R_RANGE);
454                         conf->ndots = (isc_uint8_t)ndots;
455                 }
456
457                 if (delim == EOF || delim == '\n')
458                         break;
459                 else
460                         delim = getword(fp, word, sizeof(word));
461         }
462
463         return (ISC_R_SUCCESS);
464 }
465
466 static isc_result_t
467 add_search(irs_resconf_t *conf, char *domain) {
468         irs_resconf_search_t *entry;
469
470         entry = isc_mem_get(conf->mctx, sizeof(*entry));
471         if (entry == NULL)
472                 return (ISC_R_NOMEMORY);
473
474         entry->domain = domain;
475         ISC_LINK_INIT(entry, link);
476         ISC_LIST_APPEND(conf->searchlist, entry, link);
477
478         return (ISC_R_SUCCESS);
479 }
480
481 /*% parses a file and fills in the data structure. */
482 isc_result_t
483 irs_resconf_load(isc_mem_t *mctx, const char *filename, irs_resconf_t **confp)
484 {
485         FILE *fp = NULL;
486         char word[256];
487         isc_result_t rval, ret = ISC_R_SUCCESS;
488         irs_resconf_t *conf;
489         int i, stopchar;
490
491         REQUIRE(mctx != NULL);
492         REQUIRE(filename != NULL);
493         REQUIRE(strlen(filename) > 0U);
494         REQUIRE(confp != NULL && *confp == NULL);
495
496         conf = isc_mem_get(mctx, sizeof(*conf));
497         if (conf == NULL)
498                 return (ISC_R_NOMEMORY);
499
500         conf->mctx = mctx;
501         ISC_LIST_INIT(conf->nameservers);
502         conf->numns = 0;
503         conf->domainname = NULL;
504         conf->searchnxt = 0;
505         conf->resdebug = 0;
506         conf->ndots = 1;
507         for (i = 0; i < RESCONFMAXSEARCH; i++)
508                 conf->search[i] = NULL;
509
510         errno = 0;
511         if ((fp = fopen(filename, "r")) != NULL) {
512                 do {
513                         stopchar = getword(fp, word, sizeof(word));
514                         if (stopchar == EOF) {
515                                 rval = ISC_R_SUCCESS;
516                                 POST(rval);
517                                 break;
518                         }
519
520                         if (strlen(word) == 0U)
521                                 rval = ISC_R_SUCCESS;
522                         else if (strcmp(word, "nameserver") == 0)
523                                 rval = resconf_parsenameserver(conf, fp);
524                         else if (strcmp(word, "domain") == 0)
525                                 rval = resconf_parsedomain(conf, fp);
526                         else if (strcmp(word, "search") == 0)
527                                 rval = resconf_parsesearch(conf, fp);
528                         else if (strcmp(word, "sortlist") == 0)
529                                 rval = resconf_parsesortlist(conf, fp);
530                         else if (strcmp(word, "options") == 0)
531                                 rval = resconf_parseoption(conf, fp);
532                         else {
533                                 /* unrecognised word. Ignore entire line */
534                                 rval = ISC_R_SUCCESS;
535                                 stopchar = eatline(fp);
536                                 if (stopchar == EOF) {
537                                         break;
538                                 }
539                         }
540                         if (ret == ISC_R_SUCCESS && rval != ISC_R_SUCCESS)
541                                 ret = rval;
542                 } while (1);
543
544                 fclose(fp);
545         } else {
546                 switch (errno) {
547                 case ENOENT:
548                         break;
549                 default:
550                         isc_mem_put(mctx, conf, sizeof(*conf));
551                         return (ISC_R_INVALIDFILE);
552                 }
553         }
554
555         /* If we don't find a nameserver fall back to localhost */
556         if (conf->numns == 0) {
557                 INSIST(ISC_LIST_EMPTY(conf->nameservers));
558
559                 /* XXX: should we catch errors? */
560                 (void)add_server(conf->mctx, "127.0.0.1", &conf->nameservers);
561                 (void)add_server(conf->mctx, "::1", &conf->nameservers);
562         }
563
564         /*
565          * Construct unified search list from domain or configured
566          * search list
567          */
568         ISC_LIST_INIT(conf->searchlist);
569         if (conf->domainname != NULL) {
570                 ret = add_search(conf, conf->domainname);
571         } else if (conf->searchnxt > 0) {
572                 for (i = 0; i < conf->searchnxt; i++) {
573                         ret = add_search(conf, conf->search[i]);
574                         if (ret != ISC_R_SUCCESS)
575                                 break;
576                 }
577         }
578
579         conf->magic = IRS_RESCONF_MAGIC;
580
581         if (ret != ISC_R_SUCCESS)
582                 irs_resconf_destroy(&conf);
583         else {
584                 if (fp == NULL)
585                         ret = ISC_R_FILENOTFOUND;
586                 *confp = conf;
587         }
588
589         return (ret);
590 }
591
592 void
593 irs_resconf_destroy(irs_resconf_t **confp) {
594         irs_resconf_t *conf;
595         isc_sockaddr_t *address;
596         irs_resconf_search_t *searchentry;
597         int i;
598
599         REQUIRE(confp != NULL);
600         conf = *confp;
601         REQUIRE(IRS_RESCONF_VALID(conf));
602
603         while ((searchentry = ISC_LIST_HEAD(conf->searchlist)) != NULL) {
604                 ISC_LIST_UNLINK(conf->searchlist, searchentry, link);
605                 isc_mem_put(conf->mctx, searchentry, sizeof(*searchentry));
606         }
607
608         while ((address = ISC_LIST_HEAD(conf->nameservers)) != NULL) {
609                 ISC_LIST_UNLINK(conf->nameservers, address, link);
610                 isc_mem_put(conf->mctx, address, sizeof(*address));
611         }
612
613         if (conf->domainname != NULL)
614                 isc_mem_free(conf->mctx, conf->domainname);
615
616         for (i = 0; i < RESCONFMAXSEARCH; i++) {
617                 if (conf->search[i] != NULL)
618                         isc_mem_free(conf->mctx, conf->search[i]);
619         }
620
621         isc_mem_put(conf->mctx, conf, sizeof(*conf));
622
623         *confp = NULL;
624 }
625
626 isc_sockaddrlist_t *
627 irs_resconf_getnameservers(irs_resconf_t *conf) {
628         REQUIRE(IRS_RESCONF_VALID(conf));
629
630         return (&conf->nameservers);
631 }
632
633 irs_resconf_searchlist_t *
634 irs_resconf_getsearchlist(irs_resconf_t *conf) {
635         REQUIRE(IRS_RESCONF_VALID(conf));
636
637         return (&conf->searchlist);
638 }
639
640 unsigned int
641 irs_resconf_getndots(irs_resconf_t *conf) {
642         REQUIRE(IRS_RESCONF_VALID(conf));
643
644         return ((unsigned int)conf->ndots);
645 }