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