xref: /openbsd-src/lib/libc/asr/asr.c (revision 5826fd8c7d12c123a8902345d0fa32381e3d949d)
1 /*	$OpenBSD: asr.c,v 1.41 2015/09/14 11:52:49 guenther Exp $	*/
2 /*
3  * Copyright (c) 2010-2012 Eric Faurot <eric@openbsd.org>
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 THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <sys/stat.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23 #include <arpa/nameser.h>
24 #include <netdb.h>
25 
26 #include <asr.h>
27 #include <err.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <resolv.h>
31 #include <poll.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <limits.h>
37 
38 #include "asr_private.h"
39 
40 #ifndef ASR_OPT_THREADSAFE
41 #define ASR_OPT_THREADSAFE 1
42 #endif
43 #ifndef ASR_OPT_HOSTALIASES
44 #define ASR_OPT_HOSTALIASES 1
45 #endif
46 #ifndef ASR_OPT_ENVOPTS
47 #define ASR_OPT_ENVOPTS 1
48 #endif
49 #ifndef ASR_OPT_RELOADCONF
50 #define ASR_OPT_RELOADCONF 1
51 #endif
52 #ifndef ASR_OPT_ALTCONF
53 #define ASR_OPT_ALTCONF 1
54 #endif
55 
56 #if ASR_OPT_THREADSAFE
57 #include "thread_private.h"
58 #endif
59 
60 #define DEFAULT_CONFFILE	_PATH_RESCONF
61 #define DEFAULT_CONF		"lookup file\n"
62 #define DEFAULT_LOOKUP		"lookup bind file"
63 
64 #define RELOAD_DELAY		15 /* seconds */
65 
66 static void asr_check_reload(struct asr *);
67 static struct asr_ctx *asr_ctx_create(void);
68 static void asr_ctx_ref(struct asr_ctx *);
69 static void asr_ctx_free(struct asr_ctx *);
70 static int asr_ctx_add_searchdomain(struct asr_ctx *, const char *);
71 static int asr_ctx_from_file(struct asr_ctx *, const char *);
72 static int asr_ctx_from_string(struct asr_ctx *, const char *);
73 static int asr_ctx_parse(struct asr_ctx *, const char *);
74 static int asr_parse_nameserver(struct sockaddr *, const char *);
75 static int asr_ndots(const char *);
76 static void pass0(char **, int, struct asr_ctx *);
77 static int strsplit(char *, char **, int);
78 #if ASR_OPT_ENVOPTS
79 static void asr_ctx_envopts(struct asr_ctx *);
80 #endif
81 #if ASR_OPT_THREADSAFE
82 static void *__THREAD_NAME(_asr);
83 #else
84 #	define _THREAD_PRIVATE(a, b, c)  (c)
85 #endif
86 
87 static struct asr *_asr = NULL;
88 
89 /* Allocate and configure an async "resolver". */
90 void *
91 _asr_resolver(const char *conf)
92 {
93 	static int	 init = 0;
94 	struct asr	*asr;
95 
96 	if (init == 0) {
97 #ifdef DEBUG
98 		if (getenv("ASR_DEBUG"))
99 			asr_debug = stderr;
100 #endif
101 		init = 1;
102 	}
103 
104 	if ((asr = calloc(1, sizeof(*asr))) == NULL)
105 		goto fail;
106 
107 #if ASR_OPT_ALTCONF
108 	/* If not setuid/setgid, allow to use an alternate config. */
109 	if (conf == NULL && !issetugid())
110 		conf = getenv("ASR_CONFIG");
111 #endif
112 
113 	if (conf == NULL)
114 		conf = DEFAULT_CONFFILE;
115 
116 	if (conf[0] == '!') {
117 		/* Use the rest of the string as config file */
118 		if ((asr->a_ctx = asr_ctx_create()) == NULL)
119 			goto fail;
120 		if (asr_ctx_from_string(asr->a_ctx, conf + 1) == -1)
121 			goto fail;
122 	} else {
123 		/* Use the given config file */
124 		asr->a_path = strdup(conf);
125 		if (asr->a_path == NULL)
126 			goto fail;
127 		asr_check_reload(asr);
128 		if (asr->a_ctx == NULL) {
129 			if ((asr->a_ctx = asr_ctx_create()) == NULL)
130 				goto fail;
131 			if (asr_ctx_from_string(asr->a_ctx, DEFAULT_CONF) == -1)
132 				goto fail;
133 #if ASR_OPT_ENVOPTS
134 			asr_ctx_envopts(asr->a_ctx);
135 #endif
136 		}
137 	}
138 
139 #ifdef DEBUG
140 	asr_dump_config(asr_debug, asr);
141 #endif
142 	return (asr);
143 
144     fail:
145 	if (asr) {
146 		if (asr->a_ctx)
147 			asr_ctx_free(asr->a_ctx);
148 		free(asr->a_path);
149 		free(asr);
150 	}
151 
152 	return (NULL);
153 }
154 
155 /*
156  * Free the "asr" async resolver (or the thread-local resolver if NULL).
157  * Drop the reference to the current context.
158  */
159 void
160 _asr_resolver_done(void *arg)
161 {
162 	struct asr *asr = arg;
163 	struct asr **priv;
164 
165 	if (asr == NULL) {
166 		priv = _THREAD_PRIVATE(_asr, _asr, &_asr);
167 		if (*priv == NULL)
168 			return;
169 		asr = *priv;
170 		*priv = NULL;
171 	}
172 
173 	_asr_ctx_unref(asr->a_ctx);
174 	free(asr->a_path);
175 	free(asr);
176 }
177 
178 /*
179  * Cancel an async query.
180  */
181 void
182 asr_abort(struct asr_query *as)
183 {
184 	_asr_async_free(as);
185 }
186 
187 /*
188  * Resume the "as" async query resolution.  Return one of ASYNC_COND,
189  * or ASYNC_DONE and put query-specific return values in the user-allocated
190  * memory at "ar".
191  */
192 int
193 asr_run(struct asr_query *as, struct asr_result *ar)
194 {
195 	int	r, saved_errno = errno;
196 
197 	DPRINT("asr: asr_run(%p, %p) %s ctx=[%p]\n", as, ar,
198 	    asr_querystr(as->as_type), as->as_ctx);
199 	r = as->as_run(as, ar);
200 
201 	DPRINT("asr: asr_run(%p, %p) -> %s", as, ar, asr_transitionstr(r));
202 #ifdef DEBUG
203 	if (r == ASYNC_COND)
204 #endif
205 		DPRINT(" fd=%i timeout=%i", ar->ar_fd, ar->ar_timeout);
206 	DPRINT("\n");
207 	if (r == ASYNC_DONE)
208 		_asr_async_free(as);
209 
210 	errno = saved_errno;
211 
212 	return (r);
213 }
214 DEF_WEAK(asr_run);
215 
216 /*
217  * Same as above, but run in a loop that handles the fd conditions result.
218  */
219 int
220 asr_run_sync(struct asr_query *as, struct asr_result *ar)
221 {
222 	struct pollfd	 fds[1];
223 	int		 r, saved_errno = errno;
224 
225 	while ((r = asr_run(as, ar)) == ASYNC_COND) {
226 		fds[0].fd = ar->ar_fd;
227 		fds[0].events = (ar->ar_cond == ASR_WANT_READ) ? POLLIN:POLLOUT;
228 	again:
229 		r = poll(fds, 1, ar->ar_timeout);
230 		if (r == -1 && errno == EINTR)
231 			goto again;
232 		/*
233 		 * Otherwise, just ignore the error and let asr_run()
234 		 * catch the failure.
235 		 */
236 	}
237 
238 	errno = saved_errno;
239 
240 	return (r);
241 }
242 DEF_WEAK(asr_run_sync);
243 
244 /*
245  * Create a new async request of the given "type" on the async context "ac".
246  * Take a reference on it so it does not gets deleted while the async query
247  * is running.
248  */
249 struct asr_query *
250 _asr_async_new(struct asr_ctx *ac, int type)
251 {
252 	struct asr_query	*as;
253 
254 	DPRINT("asr: asr_async_new(ctx=%p) type=%i refcount=%i\n", ac, type,
255 	    ac ? ac->ac_refcount : 0);
256 	if (ac == NULL || (as = calloc(1, sizeof(*as))) == NULL)
257 		return (NULL);
258 
259 	ac->ac_refcount += 1;
260 	as->as_ctx = ac;
261 	as->as_fd = -1;
262 	as->as_type = type;
263 	as->as_state = ASR_STATE_INIT;
264 
265 	return (as);
266 }
267 
268 /*
269  * Free an async query and unref the associated context.
270  */
271 void
272 _asr_async_free(struct asr_query *as)
273 {
274 	DPRINT("asr: asr_async_free(%p)\n", as);
275 	switch (as->as_type) {
276 	case ASR_SEND:
277 		if (as->as_fd != -1)
278 			close(as->as_fd);
279 		if (as->as.dns.obuf && !(as->as.dns.flags & ASYNC_EXTOBUF))
280 			free(as->as.dns.obuf);
281 		if (as->as.dns.ibuf)
282 			free(as->as.dns.ibuf);
283 		if (as->as.dns.dname)
284 			free(as->as.dns.dname);
285 		break;
286 
287 	case ASR_SEARCH:
288 		if (as->as.search.subq)
289 			_asr_async_free(as->as.search.subq);
290 		if (as->as.search.name)
291 			free(as->as.search.name);
292 		break;
293 
294 	case ASR_GETRRSETBYNAME:
295 		if (as->as.rrset.subq)
296 			_asr_async_free(as->as.rrset.subq);
297 		if (as->as.rrset.name)
298 			free(as->as.rrset.name);
299 		break;
300 
301 	case ASR_GETHOSTBYNAME:
302 	case ASR_GETHOSTBYADDR:
303 		if (as->as.hostnamadr.subq)
304 			_asr_async_free(as->as.hostnamadr.subq);
305 		if (as->as.hostnamadr.name)
306 			free(as->as.hostnamadr.name);
307 		break;
308 
309 	case ASR_GETNETBYNAME:
310 	case ASR_GETNETBYADDR:
311 		if (as->as.netnamadr.subq)
312 			_asr_async_free(as->as.netnamadr.subq);
313 		if (as->as.netnamadr.name)
314 			free(as->as.netnamadr.name);
315 		break;
316 
317 	case ASR_GETADDRINFO:
318 		if (as->as.ai.subq)
319 			_asr_async_free(as->as.ai.subq);
320 		if (as->as.ai.aifirst)
321 			freeaddrinfo(as->as.ai.aifirst);
322 		if (as->as.ai.hostname)
323 			free(as->as.ai.hostname);
324 		if (as->as.ai.servname)
325 			free(as->as.ai.servname);
326 		if (as->as.ai.fqdn)
327 			free(as->as.ai.fqdn);
328 		break;
329 
330 	case ASR_GETNAMEINFO:
331 		if (as->as.ni.subq)
332 			_asr_async_free(as->as.ni.subq);
333 		break;
334 	}
335 
336 	_asr_ctx_unref(as->as_ctx);
337 	free(as);
338 }
339 
340 /*
341  * Get a context from the given resolver. This takes a new reference to
342  * the returned context, which *must* be explicitely dropped when done
343  * using this context.
344  */
345 struct asr_ctx *
346 _asr_use_resolver(void *arg)
347 {
348 	struct asr *asr = arg;
349 	struct asr **priv;
350 
351 	if (asr == NULL) {
352 		DPRINT("using thread-local resolver\n");
353 		priv = _THREAD_PRIVATE(_asr, _asr, &_asr);
354 		if (*priv == NULL) {
355 			DPRINT("setting up thread-local resolver\n");
356 			*priv = _asr_resolver(NULL);
357 		}
358 		asr = *priv;
359 	}
360 	if (asr != NULL) {
361 		asr_check_reload(asr);
362 		asr_ctx_ref(asr->a_ctx);
363 		return (asr->a_ctx);
364 	}
365 	return (NULL);
366 }
367 
368 static void
369 asr_ctx_ref(struct asr_ctx *ac)
370 {
371 	DPRINT("asr: asr_ctx_ref(ctx=%p) refcount=%i\n", ac, ac->ac_refcount);
372 	ac->ac_refcount += 1;
373 }
374 
375 /*
376  * Drop a reference to an async context, freeing it if the reference
377  * count drops to 0.
378  */
379 void
380 _asr_ctx_unref(struct asr_ctx *ac)
381 {
382 	DPRINT("asr: asr_ctx_unref(ctx=%p) refcount=%i\n", ac,
383 	    ac ? ac->ac_refcount : 0);
384 	if (ac == NULL)
385 		return;
386 	if (--ac->ac_refcount)
387 		return;
388 
389 	asr_ctx_free(ac);
390 }
391 
392 static void
393 asr_ctx_free(struct asr_ctx *ac)
394 {
395 	int i;
396 
397 	if (ac->ac_domain)
398 		free(ac->ac_domain);
399 	for (i = 0; i < ASR_MAXNS; i++)
400 		free(ac->ac_ns[i]);
401 	for (i = 0; i < ASR_MAXDOM; i++)
402 		free(ac->ac_dom[i]);
403 
404 	free(ac);
405 }
406 
407 /*
408  * Reload the configuration file if it has changed on disk.
409  */
410 static void
411 asr_check_reload(struct asr *asr)
412 {
413 	struct asr_ctx	*ac;
414 #if ASR_OPT_RELOADCONF
415 	struct stat	 st;
416 	struct timespec	 ts;
417 	pid_t		 pid;
418 #endif
419 
420 	if (asr->a_path == NULL)
421 		return;
422 
423 #if ASR_OPT_RELOADCONF
424 
425 	pid = getpid();
426 	if (pid != asr->a_pid) {
427 		asr->a_pid = pid;
428 		asr->a_rtime = 0;
429 	}
430 
431 	if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
432 		return;
433 
434 	if ((ts.tv_sec - asr->a_rtime) < RELOAD_DELAY && asr->a_rtime != 0)
435 		return;
436 	asr->a_rtime = ts.tv_sec;
437 
438 	DPRINT("asr: checking for update of \"%s\"\n", asr->a_path);
439 	if (stat(asr->a_path, &st) == -1 ||
440 	    asr->a_mtime == st.st_mtime ||
441 	    (ac = asr_ctx_create()) == NULL)
442 		return;
443 	asr->a_mtime = st.st_mtime;
444 #else
445 	if ((ac = asr_ctx_create()) == NULL)
446 		return;
447 #endif
448 
449 	DPRINT("asr: reloading config file\n");
450 	if (asr_ctx_from_file(ac, asr->a_path) == -1) {
451 		asr_ctx_free(ac);
452 		return;
453 	}
454 
455 #if ASR_OPT_ENVOPTS
456 	asr_ctx_envopts(ac);
457 #endif
458 	if (asr->a_ctx)
459 		_asr_ctx_unref(asr->a_ctx);
460 	asr->a_ctx = ac;
461 }
462 
463 /*
464  * Construct a fully-qualified domain name for the given name and domain.
465  * If "name" ends with a '.' it is considered as a FQDN by itself.
466  * Otherwise, the domain, which must be a FQDN, is appended to "name" (it
467  * may have a leading dot which would be ignored). If the domain is null,
468  * then "." is used. Return the length of the constructed FQDN or (0) on
469  * error.
470  */
471 size_t
472 _asr_make_fqdn(const char *name, const char *domain, char *buf, size_t buflen)
473 {
474 	size_t	len;
475 
476 	if (domain == NULL)
477 		domain = ".";
478 	else if ((len = strlen(domain)) == 0)
479 		return (0);
480 	else if (domain[len -1] != '.')
481 		return (0);
482 
483 	len = strlen(name);
484 	if (len == 0) {
485 		if (strlcpy(buf, domain, buflen) >= buflen)
486 			return (0);
487 	} else if (name[len - 1] !=  '.') {
488 		if (domain[0] == '.')
489 			domain += 1;
490 		if (strlcpy(buf, name, buflen) >= buflen ||
491 		    strlcat(buf, ".", buflen) >= buflen ||
492 		    strlcat(buf, domain, buflen) >= buflen)
493 			return (0);
494 	} else {
495 		if (strlcpy(buf, name, buflen) >= buflen)
496 			return (0);
497 	}
498 
499 	return (strlen(buf));
500 }
501 
502 /*
503  * Count the dots in a string.
504  */
505 static int
506 asr_ndots(const char *s)
507 {
508 	int n;
509 
510 	for (n = 0; *s; s++)
511 		if (*s == '.')
512 			n += 1;
513 
514 	return (n);
515 }
516 
517 /*
518  * Allocate a new empty context.
519  */
520 static struct asr_ctx *
521 asr_ctx_create(void)
522 {
523 	struct asr_ctx	*ac;
524 
525 	if ((ac = calloc(1, sizeof(*ac))) == NULL)
526 		return (NULL);
527 
528 	ac->ac_options = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH;
529 	ac->ac_refcount = 1;
530 	ac->ac_ndots = 1;
531 	ac->ac_family[0] = AF_INET;
532 	ac->ac_family[1] = AF_INET6;
533 	ac->ac_family[2] = -1;
534 
535 	ac->ac_nscount = 0;
536 	ac->ac_nstimeout = 5;
537 	ac->ac_nsretries = 4;
538 
539 	return (ac);
540 }
541 
542 /*
543  * Add a search domain to the async context.
544  */
545 static int
546 asr_ctx_add_searchdomain(struct asr_ctx *ac, const char *domain)
547 {
548 	char buf[MAXDNAME];
549 
550 	if (ac->ac_domcount == ASR_MAXDOM)
551 		return (-1);
552 
553 	if (_asr_make_fqdn(domain, NULL, buf, sizeof(buf)) == 0)
554 		return (-1);
555 
556 	if ((ac->ac_dom[ac->ac_domcount] = strdup(buf)) == NULL)
557 		return (0);
558 
559 	ac->ac_domcount += 1;
560 
561 	return (1);
562 }
563 
564 static int
565 strsplit(char *line, char **tokens, int ntokens)
566 {
567 	int	ntok;
568 	char	*cp, **tp;
569 
570 	for (cp = line, tp = tokens, ntok = 0;
571 	    ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; )
572 		if (**tp != '\0') {
573 			tp++;
574 			ntok++;
575 		}
576 
577 	return (ntok);
578 }
579 
580 /*
581  * Pass on a split config line.
582  */
583 static void
584 pass0(char **tok, int n, struct asr_ctx *ac)
585 {
586 	int		 i, j, d;
587 	const char	*e;
588 	struct sockaddr_storage	ss;
589 
590 	if (!strcmp(tok[0], "nameserver")) {
591 		if (ac->ac_nscount == ASR_MAXNS)
592 			return;
593 		if (n != 2)
594 			return;
595 		if (asr_parse_nameserver((struct sockaddr *)&ss, tok[1]))
596 			return;
597 		if ((ac->ac_ns[ac->ac_nscount] = calloc(1, ss.ss_len)) == NULL)
598 			return;
599 		memmove(ac->ac_ns[ac->ac_nscount], &ss, ss.ss_len);
600 		ac->ac_nscount += 1;
601 
602 	} else if (!strcmp(tok[0], "domain")) {
603 		if (n != 2)
604 			return;
605 		if (ac->ac_domain)
606 			return;
607 		ac->ac_domain = strdup(tok[1]);
608 
609 	} else if (!strcmp(tok[0], "lookup")) {
610 		/* ensure that each lookup is only given once */
611 		for (i = 1; i < n; i++)
612 			for (j = i + 1; j < n; j++)
613 				if (!strcmp(tok[i], tok[j]))
614 					return;
615 		ac->ac_dbcount = 0;
616 		for (i = 1; i < n && ac->ac_dbcount < ASR_MAXDB; i++) {
617 			if (!strcmp(tok[i], "yp"))
618 				ac->ac_db[ac->ac_dbcount++] = ASR_DB_YP;
619 			else if (!strcmp(tok[i], "bind"))
620 				ac->ac_db[ac->ac_dbcount++] = ASR_DB_DNS;
621 			else if (!strcmp(tok[i], "file"))
622 				ac->ac_db[ac->ac_dbcount++] = ASR_DB_FILE;
623 		}
624 	} else if (!strcmp(tok[0], "search")) {
625 		/* resolv.conf says the last line wins */
626 		for (i = 0; i < ASR_MAXDOM; i++)
627 			free(ac->ac_dom[i]);
628 		ac->ac_domcount = 0;
629 		for (i = 1; i < n; i++)
630 			asr_ctx_add_searchdomain(ac, tok[i]);
631 
632 	} else if (!strcmp(tok[0], "family")) {
633 		if (n == 1 || n > 3)
634 			return;
635 		for (i = 1; i < n; i++)
636 			if (strcmp(tok[i], "inet4") && strcmp(tok[i], "inet6"))
637 				return;
638 		for (i = 1; i < n; i++)
639 			ac->ac_family[i - 1] = strcmp(tok[i], "inet4") ? \
640 			    AF_INET6 : AF_INET;
641 		ac->ac_family[i - 1] = -1;
642 
643 	} else if (!strcmp(tok[0], "options")) {
644 		for (i = 1; i < n; i++) {
645 			if (!strcmp(tok[i], "tcp"))
646 				ac->ac_options |= RES_USEVC;
647 			else if ((!strncmp(tok[i], "ndots:", 6))) {
648 				e = NULL;
649 				d = strtonum(tok[i] + 6, 1, 16, &e);
650 				if (e == NULL)
651 					ac->ac_ndots = d;
652 			}
653 		}
654 	}
655 }
656 
657 /*
658  * Setup an async context with the config specified in the string "str".
659  */
660 static int
661 asr_ctx_from_string(struct asr_ctx *ac, const char *str)
662 {
663 	char		 buf[512], *ch;
664 
665 	asr_ctx_parse(ac, str);
666 
667 	if (ac->ac_dbcount == 0) {
668 		/* No lookup directive */
669 		asr_ctx_parse(ac, DEFAULT_LOOKUP);
670 	}
671 
672 	if (ac->ac_nscount == 0)
673 		asr_ctx_parse(ac, "nameserver 127.0.0.1");
674 
675 	if (ac->ac_domain == NULL)
676 		if (gethostname(buf, sizeof buf) == 0) {
677 			ch = strchr(buf, '.');
678 			if (ch)
679 				ac->ac_domain = strdup(ch + 1);
680 			else /* Assume root. see resolv.conf(5) */
681 				ac->ac_domain = strdup("");
682 		}
683 
684 	/* If no search domain was specified, use the local subdomains */
685 	if (ac->ac_domcount == 0)
686 		for (ch = ac->ac_domain; ch; ) {
687 			asr_ctx_add_searchdomain(ac, ch);
688 			ch = strchr(ch, '.');
689 			if (ch && asr_ndots(++ch) == 0)
690 				break;
691 		}
692 
693 	return (0);
694 }
695 
696 /*
697  * Setup the "ac" async context from the file at location "path".
698  */
699 static int
700 asr_ctx_from_file(struct asr_ctx *ac, const char *path)
701 {
702 	FILE	*cf;
703 	char	 buf[4096];
704 	ssize_t	 r;
705 
706 	cf = fopen(path, "re");
707 	if (cf == NULL)
708 		return (-1);
709 
710 	r = fread(buf, 1, sizeof buf - 1, cf);
711 	if (feof(cf) == 0) {
712 		DPRINT("asr: config file too long: \"%s\"\n", path);
713 		r = -1;
714 	}
715 	fclose(cf);
716 	if (r == -1)
717 		return (-1);
718 	buf[r] = '\0';
719 
720 	return asr_ctx_from_string(ac, buf);
721 }
722 
723 /*
724  * Parse lines in the configuration string. For each one, split it into
725  * tokens and pass them to "pass0" for processing.
726  */
727 static int
728 asr_ctx_parse(struct asr_ctx *ac, const char *str)
729 {
730 	size_t		 len;
731 	const char	*line;
732 	char		 buf[1024];
733 	char		*tok[10];
734 	int		 ntok;
735 
736 	line = str;
737 	while (*line) {
738 		len = strcspn(line, "\n\0");
739 		if (len < sizeof buf) {
740 			memmove(buf, line, len);
741 			buf[len] = '\0';
742 		} else
743 			buf[0] = '\0';
744 		line += len;
745 		if (*line == '\n')
746 			line++;
747 		buf[strcspn(buf, ";#")] = '\0';
748 		if ((ntok = strsplit(buf, tok, 10)) == 0)
749 			continue;
750 
751 		pass0(tok, ntok, ac);
752 	}
753 
754 	return (0);
755 }
756 
757 #if ASR_OPT_ENVOPTS
758 /*
759  * Check for environment variables altering the configuration as described
760  * in resolv.conf(5).  Altough not documented there, this feature is disabled
761  * for setuid/setgid programs.
762  */
763 static void
764 asr_ctx_envopts(struct asr_ctx *ac)
765 {
766 	char	buf[4096], *e;
767 	size_t	s;
768 
769 	if (issetugid()) {
770 		ac->ac_options |= RES_NOALIASES;
771 		return;
772 	}
773 
774 	if ((e = getenv("RES_OPTIONS")) != NULL) {
775 		strlcpy(buf, "options ", sizeof buf);
776 		strlcat(buf, e, sizeof buf);
777 		s = strlcat(buf, "\n", sizeof buf);
778 		s = strlcat(buf, "\n", sizeof buf);
779 		if (s < sizeof buf)
780 			asr_ctx_parse(ac, buf);
781 	}
782 
783 	if ((e = getenv("LOCALDOMAIN")) != NULL) {
784 		strlcpy(buf, "search ", sizeof buf);
785 		strlcat(buf, e, sizeof buf);
786 		s = strlcat(buf, "\n", sizeof buf);
787 		if (s < sizeof buf)
788 			asr_ctx_parse(ac, buf);
789 	}
790 }
791 #endif
792 
793 /*
794  * Parse a resolv.conf(5) nameserver string into a sockaddr.
795  */
796 static int
797 asr_parse_nameserver(struct sockaddr *sa, const char *s)
798 {
799 	const char	*estr;
800 	char		 buf[256];
801 	char		*port = NULL;
802 	in_port_t	 portno = 53;
803 
804 	if (*s == '[') {
805 		strlcpy(buf, s + 1, sizeof buf);
806 		s = buf;
807 		port = strchr(buf, ']');
808 		if (port == NULL)
809 			return (-1);
810 		*port++ = '\0';
811 		if (*port != ':')
812 			return (-1);
813 		port++;
814 	}
815 
816 	if (port) {
817 		portno = strtonum(port, 1, USHRT_MAX, &estr);
818 		if (estr)
819 			return (-1);
820 	}
821 
822 	if (_asr_sockaddr_from_str(sa, PF_UNSPEC, s) == -1)
823 		return (-1);
824 
825 	if (sa->sa_family == PF_INET)
826 		((struct sockaddr_in *)sa)->sin_port = htons(portno);
827 	else if (sa->sa_family == PF_INET6)
828 		((struct sockaddr_in6 *)sa)->sin6_port = htons(portno);
829 
830 	return (0);
831 }
832 
833 /*
834  * Turn a (uncompressed) DNS domain name into a regular nul-terminated string
835  * where labels are separated by dots. The result is put into the "buf" buffer,
836  * truncated if it exceeds "max" chars. The function returns "buf".
837  */
838 char *
839 _asr_strdname(const char *_dname, char *buf, size_t max)
840 {
841 	const unsigned char *dname = _dname;
842 	char	*res;
843 	size_t	 left, n, count;
844 
845 	if (_dname[0] == 0) {
846 		strlcpy(buf, ".", max);
847 		return buf;
848 	}
849 
850 	res = buf;
851 	left = max - 1;
852 	for (n = 0; dname[0] && left; n += dname[0]) {
853 		count = (dname[0] < (left - 1)) ? dname[0] : (left - 1);
854 		memmove(buf, dname + 1, count);
855 		dname += dname[0] + 1;
856 		left -= count;
857 		buf += count;
858 		if (left) {
859 			left -= 1;
860 			*buf++ = '.';
861 		}
862 	}
863 	buf[0] = 0;
864 
865 	return (res);
866 }
867 
868 /*
869  * Read and split the next line from the given namedb file.
870  * Return -1 on error, or put the result in the "tokens" array of
871  * size "ntoken" and returns the number of token on the line.
872  */
873 int
874 _asr_parse_namedb_line(FILE *file, char **tokens, int ntoken, char *lbuf, size_t sz)
875 {
876 	size_t	  len;
877 	char	 *buf;
878 	int	  ntok;
879 
880     again:
881 	if ((buf = fgetln(file, &len)) == NULL)
882 		return (-1);
883 
884 	if (len >= sz)
885 		goto again;
886 
887 	if (buf[len - 1] == '\n')
888 		len--;
889 	else {
890 		memcpy(lbuf, buf, len);
891 		buf = lbuf;
892 	}
893 
894 	buf[len] = '\0';
895 	buf[strcspn(buf, "#")] = '\0';
896 	if ((ntok = strsplit(buf, tokens, ntoken)) == 0)
897 		goto again;
898 
899 	return (ntok);
900 }
901 
902 /*
903  * Update the async context so that it uses the next configured DB.
904  * Return 0 on success, or -1 if no more DBs is available.
905  */
906 int
907 _asr_iter_db(struct asr_query *as)
908 {
909 	if (as->as_db_idx >= as->as_ctx->ac_dbcount) {
910 		DPRINT("asr_iter_db: done\n");
911 		return (-1);
912 	}
913 
914 	as->as_db_idx += 1;
915 	DPRINT("asr_iter_db: %i\n", as->as_db_idx);
916 
917 	return (0);
918 }
919 
920 /*
921  * Check if the hostname "name" is a user-defined alias as per hostname(7).
922  * If so, copies the result in the buffer "abuf" of size "abufsz" and
923  * return "abuf". Otherwise return NULL.
924  */
925 char *
926 _asr_hostalias(struct asr_ctx *ac, const char *name, char *abuf, size_t abufsz)
927 {
928 #if ASR_OPT_HOSTALIASES
929 	FILE	 *fp;
930 	size_t	  len;
931 	char	 *file, *buf, *tokens[2];
932 	int	  ntok;
933 
934 	if (ac->ac_options & RES_NOALIASES ||
935 	    asr_ndots(name) != 0 ||
936 	    issetugid() ||
937 	    (file = getenv("HOSTALIASES")) == NULL ||
938 	    (fp = fopen(file, "re")) == NULL)
939 		return (NULL);
940 
941 	DPRINT("asr: looking up aliases in \"%s\"\n", file);
942 
943 	while ((buf = fgetln(fp, &len)) != NULL) {
944 		if (buf[len - 1] == '\n')
945 			len--;
946 		buf[len] = '\0';
947 		if ((ntok = strsplit(buf, tokens, 2)) != 2)
948 			continue;
949 		if (!strcasecmp(tokens[0], name)) {
950 			if (strlcpy(abuf, tokens[1], abufsz) > abufsz)
951 				continue;
952 			DPRINT("asr: found alias \"%s\"\n", abuf);
953 			fclose(fp);
954 			return (abuf);
955 		}
956 	}
957 
958 	fclose(fp);
959 #endif
960 	return (NULL);
961 }
962