Remove building with NOCRYPTO option
[minix.git] / external / bsd / bind / dist / lib / irs / resconf.c
blob4d52d78f0783bc5d33c67a284ca12b719c72fdb4
1 /* $NetBSD: resconf.c,v 1.8 2014/12/10 04:37:59 christos Exp $ */
3 /*
4 * Copyright (C) 2009, 2011, 2012, 2014 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 */
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 #ifndef WIN32
47 #include <sys/types.h>
48 #include <sys/socket.h>
49 #include <netdb.h>
50 #endif
52 #include <ctype.h>
53 #include <errno.h>
54 #include <stdlib.h>
55 #include <stdio.h>
56 #include <string.h>
58 #include <isc/magic.h>
59 #include <isc/mem.h>
60 #include <isc/netaddr.h>
61 #include <isc/sockaddr.h>
62 #include <isc/util.h>
64 #include <irs/netdb.h>
65 #include <irs/resconf.h>
67 #define IRS_RESCONF_MAGIC ISC_MAGIC('R', 'E', 'S', 'c')
68 #define IRS_RESCONF_VALID(c) ISC_MAGIC_VALID(c, IRS_RESCONF_MAGIC)
70 /*!
71 * protocol constants
74 #if ! defined(NS_INADDRSZ)
75 #define NS_INADDRSZ 4
76 #endif
78 #if ! defined(NS_IN6ADDRSZ)
79 #define NS_IN6ADDRSZ 16
80 #endif
82 /*!
83 * resolv.conf parameters
86 #define RESCONFMAXNAMESERVERS 3 /*%< max 3 "nameserver" entries */
87 #define RESCONFMAXSEARCH 8 /*%< max 8 domains in "search" entry */
88 #define RESCONFMAXLINELEN 256 /*%< max size of a line */
89 #define RESCONFMAXSORTLIST 10 /*%< max 10 */
91 /*!
92 * configuration data structure
95 struct irs_resconf {
97 * The configuration data is a thread-specific object, and does not
98 * need to be locked.
100 unsigned int magic;
101 isc_mem_t *mctx;
103 isc_sockaddrlist_t nameservers;
104 unsigned int numns; /*%< number of configured servers */
106 char *domainname;
107 char *search[RESCONFMAXSEARCH];
108 isc_uint8_t searchnxt; /*%< index for next free slot */
110 irs_resconf_searchlist_t searchlist;
112 struct {
113 isc_netaddr_t addr;
114 /*% mask has a non-zero 'family' if set */
115 isc_netaddr_t mask;
116 } sortlist[RESCONFMAXSORTLIST];
117 isc_uint8_t sortlistnxt;
119 /*%< non-zero if 'options debug' set */
120 isc_uint8_t resdebug;
121 /*%< set to n in 'options ndots:n' */
122 isc_uint8_t ndots;
125 static isc_result_t
126 resconf_parsenameserver(irs_resconf_t *conf, FILE *fp);
127 static isc_result_t
128 resconf_parsedomain(irs_resconf_t *conf, FILE *fp);
129 static isc_result_t
130 resconf_parsesearch(irs_resconf_t *conf, FILE *fp);
131 static isc_result_t
132 resconf_parsesortlist(irs_resconf_t *conf, FILE *fp);
133 static isc_result_t
134 resconf_parseoption(irs_resconf_t *ctx, FILE *fp);
137 * Eat characters from FP until EOL or EOF. Returns EOF or '\n'
139 static int
140 eatline(FILE *fp) {
141 int ch;
143 ch = fgetc(fp);
144 while (ch != '\n' && ch != EOF)
145 ch = fgetc(fp);
147 return (ch);
151 * Eats white space up to next newline or non-whitespace character (of
152 * EOF). Returns the last character read. Comments are considered white
153 * space.
155 static int
156 eatwhite(FILE *fp) {
157 int ch;
159 ch = fgetc(fp);
160 while (ch != '\n' && ch != EOF && isspace((unsigned char)ch))
161 ch = fgetc(fp);
163 if (ch == ';' || ch == '#')
164 ch = eatline(fp);
166 return (ch);
170 * Skip over any leading whitespace and then read in the next sequence of
171 * non-whitespace characters. In this context newline is not considered
172 * whitespace. Returns EOF on end-of-file, or the character
173 * that caused the reading to stop.
175 static int
176 getword(FILE *fp, char *buffer, size_t size) {
177 int ch;
178 char *p = buffer;
180 REQUIRE(buffer != NULL);
181 REQUIRE(size > 0U);
183 *p = '\0';
185 ch = eatwhite(fp);
187 if (ch == EOF)
188 return (EOF);
190 do {
191 *p = '\0';
193 if (ch == EOF || isspace((unsigned char)ch))
194 break;
195 else if ((size_t) (p - buffer) == size - 1)
196 return (EOF); /* Not enough space. */
198 *p++ = (char)ch;
199 ch = fgetc(fp);
200 } while (1);
202 return (ch);
205 static isc_result_t
206 add_server(isc_mem_t *mctx, const char *address_str,
207 isc_sockaddrlist_t *nameservers)
209 int error;
210 isc_sockaddr_t *address = NULL;
211 struct addrinfo hints, *res;
212 isc_result_t result = ISC_R_SUCCESS;
214 res = NULL;
215 memset(&hints, 0, sizeof(hints));
216 hints.ai_family = AF_UNSPEC;
217 hints.ai_socktype = SOCK_DGRAM;
218 hints.ai_protocol = IPPROTO_UDP;
219 hints.ai_flags = AI_NUMERICHOST;
220 error = getaddrinfo(address_str, "53", &hints, &res);
221 if (error != 0)
222 return (ISC_R_BADADDRESSFORM);
224 /* XXX: special case: treat all-0 IPv4 address as loopback */
225 if (res->ai_family == AF_INET) {
226 struct in_addr *v4;
227 unsigned char zeroaddress[] = {0, 0, 0, 0};
228 unsigned char loopaddress[] = {127, 0, 0, 1};
230 v4 = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
231 if (memcmp(v4, zeroaddress, 4) == 0)
232 memmove(v4, loopaddress, 4);
235 address = isc_mem_get(mctx, sizeof(*address));
236 if (address == NULL) {
237 result = ISC_R_NOMEMORY;
238 goto cleanup;
240 if (res->ai_addrlen > sizeof(address->type)) {
241 isc_mem_put(mctx, address, sizeof(*address));
242 result = ISC_R_RANGE;
243 goto cleanup;
245 address->length = (unsigned int)res->ai_addrlen;
246 memmove(&address->type.ss, res->ai_addr, res->ai_addrlen);
247 ISC_LINK_INIT(address, link);
248 ISC_LIST_APPEND(*nameservers, address, link);
250 cleanup:
251 freeaddrinfo(res);
253 return (result);
256 static isc_result_t
257 create_addr(const char *buffer, isc_netaddr_t *addr, int convert_zero) {
258 struct in_addr v4;
259 struct in6_addr v6;
261 if (inet_aton(buffer, &v4) == 1) {
262 if (convert_zero) {
263 unsigned char zeroaddress[] = {0, 0, 0, 0};
264 unsigned char loopaddress[] = {127, 0, 0, 1};
265 if (memcmp(&v4, zeroaddress, 4) == 0)
266 memmove(&v4, loopaddress, 4);
268 addr->family = AF_INET;
269 memmove(&addr->type.in, &v4, NS_INADDRSZ);
270 addr->zone = 0;
271 } else if (inet_pton(AF_INET6, buffer, &v6) == 1) {
272 addr->family = AF_INET6;
273 memmove(&addr->type.in6, &v6, NS_IN6ADDRSZ);
274 addr->zone = 0;
275 } else
276 return (ISC_R_BADADDRESSFORM); /* Unrecognised format. */
278 return (ISC_R_SUCCESS);
281 static isc_result_t
282 resconf_parsenameserver(irs_resconf_t *conf, FILE *fp) {
283 char word[RESCONFMAXLINELEN];
284 int cp;
285 isc_result_t result;
287 if (conf->numns == RESCONFMAXNAMESERVERS)
288 return (ISC_R_SUCCESS);
290 cp = getword(fp, word, sizeof(word));
291 if (strlen(word) == 0U)
292 return (ISC_R_UNEXPECTEDEND); /* Nothing on line. */
293 else if (cp == ' ' || cp == '\t')
294 cp = eatwhite(fp);
296 if (cp != EOF && cp != '\n')
297 return (ISC_R_UNEXPECTEDTOKEN); /* Extra junk on line. */
299 result = add_server(conf->mctx, word, &conf->nameservers);
300 if (result != ISC_R_SUCCESS)
301 return (result);
302 conf->numns++;
304 return (ISC_R_SUCCESS);
307 static isc_result_t
308 resconf_parsedomain(irs_resconf_t *conf, FILE *fp) {
309 char word[RESCONFMAXLINELEN];
310 int res, i;
312 res = getword(fp, word, sizeof(word));
313 if (strlen(word) == 0U)
314 return (ISC_R_UNEXPECTEDEND); /* Nothing else on line. */
315 else if (res == ' ' || res == '\t')
316 res = eatwhite(fp);
318 if (res != EOF && res != '\n')
319 return (ISC_R_UNEXPECTEDTOKEN); /* Extra junk on line. */
321 if (conf->domainname != NULL)
322 isc_mem_free(conf->mctx, conf->domainname);
325 * Search and domain are mutually exclusive.
327 for (i = 0; i < RESCONFMAXSEARCH; i++) {
328 if (conf->search[i] != NULL) {
329 isc_mem_free(conf->mctx, conf->search[i]);
330 conf->search[i] = NULL;
333 conf->searchnxt = 0;
335 conf->domainname = isc_mem_strdup(conf->mctx, word);
336 if (conf->domainname == NULL)
337 return (ISC_R_NOMEMORY);
339 return (ISC_R_SUCCESS);
342 static isc_result_t
343 resconf_parsesearch(irs_resconf_t *conf, FILE *fp) {
344 int idx, delim;
345 char word[RESCONFMAXLINELEN];
347 if (conf->domainname != NULL) {
349 * Search and domain are mutually exclusive.
351 isc_mem_free(conf->mctx, conf->domainname);
352 conf->domainname = NULL;
356 * Remove any previous search definitions.
358 for (idx = 0; idx < RESCONFMAXSEARCH; idx++) {
359 if (conf->search[idx] != NULL) {
360 isc_mem_free(conf->mctx, conf->search[idx]);
361 conf->search[idx] = NULL;
364 conf->searchnxt = 0;
366 delim = getword(fp, word, sizeof(word));
367 if (strlen(word) == 0U)
368 return (ISC_R_UNEXPECTEDEND); /* Nothing else on line. */
370 idx = 0;
371 while (strlen(word) > 0U) {
372 if (conf->searchnxt == RESCONFMAXSEARCH)
373 goto ignore; /* Too many domains. */
375 conf->search[idx] = isc_mem_strdup(conf->mctx, word);
376 if (conf->search[idx] == NULL)
377 return (ISC_R_NOMEMORY);
378 idx++;
379 conf->searchnxt++;
381 ignore:
382 if (delim == EOF || delim == '\n')
383 break;
384 else
385 delim = getword(fp, word, sizeof(word));
388 return (ISC_R_SUCCESS);
391 static isc_result_t
392 resconf_parsesortlist(irs_resconf_t *conf, FILE *fp) {
393 int delim, res, idx;
394 char word[RESCONFMAXLINELEN];
395 char *p;
397 delim = getword(fp, word, sizeof(word));
398 if (strlen(word) == 0U)
399 return (ISC_R_UNEXPECTEDEND); /* Empty line after keyword. */
401 while (strlen(word) > 0U) {
402 if (conf->sortlistnxt == RESCONFMAXSORTLIST)
403 return (ISC_R_QUOTA); /* Too many values. */
405 p = strchr(word, '/');
406 if (p != NULL)
407 *p++ = '\0';
409 idx = conf->sortlistnxt;
410 res = create_addr(word, &conf->sortlist[idx].addr, 1);
411 if (res != ISC_R_SUCCESS)
412 return (res);
414 if (p != NULL) {
415 res = create_addr(p, &conf->sortlist[idx].mask, 0);
416 if (res != ISC_R_SUCCESS)
417 return (res);
418 } else {
420 * Make up a mask. (XXX: is this correct?)
422 conf->sortlist[idx].mask = conf->sortlist[idx].addr;
423 memset(&conf->sortlist[idx].mask.type, 0xff,
424 sizeof(conf->sortlist[idx].mask.type));
427 conf->sortlistnxt++;
429 if (delim == EOF || delim == '\n')
430 break;
431 else
432 delim = getword(fp, word, sizeof(word));
435 return (ISC_R_SUCCESS);
438 static isc_result_t
439 resconf_parseoption(irs_resconf_t *conf, FILE *fp) {
440 int delim;
441 long ndots;
442 char *p;
443 char word[RESCONFMAXLINELEN];
445 delim = getword(fp, word, sizeof(word));
446 if (strlen(word) == 0U)
447 return (ISC_R_UNEXPECTEDEND); /* Empty line after keyword. */
449 while (strlen(word) > 0U) {
450 if (strcmp("debug", word) == 0) {
451 conf->resdebug = 1;
452 } else if (strncmp("ndots:", word, 6) == 0) {
453 ndots = strtol(word + 6, &p, 10);
454 if (*p != '\0') /* Bad string. */
455 return (ISC_R_UNEXPECTEDTOKEN);
456 if (ndots < 0 || ndots > 0xff) /* Out of range. */
457 return (ISC_R_RANGE);
458 conf->ndots = (isc_uint8_t)ndots;
461 if (delim == EOF || delim == '\n')
462 break;
463 else
464 delim = getword(fp, word, sizeof(word));
467 return (ISC_R_SUCCESS);
470 static isc_result_t
471 add_search(irs_resconf_t *conf, char *domain) {
472 irs_resconf_search_t *entry;
474 entry = isc_mem_get(conf->mctx, sizeof(*entry));
475 if (entry == NULL)
476 return (ISC_R_NOMEMORY);
478 entry->domain = domain;
479 ISC_LINK_INIT(entry, link);
480 ISC_LIST_APPEND(conf->searchlist, entry, link);
482 return (ISC_R_SUCCESS);
485 /*% parses a file and fills in the data structure. */
486 isc_result_t
487 irs_resconf_load(isc_mem_t *mctx, const char *filename, irs_resconf_t **confp)
489 FILE *fp = NULL;
490 char word[256];
491 isc_result_t rval, ret = ISC_R_SUCCESS;
492 irs_resconf_t *conf;
493 int i, stopchar;
495 REQUIRE(mctx != NULL);
496 REQUIRE(filename != NULL);
497 REQUIRE(strlen(filename) > 0U);
498 REQUIRE(confp != NULL && *confp == NULL);
500 conf = isc_mem_get(mctx, sizeof(*conf));
501 if (conf == NULL)
502 return (ISC_R_NOMEMORY);
504 conf->mctx = mctx;
505 ISC_LIST_INIT(conf->nameservers);
506 conf->numns = 0;
507 conf->domainname = NULL;
508 conf->searchnxt = 0;
509 conf->resdebug = 0;
510 conf->ndots = 1;
511 for (i = 0; i < RESCONFMAXSEARCH; i++)
512 conf->search[i] = NULL;
514 errno = 0;
515 if ((fp = fopen(filename, "r")) != NULL) {
516 do {
517 stopchar = getword(fp, word, sizeof(word));
518 if (stopchar == EOF) {
519 rval = ISC_R_SUCCESS;
520 POST(rval);
521 break;
524 if (strlen(word) == 0U)
525 rval = ISC_R_SUCCESS;
526 else if (strcmp(word, "nameserver") == 0)
527 rval = resconf_parsenameserver(conf, fp);
528 else if (strcmp(word, "domain") == 0)
529 rval = resconf_parsedomain(conf, fp);
530 else if (strcmp(word, "search") == 0)
531 rval = resconf_parsesearch(conf, fp);
532 else if (strcmp(word, "sortlist") == 0)
533 rval = resconf_parsesortlist(conf, fp);
534 else if (strcmp(word, "options") == 0)
535 rval = resconf_parseoption(conf, fp);
536 else {
537 /* unrecognised word. Ignore entire line */
538 rval = ISC_R_SUCCESS;
539 stopchar = eatline(fp);
540 if (stopchar == EOF) {
541 break;
544 if (ret == ISC_R_SUCCESS && rval != ISC_R_SUCCESS)
545 ret = rval;
546 } while (1);
548 fclose(fp);
549 } else {
550 switch (errno) {
551 case ENOENT:
552 break;
553 default:
554 isc_mem_put(mctx, conf, sizeof(*conf));
555 return (ISC_R_INVALIDFILE);
559 /* If we don't find a nameserver fall back to localhost */
560 if (conf->numns == 0) {
561 INSIST(ISC_LIST_EMPTY(conf->nameservers));
563 /* XXX: should we catch errors? */
564 (void)add_server(conf->mctx, "127.0.0.1", &conf->nameservers);
565 (void)add_server(conf->mctx, "::1", &conf->nameservers);
569 * Construct unified search list from domain or configured
570 * search list
572 ISC_LIST_INIT(conf->searchlist);
573 if (conf->domainname != NULL) {
574 ret = add_search(conf, conf->domainname);
575 } else if (conf->searchnxt > 0) {
576 for (i = 0; i < conf->searchnxt; i++) {
577 ret = add_search(conf, conf->search[i]);
578 if (ret != ISC_R_SUCCESS)
579 break;
583 conf->magic = IRS_RESCONF_MAGIC;
585 if (ret != ISC_R_SUCCESS)
586 irs_resconf_destroy(&conf);
587 else {
588 if (fp == NULL)
589 ret = ISC_R_FILENOTFOUND;
590 *confp = conf;
593 return (ret);
596 void
597 irs_resconf_destroy(irs_resconf_t **confp) {
598 irs_resconf_t *conf;
599 isc_sockaddr_t *address;
600 irs_resconf_search_t *searchentry;
601 int i;
603 REQUIRE(confp != NULL);
604 conf = *confp;
605 REQUIRE(IRS_RESCONF_VALID(conf));
607 while ((searchentry = ISC_LIST_HEAD(conf->searchlist)) != NULL) {
608 ISC_LIST_UNLINK(conf->searchlist, searchentry, link);
609 isc_mem_put(conf->mctx, searchentry, sizeof(*searchentry));
612 while ((address = ISC_LIST_HEAD(conf->nameservers)) != NULL) {
613 ISC_LIST_UNLINK(conf->nameservers, address, link);
614 isc_mem_put(conf->mctx, address, sizeof(*address));
617 if (conf->domainname != NULL)
618 isc_mem_free(conf->mctx, conf->domainname);
620 for (i = 0; i < RESCONFMAXSEARCH; i++) {
621 if (conf->search[i] != NULL)
622 isc_mem_free(conf->mctx, conf->search[i]);
625 isc_mem_put(conf->mctx, conf, sizeof(*conf));
627 *confp = NULL;
630 isc_sockaddrlist_t *
631 irs_resconf_getnameservers(irs_resconf_t *conf) {
632 REQUIRE(IRS_RESCONF_VALID(conf));
634 return (&conf->nameservers);
637 irs_resconf_searchlist_t *
638 irs_resconf_getsearchlist(irs_resconf_t *conf) {
639 REQUIRE(IRS_RESCONF_VALID(conf));
641 return (&conf->searchlist);
644 unsigned int
645 irs_resconf_getndots(irs_resconf_t *conf) {
646 REQUIRE(IRS_RESCONF_VALID(conf));
648 return ((unsigned int)conf->ndots);