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