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