summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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);
+}