1135446Strhodes/*
2262706Serwin * Copyright (C) 2004-2008, 2011, 2012, 2014  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 2000-2003  Internet Software Consortium.
4135446Strhodes *
5193149Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18234010Sdougb/* $Id$ */
19135446Strhodes
20170222Sdougb/*! \file */
21135446Strhodes
22170222Sdougb/**
23170222Sdougb * Module for parsing resolv.conf files.
24170222Sdougb *
25170222Sdougb *    lwres_conf_init() creates an empty lwres_conf_t structure for
26170222Sdougb *    lightweight resolver context ctx.
27193149Sdougb *
28170222Sdougb *    lwres_conf_clear() frees up all the internal memory used by that
29170222Sdougb *    lwres_conf_t structure in resolver context ctx.
30193149Sdougb *
31170222Sdougb *    lwres_conf_parse() opens the file filename and parses it to initialise
32170222Sdougb *    the resolver context ctx's lwres_conf_t structure.
33193149Sdougb *
34170222Sdougb *    lwres_conf_print() prints the lwres_conf_t structure for resolver
35170222Sdougb *    context ctx to the FILE fp.
36193149Sdougb *
37170222Sdougb * \section lwconfig_return Return Values
38193149Sdougb *
39170222Sdougb *    lwres_conf_parse() returns #LWRES_R_SUCCESS if it successfully read and
40170222Sdougb *    parsed filename. It returns #LWRES_R_FAILURE if filename could not be
41170222Sdougb *    opened or contained incorrect resolver statements.
42193149Sdougb *
43170222Sdougb *    lwres_conf_print() returns #LWRES_R_SUCCESS unless an error occurred
44170222Sdougb *    when converting the network addresses to a numeric host address
45170222Sdougb *    string. If this happens, the function returns #LWRES_R_FAILURE.
46193149Sdougb *
47170222Sdougb * \section lwconfig_see See Also
48193149Sdougb *
49170222Sdougb *    stdio(3), \link resolver resolver \endlink
50193149Sdougb *
51170222Sdougb * \section files Files
52193149Sdougb *
53170222Sdougb *    /etc/resolv.conf
54170222Sdougb */
55170222Sdougb
56135446Strhodes#include <config.h>
57135446Strhodes
58135446Strhodes#include <assert.h>
59135446Strhodes#include <ctype.h>
60135446Strhodes#include <errno.h>
61135446Strhodes#include <stdlib.h>
62135446Strhodes#include <stdio.h>
63135446Strhodes#include <string.h>
64135446Strhodes#include <unistd.h>
65135446Strhodes
66135446Strhodes#include <lwres/lwbuffer.h>
67135446Strhodes#include <lwres/lwres.h>
68135446Strhodes#include <lwres/net.h>
69135446Strhodes#include <lwres/result.h>
70135446Strhodes
71135446Strhodes#include "assert_p.h"
72135446Strhodes#include "context_p.h"
73135446Strhodes
74135446Strhodes
75135446Strhodes#if ! defined(NS_INADDRSZ)
76135446Strhodes#define NS_INADDRSZ	 4
77135446Strhodes#endif
78135446Strhodes
79135446Strhodes#if ! defined(NS_IN6ADDRSZ)
80135446Strhodes#define NS_IN6ADDRSZ	16
81135446Strhodes#endif
82135446Strhodes
83135446Strhodesstatic lwres_result_t
84135446Strhodeslwres_conf_parsenameserver(lwres_context_t *ctx,  FILE *fp);
85135446Strhodes
86135446Strhodesstatic lwres_result_t
87135446Strhodeslwres_conf_parselwserver(lwres_context_t *ctx,  FILE *fp);
88135446Strhodes
89135446Strhodesstatic lwres_result_t
90135446Strhodeslwres_conf_parsedomain(lwres_context_t *ctx, FILE *fp);
91135446Strhodes
92135446Strhodesstatic lwres_result_t
93135446Strhodeslwres_conf_parsesearch(lwres_context_t *ctx,  FILE *fp);
94135446Strhodes
95135446Strhodesstatic lwres_result_t
96135446Strhodeslwres_conf_parsesortlist(lwres_context_t *ctx,  FILE *fp);
97135446Strhodes
98135446Strhodesstatic lwres_result_t
99135446Strhodeslwres_conf_parseoption(lwres_context_t *ctx,  FILE *fp);
100135446Strhodes
101135446Strhodesstatic void
102135446Strhodeslwres_resetaddr(lwres_addr_t *addr);
103135446Strhodes
104135446Strhodesstatic lwres_result_t
105135446Strhodeslwres_create_addr(const char *buff, lwres_addr_t *addr, int convert_zero);
106135446Strhodes
107135446Strhodesstatic int lwresaddr2af(int lwresaddrtype);
108135446Strhodes
109135446Strhodes
110135446Strhodesstatic int
111135446Strhodeslwresaddr2af(int lwresaddrtype)
112135446Strhodes{
113135446Strhodes	int af = 0;
114135446Strhodes
115135446Strhodes	switch (lwresaddrtype) {
116135446Strhodes	case LWRES_ADDRTYPE_V4:
117135446Strhodes		af = AF_INET;
118135446Strhodes		break;
119135446Strhodes
120135446Strhodes	case LWRES_ADDRTYPE_V6:
121135446Strhodes		af = AF_INET6;
122135446Strhodes		break;
123135446Strhodes	}
124135446Strhodes
125135446Strhodes	return (af);
126135446Strhodes}
127135446Strhodes
128135446Strhodes
129170222Sdougb/*!
130135446Strhodes * Eat characters from FP until EOL or EOF. Returns EOF or '\n'
131135446Strhodes */
132135446Strhodesstatic int
133135446Strhodeseatline(FILE *fp) {
134135446Strhodes	int ch;
135135446Strhodes
136135446Strhodes	ch = fgetc(fp);
137135446Strhodes	while (ch != '\n' && ch != EOF)
138135446Strhodes		ch = fgetc(fp);
139135446Strhodes
140135446Strhodes	return (ch);
141135446Strhodes}
142135446Strhodes
143135446Strhodes
144170222Sdougb/*!
145135446Strhodes * Eats white space up to next newline or non-whitespace character (of
146135446Strhodes * EOF). Returns the last character read. Comments are considered white
147135446Strhodes * space.
148135446Strhodes */
149135446Strhodesstatic int
150135446Strhodeseatwhite(FILE *fp) {
151135446Strhodes	int ch;
152135446Strhodes
153135446Strhodes	ch = fgetc(fp);
154135446Strhodes	while (ch != '\n' && ch != EOF && isspace((unsigned char)ch))
155135446Strhodes		ch = fgetc(fp);
156135446Strhodes
157135446Strhodes	if (ch == ';' || ch == '#')
158135446Strhodes		ch = eatline(fp);
159135446Strhodes
160135446Strhodes	return (ch);
161135446Strhodes}
162135446Strhodes
163135446Strhodes
164170222Sdougb/*!
165135446Strhodes * Skip over any leading whitespace and then read in the next sequence of
166135446Strhodes * non-whitespace characters. In this context newline is not considered
167135446Strhodes * whitespace. Returns EOF on end-of-file, or the character
168135446Strhodes * that caused the reading to stop.
169135446Strhodes */
170135446Strhodesstatic int
171135446Strhodesgetword(FILE *fp, char *buffer, size_t size) {
172135446Strhodes	int ch;
173135446Strhodes	char *p = buffer;
174135446Strhodes
175135446Strhodes	REQUIRE(buffer != NULL);
176135446Strhodes	REQUIRE(size > 0U);
177135446Strhodes
178135446Strhodes	*p = '\0';
179135446Strhodes
180135446Strhodes	ch = eatwhite(fp);
181135446Strhodes
182135446Strhodes	if (ch == EOF)
183135446Strhodes		return (EOF);
184135446Strhodes
185135446Strhodes	do {
186135446Strhodes		*p = '\0';
187135446Strhodes
188135446Strhodes		if (ch == EOF || isspace((unsigned char)ch))
189135446Strhodes			break;
190135446Strhodes		else if ((size_t) (p - buffer) == size - 1)
191135446Strhodes			return (EOF);	/* Not enough space. */
192135446Strhodes
193135446Strhodes		*p++ = (char)ch;
194135446Strhodes		ch = fgetc(fp);
195135446Strhodes	} while (1);
196135446Strhodes
197135446Strhodes	return (ch);
198135446Strhodes}
199135446Strhodes
200135446Strhodesstatic void
201135446Strhodeslwres_resetaddr(lwres_addr_t *addr) {
202135446Strhodes	REQUIRE(addr != NULL);
203135446Strhodes
204135446Strhodes	memset(addr->address, 0, LWRES_ADDR_MAXLEN);
205135446Strhodes	addr->family = 0;
206135446Strhodes	addr->length = 0;
207135446Strhodes}
208135446Strhodes
209135446Strhodesstatic char *
210135446Strhodeslwres_strdup(lwres_context_t *ctx, const char *str) {
211135446Strhodes	char *p;
212135446Strhodes
213135446Strhodes	REQUIRE(str != NULL);
214135446Strhodes	REQUIRE(strlen(str) > 0U);
215135446Strhodes
216135446Strhodes	p = CTXMALLOC(strlen(str) + 1);
217135446Strhodes	if (p != NULL)
218135446Strhodes		strcpy(p, str);
219135446Strhodes
220135446Strhodes	return (p);
221135446Strhodes}
222135446Strhodes
223170222Sdougb/*% intializes data structure for subsequent config parsing. */
224135446Strhodesvoid
225135446Strhodeslwres_conf_init(lwres_context_t *ctx) {
226135446Strhodes	int i;
227135446Strhodes	lwres_conf_t *confdata;
228135446Strhodes
229135446Strhodes	REQUIRE(ctx != NULL);
230135446Strhodes	confdata = &ctx->confdata;
231135446Strhodes
232135446Strhodes	confdata->nsnext = 0;
233135446Strhodes	confdata->lwnext = 0;
234135446Strhodes	confdata->domainname = NULL;
235135446Strhodes	confdata->searchnxt = 0;
236135446Strhodes	confdata->sortlistnxt = 0;
237135446Strhodes	confdata->resdebug = 0;
238135446Strhodes	confdata->ndots = 1;
239135446Strhodes	confdata->no_tld_query = 0;
240135446Strhodes
241135446Strhodes	for (i = 0; i < LWRES_CONFMAXNAMESERVERS; i++)
242135446Strhodes		lwres_resetaddr(&confdata->nameservers[i]);
243135446Strhodes
244135446Strhodes	for (i = 0; i < LWRES_CONFMAXSEARCH; i++)
245135446Strhodes		confdata->search[i] = NULL;
246135446Strhodes
247135446Strhodes	for (i = 0; i < LWRES_CONFMAXSORTLIST; i++) {
248135446Strhodes		lwres_resetaddr(&confdata->sortlist[i].addr);
249135446Strhodes		lwres_resetaddr(&confdata->sortlist[i].mask);
250135446Strhodes	}
251135446Strhodes}
252135446Strhodes
253170222Sdougb/*% Frees up all the internal memory used by the config data structure, returning it to the lwres_context_t. */
254135446Strhodesvoid
255135446Strhodeslwres_conf_clear(lwres_context_t *ctx) {
256135446Strhodes	int i;
257135446Strhodes	lwres_conf_t *confdata;
258135446Strhodes
259135446Strhodes	REQUIRE(ctx != NULL);
260135446Strhodes	confdata = &ctx->confdata;
261135446Strhodes
262135446Strhodes	for (i = 0; i < confdata->nsnext; i++)
263135446Strhodes		lwres_resetaddr(&confdata->nameservers[i]);
264135446Strhodes
265135446Strhodes	if (confdata->domainname != NULL) {
266135446Strhodes		CTXFREE(confdata->domainname,
267135446Strhodes			strlen(confdata->domainname) + 1);
268135446Strhodes		confdata->domainname = NULL;
269135446Strhodes	}
270135446Strhodes
271135446Strhodes	for (i = 0; i < confdata->searchnxt; i++) {
272135446Strhodes		if (confdata->search[i] != NULL) {
273135446Strhodes			CTXFREE(confdata->search[i],
274135446Strhodes				strlen(confdata->search[i]) + 1);
275135446Strhodes			confdata->search[i] = NULL;
276135446Strhodes		}
277135446Strhodes	}
278135446Strhodes
279135446Strhodes	for (i = 0; i < LWRES_CONFMAXSORTLIST; i++) {
280135446Strhodes		lwres_resetaddr(&confdata->sortlist[i].addr);
281135446Strhodes		lwres_resetaddr(&confdata->sortlist[i].mask);
282135446Strhodes	}
283135446Strhodes
284135446Strhodes	confdata->nsnext = 0;
285135446Strhodes	confdata->lwnext = 0;
286135446Strhodes	confdata->domainname = NULL;
287135446Strhodes	confdata->searchnxt = 0;
288135446Strhodes	confdata->sortlistnxt = 0;
289135446Strhodes	confdata->resdebug = 0;
290135446Strhodes	confdata->ndots = 1;
291135446Strhodes	confdata->no_tld_query = 0;
292135446Strhodes}
293135446Strhodes
294135446Strhodesstatic lwres_result_t
295135446Strhodeslwres_conf_parsenameserver(lwres_context_t *ctx,  FILE *fp) {
296135446Strhodes	char word[LWRES_CONFMAXLINELEN];
297135446Strhodes	int res;
298135446Strhodes	lwres_conf_t *confdata;
299153816Sdougb	lwres_addr_t address;
300135446Strhodes
301135446Strhodes	confdata = &ctx->confdata;
302135446Strhodes
303135446Strhodes	if (confdata->nsnext == LWRES_CONFMAXNAMESERVERS)
304135446Strhodes		return (LWRES_R_SUCCESS);
305135446Strhodes
306135446Strhodes	res = getword(fp, word, sizeof(word));
307135446Strhodes	if (strlen(word) == 0U)
308135446Strhodes		return (LWRES_R_FAILURE); /* Nothing on line. */
309135446Strhodes	else if (res == ' ' || res == '\t')
310135446Strhodes		res = eatwhite(fp);
311135446Strhodes
312135446Strhodes	if (res != EOF && res != '\n')
313135446Strhodes		return (LWRES_R_FAILURE); /* Extra junk on line. */
314135446Strhodes
315153816Sdougb	res = lwres_create_addr(word, &address, 1);
316193149Sdougb	if (res == LWRES_R_SUCCESS &&
317193149Sdougb	    ((address.family == LWRES_ADDRTYPE_V4 && ctx->use_ipv4 == 1) ||
318193149Sdougb	     (address.family == LWRES_ADDRTYPE_V6 && ctx->use_ipv6 == 1))) {
319153816Sdougb		confdata->nameservers[confdata->nsnext++] = address;
320193149Sdougb	}
321135446Strhodes
322135446Strhodes	return (LWRES_R_SUCCESS);
323135446Strhodes}
324135446Strhodes
325135446Strhodesstatic lwres_result_t
326135446Strhodeslwres_conf_parselwserver(lwres_context_t *ctx,  FILE *fp) {
327135446Strhodes	char word[LWRES_CONFMAXLINELEN];
328135446Strhodes	int res;
329135446Strhodes	lwres_conf_t *confdata;
330135446Strhodes
331135446Strhodes	confdata = &ctx->confdata;
332135446Strhodes
333135446Strhodes	if (confdata->lwnext == LWRES_CONFMAXLWSERVERS)
334135446Strhodes		return (LWRES_R_SUCCESS);
335135446Strhodes
336135446Strhodes	res = getword(fp, word, sizeof(word));
337135446Strhodes	if (strlen(word) == 0U)
338135446Strhodes		return (LWRES_R_FAILURE); /* Nothing on line. */
339135446Strhodes	else if (res == ' ' || res == '\t')
340135446Strhodes		res = eatwhite(fp);
341135446Strhodes
342135446Strhodes	if (res != EOF && res != '\n')
343135446Strhodes		return (LWRES_R_FAILURE); /* Extra junk on line. */
344135446Strhodes
345135446Strhodes	res = lwres_create_addr(word,
346135446Strhodes				&confdata->lwservers[confdata->lwnext++], 1);
347135446Strhodes	if (res != LWRES_R_SUCCESS)
348135446Strhodes		return (res);
349135446Strhodes
350135446Strhodes	return (LWRES_R_SUCCESS);
351135446Strhodes}
352135446Strhodes
353135446Strhodesstatic lwres_result_t
354135446Strhodeslwres_conf_parsedomain(lwres_context_t *ctx,  FILE *fp) {
355135446Strhodes	char word[LWRES_CONFMAXLINELEN];
356135446Strhodes	int res, i;
357135446Strhodes	lwres_conf_t *confdata;
358135446Strhodes
359135446Strhodes	confdata = &ctx->confdata;
360135446Strhodes
361135446Strhodes	res = getword(fp, word, sizeof(word));
362135446Strhodes	if (strlen(word) == 0U)
363135446Strhodes		return (LWRES_R_FAILURE); /* Nothing else on line. */
364135446Strhodes	else if (res == ' ' || res == '\t')
365135446Strhodes		res = eatwhite(fp);
366135446Strhodes
367135446Strhodes	if (res != EOF && res != '\n')
368135446Strhodes		return (LWRES_R_FAILURE); /* Extra junk on line. */
369135446Strhodes
370135446Strhodes	if (confdata->domainname != NULL)
371135446Strhodes		CTXFREE(confdata->domainname,
372135446Strhodes			strlen(confdata->domainname) + 1); /*  */
373135446Strhodes
374135446Strhodes	/*
375135446Strhodes	 * Search and domain are mutually exclusive.
376135446Strhodes	 */
377135446Strhodes	for (i = 0; i < LWRES_CONFMAXSEARCH; i++) {
378135446Strhodes		if (confdata->search[i] != NULL) {
379135446Strhodes			CTXFREE(confdata->search[i],
380135446Strhodes				strlen(confdata->search[i])+1);
381135446Strhodes			confdata->search[i] = NULL;
382135446Strhodes		}
383135446Strhodes	}
384135446Strhodes	confdata->searchnxt = 0;
385135446Strhodes
386135446Strhodes	confdata->domainname = lwres_strdup(ctx, word);
387135446Strhodes
388135446Strhodes	if (confdata->domainname == NULL)
389135446Strhodes		return (LWRES_R_FAILURE);
390135446Strhodes
391135446Strhodes	return (LWRES_R_SUCCESS);
392135446Strhodes}
393135446Strhodes
394135446Strhodesstatic lwres_result_t
395135446Strhodeslwres_conf_parsesearch(lwres_context_t *ctx,  FILE *fp) {
396135446Strhodes	int idx, delim;
397135446Strhodes	char word[LWRES_CONFMAXLINELEN];
398135446Strhodes	lwres_conf_t *confdata;
399135446Strhodes
400135446Strhodes	confdata = &ctx->confdata;
401135446Strhodes
402135446Strhodes	if (confdata->domainname != NULL) {
403135446Strhodes		/*
404135446Strhodes		 * Search and domain are mutually exclusive.
405135446Strhodes		 */
406135446Strhodes		CTXFREE(confdata->domainname,
407135446Strhodes			strlen(confdata->domainname) + 1);
408135446Strhodes		confdata->domainname = NULL;
409135446Strhodes	}
410135446Strhodes
411135446Strhodes	/*
412135446Strhodes	 * Remove any previous search definitions.
413135446Strhodes	 */
414135446Strhodes	for (idx = 0; idx < LWRES_CONFMAXSEARCH; idx++) {
415135446Strhodes		if (confdata->search[idx] != NULL) {
416135446Strhodes			CTXFREE(confdata->search[idx],
417135446Strhodes				strlen(confdata->search[idx])+1);
418135446Strhodes			confdata->search[idx] = NULL;
419135446Strhodes		}
420135446Strhodes	}
421135446Strhodes	confdata->searchnxt = 0;
422135446Strhodes
423135446Strhodes	delim = getword(fp, word, sizeof(word));
424135446Strhodes	if (strlen(word) == 0U)
425135446Strhodes		return (LWRES_R_FAILURE); /* Nothing else on line. */
426135446Strhodes
427135446Strhodes	idx = 0;
428135446Strhodes	while (strlen(word) > 0U) {
429135446Strhodes		if (confdata->searchnxt == LWRES_CONFMAXSEARCH)
430135446Strhodes			goto ignore; /* Too many domains. */
431135446Strhodes
432135446Strhodes		confdata->search[idx] = lwres_strdup(ctx, word);
433135446Strhodes		if (confdata->search[idx] == NULL)
434135446Strhodes			return (LWRES_R_FAILURE);
435135446Strhodes		idx++;
436135446Strhodes		confdata->searchnxt++;
437135446Strhodes
438135446Strhodes	ignore:
439135446Strhodes		if (delim == EOF || delim == '\n')
440135446Strhodes			break;
441135446Strhodes		else
442135446Strhodes			delim = getword(fp, word, sizeof(word));
443135446Strhodes	}
444135446Strhodes
445135446Strhodes	return (LWRES_R_SUCCESS);
446135446Strhodes}
447135446Strhodes
448135446Strhodesstatic lwres_result_t
449135446Strhodeslwres_create_addr(const char *buffer, lwres_addr_t *addr, int convert_zero) {
450135446Strhodes	struct in_addr v4;
451135446Strhodes	struct in6_addr v6;
452135446Strhodes
453135446Strhodes	if (lwres_net_aton(buffer, &v4) == 1) {
454135446Strhodes		if (convert_zero) {
455135446Strhodes			unsigned char zeroaddress[] = {0, 0, 0, 0};
456135446Strhodes			unsigned char loopaddress[] = {127, 0, 0, 1};
457135446Strhodes			if (memcmp(&v4, zeroaddress, 4) == 0)
458262706Serwin				memmove(&v4, loopaddress, 4);
459135446Strhodes		}
460135446Strhodes		addr->family = LWRES_ADDRTYPE_V4;
461135446Strhodes		addr->length = NS_INADDRSZ;
462262706Serwin		memmove((void *)addr->address, &v4, NS_INADDRSZ);
463135446Strhodes
464135446Strhodes	} else if (lwres_net_pton(AF_INET6, buffer, &v6) == 1) {
465135446Strhodes		addr->family = LWRES_ADDRTYPE_V6;
466135446Strhodes		addr->length = NS_IN6ADDRSZ;
467262706Serwin		memmove((void *)addr->address, &v6, NS_IN6ADDRSZ);
468135446Strhodes	} else {
469135446Strhodes		return (LWRES_R_FAILURE); /* Unrecognised format. */
470135446Strhodes	}
471135446Strhodes
472135446Strhodes	return (LWRES_R_SUCCESS);
473135446Strhodes}
474135446Strhodes
475135446Strhodesstatic lwres_result_t
476135446Strhodeslwres_conf_parsesortlist(lwres_context_t *ctx,  FILE *fp) {
477135446Strhodes	int delim, res, idx;
478135446Strhodes	char word[LWRES_CONFMAXLINELEN];
479135446Strhodes	char *p;
480135446Strhodes	lwres_conf_t *confdata;
481135446Strhodes
482135446Strhodes	confdata = &ctx->confdata;
483135446Strhodes
484135446Strhodes	delim = getword(fp, word, sizeof(word));
485135446Strhodes	if (strlen(word) == 0U)
486135446Strhodes		return (LWRES_R_FAILURE); /* Empty line after keyword. */
487135446Strhodes
488135446Strhodes	while (strlen(word) > 0U) {
489135446Strhodes		if (confdata->sortlistnxt == LWRES_CONFMAXSORTLIST)
490135446Strhodes			return (LWRES_R_FAILURE); /* Too many values. */
491135446Strhodes
492135446Strhodes		p = strchr(word, '/');
493135446Strhodes		if (p != NULL)
494135446Strhodes			*p++ = '\0';
495135446Strhodes
496135446Strhodes		idx = confdata->sortlistnxt;
497135446Strhodes		res = lwres_create_addr(word, &confdata->sortlist[idx].addr, 1);
498135446Strhodes		if (res != LWRES_R_SUCCESS)
499135446Strhodes			return (res);
500135446Strhodes
501135446Strhodes		if (p != NULL) {
502135446Strhodes			res = lwres_create_addr(p,
503135446Strhodes						&confdata->sortlist[idx].mask,
504135446Strhodes						0);
505135446Strhodes			if (res != LWRES_R_SUCCESS)
506135446Strhodes				return (res);
507135446Strhodes		} else {
508135446Strhodes			/*
509135446Strhodes			 * Make up a mask.
510135446Strhodes			 */
511135446Strhodes			confdata->sortlist[idx].mask =
512135446Strhodes				confdata->sortlist[idx].addr;
513135446Strhodes
514135446Strhodes			memset(&confdata->sortlist[idx].mask.address, 0xff,
515135446Strhodes			       confdata->sortlist[idx].addr.length);
516135446Strhodes		}
517135446Strhodes
518135446Strhodes		confdata->sortlistnxt++;
519135446Strhodes
520135446Strhodes		if (delim == EOF || delim == '\n')
521135446Strhodes			break;
522135446Strhodes		else
523135446Strhodes			delim = getword(fp, word, sizeof(word));
524135446Strhodes	}
525135446Strhodes
526135446Strhodes	return (LWRES_R_SUCCESS);
527135446Strhodes}
528135446Strhodes
529135446Strhodesstatic lwres_result_t
530135446Strhodeslwres_conf_parseoption(lwres_context_t *ctx,  FILE *fp) {
531135446Strhodes	int delim;
532135446Strhodes	long ndots;
533135446Strhodes	char *p;
534135446Strhodes	char word[LWRES_CONFMAXLINELEN];
535135446Strhodes	lwres_conf_t *confdata;
536135446Strhodes
537135446Strhodes	REQUIRE(ctx != NULL);
538135446Strhodes	confdata = &ctx->confdata;
539135446Strhodes
540135446Strhodes	delim = getword(fp, word, sizeof(word));
541135446Strhodes	if (strlen(word) == 0U)
542135446Strhodes		return (LWRES_R_FAILURE); /* Empty line after keyword. */
543135446Strhodes
544135446Strhodes	while (strlen(word) > 0U) {
545135446Strhodes		if (strcmp("debug", word) == 0) {
546135446Strhodes			confdata->resdebug = 1;
547135446Strhodes		} else if (strcmp("no_tld_query", word) == 0) {
548135446Strhodes			confdata->no_tld_query = 1;
549135446Strhodes		} else if (strncmp("ndots:", word, 6) == 0) {
550135446Strhodes			ndots = strtol(word + 6, &p, 10);
551135446Strhodes			if (*p != '\0') /* Bad string. */
552135446Strhodes				return (LWRES_R_FAILURE);
553135446Strhodes			if (ndots < 0 || ndots > 0xff) /* Out of range. */
554135446Strhodes				return (LWRES_R_FAILURE);
555135446Strhodes			confdata->ndots = (lwres_uint8_t)ndots;
556135446Strhodes		}
557135446Strhodes
558135446Strhodes		if (delim == EOF || delim == '\n')
559135446Strhodes			break;
560135446Strhodes		else
561135446Strhodes			delim = getword(fp, word, sizeof(word));
562135446Strhodes	}
563135446Strhodes
564135446Strhodes	return (LWRES_R_SUCCESS);
565135446Strhodes}
566135446Strhodes
567170222Sdougb/*% parses a file and fills in the data structure. */
568135446Strhodeslwres_result_t
569135446Strhodeslwres_conf_parse(lwres_context_t *ctx, const char *filename) {
570135446Strhodes	FILE *fp = NULL;
571135446Strhodes	char word[256];
572135446Strhodes	lwres_result_t rval, ret;
573135446Strhodes	lwres_conf_t *confdata;
574135446Strhodes	int stopchar;
575135446Strhodes
576135446Strhodes	REQUIRE(ctx != NULL);
577135446Strhodes	confdata = &ctx->confdata;
578135446Strhodes
579135446Strhodes	REQUIRE(filename != NULL);
580135446Strhodes	REQUIRE(strlen(filename) > 0U);
581135446Strhodes	REQUIRE(confdata != NULL);
582135446Strhodes
583135446Strhodes	errno = 0;
584135446Strhodes	if ((fp = fopen(filename, "r")) == NULL)
585165071Sdougb		return (LWRES_R_NOTFOUND);
586135446Strhodes
587135446Strhodes	ret = LWRES_R_SUCCESS;
588135446Strhodes	do {
589135446Strhodes		stopchar = getword(fp, word, sizeof(word));
590135446Strhodes		if (stopchar == EOF) {
591135446Strhodes			rval = LWRES_R_SUCCESS;
592225361Sdougb			POST(rval);
593135446Strhodes			break;
594135446Strhodes		}
595135446Strhodes
596135446Strhodes		if (strlen(word) == 0U)
597135446Strhodes			rval = LWRES_R_SUCCESS;
598135446Strhodes		else if (strcmp(word, "nameserver") == 0)
599135446Strhodes			rval = lwres_conf_parsenameserver(ctx, fp);
600135446Strhodes		else if (strcmp(word, "lwserver") == 0)
601135446Strhodes			rval = lwres_conf_parselwserver(ctx, fp);
602135446Strhodes		else if (strcmp(word, "domain") == 0)
603135446Strhodes			rval = lwres_conf_parsedomain(ctx, fp);
604135446Strhodes		else if (strcmp(word, "search") == 0)
605135446Strhodes			rval = lwres_conf_parsesearch(ctx, fp);
606135446Strhodes		else if (strcmp(word, "sortlist") == 0)
607135446Strhodes			rval = lwres_conf_parsesortlist(ctx, fp);
608135446Strhodes		else if (strcmp(word, "options") == 0)
609135446Strhodes			rval = lwres_conf_parseoption(ctx, fp);
610135446Strhodes		else {
611135446Strhodes			/* unrecognised word. Ignore entire line */
612135446Strhodes			rval = LWRES_R_SUCCESS;
613135446Strhodes			stopchar = eatline(fp);
614135446Strhodes			if (stopchar == EOF) {
615135446Strhodes				break;
616135446Strhodes			}
617135446Strhodes		}
618135446Strhodes		if (ret == LWRES_R_SUCCESS && rval != LWRES_R_SUCCESS)
619135446Strhodes			ret = rval;
620135446Strhodes	} while (1);
621135446Strhodes
622135446Strhodes	fclose(fp);
623135446Strhodes
624135446Strhodes	return (ret);
625135446Strhodes}
626135446Strhodes
627170222Sdougb/*% Prints the config data structure to the FILE. */
628135446Strhodeslwres_result_t
629135446Strhodeslwres_conf_print(lwres_context_t *ctx, FILE *fp) {
630135446Strhodes	int i;
631135446Strhodes	int af;
632135446Strhodes	char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
633135446Strhodes	const char *p;
634135446Strhodes	lwres_conf_t *confdata;
635135446Strhodes	lwres_addr_t tmpaddr;
636135446Strhodes
637135446Strhodes	REQUIRE(ctx != NULL);
638135446Strhodes	confdata = &ctx->confdata;
639135446Strhodes
640135446Strhodes	REQUIRE(confdata->nsnext <= LWRES_CONFMAXNAMESERVERS);
641135446Strhodes
642135446Strhodes	for (i = 0; i < confdata->nsnext; i++) {
643135446Strhodes		af = lwresaddr2af(confdata->nameservers[i].family);
644135446Strhodes
645135446Strhodes		p = lwres_net_ntop(af, confdata->nameservers[i].address,
646135446Strhodes				   tmp, sizeof(tmp));
647135446Strhodes		if (p != tmp)
648135446Strhodes			return (LWRES_R_FAILURE);
649135446Strhodes
650135446Strhodes		fprintf(fp, "nameserver %s\n", tmp);
651135446Strhodes	}
652135446Strhodes
653135446Strhodes	for (i = 0; i < confdata->lwnext; i++) {
654135446Strhodes		af = lwresaddr2af(confdata->lwservers[i].family);
655135446Strhodes
656135446Strhodes		p = lwres_net_ntop(af, confdata->lwservers[i].address,
657135446Strhodes				   tmp, sizeof(tmp));
658135446Strhodes		if (p != tmp)
659135446Strhodes			return (LWRES_R_FAILURE);
660135446Strhodes
661135446Strhodes		fprintf(fp, "lwserver %s\n", tmp);
662135446Strhodes	}
663135446Strhodes
664135446Strhodes	if (confdata->domainname != NULL) {
665135446Strhodes		fprintf(fp, "domain %s\n", confdata->domainname);
666135446Strhodes	} else if (confdata->searchnxt > 0) {
667135446Strhodes		REQUIRE(confdata->searchnxt <= LWRES_CONFMAXSEARCH);
668135446Strhodes
669135446Strhodes		fprintf(fp, "search");
670135446Strhodes		for (i = 0; i < confdata->searchnxt; i++)
671135446Strhodes			fprintf(fp, " %s", confdata->search[i]);
672135446Strhodes		fputc('\n', fp);
673135446Strhodes	}
674135446Strhodes
675135446Strhodes	REQUIRE(confdata->sortlistnxt <= LWRES_CONFMAXSORTLIST);
676135446Strhodes
677135446Strhodes	if (confdata->sortlistnxt > 0) {
678135446Strhodes		fputs("sortlist", fp);
679135446Strhodes		for (i = 0; i < confdata->sortlistnxt; i++) {
680135446Strhodes			af = lwresaddr2af(confdata->sortlist[i].addr.family);
681135446Strhodes
682135446Strhodes			p = lwres_net_ntop(af,
683135446Strhodes					   confdata->sortlist[i].addr.address,
684135446Strhodes					   tmp, sizeof(tmp));
685135446Strhodes			if (p != tmp)
686135446Strhodes				return (LWRES_R_FAILURE);
687135446Strhodes
688135446Strhodes			fprintf(fp, " %s", tmp);
689135446Strhodes
690135446Strhodes			tmpaddr = confdata->sortlist[i].mask;
691135446Strhodes			memset(&tmpaddr.address, 0xff, tmpaddr.length);
692135446Strhodes
693135446Strhodes			if (memcmp(&tmpaddr.address,
694135446Strhodes				   confdata->sortlist[i].mask.address,
695135446Strhodes				   confdata->sortlist[i].mask.length) != 0) {
696135446Strhodes				af = lwresaddr2af(
697135446Strhodes					    confdata->sortlist[i].mask.family);
698135446Strhodes				p = lwres_net_ntop
699135446Strhodes					(af,
700135446Strhodes					 confdata->sortlist[i].mask.address,
701135446Strhodes					 tmp, sizeof(tmp));
702135446Strhodes				if (p != tmp)
703135446Strhodes					return (LWRES_R_FAILURE);
704135446Strhodes
705135446Strhodes				fprintf(fp, "/%s", tmp);
706135446Strhodes			}
707135446Strhodes		}
708135446Strhodes		fputc('\n', fp);
709135446Strhodes	}
710135446Strhodes
711135446Strhodes	if (confdata->resdebug)
712135446Strhodes		fprintf(fp, "options debug\n");
713135446Strhodes
714135446Strhodes	if (confdata->ndots > 0)
715135446Strhodes		fprintf(fp, "options ndots:%d\n", confdata->ndots);
716135446Strhodes
717135446Strhodes	if (confdata->no_tld_query)
718135446Strhodes		fprintf(fp, "options no_tld_query\n");
719135446Strhodes
720135446Strhodes	return (LWRES_R_SUCCESS);
721135446Strhodes}
722135446Strhodes
723170222Sdougb/*% Returns a pointer to the current config structure. */
724135446Strhodeslwres_conf_t *
725135446Strhodeslwres_conf_get(lwres_context_t *ctx) {
726135446Strhodes	REQUIRE(ctx != NULL);
727135446Strhodes
728135446Strhodes	return (&ctx->confdata);
729135446Strhodes}
730