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