summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorEric Faurot <eric@cvs.openbsd.org>2012-04-14 09:24:19 +0000
committerEric Faurot <eric@cvs.openbsd.org>2012-04-14 09:24:19 +0000
commit852a7ed231a6296007177e8f5df9ec3978d4dab8 (patch)
treeda2dada44712de6b50d559bd5cf9b3869eba3746 /lib
parentb24bb61092bfc78fc1d60744dd936d4f10e3de57 (diff)
Import asr, an experimental async resolver implementation.
The idea is to eventually replace the existing resolver with something better. Time to start working on it in tree. ok deraadt@
Diffstat (limited to 'lib')
-rw-r--r--lib/libc/asr/Makefile.inc34
-rw-r--r--lib/libc/asr/asr.c1102
-rw-r--r--lib/libc/asr/asr.h106
-rw-r--r--lib/libc/asr/asr_debug.c569
-rw-r--r--lib/libc/asr/asr_private.h372
-rw-r--r--lib/libc/asr/asr_resolver.c457
-rw-r--r--lib/libc/asr/asr_utils.c450
-rw-r--r--lib/libc/asr/async_resolver.3358
-rw-r--r--lib/libc/asr/getaddrinfo_async.c500
-rw-r--r--lib/libc/asr/gethostnamadr_async.c592
-rw-r--r--lib/libc/asr/getnameinfo_async.c261
-rw-r--r--lib/libc/asr/getnetnamadr_async.c418
-rw-r--r--lib/libc/asr/getrrsetbyname_async.c590
-rw-r--r--lib/libc/asr/hostaddr_async.c342
-rw-r--r--lib/libc/asr/res_search_async.c224
-rw-r--r--lib/libc/asr/res_send_async.c809
16 files changed, 7184 insertions, 0 deletions
diff --git a/lib/libc/asr/Makefile.inc b/lib/libc/asr/Makefile.inc
new file mode 100644
index 00000000000..f875dcf91b2
--- /dev/null
+++ b/lib/libc/asr/Makefile.inc
@@ -0,0 +1,34 @@
+# $OpenBSD: Makefile.inc,v 1.1 2012/04/14 09:24:18 eric Exp $
+
+# asr sources
+.PATH: ${LIBCSRCDIR}/asr
+
+CFLAGS+=-DDEBUG
+SRCS+= asr.c asr_debug.c asr_utils.c \
+ res_send_async.c res_search_async.c getrrsetbyname_async.c \
+ gethostnamadr_async.c getnetnamadr_async.c \
+ hostaddr_async.c getaddrinfo_async.c getnameinfo_async.c
+
+# resolver replacement. comment out to disable.
+SRCS+= asr_resolver.c
+
+MLINKS+ = \
+ async_resolver.3 async_resolver_done.3 \
+ async_resolver.3 async_run.3 \
+ async_resolver.3 async_run_sync.3 \
+ async_resolver.3 async_abort.3 \
+ async_resolver.3 res_send_async.3 \
+ async_resolver.3 res_query_async.3 \
+ async_resolver.3 res_search_async.3 \
+ async_resolver.3 getrrsetbyname_async.3 \
+ async_resolver.3 gethostbyname_async.3 \
+ async_resolver.3 gethostbyname2_async.3 \
+ async_resolver.3 gethostbyaddr_async.3 \
+ async_resolver.3 freehostent.3 \
+ async_resolver.3 getnetbyname_async.3 \
+ async_resolver.3 getnetbyaddr_async.3 \
+ async_resolver.3 freenetent.3 \
+ async_resolver.3 getaddrinfo_async.3 \
+ async_resolver.3 getnameinfo_async.3
+
+.include <bsd.lib.mk>
diff --git a/lib/libc/asr/asr.c b/lib/libc/asr/asr.c
new file mode 100644
index 00000000000..0980a739ba8
--- /dev/null
+++ b/lib/libc/asr/asr.c
@@ -0,0 +1,1102 @@
+/* $OpenBSD: asr.c,v 1.1 2012/04/14 09:24:18 eric Exp $ */
+/*
+ * Copyright (c) 2010-2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asr.h"
+#include "asr_private.h"
+
+#define DEFAULT_CONFFILE "/etc/resolv.conf"
+#define DEFAULT_HOSTFILE "/etc/hosts"
+#define DEFAULT_CONF "lookup bind file\nnameserver 127.0.0.1\n"
+#define DEFAULT_LOOKUP "lookup bind file"
+
+#define RELOAD_DELAY 15 /* seconds */
+
+static void asr_check_reload(struct asr *);
+static struct asr_ctx *asr_ctx_create(void);
+static void asr_ctx_ref(struct asr_ctx *);
+static void asr_ctx_free(struct asr_ctx *);
+static int asr_ctx_add_searchdomain(struct asr_ctx *, const char *);
+static int asr_ctx_from_file(struct asr_ctx *, const char *);
+static int asr_ctx_from_string(struct asr_ctx *, const char *);
+static int asr_ctx_parse(const char*, int(*)(char**, int, struct asr_ctx*),
+ struct asr_ctx *);
+static int asr_parse_nameserver(struct sockaddr *, const char *);
+static char *asr_hostalias(const char *, char *, size_t);
+static int asr_ndots(const char *);
+static void asr_ctx_envopts(struct asr_ctx *);
+static int pass0(char **, int, struct asr_ctx *);
+
+static struct asr * _default_resolver = NULL;
+
+/* Allocate and configure an async "resolver". */
+struct asr *
+async_resolver(const char *conf)
+{
+ static int init = 0;
+ struct asr *asr;
+
+#ifdef DEBUG
+ if (init == 0) {
+ if (getenv("ASR_DEBUG"))
+ asr_debug = 1;
+ init = 1;
+ }
+#endif
+ if ((asr = calloc(1, sizeof(*asr))) == NULL)
+ goto fail;
+
+ /* If not setuid/setgid, allow to use an alternate config. */
+ if (conf == NULL && !issetugid())
+ conf = getenv("ASR_CONFIG");
+
+ if (conf == NULL)
+ conf = DEFAULT_CONFFILE;
+
+ if (conf[0] == '!') {
+ /* Use the rest of the string as config file */
+ if ((asr->a_ctx = asr_ctx_create()) == NULL)
+ goto fail;
+ if (asr_ctx_from_string(asr->a_ctx, conf + 1) == -1)
+ goto fail;
+ } else {
+ /* Use the given config file */
+ asr->a_path = strdup(conf);
+ asr_check_reload(asr);
+ if (asr->a_ctx == NULL) {
+ if ((asr->a_ctx = asr_ctx_create()) == NULL)
+ goto fail;
+ if (asr_ctx_from_string(asr->a_ctx, DEFAULT_CONF) == -1)
+ goto fail;
+ asr_ctx_envopts(asr->a_ctx);
+ }
+ }
+
+#ifdef DEBUG
+ asr_dump(asr);
+#endif
+ return (asr);
+
+ fail:
+ if (asr) {
+ if (asr->a_ctx)
+ asr_ctx_free(asr->a_ctx);
+ free(asr);
+ }
+
+ return (NULL);
+}
+
+/*
+ * Free the "asr" async resolver (or the thread-local resolver if NULL).
+ * Drop the reference to the current context.
+ */
+void
+async_resolver_done(struct asr *asr)
+{
+ if (asr == NULL) {
+ if (_default_resolver == NULL)
+ return;
+ asr = _default_resolver;
+ _default_resolver = NULL;
+ }
+
+ asr_ctx_unref(asr->a_ctx);
+ if (asr->a_path)
+ free(asr->a_path);
+ free(asr);
+}
+
+/*
+ * Cancel an async query.
+ */
+void
+async_abort(struct async *as)
+{
+ async_free(as);
+}
+
+/*
+ * Resume the "as" async query resolution. Return one of ASYNC_COND,
+ * ASYNC_YIELD or ASYNC_DONE and put query-specific return values in
+ * the user-allocated memory at "ar".
+ */
+int
+async_run(struct async *as, struct async_res *ar)
+{
+ int r;
+
+#ifdef DEBUG
+ asr_printf("asr: async_run(%p, %p) %s ctx=[%p]\n",
+ as, ar, asr_querystr(as->as_type), as->as_ctx);
+#endif
+ r = as->as_run(as, ar);
+
+#ifdef DEBUG
+ if (asr_debug) {
+ asr_printf("asr: async_run(%p, %p) -> %s", as, ar,
+ asr_transitionstr(r));
+ if (r == ASYNC_COND)
+ asr_printf(" fd=%i timeout=%i\n",
+ ar->ar_fd, ar->ar_timeout);
+ else
+ asr_printf("\n");
+ fflush(stderr);
+ }
+#endif
+ if (r == ASYNC_DONE)
+ async_free(as);
+
+ return (r);
+}
+
+/*
+ * Same as above, but run in a loop that handles the fd conditions result.
+ */
+int
+async_run_sync(struct async *as, struct async_res *ar)
+{
+ struct pollfd fds[1];
+ int r;
+
+ while((r = async_run(as, ar)) == ASYNC_COND) {
+ fds[0].fd = ar->ar_fd;
+ fds[0].events = (ar->ar_cond == ASYNC_READ) ? POLLIN : POLLOUT;
+ again:
+ r = poll(fds, 1, ar->ar_timeout);
+ if (r == -1 && errno == EINTR)
+ goto again;
+ if (r == -1) /* XXX Is it possible? and what to do if so? */
+ err(1, "poll");
+ }
+
+ return (r);
+}
+
+/*
+ * Create a new async request of the given "type" on the async context "ac".
+ * Take a reference on it so it does not gets deleted while the async query
+ * is running.
+ */
+struct async *
+async_new(struct asr_ctx *ac, int type)
+{
+ struct async *as;
+#ifdef DEBUG
+ asr_printf("asr: async_new(ctx=%p) type=%i refcount=%i\n",
+ ac, type, ac->ac_refcount);
+#endif
+ if ((as = calloc(1, sizeof(*as))) == NULL)
+ return (NULL);
+
+ ac->ac_refcount += 1;
+ as->as_ctx = ac;
+ as->as_fd = -1;
+ as->as_type = type;
+ as->as_state = ASR_STATE_INIT;
+
+ return (as);
+}
+
+/*
+ * Free an async query and unref the associated context.
+ */
+void
+async_free(struct async *as)
+{
+#ifdef DEBUG
+ asr_printf("asr: async_free(%p)\n", as);
+#endif
+ switch(as->as_type) {
+ case ASR_SEND:
+ if (as->as_fd != -1)
+ close(as->as_fd);
+ if (as->as.dns.obuf && !(as->as.dns.flags & ASYNC_EXTOBUF))
+ free (as->as.dns.obuf);
+ if (as->as.dns.ibuf && !(as->as.dns.flags & ASYNC_EXTIBUF))
+ free (as->as.dns.ibuf);
+ if (as->as.dns.dname)
+ free(as->as.dns.dname);
+ break;
+
+ case ASR_SEARCH:
+ if (as->as.search.subq)
+ async_free(as->as.search.subq);
+ if (as->as.search.name)
+ free(as->as.search.name);
+ break;
+
+ case ASR_GETRRSETBYNAME:
+ if (as->as.rrset.subq)
+ async_free(as->as.rrset.subq);
+ if (as->as.rrset.name)
+ free(as->as.rrset.name);
+ break;
+
+ case ASR_GETHOSTBYNAME:
+ case ASR_GETHOSTBYADDR:
+ if (as->as.hostnamadr.subq)
+ async_free(as->as.hostnamadr.subq);
+ if (as->as.hostnamadr.name)
+ free(as->as.hostnamadr.name);
+ if (as->as.hostnamadr.dname)
+ free(as->as.hostnamadr.dname);
+ break;
+
+ case ASR_GETNETBYNAME:
+ case ASR_GETNETBYADDR:
+ if (as->as.netnamadr.subq)
+ async_free(as->as.netnamadr.subq);
+ if (as->as.netnamadr.name)
+ free(as->as.netnamadr.name);
+ break;
+
+ case ASR_GETADDRINFO:
+ if (as->as.ai.subq)
+ async_free(as->as.ai.subq);
+ if (as->as.ai.aifirst)
+ freeaddrinfo(as->as.ai.aifirst);
+ if (as->as.ai.hostname)
+ free(as->as.ai.hostname);
+ if (as->as.ai.servname)
+ free(as->as.ai.servname);
+ break;
+
+ case ASR_GETNAMEINFO:
+ if (as->as.ni.subq)
+ async_free(as->as.ni.subq);
+ break;
+
+ case ASR_HOSTADDR:
+ if (as->as.host.name)
+ free(as->as.host.name);
+ if (as->as.host.subq)
+ async_free(as->as.host.subq);
+ if (as->as.host.pkt)
+ free(as->as.host.pkt);
+ if (as->as.host.file)
+ fclose(as->as.host.file);
+ break;
+ }
+
+ asr_ctx_unref(as->as_ctx);
+ free(as);
+}
+
+/*
+ * Get a context from the given resolver. This takes a new reference to
+ * the returned context, which *must* be explicitely dropped when done
+ * using this context.
+ */
+struct asr_ctx *
+asr_use_resolver(struct asr *asr)
+{
+ if (asr == NULL) {
+ /* We want the use the global resolver. */
+
+ /* _THREAD_PRIVATE_MUTEX_LOCK(_asr_mutex); */
+ if (_default_resolver != NULL)
+ asr_check_reload(asr);
+ else
+ _default_resolver = async_resolver(NULL);
+ asr = _default_resolver;
+ /* _THREAD_PRIVATE_MUTEX_UNLOCK(_asr_mutex); */
+ }
+
+ asr_check_reload(asr);
+ asr_ctx_ref(asr->a_ctx);
+ return (asr->a_ctx);
+}
+
+static void
+asr_ctx_ref(struct asr_ctx *ac)
+{
+#ifdef DEBUG
+ asr_printf("asr: asr_ctx_ref(ctx=%p) refcount=%i\n",
+ ac, ac->ac_refcount);
+#endif
+ ac->ac_refcount += 1;
+}
+
+/*
+ * Drop a reference to an async context, freeing it if the reference
+ * count drops to 0.
+ */
+void
+asr_ctx_unref(struct asr_ctx *ac)
+{
+#ifdef DEBUG
+ asr_printf("asr: asr_ctx_unref(ctx=%p) refcount=%i\n",
+ ac, ac->ac_refcount);
+#endif
+ if (--ac->ac_refcount)
+ return;
+
+ asr_ctx_free(ac);
+}
+
+static void
+asr_ctx_free(struct asr_ctx *ac)
+{
+ int i;
+
+ if (ac->ac_domain)
+ free(ac->ac_domain);
+ for(i = 0; i < ac->ac_nscount; i++)
+ free(ac->ac_ns[i]);
+ for(i = 0; i < ac->ac_domcount; i++)
+ free(ac->ac_dom[i]);
+
+ free(ac);
+}
+
+/*
+ * Reload the configuration file if it has changed on disk.
+ */
+static void
+asr_check_reload(struct asr *asr)
+{
+ struct stat st;
+ struct asr_ctx *ac;
+ struct timespec tp;
+
+ if (asr->a_path == NULL)
+ return;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &tp) == -1)
+ return;
+
+ if ((tp.tv_sec - asr->a_rtime) < RELOAD_DELAY)
+ return;
+ asr->a_rtime = tp.tv_sec;
+
+#ifdef DEBUG
+ asr_printf("asr: checking for update of \"%s\"\n", asr->a_path);
+#endif
+
+ if (stat(asr->a_path, &st) == -1 ||
+ asr->a_mtime == st.st_mtime ||
+ (ac = asr_ctx_create()) == NULL)
+ return;
+ asr->a_mtime = st.st_mtime;
+
+#ifdef DEBUG
+ asr_printf("asr: reloading config file\n");
+#endif
+
+ if (asr_ctx_from_file(ac, asr->a_path) == -1) {
+ asr_ctx_free(ac);
+ return;
+ }
+
+ asr_ctx_envopts(ac);
+ if (asr->a_ctx)
+ asr_ctx_unref(asr->a_ctx);
+ asr->a_ctx = ac;
+}
+
+/*
+ * Construct a fully-qualified domain name for the given name and domain.
+ * If "name" ends with a '.' it is considered as a FQDN by itself.
+ * Otherwise, the domain, which must be a FQDN, is appended to "name" (it
+ * may have a leading dot which would be ignored). If the domain is null,
+ * then "." is used. Return the length of the constructed FQDN or (0) on
+ * error.
+ */
+size_t
+asr_make_fqdn(const char *name, const char *domain, char *buf, size_t buflen)
+{
+ size_t len;
+
+ if (domain == NULL)
+ domain = ".";
+ else if ((len = strlen(domain)) == 0)
+ return (0);
+ else if (domain[len -1] != '.')
+ return (0);
+
+ len = strlen(name);
+ if (len == 0) {
+ strlcpy(buf, domain, buflen);
+ } else if (name[len - 1] != '.') {
+ if (domain[0] == '.')
+ domain += 1;
+ strlcpy(buf, name, buflen);
+ strlcat(buf, ".", buflen);
+ strlcat(buf, domain, buflen);
+ } else {
+ strlcpy(buf, name, buflen);
+ }
+
+ return (strlen(buf));
+}
+
+/*
+ * Concatenate a name and a domain name. The result has no trailing dot.
+ */
+size_t
+asr_domcat(const char *name, const char *domain, char *buf, size_t buflen)
+{
+ size_t r;
+
+ r = asr_make_fqdn(name, domain, buf, buflen);
+ if (r == 0)
+ return (0);
+ buf[r - 1] = '\0';
+
+ return (r - 1);
+}
+
+/*
+ * Count the dots in a string.
+ */
+static int
+asr_ndots(const char *s)
+{
+ int n;
+
+ for(n = 0; *s; s++)
+ if (*s == '.')
+ n += 1;
+
+ return (n);
+}
+
+/*
+ * Allocate a new empty context.
+ */
+static struct asr_ctx *
+asr_ctx_create(void)
+{
+ struct asr_ctx *ac;
+
+ if ((ac = calloc(1, sizeof(*ac))) == NULL)
+ return (NULL);
+
+ ac->ac_options = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH;
+ ac->ac_refcount = 1;
+ ac->ac_ndots = 1;
+ ac->ac_family[0] = AF_INET;
+ ac->ac_family[1] = AF_INET6;
+ ac->ac_family[2] = -1;
+
+ ac->ac_hostfile = DEFAULT_HOSTFILE;
+
+ ac->ac_nscount = 0;
+ ac->ac_nstimeout = 1000;
+ ac->ac_nsretries = 3;
+
+ return (ac);
+}
+
+/*
+ * Add a search domain to the async context.
+ */
+static int
+asr_ctx_add_searchdomain(struct asr_ctx *ac, const char *domain)
+{
+ char buf[MAXDNAME];
+
+ if (ac->ac_domcount == ASR_MAXDOM)
+ return (-1);
+
+ if (asr_make_fqdn(domain, NULL, buf, sizeof(buf)) == 0)
+ return (-1);
+
+ if ((ac->ac_dom[ac->ac_domcount] = strdup(buf)) == NULL)
+ return (0);
+
+ ac->ac_domcount += 1;
+
+ return (1);
+}
+
+/*
+ * Pass on a split config line.
+ */
+static int
+pass0(char **tok, int n, struct asr_ctx *ac)
+{
+ int i, j, d;
+ const char *e;
+ struct sockaddr_storage ss;
+
+ if (!strcmp(tok[0], "nameserver")) {
+ if (ac->ac_nscount == ASR_MAXNS)
+ return (0);
+ if (n != 2)
+ return (0);
+ if (asr_parse_nameserver((struct sockaddr*)&ss, tok[1]))
+ return (0);
+ if ((ac->ac_ns[ac->ac_nscount] = calloc(1, ss.ss_len)) == NULL)
+ return (0);
+ memmove(ac->ac_ns[ac->ac_nscount], &ss, ss.ss_len);
+ ac->ac_nscount += 1;
+
+ } else if (!strcmp(tok[0], "domain")) {
+ if (n != 2)
+ return (0);
+ if (ac->ac_domain)
+ return (0);
+ ac->ac_domain = strdup(tok[1]);
+
+ } else if (!strcmp(tok[0], "lookup")) {
+ /* ignore the line if we already set lookup */
+ if (ac->ac_dbcount != 0)
+ return (0);
+ if (n - 1 > ASR_MAXDB)
+ return (0);
+ /* ensure that each lookup is only given once */
+ for(i = 1; i < n; i++)
+ for(j = i + 1; j < n; j++)
+ if (!strcmp(tok[i], tok[j]))
+ return (0);
+ for(i = 1; i < n; i++, ac->ac_dbcount++) {
+ if (!strcmp(tok[i], "yp")) {
+ ac->ac_db[i-1] = ASR_DB_YP;
+ } else if (!strcmp(tok[i], "bind")) {
+ ac->ac_db[i-1] = ASR_DB_DNS;
+ } else if (!strcmp(tok[i], "file")) {
+ ac->ac_db[i-1] = ASR_DB_FILE;
+ } else {
+ /* ignore the line */
+ ac->ac_dbcount = 0;
+ return (0);
+ }
+ }
+ } else if (!strcmp(tok[0], "search")) {
+ /* resolv.conf says the last line wins */
+ for(i = 0; i < ac->ac_domcount; i++)
+ free(ac->ac_dom[i]);
+ ac->ac_domcount = 0;
+ for(i = 1; i < n; i++)
+ asr_ctx_add_searchdomain(ac, tok[i]);
+
+ } else if (!strcmp(tok[0], "family")) {
+ if (n == 1 || n > 3)
+ return (0);
+ for (i = 1; i < n; i++)
+ if (strcmp(tok[i], "inet4") && strcmp(tok[i], "inet6"))
+ return (0);
+ for (i = 1; i < n; i++)
+ ac->ac_family[i - 1] = strcmp(tok[i], "inet4") ? \
+ AF_INET6 : AF_INET;
+ ac->ac_family[i - 1] = -1;
+
+ } else if (!strcmp(tok[0], "options")) {
+ for(i = 1; i < n; i++) {
+ if (!strcmp(tok[i], "tcp"))
+ ac->ac_options |= RES_USEVC;
+ else if ((!strncmp(tok[i], "ndots:", 6))) {
+ e = NULL;
+ d = strtonum(tok[i] + 6, 1, 16, &e);
+ if (e == NULL)
+ ac->ac_ndots = d;
+ }
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Setup an async context with the config specified in the string "str".
+ */
+static int
+asr_ctx_from_string(struct asr_ctx *ac, const char *str)
+{
+ char buf[512], *ch;
+
+ asr_ctx_parse(str, pass0, ac);
+
+ if (ac->ac_dbcount == 0) {
+ /* No lookup directive */
+ asr_ctx_parse(DEFAULT_LOOKUP, pass0, ac);
+ }
+
+ if (ac->ac_nscount == 0)
+ asr_ctx_parse("nameserver 127.0.0.1", pass0, ac);
+
+ if (ac->ac_domain == NULL)
+ if (gethostname(buf, sizeof buf) == 0) {
+ ch = strchr(buf, '.');
+ if (ch)
+ ac->ac_domain = strdup(ch + 1);
+ else /* Assume root. see resolv.conf(5) */
+ ac->ac_domain = strdup("");
+ }
+
+ /* If no search domain was specified, use the local subdomains */
+ if (ac->ac_domcount == 0)
+ for(ch = ac->ac_domain; ch; ) {
+ asr_ctx_add_searchdomain(ac, ch);
+ ch = strchr(ch, '.');
+ if (ch && asr_ndots(++ch) == 0)
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * Setup the "ac" async context from the file at location "path".
+ */
+static int
+asr_ctx_from_file(struct asr_ctx *ac, const char *path)
+{
+ FILE *cf;
+ char buf[4096];
+ ssize_t r;
+
+ cf = fopen(path, "r");
+ if (cf == NULL)
+ return (-1);
+
+ r = fread(buf, 1, sizeof buf - 1, cf);
+ if (feof(cf) == 0) {
+#ifdef DEBUG
+ asr_printf("asr: config file too long: \"%s\"\n", path);
+#endif
+ r = -1;
+ }
+ fclose(cf);
+ if (r == -1)
+ return (-1);
+ buf[r] = '\0';
+
+ return asr_ctx_from_string(ac, buf);
+}
+
+/*
+ * Parse a configuration string. Lines are read one by one, comments are
+ * stripped and the remaining line is split into tokens which are passed
+ * to the "cb" callback function. Parsing stops if the callback returns
+ * non-zero.
+ */
+static int
+asr_ctx_parse(const char *str, int (*cb)(char**, int, struct asr_ctx*),
+ struct asr_ctx *ac)
+{
+ size_t len;
+ const char *line;
+ char buf[1024];
+ char *tok[10], **tp, *cp;
+ int ntok;
+
+ line = str;
+ while (*line) {
+ len = strcspn(line, "\n\0");
+ if (len < sizeof buf) {
+ memmove(buf, line, len);
+ buf[len] = '\0';
+ } else
+ buf[0] = '\0';
+ line += len;
+ if (*line == '\n')
+ line++;
+ buf[strcspn(buf, ";#")] = '\0';
+ for(cp = buf, tp = tok, ntok = 0;
+ tp < &tok[10] && (*tp = strsep(&cp, " \t")) != NULL; )
+ if (**tp != '\0') {
+ tp++;
+ ntok++;
+ }
+ *tp = NULL;
+
+ if (tok[0] == NULL)
+ continue;
+
+ if (cb(tok, ntok, ac))
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * Check for environment variables altering the configuration as described
+ * in resolv.conf(5). Altough not documented there, this feature is disabled
+ * for setuid/setgid programs.
+ */
+static void
+asr_ctx_envopts(struct asr_ctx *ac)
+{
+ char buf[4096], *e;
+ size_t s;
+
+ if (issetugid()) {
+ ac->ac_options |= RES_NOALIASES;
+ return;
+ }
+
+ if ((e = getenv("RES_OPTIONS")) != NULL) {
+ strlcpy(buf, "options ", sizeof buf);
+ strlcat(buf, e, sizeof buf);
+ s = strlcat(buf, "\n", sizeof buf);
+ s = strlcat(buf, "\n", sizeof buf);
+ if (s < sizeof buf)
+ asr_ctx_parse(buf, pass0, ac);
+ }
+
+ if ((e = getenv("LOCALDOMAIN")) != NULL) {
+ strlcpy(buf, "search ", sizeof buf);
+ strlcat(buf, e, sizeof buf);
+ s = strlcat(buf, "\n", sizeof buf);
+ if (s < sizeof buf)
+ asr_ctx_parse(buf, pass0, ac);
+ }
+}
+
+/*
+ * Parse a resolv.conf(5) nameserver string into a sockaddr.
+ */
+static int
+asr_parse_nameserver(struct sockaddr *sa, const char *s)
+{
+ const char *estr;
+ char buf[256];
+ char *port = NULL;
+ in_port_t portno = 53;
+
+ if (*s == '[') {
+ strlcpy(buf, s + 1, sizeof buf);
+ s = buf;
+ port = strchr(buf, ']');
+ if (port == NULL)
+ return (-1);
+ *port++ = '\0';
+ if (*port != ':')
+ return (-1);
+ port++;
+ }
+
+ if (port) {
+ portno = strtonum(port, 1, USHRT_MAX, &estr);
+ if (estr)
+ return (-1);
+ }
+
+ if (sockaddr_from_str(sa, PF_UNSPEC, s) == -1)
+ return (-1);
+
+ if (sa->sa_family == PF_INET)
+ ((struct sockaddr_in *)sa)->sin_port = htons(portno);
+ else if (sa->sa_family == PF_INET6)
+ ((struct sockaddr_in6 *)sa)->sin6_port = htons(portno);
+
+ return (0);
+}
+
+/*
+ * Turn a (uncompressed) DNS domain name into a regular nul-terminated string
+ * where labels are separated by dots. The result is put into the "buf" buffer,
+ * truncated if it exceeds "max" chars. The function returns "buf".
+ */
+char*
+asr_strdname(const char *_dname, char *buf, size_t max)
+{
+ const unsigned char *dname = _dname;
+ char *res;
+ size_t left, n, count;
+
+ if (_dname[0] == 0) {
+ strlcpy(buf, ".", max);
+ return buf;
+ }
+
+ res = buf;
+ left = max - 1;
+ for (n = 0; dname[0] && left; n += dname[0]) {
+ count = (dname[0] < (left - 1)) ? dname[0] : (left - 1);
+ memmove(buf, dname + 1, count);
+ dname += dname[0] + 1;
+ left -= count;
+ buf += count;
+ if (left) {
+ left -= 1;
+ *buf++ = '.';
+ }
+ }
+ buf[0] = 0;
+
+ return (res);
+}
+
+/*
+ * Read and split the next line from the given namedb file.
+ * Return -1 on error, or put the result in the "tokens" array of
+ * size "ntoken" and returns the number of token on the line.
+ */
+int
+asr_parse_namedb_line(FILE *file, char **tokens, int ntoken)
+{
+ size_t len;
+ char *buf, *cp, **tp;
+ int ntok;
+
+ again:
+ if ((buf = fgetln(file, &len)) == NULL)
+ return (-1);
+
+ if (buf[len - 1] == '\n')
+ len--;
+
+ buf[len] = '\0';
+ buf[strcspn(buf, "#")] = '\0';
+ for(cp = buf, tp = tokens, ntok = 0;
+ ntok < ntoken && (*tp = strsep(&cp, " \t")) != NULL;)
+ if (**tp != '\0') {
+ tp++;
+ ntok++;
+ }
+ *tp = NULL;
+ if (tokens[0] == NULL)
+ goto again;
+
+ return (ntok);
+}
+
+/*
+ * Update the async context so that it uses the next configured DB.
+ * Return 0 on success, or -1 if no more DBs is available.
+ */
+int
+asr_iter_db(struct async *as)
+{
+ if (as->as_db_idx >= as->as_ctx->ac_dbcount) {
+#if DEBUG
+ asr_printf("asr_iter_db: done\n");
+#endif
+ return (-1);
+ }
+
+ as->as_db_idx += 1;
+ as->as_ns_idx = 0;
+#if DEBUG
+ asr_printf("asr_iter_db: %i\n", as->as_db_idx);
+#endif
+ return (0);
+}
+
+/*
+ * Set the async context nameserver index to the next nameserver of the
+ * currently used DB (assuming it is DNS), cycling over the list until the
+ * maximum retry counter is reached. Return 0 on success, or -1 if all
+ * nameservers were used.
+ */
+int
+asr_iter_ns(struct async *as)
+{
+ for (;;) {
+ if (as->as_ns_cycles >= as->as_ctx->ac_nsretries)
+ return (-1);
+
+ as->as_ns_idx += 1;
+ if (as->as_ns_idx <= as->as_ctx->ac_nscount)
+ break;
+ as->as_ns_idx = 0;
+ as->as_ns_cycles++;
+#if DEBUG
+ asr_printf("asr: asr_iter_ns(): cycle %i\n", as->as_ns_cycles);
+#endif
+ }
+
+ return (0);
+}
+
+enum {
+ DOM_INIT,
+ DOM_DOMAIN,
+ DOM_DONE
+};
+
+/*
+ * Implement the search domain strategy.
+ *
+ * This function works as a generator that constructs complete domains in
+ * buffer "buf" of size "len" for the given host name "name", according to the
+ * search rules defined by the resolving context. It is supposed to be called
+ * multiple times (with the same name) to generate the next possible domain
+ * name, if any.
+ *
+ * It returns 0 if it could generate a new domain name, or -1 when all
+ * possibilites have been exhausted.
+ */
+int
+asr_iter_domain(struct async *as, const char *name, char * buf, size_t len)
+{
+ char *alias;
+
+ switch(as->as_dom_step) {
+
+ case DOM_INIT:
+ /* First call */
+
+ /*
+ * If "name" is an FQDN, that's the only result and we
+ * don't try anything else.
+ */
+ if (strlen(name) && name[strlen(name) - 1] == '.') {
+#if DEBUG
+ asr_printf("asr: asr_iter_domain(\"%s\") fqdn\n", name);
+#endif
+ as->as_dom_flags |= ASYNC_DOM_FQDN;
+ as->as_dom_step = DOM_DONE;
+ return (asr_domcat(name, NULL, buf, len));
+ }
+
+ /*
+ * If "name" has no dots, it might be an alias. If so,
+ * That's also the only result.
+ */
+ if ((as->as_ctx->ac_options & RES_NOALIASES) == 0 &&
+ asr_ndots(name) == 0 &&
+ (alias = asr_hostalias(name, buf, len)) != NULL) {
+#if DEBUG
+ asr_printf("asr: asr_iter_domain(\"%s\") is alias "
+ "\"%s\"\n", name, alias);
+#endif
+ as->as_dom_flags |= ASYNC_DOM_HOSTALIAS;
+ as->as_dom_step = DOM_DONE;
+ return (asr_domcat(alias, NULL, buf, len));
+ }
+
+ /*
+ * Otherwise, we iterate through the specified search domains.
+ */
+ as->as_dom_step = DOM_DOMAIN;
+ as->as_dom_idx = 0;
+
+ /*
+ * If "name" as enough dots, use it as-is first, as indicated
+ * in resolv.conf(5).
+ */
+ if ((asr_ndots(name)) >= as->as_ctx->ac_ndots) {
+#ifdef DEBUG
+ asr_printf("asr: asr_iter_domain(\"%s\") ndots\n",
+ name);
+#endif
+ as->as_dom_flags |= ASYNC_DOM_NDOTS;
+ strlcpy(buf, name, len);
+ return (0);
+ }
+ /* Otherwise, starts using the search domains */
+ /* FALLTHROUGH */
+
+ case DOM_DOMAIN:
+ if (as->as_dom_idx < as->as_ctx->ac_domcount) {
+#ifdef DEBUG
+ asr_printf("asr: asr_iter_domain(\"%s\") "
+ "domain \"%s\"\n", name,
+ as->as_ctx->ac_dom[as->as_dom_idx]);
+#endif
+ as->as_dom_flags |= ASYNC_DOM_DOMAIN;
+ return (asr_domcat(name,
+ as->as_ctx->ac_dom[as->as_dom_idx++], buf, len));
+ }
+
+ /* No more domain to try. */
+
+ as->as_dom_step = DOM_DONE;
+
+ /*
+ * If the name was not tried as an absolute name before,
+ * do it now.
+ */
+ if (!(as->as_dom_flags & ASYNC_DOM_NDOTS)) {
+#ifdef DEBUG
+ asr_printf("asr: asr_iter_domain(\"%s\") as is\n",
+ name);
+#endif
+ as->as_dom_flags |= ASYNC_DOM_ASIS;
+ strlcpy(buf, name, len);
+ return (0);
+ }
+ /* Otherwise, we are done. */
+
+ case DOM_DONE:
+ default:
+#ifdef DEBUG
+ asr_printf("asr: asr_iter_domain(\"%s\") done\n", name);
+#endif
+ return (-1);
+ }
+}
+
+/*
+ * Check if the hostname "name" is a user-defined alias as per hostname(7).
+ * If so, copies the result in the buffer "abuf" of size "abufsz" and
+ * return "abuf". Otherwise return NULL.
+ */
+static char *
+asr_hostalias(const char *name, char *abuf, size_t abufsz)
+{
+ FILE *fp;
+ size_t len;
+ char *file, *buf, *cp, **tp, *tokens[2];
+ int ntok;
+
+ file = getenv("HOSTALIASES");
+ if (file == NULL || issetugid() != 0 || (fp = fopen(file, "r")) == NULL)
+ return (NULL);
+
+#ifdef DEBUG
+ asr_printf("asr: looking up aliases in \"%s\"\n", file);
+#endif
+
+ while ((buf = fgetln(fp, &len)) != NULL) {
+ if (buf[len - 1] == '\n')
+ len--;
+ buf[len] = '\0';
+ for(cp = buf, tp = tokens, ntok = 0;
+ ntok < 2 && (*tp = strsep(&cp, " \t")) != NULL; )
+ if (**tp != '\0') {
+ tp++;
+ ntok++;
+ }
+ if (ntok != 2)
+ continue;
+ if (!strcasecmp(tokens[0], name)) {
+ if (strlcpy(abuf, tokens[1], abufsz) > abufsz)
+ continue;
+#ifdef DEBUG
+ asr_printf("asr: found alias \"%s\"\n", abuf);
+#endif
+ fclose(fp);
+ return (abuf);
+ }
+ }
+
+ fclose(fp);
+ return (NULL);
+}
diff --git a/lib/libc/asr/asr.h b/lib/libc/asr/asr.h
new file mode 100644
index 00000000000..2a562092326
--- /dev/null
+++ b/lib/libc/asr/asr.h
@@ -0,0 +1,106 @@
+/* $OpenBSD: asr.h,v 1.1 2012/04/14 09:24:18 eric Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netdb.h>
+#include <netinet/in.h>
+
+/*
+ * This part is the generic API for the async mechanism. It could be useful
+ * beyond the resolver.
+ */
+
+/* Return values for async_run() */
+#define ASYNC_COND 0 /* wait for fd condition */
+#define ASYNC_YIELD 1 /* partial result */
+#define ASYNC_DONE 2 /* done */
+
+/* Expected fd conditions */
+#define ASYNC_READ 1
+#define ASYNC_WRITE 2
+
+/* This opaque structure holds an async query state. */
+struct async;
+
+/*
+ * This is the structure through which async_run() returns async
+ * results to the caller.
+ */
+struct async_res {
+ int ar_cond;
+ int ar_fd;
+ int ar_timeout;
+
+ int ar_errno;
+ int ar_h_errno;
+ int ar_gai_errno;
+ int ar_rrset_errno;
+
+ int ar_rcode;
+ void *ar_data;
+ int ar_datalen;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sain;
+ struct sockaddr_in6 sain6;
+ } ar_sa;
+ char *ar_cname;
+ int ar_count;
+
+ struct addrinfo *ar_addrinfo;
+ struct rrsetinfo *ar_rrsetinfo;
+ struct hostent *ar_hostent;
+ struct netent *ar_netent;
+};
+
+int async_run(struct async *, struct async_res *);
+int async_run_sync(struct async *, struct async_res *);
+void async_abort(struct async *);
+
+/* This opaque structure holds an async resolver context. */
+struct asr;
+
+struct asr *async_resolver(const char*);
+void async_resolver_done(struct asr*);
+
+/* Async version of the resolver API */
+
+struct async *res_send_async(const unsigned char *, int, unsigned char *, int,
+ struct asr *);
+struct async *res_query_async(const char *, int, int, unsigned char *, int,
+ struct asr *);
+struct async *res_search_async(const char *, int, int, unsigned char *, int,
+ struct asr *);
+
+struct async *getrrsetbyname_async(const char *, unsigned int, unsigned int,
+ unsigned int, struct asr *);
+
+struct async *gethostbyname_async(const char *, struct asr *);
+struct async *gethostbyname2_async(const char *, int, struct asr *);
+struct async *gethostbyaddr_async(const void *, socklen_t, int, struct asr *);
+void freehostent(struct hostent *);
+
+struct async *getnetbyname_async(const char *, struct asr *);
+struct async *getnetbyaddr_async(in_addr_t, int, struct asr *);
+void freenetent(struct netent *);
+
+struct async *getaddrinfo_async(const char *, const char *,
+ const struct addrinfo *, struct asr *);
+struct async *getnameinfo_async(const struct sockaddr *, socklen_t, char *,
+ size_t, char *, size_t, int, struct asr *);
diff --git a/lib/libc/asr/asr_debug.c b/lib/libc/asr/asr_debug.c
new file mode 100644
index 00000000000..1305633c352
--- /dev/null
+++ b/lib/libc/asr/asr_debug.c
@@ -0,0 +1,569 @@
+/* $OpenBSD: asr_debug.c,v 1.1 2012/04/14 09:24:18 eric Exp $ */
+/*
+ * Copyright (c) 2010-2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <inttypes.h>
+#include <resolv.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "asr.h"
+#include "asr_private.h"
+
+static void asr_vdebug(const char *, va_list);
+
+static char *print_dname(const char *, char *, size_t);
+static char *print_host(const struct sockaddr *, char *, size_t);
+
+static const char *typetostr(uint16_t);
+static const char *classtostr(uint16_t);
+static const char *rcodetostr(uint16_t);
+
+static const char *inet6_ntoa(struct in6_addr);
+
+
+#define OPCODE_SHIFT 11
+#define Z_SHIFT 4
+
+struct keyval {
+ const char *key;
+ uint16_t value;
+};
+
+static struct keyval kv_class[] = {
+ { "IN", C_IN },
+ { "CHAOS", C_CHAOS },
+ { "HS", C_HS },
+ { "ANY", C_ANY },
+ { NULL, 0 },
+};
+
+static struct keyval kv_type[] = {
+ { "A", T_A },
+ { "NS", T_NS },
+ { "MD", T_MD },
+ { "MF", T_MF },
+ { "CNAME", T_CNAME },
+ { "SOA", T_SOA },
+ { "MB", T_MB },
+ { "MG", T_MG },
+ { "MR", T_MR },
+ { "NULL", T_NULL },
+ { "WKS", T_WKS },
+ { "PTR", T_PTR },
+ { "HINFO", T_HINFO },
+ { "MINFO", T_MINFO },
+ { "MX", T_MX },
+ { "TXT", T_TXT },
+
+ { "AAAA", T_AAAA },
+
+ { "AXFR", T_AXFR },
+ { "MAILB", T_MAILB },
+ { "MAILA", T_MAILA },
+ { "ANY", T_ANY },
+ { NULL, 0 },
+};
+
+static struct keyval kv_rcode[] = {
+ { "NOERROR", NOERROR },
+ { "FORMERR", FORMERR },
+ { "SERVFAIL", SERVFAIL },
+ { "NXDOMAIN", NXDOMAIN },
+ { "NOTIMP", NOTIMP },
+ { "REFUSED", REFUSED },
+ { NULL, 0 },
+};
+
+static const char *
+typetostr(uint16_t v)
+{
+ static char buf[16];
+ size_t i;
+
+ for(i = 0; kv_type[i].key; i++)
+ if (kv_type[i].value == v)
+ return (kv_type[i].key);
+
+ snprintf(buf, sizeof buf, "%"PRIu16"?", v);
+
+ return (buf);
+}
+
+static const char *
+classtostr(uint16_t v)
+{
+ static char buf[16];
+ size_t i;
+
+ for(i = 0; kv_class[i].key; i++)
+ if (kv_class[i].value == v)
+ return (kv_class[i].key);
+
+ snprintf(buf, sizeof buf, "%"PRIu16"?", v);
+
+ return (buf);
+}
+
+static const char *
+rcodetostr(uint16_t v)
+{
+ static char buf[16];
+ size_t i;
+
+ for(i = 0; kv_rcode[i].key; i++)
+ if (kv_rcode[i].value == v)
+ return (kv_rcode[i].key);
+
+ snprintf(buf, sizeof buf, "%"PRIu16"?", v);
+
+ return (buf);
+}
+
+static const char *
+inet6_ntoa(struct in6_addr a)
+{
+ static char buf[256];
+ struct sockaddr_in6 si;
+
+ si.sin6_len = sizeof(si);
+ si.sin6_family = PF_INET6;
+ si.sin6_addr = a;
+
+ return print_host((struct sockaddr*)&si, buf, sizeof buf);
+}
+
+static char*
+print_rr(struct rr *rr, char *buf, size_t max)
+{
+ char *res;
+ char tmp[256];
+ char tmp2[256];
+ int r;
+
+ res = buf;
+
+ r = snprintf(buf, max, "%s %u %s %s ",
+ print_dname(rr->rr_dname, tmp, sizeof tmp),
+ rr->rr_ttl,
+ classtostr(rr->rr_class),
+ typetostr(rr->rr_type));
+ if (r == -1) {
+ buf[0] = '\0';
+ return buf;
+ }
+
+ if ((size_t)r >= max)
+ return buf;
+
+ max -= r;
+ buf += r;
+
+ switch(rr->rr_type) {
+ case T_CNAME:
+ print_dname(rr->rr.cname.cname, buf, max);
+ break;
+ case T_MX:
+ snprintf(buf, max, "%"PRIu32" %s",
+ rr->rr.mx.preference,
+ print_dname(rr->rr.mx.exchange, tmp, sizeof tmp));
+ break;
+ case T_NS:
+ print_dname(rr->rr.ns.nsname, buf, max);
+ break;
+ case T_PTR:
+ print_dname(rr->rr.ptr.ptrname, buf, max);
+ break;
+ case T_SOA:
+ snprintf(buf, max,
+ "%s %s %" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32,
+ print_dname(rr->rr.soa.rname, tmp, sizeof tmp),
+ print_dname(rr->rr.soa.mname, tmp2, sizeof tmp2),
+ rr->rr.soa.serial,
+ rr->rr.soa.refresh,
+ rr->rr.soa.retry,
+ rr->rr.soa.expire,
+ rr->rr.soa.minimum);
+ break;
+ case T_A:
+ if (rr->rr_class != C_IN)
+ goto other;
+ snprintf(buf, max, "%s", inet_ntoa(rr->rr.in_a.addr));
+ break;
+ case T_AAAA:
+ if (rr->rr_class != C_IN)
+ goto other;
+ snprintf(buf, max, "%s", inet6_ntoa(rr->rr.in_aaaa.addr6));
+ break;
+ default:
+ other:
+ snprintf(buf, max, "(rdlen=%"PRIu16 ")", rr->rr.other.rdlen);
+ break;
+ }
+
+ return (res);
+}
+
+static char*
+print_query(struct query *q, char *buf, size_t max)
+{
+ char b[256];
+
+ snprintf(buf, max, "%s %s %s",
+ print_dname(q->q_dname, b, sizeof b),
+ classtostr(q->q_class), typetostr(q->q_type));
+
+ return (buf);
+}
+
+static char*
+print_dname(const char *_dname, char *buf, size_t max)
+{
+ return asr_strdname(_dname, buf, max);
+}
+
+static char*
+print_header(struct header *h, char *buf, size_t max, int noid)
+{
+ snprintf(buf, max,
+ "id:0x%04x %s op:%i %s %s %s %s z:%i r:%s qd:%i an:%i ns:%i ar:%i",
+ noid ? 0 : ((int)h->id),
+ (h->flags & QR_MASK) ? "QR":" ",
+ (int)(OPCODE(h->flags) >> OPCODE_SHIFT),
+ (h->flags & AA_MASK) ? "AA":" ",
+ (h->flags & TC_MASK) ? "TC":" ",
+ (h->flags & RD_MASK) ? "RD":" ",
+ (h->flags & RA_MASK) ? "RA":" ",
+ ((h->flags & Z_MASK) >> Z_SHIFT),
+ rcodetostr(RCODE(h->flags)),
+ h->qdcount, h->ancount, h->nscount, h->arcount);
+
+ return buf;
+}
+
+static char *
+print_host(const struct sockaddr *sa, char *buf, size_t len)
+{
+ int e;
+
+ if ((e = getnameinfo(sa, sa->sa_len,
+ buf, len, NULL, 0, NI_NUMERICHOST)) != 0) {
+ buf[0] = '\0';
+ return (NULL);
+ }
+ return (buf);
+}
+
+char *
+print_addr(const struct sockaddr *sa, char *buf, size_t len)
+{
+ char h[256];
+
+ print_host(sa, h, sizeof h);
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ snprintf(buf, len, "%s:%i", h,
+ ntohs(((struct sockaddr_in*)(sa))->sin_port));
+ break;
+ case AF_INET6:
+ snprintf(buf, len, "[%s]:%i", h,
+ ntohs(((struct sockaddr_in6*)(sa))->sin6_port));
+ break;
+ default:
+ snprintf(buf, len, "?");
+ break;
+ }
+
+ return (buf);
+}
+
+struct kv { int code; const char *name; };
+
+static const char* kvlookup(struct kv *, int);
+
+int asr_debug = 0;
+
+void
+asr_dump(struct asr *a)
+{
+ char buf[256];
+ int i;
+ struct asr_ctx *ac;
+ unsigned int options;
+
+ ac = a->a_ctx;
+
+ asr_printf("--------- ASR CONFIG ---------------\n");
+ if (a->a_path)
+ asr_printf("CONF FILE \"%s\"\n", a->a_path);
+ else
+ asr_printf("STATIC CONF\n");
+ asr_printf("DOMAIN \"%s\"\n", ac->ac_domain);
+ asr_printf("SEARCH\n");
+ for(i = 0; i < ac->ac_domcount; i++)
+ asr_printf(" \"%s\"\n", ac->ac_dom[i]);
+ asr_printf("OPTIONS\n");
+ asr_printf(" options:");
+ options = ac->ac_options;
+ if (options & RES_INIT) {
+ asr_printf(" INIT"); options &= ~RES_INIT;
+ }
+ if (options & RES_DEBUG) {
+ asr_printf(" DEBUG"); options &= ~RES_DEBUG;
+ }
+ if (options & RES_USEVC) {
+ asr_printf(" USEVC"); options &= ~RES_USEVC;
+ }
+ if (options & RES_IGNTC) {
+ asr_printf(" IGNTC"); options &= ~RES_IGNTC;
+ }
+ if (options & RES_RECURSE) {
+ asr_printf(" RECURSE"); options &= ~RES_RECURSE;
+ }
+ if (options & RES_DEFNAMES) {
+ asr_printf(" DEFNAMES"); options &= ~RES_DEFNAMES;
+ }
+ if (options & RES_STAYOPEN) {
+ asr_printf(" STAYOPEN"); options &= ~RES_STAYOPEN;
+ }
+ if (options & RES_DNSRCH) {
+ asr_printf(" DNSRCH"); options &= ~RES_DNSRCH;
+ }
+ if (options & RES_NOALIASES) {
+ asr_printf(" NOALIASES"); options &= ~RES_NOALIASES;
+ }
+ if (options & RES_USE_EDNS0) {
+ asr_printf(" USE_EDNS0"); options &= ~RES_USE_EDNS0;
+ }
+ if (options & RES_USE_DNSSEC) {
+ asr_printf(" USE_DNSSEC"); options &= ~RES_USE_DNSSEC;
+ }
+ if (options)
+ asr_printf("0x%08x\n", options);
+ asr_printf("\n", ac->ac_options);
+
+ asr_printf(" ndots: %i\n", ac->ac_ndots);
+ asr_printf(" family:");
+ for(i = 0; ac->ac_family[i] != -1; i++)
+ asr_printf(" %s", (ac->ac_family[i] == AF_INET) ? "inet" : "inet6");
+ asr_printf("\n");
+ asr_printf("NAMESERVERS timeout=%i retry=%i\n",
+ ac->ac_nstimeout,
+ ac->ac_nsretries);
+ for(i = 0; i < ac->ac_nscount; i++)
+ asr_printf(" %s\n", print_addr(ac->ac_ns[i], buf, sizeof buf));
+ asr_printf("HOSTFILE %s\n", ac->ac_hostfile);
+ asr_printf("LOOKUP");
+ for(i = 0; i < ac->ac_dbcount; i++) {
+ switch (ac->ac_db[i]) {
+ case ASR_DB_FILE:
+ asr_printf(" file");
+ break;
+ case ASR_DB_DNS:
+ asr_printf(" dns");
+ break;
+ case ASR_DB_YP:
+ asr_printf(" yp");
+ break;
+ default:
+ asr_printf(" ?%i", ac->ac_db[i]);
+ }
+ }
+ asr_printf("\n------------------------------------\n");
+}
+
+static const char *
+kvlookup(struct kv *kv, int code)
+{
+ while (kv->name) {
+ if (kv->code == code)
+ return (kv->name);
+ kv++;
+ }
+ return "???";
+}
+
+struct kv kv_query_type[] = {
+ { ASR_SEND, "ASR_SEND" },
+ { ASR_SEARCH, "ASR_SEARCH" },
+ { ASR_GETRRSETBYNAME, "ASR_GETRRSETBYNAME" },
+ { ASR_GETHOSTBYNAME, "ASR_GETHOSTBYNAME" },
+ { ASR_GETHOSTBYADDR, "ASR_GETHOSTBYADDR" },
+ { ASR_GETNETBYNAME, "ASR_GETNETBYNAME" },
+ { ASR_GETNETBYADDR, "ASR_GETNETBYADDR" },
+ { ASR_GETADDRINFO, "ASR_GETADDRINFO" },
+ { ASR_GETNAMEINFO, "ASR_GETNAMEINFO" },
+ { ASR_HOSTADDR, "ASR_HOSTADDR" },
+ { 0, NULL }
+};
+
+struct kv kv_db_type[] = {
+ { ASR_DB_FILE, "ASR_DB_FILE" },
+ { ASR_DB_DNS, "ASR_DB_DNS" },
+ { ASR_DB_YP, "ASR_DB_YP" },
+ { 0, NULL }
+};
+
+struct kv kv_state[] = {
+ { ASR_STATE_INIT, "ASR_STATE_INIT" },
+ { ASR_STATE_SEARCH_DOMAIN, "ASR_STATE_SEARCH_DOMAIN" },
+ { ASR_STATE_LOOKUP_DOMAIN, "ASR_STATE_LOOKUP_DOMAIN" },
+ { ASR_STATE_NEXT_DOMAIN, "ASR_STATE_NEXT_DOMAIN" },
+ { ASR_STATE_NEXT_DB, "ASR_STATE_NEXT_DB" },
+ { ASR_STATE_SAME_DB, "ASR_STATE_SAME_DB" },
+ { ASR_STATE_NEXT_FAMILY, "ASR_STATE_NEXT_FAMILY" },
+ { ASR_STATE_LOOKUP_FAMILY, "ASR_STATE_LOOKUP_FAMILY" },
+ { ASR_STATE_NEXT_NS, "ASR_STATE_NEXT_NS" },
+ { ASR_STATE_READ_RR, "ASR_STATE_READ_RR" },
+ { ASR_STATE_READ_FILE, "ASR_STATE_READ_FILE" },
+ { ASR_STATE_UDP_SEND, "ASR_STATE_UDP_SEND" },
+ { ASR_STATE_UDP_RECV, "ASR_STATE_UDP_RECV" },
+ { ASR_STATE_TCP_WRITE, "ASR_STATE_TCP_WRITE" },
+ { ASR_STATE_TCP_READ, "ASR_STATE_TCP_READ" },
+ { ASR_STATE_PACKET, "ASR_STATE_PACKET" },
+ { ASR_STATE_SUBQUERY, "ASR_STATE_SUBQUERY" },
+ { ASR_STATE_NOT_FOUND, "ASR_STATE_NOT_FOUND", },
+ { ASR_STATE_HALT, "ASR_STATE_HALT" },
+ { 0, NULL }
+};
+
+struct kv kv_transition[] = {
+ { ASYNC_COND, "ASYNC_COND" },
+ { ASYNC_YIELD, "ASYNC_YIELD" },
+ { ASYNC_DONE, "ASYNC_DONE" },
+ { 0, NULL }
+};
+
+const char *
+asr_querystr(int type)
+{
+ return kvlookup(kv_query_type, type);
+}
+
+const char *
+asr_transitionstr(int type)
+{
+ return kvlookup(kv_transition, type);
+}
+
+void
+asr_dump_async(struct async *as)
+{
+ asr_printf("%s fd=%i timeout=%i"
+ " dom_idx=%i db_idx=%i ns_idx=%i ns_cycles=%i\n",
+ kvlookup(kv_state, as->as_state),
+ as->as_fd,
+ as->as_timeout,
+
+ as->as_dom_idx,
+ as->as_db_idx,
+ as->as_ns_idx,
+ as->as_ns_cycles);
+}
+
+void
+asr_dump_packet(FILE *f, const void *data, size_t len, int noid)
+{
+ char buf[1024];
+ struct packed p;
+ struct header h;
+ struct query q;
+ struct rr rr;
+ int i, an, ns, ar, n;
+
+ if (f == NULL)
+ return;
+
+ packed_init(&p, (char *)data, len);
+
+ if (unpack_header(&p, &h) == -1) {
+ fprintf(f, ";; BAD PACKET: %s\n", p.err);
+ return;
+ }
+
+ fprintf(f, ";; HEADER %s\n", print_header(&h, buf, sizeof buf, noid));
+
+ if (h.qdcount)
+ fprintf(f, ";; QUERY SECTION:\n");
+ for (i = 0; i < h.qdcount; i++) {
+ if (unpack_query(&p, &q) == -1)
+ goto error;
+ fprintf(f, "%s\n", print_query(&q, buf, sizeof buf));
+ }
+
+ an = 0;
+ ns = an + h.ancount;
+ ar = ns + h.nscount;
+ n = ar + h.arcount;
+
+ for (i = 0; i < n; i++) {
+ if (i == an)
+ fprintf(f, "\n;; ANSWER SECTION:\n");
+ if (i == ns)
+ fprintf(f, "\n;; AUTHORITY SECTION:\n");
+ if (i == ar)
+ fprintf(f, "\n;; ADDITIONAL SECTION:\n");
+
+ if (unpack_rr(&p, &rr) == -1)
+ goto error;
+ fprintf(f, "%s\n", print_rr(&rr, buf, sizeof buf));
+ }
+
+ if (p.offset != len)
+ fprintf(f, ";; REMAINING GARBAGE %zu\n", len - p.offset);
+
+ error:
+ if (p.err)
+ fprintf(f, ";; ERROR AT OFFSET %zu/%zu: %s\n", p.offset, p.len,
+ p.err);
+
+ return;
+}
+
+static void
+asr_vdebug(const char *fmt, va_list ap)
+{
+ if (asr_debug)
+ vfprintf(stderr, fmt, ap);
+}
+
+void
+asr_printf(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ asr_vdebug(fmt, ap);
+ va_end(ap);
+}
+
+void
+async_set_state(struct async *as, int state)
+{
+ asr_printf("asr: [%s@%p] %s -> %s\n",
+ kvlookup(kv_query_type, as->as_type),
+ as,
+ kvlookup(kv_state, as->as_state),
+ kvlookup(kv_state, state));
+ as->as_state = state;
+}
diff --git a/lib/libc/asr/asr_private.h b/lib/libc/asr/asr_private.h
new file mode 100644
index 00000000000..bd86912e92e
--- /dev/null
+++ b/lib/libc/asr/asr_private.h
@@ -0,0 +1,372 @@
+/* $OpenBSD: asr_private.h,v 1.1 2012/04/14 09:24:18 eric Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <stdio.h>
+
+#define QR_MASK (0x1 << 15)
+#define OPCODE_MASK (0xf << 11)
+#define AA_MASK (0x1 << 10)
+#define TC_MASK (0x1 << 9)
+#define RD_MASK (0x1 << 8)
+#define RA_MASK (0x1 << 7)
+#define Z_MASK (0x7 << 4)
+#define RCODE_MASK (0xf)
+
+#define OPCODE(v) ((v) & OPCODE_MASK)
+#define RCODE(v) ((v) & RCODE_MASK)
+
+
+struct packed {
+ char *data;
+ size_t len;
+ size_t offset;
+ const char *err;
+};
+
+struct header {
+ uint16_t id;
+ uint16_t flags;
+ uint16_t qdcount;
+ uint16_t ancount;
+ uint16_t nscount;
+ uint16_t arcount;
+};
+
+struct query {
+ char q_dname[MAXDNAME];
+ uint16_t q_type;
+ uint16_t q_class;
+};
+
+struct rr {
+ char rr_dname[MAXDNAME];
+ uint16_t rr_type;
+ uint16_t rr_class;
+ uint32_t rr_ttl;
+ union {
+ struct {
+ char cname[MAXDNAME];
+ } cname;
+ struct {
+ uint16_t preference;
+ char exchange[MAXDNAME];
+ } mx;
+ struct {
+ char nsname[MAXDNAME];
+ } ns;
+ struct {
+ char ptrname[MAXDNAME];
+ } ptr;
+ struct {
+ char mname[MAXDNAME];
+ char rname[MAXDNAME];
+ uint32_t serial;
+ uint32_t refresh;
+ uint32_t retry;
+ uint32_t expire;
+ uint32_t minimum;
+ } soa;
+ struct {
+ struct in_addr addr;
+ } in_a;
+ struct {
+ struct in6_addr addr6;
+ } in_aaaa;
+ struct {
+ uint16_t rdlen;
+ const void *rdata;
+ } other;
+ } rr;
+};
+
+
+#define ASR_MAXNS 5
+#define ASR_MAXDB 3
+#define ASR_MAXDOM 10
+
+enum async_type {
+ ASR_SEND,
+ ASR_SEARCH,
+ ASR_GETRRSETBYNAME,
+ ASR_GETHOSTBYNAME,
+ ASR_GETHOSTBYADDR,
+ ASR_GETNETBYNAME,
+ ASR_GETNETBYADDR,
+ ASR_GETADDRINFO,
+ ASR_GETNAMEINFO,
+ ASR_HOSTADDR,
+};
+
+enum asr_db_type {
+ ASR_DB_FILE,
+ ASR_DB_DNS,
+ ASR_DB_YP,
+};
+
+struct asr_ctx {
+ int ac_refcount;
+ int ac_options;
+ int ac_ndots;
+ char *ac_domain;
+ int ac_domcount;
+ char *ac_dom[ASR_MAXDOM];
+ int ac_dbcount;
+ int ac_db[ASR_MAXDB];
+ int ac_family[3];
+
+ char *ac_hostfile;
+
+ int ac_nscount;
+ int ac_nstimeout;
+ int ac_nsretries;
+ struct sockaddr *ac_ns[ASR_MAXNS];
+
+};
+
+struct asr {
+ char *a_path;
+ time_t a_mtime;
+ time_t a_rtime;
+ struct asr_ctx *a_ctx;
+};
+
+
+#define ASYNC_DOM_FQDN 0x00000001
+#define ASYNC_DOM_NDOTS 0x00000002
+#define ASYNC_DOM_HOSTALIAS 0x00000004
+#define ASYNC_DOM_DOMAIN 0x00000008
+#define ASYNC_DOM_ASIS 0x00000010
+
+#define ASYNC_NODATA 0x00000100
+#define ASYNC_AGAIN 0x00000200
+
+#define ASYNC_EXTIBUF 0x00001000
+#define ASYNC_EXTOBUF 0x00002000
+
+
+struct async {
+ int (*as_run)(struct async *, struct async_res *);
+ struct asr_ctx *as_ctx;
+ int as_type;
+ int as_state;
+
+ /* cond */
+ int as_timeout;
+ int as_fd;
+
+ /* loop indices in ctx */
+ int as_dom_step;
+ int as_dom_idx;
+ int as_dom_flags;
+ int as_family_idx;
+ int as_db_idx;
+ int as_ns_idx;
+ int as_ns_cycles;
+
+ int as_count;
+
+ union {
+ struct {
+ int flags;
+ uint16_t reqid;
+ int class;
+ int type;
+ char *dname; /* not fqdn! */
+ int rcode; /* response code */
+ int ancount; /* answer count */
+
+ /* io buffers for query/response */
+ unsigned char *obuf;
+ size_t obuflen;
+ size_t obufsize;
+ unsigned char *ibuf;
+ size_t ibuflen;
+ size_t ibufsize;
+ size_t bufpos;
+ size_t datalen; /* for tcp io */
+ } dns;
+
+ struct {
+ int flags;
+ int class;
+ int type;
+ char *name;
+ struct async *subq;
+ int saved_h_errno;
+ unsigned char *ibuf;
+ size_t ibuflen;
+ size_t ibufsize;
+ } search;
+
+ struct {
+ int flags;
+ int class;
+ int type;
+ char *name;
+ struct async *subq;
+ } rrset;
+
+ struct {
+ char *name;
+ int family;
+ char *dname;
+ struct async *subq;
+ char addr[16];
+ int addrlen;
+ } hostnamadr;
+
+ struct {
+ char *name;
+ int family;
+ struct async *subq;
+ in_addr_t addr;
+ } netnamadr;
+
+ struct {
+ char *hostname;
+ char *servname;
+ int port_tcp;
+ int port_udp;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sain;
+ struct sockaddr_in6 sain6;
+ } sa;
+
+ struct addrinfo hints;
+ struct addrinfo *aifirst;
+ struct addrinfo *ailast;
+ struct async *subq;
+ int flags;
+ } ai;
+
+ struct {
+ char *hostname;
+ char *servname;
+ size_t hostnamelen;
+ size_t servnamelen;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sain;
+ struct sockaddr_in6 sain6;
+ } sa;
+ int flags;
+ struct async *subq;
+ } ni;
+
+ struct {
+ char *name;
+ int family;
+ int aiflags;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sain;
+ struct sockaddr_in6 sain6;
+ } sa;
+
+ struct async *subq;
+ int class;
+ int type;
+ int ancount;
+ unsigned char *pkt;
+ size_t pktlen;
+ size_t pktpos;
+ FILE *file;
+#define MAXTOKEN 10
+ char *tokens[MAXTOKEN];
+ int token_count;
+ int token_idx;
+ } host;
+ } as;
+
+};
+
+#define AS_DB(p) ((p)->as_ctx->ac_db[(p)->as_db_idx - 1])
+#define AS_FAMILY(p) ((p)->as_ctx->ac_family[(p)->as_family_idx])
+
+enum asr_state {
+ ASR_STATE_INIT,
+ ASR_STATE_SEARCH_DOMAIN,
+ ASR_STATE_LOOKUP_DOMAIN,
+ ASR_STATE_NEXT_DOMAIN,
+ ASR_STATE_NEXT_DB,
+ ASR_STATE_SAME_DB,
+ ASR_STATE_NEXT_FAMILY,
+ ASR_STATE_LOOKUP_FAMILY,
+ ASR_STATE_NEXT_NS,
+ ASR_STATE_READ_RR,
+ ASR_STATE_READ_FILE,
+ ASR_STATE_UDP_SEND,
+ ASR_STATE_UDP_RECV,
+ ASR_STATE_TCP_WRITE,
+ ASR_STATE_TCP_READ,
+ ASR_STATE_PACKET,
+ ASR_STATE_SUBQUERY,
+ ASR_STATE_NOT_FOUND,
+ ASR_STATE_HALT,
+};
+
+
+/* asr_utils.c */
+void packed_init(struct packed*, char*, size_t);
+int pack_header(struct packed*, const struct header*);
+int pack_query(struct packed*, uint16_t, uint16_t, const char*);
+int unpack_header(struct packed*, struct header*);
+int unpack_query(struct packed*, struct query*);
+int unpack_rr(struct packed*, struct rr*);
+int sockaddr_from_str(struct sockaddr *, int, const char *);
+ssize_t dname_from_fqdn(const char*, char*, size_t);
+
+/* asr.c */
+struct asr_ctx *asr_use_resolver(struct asr *);
+void asr_ctx_unref(struct asr_ctx *);
+struct async *async_new(struct asr_ctx *, int);
+void async_free(struct async *);
+size_t asr_make_fqdn(const char *, const char *, char *, size_t);
+size_t asr_domcat(const char *, const char *, char *, size_t);
+char *asr_strdname(const char *, char *, size_t);
+int asr_iter_db(struct async *);
+int asr_iter_ns(struct async *);
+int asr_iter_domain(struct async *, const char *, char *, size_t);
+int asr_parse_namedb_line(FILE *, char **, int);
+
+/* <*>_async.h */
+struct async *res_query_async_ctx(const char *, int, int, unsigned char *, int,
+ struct asr_ctx *);
+struct async *res_search_async_ctx(const char *, int, int, unsigned char *, int,
+ struct asr_ctx *);
+struct async *gethostbyaddr_async_ctx(const void *, socklen_t, int,
+ struct asr_ctx *);
+struct async *hostaddr_async_ctx(const char *, int, int, struct asr_ctx *);
+
+#ifdef DEBUG
+
+extern int asr_debug;
+
+/* asr_debug.h */
+const char *asr_querystr(int);
+const char *asr_transitionstr(int);
+void asr_dump(struct asr *);
+void asr_dump_async(struct async *);
+void asr_dump_packet(FILE *, const void *, size_t, int);
+void asr_printf(const char *fmt, ...);
+void async_set_state(struct async *, int);
+
+#else /* DEBUG */
+
+#define async_set_state(a, s) do { (a)->as_state = (s); } while (0)
+
+#endif /* DEBUG */
diff --git a/lib/libc/asr/asr_resolver.c b/lib/libc/asr/asr_resolver.c
new file mode 100644
index 00000000000..1eb36c17ab3
--- /dev/null
+++ b/lib/libc/asr/asr_resolver.c
@@ -0,0 +1,457 @@
+/* $OpenBSD: asr_resolver.c,v 1.1 2012/04/14 09:24:18 eric Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <resolv.h>
+#include <string.h>
+
+#include "asr.h"
+
+/*
+ * XXX this function is actually internal to asr, but we use it here to force
+ * the creation a default resolver context in res_init().
+ */
+struct asr_ctx *asr_use_resolver(struct asr *);
+void asr_ctx_unref(struct asr_ctx *);
+
+static struct hostent *_gethostbyname(const char *, int);
+static struct hostent *_mkstatichostent(struct hostent *);
+static struct netent *_mkstaticnetent(struct netent *);
+
+
+/* in res_init.c */
+struct __res_state _res;
+struct __res_state_ext _res_ext;
+
+/* in res_query.c */
+int h_errno;
+
+
+int
+res_init(void)
+{
+ async_resolver_done(NULL);
+ asr_ctx_unref(asr_use_resolver(NULL));
+
+ return (0);
+}
+
+int
+res_send(const u_char *buf, int buflen, u_char *ans, int anslen)
+{
+ struct async *as;
+ struct async_res ar;
+
+ if (ans == NULL || anslen <= 0) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ as = res_send_async(buf, buflen, ans, anslen, NULL);
+ if (as == NULL)
+ return (-1); /* errno set */
+
+ async_run_sync(as, &ar);
+
+ if (ar.ar_errno) {
+ errno = ar.ar_errno;
+ return (-1);
+ }
+
+ return (ar.ar_datalen);
+}
+
+int
+res_query(const char *name, int class, int type, u_char *ans, int anslen)
+{
+ struct async *as;
+ struct async_res ar;
+
+ if (ans == NULL || anslen <= 0) {
+ h_errno = NO_RECOVERY;
+ errno = EINVAL;
+ return (-1);
+ }
+
+ as = res_query_async(name, class, type, ans, anslen, NULL);
+ if (as == NULL) {
+ if (errno == EINVAL)
+ h_errno = NO_RECOVERY;
+ else
+ h_errno = NETDB_INTERNAL;
+ return (-1); /* errno set */
+ }
+
+ async_run_sync(as, &ar);
+
+ if (ar.ar_errno)
+ errno = ar.ar_errno;
+ h_errno = ar.ar_h_errno;
+
+ if (ar.ar_h_errno != NETDB_SUCCESS)
+ return (-1);
+
+ return (ar.ar_datalen);
+}
+
+int
+res_search(const char *name, int class, int type, u_char *ans, int anslen)
+{
+ struct async *as;
+ struct async_res ar;
+
+ if (ans == NULL || anslen <= 0) {
+ h_errno = NO_RECOVERY;
+ errno = EINVAL;
+ return (-1);
+ }
+
+ as = res_search_async(name, class, type, ans, anslen, NULL);
+ if (as == NULL) {
+ if (errno == EINVAL)
+ h_errno = NO_RECOVERY;
+ else
+ h_errno = NETDB_INTERNAL;
+ return (-1); /* errno set */
+ }
+
+ async_run_sync(as, &ar);
+
+ if (ar.ar_errno)
+ errno = ar.ar_errno;
+ h_errno = ar.ar_h_errno;
+
+ if (ar.ar_h_errno != NETDB_SUCCESS)
+ return (-1);
+
+ return (ar.ar_datalen);
+}
+
+int
+getrrsetbyname(const char *name, unsigned int class, unsigned int type,
+ unsigned int flags, struct rrsetinfo **res)
+{
+ struct async *as;
+ struct async_res ar;
+
+ as = getrrsetbyname_async(name, class, type, flags, NULL);
+ if (as == NULL)
+ return (errno == ENOMEM) ? ERRSET_NOMEMORY : ERRSET_FAIL;
+
+ async_run_sync(as, &ar);
+
+ if (ar.ar_errno)
+ errno = ar.ar_errno;
+
+ *res = ar.ar_rrsetinfo;
+ return (ar.ar_rrset_errno);
+}
+
+#define MAXALIASES 16
+#define MAXADDRS 16
+
+/* XXX bound checks are incorrect */
+static struct hostent *
+_mkstatichostent(struct hostent *h)
+{
+ static struct hostent r;
+ static char buf[4096];
+ static char *aliases[MAXALIASES+1];
+ static uint64_t addrbuf[64];
+ static char *addr_list[MAXADDRS + 1];
+
+ char *pos, **c;
+ size_t left, n;
+ int naliases = 0, naddrs = 0;
+
+ r.h_addrtype = h->h_addrtype;
+ r.h_length = h->h_length;
+ r.h_name = buf;
+ r.h_aliases = aliases;
+ r.h_addr_list = addr_list;
+
+ pos = buf;
+ left = sizeof(buf);
+ n = strlcpy(pos, h->h_name, left);
+ pos += n + 1;
+ left -= n + 1;
+
+ for(c = h->h_aliases; left && *c && naliases < MAXALIASES; c++) {
+ n = strlcpy(pos, *c, left);
+ if (n >= left + 1)
+ break;
+ aliases[naliases++] = pos;
+ pos += n + 1;
+ left -= n + 1;
+ }
+ aliases[naliases] = NULL;
+
+ pos = (char*)addrbuf;
+ left = sizeof(addrbuf);
+ for(c = h->h_addr_list; *c && naddrs < MAXADDRS; c++) {
+ memmove(pos, *c, r.h_length);
+ addr_list[naddrs++] = pos;
+ pos += r.h_length;
+ left -= r.h_length;
+ }
+ addr_list[naddrs] = NULL;
+
+ return (&r);
+}
+
+static struct hostent *
+_gethostbyname(const char *name, int af)
+{
+ struct async *as;
+ struct async_res ar;
+ struct hostent *h;
+
+ if (af == -1)
+ as = gethostbyname_async(name, NULL);
+ else
+ as = gethostbyname2_async(name, af, NULL);
+
+ if (as == NULL) {
+ h_errno = NETDB_INTERNAL;
+ return (NULL);
+ }
+
+ async_run_sync(as, &ar);
+
+ errno = ar.ar_errno;
+ h_errno = ar.ar_h_errno;
+ if (ar.ar_hostent == NULL)
+ return (NULL);
+
+ h = _mkstatichostent(ar.ar_hostent);
+ freehostent(ar.ar_hostent);
+
+ return (h);
+}
+
+struct hostent *
+gethostbyname(const char *name)
+{
+ return _gethostbyname(name, -1);
+}
+
+struct hostent *
+gethostbyname2(const char *name, int af)
+{
+ return _gethostbyname(name, af);
+}
+
+struct hostent *
+gethostbyaddr(const void *addr, socklen_t len, int af)
+{
+ struct async *as;
+ struct async_res ar;
+ struct hostent *h;
+
+ as = gethostbyaddr_async(addr, len, af, NULL);
+ if (as == NULL) {
+ h_errno = NETDB_INTERNAL;
+ return (NULL);
+ }
+
+ async_run_sync(as, &ar);
+
+ errno = ar.ar_errno;
+ h_errno = ar.ar_h_errno;
+ if (ar.ar_hostent == NULL)
+ return (NULL);
+
+ h = _mkstatichostent(ar.ar_hostent);
+ freehostent(ar.ar_hostent);
+
+ return (h);
+}
+
+/* XXX bound checks are incorrect */
+static struct netent *
+_mkstaticnetent(struct netent *n)
+{
+ static struct netent r;
+ static char buf[4096];
+ static char *aliases[MAXALIASES+1];
+
+ char *pos, **c;
+ size_t left, s;
+ int naliases = 0;
+
+ r.n_addrtype = n->n_addrtype;
+ r.n_net = n->n_net;
+
+ r.n_name = buf;
+ r.n_aliases = aliases;
+
+ pos = buf;
+ left = sizeof(buf);
+ s = strlcpy(pos, n->n_name, left);
+ pos += s + 1;
+ left -= s + 1;
+
+ for(c = n->n_aliases; left && *c && naliases < MAXALIASES; c++) {
+ s = strlcpy(pos, *c, left);
+ if (s >= left + 1)
+ break;
+ aliases[naliases++] = pos;
+ pos += s + 1;
+ left -= s + 1;
+ }
+ aliases[naliases] = NULL;
+
+ return (&r);
+}
+
+struct netent *
+getnetbyname(const char *name)
+{
+ struct async *as;
+ struct async_res ar;
+ struct netent *n;
+
+ as = getnetbyname_async(name, NULL);
+ if (as == NULL) {
+ h_errno = NETDB_INTERNAL;
+ return (NULL);
+ }
+
+ async_run_sync(as, &ar);
+
+ errno = ar.ar_errno;
+ h_errno = ar.ar_h_errno;
+ if (ar.ar_netent == NULL)
+ return (NULL);
+
+ n = _mkstaticnetent(ar.ar_netent);
+ freenetent(ar.ar_netent);
+
+ return (n);
+}
+
+struct netent *
+getnetbyaddr(in_addr_t net, int type)
+{
+ struct async *as;
+ struct async_res ar;
+ struct netent *n;
+
+ as = getnetbyaddr_async(net, type, NULL);
+ if (as == NULL) {
+ h_errno = NETDB_INTERNAL;
+ return (NULL);
+ }
+
+ async_run_sync(as, &ar);
+
+ errno = ar.ar_errno;
+ h_errno = ar.ar_h_errno;
+ if (ar.ar_netent == NULL)
+ return (NULL);
+
+ n = _mkstaticnetent(ar.ar_netent);
+ freenetent(ar.ar_netent);
+
+ return (n);
+}
+
+int
+getaddrinfo(const char *hostname, const char *servname,
+ const struct addrinfo *hints, struct addrinfo **res)
+{
+ struct async *as;
+ struct async_res ar;
+
+ as = getaddrinfo_async(hostname, servname, hints, NULL);
+ if (as == NULL)
+ return ((errno == ENOMEM) ? EAI_MEMORY : EAI_SYSTEM);
+
+ async_run_sync(as, &ar);
+
+ errno = ar.ar_errno;
+ h_errno = ar.ar_h_errno;
+ *res = ar.ar_addrinfo;
+
+ return (ar.ar_gai_errno);
+}
+
+int
+getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host,
+ size_t hostlen, char *serv, size_t servlen, int flags)
+{
+ struct async *as;
+ struct async_res ar;
+
+ as = getnameinfo_async(sa, salen, host, hostlen, serv, servlen, flags,
+ NULL);
+ if (as == NULL)
+ return ((errno == ENOMEM) ? EAI_MEMORY : EAI_SYSTEM);
+
+ async_run_sync(as, &ar);
+
+ errno = ar.ar_errno;
+ h_errno = ar.ar_h_errno;
+
+ return (ar.ar_gai_errno);
+}
+
+/* XXX see what to do */
+void
+sethostent(int stayopen)
+{
+}
+
+void
+endhostent(void)
+{
+}
+
+/* from getrrsetbyname.c */
+void
+freerrset(struct rrsetinfo *rrset)
+{
+ u_int16_t i;
+
+ if (rrset == NULL)
+ return;
+
+ if (rrset->rri_rdatas) {
+ for (i = 0; i < rrset->rri_nrdatas; i++) {
+ if (rrset->rri_rdatas[i].rdi_data == NULL)
+ break;
+ free(rrset->rri_rdatas[i].rdi_data);
+ }
+ free(rrset->rri_rdatas);
+ }
+
+ if (rrset->rri_sigs) {
+ for (i = 0; i < rrset->rri_nsigs; i++) {
+ if (rrset->rri_sigs[i].rdi_data == NULL)
+ break;
+ free(rrset->rri_sigs[i].rdi_data);
+ }
+ free(rrset->rri_sigs);
+ }
+
+ if (rrset->rri_name)
+ free(rrset->rri_name);
+ free(rrset);
+}
diff --git a/lib/libc/asr/asr_utils.c b/lib/libc/asr/asr_utils.c
new file mode 100644
index 00000000000..2329de71850
--- /dev/null
+++ b/lib/libc/asr/asr_utils.c
@@ -0,0 +1,450 @@
+/* $OpenBSD: asr_utils.c,v 1.1 2012/04/14 09:24:18 eric Exp $ */
+/*
+ * Copyright (c) 2009-2012 Eric Faurot <eric@faurot.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asr.h"
+#include "asr_private.h"
+
+static int dname_check_label(const char*, size_t);
+static ssize_t dname_expand(const unsigned char*, size_t, size_t, size_t*,
+ char *, size_t);
+
+static int unpack_data(struct packed*, void*, size_t);
+static int unpack_u16(struct packed*, uint16_t*);
+static int unpack_u32(struct packed*, uint32_t*);
+static int unpack_inaddr(struct packed*, struct in_addr*);
+static int unpack_in6addr(struct packed*, struct in6_addr*);
+static int unpack_dname(struct packed*, char*, size_t);
+
+static int pack_data(struct packed*, const void*, size_t);
+static int pack_u16(struct packed*, uint16_t);
+static int pack_dname(struct packed*, const char*);
+
+static int
+dname_check_label(const char *s, size_t l)
+{
+ if (l == 0 || l > 63)
+ return (-1);
+
+ for(l--; l; l--, s++)
+ if (!(isalnum(*s) || *s == '_' || *s == '-'))
+ return (-1);
+
+ return (0);
+}
+
+ssize_t
+dname_from_fqdn(const char *str, char *dst, size_t max)
+{
+ ssize_t res;
+ size_t l, n;
+ char *d;
+
+ res = 0;
+
+ /* special case: the root domain */
+ if (str[0] == '.') {
+ if (str[1] != '\0')
+ return (-1);
+ if (dst && max >= 1)
+ *dst = '\0';
+ return (1);
+ }
+
+ for(; *str; str = d + 1) {
+
+ d = strchr(str, '.');
+ if (d == NULL || d == str)
+ return (-1);
+
+ l = (d - str);
+
+ if (dname_check_label(str, l) == -1)
+ return (-1);
+
+ res += l + 1;
+
+ if (dst) {
+ *dst++ = l;
+ max -= 1;
+ n = (l > max) ? max : l;
+ memmove(dst, str, n);
+ max -= n;
+ if (max == 0)
+ dst = NULL;
+ else
+ dst += n;
+ }
+ }
+
+ if (dst)
+ *dst++ = '\0';
+
+ return (res + 1);
+}
+
+static ssize_t
+dname_expand(const unsigned char *data, size_t len, size_t offset,
+ size_t *newoffset, char *dst, size_t max)
+{
+ size_t n, count, end, ptr, start;
+ ssize_t res;
+
+ if (offset >= len)
+ return (-1);
+
+ res = 0;
+ end = start = offset;
+
+ for(; (n = data[offset]); ) {
+ if ((n & 0xc0) == 0xc0) {
+ if (offset + 2 > len)
+ return (-1);
+ ptr = 256 * (n & ~0xc0) + data[offset + 1];
+ if (ptr >= start)
+ return (-1);
+ if (end < offset + 2)
+ end = offset + 2;
+ offset = ptr;
+ continue;
+ }
+ if (offset + n + 1 > len)
+ return (-1);
+
+ if (dname_check_label(data + offset + 1, n) == -1)
+ return (-1);
+
+ /* copy n + at offset+1 */
+ if (dst != NULL && max != 0) {
+ count = (max < n + 1) ? (max) : (n + 1);
+ memmove(dst, data + offset, count);
+ dst += count;
+ max -= count;
+ }
+ res += n + 1;
+ offset += n + 1;
+ if (end < offset)
+ end = offset;
+ }
+ if (end < offset + 1)
+ end = offset + 1;
+
+ if (dst != NULL && max != 0)
+ dst[0] = 0;
+ if (newoffset)
+ *newoffset = end;
+ return (res + 1);
+}
+
+void
+packed_init(struct packed *pack, char *data, size_t len)
+{
+ pack->data = data;
+ pack->len = len;
+ pack->offset = 0;
+ pack->err = NULL;
+}
+
+static int
+unpack_data(struct packed *p, void *data, size_t len)
+{
+ if (p->err)
+ return (-1);
+
+ if (p->len - p->offset < len) {
+ p->err = "too short";
+ return (-1);
+ }
+
+ memmove(data, p->data + p->offset, len);
+ p->offset += len;
+
+ return (0);
+}
+
+static int
+unpack_u16(struct packed *p, uint16_t *u16)
+{
+ if (unpack_data(p, u16, 2) == -1)
+ return (-1);
+
+ *u16 = ntohs(*u16);
+
+ return (0);
+}
+
+static int
+unpack_u32(struct packed *p, uint32_t *u32)
+{
+ if (unpack_data(p, u32, 4) == -1)
+ return (-1);
+
+ *u32 = ntohl(*u32);
+
+ return (0);
+}
+
+static int
+unpack_inaddr(struct packed *p, struct in_addr *a)
+{
+ return (unpack_data(p, a, 4));
+}
+
+static int
+unpack_in6addr(struct packed *p, struct in6_addr *a6)
+{
+ return (unpack_data(p, a6, 16));
+}
+
+static int
+unpack_dname(struct packed *p, char *dst, size_t max)
+{
+ ssize_t e;
+
+ if (p->err)
+ return (-1);
+
+ e = dname_expand(p->data, p->len, p->offset, &p->offset, dst, max);
+ if (e == -1) {
+ p->err = "bad domain name";
+ return (-1);
+ }
+ if (e < 0 || e > MAXDNAME) {
+ p->err = "domain name too long";
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+unpack_header(struct packed *p, struct header *h)
+{
+ if (unpack_data(p, h, HFIXEDSZ) == -1)
+ return (-1);
+
+ h->flags = ntohs(h->flags);
+ h->qdcount = ntohs(h->qdcount);
+ h->ancount = ntohs(h->ancount);
+ h->nscount = ntohs(h->nscount);
+ h->arcount = ntohs(h->arcount);
+
+ return (0);
+}
+
+int
+unpack_query(struct packed *p, struct query *q)
+{
+ unpack_dname(p, q->q_dname, sizeof(q->q_dname));
+ unpack_u16(p, &q->q_type);
+ unpack_u16(p, &q->q_class);
+
+ return (p->err) ? (-1) : (0);
+}
+
+int
+unpack_rr(struct packed *p, struct rr *rr)
+{
+ uint16_t rdlen;
+ size_t save_offset;
+
+ unpack_dname(p, rr->rr_dname, sizeof(rr->rr_dname));
+ unpack_u16(p, &rr->rr_type);
+ unpack_u16(p, &rr->rr_class);
+ unpack_u32(p, &rr->rr_ttl);
+ unpack_u16(p, &rdlen);
+
+ if (p->err)
+ return (-1);
+
+ if (p->len - p->offset < rdlen) {
+ p->err = "too short";
+ return (-1);
+ }
+
+ save_offset = p->offset;
+
+ switch(rr->rr_type) {
+
+ case T_CNAME:
+ unpack_dname(p, rr->rr.cname.cname, sizeof(rr->rr.cname.cname));
+ break;
+
+ case T_MX:
+ unpack_u16(p, &rr->rr.mx.preference);
+ unpack_dname(p, rr->rr.mx.exchange, sizeof(rr->rr.mx.exchange));
+ break;
+
+ case T_NS:
+ unpack_dname(p, rr->rr.ns.nsname, sizeof(rr->rr.ns.nsname));
+ break;
+
+ case T_PTR:
+ unpack_dname(p, rr->rr.ptr.ptrname, sizeof(rr->rr.ptr.ptrname));
+ break;
+
+ case T_SOA:
+ unpack_dname(p, rr->rr.soa.mname, sizeof(rr->rr.soa.mname));
+ unpack_dname(p, rr->rr.soa.rname, sizeof(rr->rr.soa.rname));
+ unpack_u32(p, &rr->rr.soa.serial);
+ unpack_u32(p, &rr->rr.soa.refresh);
+ unpack_u32(p, &rr->rr.soa.retry);
+ unpack_u32(p, &rr->rr.soa.expire);
+ unpack_u32(p, &rr->rr.soa.minimum);
+ break;
+
+ case T_A:
+ if (rr->rr_class != C_IN)
+ goto other;
+ unpack_inaddr(p, &rr->rr.in_a.addr);
+ break;
+
+ case T_AAAA:
+ if (rr->rr_class != C_IN)
+ goto other;
+ unpack_in6addr(p, &rr->rr.in_aaaa.addr6);
+ break;
+ default:
+ other:
+ rr->rr.other.rdata = p->data + p->offset;
+ rr->rr.other.rdlen = rdlen;
+ p->offset += rdlen;
+ }
+
+ if (p->err)
+ return (-1);
+
+ /* make sure that the advertised rdlen is really ok */
+ if (p->offset - save_offset != rdlen)
+ p->err = "bad dlen";
+
+ return (p->err) ? (-1) : (0);
+}
+
+static int
+pack_data(struct packed *p, const void *data, size_t len)
+{
+ if (p->err)
+ return (-1);
+
+ if (p->len < p->offset + len) {
+ p->err = "no space";
+ return (-1);
+ }
+
+ memmove(p->data + p->offset, data, len);
+ p->offset += len;
+
+ return (0);
+}
+
+static int
+pack_u16(struct packed *p, uint16_t v)
+{
+ v = htons(v);
+
+ return (pack_data(p, &v, 2));
+}
+
+static int
+pack_dname(struct packed *p, const char *dname)
+{
+ /* dname compression would be nice to have here.
+ * need additionnal context.
+ */
+ return (pack_data(p, dname, strlen(dname) + 1));
+}
+
+int
+pack_header(struct packed *p, const struct header *h)
+{
+ struct header c;
+
+ c.id = h->id;
+ c.flags = htons(h->flags);
+ c.qdcount = htons(h->qdcount);
+ c.ancount = htons(h->ancount);
+ c.nscount = htons(h->nscount);
+ c.arcount = htons(h->arcount);
+
+ return (pack_data(p, &c, HFIXEDSZ));
+}
+
+int
+pack_query(struct packed *p, uint16_t type, uint16_t class, const char *dname)
+{
+ pack_dname(p, dname);
+ pack_u16(p, type);
+ pack_u16(p, class);
+
+ return (p->err) ? (-1) : (0);
+}
+
+int
+sockaddr_from_str(struct sockaddr *sa, int family, const char *str)
+{
+ struct in_addr ina;
+ struct in6_addr in6a;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+
+ switch (family) {
+ case PF_UNSPEC:
+ if (sockaddr_from_str(sa, PF_INET, str) == 0)
+ return (0);
+ return sockaddr_from_str(sa, PF_INET6, str);
+
+ case PF_INET:
+ if (inet_pton(PF_INET, str, &ina) != 1)
+ return (-1);
+
+ sin = (struct sockaddr_in *)sa;
+ memset(sin, 0, sizeof *sin);
+ sin->sin_len = sizeof(struct sockaddr_in);
+ sin->sin_family = PF_INET;
+ sin->sin_addr.s_addr = ina.s_addr;
+ return (0);
+
+ case PF_INET6:
+ if (inet_pton(PF_INET6, str, &in6a) != 1)
+ return (-1);
+
+ sin6 = (struct sockaddr_in6 *)sa;
+ memset(sin6, 0, sizeof *sin6);
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_family = PF_INET6;
+ sin6->sin6_addr = in6a;
+ return (0);
+
+ default:
+ break;
+ }
+
+ return (-1);
+}
diff --git a/lib/libc/asr/async_resolver.3 b/lib/libc/asr/async_resolver.3
new file mode 100644
index 00000000000..23e46f42656
--- /dev/null
+++ b/lib/libc/asr/async_resolver.3
@@ -0,0 +1,358 @@
+.\" $OpenBSD: async_resolver.3,v 1.1 2012/04/14 09:24:18 eric Exp $
+.\"
+.\" Copyright (c) 2012, Eric Faurot <eric@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: April 14 2012 $
+.Dt ASYN_RESOLVER 3
+.Os
+.Sh NAME
+.Nm async_resolver ,
+.Nm async_resolver_done ,
+.Nm async_run ,
+.Nm async_run_sync ,
+.Nm async_abort ,
+.Nm res_send_async ,
+.Nm res_query_async ,
+.Nm res_search_async ,
+.Nm getrrsetbyname_async ,
+.Nm gethostbyname_async ,
+.Nm gethostbyname2_async ,
+.Nm gethostbyaddr_async ,
+.Nm freehostent ,
+.Nm getnetbyname_async ,
+.Nm getnetbyaddr_async ,
+.Nm freenetent ,
+.Nm getaddrinfo_async ,
+.Nm getnameinfo_async
+.Nd asynchronous resolver functions
+.Sh SYNOPSIS
+.Fd #include <asr.h>
+.Ft struct asr*
+.Fn async_resolver "const char *conf"
+.Ft void
+.Fn async_resolver_done "struct asr *asr"
+.Ft int
+.Fn async_run "struct async *as" "struct async_res *ar"
+.Ft int
+.Fn async_run_sync "struct async *as" "struct async_res *ar"
+.Ft void
+.Fn async_abort "struct async *as"
+.Ft struct async*
+.Fn res_send_async "const unsigned char *pkt" "int pktlen" "unsigned char *ans" "int anslen" "struct asr *asr"
+.Ft struct async*
+.Fn res_query_async "const char *name" "int class" "int type" "unsigned char *ans" "int anslen" "struct asr *asr"
+.Ft struct async*
+.Fn getrrsetbyname_async "const char *hostname" "unsigned int rdclass" "unsigned int rdtype" "unsigned int flags" "struct asr *asr"
+.Ft struct async*
+.Fn gethostbyname_async "const char *name" "struct asr *asr"
+.Ft struct async*
+.Fn gethostbyname2_async "const char *name" "int af" "struct asr *asr"
+.Ft struct async*
+.Fn gethostbyaddr_async "const void *addr" "socklen_t len" "int af" "struct asr *asr"
+.Ft void
+.Fn freehostent "struct hostent *h"
+.Fn getnetbyname_async "const char *name" "struct asr *asr"
+.Ft struct async*
+.Fn getnetbyaddr_async "in_addr_t net" "int type" "struct asr *asr"
+.Ft void
+.Fn freenetent "struct netent *n"
+.Ft struct async*
+.Fn getaddrinfo_async "const char *hostname" "const char *servname" "const struct addrinfo *hints" "struct asr *asr"
+.Ft struct async*
+.Fn getnameinfo_async "const struct sockaddr *sa" "socklen_t salen" "char *host" "size_t hostlen" "char *serv" "size_t servlen" "int flags" "struct asr *asr"
+.Sh DESCRIPTION
+The
+.Nm asr
+functions provide a simple interface for asynchronous address
+resolution and nameserver querying.
+They should be used in place of the classical resolver functions
+of libc when blocking is not desirable.
+.Pp
+The principle of operation is as follows:
+All async requests are made against an
+.Nm asr
+context which basically defines a list of sources to query and a
+strategy to do so.
+The user creates a query through one of the dedicated functions.
+A query is a state-machine that can be run to try to fulfill a
+particular request.
+This is done by calling in a generic API that performs the state
+transitions until it needs to give the control back to the user,
+either because a result is available, or because the next transition
+implies a blocking call (a file descriptor needs to be read from or
+written to).
+The user is responsible for dealing with the situation (fetch the result,
+or wait until the fd conditions are met), and call back into the resolving
+machinery when it is ready to proceed.
+.Pp
+.Fn async_resolver
+is the function used to create a new resolver context.
+The
+.Fa conf
+argument is a path to the resolver configuration file
+as described in
+.Xr resolv.conf 5 .
+If NULL, the default
+.Pa /etc/resolv.conf
+file is used.
+The context tracks file changes to automatically update its configuration
+if needed, replacing the current setup if a valid one can be reloaded from
+the file.
+If configuration file cannot be loaded at context creation time, it falls
+back to the equivalent of:
+.Bd -literal -offset indent
+lookup bind file
+nameserver 127.0.0.1
+.Ed
+.Pp
+If the first character of the
+.Fa conf
+string is a '!', the configuration is read from the rest of the string rather
+than loaded from a file.
+No further update occurs in this case.
+.Pp
+.Fn async_resolver_done
+is used to discard the
+.Fa asr
+context when it is not used anymore.
+Once called, that context is invalidated and cannot be used to create new
+queries.
+Internally, the context is refcounted, so that existing queries made against
+it will be able to complete safely.
+All relevant resources are effectively
+freed when all such queries are cleared.
+.Pp
+The
+.Fn async_run
+function drives the resolving process.
+It runs the
+.Fa as
+asynchronous query until an answer is available, or until it cannot continue
+without blocking.
+The results are returned to the user through the
+.Fa ar
+parameter, which must be a valid pointer to user allocated memory.
+.Fa ar
+is defined as:
+.Bd -literal -offset indent
+struct async_res {
+ int ar_cond;
+ int ar_fd;
+ int ar_timeout;
+
+ int ar_errno;
+ int ar_h_errno;
+ int ar_gai_errno;
+ int ar_rrset_errno;
+
+ int ar_rcode;
+ void *ar_data;
+ int ar_datalen;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sain;
+ struct sockaddr_in6 sain6;
+ } ar_sa;
+
+ char *ar_cname;
+ int ar_count;
+
+ struct addrinfo *ar_addrinfo;
+ struct rrsetinfo *ar_rrsetinfo;
+ struct hostent *ar_hostent;
+ struct netent *ar_netent;
+};
+.Ed
+.Pp
+The function returns one of the following values:
+.Bl -tag -width "ASYNC_YIELD " -offset indent
+.It ASYNC_COND
+The query cannot be processed further until a specific condition on a
+file descriptor becomes true.
+The following members of the
+.Fa ar
+structure are filled:
+.Pp
+.Bl -tag -width "ar_timeout " -compact
+.It Fa ar_cond
+One of ASYNC_READ or ASYNC_WRITE.
+.It Fa ar_fd
+The file descriptor waiting for an IO operation.
+.It Fa ar_timeout
+The timeout, expressed in milliseconds.
+.El
+.Pp
+The caller is expected to call
+.Fn async_run
+again once the condition holds or the timeout expires.
+.It ASYNC_DONE
+The query is completed.
+The members relevant to the actual async query type are set accordingly,
+including error conditions.
+In any case, the query is cleared and its address is invalidated.
+.It ASR_YIELD
+A partial result is available.
+This code is used for async queries that behave as iterators over the result
+set.
+The query-specific members of
+.Fa ar
+are set accordingly and the resolving process can be resumed by calling
+.Fn async_run .
+.El
+.Pp
+The
+.Fn async_run_sync
+function is a wrapper around
+.Fn async_run
+that handles the read/write conditions, thus falling back to a blocking
+interface.
+It only returns partial and complete results through ASYNC_YIELD and ASYNC_DONE
+respectively.
+.Pp
+The
+.Fn async_abort
+function clears a running query.
+It can be called after a partial result has been retrieved or when the query
+is waiting on a file descriptor.
+Note that a completed query is already cleared when
+.Fn async_run
+returns, so
+.Fn async_abort
+must not be called in this case.
+.Pp
+The remaining functions are used to initiate different kinds of query
+on the
+.Fa asr
+resolver context.
+The specific operational details for each of them are described below.
+All functions return NULL if they could not allocate the necessary resources
+to initiate the query.
+All other errors (especially invalid parameters)
+are reported when calling
+.Fn async_run .
+They usually have the same interface as an exisiting resolver function, with
+an additionnal
+.Ar asr
+contex argument, which specifies the context to use for this request.
+If NULL, the default thread-local context is used.
+.Pp
+The
+.Fn res_send_async , res_query_async
+and
+.Fn res_search_async
+functions are asynchronous versions of the standard libc resolver routines.
+Their interface is very similar, except that they take a resolver context as
+last argument, and the return value is found upon completion in the
+.Fa ar_datalen
+member of the response structure.
+In addition, the
+.Fa ar_sa
+union contains the address of the DNS server that sent the response,
+.Fa ar_rcode
+contains the code returned by the server in the DNS responce packet, and
+.Fa ar_count
+contains the number of answer in the packet.
+If no answer buffer is provided, a new one is allocated to fit the response
+and returned as the
+.Fa ar_data
+member. This buffer must be freed by the caller.
+On error, the
+.Fa ar_errno
+and
+.Fa ar_h_errno
+members are set accordingly.
+.Pp
+The
+.Fn getrrsetbyname_async
+function is an asynchronous version of
+.Xr getrrsetbyname 3 .
+Upon completion, the return code is found in
+.Fa ar_rrset_errno
+and the address to the newly allocated result set is set in
+.Fa ar_rrsetinfo .
+As for the blocking function, it must be freed by calling
+.Fn freerrset 3 .
+.Pp
+The
+.Fn gethostbyname_async ,
+.Fn gethostbyname2_async
+and
+.Fn gethostbyaddr_async
+functions provide an asynchronous version of the network host entry functions.
+Upon completion,
+.Ar ar_h_errno
+is set and the resulting hostent address, if found, is set
+in the
+.Ar ar_hostent
+field.
+Note that unlike their blocking counterparts, these functions always return a
+pointer to newly allocated memory.
+Therefore, the pointer must be freed through the new
+.Fn freehostent
+call.
+.Pp
+Similarly, the
+.Fn getnetbyname_async
+and
+.Fn getnetbyaddr_async
+functions provide an asynchronous version of the network entry functions.
+Upon completion,
+.Ar ar_h_errno
+is set and the resulting netent address, if found, is set
+in the
+.Ar ar_netent
+field.
+The memory there is also allocated for the request, and it must be freed by
+.Fn freenetent .
+.Pp
+The
+.Fn getaddrinfo_async
+function is an asynchronous version of the
+.Xr getaddrinfo 3
+call.
+It provides a chain of addrinfo structures with all valid combinations of
+socket address for the given
+.Fa hostname ,
+.Fa servname
+and
+.Fa hints .
+Those three parameters have the same meaning as for the blocking counterpart.
+Upon completion the return code is set in
+.Fa ar_gai_errno .
+The
+.Fa ar_errno
+member may also be set.
+On success, the
+.Fa ar_addrinfo
+member points to a newly allocated list of addrinfo.
+This list must be freed with
+.Xr freeaddrinfo 3 .
+The
+.Fa ar_count
+contains the number of elements in the list.
+.Pp
+.Sh SEE ALSO
+.Xr res_send 3 ,
+.Xr getrrsetbyname 3 ,
+.Xr gethostbyname 3 ,
+.Xr getnetbyname 3 ,
+.Xr getaddrinfo 3 ,
+.Xr getnameinfo 3 ,
+.Xr resolv.conf 5
+.Sh LIMITATIONS
+This DNS resolver implementation doesn't support
+the EDNS0 protocol extension yet.
+.Pp
+The current implementation does not handle YP databases.
diff --git a/lib/libc/asr/getaddrinfo_async.c b/lib/libc/asr/getaddrinfo_async.c
new file mode 100644
index 00000000000..b6c079c7850
--- /dev/null
+++ b/lib/libc/asr/getaddrinfo_async.c
@@ -0,0 +1,500 @@
+/* $OpenBSD: getaddrinfo_async.c,v 1.1 2012/04/14 09:24:18 eric Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <arpa/nameser.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asr.h"
+#include "asr_private.h"
+
+struct match {
+ int family;
+ int socktype;
+ int protocol;
+};
+
+static int getaddrinfo_async_run(struct async *, struct async_res *);
+static int get_port(const char *, const char *, int);
+static int iter_family(struct async *, int);
+static int add_sockaddr(struct async *, struct sockaddr *, const char *);
+
+static const struct match matches[] = {
+ { PF_INET, SOCK_DGRAM, IPPROTO_UDP },
+ { PF_INET, SOCK_STREAM, IPPROTO_TCP },
+ { PF_INET, SOCK_RAW, 0 },
+ { PF_INET6, SOCK_DGRAM, IPPROTO_UDP },
+ { PF_INET6, SOCK_STREAM, IPPROTO_TCP },
+ { PF_INET6, SOCK_RAW, 0 },
+ { -1, 0, 0, },
+};
+
+#define MATCH_FAMILY(a, b) ((a) == matches[(b)].family || (a) == PF_UNSPEC)
+#define MATCH_PROTO(a, b) ((a) == matches[(b)].protocol || (a) == 0)
+/* Do not match SOCK_RAW unless explicitely specified */
+#define MATCH_SOCKTYPE(a, b) ((a) == matches[(b)].socktype || ((a) == 0 && \
+ matches[(b)].socktype != SOCK_RAW))
+
+struct async *
+getaddrinfo_async(const char *hostname, const char *servname,
+ const struct addrinfo *hints, struct asr *asr)
+{
+ struct asr_ctx *ac;
+ struct async *as;
+
+ ac = asr_use_resolver(asr);
+ if ((as = async_new(ac, ASR_GETADDRINFO)) == NULL)
+ goto abort; /* errno set */
+ as->as_run = getaddrinfo_async_run;
+
+ if (hostname && (as->as.ai.hostname = strdup(hostname)) == NULL)
+ goto abort; /* errno set */
+ if (servname && (as->as.ai.servname = strdup(servname)) == NULL)
+ goto abort; /* errno set */
+ if (hints)
+ memmove(&as->as.ai.hints, hints, sizeof *hints);
+ else {
+ memset(&as->as.ai.hints, 0, sizeof as->as.ai.hints);
+ as->as.ai.hints.ai_family = PF_UNSPEC;
+ }
+
+ asr_ctx_unref(ac);
+ return (as);
+ abort:
+ if (as)
+ async_free(as);
+ asr_ctx_unref(ac);
+ return (NULL);
+}
+
+static int
+getaddrinfo_async_run(struct async *as, struct async_res *ar)
+{
+ const char *str;
+ struct addrinfo *ai;
+ int i, family, r;
+ char fqdn[MAXDNAME];
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sain;
+ struct sockaddr_in6 sain6;
+ } sa;
+
+ next:
+ switch(as->as_state) {
+
+ case ASR_STATE_INIT:
+
+ /*
+ * First, make sure the parameters are valid.
+ */
+
+ as->as_count = 0;
+ async_set_state(as, ASR_STATE_HALT);
+ ar->ar_errno = 0;
+ ar->ar_h_errno = NETDB_SUCCESS;
+ ar->ar_gai_errno = 0;
+
+ if (as->as.ai.hostname == NULL &&
+ as->as.ai.servname == NULL) {
+ ar->ar_h_errno = NO_RECOVERY;
+ ar->ar_gai_errno = EAI_NONAME;
+ break;
+ }
+
+ ai = &as->as.ai.hints;
+
+ if (ai->ai_addrlen ||
+ ai->ai_canonname ||
+ ai->ai_addr ||
+ ai->ai_next) {
+ ar->ar_h_errno = NO_RECOVERY;
+ ar->ar_gai_errno = EAI_BADHINTS;
+ break;
+ }
+
+ if (ai->ai_flags & ~AI_MASK ||
+ (ai->ai_flags & AI_CANONNAME && ai->ai_flags & AI_FQDN)) {
+ ar->ar_h_errno = NO_RECOVERY;
+ ar->ar_gai_errno = EAI_BADFLAGS;
+ break;
+ }
+
+ if (ai->ai_family != PF_UNSPEC &&
+ ai->ai_family != PF_INET &&
+ ai->ai_family != PF_INET6) {
+ ar->ar_h_errno = NO_RECOVERY;
+ ar->ar_gai_errno = EAI_FAMILY;
+ break;
+ }
+
+ if (ai->ai_socktype &&
+ ai->ai_socktype != SOCK_DGRAM &&
+ ai->ai_socktype != SOCK_STREAM &&
+ ai->ai_socktype != SOCK_RAW) {
+ ar->ar_h_errno = NO_RECOVERY;
+ ar->ar_gai_errno = EAI_SOCKTYPE;
+ break;
+ }
+
+ if (ai->ai_protocol &&
+ ai->ai_protocol != IPPROTO_UDP &&
+ ai->ai_protocol != IPPROTO_TCP) {
+ ar->ar_h_errno = NO_RECOVERY;
+ ar->ar_gai_errno = EAI_PROTOCOL;
+ break;
+ }
+
+ if (ai->ai_socktype == SOCK_RAW &&
+ as->as.ai.servname != NULL) {
+ ar->ar_h_errno = NO_RECOVERY;
+ ar->ar_gai_errno = EAI_SERVICE;
+ break;
+ }
+
+ /* Make sure there is at least a valid combination */
+ for (i = 0; matches[i].family != -1; i++)
+ if (MATCH_FAMILY(ai->ai_family, i) &&
+ MATCH_SOCKTYPE(ai->ai_socktype, i) &&
+ MATCH_PROTO(ai->ai_protocol, i))
+ break;
+ if (matches[i].family == -1) {
+ ar->ar_h_errno = NO_RECOVERY;
+ ar->ar_gai_errno = EAI_BADHINTS;
+ break;
+ }
+
+ if (as->as.ai.servname) {
+ as->as.ai.port_udp = get_port(as->as.ai.servname,
+ "udp", as->as.ai.hints.ai_flags & AI_NUMERICSERV);
+ as->as.ai.port_tcp = get_port(as->as.ai.servname,
+ "tcp", as->as.ai.hints.ai_flags & AI_NUMERICSERV);
+ if (as->as.ai.port_tcp < 0 || as->as.ai.port_udp < 0) {
+ ar->ar_h_errno = NO_RECOVERY;
+ ar->ar_gai_errno = EAI_SERVICE;
+ break;
+ }
+ }
+
+ /* If hostname is NULL, use local address */
+ if (as->as.ai.hostname == NULL) {
+ for(family = iter_family(as, 1);
+ family != -1;
+ family = iter_family(as, 0)) {
+ /*
+ * We could use statically built sockaddrs for
+ * those, rather than parsing over and over.
+ */
+ if (family == PF_INET)
+ str = (ai->ai_flags & AI_PASSIVE) ? \
+ "0.0.0.0" : "127.0.0.1";
+ else /* PF_INET6 */
+ str = (ai->ai_flags & AI_PASSIVE) ? \
+ "::" : "::1";
+ /* This can't fail */
+ sockaddr_from_str(&sa.sa, family, str);
+ if ((r = add_sockaddr(as, &sa.sa, NULL))) {
+ ar->ar_errno = errno;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ ar->ar_gai_errno = r;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ }
+ if (ar->ar_gai_errno == 0 && as->as_count == 0) {
+ ar->ar_h_errno = NO_DATA;
+ ar->ar_gai_errno = EAI_NODATA;
+ }
+ break;
+ }
+
+ /* Try numeric addresses first */
+ for(family = iter_family(as, 1);
+ family != -1;
+ family = iter_family(as, 0)) {
+
+ if (sockaddr_from_str(&sa.sa, family,
+ as->as.ai.hostname) == -1)
+ continue;
+
+ if ((r = add_sockaddr(as, &sa.sa, NULL))) {
+ ar->ar_errno = errno;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ ar->ar_gai_errno = r;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ if (ar->ar_gai_errno || as->as_count)
+ break;
+
+ if (ai->ai_flags & AI_NUMERICHOST) {
+ ar->ar_h_errno = NO_RECOVERY;
+ ar->ar_gai_errno = EAI_FAIL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ /* Starting domain lookup */
+ async_set_state(as, ASR_STATE_SEARCH_DOMAIN);
+ break;
+
+ case ASR_STATE_SEARCH_DOMAIN:
+
+ r = asr_iter_domain(as, as->as.ai.hostname, fqdn, sizeof(fqdn));
+ if (r == -1) {
+ async_set_state(as, ASR_STATE_NOT_FOUND);
+ break;
+ }
+ if (r > sizeof(fqdn)) {
+ ar->ar_errno = EINVAL;
+ ar->ar_h_errno = NO_RECOVERY;
+ ar->ar_gai_errno = EAI_OVERFLOW;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ /*
+ * Create a subquery to lookup the host addresses.
+ * We use the special hostaddr_async() API, which has the
+ * nice property of honoring the "lookup" and "family" keyword
+ * in the configuration, thus returning the right address
+ * families in the right order, and thus fixing the current
+ * getaddrinfo() feature documented in the BUGS section of
+ * resolver.conf(5).
+ */
+ as->as.ai.subq = hostaddr_async_ctx(fqdn,
+ as->as.ai.hints.ai_family, as->as.ai.hints.ai_flags,
+ as->as_ctx);
+ if (as->as.ai.subq == NULL) {
+ ar->ar_errno = errno;
+ if (errno == EINVAL) {
+ ar->ar_h_errno = NO_RECOVERY;
+ ar->ar_gai_errno = EAI_FAIL;
+ } else {
+ ar->ar_h_errno = NETDB_INTERNAL;
+ ar->ar_gai_errno = EAI_MEMORY;
+ }
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ async_set_state(as, ASR_STATE_LOOKUP_DOMAIN);
+ break;
+
+ case ASR_STATE_LOOKUP_DOMAIN:
+
+ /* Run the subquery */
+ if ((r = async_run(as->as.ai.subq, ar)) == ASYNC_COND)
+ return (ASYNC_COND);
+
+ /* Got one more address, use it to extend the result list. */
+ if (r == ASYNC_YIELD) {
+ if ((r = add_sockaddr(as, &ar->ar_sa.sa,
+ ar->ar_cname))) {
+ ar->ar_errno = errno;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ ar->ar_gai_errno = r;
+ async_set_state(as, ASR_STATE_HALT);
+ }
+ if (ar->ar_cname)
+ free(ar->ar_cname);
+ break;
+ }
+
+ /*
+ * The subquery is done. Stop there if we have at least one
+ * answer.
+ */
+ as->as.ai.subq = NULL;
+ if (ar->ar_count) {
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ /*
+ * No anwser for this domain, but we might be suggested to
+ * try again later, so remember this. Then search the next
+ * domain.
+ */
+ if (ar->ar_gai_errno == EAI_AGAIN)
+ as->as.ai.flags |= ASYNC_AGAIN;
+ async_set_state(as, ASR_STATE_SEARCH_DOMAIN);
+ break;
+
+ case ASR_STATE_NOT_FOUND:
+
+ /*
+ * No result found. Maybe we can try again.
+ */
+ ar->ar_errno = 0;
+ if (as->as.ai.flags & ASYNC_AGAIN) {
+ ar->ar_h_errno = TRY_AGAIN;
+ ar->ar_gai_errno = EAI_AGAIN;
+ } else {
+ ar->ar_h_errno = NO_DATA;
+ ar->ar_gai_errno = EAI_NODATA;
+ }
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+
+ case ASR_STATE_HALT:
+
+ /* Set the results. */
+
+ if (ar->ar_gai_errno == 0) {
+ ar->ar_count = as->as_count;
+ ar->ar_addrinfo = as->as.ai.aifirst;
+ as->as.ai.aifirst = NULL;
+ } else {
+ ar->ar_count = 0;
+ ar->ar_addrinfo = NULL;
+ }
+ return (ASYNC_DONE);
+
+ default:
+ ar->ar_errno = EOPNOTSUPP;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ ar->ar_gai_errno = EAI_SYSTEM;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ goto next;
+}
+
+/*
+ * Retreive the port number for the service name "servname" and
+ * the protocol "proto".
+ */
+static int
+get_port(const char *servname, const char *proto, int numonly)
+{
+ struct servent se;
+ struct servent_data sed;
+ int port, r;
+ const char* e;
+
+ if (servname == NULL)
+ return (0);
+
+ e = NULL;
+ port = strtonum(servname, 0, USHRT_MAX, &e);
+ if (e == NULL)
+ return (port);
+ if (errno == ERANGE)
+ return (-2); /* invalid */
+ if (numonly)
+ return (-2);
+
+ memset(&sed, 0, sizeof(sed));
+ r = getservbyname_r(servname, proto, &se, &sed);
+ port = ntohs(se.s_port);
+ endservent_r(&sed);
+
+ if (r == -1)
+ return (-1); /* not found */
+
+ return (port);
+}
+
+/*
+ * Iterate over the address families that are to be queried. Use the
+ * list on the async context, unless a specific family was given in hints.
+ */
+static int
+iter_family(struct async *as, int first)
+{
+ if (first) {
+ as->as_family_idx = 0;
+ if (as->as.ai.hints.ai_family != PF_UNSPEC)
+ return as->as.ai.hints.ai_family;
+ return AS_FAMILY(as);
+ }
+
+ if (as->as.ai.hints.ai_family != PF_UNSPEC)
+ return (-1);
+
+ as->as_family_idx++;
+
+ return AS_FAMILY(as);
+}
+
+/*
+ * Use the sockaddr at "sa" to extend the result list on the "as" context,
+ * with the specified canonical name "cname". This function adds one
+ * entry per protocol/socktype match.
+ */
+static int
+add_sockaddr(struct async *as, struct sockaddr *sa, const char *cname)
+{
+ struct addrinfo *ai;
+ int i, port;
+
+ for(i = 0; matches[i].family != -1; i++) {
+ if (matches[i].family != sa->sa_family ||
+ !MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) ||
+ !MATCH_PROTO(as->as.ai.hints.ai_protocol, i))
+ continue;
+
+ if (matches[i].protocol == IPPROTO_TCP)
+ port = as->as.ai.port_tcp;
+ else if (matches[i].protocol == IPPROTO_UDP)
+ port = as->as.ai.port_udp;
+ else
+ port = 0;
+
+ ai = calloc(1, sizeof(*ai) + sa->sa_len);
+ if (ai == NULL)
+ return (EAI_MEMORY);
+ ai->ai_family = sa->sa_family;
+ ai->ai_socktype = matches[i].socktype;
+ ai->ai_protocol = matches[i].protocol;
+ ai->ai_addrlen = sa->sa_len;
+ ai->ai_addr = (void*)(ai + 1);
+ if (cname &&
+ as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) {
+ if ((ai->ai_canonname = strdup(cname)) == NULL) {
+ free(ai);
+ return (EAI_MEMORY);
+ }
+ }
+ memmove(ai->ai_addr, sa, sa->sa_len);
+ if (sa->sa_family == PF_INET)
+ ((struct sockaddr_in *)ai->ai_addr)->sin_port =
+ htons(port);
+ else if (sa->sa_family == PF_INET6)
+ ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port =
+ htons(port);
+
+ if (as->as.ai.aifirst == NULL)
+ as->as.ai.aifirst = ai;
+ if (as->as.ai.ailast)
+ as->as.ai.ailast->ai_next = ai;
+ as->as.ai.ailast = ai;
+ as->as_count += 1;
+ }
+
+ return (0);
+}
diff --git a/lib/libc/asr/gethostnamadr_async.c b/lib/libc/asr/gethostnamadr_async.c
new file mode 100644
index 00000000000..f4a777d39ac
--- /dev/null
+++ b/lib/libc/asr/gethostnamadr_async.c
@@ -0,0 +1,592 @@
+/* $OpenBSD: gethostnamadr_async.c,v 1.1 2012/04/14 09:24:18 eric Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asr.h"
+#include "asr_private.h"
+
+
+#define MAXALIASES 16
+#define MAXADDRS 16
+
+ssize_t addr_as_fqdn(const char *, int, char *, size_t);
+
+static int gethostnamadr_async_run(struct async *, struct async_res *);
+static struct hostent *hostent_alloc(int);
+static int hostent_set_cname(struct hostent *, const char *, int);
+static int hostent_add_alias(struct hostent *, const char *, int);
+static int hostent_add_addr(struct hostent *, const void *, int);
+static int hostent_file_match(FILE *, int, int, int, const char *,
+ char *, char **, int);
+static int hostent_from_packet(struct hostent *, int, char *, size_t);
+
+struct async *
+gethostbyname_async(const char *name, struct asr *asr)
+{
+ return gethostbyname2_async(name, AF_INET, asr);
+}
+
+struct async *
+gethostbyname2_async(const char *name, int af, struct asr *asr)
+{
+ struct asr_ctx *ac;
+ struct async *as;
+
+ /* the original segfaults */
+ if (name == NULL) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ ac = asr_use_resolver(asr);
+ if ((as = async_new(ac, ASR_GETHOSTBYNAME)) == NULL)
+ goto abort; /* errno set */
+ as->as_run = gethostnamadr_async_run;
+
+ as->as.hostnamadr.family = af;
+ if (af == AF_INET)
+ as->as.hostnamadr.addrlen = INADDRSZ;
+ else if (af == AF_INET6)
+ as->as.hostnamadr.addrlen = IN6ADDRSZ;
+ as->as.hostnamadr.name = strdup(name);
+ if (as->as.hostnamadr.name == NULL)
+ goto abort; /* errno set */
+
+ asr_ctx_unref(ac);
+ return (as);
+
+ abort:
+ if (as)
+ async_free(as);
+ asr_ctx_unref(ac);
+ return (NULL);
+}
+
+struct async *
+gethostbyaddr_async(const void *addr, socklen_t len, int af, struct asr *asr)
+{
+ struct asr_ctx *ac;
+ struct async *as;
+
+ ac = asr_use_resolver(asr);
+ as = gethostbyaddr_async_ctx(addr, len, af, ac);
+ asr_ctx_unref(ac);
+
+ return (as);
+}
+
+struct async *
+gethostbyaddr_async_ctx(const void *addr, socklen_t len, int af,
+ struct asr_ctx *ac)
+{
+ struct async *as;
+
+ if ((as = async_new(ac, ASR_GETHOSTBYADDR)) == NULL)
+ goto abort; /* errno set */
+ as->as_run = gethostnamadr_async_run;
+
+ as->as.hostnamadr.family = af;
+ as->as.hostnamadr.addrlen = len;
+ if (len > 0)
+ memmove(as->as.hostnamadr.addr, addr, (len > 16) ? 16 : len);
+
+ return (as);
+
+ abort:
+ if (as)
+ async_free(as);
+ return (NULL);
+}
+
+static int
+gethostnamadr_async_run(struct async *as, struct async_res *ar)
+{
+ struct hostent *e;
+ int i, n, r, type;
+ FILE *f;
+ char *toks[MAXTOKEN], addr[16], dname[MAXDNAME], *data;
+
+ next:
+ switch(as->as_state) {
+
+ case ASR_STATE_INIT:
+
+ if (as->as.hostnamadr.family != AF_INET &&
+ as->as.hostnamadr.family != AF_INET6) {
+ ar->ar_h_errno = NETDB_INTERNAL;
+ ar->ar_errno = EAFNOSUPPORT;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if ((as->as.hostnamadr.family == AF_INET &&
+ as->as.hostnamadr.addrlen != INADDRSZ) ||
+ (as->as.hostnamadr.family == AF_INET6 &&
+ as->as.hostnamadr.addrlen != IN6ADDRSZ)) {
+ ar->ar_h_errno = NETDB_INTERNAL;
+ ar->ar_errno = EINVAL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if (as->as_type == ASR_GETHOSTBYNAME)
+ async_set_state(as, ASR_STATE_NEXT_DOMAIN);
+ else
+ async_set_state(as, ASR_STATE_NEXT_DB);
+ break;
+
+ case ASR_STATE_NEXT_DOMAIN:
+
+ r = asr_iter_domain(as, as->as.hostnamadr.name, dname, sizeof(dname));
+ if (r == -1) {
+ async_set_state(as, ASR_STATE_NOT_FOUND);
+ break;
+ }
+
+ if (as->as.hostnamadr.dname)
+ free(as->as.hostnamadr.dname);
+ if ((as->as.hostnamadr.dname = strdup(dname)) == NULL) {
+ ar->ar_h_errno = NETDB_INTERNAL;
+ ar->ar_errno = errno;
+ async_set_state(as, ASR_STATE_HALT);
+ }
+
+ as->as_db_idx = 0;
+ async_set_state(as, ASR_STATE_NEXT_DB);
+ break;
+
+ case ASR_STATE_NEXT_DB:
+
+ if (asr_iter_db(as) == -1) {
+ if (as->as_type == ASR_GETHOSTBYNAME)
+ async_set_state(as, ASR_STATE_NEXT_DOMAIN);
+ else
+ async_set_state(as, ASR_STATE_NOT_FOUND);
+ break;
+ }
+
+ switch(AS_DB(as)) {
+
+ case ASR_DB_DNS:
+
+ /* Create a subquery to do the DNS lookup */
+
+ if (as->as_type == ASR_GETHOSTBYNAME) {
+ type = (as->as.hostnamadr.family == AF_INET) ?
+ T_A : T_AAAA;
+ as->as.hostnamadr.subq = res_query_async_ctx(
+ as->as.hostnamadr.dname,
+ C_IN, type, NULL, 0, as->as_ctx);
+ } else {
+ addr_as_fqdn(as->as.hostnamadr.addr,
+ as->as.hostnamadr.family,
+ dname, sizeof(dname));
+ as->as.hostnamadr.subq = res_query_async_ctx(
+ dname, C_IN, T_PTR, NULL, 0, as->as_ctx);
+ }
+
+ if (as->as.hostnamadr.subq == NULL) {
+ ar->ar_errno = errno;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ async_set_state(as, ASR_STATE_SUBQUERY);
+ break;
+
+ case ASR_DB_FILE:
+
+ /* Try to find a match in the host file */
+
+ if ((f = fopen(as->as_ctx->ac_hostfile, "r")) == NULL)
+ break;
+
+ if (as->as_type == ASR_GETHOSTBYNAME)
+ data = as->as.hostnamadr.dname;
+ else
+ data = as->as.hostnamadr.addr;
+
+ if (( n = hostent_file_match(f, as->as_type,
+ as->as.hostnamadr.family,
+ as->as.hostnamadr.addrlen, data, addr,
+ toks, MAXTOKEN)) == -1) {
+ fclose(f);
+ break;
+ }
+ e = hostent_alloc(as->as.hostnamadr.family);
+ if (e == NULL) {
+ ar->ar_errno = errno;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ hostent_set_cname(e, toks[1], 0);
+ for (i = 2; i < n; i ++)
+ hostent_add_alias(e, toks[i], 0);
+ hostent_add_addr(e, addr, e->h_length);
+ fclose(f);
+
+ ar->ar_h_errno = NETDB_SUCCESS;
+ ar->ar_hostent = e;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ break;
+
+ case ASR_STATE_SUBQUERY:
+
+ /* Run the DNS subquery. */
+
+ if ((r = async_run(as->as.hostnamadr.subq, ar)) == ASYNC_COND)
+ return (ASYNC_COND);
+
+ /* Done. */
+ as->as.hostnamadr.subq = NULL;
+
+ if (ar->ar_datalen == -1) {
+ async_set_state(as, ASR_STATE_NEXT_DB);
+ break;
+ }
+
+ /* If we got a packet but no anwser, use the next DB. */
+ if (ar->ar_count == 0) {
+ free(ar->ar_data);
+ async_set_state(as, ASR_STATE_NEXT_DB);
+ break;
+ }
+
+ /* Read the hostent from the packet. */
+ if ((e = hostent_alloc(as->as.hostnamadr.family)) == NULL) {
+ ar->ar_errno = errno;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ free(ar->ar_data);
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if (as->as_type == ASR_GETHOSTBYADDR) {
+ e->h_addr_list[0] = malloc(as->as.hostnamadr.addrlen);
+ if (e->h_addr_list[0])
+ memmove(e->h_addr_list[0],
+ as->as.hostnamadr.addr,
+ as->as.hostnamadr.addrlen);
+ }
+
+ hostent_from_packet(e, as->as_type, ar->ar_data,
+ ar->ar_datalen);
+ free(ar->ar_data);
+
+ /*
+ * No address found in the dns packet. The blocking version
+ * reports this as an error.
+ */
+ if (as->as_type == ASR_GETHOSTBYNAME &&
+ e->h_addr_list[0] == NULL) {
+ freehostent(e);
+ async_set_state(as, ASR_STATE_NEXT_DB);
+ break;
+ }
+
+ ar->ar_h_errno = NETDB_SUCCESS;
+ ar->ar_hostent = e;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+
+ case ASR_STATE_NOT_FOUND:
+ ar->ar_errno = 0;
+ ar->ar_h_errno = HOST_NOT_FOUND;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+
+ case ASR_STATE_HALT:
+ if (ar->ar_h_errno)
+ ar->ar_hostent = NULL;
+ else
+ ar->ar_errno = 0;
+ return (ASYNC_DONE);
+
+ default:
+ ar->ar_errno = EOPNOTSUPP;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ ar->ar_gai_errno = EAI_SYSTEM;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ goto next;
+}
+
+/*
+ * Lookup the first matching entry in the hostfile, either by address or by
+ * name. Split the matching line into tokens in the "token" array and return
+ * the number of tokens.
+ */
+static int
+hostent_file_match(FILE *f, int type, int family, int len, const char *data,
+ char *addr, char **tokens, int ntokens)
+{
+ int n, i;
+
+ for(;;) {
+ n = asr_parse_namedb_line(f, tokens, MAXTOKEN);
+ if (n == -1)
+ return (-1);
+
+ if (type == ASR_GETHOSTBYNAME) {
+ for (i = 1; i < n; i++) {
+ if (strcasecmp(data, tokens[i]))
+ continue;
+ if (inet_pton(family, tokens[0], addr) == 1)
+ return (n);
+ }
+ continue;
+ }
+
+ if (inet_pton(family, tokens[0], addr) == 1)
+ if (memcmp(addr, data, len) == 0)
+ return (n);
+ }
+}
+
+/*
+ * Fill the hostent from the given DNS packet.
+ */
+static int
+hostent_from_packet(struct hostent *h, int action, char *pkt, size_t pktlen)
+{
+ struct packed p;
+ struct header hdr;
+ struct query q;
+ struct rr rr;
+ int r;
+
+ packed_init(&p, pkt, pktlen);
+ unpack_header(&p, &hdr);
+ for(; hdr.qdcount; hdr.qdcount--)
+ unpack_query(&p, &q);
+ for(; hdr.ancount; hdr.ancount--) {
+ unpack_rr(&p, &rr);
+ if (rr.rr_class != C_IN)
+ continue;
+ switch (rr.rr_type) {
+
+ case T_CNAME:
+ if (action == ASR_GETHOSTBYNAME)
+ r = hostent_add_alias(h, rr.rr_dname, 1);
+ else
+ r = hostent_set_cname(h, rr.rr_dname, 1);
+ break;
+
+ case T_PTR:
+ if (action != ASR_GETHOSTBYADDR)
+ continue;
+ r = hostent_set_cname(h, rr.rr.ptr.ptrname, 1);
+ /* XXX See if we need MULTI_PTRS_ARE_ALIASES */
+ break;
+
+ case T_A:
+ if (h->h_addrtype != AF_INET)
+ break;
+ r = hostent_set_cname(h, rr.rr_dname, 1);
+ r = hostent_add_addr(h, &rr.rr.in_a.addr, 4);
+ break;
+
+ case T_AAAA:
+ if (h->h_addrtype != AF_INET6)
+ break;
+ r = hostent_set_cname(h, rr.rr_dname, 1);
+ r = hostent_add_addr(h, &rr.rr.in_aaaa.addr6, 16);
+ break;
+ }
+ }
+
+ return (0);
+}
+
+static struct hostent *
+hostent_alloc(int family)
+{
+ struct hostent *h;
+
+ h = calloc(1, sizeof *h);
+ if (h == NULL)
+ return (NULL);
+
+ h->h_aliases = calloc(MAXALIASES, sizeof *h->h_aliases);
+ h->h_addr_list = calloc(MAXADDRS, sizeof *h->h_addr_list);
+ if (h->h_aliases == NULL || h->h_addr_list == NULL) {
+ freehostent(h);
+ return (NULL);
+ }
+ h->h_addrtype = family;
+ h->h_length = (family == AF_INET) ? 4 : 16;
+
+ return (h);
+}
+
+static int
+hostent_set_cname(struct hostent *h, const char *name, int isdname)
+{
+ char buf[MAXDNAME];
+
+ if (h->h_name)
+ return (0);
+
+ if (isdname) {
+ asr_strdname(name, buf, sizeof buf);
+ buf[strlen(buf) - 1] = '\0';
+ h->h_name = strdup(buf);
+ } else {
+ h->h_name = strdup(name);
+ }
+ if (h->h_name == NULL)
+ return (-1);
+
+ return (0);
+}
+
+static int
+hostent_add_alias(struct hostent *h, const char *name, int isdname)
+{
+ char buf[MAXDNAME];
+ size_t i;
+
+ for (i = 0; i < MAXALIASES; i++)
+ if (h->h_aliases[i] == NULL)
+ break;
+ if (i == MAXALIASES)
+ return (0);
+
+ if (isdname) {
+ asr_strdname(name, buf, sizeof buf);
+ buf[strlen(buf)-1] = '\0';
+ h->h_aliases[i] = strdup(buf);
+ } else {
+ h->h_aliases[i] = strdup(name);
+ }
+ if (h->h_aliases[i] == NULL)
+ return (-1);
+
+ return (0);
+}
+
+static int
+hostent_add_addr(struct hostent *h, const void *addr, int size)
+{
+ int i;
+
+ for (i = 0; i < MAXADDRS; i++)
+ if (h->h_addr_list[i] == NULL)
+ break;
+ if (i == MAXADDRS)
+ return (0);
+
+ h->h_addr_list[i] = malloc(size);
+ if (h->h_addr_list[i] == NULL)
+ return (-1);
+ memmove(h->h_addr_list[i], addr, size);
+
+ return (0);
+}
+
+void
+freehostent(struct hostent *h)
+{
+ char **c;
+
+ free(h->h_name);
+ for (c = h->h_aliases; *c; c++)
+ free(*c);
+ free(h->h_aliases);
+ for (c = h->h_addr_list; *c; c++)
+ free(*c);
+ free(h->h_addr_list);
+ free(h);
+}
+
+
+ssize_t
+addr_as_fqdn(const char *addr, int family, char *dst, size_t max)
+{
+ const struct in6_addr *in6_addr;
+ in_addr_t in_addr;
+
+ switch (family) {
+ case AF_INET:
+ in_addr = ntohl(*((in_addr_t *)addr));
+ snprintf(dst, max,
+ "%d.%d.%d.%d.in-addr.arpa.",
+ in_addr & 0xff,
+ (in_addr >> 8) & 0xff,
+ (in_addr >> 16) & 0xff,
+ (in_addr >> 24) & 0xff);
+ break;
+ case AF_INET6:
+ in6_addr = (struct in6_addr *)addr;
+ snprintf(dst, max,
+ "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x."
+ "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x."
+ "ip6.arpa.",
+ in6_addr->s6_addr[15] & 0xf,
+ (in6_addr->s6_addr[15] >> 4) & 0xf,
+ in6_addr->s6_addr[14] & 0xf,
+ (in6_addr->s6_addr[14] >> 4) & 0xf,
+ in6_addr->s6_addr[13] & 0xf,
+ (in6_addr->s6_addr[13] >> 4) & 0xf,
+ in6_addr->s6_addr[12] & 0xf,
+ (in6_addr->s6_addr[12] >> 4) & 0xf,
+ in6_addr->s6_addr[11] & 0xf,
+ (in6_addr->s6_addr[11] >> 4) & 0xf,
+ in6_addr->s6_addr[10] & 0xf,
+ (in6_addr->s6_addr[10] >> 4) & 0xf,
+ in6_addr->s6_addr[9] & 0xf,
+ (in6_addr->s6_addr[9] >> 4) & 0xf,
+ in6_addr->s6_addr[8] & 0xf,
+ (in6_addr->s6_addr[8] >> 4) & 0xf,
+ in6_addr->s6_addr[7] & 0xf,
+ (in6_addr->s6_addr[7] >> 4) & 0xf,
+ in6_addr->s6_addr[6] & 0xf,
+ (in6_addr->s6_addr[6] >> 4) & 0xf,
+ in6_addr->s6_addr[5] & 0xf,
+ (in6_addr->s6_addr[5] >> 4) & 0xf,
+ in6_addr->s6_addr[4] & 0xf,
+ (in6_addr->s6_addr[4] >> 4) & 0xf,
+ in6_addr->s6_addr[3] & 0xf,
+ (in6_addr->s6_addr[3] >> 4) & 0xf,
+ in6_addr->s6_addr[2] & 0xf,
+ (in6_addr->s6_addr[2] >> 4) & 0xf,
+ in6_addr->s6_addr[1] & 0xf,
+ (in6_addr->s6_addr[1] >> 4) & 0xf,
+ in6_addr->s6_addr[0] & 0xf,
+ (in6_addr->s6_addr[0] >> 4) & 0xf);
+ break;
+ default:
+ return (-1);
+ }
+ return (0);
+}
diff --git a/lib/libc/asr/getnameinfo_async.c b/lib/libc/asr/getnameinfo_async.c
new file mode 100644
index 00000000000..1ba853d0a7d
--- /dev/null
+++ b/lib/libc/asr/getnameinfo_async.c
@@ -0,0 +1,261 @@
+/* $OpenBSD: getnameinfo_async.c,v 1.1 2012/04/14 09:24:18 eric Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asr.h"
+#include "asr_private.h"
+
+static int getnameinfo_async_run(struct async *, struct async_res *);
+static int _servname(struct async *);
+static int _numerichost(struct async *);
+
+struct async *
+getnameinfo_async(const struct sockaddr *sa, socklen_t slen, char *host,
+ size_t hostlen, char *serv, size_t servlen, int flags, struct asr *asr)
+{
+ struct asr_ctx *ac;
+ struct async *as;
+
+ ac = asr_use_resolver(asr);
+ if ((as = async_new(ac, ASR_GETNAMEINFO)) == NULL)
+ goto abort; /* errno set */
+ as->as_run = getnameinfo_async_run;
+
+ if (sa->sa_family == AF_INET)
+ memmove(&as->as.ni.sa.sa, sa, sizeof (as->as.ni.sa.sain));
+ else if (sa->sa_family == AF_INET6)
+ memmove(&as->as.ni.sa.sa, sa, sizeof (as->as.ni.sa.sain6));
+
+ as->as.ni.sa.sa.sa_len = slen;
+ as->as.ni.hostname = host;
+ as->as.ni.hostnamelen = hostlen;
+ as->as.ni.servname = serv;
+ as->as.ni.servnamelen = servlen;
+ as->as.ni.flags = flags;
+
+ asr_ctx_unref(ac);
+ return (as);
+
+ abort:
+ if (as)
+ async_free(as);
+ asr_ctx_unref(ac);
+ return (NULL);
+}
+
+static int
+getnameinfo_async_run(struct async *as, struct async_res *ar)
+{
+ void *addr;
+ socklen_t addrlen;
+ int r;
+
+ next:
+ switch(as->as_state) {
+
+ case ASR_STATE_INIT:
+
+ /* Make sure the parameters are all valid. */
+
+ if (as->as.ni.sa.sa.sa_family != AF_INET &&
+ as->as.ni.sa.sa.sa_family != AF_INET6) {
+ ar->ar_gai_errno = EAI_FAMILY;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if ((as->as.ni.sa.sa.sa_family == AF_INET &&
+ (as->as.ni.sa.sa.sa_len != sizeof (as->as.ni.sa.sain))) ||
+ (as->as.ni.sa.sa.sa_family == AF_INET6 &&
+ (as->as.ni.sa.sa.sa_len != sizeof (as->as.ni.sa.sain6)))) {
+ ar->ar_gai_errno = EAI_FAIL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ /* Set the service name first, if needed. */
+ if (_servname(as) == -1) {
+ ar->ar_gai_errno = EAI_OVERFLOW;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if (as->as.ni.hostname == NULL || as->as.ni.hostnamelen == 0) {
+ ar->ar_gai_errno = 0;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if (as->as.ni.flags & NI_NUMERICHOST) {
+ if (_numerichost(as) == -1) {
+ ar->ar_errno = errno;
+ if (ar->ar_errno == ENOMEM)
+ ar->ar_gai_errno = EAI_MEMORY;
+ else if (ar->ar_errno == ENOSPC)
+ ar->ar_gai_errno = EAI_OVERFLOW;
+ else
+ ar->ar_gai_errno = EAI_SYSTEM;
+ } else
+ ar->ar_gai_errno = 0;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if (as->as.ni.sa.sa.sa_family == AF_INET) {
+ addrlen = sizeof(as->as.ni.sa.sain.sin_addr);
+ addr = &as->as.ni.sa.sain.sin_addr;
+ } else {
+ addrlen = sizeof(as->as.ni.sa.sain6.sin6_addr);
+ addr = &as->as.ni.sa.sain6.sin6_addr;
+ }
+
+ /*
+ * Create a subquery to lookup the address.
+ */
+ as->as.ni.subq = gethostbyaddr_async_ctx(addr, addrlen,
+ as->as.ni.sa.sa.sa_family,
+ as->as_ctx);
+ if (as->as.ni.subq == NULL) {
+ ar->ar_errno = errno;
+ ar->ar_gai_errno = EAI_MEMORY;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ async_set_state(as, ASR_STATE_SUBQUERY);
+ break;
+
+ case ASR_STATE_SUBQUERY:
+
+ if ((r = async_run(as->as.ni.subq, ar)) == ASYNC_COND)
+ return (ASYNC_COND);
+
+ /*
+ * Request done.
+ */
+ as->as.ni.subq = NULL;
+
+ if (ar->ar_hostent == NULL) {
+ if (as->as.ni.flags & NI_NAMEREQD) {
+ ar->ar_gai_errno = EAI_NONAME;
+ } else if (_numerichost(as) == -1) {
+ ar->ar_errno = errno;
+ if (ar->ar_errno == ENOMEM)
+ ar->ar_gai_errno = EAI_MEMORY;
+ else if (ar->ar_errno == ENOSPC)
+ ar->ar_gai_errno = EAI_OVERFLOW;
+ else
+ ar->ar_gai_errno = EAI_SYSTEM;
+ } else
+ ar->ar_gai_errno = 0;
+ } else {
+ if (strlcpy(as->as.ni.hostname,
+ ar->ar_hostent->h_name,
+ as->as.ni.hostnamelen) >= as->as.ni.hostnamelen)
+ ar->ar_gai_errno = EAI_OVERFLOW;
+ else
+ ar->ar_gai_errno = 0;
+ freehostent(ar->ar_hostent);
+ }
+
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+
+ case ASR_STATE_HALT:
+ return (ASYNC_DONE);
+
+ default:
+ ar->ar_errno = EOPNOTSUPP;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ ar->ar_gai_errno = EAI_SYSTEM;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ goto next;
+}
+
+
+/*
+ * Set the service name on the result buffer is not NULL.
+ * return (-1) if the buffer is too small.
+ */
+static int
+_servname(struct async *as)
+{
+ struct servent s;
+ struct servent_data sd;
+ int port, r;
+ char *buf = as->as.ni.servname;
+ size_t buflen = as->as.ni.servnamelen;
+
+ if (as->as.ni.servname == NULL || as->as.ni.servnamelen == 0)
+ return (0);
+
+ if (as->as.ni.sa.sa.sa_family == AF_INET)
+ port = as->as.ni.sa.sain.sin_port;
+ else
+ port = as->as.ni.sa.sain6.sin6_port;
+
+ if (!(as->as.ni.flags & NI_NUMERICSERV)) {
+ memset(&sd, 0, sizeof (sd));
+ if (getservbyport_r(port,
+ (as->as.ni.flags & NI_DGRAM) ? "udp" : "tcp",
+ &s, &sd) != -1) {
+ r = strlcpy(buf, s.s_name, buflen) >= buflen;
+ endservent_r(&sd);
+ return (r ? -1 : 0);
+ }
+ }
+
+ r = snprintf(buf, buflen, "%u", ntohs(port));
+ if (r == -1 || r >= buflen)
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * Write the numeric address
+ */
+static int
+_numerichost(struct async *as)
+{
+ void *addr;
+ char *buf = as->as.ni.hostname;
+ size_t buflen = as->as.ni.hostnamelen;
+
+ if (as->as.ni.sa.sa.sa_family == AF_INET)
+ addr = &as->as.ni.sa.sain.sin_addr;
+ else
+ addr = &as->as.ni.sa.sain6.sin6_addr;
+
+ if (inet_ntop(as->as.ni.sa.sa.sa_family, addr, buf, buflen) == NULL)
+ /* errno set */
+ return (-1);
+
+ return (0);
+}
diff --git a/lib/libc/asr/getnetnamadr_async.c b/lib/libc/asr/getnetnamadr_async.c
new file mode 100644
index 00000000000..0bc8a36cfb0
--- /dev/null
+++ b/lib/libc/asr/getnetnamadr_async.c
@@ -0,0 +1,418 @@
+/* $OpenBSD: getnetnamadr_async.c,v 1.1 2012/04/14 09:24:18 eric Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asr.h"
+#include "asr_private.h"
+
+#define MAXALIASES 16
+
+ssize_t addr_as_fqdn(const char *, int, char *, size_t);
+
+static int getnetnamadr_async_run(struct async *, struct async_res *);
+static struct netent *netent_alloc(int);
+static int netent_set_cname(struct netent *, const char *, int);
+static int netent_add_alias(struct netent *, const char *, int);
+static int netent_file_match(FILE *, int, const char *, char **, int);
+static int netent_from_packet(struct netent *, int, char *, size_t);
+
+struct async *
+getnetbyname_async(const char *name, struct asr *asr)
+{
+ struct asr_ctx *ac;
+ struct async *as;
+
+ /* The current resolver segfaults. */
+ if (name == NULL) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ ac = asr_use_resolver(asr);
+ if ((as = async_new(ac, ASR_GETNETBYNAME)) == NULL)
+ goto abort; /* errno set */
+ as->as_run = getnetnamadr_async_run;
+
+ as->as.netnamadr.family = AF_INET;
+ as->as.netnamadr.name = strdup(name);
+ if (as->as.netnamadr.name == NULL)
+ goto abort; /* errno set */
+
+ asr_ctx_unref(ac);
+ return (as);
+
+ abort:
+ if (as)
+ async_free(as);
+ asr_ctx_unref(ac);
+ return (NULL);
+}
+
+struct async *
+getnetbyaddr_async(in_addr_t net, int family, struct asr *asr)
+{
+ struct asr_ctx *ac;
+ struct async *as;
+
+ ac = asr_use_resolver(asr);
+ if ((as = async_new(ac, ASR_GETNETBYADDR)) == NULL)
+ goto abort; /* errno set */
+ as->as_run = getnetnamadr_async_run;
+
+ as->as.netnamadr.family = family;
+ as->as.netnamadr.addr = net;
+
+ asr_ctx_unref(ac);
+ return (as);
+
+ abort:
+ if (as)
+ async_free(as);
+ asr_ctx_unref(ac);
+ return (NULL);
+}
+
+static int
+getnetnamadr_async_run(struct async *as, struct async_res *ar)
+{
+ struct netent *e;
+ int i, n, r, type;
+ FILE *f;
+ char *toks[MAXTOKEN], dname[MAXDNAME], *name, *data;
+ in_addr_t in;
+
+ next:
+ switch(as->as_state) {
+
+ case ASR_STATE_INIT:
+
+ if (as->as.netnamadr.family != AF_INET) {
+ ar->ar_h_errno = NETDB_INTERNAL;
+ ar->ar_errno = EAFNOSUPPORT;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ async_set_state(as, ASR_STATE_NEXT_DB);
+ break;
+
+ case ASR_STATE_NEXT_DB:
+
+ if (asr_iter_db(as) == -1) {
+ async_set_state(as, ASR_STATE_NOT_FOUND);
+ break;
+ }
+
+ switch(AS_DB(as)) {
+ case ASR_DB_DNS:
+
+ if (as->as_type == ASR_GETNETBYNAME) {
+ type = T_A;
+ /*
+ * I think we want to do the former, but our
+ * resolver is doing the following, so let's
+ * preserve bugward-compatibility there.
+ */
+ type = T_PTR;
+ name = as->as.netnamadr.name;
+ as->as.netnamadr.subq = res_search_async_ctx(
+ name, C_IN, type, NULL, 0, as->as_ctx);
+ } else {
+ type = T_PTR;
+ name = dname;
+
+ in = htonl(as->as.netnamadr.addr);
+ addr_as_fqdn((char*)&in,
+ as->as.netnamadr.family,
+ dname, sizeof(dname));
+ as->as.netnamadr.subq = res_query_async_ctx(
+ name, C_IN, type, NULL, 0, as->as_ctx);
+ }
+
+ if (as->as.netnamadr.subq == NULL) {
+ ar->ar_errno = errno;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ async_set_state(as, ASR_STATE_HALT);
+ }
+ async_set_state(as, ASR_STATE_SUBQUERY);
+ break;
+
+ case ASR_DB_FILE:
+
+ if ((f = fopen("/etc/networks", "r")) == NULL)
+ break;
+
+ if (as->as_type == ASR_GETNETBYNAME)
+ data = as->as.netnamadr.name;
+ else
+ data = (void*)&as->as.netnamadr.addr;
+ n = netent_file_match(f, as->as_type, data, toks,
+ MAXTOKEN);
+ if (n == -1) {
+ fclose(f);
+ break;
+ }
+ e = netent_alloc(as->as.netnamadr.family);
+ if (e == NULL) {
+ ar->ar_errno = errno;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ netent_set_cname(e, toks[0], 0);
+ for (i = 2; i < n; i ++)
+ netent_add_alias(e, toks[i], 0);
+ e->n_net = inet_network(toks[1]);
+ fclose(f);
+
+ ar->ar_h_errno = NETDB_SUCCESS;
+ ar->ar_netent = e;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ break;
+
+ case ASR_STATE_SUBQUERY:
+
+ if ((r = async_run(as->as.netnamadr.subq, ar)) == ASYNC_COND)
+ return (ASYNC_COND);
+ as->as.netnamadr.subq = NULL;
+
+ if (ar->ar_datalen == -1) {
+ async_set_state(as, ASR_STATE_NEXT_DB);
+ break;
+ }
+
+ /* Got packet, but no answer */
+ if (ar->ar_count == 0) {
+ free(ar->ar_data);
+ async_set_state(as, ASR_STATE_NEXT_DB);
+ break;
+ }
+
+ if ((e = netent_alloc(as->as.netnamadr.family)) == NULL) {
+ ar->ar_errno = errno;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ free(ar->ar_data);
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if (as->as_type == ASR_GETNETBYADDR)
+ e->n_net = as->as.netnamadr.addr;
+
+ netent_from_packet(e, as->as_type, ar->ar_data, ar->ar_datalen);
+ free(ar->ar_data);
+
+ /*
+ * No address found in the dns packet. The blocking version
+ * reports this as an error.
+ */
+ if (as->as_type == ASR_GETNETBYNAME && e->n_net == 0) {
+ /* XXX wrong */
+ freenetent(e);
+ async_set_state(as, ASR_STATE_NEXT_DB);
+ } else {
+ ar->ar_h_errno = NETDB_SUCCESS;
+ ar->ar_netent = e;
+ async_set_state(as, ASR_STATE_HALT);
+ }
+ break;
+
+ case ASR_STATE_NOT_FOUND:
+
+ ar->ar_errno = 0;
+ ar->ar_h_errno = HOST_NOT_FOUND;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+
+ case ASR_STATE_HALT:
+
+ if (ar->ar_h_errno)
+ ar->ar_netent = NULL;
+ else
+ ar->ar_errno = 0;
+ return (ASYNC_DONE);
+
+ default:
+ ar->ar_errno = EOPNOTSUPP;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ ar->ar_gai_errno = EAI_SYSTEM;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ goto next;
+}
+
+static int
+netent_file_match(FILE *f, int type, const char *data, char **tokens,
+ int ntokens)
+{
+ int n, i;
+ in_addr_t net;
+
+ for(;;) {
+ n = asr_parse_namedb_line(f, tokens, MAXTOKEN);
+ if (n == -1)
+ return (-1);
+
+ if (type == ASR_GETNETBYADDR) {
+ net = inet_network(tokens[1]);
+ if (memcmp(&net, data, sizeof net) == 0)
+ return (n);
+ } else {
+ for (i = 0; i < n; i++) {
+ if (i == 1)
+ continue;
+ if (strcasecmp(data, tokens[i]))
+ continue;
+ return (n);
+ }
+ }
+ }
+}
+
+static int
+netent_from_packet(struct netent *n, int action, char *pkt, size_t pktlen)
+{
+ struct packed p;
+ struct header hdr;
+ struct query q;
+ struct rr rr;
+ int r;
+
+ packed_init(&p, pkt, pktlen);
+ unpack_header(&p, &hdr);
+ for(; hdr.qdcount; hdr.qdcount--)
+ unpack_query(&p, &q);
+
+ for(; hdr.ancount; hdr.ancount--) {
+ unpack_rr(&p, &rr);
+ if (rr.rr_class != C_IN)
+ continue;
+ switch (rr.rr_type) {
+ case T_CNAME:
+ if (action == ASR_GETNETBYNAME)
+ r = netent_add_alias(n, rr.rr_dname, 1);
+ else
+ r = netent_set_cname(n, rr.rr_dname, 1);
+ break;
+ case T_PTR:
+ if (action != ASR_GETNETBYADDR)
+ continue;
+ r = netent_set_cname(n, rr.rr.ptr.ptrname, 1);
+ /* XXX See if we need to have MULTI_PTRS_ARE_ALIASES */
+ break;
+ case T_A:
+ if (n->n_addrtype != AF_INET)
+ break;
+ r = netent_set_cname(n, rr.rr_dname, 1);
+ n->n_net = ntohl(rr.rr.in_a.addr.s_addr);
+ break;
+ }
+ }
+
+ return (0);
+}
+
+static struct netent *
+netent_alloc(int family)
+{
+ struct netent *n;
+
+ n = calloc(1, sizeof *n);
+ if (n == NULL)
+ return (NULL);
+
+ n->n_aliases = calloc(MAXALIASES, sizeof *n->n_aliases);
+ if (n->n_aliases == NULL) {
+ freenetent(n);
+ return (NULL);
+ }
+ n->n_addrtype = family;
+
+ return (n);
+}
+
+static int
+netent_set_cname(struct netent *n, const char *name, int isdname)
+{
+ char buf[MAXDNAME];
+
+ if (n->n_name)
+ return (0);
+
+ if (isdname) {
+ asr_strdname(name, buf, sizeof buf);
+ buf[strlen(buf) - 1] = '\0';
+ n->n_name = strdup(buf);
+ } else {
+ n->n_name = strdup(name);
+ }
+ if (n->n_name == NULL)
+ return (-1);
+
+ return (0);
+}
+
+static int
+netent_add_alias(struct netent *n, const char *name, int isdname)
+{
+ char buf[MAXDNAME];
+ size_t i;
+
+ for (i = 0; i < MAXALIASES; i++)
+ if (n->n_aliases[i] == NULL)
+ break;
+ if (i == MAXALIASES)
+ return (0);
+
+ if (isdname) {
+ asr_strdname(name, buf, sizeof buf);
+ buf[strlen(buf)-1] = '\0';
+ n->n_aliases[i] = strdup(buf);
+ } else {
+ n->n_aliases[i] = strdup(name);
+ }
+ if (n->n_aliases[i] == NULL)
+ return (-1);
+
+ return (0);
+}
+
+void
+freenetent(struct netent *n)
+{
+ char **c;
+
+ free(n->n_name);
+ for (c = n->n_aliases; *c; c++)
+ free(*c);
+ free(n->n_aliases);
+ free(n);
+}
diff --git a/lib/libc/asr/getrrsetbyname_async.c b/lib/libc/asr/getrrsetbyname_async.c
new file mode 100644
index 00000000000..10707f8518c
--- /dev/null
+++ b/lib/libc/asr/getrrsetbyname_async.c
@@ -0,0 +1,590 @@
+/* $OpenBSD: getrrsetbyname_async.c,v 1.1 2012/04/14 09:24:18 eric Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asr.h"
+#include "asr_private.h"
+
+static int getrrsetbyname_async_run(struct async *, struct async_res *);
+static void get_response(struct async_res *, const char *, int);
+
+struct async *
+getrrsetbyname_async(const char *hostname, unsigned int rdclass,
+ unsigned int rdtype, unsigned int flags, struct asr *asr)
+{
+ struct asr_ctx *ac;
+ struct async *as;
+
+ ac = asr_use_resolver(asr);
+ if ((as = async_new(ac, ASR_GETRRSETBYNAME)) == NULL)
+ goto abort; /* errno set */
+ as->as_run = getrrsetbyname_async_run;
+
+ as->as.rrset.flags = flags;
+ as->as.rrset.class = rdclass;
+ as->as.rrset.type = rdtype;
+ as->as.rrset.name = strdup(hostname);
+ if (as->as.rrset.name == NULL)
+ goto abort; /* errno set */
+
+ asr_ctx_unref(ac);
+ return (as);
+ abort:
+ if (as)
+ async_free(as);
+
+ asr_ctx_unref(ac);
+ return (NULL);
+}
+
+static int
+getrrsetbyname_async_run(struct async *as, struct async_res *ar)
+{
+ next:
+ switch(as->as_state) {
+
+ case ASR_STATE_INIT:
+
+ /* Check for invalid class and type. */
+ if (as->as.rrset.class > 0xffff || as->as.rrset.type > 0xffff) {
+ ar->ar_rrset_errno = ERRSET_INVAL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ /* Do not allow queries of class or type ANY. */
+ if (as->as.rrset.class == 0xff || as->as.rrset.type == 0xff) {
+ ar->ar_rrset_errno = ERRSET_INVAL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ /* Do not allow flags yet, unimplemented. */
+ if (as->as.rrset.flags) {
+ ar->ar_rrset_errno = ERRSET_INVAL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ /* Create a delegate the lookup to a subquery. */
+ as->as.rrset.subq = res_query_async_ctx(
+ as->as.rrset.name,
+ as->as.rrset.class,
+ as->as.rrset.type,
+ NULL, 0, as->as_ctx);
+ if (as->as.rrset.subq == NULL) {
+ ar->ar_rrset_errno = ERRSET_FAIL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ async_set_state(as, ASR_STATE_SUBQUERY);
+ break;
+
+ case ASR_STATE_SUBQUERY:
+
+ if ((async_run(as->as.rrset.subq, ar)) == ASYNC_COND)
+ return (ASYNC_COND);
+
+ as->as.rrset.subq = NULL;
+
+ /* No packet received.*/
+ if (ar->ar_datalen == -1) {
+ switch(ar->ar_h_errno) {
+ case HOST_NOT_FOUND:
+ ar->ar_rrset_errno = ERRSET_NONAME;
+ break;
+ case NO_DATA:
+ ar->ar_rrset_errno = ERRSET_NODATA;
+ break;
+ default:
+ ar->ar_rrset_errno = ERRSET_FAIL;
+ break;
+ }
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ /* Got a packet but no answer. */
+ if (ar->ar_count == 0) {
+ free(ar->ar_data);
+ switch(ar->ar_rcode) {
+ case NXDOMAIN:
+ ar->ar_rrset_errno = ERRSET_NONAME;
+ break;
+ case NOERROR:
+ ar->ar_rrset_errno = ERRSET_NODATA;
+ break;
+ default:
+ ar->ar_rrset_errno = ERRSET_FAIL;
+ break;
+ }
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ get_response(ar, ar->ar_data, ar->ar_datalen);
+ free(ar->ar_data);
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+
+ case ASR_STATE_HALT:
+
+ if (ar->ar_rrset_errno)
+ ar->ar_rrsetinfo = NULL;
+ return (ASYNC_DONE);
+
+ default:
+ ar->ar_errno = EOPNOTSUPP;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ ar->ar_gai_errno = EAI_SYSTEM;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ goto next;
+}
+
+/* The rest of this file is taken from the orignal implementation. */
+
+/* $OpenBSD: getrrsetbyname_async.c,v 1.1 2012/04/14 09:24:18 eric Exp $ */
+
+/*
+ * Copyright (c) 2001 Jakob Schlyter. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Portions Copyright (c) 1999-2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define MAXPACKET 1024*64
+
+struct dns_query {
+ char *name;
+ u_int16_t type;
+ u_int16_t class;
+ struct dns_query *next;
+};
+
+struct dns_rr {
+ char *name;
+ u_int16_t type;
+ u_int16_t class;
+ u_int16_t ttl;
+ u_int16_t size;
+ void *rdata;
+ struct dns_rr *next;
+};
+
+struct dns_response {
+ HEADER header;
+ struct dns_query *query;
+ struct dns_rr *answer;
+ struct dns_rr *authority;
+ struct dns_rr *additional;
+};
+
+static struct dns_response *parse_dns_response(const u_char *, int);
+static struct dns_query *parse_dns_qsection(const u_char *, int,
+ const u_char **, int);
+static struct dns_rr *parse_dns_rrsection(const u_char *, int, const u_char **,
+ int);
+
+static void free_dns_query(struct dns_query *);
+static void free_dns_rr(struct dns_rr *);
+static void free_dns_response(struct dns_response *);
+
+static int count_dns_rr(struct dns_rr *, u_int16_t, u_int16_t);
+
+static void
+get_response(struct async_res *ar, const char *pkt, int pktlen)
+{
+ struct rrsetinfo *rrset = NULL;
+ struct dns_response *response = NULL;
+ struct dns_rr *rr;
+ struct rdatainfo *rdata;
+ unsigned int index_ans, index_sig;
+
+ /* parse result */
+ response = parse_dns_response(pkt, pktlen);
+ if (response == NULL) {
+ ar->ar_rrset_errno = ERRSET_FAIL;
+ goto fail;
+ }
+
+ if (response->header.qdcount != 1) {
+ ar->ar_rrset_errno = ERRSET_FAIL;
+ goto fail;
+ }
+
+ /* initialize rrset */
+ rrset = calloc(1, sizeof(struct rrsetinfo));
+ if (rrset == NULL) {
+ ar->ar_rrset_errno = ERRSET_NOMEMORY;
+ goto fail;
+ }
+ rrset->rri_rdclass = response->query->class;
+ rrset->rri_rdtype = response->query->type;
+ rrset->rri_ttl = response->answer->ttl;
+ rrset->rri_nrdatas = response->header.ancount;
+
+ /* check for authenticated data */
+ if (response->header.ad == 1)
+ rrset->rri_flags |= RRSET_VALIDATED;
+
+ /* copy name from answer section */
+ rrset->rri_name = strdup(response->answer->name);
+ if (rrset->rri_name == NULL) {
+ ar->ar_rrset_errno = ERRSET_NOMEMORY;
+ goto fail;
+ }
+
+ /* count answers */
+ rrset->rri_nrdatas = count_dns_rr(response->answer, rrset->rri_rdclass,
+ rrset->rri_rdtype);
+ rrset->rri_nsigs = count_dns_rr(response->answer, rrset->rri_rdclass,
+ T_RRSIG);
+
+ /* allocate memory for answers */
+ rrset->rri_rdatas = calloc(rrset->rri_nrdatas,
+ sizeof(struct rdatainfo));
+ if (rrset->rri_rdatas == NULL) {
+ ar->ar_rrset_errno = ERRSET_NOMEMORY;
+ goto fail;
+ }
+
+ /* allocate memory for signatures */
+ rrset->rri_sigs = calloc(rrset->rri_nsigs, sizeof(struct rdatainfo));
+ if (rrset->rri_sigs == NULL) {
+ ar->ar_rrset_errno = ERRSET_NOMEMORY;
+ goto fail;
+ }
+
+ /* copy answers & signatures */
+ for (rr = response->answer, index_ans = 0, index_sig = 0;
+ rr; rr = rr->next) {
+
+ rdata = NULL;
+
+ if (rr->class == rrset->rri_rdclass &&
+ rr->type == rrset->rri_rdtype)
+ rdata = &rrset->rri_rdatas[index_ans++];
+
+ if (rr->class == rrset->rri_rdclass &&
+ rr->type == T_RRSIG)
+ rdata = &rrset->rri_sigs[index_sig++];
+
+ if (rdata) {
+ rdata->rdi_length = rr->size;
+ rdata->rdi_data = malloc(rr->size);
+
+ if (rdata->rdi_data == NULL) {
+ ar->ar_rrset_errno = ERRSET_NOMEMORY;
+ goto fail;
+ }
+ memcpy(rdata->rdi_data, rr->rdata, rr->size);
+ }
+ }
+ free_dns_response(response);
+
+ ar->ar_rrsetinfo = rrset;
+ ar->ar_rrset_errno = ERRSET_SUCCESS;
+ return;
+
+fail:
+ if (rrset != NULL)
+ freerrset(rrset);
+ if (response != NULL)
+ free_dns_response(response);
+}
+
+/*
+ * DNS response parsing routines
+ */
+static struct dns_response *
+parse_dns_response(const u_char *answer, int size)
+{
+ struct dns_response *resp;
+ const u_char *cp;
+
+ /* allocate memory for the response */
+ resp = calloc(1, sizeof(*resp));
+ if (resp == NULL)
+ return (NULL);
+
+ /* initialize current pointer */
+ cp = answer;
+
+ /* copy header */
+ memcpy(&resp->header, cp, HFIXEDSZ);
+ cp += HFIXEDSZ;
+
+ /* fix header byte order */
+ resp->header.qdcount = ntohs(resp->header.qdcount);
+ resp->header.ancount = ntohs(resp->header.ancount);
+ resp->header.nscount = ntohs(resp->header.nscount);
+ resp->header.arcount = ntohs(resp->header.arcount);
+
+ /* there must be at least one query */
+ if (resp->header.qdcount < 1) {
+ free_dns_response(resp);
+ return (NULL);
+ }
+
+ /* parse query section */
+ resp->query = parse_dns_qsection(answer, size, &cp,
+ resp->header.qdcount);
+ if (resp->header.qdcount && resp->query == NULL) {
+ free_dns_response(resp);
+ return (NULL);
+ }
+
+ /* parse answer section */
+ resp->answer = parse_dns_rrsection(answer, size, &cp,
+ resp->header.ancount);
+ if (resp->header.ancount && resp->answer == NULL) {
+ free_dns_response(resp);
+ return (NULL);
+ }
+
+ /* parse authority section */
+ resp->authority = parse_dns_rrsection(answer, size, &cp,
+ resp->header.nscount);
+ if (resp->header.nscount && resp->authority == NULL) {
+ free_dns_response(resp);
+ return (NULL);
+ }
+
+ /* parse additional section */
+ resp->additional = parse_dns_rrsection(answer, size, &cp,
+ resp->header.arcount);
+ if (resp->header.arcount && resp->additional == NULL) {
+ free_dns_response(resp);
+ return (NULL);
+ }
+
+ return (resp);
+}
+
+static struct dns_query *
+parse_dns_qsection(const u_char *answer, int size, const u_char **cp, int count)
+{
+ struct dns_query *head, *curr, *prev;
+ int i, length;
+ char name[MAXDNAME];
+
+ for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) {
+
+ /* allocate and initialize struct */
+ curr = calloc(1, sizeof(struct dns_query));
+ if (curr == NULL) {
+ free_dns_query(head);
+ return (NULL);
+ }
+ if (head == NULL)
+ head = curr;
+ if (prev != NULL)
+ prev->next = curr;
+
+ /* name */
+ length = dn_expand(answer, answer + size, *cp, name,
+ sizeof(name));
+ if (length < 0) {
+ free_dns_query(head);
+ return (NULL);
+ }
+ curr->name = strdup(name);
+ if (curr->name == NULL) {
+ free_dns_query(head);
+ return (NULL);
+ }
+ *cp += length;
+
+ /* type */
+ curr->type = _getshort(*cp);
+ *cp += INT16SZ;
+
+ /* class */
+ curr->class = _getshort(*cp);
+ *cp += INT16SZ;
+ }
+
+ return (head);
+}
+
+static struct dns_rr *
+parse_dns_rrsection(const u_char *answer, int size, const u_char **cp,
+ int count)
+{
+ struct dns_rr *head, *curr, *prev;
+ int i, length;
+ char name[MAXDNAME];
+
+ for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) {
+
+ /* allocate and initialize struct */
+ curr = calloc(1, sizeof(struct dns_rr));
+ if (curr == NULL) {
+ free_dns_rr(head);
+ return (NULL);
+ }
+ if (head == NULL)
+ head = curr;
+ if (prev != NULL)
+ prev->next = curr;
+
+ /* name */
+ length = dn_expand(answer, answer + size, *cp, name,
+ sizeof(name));
+ if (length < 0) {
+ free_dns_rr(head);
+ return (NULL);
+ }
+ curr->name = strdup(name);
+ if (curr->name == NULL) {
+ free_dns_rr(head);
+ return (NULL);
+ }
+ *cp += length;
+
+ /* type */
+ curr->type = _getshort(*cp);
+ *cp += INT16SZ;
+
+ /* class */
+ curr->class = _getshort(*cp);
+ *cp += INT16SZ;
+
+ /* ttl */
+ curr->ttl = _getlong(*cp);
+ *cp += INT32SZ;
+
+ /* rdata size */
+ curr->size = _getshort(*cp);
+ *cp += INT16SZ;
+
+ /* rdata itself */
+ curr->rdata = malloc(curr->size);
+ if (curr->rdata == NULL) {
+ free_dns_rr(head);
+ return (NULL);
+ }
+ memcpy(curr->rdata, *cp, curr->size);
+ *cp += curr->size;
+ }
+
+ return (head);
+}
+
+static void
+free_dns_query(struct dns_query *p)
+{
+ if (p == NULL)
+ return;
+
+ if (p->name)
+ free(p->name);
+ free_dns_query(p->next);
+ free(p);
+}
+
+static void
+free_dns_rr(struct dns_rr *p)
+{
+ if (p == NULL)
+ return;
+
+ if (p->name)
+ free(p->name);
+ if (p->rdata)
+ free(p->rdata);
+ free_dns_rr(p->next);
+ free(p);
+}
+
+static void
+free_dns_response(struct dns_response *p)
+{
+ if (p == NULL)
+ return;
+
+ free_dns_query(p->query);
+ free_dns_rr(p->answer);
+ free_dns_rr(p->authority);
+ free_dns_rr(p->additional);
+ free(p);
+}
+
+static int
+count_dns_rr(struct dns_rr *p, u_int16_t class, u_int16_t type)
+{
+ int n = 0;
+
+ while(p) {
+ if (p->class == class && p->type == type)
+ n++;
+ p = p->next;
+ }
+
+ return (n);
+}
diff --git a/lib/libc/asr/hostaddr_async.c b/lib/libc/asr/hostaddr_async.c
new file mode 100644
index 00000000000..ecea827186a
--- /dev/null
+++ b/lib/libc/asr/hostaddr_async.c
@@ -0,0 +1,342 @@
+/* $OpenBSD: hostaddr_async.c,v 1.1 2012/04/14 09:24:18 eric Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <arpa/nameser.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asr.h"
+#include "asr_private.h"
+
+static int hostaddr_async_run(struct async *, struct async_res *);
+static int sockaddr_from_rr(struct sockaddr *, struct rr *);
+
+/*
+ * This API function allows to iterate over host addresses, for the given
+ * family which, must be AF_INET, AF_INET6, or AF_UNSPEC (in which case
+ * the family lookup list from the resolver is used). The strategy is to
+ * return all addresses found for the first DB that returned at least one
+ * address.
+ *
+ * Flags can be 0, or one of AI_CANONNAME or AI_FQDN. If set, the
+ * canonical name will be returned along with the address.
+ */
+struct async *
+hostaddr_async_ctx(const char *name, int family, int flags, struct asr_ctx *ac)
+{
+ struct async *as;
+ char buf[MAXDNAME];
+
+#ifdef DEBUG
+ asr_printf("asr: hostaddr_async_ctx(\"%s\", %i)\n", name, family);
+#endif
+ if (asr_domcat(name, NULL, buf, sizeof buf) == 0) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if ((as = async_new(ac, ASR_HOSTADDR)) == NULL)
+ goto err; /* errno set */
+ as->as_run = hostaddr_async_run;
+
+ as->as.host.aiflags = flags;
+ as->as.host.family = family;
+ as->as.host.name = strdup(buf);
+ if (as->as.host.name == NULL)
+ goto err; /* errno set */
+
+ return (as);
+ err:
+ if (as)
+ async_free(as);
+ return (NULL);
+}
+
+static int
+hostaddr_async_run(struct async *as, struct async_res *ar)
+{
+ struct packed p;
+ struct header h;
+ struct query q;
+ struct rr rr;
+ char buf[MAXDNAME], *c, *name;
+ int i, n, family, type, r;
+
+ next:
+ switch(as->as_state) {
+
+ case ASR_STATE_INIT:
+
+ if (as->as.host.family != AF_INET &&
+ as->as.host.family != AF_INET6 &&
+ as->as.host.family != AF_UNSPEC) {
+ ar->ar_gai_errno = EAI_FAMILY;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ as->as_count = 0;
+ as->as_db_idx = 0;
+ async_set_state(as, ASR_STATE_NEXT_DB);
+ break;
+
+ case ASR_STATE_NEXT_FAMILY:
+
+ as->as_family_idx += 1;
+ if (as->as.host.family != AF_UNSPEC || AS_FAMILY(as) == -1) {
+ /* The family was specified, or we have tried all
+ * families with this DB.
+ */
+ if (as->as_count) {
+ ar->ar_errno = 0;
+ ar->ar_gai_errno = 0;
+ async_set_state(as, ASR_STATE_HALT);
+ } else
+ async_set_state(as, ASR_STATE_NEXT_DB);
+ break;
+ }
+
+ async_set_state(as, ASR_STATE_LOOKUP_FAMILY);
+ break;
+
+ case ASR_STATE_LOOKUP_FAMILY:
+
+ async_set_state(as, ASR_STATE_SAME_DB);
+ break;
+
+ case ASR_STATE_NEXT_DB:
+
+ if (asr_iter_db(as) == -1) {
+ async_set_state(as, ASR_STATE_NOT_FOUND);
+ break;
+ }
+ as->as_family_idx = 0;
+ /* FALLTHROUGH */
+
+ case ASR_STATE_SAME_DB:
+
+ /* Query the current DB again. */
+
+ switch(AS_DB(as)) {
+ case ASR_DB_DNS:
+
+ family = (as->as.host.family == AF_UNSPEC) ?
+ AS_FAMILY(as) : as->as.host.family;
+ type = (family == AF_INET6) ? T_AAAA : T_A;
+ name = as->as.host.name;
+ as->as.host.subq = res_query_async_ctx(name, C_IN,
+ type, NULL, 0, as->as_ctx);
+ if (as->as.host.subq == NULL) {
+ ar->ar_errno = errno;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ if (errno == ENOMEM)
+ ar->ar_gai_errno = EAI_MEMORY;
+ else
+ ar->ar_gai_errno = EAI_FAIL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ async_set_state(as, ASR_STATE_SUBQUERY);
+ break;
+
+ case ASR_DB_FILE:
+
+ as->as.host.file = fopen(as->as_ctx->ac_hostfile, "r");
+ if (as->as.host.file == NULL)
+ async_set_state(as, ASR_STATE_NEXT_DB);
+ else
+ async_set_state(as, ASR_STATE_READ_FILE);
+ break;
+
+ default:
+ async_set_state(as, ASR_STATE_NEXT_DB);
+ }
+ break;
+
+ case ASR_STATE_SUBQUERY:
+ if ((r = async_run(as->as.host.subq, ar)) == ASYNC_COND)
+ return (ASYNC_COND);
+ as->as.host.subq = NULL;
+
+ if (ar->ar_datalen == -1) {
+ async_set_state(as, ASR_STATE_NEXT_DB);
+ break;
+ }
+
+ as->as.host.pkt = ar->ar_data;
+ as->as.host.pktlen = ar->ar_datalen;
+ packed_init(&p, as->as.host.pkt, as->as.host.pktlen);
+ unpack_header(&p, &h);
+ for(; h.qdcount; h.qdcount--)
+ unpack_query(&p, &q);
+ as->as.host.pktpos = p.offset;
+ as->as.host.ancount = h.ancount;
+ as->as.host.class = q.q_class;
+ as->as.host.type = q.q_type;
+ async_set_state(as, ASR_STATE_READ_RR);
+ break;
+
+ case ASR_STATE_READ_RR:
+
+ /* When done with this NS, try with next family */
+ if (as->as.host.ancount == 0) {
+ free(as->as.host.pkt);
+ as->as.host.pkt = NULL;
+ async_set_state(as, ASR_STATE_NEXT_FAMILY);
+ break;
+ }
+
+ /* Continue reading the packet where we left it. */
+ packed_init(&p, as->as.host.pkt, as->as.host.pktlen);
+ p.offset = as->as.host.pktpos;
+ unpack_rr(&p, &rr);
+ as->as.host.pktpos = p.offset;
+ as->as.host.ancount -= 1;
+ if (rr.rr_type == as->as.host.type &&
+ rr.rr_class == as->as.host.class) {
+ as->as_count += 1;
+ ar->ar_count = as->as_count;
+ sockaddr_from_rr(&ar->ar_sa.sa, &rr);
+ if (as->as.host.aiflags & AI_CANONNAME)
+ c = asr_strdname(rr.rr_dname, buf,
+ sizeof buf);
+ else if (as->as.host.aiflags & AI_FQDN) {
+ strlcpy(buf, as->as.host.name, sizeof buf);
+ c = buf;
+ } else
+ c = NULL;
+ if (c) {
+ if (c[strlen(c) - 1] == '.')
+ c[strlen(c) - 1] = '\0';
+ ar->ar_cname = strdup(c);
+ } else
+ ar->ar_cname = NULL;
+ return (ASYNC_YIELD);
+ }
+ break;
+
+ case ASR_STATE_READ_FILE:
+
+ /* When done with the file, try next family. */
+ n = asr_parse_namedb_line(as->as.host.file, as->as.host.tokens,
+ MAXTOKEN);
+ if (n == -1) {
+ fclose(as->as.host.file);
+ as->as.host.file = NULL;
+ async_set_state(as, ASR_STATE_NEXT_FAMILY);
+ break;
+ }
+
+ for (i = 1; i < n; i++) {
+ if (strcasecmp(as->as.host.name,
+ as->as.host.tokens[i]))
+ continue;
+
+ family = as->as.host.family;
+ if (family == AF_UNSPEC)
+ family = AS_FAMILY(as);
+
+ if (sockaddr_from_str(&ar->ar_sa.sa, family,
+ as->as.host.tokens[0]) == -1)
+ continue;
+
+ if (as->as.host.aiflags & AI_CANONNAME) {
+ strlcpy(buf, as->as.host.tokens[1],
+ sizeof buf);
+ c = buf;
+ } else if (as->as.host.aiflags & AI_FQDN) {
+ strlcpy(buf, as->as.host.name, sizeof buf);
+ c = buf;
+ } else
+ c = NULL;
+ if (c) {
+ if (c[strlen(c) - 1] == '.')
+ c[strlen(c) - 1] = '\0';
+ ar->ar_cname = strdup(c);
+ } else
+ ar->ar_cname = NULL;
+ as->as_count += 1;
+ ar->ar_count = as->as_count;
+ return (ASYNC_YIELD);
+ }
+ break;
+
+ case ASR_STATE_NOT_FOUND:
+ /* XXX the exact error depends on what query/send returned */
+ ar->ar_errno = 0;
+ ar->ar_gai_errno = EAI_NODATA;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+
+ case ASR_STATE_HALT:
+
+ ar->ar_count = as->as_count;
+ if (ar->ar_count) {
+ ar->ar_errno = 0;
+ ar->ar_gai_errno = 0;
+ }
+ return (ASYNC_DONE);
+
+ default:
+ ar->ar_errno = EOPNOTSUPP;
+ ar->ar_gai_errno = EAI_SYSTEM;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ goto next;
+}
+
+static int
+sockaddr_from_rr(struct sockaddr *sa, struct rr *rr)
+{
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+
+ if (rr->rr_class != C_IN)
+ return (-1);
+
+ switch (rr->rr_type) {
+ case T_A:
+ sin = (struct sockaddr_in*)sa;
+ memset(sin, 0, sizeof *sin);
+ sin->sin_len = sizeof *sin;
+ sin->sin_family = PF_INET;
+ sin->sin_addr = rr->rr.in_a.addr;
+ sin->sin_port = 0;
+ return (0);
+ case T_AAAA:
+ sin6 = (struct sockaddr_in6*)sa;
+ memset(sin6, 0, sizeof *sin6);
+ sin6->sin6_len = sizeof *sin6;
+ sin6->sin6_family = PF_INET6;
+ sin6->sin6_addr = rr->rr.in_aaaa.addr6;
+ sin6->sin6_port = 0;
+ return (0);
+
+ default:
+ break;
+ }
+
+ return (-1);
+}
diff --git a/lib/libc/asr/res_search_async.c b/lib/libc/asr/res_search_async.c
new file mode 100644
index 00000000000..99e315313e8
--- /dev/null
+++ b/lib/libc/asr/res_search_async.c
@@ -0,0 +1,224 @@
+/* $OpenBSD: res_search_async.c,v 1.1 2012/04/14 09:24:18 eric Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <arpa/nameser.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * TODO:
+ *
+ * - make it possible to reuse ibuf if it was NULL when first called,
+ * to avoid reallocating buffers everytime.
+ */
+
+#include "asr.h"
+#include "asr_private.h"
+
+static int res_search_async_run(struct async *, struct async_res *);
+
+/*
+ * Unlike res_query_async(), this function returns a valid packet only if
+ * h_errno is NETDB_SUCCESS.
+ */
+struct async *
+res_search_async(const char *name, int class, int type, unsigned char *ans,
+ int anslen, struct asr *asr)
+{
+ struct asr_ctx *ac;
+ struct async *as;
+#ifdef DEBUG
+ asr_printf("asr: res_search_async(\"%s\", %i, %i)\n", name, class, type);
+#endif
+ ac = asr_use_resolver(asr);
+ as = res_search_async_ctx(name, class, type, ans, anslen, ac);
+ asr_ctx_unref(ac);
+
+ return (as);
+}
+
+struct async *
+res_search_async_ctx(const char *name, int class, int type, unsigned char *ans,
+ int anslen, struct asr_ctx *ac)
+{
+ struct async *as;
+
+#ifdef DEBUG
+ asr_printf("asr: res_search_async_ctx(\"%s\", %i, %i)\n",
+ name, class, type);
+#endif
+
+ if ((as = async_new(ac, ASR_SEARCH)) == NULL)
+ goto err; /* errno set */
+ as->as_run = res_search_async_run;
+ if ((as->as.search.name = strdup(name)) == NULL)
+ goto err; /* errno set */
+
+ if (ans) {
+ as->as.search.flags |= ASYNC_EXTIBUF;
+ as->as.search.ibuf = ans;
+ as->as.search.ibufsize = anslen;
+ } else {
+ as->as.search.ibuf = NULL;
+ as->as.search.ibufsize = 0;
+ }
+ as->as.search.ibuflen = 0;
+
+ as->as.search.class = class;
+ as->as.search.type = type;
+
+ return (as);
+ err:
+ if (as)
+ async_free(as);
+ return (NULL);
+}
+
+#define HERRNO_UNSET -2
+
+static int
+res_search_async_run(struct async *as, struct async_res *ar)
+{
+ int r;
+ char fqdn[MAXDNAME];
+
+ next:
+ switch(as->as_state) {
+
+ case ASR_STATE_INIT:
+
+ as->as.search.saved_h_errno = HERRNO_UNSET;
+ async_set_state(as, ASR_STATE_NEXT_DOMAIN);
+ break;
+
+ case ASR_STATE_NEXT_DOMAIN:
+
+ /* Reset flags to be able to identify the case in STATE_SUBQUERY. */
+ as->as_dom_flags = 0;
+
+ r = asr_iter_domain(as, as->as.search.name, fqdn, sizeof(fqdn));
+ if (r == -1) {
+ async_set_state(as, ASR_STATE_NOT_FOUND);
+ break;
+ }
+ if (r > sizeof(fqdn)) {
+ ar->ar_errno = EINVAL;
+ ar->ar_h_errno = NO_RECOVERY;
+ ar->ar_datalen = -1;
+ ar->ar_data = NULL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ as->as.search.subq = res_query_async_ctx(fqdn,
+ as->as.search.class, as->as.search.type,
+ as->as.search.ibuf, as->as.search.ibufsize, as->as_ctx);
+ if (as->as.search.subq == NULL) {
+ ar->ar_errno = errno;
+ if (errno == EINVAL)
+ ar->ar_h_errno = NO_RECOVERY;
+ else
+ ar->ar_h_errno = NETDB_INTERNAL;
+ ar->ar_datalen = -1;
+ ar->ar_data = NULL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ async_set_state(as, ASR_STATE_SUBQUERY);
+ break;
+
+ case ASR_STATE_SUBQUERY:
+
+ if ((r = async_run(as->as.search.subq, ar)) == ASYNC_COND)
+ return (ASYNC_COND);
+ as->as.search.subq = NULL;
+
+ if (ar->ar_h_errno == NETDB_SUCCESS) {
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ /*
+ * The original res_search() does this in the domain search
+ * loop, but only for ECONNREFUSED. I think we can do better
+ * because technically if we get an errno, it means
+ * we couldn't reach any nameserver, so there is no point
+ * in trying further.
+ */
+ if (ar->ar_errno) {
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ /*
+ * If we don't use an external buffer, the packet was allocated
+ * by the subquery and it must be freed now.
+ */
+ if ((as->as.search.flags & ASYNC_EXTIBUF) == 0)
+ free(ar->ar_data);
+
+ /*
+ * The original resolver does something like this, to
+ */
+ if (as->as_dom_flags & (ASYNC_DOM_NDOTS | ASYNC_DOM_ASIS))
+ as->as.search.saved_h_errno = ar->ar_h_errno;
+
+ if (as->as_dom_flags & ASYNC_DOM_DOMAIN) {
+ if (ar->ar_h_errno == NO_DATA)
+ as->as.search.flags |= ASYNC_NODATA;
+ else if (ar->ar_h_errno == TRY_AGAIN)
+ as->as.search.flags |= ASYNC_AGAIN;
+ }
+
+ async_set_state(as, ASR_STATE_NEXT_DOMAIN);
+ break;
+
+ case ASR_STATE_NOT_FOUND:
+
+ if (as->as.search.saved_h_errno != HERRNO_UNSET)
+ ar->ar_h_errno = as->as.search.saved_h_errno;
+ else if (as->as.search.flags & ASYNC_NODATA)
+ ar->ar_h_errno = NO_DATA;
+ else if (as->as.search.flags & ASYNC_AGAIN)
+ ar->ar_h_errno = TRY_AGAIN;
+ /*
+ * Else, we got the ar_h_errno value set by res_query_async()
+ * for the last domain.
+ */
+ ar->ar_datalen = -1;
+ ar->ar_data = NULL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+
+ case ASR_STATE_HALT:
+
+ return (ASYNC_DONE);
+
+ default:
+ ar->ar_errno = EOPNOTSUPP;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ goto next;
+}
diff --git a/lib/libc/asr/res_send_async.c b/lib/libc/asr/res_send_async.c
new file mode 100644
index 00000000000..3516c685ad2
--- /dev/null
+++ b/lib/libc/asr/res_send_async.c
@@ -0,0 +1,809 @@
+/* $OpenBSD: res_send_async.c,v 1.1 2012/04/14 09:24:18 eric Exp $ */
+/*
+ * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <resolv.h> /* for res_random */
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asr.h"
+#include "asr_private.h"
+
+#define OP_QUERY (0)
+
+static int res_send_async_run(struct async *, struct async_res *);
+static int sockaddr_connect(const struct sockaddr *, int);
+static int udp_send(struct async *);
+static int udp_recv(struct async *);
+static int tcp_write(struct async *);
+static int tcp_read(struct async *);
+static int validate_packet(struct async *);
+static int setup_query(struct async *, const char *, const char *, int, int);
+static int ensure_ibuf(struct async *, size_t);
+
+#ifdef DEBUG
+char *print_addr(const struct sockaddr *, char *, size_t);
+#endif
+
+#define AS_NS_SA(p) ((p)->as_ctx->ac_ns[(p)->as_ns_idx - 1])
+
+
+struct async *
+res_send_async(const unsigned char *buf, int buflen, unsigned char *ans,
+ int anslen, struct asr *asr)
+{
+ struct asr_ctx *ac;
+ struct async *as;
+ struct packed p;
+ struct header h;
+ struct query q;
+
+#ifdef DEBUG
+ if (asr_debug) {
+ asr_printf("asr: res_send_async()\n");
+ asr_dump_packet(stderr, buf, buflen, 0);
+ }
+#endif
+ ac = asr_use_resolver(asr);
+ if ((as = async_new(ac, ASR_SEND)) == NULL) {
+ asr_ctx_unref(ac);
+ return (NULL); /* errno set */
+ }
+ as->as_run = res_send_async_run;
+
+ if (ans) {
+ as->as.dns.flags |= ASYNC_EXTIBUF;
+ as->as.dns.ibuf = ans;
+ as->as.dns.ibufsize = anslen;
+ as->as.dns.ibuflen = 0;
+ } else {
+ as->as.dns.ibuf = NULL;
+ as->as.dns.ibufsize = 0;
+ as->as.dns.ibuflen = 0;
+ }
+
+ as->as.dns.flags |= ASYNC_EXTOBUF;
+ as->as.dns.obuf = (unsigned char*)buf;
+ as->as.dns.obuflen = buflen;
+ as->as.dns.obufsize = buflen;
+
+ packed_init(&p, (char*)buf, buflen);
+ unpack_header(&p, &h);
+ unpack_query(&p, &q);
+ if (p.err) {
+ errno = EINVAL;
+ goto err;
+ }
+ as->as.dns.reqid = h.id;
+ as->as.dns.type = q.q_type;
+ as->as.dns.class = q.q_class;
+ as->as.dns.dname = strdup(q.q_dname);
+ if (as->as.dns.dname == NULL)
+ goto err; /* errno set */
+
+ asr_ctx_unref(ac);
+ return (as);
+ err:
+ if (as)
+ async_free(as);
+ asr_ctx_unref(ac);
+ return (NULL);
+}
+
+/*
+ * Unlike res_query(), this version will actually return the packet
+ * if it has received a valid one (errno == 0) even if h_errno is
+ * not NETDB_SUCCESS. So the packet *must* be freed if necessary
+ * (ans == NULL).
+ */
+struct async *
+res_query_async(const char *name, int class, int type, unsigned char *ans,
+ int anslen, struct asr *asr)
+{
+ struct asr_ctx *ac;
+ struct async *as;
+#ifdef DEBUG
+ asr_printf("asr: res_query_async(\"%s\", %i, %i)\n", name, class, type);
+#endif
+ ac = asr_use_resolver(asr);
+ as = res_query_async_ctx(name, class, type, ans, anslen, ac);
+ asr_ctx_unref(ac);
+
+ return (as);
+}
+
+struct async *
+res_query_async_ctx(const char *name, int class, int type, unsigned char *ans,
+ int anslen, struct asr_ctx *a_ctx)
+{
+ struct async *as;
+
+#ifdef DEBUG
+ asr_printf("asr: res_query_async_ctx(\"%s\", %i, %i)\n", name, class,
+ type);
+#endif
+ if ((as = async_new(a_ctx, ASR_SEND)) == NULL)
+ return (NULL); /* errno set */
+ as->as_run = res_send_async_run;
+
+ if (ans) {
+ as->as.dns.flags |= ASYNC_EXTIBUF;
+ as->as.dns.ibuf = ans;
+ as->as.dns.ibufsize = anslen;
+ } else {
+ as->as.dns.ibuf = NULL;
+ as->as.dns.ibufsize = 0;
+ }
+ as->as.dns.ibuflen = 0;
+
+ /* This adds a "." to name if it doesn't already has one.
+ * That's how res_query() behaves (trough res_mkquery").
+ */
+ if (setup_query(as, name, NULL, class, type) == -1)
+ goto err; /* errno set */
+
+ return (as);
+
+ err:
+ if (as)
+ async_free(as);
+
+ return (NULL);
+}
+
+static int
+res_send_async_run(struct async *as, struct async_res *ar)
+{
+ next:
+ switch(as->as_state) {
+
+ case ASR_STATE_INIT:
+
+ if (as->as_ctx->ac_nscount == 0) {
+ ar->ar_errno = ECONNREFUSED;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ async_set_state(as, ASR_STATE_NEXT_NS);
+ break;
+
+ case ASR_STATE_NEXT_NS:
+
+ if (asr_iter_ns(as) == -1) {
+ ar->ar_errno = ETIMEDOUT;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+
+ if (as->as_ctx->ac_options & RES_USEVC ||
+ as->as.dns.obuflen > PACKETSZ)
+ async_set_state(as, ASR_STATE_TCP_WRITE);
+ else
+ async_set_state(as, ASR_STATE_UDP_SEND);
+ break;
+
+ case ASR_STATE_UDP_SEND:
+
+ if (udp_send(as) == -1) {
+ async_set_state(as, ASR_STATE_NEXT_NS);
+ break;
+ }
+ async_set_state(as, ASR_STATE_UDP_RECV);
+ ar->ar_cond = ASYNC_READ;
+ ar->ar_fd = as->as_fd;
+ ar->ar_timeout = as->as_timeout;
+ return (ASYNC_COND);
+ break;
+
+ case ASR_STATE_UDP_RECV:
+
+ if (udp_recv(as) == -1) {
+ if (errno == ENOMEM) {
+ ar->ar_errno = errno;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ if (errno != EOVERFLOW) {
+ /* Fail or timeout */
+ async_set_state(as, ASR_STATE_NEXT_NS);
+ break;
+ }
+ if (as->as_ctx->ac_options & RES_IGNTC)
+ async_set_state(as, ASR_STATE_PACKET);
+ else
+ async_set_state(as, ASR_STATE_TCP_WRITE);
+ } else
+ async_set_state(as, ASR_STATE_PACKET);
+ break;
+
+ case ASR_STATE_TCP_WRITE:
+
+ switch (tcp_write(as)) {
+ case -1: /* fail or timeout */
+ async_set_state(as, ASR_STATE_NEXT_NS);
+ break;
+ case 0:
+ async_set_state(as, ASR_STATE_TCP_READ);
+ ar->ar_cond = ASYNC_READ;
+ ar->ar_fd = as->as_fd;
+ ar->ar_timeout = as->as_timeout;
+ return (ASYNC_COND);
+ case 1:
+ ar->ar_cond = ASYNC_WRITE;
+ ar->ar_fd = as->as_fd;
+ ar->ar_timeout = as->as_timeout;
+ return (ASYNC_COND);
+ }
+ break;
+
+ case ASR_STATE_TCP_READ:
+
+ switch (tcp_read(as)) {
+ case -1: /* Fail or timeout */
+ if (errno == ENOMEM) {
+ ar->ar_errno = errno;
+ async_set_state(as, ASR_STATE_HALT);
+ } else
+ async_set_state(as, ASR_STATE_NEXT_NS);
+ break;
+ case 0:
+ async_set_state(as, ASR_STATE_PACKET);
+ break;
+ case 1:
+ ar->ar_cond = ASYNC_READ;
+ ar->ar_fd = as->as_fd;
+ ar->ar_timeout = as->as_timeout;
+ return (ASYNC_COND);
+ }
+ break;
+
+ case ASR_STATE_PACKET:
+
+ memmove(&ar->ar_sa.sa, AS_NS_SA(as), AS_NS_SA(as)->sa_len);
+ ar->ar_datalen = as->as.dns.ibuflen;
+ ar->ar_data = as->as.dns.ibuf;
+ as->as.dns.ibuf = NULL;
+ ar->ar_errno = 0;
+ ar->ar_rcode = as->as.dns.rcode;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+
+ case ASR_STATE_HALT:
+
+ if (ar->ar_errno) {
+ ar->ar_h_errno = TRY_AGAIN;
+ ar->ar_count = 0;
+ ar->ar_datalen = -1;
+ ar->ar_data = NULL;
+ } else if (as->as.dns.ancount) {
+ ar->ar_h_errno = NETDB_SUCCESS;
+ ar->ar_count = as->as.dns.ancount;
+ } else {
+ ar->ar_count = 0;
+ switch(as->as.dns.rcode) {
+ case NXDOMAIN:
+ ar->ar_h_errno = HOST_NOT_FOUND;
+ break;
+ case SERVFAIL:
+ ar->ar_h_errno = TRY_AGAIN;
+ break;
+ case NOERROR:
+ ar->ar_h_errno = NO_DATA;
+ break;
+ default:
+ ar->ar_h_errno = NO_RECOVERY;
+ }
+ }
+ return (ASYNC_DONE);
+
+ default:
+
+ ar->ar_errno = EOPNOTSUPP;
+ ar->ar_h_errno = NETDB_INTERNAL;
+ async_set_state(as, ASR_STATE_HALT);
+ break;
+ }
+ goto next;
+}
+
+static int
+sockaddr_connect(const struct sockaddr *sa, int socktype)
+{
+ int errno_save, flags, sock;
+
+ if ((sock = socket(sa->sa_family, socktype, 0)) == -1)
+ goto fail;
+
+ if ((flags = fcntl(sock, F_GETFL, 0)) == -1)
+ goto fail;
+
+ flags |= O_NONBLOCK;
+
+ if ((flags = fcntl(sock, F_SETFL, flags)) == -1)
+ goto fail;
+
+ if (connect(sock, sa, sa->sa_len) == -1) {
+ if (errno == EINPROGRESS)
+ return (sock);
+ goto fail;
+ }
+
+ return (sock);
+
+ fail:
+
+ if (sock != -1) {
+ errno_save = errno;
+ close(sock);
+ errno = errno_save;
+ }
+
+ return (-1);
+}
+
+/*
+ * Prepare the DNS packet for the query type "type", class "class" and domain
+ * name created by the concatenation on "name" and "dom".
+ * Return 0 on success, set errno and return -1 on error.
+ */
+static int
+setup_query(struct async *as, const char *name, const char *dom,
+ int class, int type)
+{
+ struct packed p;
+ struct header h;
+ char fqdn[MAXDNAME];
+ char dname[MAXDNAME];
+
+ if (as->as.dns.flags & ASYNC_EXTOBUF) {
+ errno = EINVAL;
+#ifdef DEBUG
+ asr_printf("attempting to write in user packet");
+#endif
+ return (-1);
+ }
+
+ if (asr_make_fqdn(name, dom, fqdn, sizeof(fqdn)) > sizeof(fqdn)) {
+ errno = EINVAL;
+#ifdef DEBUG
+ asr_printf("asr_make_fqdn: name too long\n");
+#endif
+ return (-1);
+ }
+
+ if (dname_from_fqdn(fqdn, dname, sizeof(dname)) == -1) {
+ errno = EINVAL;
+#ifdef DEBUG
+ asr_printf("dname_from_fqdn: invalid\n");
+#endif
+ return (-1);
+ }
+
+ if (as->as.dns.obuf == NULL) {
+ as->as.dns.obufsize = PACKETSZ;
+ as->as.dns.obuf = malloc(as->as.dns.obufsize);
+ if (as->as.dns.obuf == NULL)
+ return (-1); /* errno set */
+ }
+ as->as.dns.obuflen = 0;
+
+ memset(&h, 0, sizeof h);
+ h.id = res_randomid();
+ if (as->as_ctx->ac_options & RES_RECURSE)
+ h.flags |= RD_MASK;
+ h.qdcount = 1;
+
+ packed_init(&p, as->as.dns.obuf, as->as.dns.obufsize);
+ pack_header(&p, &h);
+ pack_query(&p, type, class, dname);
+ if (p.err) {
+#ifdef DEBUG
+ asr_printf("error packing query");
+#endif
+ errno = EINVAL;
+ return (-1);
+ }
+
+ /* Remember the parameters. */
+ as->as.dns.reqid = h.id;
+ as->as.dns.type = type;
+ as->as.dns.class = class;
+ if (as->as.dns.dname)
+ free(as->as.dns.dname);
+ as->as.dns.dname = strdup(dname);
+ if (as->as.dns.dname == NULL) {
+#ifdef DEBUG
+ asr_printf("strdup");
+#endif
+ return (-1); /* errno set */
+ }
+ as->as.dns.obuflen = p.offset;
+
+#ifdef DEBUG
+ if (asr_debug) {
+ asr_printf("------- asr_setup_query(): packet -------\n");
+ asr_dump_packet(stderr, as->as.dns.obuf, as->as.dns.obuflen, 0);
+ asr_printf("-----------------------------------------\n");
+ }
+#endif
+
+ return (0);
+}
+
+/*
+ * Create a connect UDP socket and send the output packet.
+ *
+ * Return 0 on success, or -1 on error (errno set).
+ */
+static int
+udp_send(struct async *as)
+{
+ ssize_t n;
+ int save_errno;
+#ifdef DEBUG
+ char buf[256];
+
+ if (asr_debug)
+ asr_printf("asr: [%p] connecting to %s UDP\n", as,
+ print_addr(AS_NS_SA(as), buf, sizeof buf));
+#endif
+ as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_DGRAM);
+ if (as->as_fd == -1)
+ return (-1); /* errno set */
+
+ as->as_timeout = as->as_ctx->ac_nstimeout;
+
+ n = send(as->as_fd, as->as.dns.obuf, as->as.dns.obuflen, 0);
+ if (n == -1) {
+ save_errno = errno;
+ close(as->as_fd);
+ errno = save_errno;
+ as->as_fd = -1;
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Try to receive a valid packet from the current UDP socket.
+ *
+ * Return 0 if a full packet could be read, or -1 on error (errno set).
+ */
+static int
+udp_recv(struct async *as)
+{
+ ssize_t n;
+ int save_errno;
+
+ /* Allocate input buf if needed */
+ if (as->as.dns.ibuf == NULL) {
+ if (ensure_ibuf(as, PACKETSZ) == -1) {
+ save_errno = errno;
+ close(as->as_fd);
+ errno = save_errno;
+ as->as_fd = -1;
+ return (-1);
+ }
+ }
+
+ n = recv(as->as_fd, as->as.dns.ibuf, as->as.dns.ibufsize, 0);
+ save_errno = errno;
+ close(as->as_fd);
+ errno = save_errno;
+ as->as_fd = -1;
+ if (n == -1)
+ return (-1);
+
+ as->as.dns.ibuflen = n;
+
+#ifdef DEBUG
+ if (asr_debug) {
+ asr_printf("------- asr_udp_recv() packet -------\n");
+ asr_dump_packet(stderr, as->as.dns.ibuf, as->as.dns.ibuflen, 0);
+ asr_printf("-------------------------------------\n");
+ }
+#endif
+
+ if (validate_packet(as) == -1)
+ return (-1); /* errno set */
+
+ return (0);
+}
+
+/*
+ * Write the output packet to the TCP socket.
+ *
+ * Return 0 when all bytes have been sent, 1 there is no buffer space on the
+ * socket or it is not connected yet, or -1 on error (errno set).
+ */
+static int
+tcp_write(struct async *as)
+{
+ struct iovec iov[2];
+ uint16_t len;
+ ssize_t n;
+ int i, se;
+ socklen_t sl;
+#ifdef DEBUG
+ char buf[256];
+#endif
+
+ /* First try to connect if not already */
+ if (as->as_fd == -1) {
+#ifdef DEBUG
+ if (asr_debug)
+ asr_printf("asr: [%p] connecting to %s TCP\n", as,
+ print_addr(AS_NS_SA(as), buf, sizeof buf));
+#endif
+ as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_STREAM);
+ if (as->as_fd == -1)
+ return (-1); /* errno set */
+ as->as_timeout = as->as_ctx->ac_nstimeout;
+ return (1);
+ }
+
+ i = 0;
+
+ /* Check if the connection succeeded. */
+ if (as->as.dns.datalen == 0) {
+ sl = sizeof(se);
+ if (getsockopt(as->as_fd, SOL_SOCKET, SO_ERROR, &se, &sl) == -1)
+ goto close; /* errno set */
+ if (se) {
+ errno = se;
+ goto close;
+ }
+
+ as->as.dns.bufpos = 0;
+
+ /* Send the packet length first */
+ len = htons(as->as.dns.obuflen);
+ iov[i].iov_base = &len;
+ iov[i].iov_len = sizeof(len);
+ i++;
+ }
+
+ iov[i].iov_base = as->as.dns.obuf + as->as.dns.bufpos;
+ iov[i].iov_len = as->as.dns.obuflen - as->as.dns.bufpos;
+ i++;
+
+ n = writev(as->as_fd, iov, i);
+ if (n == -1)
+ goto close; /* errno set */
+
+ /*
+ * We want at least the packet length to be written out the first time.
+ * Technically we could recover but that makes little sense to support
+ * that.
+ */
+ if (as->as.dns.datalen == 0 && n < 2) {
+ errno = EIO;
+ goto close;
+ }
+
+ if (as->as.dns.datalen == 0) {
+ as->as.dns.datalen = len;
+ n -= 2;
+ }
+
+ as->as.dns.bufpos += n;
+ if (as->as.dns.bufpos == as->as.dns.obuflen) {
+ /* All sent. Prepare for TCP read */
+ as->as.dns.datalen = 0;
+ return (0);
+ }
+
+ /* More data to write */
+ as->as_timeout = as->as_ctx->ac_nstimeout;
+ return (1);
+
+close:
+ close(as->as_fd);
+ as->as_fd = -1;
+ return (-1);
+}
+
+/*
+ * Try to read a valid packet from the current TCP socket.
+ *
+ * Return 0 if a full packet could be read, 1 if more data is needed and the
+ * socket must be read again, or -1 on error (errno set).
+ */
+static int
+tcp_read(struct async *as)
+{
+ uint16_t len;
+ ssize_t n;
+ int save_errno;
+
+ /* We must read the packet len first */
+ if (as->as.dns.datalen == 0) {
+ n = read(as->as_fd, &len, sizeof(len));
+ if (n == -1)
+ goto close; /* errno set */
+ /*
+ * If the server has sent us only the first byte, we fail.
+ * Technically, we could recover but it might not be worth
+ * supporting that.
+ */
+ if (n < 2) {
+ errno = EIO;
+ goto close;
+ }
+
+ as->as.dns.datalen = ntohs(len);
+ as->as.dns.bufpos = 0;
+ as->as.dns.ibuflen = 0;
+
+ if (ensure_ibuf(as, as->as.dns.datalen) == -1)
+ goto close; /* errno set */
+ }
+
+ n = read(as->as_fd, as->as.dns.ibuf + as->as.dns.ibuflen,
+ as->as.dns.datalen - as->as.dns.ibuflen);
+ if (n == -1)
+ goto close; /* errno set */
+ if (n == 0) {
+ errno = ECONNRESET;
+ goto close;
+ }
+ as->as.dns.ibuflen += n;
+
+ /* See if we got all the advertised bytes. */
+ if (as->as.dns.ibuflen != as->as.dns.datalen)
+ return (1);
+
+#ifdef DEBUG
+ if (asr_debug) {
+ asr_printf("------- asr_tcp_read() packet -------\n");
+ asr_dump_packet(stderr, as->as.dns.ibuf, as->as.dns.ibuflen, 0);
+ asr_printf("-------------------------------------\n");
+ }
+#endif
+ if (validate_packet(as) == -1)
+ goto close; /* errno set */
+
+ errno = 0;
+close:
+ save_errno = errno;
+ close(as->as_fd);
+ errno = save_errno;
+ as->as_fd = -1;
+ return (errno ? -1 : 0);
+}
+
+/*
+ * Make sure the input buffer is at least "n" bytes long.
+ * If not (or not allocated) allocated enough space, unless the
+ * buffer is external (owned by the caller), in which case it fails.
+ */
+static int
+ensure_ibuf(struct async *as, size_t n)
+{
+ char *t;
+
+ if (as->as.dns.flags & ASYNC_EXTIBUF) {
+ if (n <= as->as.dns.ibufsize)
+ return (0);
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (as->as.dns.ibuf == NULL) {
+ as->as.dns.ibuf = malloc(n);
+ if (as->as.dns.ibuf == NULL)
+ return (-1); /* errno set */
+ as->as.dns.ibufsize = n;
+ return (0);
+ }
+
+ if (as->as.dns.ibufsize >= n)
+ return (0);
+
+ t = realloc(as->as.dns.ibuf, n);
+ if (t == NULL)
+ return (-1); /* errno set */
+ as->as.dns.ibuf = t;
+ as->as.dns.ibufsize = n;
+
+ return (0);
+}
+
+/*
+ * Check if the received packet is valid.
+ * Return 0 on success, or set errno and return -1.
+ */
+static int
+validate_packet(struct async *as)
+{
+ struct packed p;
+ struct header h;
+ struct query q;
+ struct rr rr;
+ int r;
+
+ packed_init(&p, as->as.dns.ibuf, as->as.dns.ibuflen);
+
+ unpack_header(&p, &h);
+ if (p.err)
+ goto inval;
+
+ if (h.id != as->as.dns.reqid) {
+#ifdef DEBUG
+ asr_printf("incorrect reqid\n");
+#endif
+ goto inval;
+ }
+ if (h.qdcount != 1)
+ goto inval;
+ /* Should be zero, we could allow this */
+ if ((h.flags & Z_MASK) != 0)
+ goto inval;
+ /* Actually, it depends on the request but we only use OP_QUERY */
+ if (OPCODE(h.flags) != OP_QUERY)
+ goto inval;
+ /* Must be a response */
+ if ((h.flags & QR_MASK) == 0)
+ goto inval;
+
+ as->as.dns.rcode = RCODE(h.flags);
+ as->as.dns.ancount = h.ancount;
+
+ unpack_query(&p, &q);
+ if (p.err)
+ goto inval;
+
+ if (q.q_type != as->as.dns.type ||
+ q.q_class != as->as.dns.class ||
+ strcasecmp(q.q_dname, as->as.dns.dname)) {
+#ifdef DEBUG
+ asr_printf("incorrect type/class/dname '%s' != '%s'\n",
+ q.q_dname, as->as.dns.dname);
+#endif
+ goto inval;
+ }
+
+ /* Check for truncation */
+ if (h.flags & TC_MASK) {
+ errno = EOVERFLOW;
+ return (-1);
+ }
+
+ /* Validate the rest of the packet */
+ for(r = h.ancount + h.nscount + h.arcount; r; r--)
+ unpack_rr(&p, &rr);
+
+ if (p.err || (p.offset != as->as.dns.ibuflen))
+ goto inval;
+
+ return (0);
+
+ inval:
+ errno = EINVAL;
+ return (-1);
+}