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