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