]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Untested async io integration layer for libunbound
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 30 Oct 2019 22:05:23 +0000 (16:05 -0600)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 30 Oct 2019 22:05:51 +0000 (16:05 -0600)
src/modules/rlm_unbound/all.mk.in
src/modules/rlm_unbound/io.c [new file with mode: 0644]
src/modules/rlm_unbound/io.h [new file with mode: 0644]
src/modules/rlm_unbound/rlm_unbound.c

index 2dd837d100c3ad056bc47c3d78e4c4dbc34c64d8..710cef36614ae8075b35eb438af26c07030a1b22 100644 (file)
@@ -6,7 +6,7 @@ TARGET          := $(TARGETNAME).a
 endif
 endif
 
-SOURCES                := $(TARGETNAME).c
+SOURCES                := $(TARGETNAME).c io.c
 
 SRC_CFLAGS     := @mod_cflags@
 TGT_LDLIBS     := @mod_ldflags@ $(OPENSSL_LIBS)
diff --git a/src/modules/rlm_unbound/io.c b/src/modules/rlm_unbound/io.c
new file mode 100644 (file)
index 0000000..63897f9
--- /dev/null
@@ -0,0 +1,559 @@
+/*
+ *   This program is is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or (at
+ *   your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ * @file rlm_unbound/io.c
+ * @brief Provides interface between libunbound and the FreeRADIUS event loop
+ *
+ * @copyright 2019 The FreeRADIUS server project
+ * @copyright 2019 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
+ */
+RCSID("$Id$")
+
+#define LOG_PREFIX "rlm_unbound - "
+
+#include <freeradius-devel/server/log.h>
+#include <freeradius-devel/util/event.h>
+#include <freeradius-devel/util/syserror.h>
+
+#include "io.h"
+
+#ifdef HAVE_WDOCUMENTATION
+DIAG_OFF(documentation)
+#endif
+#include <unbound-event.h>
+#ifdef HAVE_WDOCUMENTATION
+DIAG_ON(documentation)
+#endif
+
+/** Definition for libunbound's event callback
+ *
+ * Here because they don't provide one.
+ */
+typedef void(*unbound_cb_t)(int, short flags, void *uctx);
+
+/** Wrapper around our event loop specifying callbacks for creating new event handles
+ *
+ * This stores libunbound specific information in addition to the el for the current
+ * worker thread.  It's passed into the 'new_event' callback when a new event handle
+ * is created.  So we use it to pass in the el, and any other useful information.
+ *
+ * Lifetime should be bound to the thread instance.
+ */
+struct unbound_io_event_base_s {
+       struct ub_event_base    base;           //!< Interface structure for libunbound.
+                                               ///< MUST BE LISTED FIRST.
+
+       rlm_unbound_thread_t    *t;             //!< Thread instance this event base belongs to.
+
+       fr_event_list_t         *el;            //!< Event loop events should be inserted into.
+};
+
+/** Wrapper around event handle for our event loop
+ *
+ * This stores libunbound specific information for an event in our event loop.
+ *
+ * Lifetime should be bound to the event base.
+ */
+typedef struct {
+       struct ub_event         base;           //!< Unbound event base, which we populate with
+                                               ///< callback functions for adding events for FDs
+                                               ///< setting timers etc...
+                                               ///< MUST BE LISTED FIRST.
+
+       unbound_io_event_base_t *ev_b;          //!< Event base this handle was created for.
+
+       fr_event_timer_t const  *timer;         //!< Stores the pointer to the enabled timer for
+                                               ///< this event handled.  libunbound uses a single
+                                               ///< handle for managing related FD events and
+                                               ///< timers, which is weird, but ok...
+
+       short                   events;         //!< The events this event handle should receive
+                                               ///< when activated.
+
+       int                     fd;             //!< File descriptor this event handle relates to.
+
+       unbound_cb_t            cb;             //!< The callback we need to call when a specified
+                                               ///< event happens.
+
+       void                    *uctx;          //!< This is the argument libunbound wants passed to
+                                               ///< the callback when it's called.  It usually
+                                               ///< contains libunbound's internal connection handled.
+                                               ///< We don't have any visibility, it just remains
+                                               ///< an opaque blob to us.
+
+       bool                    active;         //!< Whether this event is considered active.
+} unbound_io_event_t;
+
+/** Alter the enabled flags associated with the event
+ *
+ * Event *MUST* be disabled before these flags are changed.
+ */
+static void _unbound_io_event_flags_add(struct ub_event *ub_ev, short flags)
+{
+       unbound_io_event_t      *ev = talloc_get_type_abort(ub_ev, unbound_io_event_t);
+       short                   new = ev->events | flags;
+
+       DEBUG4("unbound event %p - Adding flags %i (current %i, new %i)", ev, flags, ev->events, new);
+
+       rad_assert(!ev->active);        /* must not be active */
+
+       ev->events = new;
+}
+
+/** Alter the enabled flags associated with the event
+ *
+ * Event *MUST* be disabled before these flags are changed.
+ */
+static void _unbound_io_event_flags_del(struct ub_event *ub_ev, short flags)
+{
+       unbound_io_event_t      *ev = talloc_get_type_abort(ub_ev, unbound_io_event_t);
+       short                   new = ev->events & ~flags;
+
+       rad_assert(!ev->active);                        /* must not be active */
+
+       DEBUG4("unbound event %p - Removing flags %i (current %i, new %i)", ev, flags, ev->events, new);
+
+       ev->events = new;
+}
+
+/** Change the file descriptor associated with an event
+ *
+ * Event *MUST* be disabled before changing the fd.
+ */
+static void _unbound_io_event_fd_set(struct ub_event *ub_ev, int fd)
+{
+       unbound_io_event_t      *ev = talloc_get_type_abort(ub_ev, unbound_io_event_t);
+
+       rad_assert(!ev->active);                        /* must not be active */
+
+       DEBUG4("unbound event %p - Changed FD from %i to %i", ev, ev->fd, fd);
+
+       ev->fd = fd;
+}
+
+/** Free an event, and, by the magic of talloc, any timers or fd events
+ *
+ */
+static void _unbound_io_event_free(struct ub_event *ub_ev)
+{
+       unbound_io_event_t      *ev = talloc_get_type_abort(ub_ev, unbound_io_event_t);
+
+       DEBUG4("unbound event %p - Freed", ev);
+
+       talloc_free(ev);
+}
+
+/** Timeout fired
+ *
+ */
+static void _unbound_io_service_timer_expired(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
+{
+       unbound_io_event_t      *ev = talloc_get_type_abort(uctx, unbound_io_event_t);
+       int                     ret;
+
+       rad_assert(ev->active);                 /* must be active */
+
+       DEBUG4("unbound event %p - Timeout", ev);
+
+       ev->cb(-1, UB_EV_TIMEOUT, ev->uctx);    /* Inform libunbound */
+
+       /*
+        *      Advanced libunbound's state
+        */
+       ret = ub_process(ev->ev_b->t->ub);
+       if (ret != 0) {
+               ERROR("unbound event %p - ub_process errored after timeout event - %s (%i)",
+                     ev, ub_strerror(ret), ret);
+       }
+}
+
+/** Unbound FD became readable
+ *
+ * Because we don't have the separation between the IO event loop
+ * and the event loop processing results, we call ub_process
+ * immediately after calling the IO callback.
+ */
+static void _unbound_io_service_readable(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
+{
+       unbound_io_event_t      *ev = talloc_get_type_abort(uctx, unbound_io_event_t);
+       int                     ret;
+
+       rad_assert(ev->active);                 /* must be active */
+
+       DEBUG4("unbound event %p - FD %i now readable", ev, fd);
+
+       ev->cb(-1, UB_EV_READ, ev->uctx);               /* Inform libunbound */
+
+       /*
+        *      Remove IO events
+        */
+       if (!(ev->events | UB_EV_PERSIST)) {
+               DEBUG4("unbound event %p - UB_EV_PERSIST not set - Removing events for FD %i", ev, ev->fd);
+               if (fr_event_fd_delete(el, ev->fd, FR_EVENT_FILTER_IO) < 0) {
+                       PERROR("unbound event %p - De-registration failed for FD %i", ev, ev->fd);
+               }
+       }
+
+       /*
+        *      Advanced libunbound's state
+        */
+       ret = ub_process(ev->ev_b->t->ub);
+       if (ret != 0) {
+               ERROR("unbound event %p - ub_process errored after read event - %s (%i)",
+                     ev, ub_strerror(ret), ret);
+       }
+}
+
+/** Unbound FD became writable
+ *
+ */
+static void _unbound_io_service_writable(fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
+{
+       unbound_io_event_t      *ev = talloc_get_type_abort(uctx, unbound_io_event_t);
+
+       rad_assert(ev->active);                 /* must be active */
+
+       DEBUG4("unbound event %p - FD %i now writable", ev, fd);
+
+       ev->cb(-1, UB_EV_WRITE, ev->uctx);      /* Inform libunbound */
+
+       /*
+        *      Remove IO events
+        */
+       if (!(ev->events | UB_EV_PERSIST)) {
+               DEBUG4("unbound event %p - UB_EV_PERSIST not set - Removing events for FD %i", ev, ev->fd);
+               if (fr_event_fd_delete(el, ev->fd, FR_EVENT_FILTER_IO) < 0) {
+                       PERROR("unbound event %p - De-registration failed for FD %i", ev, ev->fd);
+               }
+       }
+}
+
+/** Unbound FD errored
+ *
+ * libunbound doesn't request errors, so tell it a timeout occurred
+ *
+ * Because we don't have the separation between the IO event loop
+ * and the event loop processing results, we call ub_process
+ * immediately after calling the IO callback.
+ */
+static void _unbound_io_service_errored(UNUSED fr_event_list_t *el,
+                                       int fd, UNUSED int flags, int fd_errno, void *uctx)
+{
+       unbound_io_event_t      *ev = talloc_get_type_abort(uctx, unbound_io_event_t);
+       int                     ret;
+
+       rad_assert(ev->active);                 /* must be active */
+
+       DEBUG4("unbound event %p - FD %i errored: %s", ev, fd, fr_syserror(fd_errno));
+
+       /*
+        *      Delete the timer as we're telling libunbound
+        *      that it fired.  This is imperfect but unbound
+        *      doesn't have a callback for receiving errors.
+        */
+       if (fr_event_timer_delete(ev->ev_b->el, &ev->timer) < 0) {
+               PERROR("ubound event %p - Failed disarming timeout", ev);
+       }
+
+       ev->cb(-1, UB_EV_TIMEOUT, ev->uctx);    /* Call libunbound - pretend this is a timeout */
+
+       /*
+        *      Advanced libunbound's state
+        */
+       ret = ub_process(ev->ev_b->t->ub);
+       if (ret != 0) {
+               ERROR("unbound event %p - ub_process errored after FD error - %s (%i)", ev, ub_strerror(ret), ret);
+       }
+}
+
+
+/** Activate FD events and set a timer for a timeout
+ *
+ */
+static int _unbound_io_event_activate(struct ub_event *ub_ev, struct timeval *tv)
+{
+       unbound_io_event_t      *ev = talloc_get_type_abort(ub_ev, unbound_io_event_t);
+
+       rad_assert(!ev->active);        /* must not be active */
+
+       /*
+        *      File descriptor event
+        */
+       if ((ev->events & UB_EV_READ) && (ev->events & UB_EV_WRITE)) {
+               rad_assert(ev->fd >= 0);        /* File descriptor must valid */
+
+               DEBUG4("unbound event %p - Registered for read+write events on FD %i", ev, ev->fd);
+
+               if (fr_event_fd_insert(ev, ev->ev_b->el, ev->fd,
+                                      _unbound_io_service_readable,
+                                      _unbound_io_service_writable,
+                                      _unbound_io_service_errored,
+                                      ev) < 0) {
+                       PERROR("unbound event %p - Registration failed for read+write+error events on FD %i",
+                              ev, ev->fd);
+
+                       return -1;
+               }
+       } else if (ev->events & UB_EV_READ) {
+               rad_assert(ev->fd >= 0);        /* File descriptor must valid */
+
+               DEBUG4("unbound event %p - Registered for read+error events on FD %i", ev, ev->fd);
+
+               if (fr_event_fd_insert(ev, ev->ev_b->el, ev->fd,
+                                      _unbound_io_service_readable,
+                                      NULL,
+                                      _unbound_io_service_errored,
+                                      ev) < 0) {
+                       PERROR("unbound event %p - Registration failed for read+error events on FD %i",
+                              ev, ev->fd);
+
+                       return -1;
+               }
+       } else if (ev->events & UB_EV_WRITE) {
+               rad_assert(ev->fd >= 0);        /* File descriptor must valid */
+
+               DEBUG4("unbound event %p - Registered for write+error events on FD %i", ev, ev->fd);
+
+               if (fr_event_fd_insert(ev, ev->ev_b->el, ev->fd,
+                                      NULL,
+                                      _unbound_io_service_writable,
+                                      _unbound_io_service_errored,
+                                      ev) < 0) {
+                       PERROR("unbound event %p - Registration failed for write+error events on FD %i",
+                              ev, ev->fd);
+
+                       return -1;
+               }
+       }
+
+       /*
+        *      Add a timeout event
+        */
+       if (ev->events & UB_EV_TIMEOUT) {
+               fr_time_delta_t timeout = fr_time_delta_from_timeval(tv);
+
+               DEBUG4("unbound event %p - Timeout in %pV seconds", ev, fr_box_time_delta(timeout));
+
+               if (fr_event_timer_in(ev, ev->ev_b->el, &ev->timer,
+                                     timeout, _unbound_io_service_timer_expired, ev) < 0) {
+                       PERROR("unbound event %p - Failed adding timeout", ev);
+
+                       if (ev->events & (UB_EV_READ | UB_EV_WRITE)) {
+                               fr_event_fd_delete(ev->ev_b->el, ev->fd, FR_EVENT_FILTER_IO);
+                       }
+
+                       return -1;
+               }
+       }
+
+       ev->active = true;      /* Event is now active! */
+
+       return 0;
+}
+
+/* Deactivate FD events and disarm the timeout
+ *
+ */
+static int _unbound_io_event_deactivate(struct ub_event *ub_ev)
+{
+       unbound_io_event_t      *ev = talloc_get_type_abort(ub_ev, unbound_io_event_t);
+       int                     ret = 0;
+
+       if (!ev->active) return 0;      /* Allow this to be called multiple times */
+
+       if (ev->events & (UB_EV_READ | UB_EV_WRITE)) {
+               DEBUG4("unbound event %p - De-registering FD %i", ev, ev->fd);
+
+               if (fr_event_fd_delete(ev->ev_b->el, ev->fd, FR_EVENT_FILTER_IO) < 0) {
+                       PERROR("unbound event %p - De-registration failed for FD %i", ev, ev->fd);
+
+                       ret = -1;
+               }
+       }
+
+       if (ev->events & UB_EV_TIMEOUT) {
+               DEBUG4("unbound event %p - Disarming timeout", ev);
+
+               if (ev->timer && (fr_event_timer_delete(ev->ev_b->el, &ev->timer) < 0)) {
+                       PERROR("ubound event %p - Failed disarming timeout", ev);
+
+                       ret = -1;
+               }
+       }
+
+       ev->active = false;     /* Event is now inactive and can be modified */
+
+       return ret;
+}
+
+/** Modify an existing timeout
+ *
+ */
+static int _unbound_io_timer_modify(struct ub_event *ub_ev, UNUSED struct ub_event_base *ev_b,
+                                   void (*cb)(int, short, void*),
+                                   void *uctx, struct timeval *tv)
+{
+       unbound_io_event_t      *ev = talloc_get_type_abort(ub_ev, unbound_io_event_t);
+       int                     ret = 0;
+       fr_time_delta_t         timeout;
+
+       rad_assert(ev->events & UB_EV_TIMEOUT);
+
+       if (ev->cb != cb) {
+               DEBUG4("unbound event %p - New callback %p (old callback was %p)",
+                      ev, cb, ev->cb);
+               ev->cb = cb;
+       }
+       if (ev->uctx != uctx) {
+               DEBUG4("unbound event %p - New uctx %p (old uctx was %p)",
+                      ev, uctx, ev->uctx);
+               ev->uctx = uctx;
+       }
+       if (ev->timer && (fr_event_timer_delete(ev->ev_b->el, &ev->timer) < 0)) {
+               PERROR("ubound event %p - Failed disarming timeout", ev);
+
+               ret = -1;       /* Continue ? */
+       }
+
+       timeout = fr_time_delta_from_timeval(tv);
+
+       DEBUG4("unbound event %p - Timeout in %pV seconds", ev, fr_box_time_delta(timeout));
+
+       if (fr_event_timer_in(ev, ev->ev_b->el, &ev->timer,
+                             timeout, _unbound_io_service_timer_expired, ev) < 0) {
+               PERROR("unbound event %p - Failed adding timeout", ev);
+
+               ret = -1;
+       }
+
+       return ret;
+}
+
+/** Deactivate a timeout
+ *
+ */
+static int _unbound_io_timer_deactivate(struct ub_event *ub_ev)
+{
+       unbound_io_event_t      *ev = talloc_get_type_abort(ub_ev, unbound_io_event_t);
+
+       rad_assert(ev->events & UB_EV_TIMEOUT);
+
+       DEBUG4("unbound event %p - Disarming timeout", ev);
+
+       if (ev->timer && (fr_event_timer_delete(ev->ev_b->el, &ev->timer) < 0)) {
+               PERROR("unbound event %p - Failed disarming timeout", ev);
+
+               return -1;
+       }
+
+       return 0;
+}
+
+/** Returns a new libunbound event handle
+ *
+ * This handle is used by libunbound to interface with the worker's event loop
+ */
+static struct ub_event *_unbound_io_event_new(struct ub_event_base* base, int fd, short flags,
+                                             void (*cb)(int, short, void*), void *uctx)
+{
+       unbound_io_event_base_t *ev_b = talloc_get_type_abort(base, unbound_io_event_base_t);
+       unbound_io_event_t      *ev;
+
+       static struct ub_event_vmt      vmt = {
+               .add_bits       = _unbound_io_event_flags_add,
+               .del_bits       = _unbound_io_event_flags_del,
+               .set_fd         = _unbound_io_event_fd_set,
+               .free           = _unbound_io_event_free,
+               .add            = _unbound_io_event_activate,
+               .del            = _unbound_io_event_deactivate,
+               .add_timer      = _unbound_io_timer_modify,
+               .del_timer      = _unbound_io_timer_deactivate
+       };
+
+       MEM(ev = talloc_zero(ev_b, unbound_io_event_t));
+       ev->base.magic = UB_EVENT_MAGIC;        /* Magic value libunbound requires */
+       ev->base.vmt = &vmt;                    /* Callbacks for adding/removing timers/fd events */
+       ev->ev_b = ev_b;                        /* Our event base (containing the el ) */
+       ev->events = flags;                     /* When this event should fire */
+       ev->fd = fd;                            /* File descriptor to register events for */
+       ev->cb = cb;                            /* Callback to execute on event */
+       ev->uctx = uctx;                        /* Lib unbound's arg to pass to the cb */
+       ev->active = false;                     /* Event is not currently active */
+
+       DEBUG4("unbound event %p - Allocated - Events %i, FD %i, callback %p, uctx %p", ev, flags, fd, cb, uctx);
+
+       return (struct ub_event *)ev;
+}
+
+/** Initialises a ub_ctx, writing it to t->ub
+ *
+ * The ub_ctx is configured to use the el specified.
+ *
+ * When the thread ctx is freed, unbound_io_free should be called to gracefully
+ * free the ub_ctx, and then the event base structure it depends on.
+ *
+ * @param[out] t       Thread instance data to populate.
+ * @parma[in] el       To use to run the unbound event loop.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+int unbound_io_init(rlm_unbound_thread_t *t, fr_event_list_t *el)
+{
+       static struct ub_event_base_vmt vmt = {
+               .new_event      = _unbound_io_event_new
+       };
+
+       /*
+        *      Should be manually freed *AFTER* t->ub
+        *      is freed.  So must be parented from the NULL
+        *      ctx.
+        */
+       MEM(t->ev_b = talloc_zero(NULL, unbound_io_event_base_t));
+       t->ev_b->base.magic = UB_EVENT_MAGIC;
+       t->ev_b->base.vmt = &vmt;
+       t->ev_b->el = el;
+       t->ev_b->t = t;
+
+       /*
+        *      Create the main ub_ctx using our event base
+        *      which specifies how libunbound integrates
+        *      with our event loop.
+        */
+       t->ub = ub_ctx_create_ub_event((struct ub_event_base *)t->ev_b);
+       if (!t->ub) {
+               ERROR("Failed creating ub_ctx");
+               TALLOC_FREE(t->ev_b);
+               return -1;
+       }
+       return 0;
+}
+
+/** Frees ub_ctx and dependent memory
+ *
+ * @param[in,out] t    Thread instance data to free resources from.
+ */
+void unbound_io_free(rlm_unbound_thread_t *t)
+{
+       if (t->ub) {
+               ub_ctx_delete(t->ub);
+               t->ub = NULL;
+       }
+
+       TALLOC_FREE(t->ev_b);
+}
diff --git a/src/modules/rlm_unbound/io.h b/src/modules/rlm_unbound/io.h
new file mode 100644 (file)
index 0000000..b8ed7bd
--- /dev/null
@@ -0,0 +1,53 @@
+#pragma once
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ *
+ * @brief Function prototypes and datatypes for the REST (HTTP) transport.
+ * @file rlm_unbound/unbound.h
+ *
+ * @copyright 2019 The FreeRADIUS server project
+ * @copyright 2019 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
+ */
+RCSIDH(rlm_unbound_io_h, "$Id$")
+
+
+#ifdef HAVE_WDOCUMENTATION
+DIAG_OFF(documentation)
+#endif
+#include <unbound.h>
+#ifdef HAVE_WDOCUMENTATION
+DIAG_ON(documentation)
+#endif
+
+typedef struct unbound_io_event_base_s unbound_io_event_base_t;
+
+typedef struct {
+       struct ub_ctx           *ub;            //!< There's one unbound context per thread, as they
+                                               ///< contain the event list configuration, so we need
+                                               ///< one per worker event loop.
+       unbound_io_event_base_t *ev_b;          //!< Contains callbacks and configuration libunbound
+                                               ///< needs when creating new rlm_unbound_event_t.
+                                               ///< Must be freed after the ub_ctx.
+} rlm_unbound_thread_t;
+
+/*
+ *     io.c
+ */
+int    unbound_io_init(rlm_unbound_thread_t *t, fr_event_list_t *el);
+void   unbound_io_free(rlm_unbound_thread_t *t);
index 1490759d204f9f86bcaaab1f6af1126d44608a08..30949de38cdcb20b910955029c574ea6a90d9b88 100644 (file)
@@ -31,13 +31,7 @@ RCSID("$Id$")
 #include <freeradius-devel/server/log.h>
 #include <fcntl.h>
 
-#ifdef HAVE_WDOCUMENTATION
-DIAG_OFF(documentation)
-#endif
-#include <unbound.h>
-#ifdef HAVE_WDOCUMENTATION
-DIAG_ON(documentation)
-#endif
+#include "io.h"
 
 typedef struct {
        struct ub_ctx   *ub;   /* This must come first.  Do not move */
@@ -311,6 +305,27 @@ error0:
        return -1;
 }
 
+typedef struct {
+       struct ub_result        *result;        //!< The result from the previous operation.
+} dns_resume_ctx_t;
+
+/*
+static xlat_action_t xlat_ptr(TALLOC_CTX *ctx, fr_cursor_t *out,
+                             REQUEST *request, void const *xlat_inst, void *xlat_thread_inst,
+                             fr_value_box_t **in)
+{
+       if (!*in) return XLAT_ACTION_DONE;
+
+       if (fr_value_box_list_concat(ctx, *in, in, FR_TYPE_STRING, true) < 0) {
+               RPEDEBUG("Failed concatenating input string for attribute reference");
+               return XLAT_ACTION_FAIL;
+       }
+
+       yield_to
+
+}
+*/
+
 static ssize_t xlat_ptr(TALLOC_CTX *ctx, char **out, size_t outlen,
                        void const *mod_inst, UNUSED void const *xlat_inst,
                        REQUEST *request, char const *fmt)
@@ -375,32 +390,6 @@ static void ub_fd_handler(UNUSED fr_event_list_t *el, UNUSED int sock, UNUSED in
        }
 }
 
-static int mod_bootstrap(void *instance, CONF_SECTION *conf)
-{
-       rlm_unbound_t *inst = instance;
-
-       inst->name = cf_section_name2(conf);
-       if (!inst->name) inst->name = cf_section_name1(conf);
-
-       if (inst->timeout > 10000) {
-               cf_log_err(conf, "timeout must be 0 to 10000");
-               return -1;
-       }
-
-       MEM(inst->xlat_a_name = talloc_typed_asprintf(inst, "%s-a", inst->name));
-       MEM(inst->xlat_aaaa_name = talloc_typed_asprintf(inst, "%s-aaaa", inst->name));
-       MEM(inst->xlat_ptr_name = talloc_typed_asprintf(inst, "%s-ptr", inst->name));
-
-       if (xlat_register(inst, inst->xlat_a_name, xlat_a, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, false) ||
-           xlat_register(inst, inst->xlat_aaaa_name, xlat_aaaa, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, false) ||
-           xlat_register(inst, inst->xlat_ptr_name, xlat_ptr, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, false)) {
-               cf_log_err(conf, "Failed registering xlats");
-               return -1;
-       }
-
-       return 0;
-}
-
 static int mod_instantiate(void *instance, CONF_SECTION *conf)
 {
        rlm_unbound_t *inst = instance;
@@ -671,6 +660,32 @@ static int mod_instantiate(void *instance, CONF_SECTION *conf)
        return -1;
 }
 
+static int mod_bootstrap(void *instance, CONF_SECTION *conf)
+{
+       rlm_unbound_t *inst = instance;
+
+       inst->name = cf_section_name2(conf);
+       if (!inst->name) inst->name = cf_section_name1(conf);
+
+       if (inst->timeout > 10000) {
+               cf_log_err(conf, "timeout must be 0 to 10000");
+               return -1;
+       }
+
+       MEM(inst->xlat_a_name = talloc_typed_asprintf(inst, "%s-a", inst->name));
+       MEM(inst->xlat_aaaa_name = talloc_typed_asprintf(inst, "%s-aaaa", inst->name));
+       MEM(inst->xlat_ptr_name = talloc_typed_asprintf(inst, "%s-ptr", inst->name));
+
+       if (xlat_register(inst, inst->xlat_a_name, xlat_a, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, false) ||
+           xlat_register(inst, inst->xlat_aaaa_name, xlat_aaaa, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, false) ||
+           xlat_register(inst, inst->xlat_ptr_name, xlat_ptr, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, false)) {
+               cf_log_err(conf, "Failed registering xlats");
+               return -1;
+       }
+
+       return 0;
+}
+
 static int mod_detach(void *instance)
 {
        rlm_unbound_t *inst = instance;
@@ -709,12 +724,13 @@ static int mod_detach(void *instance)
 
 extern module_t rlm_unbound;
 module_t rlm_unbound = {
-       .magic          = RLM_MODULE_INIT,
-       .name           = "unbound",
-       .type           = RLM_TYPE_THREAD_SAFE,
-       .inst_size      = sizeof(rlm_unbound_t),
-       .config         = module_config,
-       .bootstrap      = mod_bootstrap,
-       .instantiate    = mod_instantiate,
-       .detach         = mod_detach
+       .magic                  = RLM_MODULE_INIT,
+       .name                   = "unbound",
+       .type                   = RLM_TYPE_THREAD_SAFE,
+       .inst_size              = sizeof(rlm_unbound_t),
+       .thread_inst_size       = sizeof(rlm_unbound_thread_t),
+       .config                 = module_config,
+       .bootstrap              = mod_bootstrap,
+       .instantiate            = mod_instantiate,
+       .detach                 = mod_detach
 };