No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / bind / dist / lib / irs / resconf.c
blob5bd62fbc89df1393eaf1d785b582280915319bf6
1 /* $NetBSD$ */
3 /*
4 * Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
11 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
13 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
15 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16 * PERFORMANCE OF THIS SOFTWARE.
19 /* Id: resconf.c,v 1.3 2009/09/02 23:48:02 tbox Exp */
21 /*! \file resconf.c */
23 /**
24 * Module for parsing resolv.conf files (largely derived from lwconfig.c).
26 * irs_resconf_load() opens the file filename and parses it to initialize
27 * the configuration structure.
29 * \section lwconfig_return Return Values
31 * irs_resconf_load() returns #IRS_R_SUCCESS if it successfully read and
32 * parsed filename. It returns a non-0 error code if filename could not be
33 * opened or contained incorrect resolver statements.
35 * \section lwconfig_see See Also
37 * stdio(3), \link resolver resolver \endlink
39 * \section files Files
41 * /etc/resolv.conf
44 #include <config.h>
46 #include <sys/types.h>
47 #include <sys/socket.h>
49 #include <ctype.h>
50 #include <errno.h>
51 #include <netdb.h>
52 #include <stdlib.h>
53 #include <stdio.h>
54 #include <string.h>
56 #include <isc/magic.h>
57 #include <isc/mem.h>
58 #include <isc/netaddr.h>
59 #include <isc/sockaddr.h>
60 #include <isc/util.h>
62 #include <irs/resconf.h>
64 #define IRS_RESCONF_MAGIC ISC_MAGIC('R', 'E', 'S', 'c')
65 #define IRS_RESCONF_VALID(c) ISC_MAGIC_VALID(c, IRS_RESCONF_MAGIC)
67 /*!
68 * protocol constants
71 #if ! defined(NS_INADDRSZ)
72 #define NS_INADDRSZ 4
73 #endif
75 #if ! defined(NS_IN6ADDRSZ)
76 #define NS_IN6ADDRSZ 16
77 #endif
79 /*!
80 * resolv.conf parameters
83 #define RESCONFMAXNAMESERVERS 3 /*%< max 3 "nameserver" entries */
84 #define RESCONFMAXSEARCH 8 /*%< max 8 domains in "search" entry */
85 #define RESCONFMAXLINELEN 256 /*%< max size of a line */
86 #define RESCONFMAXSORTLIST 10 /*%< max 10 */
88 /*!
89 * configuration data structure
92 struct irs_resconf {
94 * The configuration data is a thread-specific object, and does not
95 * need to be locked.
97 unsigned int magic;
98 isc_mem_t *mctx;
100 isc_sockaddrlist_t nameservers;
101 unsigned int numns; /*%< number of configured servers */
103 char *domainname;
104 char *search[RESCONFMAXSEARCH];
105 isc_uint8_t searchnxt; /*%< index for next free slot */
107 irs_resconf_searchlist_t searchlist;
109 struct {
110 isc_netaddr_t addr;
111 /*% mask has a non-zero 'family' if set */
112 isc_netaddr_t mask;
113 } sortlist[RESCONFMAXSORTLIST];
114 isc_uint8_t sortlistnxt;
116 /*%< non-zero if 'options debug' set */
117 isc_uint8_t resdebug;
118 /*%< set to n in 'options ndots:n' */
119 isc_uint8_t ndots;
122 static isc_result_t
123 resconf_parsenameserver(irs_resconf_t *conf, FILE *fp);
124 static isc_result_t
125 resconf_parsedomain(irs_resconf_t *conf, FILE *fp);
126 static isc_result_t
127 resconf_parsesearch(irs_resconf_t *conf, FILE *fp);
128 static isc_result_t
129 resconf_parsesortlist(irs_resconf_t *conf, FILE *fp);
130 static isc_result_t
131 resconf_parseoption(irs_resconf_t *ctx, FILE *fp);
134 * Eat characters from FP until EOL or EOF. Returns EOF or '\n'
136 static int
137 eatline(FILE *fp) {
138 int ch;
140 ch = fgetc(fp);
141 while (ch != '\n' && ch != EOF)
142 ch = fgetc(fp);
144 return (ch);
148 * Eats white space up to next newline or non-whitespace character (of
149 * EOF). Returns the last character read. Comments are considered white
150 * space.
152 static int
153 eatwhite(FILE *fp) {
154 int ch;
156 ch = fgetc(fp);
157 while (ch != '\n' && ch != EOF && isspace((unsigned char)ch))
158 ch = fgetc(fp);
160 if (ch == ';' || ch == '#')
161 ch = eatline(fp);
163 return (ch);
167 * Skip over any leading whitespace and then read in the next sequence of
168 * non-whitespace characters. In this context newline is not considered
169 * whitespace. Returns EOF on end-of-file, or the character
170 * that caused the reading to stop.
172 static int
173 getword(FILE *fp, char *buffer, size_t size) {
174 int ch;
175 char *p = buffer;
177 REQUIRE(buffer != NULL);
178 REQUIRE(size > 0U);
180 *p = '\0';
182 ch = eatwhite(fp);
184 if (ch == EOF)
185 return (EOF);
187 do {
188 *p = '\0';
190 if (ch == EOF || isspace((unsigned char)ch))
191 break;
192 else if ((size_t) (p - buffer) == size - 1)
193 return (EOF); /* Not enough space. */
195 *p++ = (char)ch;
196 ch = fgetc(fp);
197 } while (1);
199 return (ch);
202 static isc_result_t
203 add_server(isc_mem_t *mctx, const char *address_str,
204 isc_sockaddrlist_t *nameservers)
206 int error;
207 isc_sockaddr_t *address = NULL;
208 struct addrinfo hints, *res;
209 isc_result_t result = ISC_R_SUCCESS;
211 res = NULL;
212 memset(&hints, 0, sizeof(hints));
213 hints.ai_family = AF_UNSPEC;
214 hints.ai_socktype = SOCK_DGRAM;
215 hints.ai_protocol = IPPROTO_UDP;
216 hints.ai_flags = AI_NUMERICHOST;
217 error = getaddrinfo(address_str, "53", &hints, &res);
218 if (error != 0)
219 return (ISC_R_BADADDRESSFORM);
221 /* XXX: special case: treat all-0 IPv4 address as loopback */
222 if (res->ai_family == AF_INET) {
223 struct in_addr *v4;
224 unsigned char zeroaddress[] = {0, 0, 0, 0};
225 unsigned char loopaddress[] = {127, 0, 0, 1};
227 v4 = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
228 if (memcmp(v4, zeroaddress, 4) == 0)
229 memcpy(v4, loopaddress, 4);
232 address = isc_mem_get(mctx, sizeof(*address));
233 if (address == NULL) {
234 result = ISC_R_NOMEMORY;
235 goto cleanup;
237 if (res->ai_addrlen > sizeof(address->type)) {
238 isc_mem_put(mctx, address, sizeof(*address));
239 result = ISC_R_RANGE;
240 goto cleanup;
242 address->length = res->ai_addrlen;
243 memcpy(&address->type.sa, res->ai_addr, res->ai_addrlen);
244 ISC_LINK_INIT(address, link);
245 ISC_LIST_APPEND(*nameservers, address, link);
247 cleanup:
248 freeaddrinfo(res);
250 return (result);
253 static isc_result_t
254 create_addr(const char *buffer, isc_netaddr_t *addr, int convert_zero) {
255 struct in_addr v4;
256 struct in6_addr v6;
258 if (inet_aton(buffer, &v4) == 1) {
259 if (convert_zero) {
260 unsigned char zeroaddress[] = {0, 0, 0, 0};
261 unsigned char loopaddress[] = {127, 0, 0, 1};
262 if (memcmp(&v4, zeroaddress, 4) == 0)
263 memcpy(&v4, loopaddress, 4);
265 addr->family = AF_INET;
266 memcpy(&addr->type.in, &v4, NS_INADDRSZ);
267 addr->zone = 0;
268 } else if (inet_pton(AF_INET6, buffer, &v6) == 1) {
269 addr->family = AF_INET6;
270 memcpy(&addr->type.in6, &v6, NS_IN6ADDRSZ);
271 addr->zone = 0;
272 } else
273 return (ISC_R_BADADDRESSFORM); /* Unrecognised format. */
275 return (ISC_R_SUCCESS);
278 static isc_result_t
279 resconf_parsenameserver(irs_resconf_t *conf, FILE *fp) {
280 char word[RESCONFMAXLINELEN];
281 int cp;
282 isc_result_t result;
284 if (conf->numns == RESCONFMAXNAMESERVERS)
285 return (ISC_R_SUCCESS);
287 cp = getword(fp, word, sizeof(word));
288 if (strlen(word) == 0U)
289 return (ISC_R_UNEXPECTEDEND); /* Nothing on line. */
290 else if (cp == ' ' || cp == '\t')
291 cp = eatwhite(fp);
293 if (cp != EOF && cp != '\n')
294 return (ISC_R_UNEXPECTEDTOKEN); /* Extra junk on line. */
296 result = add_server(conf->mctx, word, &conf->nameservers);
297 if (result != ISC_R_SUCCESS)
298 return (result);
299 conf->numns++;
301 return (ISC_R_SUCCESS);
304 static isc_result_t
305 resconf_parsedomain(irs_resconf_t *conf, FILE *fp) {
306 char word[RESCONFMAXLINELEN];
307 int res, i;
309 res = getword(fp, word, sizeof(word));
310 if (strlen(word) == 0U)
311 return (ISC_R_UNEXPECTEDEND); /* Nothing else on line. */
312 else if (res == ' ' || res == '\t')
313 res = eatwhite(fp);
315 if (res != EOF && res != '\n')
316 return (ISC_R_UNEXPECTEDTOKEN); /* Extra junk on line. */
318 if (conf->domainname != NULL)
319 isc_mem_free(conf->mctx, conf->domainname);
322 * Search and domain are mutually exclusive.
324 for (i = 0; i < RESCONFMAXSEARCH; i++) {
325 if (conf->search[i] != NULL) {
326 isc_mem_free(conf->mctx, conf->search[i]);
327 conf->search[i] = NULL;
330 conf->searchnxt = 0;
332 conf->domainname = isc_mem_strdup(conf->mctx, word);
333 if (conf->domainname == NULL)
334 return (ISC_R_NOMEMORY);
336 return (ISC_R_SUCCESS);
339 static isc_result_t
340 resconf_parsesearch(irs_resconf_t *conf, FILE *fp) {
341 int idx, delim;
342 char word[RESCONFMAXLINELEN];
344 if (conf->domainname != NULL) {
346 * Search and domain are mutually exclusive.
348 isc_mem_free(conf->mctx, conf->domainname);
349 conf->domainname = NULL;
353 * Remove any previous search definitions.
355 for (idx = 0; idx < RESCONFMAXSEARCH; idx++) {
356 if (conf->search[idx] != NULL) {
357 isc_mem_free(conf->mctx, conf->search[idx]);
358 conf->search[idx] = NULL;
361 conf->searchnxt = 0;
363 delim = getword(fp, word, sizeof(word));
364 if (strlen(word) == 0U)
365 return (ISC_R_UNEXPECTEDEND); /* Nothing else on line. */
367 idx = 0;
368 while (strlen(word) > 0U) {
369 if (conf->searchnxt == RESCONFMAXSEARCH)
370 goto ignore; /* Too many domains. */
372 conf->search[idx] = isc_mem_strdup(conf->mctx, word);
373 if (conf->search[idx] == NULL)
374 return (ISC_R_NOMEMORY);
375 idx++;
376 conf->searchnxt++;
378 ignore:
379 if (delim == EOF || delim == '\n')
380 break;
381 else
382 delim = getword(fp, word, sizeof(word));
385 return (ISC_R_SUCCESS);
388 static isc_result_t
389 resconf_parsesortlist(irs_resconf_t *conf, FILE *fp) {
390 int delim, res, idx;
391 char word[RESCONFMAXLINELEN];
392 char *p;
394 delim = getword(fp, word, sizeof(word));
395 if (strlen(word) == 0U)
396 return (ISC_R_UNEXPECTEDEND); /* Empty line after keyword. */
398 while (strlen(word) > 0U) {
399 if (conf->sortlistnxt == RESCONFMAXSORTLIST)
400 return (ISC_R_QUOTA); /* Too many values. */
402 p = strchr(word, '/');
403 if (p != NULL)
404 *p++ = '\0';
406 idx = conf->sortlistnxt;
407 res = create_addr(word, &conf->sortlist[idx].addr, 1);
408 if (res != ISC_R_SUCCESS)
409 return (res);
411 if (p != NULL) {
412 res = create_addr(p, &conf->sortlist[idx].mask, 0);
413 if (res != ISC_R_SUCCESS)
414 return (res);
415 } else {
417 * Make up a mask. (XXX: is this correct?)
419 conf->sortlist[idx].mask = conf->sortlist[idx].addr;
420 memset(&conf->sortlist[idx].mask.type, 0xff,
421 sizeof(conf->sortlist[idx].mask.type));
424 conf->sortlistnxt++;
426 if (delim == EOF || delim == '\n')
427 break;
428 else
429 delim = getword(fp, word, sizeof(word));
432 return (ISC_R_SUCCESS);
435 static isc_result_t
436 resconf_parseoption(irs_resconf_t *conf, FILE *fp) {
437 int delim;
438 long ndots;
439 char *p;
440 char word[RESCONFMAXLINELEN];
442 delim = getword(fp, word, sizeof(word));
443 if (strlen(word) == 0U)
444 return (ISC_R_UNEXPECTEDEND); /* Empty line after keyword. */
446 while (strlen(word) > 0U) {
447 if (strcmp("debug", word) == 0) {
448 conf->resdebug = 1;
449 } else if (strncmp("ndots:", word, 6) == 0) {
450 ndots = strtol(word + 6, &p, 10);
451 if (*p != '\0') /* Bad string. */
452 return (ISC_R_UNEXPECTEDTOKEN);
453 if (ndots < 0 || ndots > 0xff) /* Out of range. */
454 return (ISC_R_RANGE);
455 conf->ndots = (isc_uint8_t)ndots;
458 if (delim == EOF || delim == '\n')
459 break;
460 else
461 delim = getword(fp, word, sizeof(word));
464 return (ISC_R_SUCCESS);
467 static isc_result_t
468 add_search(irs_resconf_t *conf, char *domain) {
469 irs_resconf_search_t *entry;
471 entry = isc_mem_get(conf->mctx, sizeof(*entry));
472 if (entry == NULL)
473 return (ISC_R_NOMEMORY);
475 entry->domain = domain;
476 ISC_LINK_INIT(entry, link);
477 ISC_LIST_APPEND(conf->searchlist, entry, link);
479 return (ISC_R_SUCCESS);
482 /*% parses a file and fills in the data structure. */
483 isc_result_t
484 irs_resconf_load(isc_mem_t *mctx, const char *filename, irs_resconf_t **confp)
486 FILE *fp = NULL;
487 char word[256];
488 isc_result_t rval, ret;
489 irs_resconf_t *conf;
490 int i, stopchar;
492 REQUIRE(mctx != NULL);
493 REQUIRE(filename != NULL);
494 REQUIRE(strlen(filename) > 0U);
495 REQUIRE(confp != NULL && *confp == NULL);
497 conf = isc_mem_get(mctx, sizeof(*conf));
498 if (conf == NULL)
499 return (ISC_R_NOMEMORY);
501 conf->mctx = mctx;
502 ISC_LIST_INIT(conf->nameservers);
503 conf->numns = 0;
504 conf->domainname = NULL;
505 conf->searchnxt = 0;
506 conf->resdebug = 0;
507 conf->ndots = 1;
508 for (i = 0; i < RESCONFMAXSEARCH; i++)
509 conf->search[i] = NULL;
511 errno = 0;
512 if ((fp = fopen(filename, "r")) == NULL) {
513 isc_mem_put(mctx, conf, sizeof(*conf));
514 return (ISC_R_INVALIDFILE);
517 ret = ISC_R_SUCCESS;
518 do {
519 stopchar = getword(fp, word, sizeof(word));
520 if (stopchar == EOF) {
521 rval = ISC_R_SUCCESS;
522 break;
525 if (strlen(word) == 0U)
526 rval = ISC_R_SUCCESS;
527 else if (strcmp(word, "nameserver") == 0)
528 rval = resconf_parsenameserver(conf, fp);
529 else if (strcmp(word, "domain") == 0)
530 rval = resconf_parsedomain(conf, fp);
531 else if (strcmp(word, "search") == 0)
532 rval = resconf_parsesearch(conf, fp);
533 else if (strcmp(word, "sortlist") == 0)
534 rval = resconf_parsesortlist(conf, fp);
535 else if (strcmp(word, "options") == 0)
536 rval = resconf_parseoption(conf, fp);
537 else {
538 /* unrecognised word. Ignore entire line */
539 rval = ISC_R_SUCCESS;
540 stopchar = eatline(fp);
541 if (stopchar == EOF) {
542 break;
545 if (ret == ISC_R_SUCCESS && rval != ISC_R_SUCCESS)
546 ret = rval;
547 } while (1);
549 fclose(fp);
551 /* If we don't find a nameserver fall back to localhost */
552 if (conf->numns == 0) {
553 INSIST(ISC_LIST_EMPTY(conf->nameservers));
555 /* XXX: should we catch errors? */
556 (void)add_server(conf->mctx, "127.0.0.1", &conf->nameservers);
557 (void)add_server(conf->mctx, "::1", &conf->nameservers);
561 * Construct unified search list from domain or configured
562 * search list
564 ISC_LIST_INIT(conf->searchlist);
565 if (conf->domainname != NULL) {
566 ret = add_search(conf, conf->domainname);
567 } else if (conf->searchnxt > 0) {
568 for (i = 0; i < conf->searchnxt; i++) {
569 ret = add_search(conf, conf->search[i]);
570 if (ret != ISC_R_SUCCESS)
571 break;
575 conf->magic = IRS_RESCONF_MAGIC;
577 if (ret != ISC_R_SUCCESS)
578 irs_resconf_destroy(&conf);
579 else
580 *confp = conf;
582 return (ret);
585 void
586 irs_resconf_destroy(irs_resconf_t **confp) {
587 irs_resconf_t *conf;
588 isc_sockaddr_t *address;
589 irs_resconf_search_t *searchentry;
590 int i;
592 REQUIRE(confp != NULL);
593 conf = *confp;
594 REQUIRE(IRS_RESCONF_VALID(conf));
596 while ((searchentry = ISC_LIST_HEAD(conf->searchlist)) != NULL) {
597 ISC_LIST_UNLINK(conf->searchlist, searchentry, link);
598 isc_mem_put(conf->mctx, searchentry, sizeof(*searchentry));
601 while ((address = ISC_LIST_HEAD(conf->nameservers)) != NULL) {
602 ISC_LIST_UNLINK(conf->nameservers, address, link);
603 isc_mem_put(conf->mctx, address, sizeof(*address));
606 if (conf->domainname != NULL)
607 isc_mem_free(conf->mctx, conf->domainname);
609 for (i = 0; i < RESCONFMAXSEARCH; i++) {
610 if (conf->search[i] != NULL)
611 isc_mem_free(conf->mctx, conf->search[i]);
614 isc_mem_put(conf->mctx, conf, sizeof(*conf));
616 *confp = NULL;
619 isc_sockaddrlist_t *
620 irs_resconf_getnameservers(irs_resconf_t *conf) {
621 REQUIRE(IRS_RESCONF_VALID(conf));
623 return (&conf->nameservers);
626 irs_resconf_searchlist_t *
627 irs_resconf_getsearchlist(irs_resconf_t *conf) {
628 REQUIRE(IRS_RESCONF_VALID(conf));
630 return (&conf->searchlist);
633 unsigned int
634 irs_resconf_getndots(irs_resconf_t *conf) {
635 REQUIRE(IRS_RESCONF_VALID(conf));
637 return ((unsigned int)conf->ndots);