sync
[bitrig.git] / lib / libc / asr / asr.c
blob988900f33a3c45f55bcddcc11af3b5c983721471
1 /* $OpenBSD: asr.c,v 1.30 2013/06/01 15:02:01 eric Exp $ */
2 /*
3 * Copyright (c) 2010-2012 Eric Faurot <eric@openbsd.org>
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.
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.
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <arpa/nameser.h>
24 #include <err.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <netdb.h>
28 #include <resolv.h>
29 #include <poll.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
35 #include "asr.h"
36 #include "asr_private.h"
38 #ifndef ASR_OPT_THREADSAFE
39 #define ASR_OPT_THREADSAFE 1
40 #endif
41 #ifndef ASR_OPT_HOSTALIASES
42 #define ASR_OPT_HOSTALIASES 1
43 #endif
44 #ifndef ASR_OPT_ENVOPTS
45 #define ASR_OPT_ENVOPTS 1
46 #endif
47 #ifndef ASR_OPT_RELOADCONF
48 #define ASR_OPT_RELOADCONF 1
49 #endif
50 #ifndef ASR_OPT_ALTCONF
51 #define ASR_OPT_ALTCONF 1
52 #endif
54 #if ASR_OPT_THREADSAFE
55 #include "thread_private.h"
56 #endif
58 #define DEFAULT_CONFFILE "/etc/resolv.conf"
59 #define DEFAULT_HOSTFILE "/etc/hosts"
60 #define DEFAULT_CONF "lookup file\n"
61 #define DEFAULT_LOOKUP "lookup bind file"
63 #define RELOAD_DELAY 15 /* seconds */
65 static void asr_check_reload(struct asr *);
66 static struct asr_ctx *asr_ctx_create(void);
67 static void asr_ctx_ref(struct asr_ctx *);
68 static void asr_ctx_free(struct asr_ctx *);
69 static int asr_ctx_add_searchdomain(struct asr_ctx *, const char *);
70 static int asr_ctx_from_file(struct asr_ctx *, const char *);
71 static int asr_ctx_from_string(struct asr_ctx *, const char *);
72 static int asr_ctx_parse(struct asr_ctx *, const char *);
73 static int asr_parse_nameserver(struct sockaddr *, const char *);
74 static int asr_ndots(const char *);
75 static void pass0(char **, int, struct asr_ctx *);
76 static int strsplit(char *, char **, int);
77 #if ASR_OPT_ENVOPTS
78 static void asr_ctx_envopts(struct asr_ctx *);
79 #endif
80 #if ASR_OPT_THREADSAFE
81 static void *__THREAD_NAME(_asr);
82 #else
83 # define _THREAD_PRIVATE(a, b, c) (c)
84 #endif
86 static struct asr *_asr = NULL;
88 /* Allocate and configure an async "resolver". */
89 struct asr *
90 async_resolver(const char *conf)
92 static int init = 0;
93 struct asr *asr;
95 if (init == 0) {
96 #ifdef DEBUG
97 if (getenv("ASR_DEBUG"))
98 asr_debug = stderr;
99 #endif
100 init = 1;
103 if ((asr = calloc(1, sizeof(*asr))) == NULL)
104 goto fail;
106 #if ASR_OPT_ALTCONF
107 /* If not setuid/setgid, allow to use an alternate config. */
108 if (conf == NULL && !issetugid())
109 conf = getenv("ASR_CONFIG");
110 #endif
112 if (conf == NULL)
113 conf = DEFAULT_CONFFILE;
115 if (conf[0] == '!') {
116 /* Use the rest of the string as config file */
117 if ((asr->a_ctx = asr_ctx_create()) == NULL)
118 goto fail;
119 if (asr_ctx_from_string(asr->a_ctx, conf + 1) == -1)
120 goto fail;
121 } else {
122 /* Use the given config file */
123 asr->a_path = strdup(conf);
124 if (asr->a_path == NULL)
125 goto fail;
126 asr_check_reload(asr);
127 if (asr->a_ctx == NULL) {
128 if ((asr->a_ctx = asr_ctx_create()) == NULL)
129 goto fail;
130 if (asr_ctx_from_string(asr->a_ctx, DEFAULT_CONF) == -1)
131 goto fail;
132 #if ASR_OPT_ENVOPTS
133 asr_ctx_envopts(asr->a_ctx);
134 #endif
138 #ifdef DEBUG
139 asr_dump_config(asr_debug, asr);
140 #endif
141 return (asr);
143 fail:
144 if (asr) {
145 if (asr->a_ctx)
146 asr_ctx_free(asr->a_ctx);
147 free(asr->a_path);
148 free(asr);
151 return (NULL);
155 * Free the "asr" async resolver (or the thread-local resolver if NULL).
156 * Drop the reference to the current context.
158 void
159 async_resolver_done(struct asr *asr)
161 struct asr **priv;
163 if (asr == NULL) {
164 priv = _THREAD_PRIVATE(_asr, _asr, &_asr);
165 if (*priv == NULL)
166 return;
167 asr = *priv;
168 *priv = NULL;
171 asr_ctx_unref(asr->a_ctx);
172 free(asr->a_path);
173 free(asr);
177 * Cancel an async query.
179 void
180 async_abort(struct async *as)
182 async_free(as);
186 * Resume the "as" async query resolution. Return one of ASYNC_COND,
187 * ASYNC_YIELD or ASYNC_DONE and put query-specific return values in
188 * the user-allocated memory at "ar".
191 async_run(struct async *as, struct async_res *ar)
193 int r, saved_errno = errno;
195 DPRINT("asr: async_run(%p, %p) %s ctx=[%p]\n", as, ar,
196 asr_querystr(as->as_type), as->as_ctx);
197 r = as->as_run(as, ar);
199 DPRINT("asr: async_run(%p, %p) -> %s", as, ar, asr_transitionstr(r));
200 #ifdef DEBUG
201 if (r == ASYNC_COND)
202 #endif
203 DPRINT(" fd=%i timeout=%i", ar->ar_fd, ar->ar_timeout);
204 DPRINT("\n");
205 if (r == ASYNC_DONE)
206 async_free(as);
208 errno = saved_errno;
210 return (r);
214 * Same as above, but run in a loop that handles the fd conditions result.
217 async_run_sync(struct async *as, struct async_res *ar)
219 struct pollfd fds[1];
220 int r, saved_errno = errno;
222 while ((r = async_run(as, ar)) == ASYNC_COND) {
223 fds[0].fd = ar->ar_fd;
224 fds[0].events = (ar->ar_cond == ASYNC_READ) ? POLLIN : POLLOUT;
225 again:
226 r = poll(fds, 1, ar->ar_timeout);
227 if (r == -1 && errno == EINTR)
228 goto again;
230 * Otherwise, just ignore the error and let async_run()
231 * catch the failure.
235 errno = saved_errno;
237 return (r);
241 * Create a new async request of the given "type" on the async context "ac".
242 * Take a reference on it so it does not gets deleted while the async query
243 * is running.
245 struct async *
246 async_new(struct asr_ctx *ac, int type)
248 struct async *as;
250 DPRINT("asr: async_new(ctx=%p) type=%i refcount=%i\n", ac, type,
251 ac ? ac->ac_refcount : 0);
252 if (ac == NULL || (as = calloc(1, sizeof(*as))) == NULL)
253 return (NULL);
255 ac->ac_refcount += 1;
256 as->as_ctx = ac;
257 as->as_fd = -1;
258 as->as_type = type;
259 as->as_state = ASR_STATE_INIT;
261 return (as);
265 * Free an async query and unref the associated context.
267 void
268 async_free(struct async *as)
270 DPRINT("asr: async_free(%p)\n", as);
271 switch (as->as_type) {
272 case ASR_SEND:
273 if (as->as_fd != -1)
274 close(as->as_fd);
275 if (as->as.dns.obuf && !(as->as.dns.flags & ASYNC_EXTOBUF))
276 free(as->as.dns.obuf);
277 if (as->as.dns.ibuf)
278 free(as->as.dns.ibuf);
279 if (as->as.dns.dname)
280 free(as->as.dns.dname);
281 break;
283 case ASR_SEARCH:
284 if (as->as.search.subq)
285 async_free(as->as.search.subq);
286 if (as->as.search.name)
287 free(as->as.search.name);
288 break;
290 case ASR_GETRRSETBYNAME:
291 if (as->as.rrset.subq)
292 async_free(as->as.rrset.subq);
293 if (as->as.rrset.name)
294 free(as->as.rrset.name);
295 break;
297 case ASR_GETHOSTBYNAME:
298 case ASR_GETHOSTBYADDR:
299 if (as->as.hostnamadr.subq)
300 async_free(as->as.hostnamadr.subq);
301 if (as->as.hostnamadr.name)
302 free(as->as.hostnamadr.name);
303 break;
305 case ASR_GETNETBYNAME:
306 case ASR_GETNETBYADDR:
307 if (as->as.netnamadr.subq)
308 async_free(as->as.netnamadr.subq);
309 if (as->as.netnamadr.name)
310 free(as->as.netnamadr.name);
311 break;
313 case ASR_GETADDRINFO:
314 if (as->as.ai.subq)
315 async_free(as->as.ai.subq);
316 if (as->as.ai.aifirst)
317 freeaddrinfo(as->as.ai.aifirst);
318 if (as->as.ai.hostname)
319 free(as->as.ai.hostname);
320 if (as->as.ai.servname)
321 free(as->as.ai.servname);
322 if (as->as.ai.fqdn)
323 free(as->as.ai.fqdn);
324 break;
326 case ASR_GETNAMEINFO:
327 if (as->as.ni.subq)
328 async_free(as->as.ni.subq);
329 break;
332 asr_ctx_unref(as->as_ctx);
333 free(as);
337 * Get a context from the given resolver. This takes a new reference to
338 * the returned context, which *must* be explicitely dropped when done
339 * using this context.
341 struct asr_ctx *
342 asr_use_resolver(struct asr *asr)
344 struct asr **priv;
346 if (asr == NULL) {
347 DPRINT("using thread-local resolver\n");
348 priv = _THREAD_PRIVATE(_asr, _asr, &_asr);
349 if (*priv == NULL) {
350 DPRINT("setting up thread-local resolver\n");
351 *priv = async_resolver(NULL);
353 asr = *priv;
355 if (asr != NULL) {
356 asr_check_reload(asr);
357 asr_ctx_ref(asr->a_ctx);
358 return (asr->a_ctx);
360 return (NULL);
363 static void
364 asr_ctx_ref(struct asr_ctx *ac)
366 DPRINT("asr: asr_ctx_ref(ctx=%p) refcount=%i\n", ac, ac->ac_refcount);
367 ac->ac_refcount += 1;
371 * Drop a reference to an async context, freeing it if the reference
372 * count drops to 0.
374 void
375 asr_ctx_unref(struct asr_ctx *ac)
377 DPRINT("asr: asr_ctx_unref(ctx=%p) refcount=%i\n", ac,
378 ac ? ac->ac_refcount : 0);
379 if (ac == NULL)
380 return;
381 if (--ac->ac_refcount)
382 return;
384 asr_ctx_free(ac);
387 static void
388 asr_ctx_free(struct asr_ctx *ac)
390 int i;
392 if (ac->ac_domain)
393 free(ac->ac_domain);
394 for (i = 0; i < ASR_MAXNS; i++)
395 free(ac->ac_ns[i]);
396 for (i = 0; i < ASR_MAXDOM; i++)
397 free(ac->ac_dom[i]);
399 free(ac);
403 * Reload the configuration file if it has changed on disk.
405 static void
406 asr_check_reload(struct asr *asr)
408 struct asr_ctx *ac;
409 #if ASR_OPT_RELOADCONF
410 struct stat st;
411 struct timespec ts;
412 #endif
414 if (asr->a_path == NULL)
415 return;
417 #if ASR_OPT_RELOADCONF
418 if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
419 return;
421 if ((ts.tv_sec - asr->a_rtime) < RELOAD_DELAY && asr->a_rtime != 0)
422 return;
423 asr->a_rtime = ts.tv_sec;
425 DPRINT("asr: checking for update of \"%s\"\n", asr->a_path);
426 if (stat(asr->a_path, &st) == -1 ||
427 asr->a_mtime == st.st_mtime ||
428 (ac = asr_ctx_create()) == NULL)
429 return;
430 asr->a_mtime = st.st_mtime;
431 #else
432 if ((ac = asr_ctx_create()) == NULL)
433 return;
434 #endif
436 DPRINT("asr: reloading config file\n");
437 if (asr_ctx_from_file(ac, asr->a_path) == -1) {
438 asr_ctx_free(ac);
439 return;
442 #if ASR_OPT_ENVOPTS
443 asr_ctx_envopts(ac);
444 #endif
445 if (asr->a_ctx)
446 asr_ctx_unref(asr->a_ctx);
447 asr->a_ctx = ac;
451 * Construct a fully-qualified domain name for the given name and domain.
452 * If "name" ends with a '.' it is considered as a FQDN by itself.
453 * Otherwise, the domain, which must be a FQDN, is appended to "name" (it
454 * may have a leading dot which would be ignored). If the domain is null,
455 * then "." is used. Return the length of the constructed FQDN or (0) on
456 * error.
458 size_t
459 asr_make_fqdn(const char *name, const char *domain, char *buf, size_t buflen)
461 size_t len;
463 if (domain == NULL)
464 domain = ".";
465 else if ((len = strlen(domain)) == 0)
466 return (0);
467 else if (domain[len -1] != '.')
468 return (0);
470 len = strlen(name);
471 if (len == 0) {
472 if (strlcpy(buf, domain, buflen) >= buflen)
473 return (0);
474 } else if (name[len - 1] != '.') {
475 if (domain[0] == '.')
476 domain += 1;
477 if (strlcpy(buf, name, buflen) >= buflen ||
478 strlcat(buf, ".", buflen) >= buflen ||
479 strlcat(buf, domain, buflen) >= buflen)
480 return (0);
481 } else {
482 if (strlcpy(buf, name, buflen) >= buflen)
483 return (0);
486 return (strlen(buf));
490 * Count the dots in a string.
492 static int
493 asr_ndots(const char *s)
495 int n;
497 for (n = 0; *s; s++)
498 if (*s == '.')
499 n += 1;
501 return (n);
505 * Allocate a new empty context.
507 static struct asr_ctx *
508 asr_ctx_create(void)
510 struct asr_ctx *ac;
512 if ((ac = calloc(1, sizeof(*ac))) == NULL)
513 return (NULL);
515 ac->ac_options = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH;
516 ac->ac_refcount = 1;
517 ac->ac_ndots = 1;
518 ac->ac_family[0] = AF_INET;
519 ac->ac_family[1] = AF_INET6;
520 ac->ac_family[2] = -1;
522 ac->ac_hostfile = DEFAULT_HOSTFILE;
524 ac->ac_nscount = 0;
525 ac->ac_nstimeout = 5;
526 ac->ac_nsretries = 4;
528 return (ac);
532 * Add a search domain to the async context.
534 static int
535 asr_ctx_add_searchdomain(struct asr_ctx *ac, const char *domain)
537 char buf[MAXDNAME];
539 if (ac->ac_domcount == ASR_MAXDOM)
540 return (-1);
542 if (asr_make_fqdn(domain, NULL, buf, sizeof(buf)) == 0)
543 return (-1);
545 if ((ac->ac_dom[ac->ac_domcount] = strdup(buf)) == NULL)
546 return (0);
548 ac->ac_domcount += 1;
550 return (1);
553 static int
554 strsplit(char *line, char **tokens, int ntokens)
556 int ntok;
557 char *cp, **tp;
559 for (cp = line, tp = tokens, ntok = 0;
560 ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; )
561 if (**tp != '\0') {
562 tp++;
563 ntok++;
566 return (ntok);
570 * Pass on a split config line.
572 static void
573 pass0(char **tok, int n, struct asr_ctx *ac)
575 int i, j, d;
576 const char *e;
577 struct sockaddr_storage ss;
579 if (!strcmp(tok[0], "nameserver")) {
580 if (ac->ac_nscount == ASR_MAXNS)
581 return;
582 if (n != 2)
583 return;
584 if (asr_parse_nameserver((struct sockaddr *)&ss, tok[1]))
585 return;
586 if ((ac->ac_ns[ac->ac_nscount] = calloc(1, ss.ss_len)) == NULL)
587 return;
588 memmove(ac->ac_ns[ac->ac_nscount], &ss, ss.ss_len);
589 ac->ac_nscount += 1;
591 } else if (!strcmp(tok[0], "domain")) {
592 if (n != 2)
593 return;
594 if (ac->ac_domain)
595 return;
596 ac->ac_domain = strdup(tok[1]);
598 } else if (!strcmp(tok[0], "lookup")) {
599 /* ensure that each lookup is only given once */
600 for (i = 1; i < n; i++)
601 for (j = i + 1; j < n; j++)
602 if (!strcmp(tok[i], tok[j]))
603 return;
604 ac->ac_dbcount = 0;
605 for (i = 1; i < n && ac->ac_dbcount < ASR_MAXDB; i++) {
606 if (!strcmp(tok[i], "yp"))
607 ac->ac_db[ac->ac_dbcount++] = ASR_DB_YP;
608 else if (!strcmp(tok[i], "bind"))
609 ac->ac_db[ac->ac_dbcount++] = ASR_DB_DNS;
610 else if (!strcmp(tok[i], "file"))
611 ac->ac_db[ac->ac_dbcount++] = ASR_DB_FILE;
613 } else if (!strcmp(tok[0], "search")) {
614 /* resolv.conf says the last line wins */
615 for (i = 0; i < ASR_MAXDOM; i++)
616 free(ac->ac_dom[i]);
617 ac->ac_domcount = 0;
618 for (i = 1; i < n; i++)
619 asr_ctx_add_searchdomain(ac, tok[i]);
621 } else if (!strcmp(tok[0], "family")) {
622 if (n == 1 || n > 3)
623 return;
624 for (i = 1; i < n; i++)
625 if (strcmp(tok[i], "inet4") && strcmp(tok[i], "inet6"))
626 return;
627 for (i = 1; i < n; i++)
628 ac->ac_family[i - 1] = strcmp(tok[i], "inet4") ? \
629 AF_INET6 : AF_INET;
630 ac->ac_family[i - 1] = -1;
632 } else if (!strcmp(tok[0], "options")) {
633 for (i = 1; i < n; i++) {
634 if (!strcmp(tok[i], "tcp"))
635 ac->ac_options |= RES_USEVC;
636 else if ((!strncmp(tok[i], "ndots:", 6))) {
637 e = NULL;
638 d = strtonum(tok[i] + 6, 1, 16, &e);
639 if (e == NULL)
640 ac->ac_ndots = d;
647 * Setup an async context with the config specified in the string "str".
649 static int
650 asr_ctx_from_string(struct asr_ctx *ac, const char *str)
652 char buf[512], *ch;
654 asr_ctx_parse(ac, str);
656 if (ac->ac_dbcount == 0) {
657 /* No lookup directive */
658 asr_ctx_parse(ac, DEFAULT_LOOKUP);
661 if (ac->ac_nscount == 0)
662 asr_ctx_parse(ac, "nameserver 127.0.0.1");
664 if (ac->ac_domain == NULL)
665 if (gethostname(buf, sizeof buf) == 0) {
666 ch = strchr(buf, '.');
667 if (ch)
668 ac->ac_domain = strdup(ch + 1);
669 else /* Assume root. see resolv.conf(5) */
670 ac->ac_domain = strdup("");
673 /* If no search domain was specified, use the local subdomains */
674 if (ac->ac_domcount == 0)
675 for (ch = ac->ac_domain; ch; ) {
676 asr_ctx_add_searchdomain(ac, ch);
677 ch = strchr(ch, '.');
678 if (ch && asr_ndots(++ch) == 0)
679 break;
682 return (0);
686 * Setup the "ac" async context from the file at location "path".
688 static int
689 asr_ctx_from_file(struct asr_ctx *ac, const char *path)
691 FILE *cf;
692 char buf[4096];
693 ssize_t r;
695 cf = fopen(path, "r");
696 if (cf == NULL)
697 return (-1);
699 r = fread(buf, 1, sizeof buf - 1, cf);
700 if (feof(cf) == 0) {
701 DPRINT("asr: config file too long: \"%s\"\n", path);
702 r = -1;
704 fclose(cf);
705 if (r == -1)
706 return (-1);
707 buf[r] = '\0';
709 return asr_ctx_from_string(ac, buf);
713 * Parse lines in the configuration string. For each one, split it into
714 * tokens and pass them to "pass0" for processing.
716 static int
717 asr_ctx_parse(struct asr_ctx *ac, const char *str)
719 size_t len;
720 const char *line;
721 char buf[1024];
722 char *tok[10];
723 int ntok;
725 line = str;
726 while (*line) {
727 len = strcspn(line, "\n\0");
728 if (len < sizeof buf) {
729 memmove(buf, line, len);
730 buf[len] = '\0';
731 } else
732 buf[0] = '\0';
733 line += len;
734 if (*line == '\n')
735 line++;
736 buf[strcspn(buf, ";#")] = '\0';
737 if ((ntok = strsplit(buf, tok, 10)) == 0)
738 continue;
740 pass0(tok, ntok, ac);
743 return (0);
746 #if ASR_OPT_ENVOPTS
748 * Check for environment variables altering the configuration as described
749 * in resolv.conf(5). Altough not documented there, this feature is disabled
750 * for setuid/setgid programs.
752 static void
753 asr_ctx_envopts(struct asr_ctx *ac)
755 char buf[4096], *e;
756 size_t s;
758 if (issetugid()) {
759 ac->ac_options |= RES_NOALIASES;
760 return;
763 if ((e = getenv("RES_OPTIONS")) != NULL) {
764 strlcpy(buf, "options ", sizeof buf);
765 strlcat(buf, e, sizeof buf);
766 s = strlcat(buf, "\n", sizeof buf);
767 s = strlcat(buf, "\n", sizeof buf);
768 if (s < sizeof buf)
769 asr_ctx_parse(ac, buf);
772 if ((e = getenv("LOCALDOMAIN")) != NULL) {
773 strlcpy(buf, "search ", sizeof buf);
774 strlcat(buf, e, sizeof buf);
775 s = strlcat(buf, "\n", sizeof buf);
776 if (s < sizeof buf)
777 asr_ctx_parse(ac, buf);
780 #endif
783 * Parse a resolv.conf(5) nameserver string into a sockaddr.
785 static int
786 asr_parse_nameserver(struct sockaddr *sa, const char *s)
788 const char *estr;
789 char buf[256];
790 char *port = NULL;
791 in_port_t portno = 53;
793 if (*s == '[') {
794 strlcpy(buf, s + 1, sizeof buf);
795 s = buf;
796 port = strchr(buf, ']');
797 if (port == NULL)
798 return (-1);
799 *port++ = '\0';
800 if (*port != ':')
801 return (-1);
802 port++;
805 if (port) {
806 portno = strtonum(port, 1, USHRT_MAX, &estr);
807 if (estr)
808 return (-1);
811 if (sockaddr_from_str(sa, PF_UNSPEC, s) == -1)
812 return (-1);
814 if (sa->sa_family == PF_INET)
815 ((struct sockaddr_in *)sa)->sin_port = htons(portno);
816 else if (sa->sa_family == PF_INET6)
817 ((struct sockaddr_in6 *)sa)->sin6_port = htons(portno);
819 return (0);
823 * Turn a (uncompressed) DNS domain name into a regular nul-terminated string
824 * where labels are separated by dots. The result is put into the "buf" buffer,
825 * truncated if it exceeds "max" chars. The function returns "buf".
827 char *
828 asr_strdname(const char *_dname, char *buf, size_t max)
830 const unsigned char *dname = _dname;
831 char *res;
832 size_t left, n, count;
834 if (_dname[0] == 0) {
835 strlcpy(buf, ".", max);
836 return buf;
839 res = buf;
840 left = max - 1;
841 for (n = 0; dname[0] && left; n += dname[0]) {
842 count = (dname[0] < (left - 1)) ? dname[0] : (left - 1);
843 memmove(buf, dname + 1, count);
844 dname += dname[0] + 1;
845 left -= count;
846 buf += count;
847 if (left) {
848 left -= 1;
849 *buf++ = '.';
852 buf[0] = 0;
854 return (res);
858 * Read and split the next line from the given namedb file.
859 * Return -1 on error, or put the result in the "tokens" array of
860 * size "ntoken" and returns the number of token on the line.
863 asr_parse_namedb_line(FILE *file, char **tokens, int ntoken)
865 size_t len;
866 char *buf;
867 int ntok;
869 again:
870 if ((buf = fgetln(file, &len)) == NULL)
871 return (-1);
873 if (buf[len - 1] == '\n')
874 len--;
876 buf[len] = '\0';
877 buf[strcspn(buf, "#")] = '\0';
878 if ((ntok = strsplit(buf, tokens, ntoken)) == 0)
879 goto again;
881 return (ntok);
885 * Update the async context so that it uses the next configured DB.
886 * Return 0 on success, or -1 if no more DBs is available.
889 asr_iter_db(struct async *as)
891 if (as->as_db_idx >= as->as_ctx->ac_dbcount) {
892 DPRINT("asr_iter_db: done\n");
893 return (-1);
896 as->as_db_idx += 1;
897 DPRINT("asr_iter_db: %i\n", as->as_db_idx);
899 return (0);
903 * Check if the hostname "name" is a user-defined alias as per hostname(7).
904 * If so, copies the result in the buffer "abuf" of size "abufsz" and
905 * return "abuf". Otherwise return NULL.
907 char *
908 asr_hostalias(struct asr_ctx *ac, const char *name, char *abuf, size_t abufsz)
910 #if ASR_OPT_HOSTALIASES
911 FILE *fp;
912 size_t len;
913 char *file, *buf, *tokens[2];
914 int ntok;
916 if (ac->ac_options & RES_NOALIASES ||
917 asr_ndots(name) != 0 ||
918 issetugid() ||
919 (file = getenv("HOSTALIASES")) == NULL ||
920 (fp = fopen(file, "r")) == NULL)
921 return (NULL);
923 DPRINT("asr: looking up aliases in \"%s\"\n", file);
925 while ((buf = fgetln(fp, &len)) != NULL) {
926 if (buf[len - 1] == '\n')
927 len--;
928 buf[len] = '\0';
929 if ((ntok = strsplit(buf, tokens, 2)) != 2)
930 continue;
931 if (!strcasecmp(tokens[0], name)) {
932 if (strlcpy(abuf, tokens[1], abufsz) > abufsz)
933 continue;
934 DPRINT("asr: found alias \"%s\"\n", abuf);
935 fclose(fp);
936 return (abuf);
940 fclose(fp);
941 #endif
942 return (NULL);