1 /* $OpenBSD: asr.c,v 1.61 2018/10/22 17:31:24 krw 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 "includes.h"
19
20 #include <sys/types.h>
21 #include <sys/socket.h>
22 #include <sys/stat.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
25 #include <arpa/nameser.h>
26 #include <netdb.h>
27
28 #include <asr.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <resolv.h>
32 #include <poll.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <limits.h>
38
39 #include "asr_private.h"
40
41 #include "thread_private.h"
42
43 #define DEFAULT_CONF "lookup file\n"
44 #define DEFAULT_LOOKUP "lookup bind file"
45
46 #define RELOAD_DELAY 15 /* seconds */
47
48 static void asr_check_reload(struct asr *);
49 static struct asr_ctx *asr_ctx_create(void);
50 static void asr_ctx_ref(struct asr_ctx *);
51 static void asr_ctx_free(struct asr_ctx *);
52 static int asr_ctx_add_searchdomain(struct asr_ctx *, const char *);
53 static int asr_ctx_from_file(struct asr_ctx *, const char *);
54 static int asr_ctx_from_string(struct asr_ctx *, const char *);
55 static int asr_ctx_parse(struct asr_ctx *, const char *);
56 static int asr_parse_nameserver(struct sockaddr *, const char *);
57 static int asr_ndots(const char *);
58 static void pass0(char **, int, struct asr_ctx *);
59 static int strsplit(char *, char **, int);
60 static void asr_ctx_envopts(struct asr_ctx *);
61 static void *__THREAD_NAME(_asr);
62
63 static struct asr *_asr = NULL;
64
65 #ifndef HAVE_ISSETUGID
66 #define issetugid() ((getuid() != geteuid()))
67 #endif
68
69 /* Allocate and configure an async "resolver". */
70 static void *
_asr_resolver(void)71 _asr_resolver(void)
72 {
73 static int init = 0;
74 struct asr *asr;
75
76 if (init == 0) {
77 #ifdef DEBUG
78 if (getenv("ASR_DEBUG"))
79 _asr_debug = stderr;
80 #endif
81 init = 1;
82 }
83
84 if ((asr = calloc(1, sizeof(*asr))) == NULL)
85 goto fail;
86
87 asr_check_reload(asr);
88 if (asr->a_ctx == NULL) {
89 if ((asr->a_ctx = asr_ctx_create()) == NULL)
90 goto fail;
91 if (asr_ctx_from_string(asr->a_ctx, DEFAULT_CONF) == -1)
92 goto fail;
93 asr_ctx_envopts(asr->a_ctx);
94 }
95
96 #ifdef DEBUG
97 _asr_dump_config(_asr_debug, asr);
98 #endif
99 return (asr);
100
101 fail:
102 if (asr) {
103 if (asr->a_ctx)
104 asr_ctx_free(asr->a_ctx);
105 free(asr);
106 }
107
108 return (NULL);
109 }
110
111 /*
112 * Free the "asr" async resolver (or the thread-local resolver if NULL).
113 * Drop the reference to the current context.
114 */
115 void
_asr_resolver_done(void * arg)116 _asr_resolver_done(void *arg)
117 {
118 struct asr *asr = arg;
119 struct asr **priv;
120
121 if (asr == NULL) {
122 priv = _THREAD_PRIVATE(_asr, _asr, &_asr);
123 if (*priv == NULL)
124 return;
125 asr = *priv;
126 *priv = NULL;
127 }
128
129 _asr_ctx_unref(asr->a_ctx);
130 free(asr);
131 }
132
133 /*
134 * Cancel an async query.
135 */
136 void
asr_abort(struct asr_query * as)137 asr_abort(struct asr_query *as)
138 {
139 _asr_async_free(as);
140 }
141
142 /*
143 * Resume the "as" async query resolution. Return one of ASYNC_COND,
144 * or ASYNC_DONE and put query-specific return values in the user-allocated
145 * memory at "ar".
146 */
147 int
asr_run(struct asr_query * as,struct asr_result * ar)148 asr_run(struct asr_query *as, struct asr_result *ar)
149 {
150 int r, saved_errno = errno;
151
152 DPRINT("asr: asr_run(%p, %p) %s ctx=[%p]\n", as, ar,
153 _asr_querystr(as->as_type), as->as_ctx);
154 r = as->as_run(as, ar);
155
156 DPRINT("asr: asr_run(%p, %p) -> %s", as, ar, _asr_transitionstr(r));
157 #ifdef DEBUG
158 if (r == ASYNC_COND)
159 #endif
160 DPRINT(" fd=%i timeout=%i", ar->ar_fd, ar->ar_timeout);
161 DPRINT("\n");
162 if (r == ASYNC_DONE)
163 _asr_async_free(as);
164
165 errno = saved_errno;
166
167 return (r);
168 }
169 DEF_WEAK(asr_run);
170
171 static int
poll_intrsafe(struct pollfd * fds,nfds_t nfds,int timeout)172 poll_intrsafe(struct pollfd *fds, nfds_t nfds, int timeout)
173 {
174 struct timespec pollstart, pollend, elapsed;
175 int r;
176
177 if (clock_gettime(CLOCK_MONOTONIC, &pollstart))
178 return -1;
179
180 while ((r = poll(fds, 1, timeout)) == -1 && errno == EINTR) {
181 if (clock_gettime(CLOCK_MONOTONIC, &pollend))
182 return -1;
183 timespecsub(&pollend, &pollstart, &elapsed);
184 timeout -= elapsed.tv_sec * 1000 + elapsed.tv_nsec / 1000000;
185 if (timeout < 1)
186 return 0;
187 }
188
189 return r;
190 }
191
192 /*
193 * Same as asr_run, but run in a loop that handles the fd conditions result.
194 */
195 int
asr_run_sync(struct asr_query * as,struct asr_result * ar)196 asr_run_sync(struct asr_query *as, struct asr_result *ar)
197 {
198 struct pollfd fds[1];
199 int r, saved_errno = errno;
200
201 while ((r = asr_run(as, ar)) == ASYNC_COND) {
202 fds[0].fd = ar->ar_fd;
203 fds[0].events = (ar->ar_cond == ASR_WANT_READ) ? POLLIN:POLLOUT;
204
205 if (poll_intrsafe(fds, 1, ar->ar_timeout) == -1) {
206 memset(ar, 0, sizeof(*ar));
207 ar->ar_errno = errno;
208 ar->ar_h_errno = NETDB_INTERNAL;
209 ar->ar_gai_errno = EAI_SYSTEM;
210 ar->ar_rrset_errno = NETDB_INTERNAL;
211 _asr_async_free(as);
212 errno = saved_errno;
213 return ASYNC_DONE;
214 }
215
216 /*
217 * Otherwise, just ignore the error and let asr_run()
218 * catch the failure.
219 */
220 }
221
222 errno = saved_errno;
223
224 return (r);
225 }
226 DEF_WEAK(asr_run_sync);
227
228 /*
229 * Create a new async request of the given "type" on the async context "ac".
230 * Take a reference on it so it does not get deleted while the async query
231 * is running.
232 */
233 struct asr_query *
_asr_async_new(struct asr_ctx * ac,int type)234 _asr_async_new(struct asr_ctx *ac, int type)
235 {
236 struct asr_query *as;
237
238 DPRINT("asr: asr_async_new(ctx=%p) type=%i refcount=%i\n", ac, type,
239 ac ? ac->ac_refcount : 0);
240 if (ac == NULL || (as = calloc(1, sizeof(*as))) == NULL)
241 return (NULL);
242
243 ac->ac_refcount += 1;
244 as->as_ctx = ac;
245 as->as_fd = -1;
246 as->as_type = type;
247 as->as_state = ASR_STATE_INIT;
248
249 return (as);
250 }
251
252 /*
253 * Free an async query and unref the associated context.
254 */
255 void
_asr_async_free(struct asr_query * as)256 _asr_async_free(struct asr_query *as)
257 {
258 DPRINT("asr: asr_async_free(%p)\n", as);
259
260 if (as->as_subq)
261 _asr_async_free(as->as_subq);
262
263 switch (as->as_type) {
264 case ASR_SEND:
265 if (as->as_fd != -1)
266 close(as->as_fd);
267 if (as->as.dns.obuf && !(as->as_flags & ASYNC_EXTOBUF))
268 free(as->as.dns.obuf);
269 if (as->as.dns.ibuf)
270 free(as->as.dns.ibuf);
271 if (as->as.dns.dname)
272 free(as->as.dns.dname);
273 break;
274
275 case ASR_SEARCH:
276 if (as->as.search.name)
277 free(as->as.search.name);
278 break;
279
280 case ASR_GETRRSETBYNAME:
281 if (as->as.rrset.name)
282 free(as->as.rrset.name);
283 break;
284
285 case ASR_GETHOSTBYNAME:
286 case ASR_GETHOSTBYADDR:
287 if (as->as.hostnamadr.name)
288 free(as->as.hostnamadr.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 explicitly dropped when done
313 * using this context.
314 */
315 struct asr_ctx *
_asr_use_resolver(void * arg)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
asr_ctx_ref(struct asr_ctx * ac)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
_asr_ctx_unref(struct asr_ctx * ac)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
asr_ctx_free(struct asr_ctx * ac)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
asr_check_reload(struct asr * asr)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
_asr_make_fqdn(const char * name,const char * domain,char * buf,size_t buflen)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
asr_ndots(const char * s)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 *
asr_ctx_create(void)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 #ifndef ASR_IPV4_BEFORE_IPV6
489 ac->ac_family[0] = AF_INET6;
490 ac->ac_family[1] = AF_INET;
491 #else
492 ac->ac_family[0] = AF_INET;
493 ac->ac_family[1] = AF_INET6;
494 #endif
495 ac->ac_family[2] = -1;
496
497 ac->ac_nscount = 0;
498 ac->ac_nstimeout = 5;
499 ac->ac_nsretries = 4;
500
501 return (ac);
502 }
503
504 struct asr_ctx *
_asr_no_resolver(void)505 _asr_no_resolver(void)
506 {
507 return asr_ctx_create();
508 }
509
510 /*
511 * Add a search domain to the async context.
512 */
513 static int
asr_ctx_add_searchdomain(struct asr_ctx * ac,const char * domain)514 asr_ctx_add_searchdomain(struct asr_ctx *ac, const char *domain)
515 {
516 char buf[MAXDNAME];
517
518 if (ac->ac_domcount == ASR_MAXDOM)
519 return (-1);
520
521 if (_asr_make_fqdn(domain, NULL, buf, sizeof(buf)) == 0)
522 return (-1);
523
524 if ((ac->ac_dom[ac->ac_domcount] = strdup(buf)) == NULL)
525 return (0);
526
527 ac->ac_domcount += 1;
528
529 return (1);
530 }
531
532 static int
strsplit(char * line,char ** tokens,int ntokens)533 strsplit(char *line, char **tokens, int ntokens)
534 {
535 int ntok;
536 char *cp, **tp;
537
538 for (cp = line, tp = tokens, ntok = 0;
539 ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; )
540 if (**tp != '\0') {
541 tp++;
542 ntok++;
543 }
544
545 return (ntok);
546 }
547
548 /*
549 * Pass on a split config line.
550 */
551 static void
pass0(char ** tok,int n,struct asr_ctx * ac)552 pass0(char **tok, int n, struct asr_ctx *ac)
553 {
554 int i, j, d;
555 const char *e;
556 struct sockaddr_storage ss;
557
558 if (!strcmp(tok[0], "nameserver")) {
559 if (ac->ac_nscount == ASR_MAXNS)
560 return;
561 if (n != 2)
562 return;
563 if (asr_parse_nameserver((struct sockaddr *)&ss, tok[1]))
564 return;
565 if ((ac->ac_ns[ac->ac_nscount] = calloc(1, SS_LEN(&ss))) == NULL)
566 return;
567 memmove(ac->ac_ns[ac->ac_nscount], &ss, SS_LEN(&ss));
568 ac->ac_nscount += 1;
569
570 } else if (!strcmp(tok[0], "domain")) {
571 if (n != 2)
572 return;
573 if (ac->ac_domain)
574 return;
575 ac->ac_domain = strdup(tok[1]);
576
577 } else if (!strcmp(tok[0], "lookup")) {
578 /* ensure that each lookup is only given once */
579 for (i = 1; i < n; i++)
580 for (j = i + 1; j < n; j++)
581 if (!strcmp(tok[i], tok[j]))
582 return;
583 ac->ac_dbcount = 0;
584 for (i = 1; i < n && ac->ac_dbcount < ASR_MAXDB; i++) {
585 if (!strcmp(tok[i], "yp")) {
586 /* silently deprecated */
587 } else if (!strcmp(tok[i], "bind"))
588 ac->ac_db[ac->ac_dbcount++] = ASR_DB_DNS;
589 else if (!strcmp(tok[i], "file"))
590 ac->ac_db[ac->ac_dbcount++] = ASR_DB_FILE;
591 }
592 } else if (!strcmp(tok[0], "search")) {
593 /* resolv.conf says the last line wins */
594 for (i = 0; i < ASR_MAXDOM; i++) {
595 free(ac->ac_dom[i]);
596 ac->ac_dom[i] = NULL;
597 }
598 ac->ac_domcount = 0;
599 for (i = 1; i < n; i++)
600 asr_ctx_add_searchdomain(ac, tok[i]);
601
602 } else if (!strcmp(tok[0], "family")) {
603 if (n == 1 || n > 3)
604 return;
605 for (i = 1; i < n; i++)
606 if (strcmp(tok[i], "inet4") && strcmp(tok[i], "inet6"))
607 return;
608 for (i = 1; i < n; i++)
609 ac->ac_family[i - 1] = strcmp(tok[i], "inet4") ? \
610 AF_INET6 : AF_INET;
611 ac->ac_family[i - 1] = -1;
612
613 } else if (!strcmp(tok[0], "options")) {
614 for (i = 1; i < n; i++) {
615 if (!strcmp(tok[i], "tcp"))
616 ac->ac_options |= RES_USEVC;
617 else if (!strcmp(tok[i], "edns0"))
618 ac->ac_options |= RES_USE_EDNS0;
619 else if ((!strncmp(tok[i], "ndots:", 6))) {
620 e = NULL;
621 d = strtonum(tok[i] + 6, 1, 16, &e);
622 if (e == NULL)
623 ac->ac_ndots = d;
624 }
625 }
626 }
627 }
628
629 /*
630 * Setup an async context with the config specified in the string "str".
631 */
632 static int
asr_ctx_from_string(struct asr_ctx * ac,const char * str)633 asr_ctx_from_string(struct asr_ctx *ac, const char *str)
634 {
635 char buf[512], *ch;
636
637 asr_ctx_parse(ac, str);
638
639 if (ac->ac_dbcount == 0) {
640 /* No lookup directive */
641 asr_ctx_parse(ac, DEFAULT_LOOKUP);
642 }
643
644 if (ac->ac_nscount == 0)
645 asr_ctx_parse(ac, "nameserver 127.0.0.1");
646
647 if (ac->ac_domain == NULL)
648 if (gethostname(buf, sizeof buf) == 0) {
649 ch = strchr(buf, '.');
650 if (ch)
651 ac->ac_domain = strdup(ch + 1);
652 else /* Assume root. see resolv.conf(5) */
653 ac->ac_domain = strdup("");
654 }
655
656 /* If no search domain was specified, use the local subdomains */
657 if (ac->ac_domcount == 0)
658 for (ch = ac->ac_domain; ch; ) {
659 asr_ctx_add_searchdomain(ac, ch);
660 ch = strchr(ch, '.');
661 if (ch && asr_ndots(++ch) == 0)
662 break;
663 }
664
665 return (0);
666 }
667
668 /*
669 * Setup the "ac" async context from the file at location "path".
670 */
671 static int
asr_ctx_from_file(struct asr_ctx * ac,const char * path)672 asr_ctx_from_file(struct asr_ctx *ac, const char *path)
673 {
674 FILE *cf;
675 char buf[4096];
676 ssize_t r;
677
678 cf = fopen(path, "re");
679 if (cf == NULL)
680 return (-1);
681
682 r = fread(buf, 1, sizeof buf - 1, cf);
683 if (feof(cf) == 0) {
684 DPRINT("asr: config file too long: \"%s\"\n", path);
685 r = -1;
686 }
687 fclose(cf);
688 if (r == -1)
689 return (-1);
690 buf[r] = '\0';
691
692 return asr_ctx_from_string(ac, buf);
693 }
694
695 /*
696 * Parse lines in the configuration string. For each one, split it into
697 * tokens and pass them to "pass0" for processing.
698 */
699 static int
asr_ctx_parse(struct asr_ctx * ac,const char * str)700 asr_ctx_parse(struct asr_ctx *ac, const char *str)
701 {
702 size_t len;
703 const char *line;
704 char buf[1024];
705 char *tok[10];
706 int ntok;
707
708 line = str;
709 while (*line) {
710 len = strcspn(line, "\n\0");
711 if (len < sizeof buf) {
712 memmove(buf, line, len);
713 buf[len] = '\0';
714 } else
715 buf[0] = '\0';
716 line += len;
717 if (*line == '\n')
718 line++;
719 buf[strcspn(buf, ";#")] = '\0';
720 if ((ntok = strsplit(buf, tok, 10)) == 0)
721 continue;
722
723 pass0(tok, ntok, ac);
724 }
725
726 return (0);
727 }
728
729 /*
730 * Check for environment variables altering the configuration as described
731 * in resolv.conf(5). Although not documented there, this feature is disabled
732 * for setuid/setgid programs.
733 */
734 static void
asr_ctx_envopts(struct asr_ctx * ac)735 asr_ctx_envopts(struct asr_ctx *ac)
736 {
737 char buf[4096], *e;
738 size_t s;
739
740 if (issetugid()) {
741 ac->ac_options |= RES_NOALIASES;
742 return;
743 }
744
745 if ((e = getenv("RES_OPTIONS")) != NULL) {
746 strlcpy(buf, "options ", sizeof buf);
747 strlcat(buf, e, sizeof buf);
748 s = strlcat(buf, "\n", sizeof buf);
749 if (s < sizeof buf)
750 asr_ctx_parse(ac, buf);
751 }
752
753 if ((e = getenv("LOCALDOMAIN")) != NULL) {
754 strlcpy(buf, "search ", sizeof buf);
755 strlcat(buf, e, sizeof buf);
756 s = strlcat(buf, "\n", sizeof buf);
757 if (s < sizeof buf)
758 asr_ctx_parse(ac, buf);
759 }
760 }
761
762 /*
763 * Parse a resolv.conf(5) nameserver string into a sockaddr.
764 */
765 static int
asr_parse_nameserver(struct sockaddr * sa,const char * s)766 asr_parse_nameserver(struct sockaddr *sa, const char *s)
767 {
768 in_port_t portno = 53;
769
770 if (_asr_sockaddr_from_str(sa, PF_UNSPEC, s) == -1)
771 return (-1);
772
773 if (sa->sa_family == PF_INET)
774 ((struct sockaddr_in *)sa)->sin_port = htons(portno);
775 else if (sa->sa_family == PF_INET6)
776 ((struct sockaddr_in6 *)sa)->sin6_port = htons(portno);
777
778 return (0);
779 }
780
781 /*
782 * Turn a (uncompressed) DNS domain name into a regular nul-terminated string
783 * where labels are separated by dots. The result is put into the "buf" buffer,
784 * truncated if it exceeds "max" chars. The function returns "buf".
785 */
786 char *
_asr_strdname(const char * _dname,char * buf,size_t max)787 _asr_strdname(const char *_dname, char *buf, size_t max)
788 {
789 const unsigned char *dname = _dname;
790 char *res;
791 size_t left, n, count;
792
793 if (_dname[0] == 0) {
794 strlcpy(buf, ".", max);
795 return buf;
796 }
797
798 res = buf;
799 left = max - 1;
800 for (n = 0; dname[0] && left; n += dname[0]) {
801 count = (dname[0] < (left - 1)) ? dname[0] : (left - 1);
802 memmove(buf, dname + 1, count);
803 dname += dname[0] + 1;
804 left -= count;
805 buf += count;
806 if (left) {
807 left -= 1;
808 *buf++ = '.';
809 }
810 }
811 buf[0] = 0;
812
813 return (res);
814 }
815
816 /*
817 * Read and split the next line from the given namedb file.
818 * Return -1 on error, or put the result in the "tokens" array of
819 * size "ntoken" and returns the number of token on the line.
820 */
821 int
_asr_parse_namedb_line(FILE * file,char ** tokens,int ntoken,char * lbuf,size_t sz)822 _asr_parse_namedb_line(FILE *file, char **tokens, int ntoken, char *lbuf, size_t sz)
823 {
824 size_t len;
825 char *buf;
826 int ntok;
827
828 again:
829 if ((buf = fgetln(file, &len)) == NULL)
830 return (-1);
831
832 if (len >= sz)
833 goto again;
834
835 if (buf[len - 1] == '\n')
836 len--;
837 else {
838 memcpy(lbuf, buf, len);
839 buf = lbuf;
840 }
841
842 buf[len] = '\0';
843 buf[strcspn(buf, "#")] = '\0';
844 if ((ntok = strsplit(buf, tokens, ntoken)) == 0)
845 goto again;
846
847 return (ntok);
848 }
849
850 /*
851 * Update the async context so that it uses the next configured DB.
852 * Return 0 on success, or -1 if no more DBs is available.
853 */
854 int
_asr_iter_db(struct asr_query * as)855 _asr_iter_db(struct asr_query *as)
856 {
857 if (as->as_db_idx >= as->as_ctx->ac_dbcount) {
858 DPRINT("asr_iter_db: done\n");
859 return (-1);
860 }
861
862 as->as_db_idx += 1;
863 DPRINT("asr_iter_db: %i\n", as->as_db_idx);
864
865 return (0);
866 }
867