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