root/lib/libagentx/agentx.c
/*      $OpenBSD: agentx.c,v 1.26 2025/12/08 10:22:19 jsg Exp $ */
/*
 * Copyright (c) 2019 Martijn van Duren <martijn@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 <netinet/in.h>

#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>

#include "agentx_internal.h"
#include <agentx.h>

/*
 * ax:          struct agentx
 * axs:         struct agentx_session
 * axc:         struct agentx_context
 * axr:         struct agentx_region
 * axi:         struct agentx_index
 * axo:         struct agentx_object
 * axg:         struct agentx_get
 * axv:         struct agentx_varbind
 * axr:         struct agentx_request
 * cstate:      current state
 * dstate:      desired state
 */

enum agentx_index_type {
        AXI_TYPE_NEW,
        AXI_TYPE_ANY,
        AXI_TYPE_VALUE,
        AXI_TYPE_DYNAMIC
};

#define AGENTX_CONTEXT_CTX(axc) (axc->axc_name_default ? NULL : \
    &(axc->axc_name))

struct agentx_agentcaps {
        struct agentx_context *axa_axc;
        struct ax_oid axa_oid;
        struct ax_ostring axa_descr;
        enum agentx_cstate axa_cstate;
        enum agentx_dstate axa_dstate;
        TAILQ_ENTRY(agentx_agentcaps) axa_axc_agentcaps;
};

struct agentx_region {
        struct agentx_context *axr_axc;
        struct ax_oid axr_oid;
        uint8_t axr_timeout;
        uint8_t axr_priority;
        enum agentx_cstate axr_cstate;
        enum agentx_dstate axr_dstate;
        TAILQ_HEAD(, agentx_index) axr_indices;
        TAILQ_HEAD(, agentx_object) axr_objects;
        TAILQ_ENTRY(agentx_region) axr_axc_regions;
};

struct agentx_index {
        struct agentx_region *axi_axr;
        enum agentx_index_type axi_type;
        struct ax_varbind axi_vb;
        struct agentx_object **axi_object;
        size_t axi_objectlen;
        size_t axi_objectsize;
        enum agentx_cstate axi_cstate;
        enum agentx_dstate axi_dstate;
        TAILQ_ENTRY(agentx_index) axi_axr_indices;
};

struct agentx_object {
        struct agentx_region *axo_axr;
        struct ax_oid axo_oid;
        struct agentx_index *axo_index[AGENTX_OID_INDEX_MAX_LEN];
        size_t axo_indexlen;
        int axo_implied;
        uint8_t axo_timeout;
        /* Prevent freeing object while in use by get and set requesets */
        uint32_t axo_lock;
        void (*axo_get)(struct agentx_varbind *);
        enum agentx_cstate axo_cstate;
        enum agentx_dstate axo_dstate;
        RB_ENTRY(agentx_object) axo_axc_objects;
        TAILQ_ENTRY(agentx_object) axo_axr_objects;
};

struct agentx_varbind {
        struct agentx_get *axv_axg;
        struct agentx_object *axv_axo;
        struct agentx_varbind_index {
                struct agentx_index *axv_axi;
                union ax_data axv_idata;
        } axv_index[AGENTX_OID_INDEX_MAX_LEN];
        size_t axv_indexlen;
        int axv_initialized;
        int axv_include;
        struct ax_varbind axv_vb;
        struct ax_oid axv_start;
        struct ax_oid axv_end;
        enum ax_pdu_error axv_error;
};

#define AGENTX_GET_CTX(axg) (axg->axg_context_default ? NULL : \
    &(axg->axg_context))
struct agentx_request {
        uint32_t axr_packetid;
        int (*axr_cb)(struct ax_pdu *, void *);
        void *axr_cookie;
        RB_ENTRY(agentx_request) axr_ax_requests;
};

static void agentx_start(struct agentx *);
static void agentx_finalize(struct agentx *, int);
static void agentx_wantwritenow(struct agentx *, int);
void (*agentx_wantwrite)(struct agentx *, int) =
    agentx_wantwritenow;
static void agentx_reset(struct agentx *);
static void agentx_free_finalize(struct agentx *);
static int agentx_session_retry(struct agentx_session *);
static int agentx_session_start(struct agentx_session *);
static int agentx_session_finalize(struct ax_pdu *, void *);
static int agentx_session_close(struct agentx_session *,
    enum ax_close_reason);
static int agentx_session_close_finalize(struct ax_pdu *, void *);
static void agentx_session_free_finalize(struct agentx_session *);
static void agentx_session_reset(struct agentx_session *);
static int agentx_context_retry(struct agentx_context *);
static void agentx_context_start(struct agentx_context *);
static void agentx_context_free_finalize(struct agentx_context *);
static void agentx_context_reset(struct agentx_context *);
static int agentx_agentcaps_start(struct agentx_agentcaps *);
static int agentx_agentcaps_finalize(struct ax_pdu *, void *);
static int agentx_agentcaps_close(struct agentx_agentcaps *);
static int agentx_agentcaps_close_finalize(struct ax_pdu *, void *);
static void agentx_agentcaps_free_finalize(struct agentx_agentcaps *);
static void agentx_agentcaps_reset(struct agentx_agentcaps *);
static int agentx_region_retry(struct agentx_region *);
static int agentx_region_start(struct agentx_region *);
static int agentx_region_finalize(struct ax_pdu *, void *);
static int agentx_region_close(struct agentx_region *);
static int agentx_region_close_finalize(struct ax_pdu *, void *);
static void agentx_region_free_finalize(struct agentx_region *);
static void agentx_region_reset(struct agentx_region *);
static struct agentx_index *agentx_index(struct agentx_region *,
    struct ax_varbind *, enum agentx_index_type);
static int agentx_index_start(struct agentx_index *);
static int agentx_index_finalize(struct ax_pdu *, void *);
static void agentx_index_free_finalize(struct agentx_index *);
static void agentx_index_reset(struct agentx_index *);
static int agentx_index_close(struct agentx_index *);
static int agentx_index_close_finalize(struct ax_pdu *, void *);
static int agentx_object_start(struct agentx_object *);
static int agentx_object_finalize(struct ax_pdu *, void *);
static int agentx_object_lock(struct agentx_object *);
static void agentx_object_unlock(struct agentx_object *);
static int agentx_object_close(struct agentx_object *);
static int agentx_object_close_finalize(struct ax_pdu *, void *);
static void agentx_object_free_finalize(struct agentx_object *);
static void agentx_object_reset(struct agentx_object *);
static int agentx_object_cmp(struct agentx_object *,
    struct agentx_object *);
static void agentx_get_start(struct agentx_context *,
    struct ax_pdu *);
static void agentx_get_finalize(struct agentx_get *);
static void agentx_get_free(struct agentx_get *);
static void agentx_varbind_start(struct agentx_varbind *);
static void agentx_varbind_finalize(struct agentx_varbind *);
static void agentx_varbind_nosuchobject(struct agentx_varbind *);
static void agentx_varbind_nosuchinstance(struct agentx_varbind *);
static void agentx_varbind_endofmibview(struct agentx_varbind *);
static void agentx_varbind_error_type(struct agentx_varbind *,
    enum ax_pdu_error, int);
static int agentx_request(struct agentx *, uint32_t,
    int (*)(struct ax_pdu *, void *), void *);
static int agentx_request_cmp(struct agentx_request *,
    struct agentx_request *);
static int agentx_strcat(char **, const char *);
static int agentx_oidfill(struct ax_oid *, const uint32_t[], size_t,
    const char **);

RB_PROTOTYPE_STATIC(ax_requests, agentx_request, axr_ax_requests,
    agentx_request_cmp)
RB_PROTOTYPE_STATIC(axc_objects, agentx_object, axo_axc_objects,
    agentx_object_cmp)

struct agentx *
agentx(void (*nofd)(struct agentx *, void *, int), void *cookie)
{
        struct agentx *ax;

        if ((ax = calloc(1, sizeof(*ax))) == NULL)
                return NULL;

        ax->ax_nofd = nofd;
        ax->ax_cookie = cookie;
        ax->ax_fd = -1;
        ax->ax_cstate = AX_CSTATE_CLOSE;
        ax->ax_dstate = AX_DSTATE_OPEN;
        TAILQ_INIT(&(ax->ax_sessions));
        TAILQ_INIT(&(ax->ax_getreqs));
        RB_INIT(&(ax->ax_requests));

        agentx_start(ax);

        return ax;
}

/*
 * agentx_finalize is not a suitable name for a public API,
 * but use it internally for consistency
 */
void
agentx_connect(struct agentx *ax, int fd)
{
        agentx_finalize(ax, fd);
}

void
agentx_retry(struct agentx *ax)
{
        struct agentx_session *axs;

        if (ax->ax_fd == -1)
                return;

        TAILQ_FOREACH(axs, &(ax->ax_sessions), axs_ax_sessions) {
                if (axs->axs_cstate == AX_CSTATE_OPEN) {
                        if (agentx_session_retry(axs) == -1)
                                return;
                } else if (axs->axs_cstate == AX_CSTATE_CLOSE) {
                        if (agentx_session_start(axs) == -1)
                                return;
                }
        }
}

static void
agentx_start(struct agentx *ax)
{
#ifdef AX_DEBUG
        if (ax->ax_cstate != AX_CSTATE_CLOSE ||
            ax->ax_dstate != AX_DSTATE_OPEN)
                agentx_log_ax_fatalx(ax, "%s: unexpected connect", __func__);
#endif
        ax->ax_cstate = AX_CSTATE_WAITOPEN;
        ax->ax_nofd(ax, ax->ax_cookie, 0);
}

static void
agentx_finalize(struct agentx *ax, int fd)
{
        struct agentx_session *axs;

        if (ax->ax_cstate != AX_CSTATE_WAITOPEN) {
#ifdef AX_DEBUG
                agentx_log_ax_fatalx(ax, "%s: agentx unexpected connect",
                    __func__);
#else
                agentx_log_ax_warnx(ax,
                    "%s: agentx unexpected connect: ignoring", __func__);
                return;
#endif
        }
        if ((ax->ax_ax = ax_new(fd)) == NULL) {
                agentx_log_ax_warn(ax, "failed to initialize");
                close(fd);
                agentx_reset(ax);
                return;
        }

        agentx_log_ax_info(ax, "new connection: %d", fd);

        ax->ax_fd = fd;
        ax->ax_cstate = AX_CSTATE_OPEN;

        TAILQ_FOREACH(axs, &(ax->ax_sessions), axs_ax_sessions) {
                if (agentx_session_start(axs) == -1)
                        break;
        }
}

static void
agentx_wantwritenow(struct agentx *ax, int fd)
{
        agentx_write(ax);
}

static void
agentx_reset(struct agentx *ax)
{
        struct agentx_session *axs, *taxs;
        struct agentx_request *axr;
        struct agentx_get *axg;
        int axfree = ax->ax_free;

        ax_free(ax->ax_ax);
        ax->ax_ax = NULL;
        ax->ax_fd = -1;
        ax->ax_free = 1;

        ax->ax_cstate = AX_CSTATE_CLOSE;

        while ((axr = RB_MIN(ax_requests, &(ax->ax_requests))) != NULL) {
                RB_REMOVE(ax_requests, &(ax->ax_requests), axr);
                free(axr);
        }
        TAILQ_FOREACH_SAFE(axs, &(ax->ax_sessions), axs_ax_sessions, taxs)
                agentx_session_reset(axs);
        while (!TAILQ_EMPTY(&(ax->ax_getreqs))) {
                axg = TAILQ_FIRST(&(ax->ax_getreqs));
                axg->axg_axc = NULL;
                TAILQ_REMOVE(&(ax->ax_getreqs), axg, axg_ax_getreqs);
        }

        if (ax->ax_dstate == AX_DSTATE_OPEN)
                agentx_start(ax);

        if (!axfree)
                agentx_free_finalize(ax);
}

void
agentx_free(struct agentx *ax)
{
        struct agentx_session *axs, *taxs;
        int axfree;

        if (ax == NULL)
                return;

        axfree = ax->ax_free;
        ax->ax_free = 1;

        /* Malloc throws abort on invalid pointers as well */
        if (ax->ax_dstate == AX_DSTATE_CLOSE)
                agentx_log_ax_fatalx(ax, "%s: double free", __func__);
        ax->ax_dstate = AX_DSTATE_CLOSE;

        TAILQ_FOREACH_SAFE(axs, &(ax->ax_sessions), axs_ax_sessions, taxs) {
                if (axs->axs_dstate != AX_DSTATE_CLOSE)
                        agentx_session_free(axs);
        }
        if (!axfree)
                agentx_free_finalize(ax);
}

static void
agentx_free_finalize(struct agentx *ax)
{
        struct agentx_session *axs, *taxs;

        ax->ax_free = 0;

        TAILQ_FOREACH_SAFE(axs, &(ax->ax_sessions), axs_ax_sessions, taxs)
                agentx_session_free_finalize(axs);

        if (!TAILQ_EMPTY(&(ax->ax_sessions)) ||
            !RB_EMPTY(&(ax->ax_requests)) ||
            ax->ax_dstate != AX_DSTATE_CLOSE)
                return;

        ax_free(ax->ax_ax);
        ax->ax_nofd(ax, ax->ax_cookie, 1);
        free(ax);
}

struct agentx_session *
agentx_session(struct agentx *ax, uint32_t oid[],
    size_t oidlen, const char *descr, uint8_t timeout)
{
        struct agentx_session *axs;
        const char *errstr;

        if ((axs = calloc(1, sizeof(*axs))) == NULL)
                return NULL;

        axs->axs_ax = ax;
        axs->axs_timeout = timeout;
        /* RFC 2741 section 6.2.1: may send a null Object Identifier */
        if (oidlen == 0)
                axs->axs_oid.aoi_idlen = oidlen;
        else {
                if (agentx_oidfill((&axs->axs_oid), oid, oidlen,
                    &errstr) == -1) {
#ifdef AX_DEBUG
                        agentx_log_ax_fatalx(ax, "%s: %s", __func__, errstr);
#else
                        free(axs);
                        return NULL;
#endif
                }
        }
        axs->axs_descr.aos_string = (unsigned char *)strdup(descr);
        if (axs->axs_descr.aos_string == NULL) {
                free(axs);
                return NULL;
        }
        axs->axs_descr.aos_slen = strlen(descr);
        axs->axs_cstate = AX_CSTATE_CLOSE;
        axs->axs_dstate = AX_DSTATE_OPEN;
        TAILQ_INIT(&(axs->axs_contexts));
        TAILQ_INSERT_HEAD(&(ax->ax_sessions), axs, axs_ax_sessions);

        if (ax->ax_cstate == AX_CSTATE_OPEN)
                (void) agentx_session_start(axs);

        return axs;
}

static int
agentx_session_retry(struct agentx_session *axs)
{
        struct agentx_context *axc;

#ifdef AX_DEBUG
        if (axs->axs_cstate != AX_CSTATE_OPEN)
                agentx_log_axs_fatalx(axs, "%s: unexpected retry", __func__);
#endif

        TAILQ_FOREACH(axc, &(axs->axs_contexts), axc_axs_contexts) {
                if (axc->axc_cstate == AX_CSTATE_OPEN) {
                        if (agentx_context_retry(axc) == -1)
                                return -1;
                } else if (axc->axc_cstate == AX_CSTATE_CLOSE)
                        agentx_context_start(axc);
        }
        return 0;
}

static int
agentx_session_start(struct agentx_session *axs)
{
        struct agentx *ax = axs->axs_ax;
        uint32_t packetid;

#ifdef AX_DEBUG
        if (ax->ax_cstate != AX_CSTATE_OPEN ||
            axs->axs_cstate != AX_CSTATE_CLOSE ||
            axs->axs_dstate != AX_DSTATE_OPEN)
                agentx_log_ax_fatalx(ax, "%s: unexpected session open",
                    __func__);
#endif
        packetid = ax_open(ax->ax_ax, axs->axs_timeout, &(axs->axs_oid),
            &(axs->axs_descr));
        if (packetid == 0) {
                agentx_log_ax_warn(ax, "couldn't generate %s",
                    ax_pdutype2string(AX_PDU_TYPE_OPEN));
                agentx_reset(ax);
                return -1;
        }
        axs->axs_packetid = packetid;
        agentx_log_ax_info(ax, "opening session");
        axs->axs_cstate = AX_CSTATE_WAITOPEN;
        return agentx_request(ax, packetid, agentx_session_finalize, axs);
}

static int
agentx_session_finalize(struct ax_pdu *pdu, void *cookie)
{
        struct agentx_session *axs = cookie;
        struct agentx *ax = axs->axs_ax;
        struct agentx_context *axc;

#ifdef AX_DEBUG
        if (axs->axs_cstate != AX_CSTATE_WAITOPEN)
                agentx_log_ax_fatalx(ax, "%s: not expecting new session",
                    __func__);
#endif

        if (pdu->ap_payload.ap_response.ap_error != AX_PDU_ERROR_NOERROR) {
                agentx_log_ax_warnx(ax, "failed to open session: %s",
                    ax_error2string(pdu->ap_payload.ap_response.ap_error));
                axs->axs_cstate = AX_CSTATE_CLOSE;
                return -1;
        }

        axs->axs_id = pdu->ap_header.aph_sessionid;
        axs->axs_cstate = AX_CSTATE_OPEN;

        if (axs->axs_dstate == AX_DSTATE_CLOSE) {
                agentx_session_close(axs, AX_CLOSE_SHUTDOWN);
                return 0;
        }

        agentx_log_axs_info(axs, "open");

        TAILQ_FOREACH(axc, &(axs->axs_contexts), axc_axs_contexts)
                agentx_context_start(axc);
        return 0;
}

static int
agentx_session_close(struct agentx_session *axs,
    enum ax_close_reason reason)
{
        struct agentx *ax = axs->axs_ax;
        uint32_t packetid;

#ifdef AX_DEBUG
        if (axs->axs_cstate != AX_CSTATE_OPEN)
                agentx_log_ax_fatalx(ax, "%s: unexpected session close",
                    __func__);
#endif
        if ((packetid = ax_close(ax->ax_ax, axs->axs_id, reason)) == 0) {
                agentx_log_axs_warn(axs, "couldn't generate %s",
                    ax_pdutype2string(AX_PDU_TYPE_CLOSE));
                agentx_reset(ax);
                return -1;
        }

        agentx_log_axs_info(axs, "closing session: %s",
            ax_closereason2string(reason));

        axs->axs_cstate = AX_CSTATE_WAITCLOSE;
        return agentx_request(ax, packetid, agentx_session_close_finalize,
            axs);
}

static int
agentx_session_close_finalize(struct ax_pdu *pdu, void *cookie)
{
        struct agentx_session *axs = cookie;
        struct agentx *ax = axs->axs_ax;
        struct agentx_context *axc, *taxc;
        int axfree = ax->ax_free;

#ifdef AX_DEBUG
        if (axs->axs_cstate != AX_CSTATE_WAITCLOSE)
                agentx_log_axs_fatalx(axs, "%s: not expecting session close",
                    __func__);
#endif

        if (pdu->ap_payload.ap_response.ap_error != AX_PDU_ERROR_NOERROR) {
                agentx_log_axs_warnx(axs, "failed to close session: %s",
                    ax_error2string(pdu->ap_payload.ap_response.ap_error));
                agentx_reset(ax);
                return -1;
        }

        axs->axs_cstate = AX_CSTATE_CLOSE;
        ax->ax_free = 1;

        agentx_log_axs_info(axs, "closed");

        TAILQ_FOREACH_SAFE(axc, &(axs->axs_contexts), axc_axs_contexts, taxc)
                agentx_context_reset(axc);

        if (ax->ax_cstate == AX_CSTATE_OPEN &&
            axs->axs_dstate == AX_DSTATE_OPEN)
                agentx_session_start(axs);
        if (!axfree)
                agentx_free_finalize(ax);
                
        return 0;
}

void
agentx_session_free(struct agentx_session *axs)
{
        struct agentx_context *axc, *taxc;
        struct agentx *ax;
        int axfree;

        if (axs == NULL)
                return;

        ax = axs->axs_ax;
        axfree = ax->ax_free;
        ax->ax_free = 1;

        if (axs->axs_dstate == AX_DSTATE_CLOSE)
                agentx_log_axs_fatalx(axs, "%s: double free", __func__);

        axs->axs_dstate = AX_DSTATE_CLOSE;

        if (axs->axs_cstate == AX_CSTATE_OPEN)
                (void) agentx_session_close(axs, AX_CLOSE_SHUTDOWN);

        TAILQ_FOREACH_SAFE(axc, &(axs->axs_contexts), axc_axs_contexts, taxc) {
                if (axc->axc_dstate != AX_DSTATE_CLOSE)
                        agentx_context_free(axc);
        }

        if (!axfree)
                agentx_free_finalize(ax);
}

static void
agentx_session_free_finalize(struct agentx_session *axs)
{
        struct agentx *ax = axs->axs_ax;
        struct agentx_context *axc, *taxc;

        TAILQ_FOREACH_SAFE(axc, &(axs->axs_contexts), axc_axs_contexts, taxc)
                agentx_context_free_finalize(axc);

        if (!TAILQ_EMPTY(&(axs->axs_contexts)) ||
            axs->axs_cstate != AX_CSTATE_CLOSE ||
            axs->axs_dstate != AX_DSTATE_CLOSE)
                return;

        TAILQ_REMOVE(&(ax->ax_sessions), axs, axs_ax_sessions);
        free(axs->axs_descr.aos_string);
        free(axs);
}

static void
agentx_session_reset(struct agentx_session *axs)
{
        struct agentx_context *axc, *taxc;
        struct agentx *ax = axs->axs_ax;
        int axfree = ax->ax_free;

        ax->ax_free = 1;

        axs->axs_cstate = AX_CSTATE_CLOSE;

        TAILQ_FOREACH_SAFE(axc, &(axs->axs_contexts), axc_axs_contexts, taxc)
                agentx_context_reset(axc);

        if (!axfree)
                agentx_free_finalize(ax);
}

struct agentx_context *
agentx_context(struct agentx_session *axs, const char *name)
{
        struct agentx_context *axc;

        if (axs->axs_dstate == AX_DSTATE_CLOSE)
                agentx_log_axs_fatalx(axs, "%s: use after free", __func__);

        if ((axc = calloc(1, sizeof(*axc))) == NULL)
                return NULL;

        axc->axc_axs = axs;
        axc->axc_name_default = (name == NULL);
        if (name != NULL) {
                axc->axc_name.aos_string = (unsigned char *)strdup(name);
                if (axc->axc_name.aos_string == NULL) {
                        free(axc);
                        return NULL;
                }
                axc->axc_name.aos_slen = strlen(name);
        }
        axc->axc_cstate = axs->axs_cstate == AX_CSTATE_OPEN ?
            AX_CSTATE_OPEN : AX_CSTATE_CLOSE;
        axc->axc_dstate = AX_DSTATE_OPEN;
        TAILQ_INIT(&(axc->axc_agentcaps));
        TAILQ_INIT(&(axc->axc_regions));

        TAILQ_INSERT_HEAD(&(axs->axs_contexts), axc, axc_axs_contexts);

        return axc;
}

static int
agentx_context_retry(struct agentx_context *axc)
{
        struct agentx_agentcaps *axa;
        struct agentx_region *axr;

#ifdef AX_DEBUG
        if (axc->axc_cstate != AX_CSTATE_OPEN)
                agentx_log_axc_fatalx(axc, "%s: unexpected retry", __func__);
#endif

        TAILQ_FOREACH(axa, &(axc->axc_agentcaps), axa_axc_agentcaps) {
                if (axa->axa_cstate == AX_CSTATE_CLOSE) {
                        if (agentx_agentcaps_start(axa) == -1)
                                return -1;
                }
        }
        TAILQ_FOREACH(axr, &(axc->axc_regions), axr_axc_regions) {
                if (axr->axr_cstate == AX_CSTATE_OPEN) {
                        if (agentx_region_retry(axr) == -1)
                                return -1;
                } else if (axr->axr_cstate == AX_CSTATE_CLOSE) {
                        if (agentx_region_start(axr) == -1)
                                return -1;
                }
        }
        return 0;
}


static void
agentx_context_start(struct agentx_context *axc)
{
        struct agentx_agentcaps *axa;
        struct agentx_region *axr;

#ifdef AX_DEBUG
        if (axc->axc_cstate != AX_CSTATE_CLOSE)
                agentx_log_axc_fatalx(axc, "%s: unexpected context start",
                    __func__);
#endif
        axc->axc_cstate = AX_CSTATE_OPEN;

        TAILQ_FOREACH(axa, &(axc->axc_agentcaps), axa_axc_agentcaps) {
                if (agentx_agentcaps_start(axa) == -1)
                        return;
        }
        TAILQ_FOREACH(axr, &(axc->axc_regions), axr_axc_regions) {
                if (agentx_region_start(axr) == -1)
                        return;
        }
}

uint32_t
agentx_context_uptime(struct agentx_context *axc)
{
        struct timespec cur, res;

        if (axc->axc_sysuptimespec.tv_sec == 0 &&
            axc->axc_sysuptimespec.tv_nsec == 0)
                return 0;

        (void) clock_gettime(CLOCK_MONOTONIC, &cur);

        timespecsub(&cur, &(axc->axc_sysuptimespec), &res);

        return axc->axc_sysuptime +
            (uint32_t) ((res.tv_sec * 100) + (res.tv_nsec / 10000000));
}

struct agentx_object *
agentx_context_object_find(struct agentx_context *axc,
    const uint32_t oid[], size_t oidlen, int active, int instance)
{
        struct agentx_object *axo, axo_search;
        const char *errstr;

        if (agentx_oidfill(&(axo_search.axo_oid), oid, oidlen, &errstr) == -1) {
                if (oidlen > AGENTX_OID_MIN_LEN) {
#ifdef AX_DEBUG
                        agentx_log_axc_fatalx(axc, "%s: %s", __func__, errstr);
#else
                        agentx_log_axc_warnx(axc, "%s: %s", __func__, errstr);
                        return NULL;
                }
#endif
                if (oidlen == 1)
                        axo_search.axo_oid.aoi_id[0] = oid[0];
                axo_search.axo_oid.aoi_idlen = oidlen;
        }

        axo = RB_FIND(axc_objects, &(axc->axc_objects), &axo_search);
        while (axo == NULL && !instance && axo_search.axo_oid.aoi_idlen > 0) {
                axo = RB_FIND(axc_objects, &(axc->axc_objects), &axo_search);
                axo_search.axo_oid.aoi_idlen--;
        }
        if (active && axo != NULL && axo->axo_cstate != AX_CSTATE_OPEN)
                return NULL;
        return axo;
}

struct agentx_object *
agentx_context_object_nfind(struct agentx_context *axc,
    const uint32_t oid[], size_t oidlen, int active, int inclusive)
{
        struct agentx_object *axo, axo_search;
        const char *errstr;

        if (agentx_oidfill(&(axo_search.axo_oid), oid, oidlen, &errstr) == -1) {
                if (oidlen > AGENTX_OID_MIN_LEN) {
#ifdef AX_DEBUG
                        agentx_log_axc_fatalx(axc, "%s: %s", __func__, errstr);
#else
                        agentx_log_axc_warnx(axc, "%s: %s", __func__, errstr);
                        return NULL;
#endif
                }
                if (oidlen == 1)
                        axo_search.axo_oid.aoi_id[0] = oid[0];
                axo_search.axo_oid.aoi_idlen = oidlen;
        }

        axo = RB_NFIND(axc_objects, &(axc->axc_objects), &axo_search);
        if (!inclusive && axo != NULL &&
            ax_oid_cmp(&(axo->axo_oid), &(axo_search.axo_oid)) <= 0) {
                axo = RB_NEXT(axc_objects, &(axc->axc_objects), axo);
        }

        while (active && axo != NULL && axo->axo_cstate != AX_CSTATE_OPEN)
                axo = RB_NEXT(axc_objects, &(axc->axc_objects), axo);
        return axo;
}

void
agentx_context_free(struct agentx_context *axc)
{
        struct agentx_agentcaps *axa, *taxa;
        struct agentx_region *axr, *taxr;

        if (axc == NULL)
                return;

#ifdef AX_DEBUG
        if (axc->axc_dstate == AX_DSTATE_CLOSE)
                agentx_log_axc_fatalx(axc, "%s: double free", __func__);
#endif
        axc->axc_dstate = AX_DSTATE_CLOSE;

        TAILQ_FOREACH_SAFE(axa, &(axc->axc_agentcaps), axa_axc_agentcaps,
            taxa) {
                if (axa->axa_dstate != AX_DSTATE_CLOSE)
                        agentx_agentcaps_free(axa);
        }
        TAILQ_FOREACH_SAFE(axr, &(axc->axc_regions), axr_axc_regions, taxr) {
                if (axr->axr_dstate != AX_DSTATE_CLOSE)
                        agentx_region_free(axr);
        }
}

static void
agentx_context_free_finalize(struct agentx_context *axc)
{
        struct agentx_session *axs = axc->axc_axs;
        struct agentx_region *axr, *taxr;
        struct agentx_agentcaps *axa, *taxa;

        TAILQ_FOREACH_SAFE(axa, &(axc->axc_agentcaps), axa_axc_agentcaps, taxa)
                agentx_agentcaps_free_finalize(axa);
        TAILQ_FOREACH_SAFE(axr, &(axc->axc_regions), axr_axc_regions, taxr)
                agentx_region_free_finalize(axr);

        if (!TAILQ_EMPTY(&(axc->axc_regions)) ||
            !TAILQ_EMPTY(&(axc->axc_agentcaps)) ||
            axc->axc_cstate != AX_CSTATE_CLOSE ||
            axc->axc_dstate != AX_DSTATE_CLOSE)
                return;

        TAILQ_REMOVE(&(axs->axs_contexts), axc, axc_axs_contexts);
        free(axc->axc_name.aos_string);
        free(axc);
}

static void
agentx_context_reset(struct agentx_context *axc)
{
        struct agentx_agentcaps *axa, *taxa;
        struct agentx_region *axr, *taxr;
        struct agentx *ax = axc->axc_axs->axs_ax;
        int axfree = ax->ax_free;

        ax->ax_free = 1;

        axc->axc_cstate = AX_CSTATE_CLOSE;
        axc->axc_sysuptimespec.tv_sec = 0;
        axc->axc_sysuptimespec.tv_nsec = 0;

        TAILQ_FOREACH_SAFE(axa, &(axc->axc_agentcaps), axa_axc_agentcaps, taxa)
                agentx_agentcaps_reset(axa);
        TAILQ_FOREACH_SAFE(axr, &(axc->axc_regions), axr_axc_regions, taxr)
                agentx_region_reset(axr);

        if (!axfree)
                agentx_free_finalize(ax);
}

struct agentx_agentcaps *
agentx_agentcaps(struct agentx_context *axc, uint32_t oid[],
    size_t oidlen, const char *descr)
{
        struct agentx_agentcaps *axa;
        const char *errstr;

        if (axc->axc_dstate == AX_DSTATE_CLOSE)
                agentx_log_axc_fatalx(axc, "%s: use after free", __func__);

        if ((axa = calloc(1, sizeof(*axa))) == NULL)
                return NULL;

        axa->axa_axc = axc;
        if (agentx_oidfill(&(axa->axa_oid), oid, oidlen, &errstr) == -1) {
#ifdef AX_DEBUG
                agentx_log_axc_fatalx(axc, "%s: %s", __func__, errstr);
#else
                agentx_log_axc_warnx(axc, "%s: %s", __func__, errstr);
                free(axa);
                return NULL;
#endif
        }
        axa->axa_descr.aos_string = (unsigned char *)strdup(descr);
        if (axa->axa_descr.aos_string == NULL) {
                free(axa);
                return NULL;
        }
        axa->axa_descr.aos_slen = strlen(descr);
        axa->axa_cstate = AX_CSTATE_CLOSE;
        axa->axa_dstate = AX_DSTATE_OPEN;

        TAILQ_INSERT_TAIL(&(axc->axc_agentcaps), axa, axa_axc_agentcaps);

        if (axc->axc_cstate == AX_CSTATE_OPEN)
                agentx_agentcaps_start(axa);

        return axa;
}

static int
agentx_agentcaps_start(struct agentx_agentcaps *axa)
{
        struct agentx_context *axc = axa->axa_axc;
        struct agentx_session *axs = axc->axc_axs;
        struct agentx *ax = axs->axs_ax;
        uint32_t packetid;

#ifdef AX_DEBUG
        if (axc->axc_cstate != AX_CSTATE_OPEN ||
            axa->axa_cstate != AX_CSTATE_CLOSE ||
            axa->axa_dstate != AX_DSTATE_OPEN)
                agentx_log_axc_fatalx(axc,
                    "%s: unexpected region registration", __func__);
#endif

        packetid = ax_addagentcaps(ax->ax_ax, axs->axs_id,
            AGENTX_CONTEXT_CTX(axc), &(axa->axa_oid), &(axa->axa_descr));
        if (packetid == 0) {
                agentx_log_axc_warn(axc, "couldn't generate %s",
                    ax_pdutype2string(AX_PDU_TYPE_ADDAGENTCAPS));
                agentx_reset(ax);
                return -1;
        }
        agentx_log_axc_info(axc, "agentcaps %s: opening",
            ax_oid2string(&(axa->axa_oid)));
        axa->axa_cstate = AX_CSTATE_WAITOPEN;
        return agentx_request(ax, packetid, agentx_agentcaps_finalize,
            axa);
}

static int
agentx_agentcaps_finalize(struct ax_pdu *pdu, void *cookie)
{
        struct agentx_agentcaps *axa = cookie;
        struct agentx_context *axc = axa->axa_axc;

#ifdef AX_DEBUG
        if (axa->axa_cstate != AX_CSTATE_WAITOPEN)
                agentx_log_axc_fatalx(axc,
                    "%s: not expecting agentcaps open", __func__);
#endif

        if (pdu->ap_payload.ap_response.ap_error != AX_PDU_ERROR_NOERROR) {
                /* Agentcaps failing is nothing too serious */
                agentx_log_axc_warn(axc, "agentcaps %s: %s",
                    ax_oid2string(&(axa->axa_oid)),
                    ax_error2string(pdu->ap_payload.ap_response.ap_error));
                axa->axa_cstate = AX_CSTATE_CLOSE;
                return 0;
        }

        axa->axa_cstate = AX_CSTATE_OPEN;

        agentx_log_axc_info(axc, "agentcaps %s: open",
            ax_oid2string(&(axa->axa_oid)));

        if (axa->axa_dstate == AX_DSTATE_CLOSE)
                agentx_agentcaps_close(axa);

        return 0;
}

static int
agentx_agentcaps_close(struct agentx_agentcaps *axa)
{
        struct agentx_context *axc = axa->axa_axc;
        struct agentx_session *axs = axc->axc_axs;
        struct agentx *ax = axs->axs_ax;
        uint32_t packetid;

#ifdef AX_DEBUG
        if (axa->axa_cstate != AX_CSTATE_OPEN)
                agentx_log_axc_fatalx(axc, "%s: unexpected agentcaps close",
                    __func__);
#endif

        axa->axa_cstate = AX_CSTATE_WAITCLOSE;
        if (axs->axs_cstate == AX_CSTATE_WAITCLOSE)
                return 0;

        packetid = ax_removeagentcaps(ax->ax_ax, axs->axs_id,
            AGENTX_CONTEXT_CTX(axc), &(axa->axa_oid));
        if (packetid == 0) {
                agentx_log_axc_warn(axc, "couldn't generate %s",
                    ax_pdutype2string(AX_PDU_TYPE_REMOVEAGENTCAPS));
                agentx_reset(ax);
                return -1;
        }
        agentx_log_axc_info(axc, "agentcaps %s: closing",
            ax_oid2string(&(axa->axa_oid)));
        return agentx_request(ax, packetid,
            agentx_agentcaps_close_finalize, axa);
}

static int
agentx_agentcaps_close_finalize(struct ax_pdu *pdu, void *cookie)
{
        struct agentx_agentcaps *axa = cookie;
        struct agentx_context *axc = axa->axa_axc;
        struct agentx_session *axs = axc->axc_axs;
        struct agentx *ax = axs->axs_ax;
        int axfree = ax->ax_free;

#ifdef AX_DEBUG
        if (axa->axa_cstate != AX_CSTATE_WAITCLOSE)
                agentx_log_axc_fatalx(axc, "%s: unexpected agentcaps close",
                    __func__);
#endif

        if (pdu->ap_payload.ap_response.ap_error != AX_PDU_ERROR_NOERROR) {
                agentx_log_axc_warnx(axc, "agentcaps %s: %s",
                    ax_oid2string(&(axa->axa_oid)),
                    ax_error2string(pdu->ap_payload.ap_response.ap_error));
                agentx_reset(ax);
                return -1;
        }

        axa->axa_cstate = AX_CSTATE_CLOSE;
        ax->ax_free = 1;

        agentx_log_axc_info(axc, "agentcaps %s: closed",
            ax_oid2string(&(axa->axa_oid)));

        if (axc->axc_cstate == AX_CSTATE_OPEN &&
            axa->axa_dstate == AX_DSTATE_OPEN)
                agentx_agentcaps_start(axa);

        if (!axfree)
                agentx_free_finalize(ax);
        return 0;
}

void
agentx_agentcaps_free(struct agentx_agentcaps *axa)
{
        struct agentx *ax;
        int axfree;

        if (axa == NULL)
                return;

        ax = axa->axa_axc->axc_axs->axs_ax;

        axfree = ax->ax_free;
        ax->ax_free = 1;

        if (axa->axa_dstate == AX_DSTATE_CLOSE)
                agentx_log_axc_fatalx(axa->axa_axc, "%s: double free",
                    __func__);

        axa->axa_dstate = AX_DSTATE_CLOSE;

        if (axa->axa_cstate == AX_CSTATE_OPEN)
                agentx_agentcaps_close(axa);

        if (!axfree)
                agentx_free_finalize(ax);
}

static void
agentx_agentcaps_free_finalize(struct agentx_agentcaps *axa)
{
        struct agentx_context *axc = axa->axa_axc;

        if (axa->axa_dstate != AX_DSTATE_CLOSE ||
            axa->axa_cstate != AX_CSTATE_CLOSE)
                return;

        TAILQ_REMOVE(&(axc->axc_agentcaps), axa, axa_axc_agentcaps);
        free(axa->axa_descr.aos_string);
        free(axa);
}

static void
agentx_agentcaps_reset(struct agentx_agentcaps *axa)
{
        struct agentx *ax = axa->axa_axc->axc_axs->axs_ax;

        axa->axa_cstate = AX_CSTATE_CLOSE;

        if (!ax->ax_free)
                agentx_free_finalize(ax);
}

struct agentx_region *
agentx_region(struct agentx_context *axc, uint32_t oid[],
    size_t oidlen, uint8_t timeout)
{
        struct agentx_region *axr;
        struct ax_oid tmpoid;
        const char *errstr;

        if (axc->axc_dstate == AX_DSTATE_CLOSE)
                agentx_log_axc_fatalx(axc, "%s: use after free", __func__);

        if (agentx_oidfill(&tmpoid, oid, oidlen, &errstr) == -1) {
#ifdef AX_DEBUG
                agentx_log_axc_fatalx(axc, "%s: %s", __func__, errstr);
#else
                return NULL;
#endif
                
        }
        TAILQ_FOREACH(axr, &(axc->axc_regions), axr_axc_regions) {
                if (ax_oid_cmp(&(axr->axr_oid), &tmpoid) == 0) {
#ifdef AX_DEBUG
                        agentx_log_axc_fatalx(axc,
                            "%s: duplicate region registration", __func__);
#else
                        errno = EINVAL;
                        return NULL;
#endif
                }
        }

        if ((axr = calloc(1, sizeof(*axr))) == NULL)
                return NULL;

        axr->axr_axc = axc;
        axr->axr_timeout = timeout;
        axr->axr_priority = AX_PRIORITY_DEFAULT;
        bcopy(&tmpoid, &(axr->axr_oid), sizeof(axr->axr_oid));
        axr->axr_cstate = AX_CSTATE_CLOSE;
        axr->axr_dstate = AX_DSTATE_OPEN;
        TAILQ_INIT(&(axr->axr_indices));
        TAILQ_INIT(&(axr->axr_objects));

        TAILQ_INSERT_HEAD(&(axc->axc_regions), axr, axr_axc_regions);

        if (axc->axc_cstate == AX_CSTATE_OPEN)
                (void) agentx_region_start(axr);

        return axr;
}

static int
agentx_region_retry(struct agentx_region *axr)
{
        struct agentx_index *axi;
        struct agentx_object *axo;

#ifdef AX_DEBUG
        if (axr->axr_cstate != AX_CSTATE_OPEN)
                agentx_log_axc_fatalx(axr->axr_axc,
                    "%s: unexpected retry", __func__);
#endif

        TAILQ_FOREACH(axi, &(axr->axr_indices), axi_axr_indices) {
                if (axi->axi_cstate == AX_CSTATE_CLOSE) {
                        if (agentx_index_start(axi) == -1)
                                return -1;
                }
        }
        TAILQ_FOREACH(axo, &(axr->axr_objects), axo_axr_objects) {
                if (axo->axo_cstate == AX_CSTATE_CLOSE) {
                        if (agentx_object_start(axo) == -1)
                                return -1;
                }
        }
        return 0;
}

static int
agentx_region_start(struct agentx_region *axr)
{
        struct agentx_context *axc = axr->axr_axc;
        struct agentx_session *axs = axc->axc_axs;
        struct agentx *ax = axs->axs_ax;
        uint32_t packetid;

#ifdef AX_DEBUG
        if (axc->axc_cstate != AX_CSTATE_OPEN ||
            axr->axr_cstate != AX_CSTATE_CLOSE ||
            axr->axr_dstate != AX_DSTATE_OPEN)
                agentx_log_axc_fatalx(axc,
                    "%s: unexpected region registration", __func__);
#endif

        packetid = ax_register(ax->ax_ax, 0, axs->axs_id,
            AGENTX_CONTEXT_CTX(axc), axr->axr_timeout, axr->axr_priority,
            0, &(axr->axr_oid), 0);
        if (packetid == 0) {
                agentx_log_axc_warn(axc, "couldn't generate %s",
                    ax_pdutype2string(AX_PDU_TYPE_REGISTER));
                agentx_reset(ax);
                return -1;
        }
        agentx_log_axc_info(axc, "region %s: opening",
            ax_oid2string(&(axr->axr_oid)));
        axr->axr_cstate = AX_CSTATE_WAITOPEN;
        return agentx_request(ax, packetid, agentx_region_finalize, axr);
}

static int
agentx_region_finalize(struct ax_pdu *pdu, void *cookie)
{
        struct agentx_region *axr = cookie;
        struct agentx_context *axc = axr->axr_axc;
        struct agentx_index *axi;
        struct agentx_object *axo;

#ifdef AX_DEBUG
        if (axr->axr_cstate != AX_CSTATE_WAITOPEN)
                agentx_log_axc_fatalx(axc, "%s: not expecting region open",
                    __func__);
#endif

        if (pdu->ap_payload.ap_response.ap_error == AX_PDU_ERROR_NOERROR) {
                axr->axr_cstate = AX_CSTATE_OPEN;
                agentx_log_axc_info(axc, "region %s: open",
                    ax_oid2string(&(axr->axr_oid)));
        } else if (pdu->ap_payload.ap_response.ap_error ==
            AX_PDU_ERROR_DUPLICATEREGISTRATION) {
                axr->axr_cstate = AX_CSTATE_CLOSE;
                /* Try at lower priority: first come first serve */
                if ((++axr->axr_priority) != 0) {
                        agentx_log_axc_warnx(axc, "region %s: duplicate, "
                            "reducing priority",
                            ax_oid2string(&(axr->axr_oid)));
                        return agentx_region_start(axr);
                }
                agentx_log_axc_info(axc, "region %s: duplicate, can't "
                    "reduce priority, ignoring",
                    ax_oid2string(&(axr->axr_oid)));
        } else {
                axr->axr_cstate = AX_CSTATE_CLOSE;
                agentx_log_axc_warnx(axc, "region %s: %s",
                     ax_oid2string(&(axr->axr_oid)),
                     ax_error2string(pdu->ap_payload.ap_response.ap_error));
                return -1;
        }

        if (axr->axr_dstate == AX_DSTATE_CLOSE) {
                if (agentx_region_close(axr) == -1)
                        return -1;
        } else {
                TAILQ_FOREACH(axi, &(axr->axr_indices), axi_axr_indices) {
                        if (agentx_index_start(axi) == -1)
                                return -1;
                }
                TAILQ_FOREACH(axo, &(axr->axr_objects), axo_axr_objects) {
                        if (agentx_object_start(axo) == -1)
                                return -1;
                }
        }
        return 0;
}

static int
agentx_region_close(struct agentx_region *axr)
{
        struct agentx_context *axc = axr->axr_axc;
        struct agentx_session *axs = axc->axc_axs;
        struct agentx *ax = axs->axs_ax;
        uint32_t packetid;

#ifdef AX_DEBUG
        if (axr->axr_cstate != AX_CSTATE_OPEN)
                agentx_log_axc_fatalx(axc, "%s: unexpected region close",
                    __func__);
#endif

        axr->axr_cstate = AX_CSTATE_WAITCLOSE;
        if (axs->axs_cstate == AX_CSTATE_WAITCLOSE)
                return 0;

        packetid = ax_unregister(ax->ax_ax, axs->axs_id,
            AGENTX_CONTEXT_CTX(axc), axr->axr_priority, 0, &(axr->axr_oid),
            0);
        if (packetid == 0) {
                agentx_log_axc_warn(axc, "couldn't generate %s",
                    ax_pdutype2string(AX_PDU_TYPE_UNREGISTER));
                agentx_reset(ax);
                return -1;
        }
        agentx_log_axc_info(axc, "region %s: closing",
            ax_oid2string(&(axr->axr_oid)));
        return agentx_request(ax, packetid, agentx_region_close_finalize,
            axr);
}

static int
agentx_region_close_finalize(struct ax_pdu *pdu, void *cookie)
{
        struct agentx_region *axr = cookie;
        struct agentx_context *axc = axr->axr_axc;
        struct agentx_session *axs = axc->axc_axs;
        struct agentx *ax = axs->axs_ax;
        int axfree = ax->ax_free;

#ifdef AX_DEBUG
        if (axr->axr_cstate != AX_CSTATE_WAITCLOSE)
                agentx_log_axc_fatalx(axc, "%s: unexpected region close",
                    __func__);
#endif

        if (pdu->ap_payload.ap_response.ap_error != AX_PDU_ERROR_NOERROR) {
                agentx_log_axc_warnx(axc, "closing %s: %s",
                    ax_oid2string(&(axr->axr_oid)),
                    ax_error2string(pdu->ap_payload.ap_response.ap_error));
                agentx_reset(ax);
                return -1;
        }

        ax->ax_free = 1;
        axr->axr_priority = AX_PRIORITY_DEFAULT;
        axr->axr_cstate = AX_CSTATE_CLOSE;

        agentx_log_axc_info(axc, "region %s: closed",
            ax_oid2string(&(axr->axr_oid)));

        if (axc->axc_cstate == AX_CSTATE_OPEN &&
            axr->axr_dstate == AX_DSTATE_OPEN)
                agentx_region_start(axr);

        if (!axfree)
                agentx_free_finalize(ax);
        return 0;
}

void
agentx_region_free(struct agentx_region *axr)
{
        struct agentx_index *axi, *taxi;
        struct agentx_object *axo, *taxo;
        struct agentx *ax;
        int axfree;

        if (axr == NULL)
                return;

        ax = axr->axr_axc->axc_axs->axs_ax;
        axfree = ax->ax_free;
        ax->ax_free = 1;

        if (axr->axr_dstate == AX_DSTATE_CLOSE)
                agentx_log_axc_fatalx(axr->axr_axc, "%s: double free",
                    __func__);

        axr->axr_dstate = AX_DSTATE_CLOSE;

        TAILQ_FOREACH_SAFE(axi, &(axr->axr_indices), axi_axr_indices, taxi) {
                if (axi->axi_dstate != AX_DSTATE_CLOSE)
                        agentx_index_free(axi);
        }

        TAILQ_FOREACH_SAFE(axo, &(axr->axr_objects), axo_axr_objects, taxo) {
                if (axo->axo_dstate != AX_DSTATE_CLOSE)
                        agentx_object_free(axo);
        }

        if (axr->axr_cstate == AX_CSTATE_OPEN)
                agentx_region_close(axr);

        if (!axfree)
                agentx_free_finalize(ax);
}

static void
agentx_region_free_finalize(struct agentx_region *axr)
{
        struct agentx_context *axc = axr->axr_axc;
        struct agentx_index *axi, *taxi;
        struct agentx_object *axo, *taxo;

        TAILQ_FOREACH_SAFE(axo, &(axr->axr_objects), axo_axr_objects, taxo)
                agentx_object_free_finalize(axo);
        TAILQ_FOREACH_SAFE(axi, &(axr->axr_indices), axi_axr_indices, taxi)
                agentx_index_free_finalize(axi);

        if (!TAILQ_EMPTY(&(axr->axr_indices)) ||
            !TAILQ_EMPTY(&(axr->axr_objects)) ||
            axr->axr_cstate != AX_CSTATE_CLOSE ||
            axr->axr_dstate != AX_DSTATE_CLOSE)
                return;

        TAILQ_REMOVE(&(axc->axc_regions), axr, axr_axc_regions);
        free(axr);
}

static void
agentx_region_reset(struct agentx_region *axr)
{
        struct agentx_index *axi, *taxi;
        struct agentx_object *axo, *taxo;
        struct agentx *ax = axr->axr_axc->axc_axs->axs_ax;
        int axfree = ax->ax_free;

        axr->axr_cstate = AX_CSTATE_CLOSE;
        axr->axr_priority = AX_PRIORITY_DEFAULT;
        ax->ax_free = 1;

        TAILQ_FOREACH_SAFE(axi, &(axr->axr_indices), axi_axr_indices, taxi)
                agentx_index_reset(axi);
        TAILQ_FOREACH_SAFE(axo, &(axr->axr_objects), axo_axr_objects, taxo)
                agentx_object_reset(axo);

        if (!axfree)
                agentx_free_finalize(ax);
}

struct agentx_index *
agentx_index_integer_new(struct agentx_region *axr, uint32_t oid[],
    size_t oidlen)
{
        struct ax_varbind vb;
        const char *errstr;

        vb.avb_type = AX_DATA_TYPE_INTEGER;
        if (agentx_oidfill(&(vb.avb_oid), oid, oidlen, &errstr) == -1) {
#ifdef AX_DEBUG
                agentx_log_axc_fatalx(axr->axr_axc, "%s: %s", __func__, errstr);
#else
                agentx_log_axc_warnx(axr->axr_axc, "%s: %s", __func__, errstr);
                return NULL;
#endif
        }
        vb.avb_data.avb_int32 = 0;

        return agentx_index(axr, &vb, AXI_TYPE_NEW);
}

struct agentx_index *
agentx_index_integer_any(struct agentx_region *axr, uint32_t oid[],
    size_t oidlen)
{
        struct ax_varbind vb;
        const char *errstr;

        vb.avb_type = AX_DATA_TYPE_INTEGER;
        if (agentx_oidfill(&(vb.avb_oid), oid, oidlen, &errstr) == -1) {
#ifdef AX_DEBUG
                agentx_log_axc_fatalx(axr->axr_axc, "%s: %s", __func__, errstr);
#else
                agentx_log_axc_warnx(axr->axr_axc, "%s: %s", __func__, errstr);
                return NULL;
#endif
        }
        vb.avb_data.avb_int32 = 0;

        return agentx_index(axr, &vb, AXI_TYPE_ANY);
}

struct agentx_index *
agentx_index_integer_value(struct agentx_region *axr, uint32_t oid[],
    size_t oidlen, int32_t value)
{
        struct ax_varbind vb;
        const char *errstr;

        if (value < 0) {
#ifdef AX_DEBUG
                agentx_log_axc_fatalx(axr->axr_axc, "%s: value < 0", __func__);
#else
                agentx_log_axc_warnx(axr->axr_axc, "%s: value < 0", __func__);
                errno = EINVAL;
                return NULL;
#endif
        }

        vb.avb_type = AX_DATA_TYPE_INTEGER;
        if (agentx_oidfill(&(vb.avb_oid), oid, oidlen, &errstr) == -1) {
#ifdef AX_DEBUG
                agentx_log_axc_fatalx(axr->axr_axc, "%s: %s", __func__, errstr);
#else
                agentx_log_axc_warnx(axr->axr_axc, "%s: %s", __func__, errstr);
                return NULL;
#endif
        }
        vb.avb_data.avb_int32 = value;

        return agentx_index(axr, &vb, AXI_TYPE_VALUE);
}

struct agentx_index *
agentx_index_integer_dynamic(struct agentx_region *axr, uint32_t oid[],
    size_t oidlen)
{
        struct ax_varbind vb;
        const char *errstr;

        vb.avb_type = AX_DATA_TYPE_INTEGER;
        if (agentx_oidfill(&(vb.avb_oid), oid, oidlen, &errstr) == -1) {
#ifdef AX_DEBUG
                agentx_log_axc_fatalx(axr->axr_axc, "%s: %s", __func__, errstr);
#else
                agentx_log_axc_warnx(axr->axr_axc, "%s: %s", __func__, errstr);
                return NULL;
#endif
        }

        return agentx_index(axr, &vb, AXI_TYPE_DYNAMIC);
}

struct agentx_index *
agentx_index_string_dynamic(struct agentx_region *axr, uint32_t oid[],
    size_t oidlen)
{
        struct ax_varbind vb;
        const char *errstr;

        vb.avb_type = AX_DATA_TYPE_OCTETSTRING;
        if (agentx_oidfill(&(vb.avb_oid), oid, oidlen, &errstr) == -1) {
#ifdef AX_DEBUG
                agentx_log_axc_fatalx(axr->axr_axc, "%s: %s", __func__, errstr);
#else
                agentx_log_axc_warnx(axr->axr_axc, "%s: %s", __func__, errstr);
                return NULL;
#endif
        }
        vb.avb_data.avb_ostring.aos_slen = 0;
        vb.avb_data.avb_ostring.aos_string = NULL;

        return agentx_index(axr, &vb, AXI_TYPE_DYNAMIC);
}

struct agentx_index *
agentx_index_nstring_dynamic(struct agentx_region *axr, uint32_t oid[],
    size_t oidlen, size_t vlen)
{
        struct ax_varbind vb;
        const char *errstr;

        if (vlen == 0 || vlen > AGENTX_OID_MAX_LEN) {
#ifdef AX_DEBUG
                agentx_log_axc_fatalx(axr->axr_axc, "%s: invalid string "
                    "length: %zu\n", __func__, vlen);
#else
                agentx_log_axc_warnx(axr->axr_axc, "%s: invalid string "
                    "length: %zu\n", __func__, vlen);
                errno = EINVAL;
                return NULL;
#endif
        }

        vb.avb_type = AX_DATA_TYPE_OCTETSTRING;
        if (agentx_oidfill(&(vb.avb_oid), oid, oidlen, &errstr) == -1) {
#ifdef AX_DEBUG
                agentx_log_axc_fatalx(axr->axr_axc, "%s: %s", __func__, errstr);
#else
                agentx_log_axc_warnx(axr->axr_axc, "%s: %s", __func__, errstr);
                return NULL;
#endif
        }
        vb.avb_data.avb_ostring.aos_slen = vlen;
        vb.avb_data.avb_ostring.aos_string = NULL;

        return agentx_index(axr, &vb, AXI_TYPE_DYNAMIC);
}

struct agentx_index *
agentx_index_oid_dynamic(struct agentx_region *axr, uint32_t oid[],
    size_t oidlen)
{
        struct ax_varbind vb;
        const char *errstr;

        vb.avb_type = AX_DATA_TYPE_OID;
        if (agentx_oidfill(&(vb.avb_oid), oid, oidlen, &errstr) == -1) {
#ifdef AX_DEBUG
                agentx_log_axc_fatalx(axr->axr_axc, "%s: %s", __func__, errstr);
#else
                agentx_log_axc_warnx(axr->axr_axc, "%s: %s", __func__, errstr);
                return NULL;
#endif
        }
        vb.avb_data.avb_oid.aoi_idlen = 0;

        return agentx_index(axr, &vb, AXI_TYPE_DYNAMIC);
}

struct agentx_index *
agentx_index_noid_dynamic(struct agentx_region *axr, uint32_t oid[],
    size_t oidlen, size_t vlen)
{
        struct ax_varbind vb;
        const char *errstr;

        if (vlen < AGENTX_OID_MIN_LEN || vlen > AGENTX_OID_MAX_LEN) {
#ifdef AX_DEBUG
                agentx_log_axc_fatalx(axr->axr_axc, "%s: invalid string "
                    "length: %zu\n", __func__, vlen);
#else
                agentx_log_axc_warnx(axr->axr_axc, "%s: invalid string "
                    "length: %zu\n", __func__, vlen);
                errno = EINVAL;
                return NULL;
#endif
        }

        vb.avb_type = AX_DATA_TYPE_OID;
        if (agentx_oidfill(&(vb.avb_oid), oid, oidlen, &errstr) == -1) {
#ifdef AX_DEBUG
                agentx_log_axc_fatalx(axr->axr_axc, "%s: %s", __func__, errstr);
#else
                agentx_log_axc_warnx(axr->axr_axc, "%s: %s", __func__, errstr);
                return NULL;
#endif
        }
        vb.avb_data.avb_oid.aoi_idlen = vlen;

        return agentx_index(axr, &vb, AXI_TYPE_DYNAMIC);
}

struct agentx_index *
agentx_index_ipaddress_dynamic(struct agentx_region *axr, uint32_t oid[],
    size_t oidlen)
{
        struct ax_varbind vb;
        const char *errstr;

        vb.avb_type = AX_DATA_TYPE_IPADDRESS;
        if (agentx_oidfill(&(vb.avb_oid), oid, oidlen, &errstr) == -1) {
#ifdef AX_DEBUG
                agentx_log_axc_fatalx(axr->axr_axc, "%s: %s", __func__, errstr);
#else
                agentx_log_axc_warnx(axr->axr_axc, "%s: %s", __func__, errstr);
                return NULL;
#endif
        }
        vb.avb_data.avb_ostring.aos_string = NULL;

        return agentx_index(axr, &vb, AXI_TYPE_DYNAMIC);
}

static struct agentx_index *
agentx_index(struct agentx_region *axr, struct ax_varbind *vb,
    enum agentx_index_type type)
{
        struct agentx_index *axi;

        if (axr->axr_dstate == AX_DSTATE_CLOSE)
                agentx_log_axc_fatalx(axr->axr_axc, "%s: use after free",
                    __func__);
        if (ax_oid_cmp(&(axr->axr_oid), &(vb->avb_oid)) != -2) {
#ifdef AX_DEBUG
                agentx_log_axc_fatalx(axr->axr_axc, "%s: oid is not child "
                    "of region %s", __func__,
                    ax_oid2string(&(vb->avb_oid)));
#else
                agentx_log_axc_warnx(axr->axr_axc, "%s: oid is not child of "
                    "region %s", __func__, ax_oid2string(&(vb->avb_oid)));
                errno = EINVAL;
                return NULL;
#endif
        }

        if ((axi = calloc(1, sizeof(*axi))) == NULL)
                return NULL;

        axi->axi_axr = axr;
        axi->axi_type = type;
        bcopy(vb, &(axi->axi_vb), sizeof(*vb));
        axi->axi_cstate = AX_CSTATE_CLOSE;
        axi->axi_dstate = AX_DSTATE_OPEN;
        TAILQ_INSERT_HEAD(&(axr->axr_indices), axi, axi_axr_indices);

        if (axr->axr_cstate == AX_CSTATE_OPEN)
                agentx_index_start(axi);

        return axi;
}

static int
agentx_index_start(struct agentx_index *axi)
{
        struct agentx_region *axr = axi->axi_axr;
        struct agentx_context *axc = axr->axr_axc;
        struct agentx_session *axs = axc->axc_axs;
        struct agentx *ax = axs->axs_ax;
        uint32_t packetid;
        int flags = 0;

#ifdef AX_DEBUG
        if (axr->axr_cstate != AX_CSTATE_OPEN ||
            axi->axi_cstate != AX_CSTATE_CLOSE ||
            axi->axi_dstate != AX_DSTATE_OPEN)
                agentx_log_axc_fatalx(axc, "%s: unexpected index allocation",
                    __func__);
#endif

        axi->axi_cstate = AX_CSTATE_WAITOPEN;

        if (axi->axi_type == AXI_TYPE_NEW)
                flags = AX_PDU_FLAG_NEW_INDEX;
        else if (axi->axi_type == AXI_TYPE_ANY)
                flags = AX_PDU_FLAG_ANY_INDEX;
        else if (axi->axi_type == AXI_TYPE_DYNAMIC) {
                agentx_index_finalize(NULL, axi);
                return 0;
        }

        /* We might be able to bundle, but if we fail we'd have to reorganise */
        packetid = ax_indexallocate(ax->ax_ax, flags, axs->axs_id,
            AGENTX_CONTEXT_CTX(axc), &(axi->axi_vb), 1);
        if (packetid == 0) {
                agentx_log_axc_warn(axc, "couldn't generate %s",
                    ax_pdutype2string(AX_PDU_TYPE_INDEXDEALLOCATE));
                agentx_reset(ax);
                return -1;
        }
        if (axi->axi_type == AXI_TYPE_VALUE)
                agentx_log_axc_info(axc, "index %s: allocating '%d'",
                    ax_oid2string(&(axi->axi_vb.avb_oid)),
                    axi->axi_vb.avb_data.avb_int32);
        else if (axi->axi_type == AXI_TYPE_ANY)
                agentx_log_axc_info(axc, "index %s: allocating any index",
                    ax_oid2string(&(axi->axi_vb.avb_oid)));
        else if (axi->axi_type == AXI_TYPE_NEW)
                agentx_log_axc_info(axc, "index %s: allocating new index",
                    ax_oid2string(&(axi->axi_vb.avb_oid)));

        return agentx_request(ax, packetid, agentx_index_finalize, axi);
}

static int
agentx_index_finalize(struct ax_pdu *pdu, void *cookie)
{
        struct agentx_index *axi = cookie;
        struct agentx_region *axr = axi->axi_axr;
        struct agentx_context *axc = axr->axr_axc;
        struct ax_pdu_response *resp;
        size_t i;

#ifdef AX_DEBUG
        if (axi->axi_cstate != AX_CSTATE_WAITOPEN)
                agentx_log_axc_fatalx(axc,
                    "%s: not expecting index allocate", __func__);
#endif
        if (axi->axi_type == AXI_TYPE_DYNAMIC) {
                axi->axi_cstate = AX_CSTATE_OPEN;
                goto objects_start;
        }

        resp = &(pdu->ap_payload.ap_response);
        if (resp->ap_error != AX_PDU_ERROR_NOERROR) {
                axi->axi_cstate = AX_CSTATE_CLOSE;
                agentx_log_axc_warnx(axc, "index %s: %s",
                    ax_oid2string(&(axr->axr_oid)),
                    ax_error2string(resp->ap_error));
                return 0;
        }
        axi->axi_cstate = AX_CSTATE_OPEN;
        if (resp->ap_nvarbind != 1) {
                agentx_log_axc_warnx(axc, "index %s: unexpected number of "
                    "indices", ax_oid2string(&(axr->axr_oid)));
                axi->axi_cstate = AX_CSTATE_CLOSE;
                return -1;
        }
        if (resp->ap_varbindlist[0].avb_type != axi->axi_vb.avb_type) {
                agentx_log_axc_warnx(axc, "index %s: unexpected index type",
                    ax_oid2string(&(axr->axr_oid)));
                axi->axi_cstate = AX_CSTATE_CLOSE;
                return -1;
        }
        if (ax_oid_cmp(&(resp->ap_varbindlist[0].avb_oid),
            &(axi->axi_vb.avb_oid)) != 0) {
                agentx_log_axc_warnx(axc, "index %s: unexpected oid",
                    ax_oid2string(&(axr->axr_oid)));
                axi->axi_cstate = AX_CSTATE_CLOSE;
                return -1;
        }

        switch (axi->axi_vb.avb_type) {
        case AX_DATA_TYPE_INTEGER:
                if (axi->axi_type == AXI_TYPE_NEW ||
                    axi->axi_type == AXI_TYPE_ANY)
                        axi->axi_vb.avb_data.avb_int32 =
                            resp->ap_varbindlist[0].avb_data.avb_int32;
                else if (axi->axi_vb.avb_data.avb_int32 !=
                    resp->ap_varbindlist[0].avb_data.avb_int32) {
                        agentx_log_axc_warnx(axc, "index %s: unexpected "
                            "index value", ax_oid2string(&(axr->axr_oid)));
                        axi->axi_cstate = AX_CSTATE_CLOSE;
                        return -1;
                }
                agentx_log_axc_info(axc, "index %s: allocated '%d'",
                    ax_oid2string(&(axi->axi_vb.avb_oid)),
                    axi->axi_vb.avb_data.avb_int32);
                break;
        default:
                agentx_log_axc_fatalx(axc, "%s: Unsupported index type",
                    __func__);
        }

        if (axi->axi_dstate == AX_DSTATE_CLOSE)
                return agentx_index_close(axi);

 objects_start:
        /* TODO Make use of range_subid register */
        for (i = 0; i < axi->axi_objectlen; i++) {
                if (axi->axi_object[i]->axo_dstate == AX_DSTATE_OPEN) {
                        if (agentx_object_start(axi->axi_object[i]) == -1)
                                return -1;
                }
        }
        return 0;
}

void
agentx_index_free(struct agentx_index *axi)
{
        size_t i;
        struct agentx_object *axo;
        struct agentx *ax;
        int axfree;

        if (axi == NULL)
                return;

        ax = axi->axi_axr->axr_axc->axc_axs->axs_ax;
        axfree = ax->ax_free;
        ax->ax_free = 1;

        if (axi->axi_dstate == AX_DSTATE_CLOSE)
                agentx_log_axc_fatalx(axi->axi_axr->axr_axc,
                    "%s: double free", __func__);

        /* TODO Do a range_subid unregister before freeing */
        for (i = 0; i < axi->axi_objectlen; i++) {
                axo = axi->axi_object[i];
                if (axo->axo_dstate != AX_DSTATE_CLOSE) {
                        agentx_object_free(axo);
                        if (axi->axi_object[i] != axo)
                                i--;
                }
        }

        axi->axi_dstate = AX_DSTATE_CLOSE;

        if (axi->axi_cstate == AX_CSTATE_OPEN)
                (void) agentx_index_close(axi);
        if (!axfree)
                agentx_free_finalize(ax);
}

static void
agentx_index_free_finalize(struct agentx_index *axi)
{
        struct agentx_region *axr = axi->axi_axr;

        if (axi->axi_cstate != AX_CSTATE_CLOSE ||
            axi->axi_dstate != AX_DSTATE_CLOSE ||
            axi->axi_objectlen != 0)
                return;

        TAILQ_REMOVE(&(axr->axr_indices), axi, axi_axr_indices);
        ax_varbind_free(&(axi->axi_vb));
        free(axi->axi_object);
        free(axi);
}

static void
agentx_index_reset(struct agentx_index *axi)
{
        struct agentx *ax = axi->axi_axr->axr_axc->axc_axs->axs_ax;

        axi->axi_cstate = AX_CSTATE_CLOSE;

        if (!ax->ax_free)
                agentx_free_finalize(ax);
}

static int
agentx_index_close(struct agentx_index *axi)
{
        struct agentx_region *axr = axi->axi_axr;
        struct agentx_context *axc = axr->axr_axc;
        struct agentx_session *axs = axc->axc_axs;
        struct agentx *ax = axs->axs_ax;
        uint32_t packetid;

#ifdef AX_DEBUG
        if (axi->axi_cstate != AX_CSTATE_OPEN)
                agentx_log_axc_fatalx(axc,
                    "%s: unexpected index deallocation", __func__);
#endif

        axi->axi_cstate = AX_CSTATE_WAITCLOSE;
        if (axs->axs_cstate == AX_CSTATE_WAITCLOSE)
                return 0;

        /* We might be able to bundle, but if we fail we'd have to reorganise */
        packetid = ax_indexdeallocate(ax->ax_ax, axs->axs_id,
            AGENTX_CONTEXT_CTX(axc), &(axi->axi_vb), 1);
        if (packetid == 0) {
                agentx_log_axc_warn(axc, "couldn't generate %s",
                    ax_pdutype2string(AX_PDU_TYPE_INDEXDEALLOCATE));
                agentx_reset(ax);
                return -1;
        }
        agentx_log_axc_info(axc, "index %s: deallocating",
            ax_oid2string(&(axi->axi_vb.avb_oid)));
        return agentx_request(ax, packetid, agentx_index_close_finalize,
            axi);
}

static int
agentx_index_close_finalize(struct ax_pdu *pdu, void *cookie)
{
        struct agentx_index *axi = cookie;
        struct agentx_region *axr = axi->axi_axr;
        struct agentx_context *axc = axr->axr_axc;
        struct agentx_session *axs = axc->axc_axs;
        struct agentx *ax = axs->axs_ax;
        struct ax_pdu_response *resp = &(pdu->ap_payload.ap_response);
        int axfree = ax->ax_free;

#ifdef AX_DEBUG
        if (axi->axi_cstate != AX_CSTATE_WAITCLOSE)
                agentx_log_axc_fatalx(axc, "%s: unexpected indexdeallocate",
                    __func__);
#endif

        if (pdu->ap_payload.ap_response.ap_error != AX_PDU_ERROR_NOERROR) {
                agentx_log_axc_warnx(axc,
                    "index %s: couldn't deallocate: %s",
                    ax_oid2string(&(axi->axi_vb.avb_oid)),
                    ax_error2string(resp->ap_error));
                agentx_reset(ax);
                return -1;
        }

        if (resp->ap_nvarbind != 1) {
                agentx_log_axc_warnx(axc,
                    "index %s: unexpected number of indices",
                    ax_oid2string(&(axr->axr_oid)));
                agentx_reset(ax);
                return -1;
        }
        if (resp->ap_varbindlist[0].avb_type != axi->axi_vb.avb_type) {
                agentx_log_axc_warnx(axc, "index %s: unexpected index type",
                    ax_oid2string(&(axr->axr_oid)));
                agentx_reset(ax);
                return -1;
        }
        if (ax_oid_cmp(&(resp->ap_varbindlist[0].avb_oid),
            &(axi->axi_vb.avb_oid)) != 0) {
                agentx_log_axc_warnx(axc, "index %s: unexpected oid",
                    ax_oid2string(&(axr->axr_oid)));
                agentx_reset(ax);
                return -1;
        }
        switch (axi->axi_vb.avb_type) {
        case AX_DATA_TYPE_INTEGER:
                if (axi->axi_vb.avb_data.avb_int32 !=
                    resp->ap_varbindlist[0].avb_data.avb_int32) {
                        agentx_log_axc_warnx(axc,
                            "index %s: unexpected index value",
                            ax_oid2string(&(axr->axr_oid)));
                        agentx_reset(ax);
                        return -1;
                }
                break;
        default:
                agentx_log_axc_fatalx(axc, "%s: Unsupported index type",
                    __func__);
        }

        axi->axi_cstate = AX_CSTATE_CLOSE;
        ax->ax_free = 1;

        agentx_log_axc_info(axc, "index %s: deallocated",
            ax_oid2string(&(axi->axi_vb.avb_oid)));

        if (axr->axr_cstate == AX_CSTATE_OPEN &&
            axi->axi_dstate == AX_DSTATE_OPEN)
                agentx_index_start(axi);

        if (!axfree)
                agentx_free_finalize(ax);
        return 0;
}

struct agentx_object *
agentx_object(struct agentx_region *axr, uint32_t oid[], size_t oidlen,
    struct agentx_index *axi[], size_t axilen, int implied,
    void (*get)(struct agentx_varbind *))
{
        struct agentx_object *axo, **taxo, axo_search;
        struct agentx_index *laxi;
        const char *errstr;
        int ready = 1;
        size_t i, j;

        if (axr->axr_dstate == AX_DSTATE_CLOSE)
                agentx_log_axc_fatalx(axr->axr_axc, "%s: use after free",
                    __func__);
        if (axilen > AGENTX_OID_INDEX_MAX_LEN) {
#ifdef AX_DEBUG
                agentx_log_axc_fatalx(axr->axr_axc, "%s: indexlen > %d",
                    __func__, AGENTX_OID_INDEX_MAX_LEN);
#else
                agentx_log_axc_warnx(axr->axr_axc, "%s: indexlen > %d",
                    __func__, AGENTX_OID_INDEX_MAX_LEN);
                errno = EINVAL;
                return NULL;
#endif
        }

        if (agentx_oidfill(&(axo_search.axo_oid), oid, oidlen, &errstr) == -1) {
#ifdef AX_DEBUG
                agentx_log_axc_fatalx(axr->axr_axc, "%s: %s", __func__, errstr);
#else
                agentx_log_axc_warnx(axr->axr_axc, "%s: %s", __func__, errstr);
                return NULL;
#endif
        }

        do {
                if (RB_FIND(axc_objects, &(axr->axr_axc->axc_objects),
                    &axo_search) != NULL) {
#ifdef AX_DEBUG
                        agentx_log_axc_fatalx(axr->axr_axc, "%s: invalid "
                            "parent child object relationship", __func__);
#else
                        agentx_log_axc_warnx(axr->axr_axc, "%s: invalid "
                            "parent child object relationship", __func__);
                        errno = EINVAL;
                        return NULL;
#endif
                }
                axo_search.axo_oid.aoi_idlen--;
        } while (axo_search.axo_oid.aoi_idlen > 0);
        axo_search.axo_oid.aoi_idlen = oidlen;
        axo = RB_NFIND(axc_objects, &(axr->axr_axc->axc_objects), &axo_search);
        if (axo != NULL &&
            ax_oid_cmp(&(axo->axo_oid), &(axo_search.axo_oid)) == 2) {
#ifdef AX_DEBUG
                agentx_log_axc_fatalx(axr->axr_axc, "%s: invalid parent "
                    "child object relationship", __func__);
#else
                agentx_log_axc_warnx(axr->axr_axc, "%s: invalid parent "
                    "child object relationship", __func__);
                errno = EINVAL;
                return NULL;
#endif
        }
        if (implied == 1) {
                laxi = axi[axilen - 1];
                if (laxi->axi_vb.avb_type == AX_DATA_TYPE_OCTETSTRING) {
                        if (laxi->axi_vb.avb_data.avb_ostring.aos_slen != 0) {
#ifdef AX_DEBUG
                                agentx_log_axc_fatalx(axr->axr_axc,
                                    "%s: implied can only be used on strings "
                                    "of dynamic length", __func__);
#else
                                agentx_log_axc_warnx(axr->axr_axc,
                                    "%s: implied can only be used on strings "
                                    "of dynamic length", __func__);
                                errno = EINVAL;
                                return NULL;
#endif
                        }
                } else if (laxi->axi_vb.avb_type == AX_DATA_TYPE_OID) {
                        if (laxi->axi_vb.avb_data.avb_oid.aoi_idlen != 0) {
#ifdef AX_DEBUG
                                agentx_log_axc_fatalx(axr->axr_axc,
                                    "%s: implied can only be used on oids of "
                                    "dynamic length", __func__);
#else
                                agentx_log_axc_warnx(axr->axr_axc,
                                    "%s: implied can only be used on oids of "
                                    "dynamic length", __func__);
                                errno = EINVAL;
                                return NULL;
#endif
                        }
                } else {
#ifdef AX_DEBUG
                        agentx_log_axc_fatalx(axr->axr_axc, "%s: implied "
                            "can only be set on oid and string indices",
                            __func__);
#else
                        agentx_log_axc_warnx(axr->axr_axc, "%s: implied can "
                            "only be set on oid and string indices", __func__);
                        errno = EINVAL;
                        return NULL;
#endif
                }
        }

        ready = axr->axr_cstate == AX_CSTATE_OPEN;
        if ((axo = calloc(1, sizeof(*axo))) == NULL)
                return NULL;
        axo->axo_axr = axr;
        bcopy(&(axo_search.axo_oid), &(axo->axo_oid), sizeof(axo->axo_oid));
        for (i = 0; i < axilen; i++) {
                axo->axo_index[i] = axi[i];
                if (axi[i]->axi_objectlen == axi[i]->axi_objectsize) {
                        taxo = recallocarray(axi[i]->axi_object,
                            axi[i]->axi_objectlen, axi[i]->axi_objectlen + 1,
                            sizeof(*axi[i]->axi_object));
                        if (taxo == NULL) {
                                free(axo);
                                return NULL;
                        }
                        axi[i]->axi_object = taxo;
                        axi[i]->axi_objectsize = axi[i]->axi_objectlen + 1;
                }
                for (j = 0; j < axi[i]->axi_objectlen; j++) {
                        if (ax_oid_cmp(&(axo->axo_oid),
                            &(axi[i]->axi_object[j]->axo_oid)) < 0) {
                                memmove(&(axi[i]->axi_object[j + 1]),
                                    &(axi[i]->axi_object[j]),
                                    sizeof(*(axi[i]->axi_object)) *
                                    (axi[i]->axi_objectlen - j));
                                break;
                        }
                }
                axi[i]->axi_object[j] = axo;
                axi[i]->axi_objectlen++;
                if (axi[i]->axi_cstate != AX_CSTATE_OPEN)
                        ready = 0;
        }
        axo->axo_indexlen = axilen;
        axo->axo_implied = implied;
        axo->axo_timeout = 0;
        axo->axo_lock = 0;
        axo->axo_get = get;
        axo->axo_cstate = AX_CSTATE_CLOSE;
        axo->axo_dstate = AX_DSTATE_OPEN;

        TAILQ_INSERT_TAIL(&(axr->axr_objects), axo, axo_axr_objects);
        RB_INSERT(axc_objects, &(axr->axr_axc->axc_objects), axo);

        if (ready)
                agentx_object_start(axo);

        return axo;
}

static int
agentx_object_start(struct agentx_object *axo)
{
        struct agentx_region *axr = axo->axo_axr;
        struct agentx_context *axc = axr->axr_axc;
        struct agentx_session *axs = axc->axc_axs;
        struct agentx *ax = axs->axs_ax;
        struct ax_oid oid;
        char oids[1024];
        size_t i;
        int needregister = 0;
        uint32_t packetid;
        uint8_t flags = AX_PDU_FLAG_INSTANCE_REGISTRATION;

#ifdef AX_DEBUG
        if (axr->axr_cstate != AX_CSTATE_OPEN ||
            axo->axo_cstate != AX_CSTATE_CLOSE ||
            axo->axo_dstate != AX_DSTATE_OPEN)
                agentx_log_axc_fatalx(axc,
                    "%s: unexpected object registration", __func__);
#endif

        if (axo->axo_timeout != 0)
                needregister = 1;
        for (i = 0; i < axo->axo_indexlen; i++) {
                if (axo->axo_index[i]->axi_cstate != AX_CSTATE_OPEN)
                        return 0;
                if (axo->axo_index[i]->axi_type != AXI_TYPE_DYNAMIC)
                        needregister = 1;
        }
        if (!needregister) {
                axo->axo_cstate = AX_CSTATE_WAITOPEN;
                agentx_object_finalize(NULL, axo);
                return 0;
        }

        bcopy(&(axo->axo_oid), &(oid), sizeof(oid));
        for (i = 0; i < axo->axo_indexlen; i++) {
                if (axo->axo_index[i]->axi_type == AXI_TYPE_DYNAMIC) {
                        flags = 0;
                        break;
                }
#ifdef AX_DEBUG
                if (axo->axo_index[i]->axi_vb.avb_type !=
                    AX_DATA_TYPE_INTEGER)
                        agentx_log_axc_fatalx(axc,
                            "%s: Unsupported allocated index type", __func__);
#endif
                oid.aoi_id[oid.aoi_idlen++] =
                    axo->axo_index[i]->axi_vb.avb_data.avb_int32;
        }
        packetid = ax_register(ax->ax_ax, flags, axs->axs_id,
            AGENTX_CONTEXT_CTX(axc), axo->axo_timeout,
            AX_PRIORITY_DEFAULT, 0, &oid, 0);
        if (packetid == 0) {
                agentx_log_axc_warn(axc, "couldn't generate %s",
                    ax_pdutype2string(AX_PDU_TYPE_REGISTER));
                agentx_reset(ax);
                return -1;
        }
        strlcpy(oids, ax_oid2string(&(axo->axo_oid)), sizeof(oids));
        agentx_log_axc_info(axc, "object %s (%s %s): opening",
            oids, flags ? "instance" : "region", ax_oid2string(&(oid)));
        axo->axo_cstate = AX_CSTATE_WAITOPEN;
        return agentx_request(ax, packetid, agentx_object_finalize, axo);
}

static int
agentx_object_finalize(struct ax_pdu *pdu, void *cookie)
{
        struct agentx_object *axo = cookie;
        struct agentx_context *axc = axo->axo_axr->axr_axc;
        struct ax_oid oid;
        char oids[1024];
        size_t i;
        uint8_t flags = 1;

#ifdef AX_DEBUG
        if (axo->axo_cstate != AX_CSTATE_WAITOPEN)
                agentx_log_axc_fatalx(axc, "%s: not expecting object open",
                    __func__);
#endif

        if (pdu == NULL) {
                axo->axo_cstate = AX_CSTATE_OPEN;
                return 0;
        }

        bcopy(&(axo->axo_oid), &oid, sizeof(oid));
        for (i = 0; i < axo->axo_indexlen; i++) {
                if (axo->axo_index[i]->axi_type == AXI_TYPE_DYNAMIC) {
                        flags = 0;
                        break;
                }
#ifdef AX_DEBUG
                if (axo->axo_index[i]->axi_vb.avb_type !=
                    AX_DATA_TYPE_INTEGER)
                        agentx_log_axc_fatalx(axc,
                            "%s: Unsupported allocated index type", __func__);
#endif

                oid.aoi_id[oid.aoi_idlen++] =
                    axo->axo_index[i]->axi_vb.avb_data.avb_int32;
        }
        strlcpy(oids, ax_oid2string(&(axo->axo_oid)), sizeof(oids));

        /*
         * We should only be here for table objects with registered indices.
         * If we fail here something is misconfigured and the admin should fix
         * it.
         */
        if (pdu->ap_payload.ap_response.ap_error != AX_PDU_ERROR_NOERROR) {
                axo->axo_cstate = AX_CSTATE_CLOSE;
                agentx_log_axc_info(axc, "object %s (%s %s): %s",
                    oids, flags ? "instance" : "region", ax_oid2string(&oid),
                    ax_error2string(pdu->ap_payload.ap_response.ap_error));
                return 0;
        }
        axo->axo_cstate = AX_CSTATE_OPEN;
        agentx_log_axc_info(axc, "object %s (%s %s): open", oids,
            flags ? "instance" : "region", ax_oid2string(&oid));

        if (axo->axo_dstate == AX_DSTATE_CLOSE)
                return agentx_object_close(axo);

        return 0;
}

static int
agentx_object_lock(struct agentx_object *axo)
{
        if (axo->axo_lock == UINT32_MAX) {
                agentx_log_axc_warnx(axo->axo_axr->axr_axc,
                    "%s: axo_lock == %u", __func__, UINT32_MAX);
                return -1;
        }
        axo->axo_lock++;
        return 0;
}

static void
agentx_object_unlock(struct agentx_object *axo)
{
        struct agentx *ax = axo->axo_axr->axr_axc->axc_axs->axs_ax;

#ifdef AX_DEBUG
        if (axo->axo_lock == 0)
                agentx_log_axc_fatalx(axo->axo_axr->axr_axc,
                    "%s: axo_lock == 0", __func__);
#endif
        axo->axo_lock--;
        if (axo->axo_lock == 0) {
                if (!ax->ax_free)
                        agentx_free_finalize(ax);
        }
}

static int
agentx_object_close(struct agentx_object *axo)
{
        struct agentx_context *axc = axo->axo_axr->axr_axc;
        struct agentx_session *axs = axc->axc_axs;
        struct agentx *ax = axs->axs_ax;
        struct ax_oid oid;
        char oids[1024];
        size_t i;
        int needclose = 0;
        uint32_t packetid;
        uint8_t flags = 1;

#ifdef AX_DEBUG
        if (axo->axo_cstate != AX_CSTATE_OPEN)
                agentx_log_axc_fatalx(axc, "%s: unexpected object close",
                    __func__);
#endif

        for (i = 0; i < axo->axo_indexlen; i++) {
#ifdef AX_DEBUG
                if (axo->axo_index[i]->axi_cstate != AX_CSTATE_OPEN)
                        agentx_log_axc_fatalx(axc,
                            "%s: Object open while index closed", __func__);
#endif
                if (axo->axo_index[i]->axi_type != AXI_TYPE_DYNAMIC)
                        needclose = 1;
        }
        axo->axo_cstate = AX_CSTATE_WAITCLOSE;
        if (axs->axs_cstate == AX_CSTATE_WAITCLOSE)
                return 0;
        if (!needclose) {
                agentx_object_close_finalize(NULL, axo);
                return 0;
        }

        bcopy(&(axo->axo_oid), &(oid), sizeof(oid));
        for (i = 0; i < axo->axo_indexlen; i++) {
                if (axo->axo_index[i]->axi_type == AXI_TYPE_DYNAMIC) {
                        flags = 0;
                        break;
                }
#ifdef AX_DEBUG
                if (axo->axo_index[i]->axi_vb.avb_type !=
                    AX_DATA_TYPE_INTEGER)
                        agentx_log_axc_fatalx(axc,
                            "%s: Unsupported allocated index type", __func__);
#endif
                oid.aoi_id[oid.aoi_idlen++] =
                    axo->axo_index[i]->axi_vb.avb_data.avb_int32;
        }
        packetid = ax_unregister(ax->ax_ax, axs->axs_id,
            AGENTX_CONTEXT_CTX(axc), AX_PRIORITY_DEFAULT, 0, &oid, 0);
        if (packetid == 0) {
                agentx_log_axc_warn(axc, "couldn't generate %s",
                    ax_pdutype2string(AX_PDU_TYPE_UNREGISTER));
                agentx_reset(ax);
                return -1;
        }
        strlcpy(oids, ax_oid2string(&(axo->axo_oid)), sizeof(oids));
        agentx_log_axc_info(axc, "object %s (%s %s): closing",
            oids, flags ? "instance" : "region", ax_oid2string(&(oid)));
        return agentx_request(ax, packetid, agentx_object_close_finalize,
            axo);
}

static int
agentx_object_close_finalize(struct ax_pdu *pdu, void *cookie)
{
        struct agentx_object *axo = cookie;
        struct agentx_region *axr = axo->axo_axr;
        struct agentx_context *axc = axr->axr_axc;
        struct agentx_session *axs = axc->axc_axs;
        struct agentx *ax = axs->axs_ax;
        struct ax_oid oid;
        char oids[1024];
        uint8_t flags = 1;
        size_t i;
        int axfree = ax->ax_free;

#ifdef AX_DEBUG
        if (axo->axo_cstate != AX_CSTATE_WAITCLOSE)
                agentx_log_axc_fatalx(axc,
                    "%s: unexpected object unregister", __func__);
#endif

        if (pdu != NULL) {
                bcopy(&(axo->axo_oid), &(oid), sizeof(oid));
                for (i = 0; i < axo->axo_indexlen; i++) {
                        if (axo->axo_index[i]->axi_type == AXI_TYPE_DYNAMIC) {
                                flags = 0;
                                break;
                        }
#ifdef AX_DEBUG
                        if (axo->axo_index[i]->axi_vb.avb_type !=
                            AX_DATA_TYPE_INTEGER)
                                agentx_log_axc_fatalx(axc,
                                    "%s: Unsupported allocated index type",
                                    __func__);
#endif
                        oid.aoi_id[oid.aoi_idlen++] =
                            axo->axo_index[i]->axi_vb.avb_data.avb_int32;
                }
                strlcpy(oids, ax_oid2string(&(axo->axo_oid)), sizeof(oids));
                if (pdu->ap_payload.ap_response.ap_error !=
                    AX_PDU_ERROR_NOERROR) {
                        agentx_log_axc_warnx(axc,
                            "closing object %s (%s %s): %s", oids,
                            flags ? "instance" : "region",
                            ax_oid2string(&oid), ax_error2string(
                            pdu->ap_payload.ap_response.ap_error));
                        agentx_reset(ax);
                        return -1;
                }
                agentx_log_axc_info(axc, "object %s (%s %s): closed", oids,
                    flags ? "instance" : "region", ax_oid2string(&oid));
        }

        ax->ax_free = 1;
        if (axr->axr_cstate == AX_CSTATE_OPEN &&
            axo->axo_dstate == AX_DSTATE_OPEN)
                agentx_object_start(axo);

        if (!axfree)
                agentx_free_finalize(ax);

        return 0;
}

void
agentx_object_free(struct agentx_object *axo)
{
        struct agentx *ax;
        int axfree;

        if (axo == NULL)
                return;

        ax = axo->axo_axr->axr_axc->axc_axs->axs_ax;
        axfree = ax->ax_free;
        ax->ax_free = 1;

        if (axo->axo_dstate == AX_DSTATE_CLOSE)
                agentx_log_axc_fatalx(axo->axo_axr->axr_axc,
                    "%s: double free", __func__);

        axo->axo_dstate = AX_DSTATE_CLOSE;

        if (axo->axo_cstate == AX_CSTATE_OPEN)
                agentx_object_close(axo);
        if (!axfree)
                agentx_free_finalize(ax);
}

static void
agentx_object_free_finalize(struct agentx_object *axo)
{
#ifdef AX_DEBUG
        struct agentx *ax = axo->axo_axr->axr_axc->axc_axs->axs_ax;
#endif
        size_t i, j;
        int found;

        if (axo->axo_dstate != AX_DSTATE_CLOSE ||
            axo->axo_cstate != AX_CSTATE_CLOSE ||
            axo->axo_lock != 0)
                return;

        RB_REMOVE(axc_objects, &(axo->axo_axr->axr_axc->axc_objects), axo);
        TAILQ_REMOVE(&(axo->axo_axr->axr_objects), axo, axo_axr_objects);

        for (i = 0; i < axo->axo_indexlen; i++) {
                found = 0;
                for (j = 0; j < axo->axo_index[i]->axi_objectlen; j++) {
                        if (axo->axo_index[i]->axi_object[j] == axo)
                                found = 1;
                        if (found && j + 1 != axo->axo_index[i]->axi_objectlen)
                                axo->axo_index[i]->axi_object[j] =
                                    axo->axo_index[i]->axi_object[j + 1];
                }
#ifdef AX_DEBUG
                if (!found)
                        agentx_log_axc_fatalx(axo->axo_axr->axr_axc,
                            "%s: object not found in index", __func__);
#endif
                axo->axo_index[i]->axi_objectlen--;
        }

        free(axo);
}

static void
agentx_object_reset(struct agentx_object *axo)
{
        struct agentx *ax = axo->axo_axr->axr_axc->axc_axs->axs_ax;

        axo->axo_cstate = AX_CSTATE_CLOSE;

        if (!ax->ax_free)
                agentx_free_finalize(ax);
}

static int
agentx_object_cmp(struct agentx_object *o1, struct agentx_object *o2)
{
        return ax_oid_cmp(&(o1->axo_oid), &(o2->axo_oid));
}

static int
agentx_object_implied(struct agentx_object *axo,
    struct agentx_index *axi)
{
        size_t i = 0;
        struct ax_varbind *vb;

        for (i = 0; i < axo->axo_indexlen; i++) {
                if (axo->axo_index[i] == axi) {
                        vb = &axi->axi_vb;
                        if (vb->avb_type == AX_DATA_TYPE_OCTETSTRING &&
                            vb->avb_data.avb_ostring.aos_slen != 0)
                                return 1;
                        else if (vb->avb_type == AX_DATA_TYPE_OID &&
                            vb->avb_data.avb_oid.aoi_idlen != 0)
                                return 1;
                        else if (i == axo->axo_indexlen - 1)
                                return axo->axo_implied;
                        return 0;
                }
        }
#ifdef AX_DEBUG
        agentx_log_axc_fatalx(axo->axo_axr->axr_axc, "%s: unsupported index",
            __func__);
#endif
        return 0;
}

static void
agentx_get_start(struct agentx_context *axc, struct ax_pdu *pdu)
{
        struct agentx_session *axs = axc->axc_axs;
        struct agentx *ax = axs->axs_ax;
        struct agentx_get *axg, taxg;
        struct ax_pdu_searchrangelist *srl;
        char *logmsg = NULL;
        size_t i, j;
        int fail = 0;

        if ((axg = calloc(1, sizeof(*axg))) == NULL) {
                taxg.axg_sessionid = pdu->ap_header.aph_sessionid;
                taxg.axg_transactionid = pdu->ap_header.aph_transactionid;
                taxg.axg_packetid = pdu->ap_header.aph_packetid;
                taxg.axg_context_default = axc->axc_name_default;
                taxg.axg_fd = axc->axc_axs->axs_ax->ax_fd;
                agentx_log_axg_warn(&taxg, "Couldn't parse request");
                agentx_reset(ax);
                return;
        }

        axg->axg_sessionid = pdu->ap_header.aph_sessionid;
        axg->axg_transactionid = pdu->ap_header.aph_transactionid;
        axg->axg_packetid = pdu->ap_header.aph_packetid;
        axg->axg_context_default = axc->axc_name_default;
        axg->axg_fd = axc->axc_axs->axs_ax->ax_fd;
        if (!axc->axc_name_default) {
                axg->axg_context.aos_string =
                    (unsigned char *)strdup((char *)axc->axc_name.aos_string);
                if (axg->axg_context.aos_string == NULL) {
                        agentx_log_axg_warn(axg, "Couldn't parse request");
                        free(axg);
                        agentx_reset(ax);
                        return;
                }
        }
        axg->axg_context.aos_slen = axc->axc_name.aos_slen;
        axg->axg_type = pdu->ap_header.aph_type;
        axg->axg_axc = axc;
        TAILQ_INSERT_TAIL(&(ax->ax_getreqs), axg, axg_ax_getreqs);
        if (axg->axg_type == AX_PDU_TYPE_GET ||
            axg->axg_type == AX_PDU_TYPE_GETNEXT) {
                srl = &(pdu->ap_payload.ap_srl);
                axg->axg_nvarbind = srl->ap_nsr;
        } else {
                axg->axg_nonrep = pdu->ap_payload.ap_getbulk.ap_nonrep;
                axg->axg_maxrep = pdu->ap_payload.ap_getbulk.ap_maxrep;
                srl = &(pdu->ap_payload.ap_getbulk.ap_srl);
                axg->axg_nvarbind = ((srl->ap_nsr - axg->axg_nonrep) *
                    axg->axg_maxrep) + axg->axg_nonrep;
        }

        if ((axg->axg_varbind = calloc(axg->axg_nvarbind,
            sizeof(*(axg->axg_varbind)))) == NULL) {
                agentx_log_axg_warn(axg, "Couldn't parse request");
                agentx_get_free(axg);
                agentx_reset(ax);
                return;
        }

        /* XXX net-snmp doesn't use getbulk, so untested */
        /* Two loops: varbind after needs to be initialized */
        for (i = 0; i < srl->ap_nsr; i++) {
                if (i < axg->axg_nonrep ||
                    axg->axg_type != AX_PDU_TYPE_GETBULK)
                        j = i;
                else if (axg->axg_maxrep == 0)
                        break;
                else
                        j = (axg->axg_maxrep * i) + axg->axg_nonrep;
                bcopy(&(srl->ap_sr[i].asr_start),
                    &(axg->axg_varbind[j].axv_vb.avb_oid),
                    sizeof(srl->ap_sr[i].asr_start));
                bcopy(&(srl->ap_sr[i].asr_start),
                    &(axg->axg_varbind[j].axv_start),
                    sizeof(srl->ap_sr[i].asr_start));
                bcopy(&(srl->ap_sr[i].asr_stop),
                    &(axg->axg_varbind[j].axv_end),
                    sizeof(srl->ap_sr[i].asr_stop));
                axg->axg_varbind[j].axv_initialized = 1;
                axg->axg_varbind[j].axv_axg = axg;
                axg->axg_varbind[j].axv_include =
                    srl->ap_sr[i].asr_start.aoi_include;
                if (j == 0)
                        fail |= agentx_strcat(&logmsg, " {");
                else
                        fail |= agentx_strcat(&logmsg, ",{");
                fail |= agentx_strcat(&logmsg,
                    ax_oid2string(&(srl->ap_sr[i].asr_start)));
                if (srl->ap_sr[i].asr_start.aoi_include)
                        fail |= agentx_strcat(&logmsg, " (inclusive)");
                if (srl->ap_sr[i].asr_stop.aoi_idlen != 0) {
                        fail |= agentx_strcat(&logmsg, " - ");
                        fail |= agentx_strcat(&logmsg,
                            ax_oid2string(&(srl->ap_sr[i].asr_stop)));
                }
                fail |= agentx_strcat(&logmsg, "}");
                if (fail) {
                        agentx_log_axg_warn(axg, "Couldn't parse request");
                        free(logmsg);
                        agentx_get_free(axg);
                        agentx_reset(ax);
                        return;
                }
        }

        agentx_log_axg_debug(axg, "%s:%s",
            ax_pdutype2string(axg->axg_type), logmsg);
        free(logmsg);

        for (i = 0; i < srl->ap_nsr; i++) {
                if (i < axg->axg_nonrep ||
                    axg->axg_type != AX_PDU_TYPE_GETBULK)
                        j = i;
                else if (axg->axg_maxrep == 0)
                        break;
                else
                        j = (axg->axg_maxrep * i) + axg->axg_nonrep;
                agentx_varbind_start(&(axg->axg_varbind[j]));
        }
}

static void
agentx_get_finalize(struct agentx_get *axg)
{
        struct agentx_context *axc = axg->axg_axc;
        struct agentx_session *axs;
        struct agentx *ax;
        size_t i, j, nvarbind = 0;
        uint16_t error = 0, index = 0;
        struct ax_varbind *vbl;
        char *logmsg = NULL;
        int fail = 0;

        for (i = 0; i < axg->axg_nvarbind; i++) {
                if (axg->axg_varbind[i].axv_initialized) {
                        if (axg->axg_varbind[i].axv_vb.avb_type == 0)
                                return;
                        nvarbind++;
                }
        }

        if (axc == NULL) {
                agentx_get_free(axg);
                return;
        }

        axs = axc->axc_axs;
        ax = axs->axs_ax;

        if ((vbl = calloc(nvarbind, sizeof(*vbl))) == NULL) {
                agentx_log_axg_warn(axg, "Couldn't parse request");
                agentx_get_free(axg);
                agentx_reset(ax);
                return;
        }
        for (i = 0, j = 0; i < axg->axg_nvarbind; i++) {
                if (axg->axg_varbind[i].axv_initialized) {
                        memcpy(&(vbl[j]), &(axg->axg_varbind[i].axv_vb),
                            sizeof(*vbl));
                        if (error == 0 && axg->axg_varbind[i].axv_error !=
                            AX_PDU_ERROR_NOERROR) {
                                error = axg->axg_varbind[i].axv_error;
                                index = j + 1;
                        }
                        if (j == 0)
                                fail |= agentx_strcat(&logmsg, " {");
                        else
                                fail |= agentx_strcat(&logmsg, ",{");
                        fail |= agentx_strcat(&logmsg,
                            ax_varbind2string(&(vbl[j])));
                        if (axg->axg_varbind[i].axv_error !=
                            AX_PDU_ERROR_NOERROR) {
                                fail |= agentx_strcat(&logmsg, "(");
                                fail |= agentx_strcat(&logmsg,
                                    ax_error2string(
                                    axg->axg_varbind[i].axv_error));
                                fail |= agentx_strcat(&logmsg, ")");
                        }
                        fail |= agentx_strcat(&logmsg, "}");
                        if (fail) {
                                agentx_log_axg_warn(axg,
                                    "Couldn't parse request");
                                free(logmsg);
                                agentx_get_free(axg);
                                return;
                        }
                        j++;
                }
        }
        agentx_log_axg_debug(axg, "response:%s", logmsg);
        free(logmsg);

        if (ax_response(ax->ax_ax, axs->axs_id, axg->axg_transactionid,
            axg->axg_packetid, 0, error, index, vbl, nvarbind) == -1) {
                agentx_log_axg_warn(axg, "Couldn't parse request");
                agentx_reset(ax);
        } else
                agentx_wantwrite(ax, ax->ax_fd);
        free(vbl);
        agentx_get_free(axg);
}

void
agentx_get_free(struct agentx_get *axg)
{
        struct agentx_varbind *axv;
        struct agentx_object *axo;
        struct agentx *ax;
        struct agentx_varbind_index *index;
        size_t i, j;

        if (axg->axg_axc != NULL) {
                ax = axg->axg_axc->axc_axs->axs_ax;
                TAILQ_REMOVE(&(ax->ax_getreqs), axg, axg_ax_getreqs);
        }

        for (i = 0; i < axg->axg_nvarbind; i++) {
                axv = &(axg->axg_varbind[i]);
                for (j = 0; axv->axv_axo != NULL &&
                    j < axv->axv_axo->axo_indexlen; j++) {
                        axo = axv->axv_axo;
                        index = &(axv->axv_index[j]);
                        if (axo->axo_index[j]->axi_vb.avb_type ==
                            AX_DATA_TYPE_OCTETSTRING ||
                            axo->axo_index[j]->axi_vb.avb_type ==
                            AX_DATA_TYPE_IPADDRESS)
                                free(index->axv_idata.avb_ostring.aos_string);
                }
                ax_varbind_free(&(axg->axg_varbind[i].axv_vb));
        }

        free(axg->axg_context.aos_string);
        free(axg->axg_varbind);
        free(axg);
}

static void
agentx_varbind_start(struct agentx_varbind *axv)
{
        struct agentx_get *axg = axv->axv_axg;
        struct agentx_context *axc = axg->axg_axc;
        struct agentx_object *axo, axo_search;
        struct agentx_varbind_index *index;
        struct ax_oid *oid;
        union ax_data *data;
        struct in_addr *ipaddress;
        unsigned char *ipbytes;
        size_t i, j, k;
        int overflow = 0, dynamic;

#ifdef AX_DEBUG
        if (!axv->axv_initialized)
                agentx_log_axg_fatalx(axv->axv_axg,
                    "%s: axv_initialized not set", __func__);
#endif

        if (axc == NULL) {
                agentx_varbind_error_type(axv, AX_PDU_ERROR_PROCESSINGERROR, 1);
                return;
        }

        bcopy(&(axv->axv_vb.avb_oid), &(axo_search.axo_oid),
            sizeof(axo_search.axo_oid));

        do {
                axo = RB_FIND(axc_objects, &(axc->axc_objects), &axo_search);
                if (axo_search.axo_oid.aoi_idlen > 0)
                        axo_search.axo_oid.aoi_idlen--;
        } while (axo == NULL && axo_search.axo_oid.aoi_idlen > 0);
        if (axo == NULL || axo->axo_cstate != AX_CSTATE_OPEN) {
                axv->axv_include = 1;
                if (axv->axv_axg->axg_type == AX_PDU_TYPE_GET) {
                        agentx_varbind_nosuchobject(axv);
                        return;
                }
                bcopy(&(axv->axv_vb.avb_oid), &(axo_search.axo_oid),
                    sizeof(axo_search.axo_oid));
                axo = RB_NFIND(axc_objects, &(axc->axc_objects), &axo_search);
getnext:
                while (axo != NULL && axo->axo_cstate != AX_CSTATE_OPEN)
                        axo = RB_NEXT(axc_objects, &(axc->axc_objects), axo);
                if (axo == NULL ||
                    ax_oid_cmp(&(axo->axo_oid), &(axv->axv_end)) >= 0) {
                        agentx_varbind_endofmibview(axv);
                        return;
                }
                bcopy(&(axo->axo_oid), &(axv->axv_vb.avb_oid),
                    sizeof(axo->axo_oid));
        }
        axv->axv_axo = axo;
        axv->axv_indexlen = axo->axo_indexlen;
        if (agentx_object_lock(axo) == -1) {
                agentx_varbind_error_type(axv,
                    AX_PDU_ERROR_PROCESSINGERROR, 1);
                return;
        }

        oid = &(axv->axv_vb.avb_oid);
        if (axo->axo_indexlen == 0) {
                if (axv->axv_axg->axg_type == AX_PDU_TYPE_GET) {
                        if (oid->aoi_idlen != axo->axo_oid.aoi_idlen + 1 ||
                            oid->aoi_id[oid->aoi_idlen - 1] != 0) {
                                agentx_varbind_nosuchinstance(axv);
                                return;
                        }
                } else {
                        if (oid->aoi_idlen == axo->axo_oid.aoi_idlen) {
                                oid->aoi_id[oid->aoi_idlen++] = 0;
                                axv->axv_include = 1;
                        } else {
                                axv->axv_axo = NULL;
                                agentx_object_unlock(axo);
                                axo = RB_NEXT(axc_objects, &(axc->axc_objects),
                                    axo);
                                goto getnext;
                        }
                }
                j = oid->aoi_idlen;
        } else
                j = axo->axo_oid.aoi_idlen;
/*
 * We can't trust what the client gives us, so sometimes we need to map it to
 * index type.
 * - AX_PDU_TYPE_GET: we always return AX_DATA_TYPE_NOSUCHINSTANCE
 * - AX_PDU_TYPE_GETNEXT:
 *   - Missing OID digits to match indices or !dynamic indices
 *     (AX_DATA_TYPE_INTEGER) underflows will result in the following indices to
 *     be NUL-initialized and the request type will be set to
 *     AGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE
 *   - An overflow can happen on AX_DATA_TYPE_OCTETSTRING and
 *     AX_DATA_TYPE_IPADDRESS data, and AX_DATA_TYPE_OCTETSTRING and
 *     AX_DATA_TYPE_OID length. This results in request type being set to
 *     AGENTX_REQUEST_TYPE_GETNEXT and will set the index to its maximum
 *     value:
 *     - AX_DATA_TYPE_INTEGER: UINT32_MAX
 *     - AX_DATA_TYPE_OCTETSTRING: aos_slen = UINT32_MAX and
 *       aos_string = NULL
 *     - AX_DATA_TYPE_OID: aoi_idlen = UINT32_MAX and aoi_id[x] = UINT32_MAX
 *     - AX_DATA_TYPE_IPADDRESS: 255.255.255.255
 */
        for (dynamic = 0, i = 0; i < axo->axo_indexlen; i++, j++) {
                index = &(axv->axv_index[i]);
                index->axv_axi = axo->axo_index[i];
                data = &(index->axv_idata);
                if (axo->axo_index[i]->axi_type == AXI_TYPE_DYNAMIC)
                        dynamic = 1;
                switch (axo->axo_index[i]->axi_vb.avb_type) {
                case AX_DATA_TYPE_INTEGER:
                        if (index->axv_axi->axi_type != AXI_TYPE_DYNAMIC) {
                                index->axv_idata.avb_int32 =
                                    index->axv_axi->axi_vb.avb_data.avb_int32;
                                if (overflow == 0) {
                                        if ((uint32_t)index->axv_idata.avb_int32 >
                                            oid->aoi_id[j])
                                                overflow = -1;
                                        else if ((uint32_t)index->axv_idata.avb_int32 <
                                            oid->aoi_id[j])
                                                overflow = 1;
                                }
                        } else if (overflow == 1)
                                index->axv_idata.avb_int32 = INT32_MAX;
                        else if (j >= oid->aoi_idlen || overflow == -1)
                                index->axv_idata.avb_int32 = 0;
                        else {
                                if (oid->aoi_id[j] > INT32_MAX) {
                                        index->axv_idata.avb_int32 = INT32_MAX;
                                        overflow = 1;
                                } else
                                        index->axv_idata.avb_int32 =
                                            oid->aoi_id[j];
                        }
                        break;
                case AX_DATA_TYPE_OCTETSTRING:
                        if (overflow == 1) {
                                data->avb_ostring.aos_slen = UINT32_MAX;
                                data->avb_ostring.aos_string = NULL;
                                continue;
                        } else if (j >= oid->aoi_idlen || overflow == -1) {
                                data->avb_ostring.aos_slen = 0;
                                data->avb_ostring.aos_string = NULL;
                                continue;
                        }
                        if (agentx_object_implied(axo, index->axv_axi))
                                data->avb_ostring.aos_slen = oid->aoi_idlen - j;
                        else {
                                data->avb_ostring.aos_slen = oid->aoi_id[j++];
                                if (data->avb_ostring.aos_slen >=
                                    AGENTX_OID_MAX_LEN - j) {
                                        data->avb_ostring.aos_slen = UINT32_MAX;
                                        overflow = 1;
                                }
                        }
                        if (data->avb_ostring.aos_slen == UINT32_MAX ||
                            data->avb_ostring.aos_slen == 0) {
                                data->avb_ostring.aos_string = NULL;
                                continue;
                        }
                        data->avb_ostring.aos_string =
                            malloc(data->avb_ostring.aos_slen + 1);
                        if (data->avb_ostring.aos_string == NULL) {
                                agentx_log_axg_warn(axg,
                                    "Failed to bind string index");
                                agentx_varbind_error_type(axv,
                                    AX_PDU_ERROR_PROCESSINGERROR, 1);
                                return;
                        }
                        for (k = 0; k < data->avb_ostring.aos_slen; k++, j++) {
                                if (j < oid->aoi_idlen && oid->aoi_id[j] > 0xff)
                                        overflow = 1;
                                if (overflow == 1)
                                        data->avb_ostring.aos_string[k] = 0xff;
                                else if (j >= oid->aoi_idlen || overflow == -1)
                                        data->avb_ostring.aos_string[k] = '\0';
                                else
                                        data->avb_ostring.aos_string[k] =
                                            oid->aoi_id[j];
                        }
                        data->avb_ostring.aos_string[k] = '\0';
                        j--;
                        break;
                case AX_DATA_TYPE_OID:
                        if (overflow == 1) {
                                data->avb_oid.aoi_idlen = UINT32_MAX;
                                continue;
                        } else if (j >= oid->aoi_idlen || overflow == -1) {
                                data->avb_oid.aoi_idlen = 0;
                                continue;
                        }
                        if (agentx_object_implied(axo, index->axv_axi))
                                data->avb_oid.aoi_idlen = oid->aoi_idlen - j;
                        else {
                                data->avb_oid.aoi_idlen = oid->aoi_id[j++];
                                if (data->avb_oid.aoi_idlen >=
                                    AGENTX_OID_MAX_LEN - j) {
                                        data->avb_oid.aoi_idlen = UINT32_MAX;
                                        overflow = 1;
                                }
                        }
                        if (data->avb_oid.aoi_idlen == UINT32_MAX ||
                            data->avb_oid.aoi_idlen == 0)
                                continue;
                        for (k = 0; k < data->avb_oid.aoi_idlen; k++, j++) {
                                if (overflow == 1)
                                        data->avb_oid.aoi_id[k] = UINT32_MAX;
                                else if (j >= oid->aoi_idlen || overflow == -1)
                                        data->avb_oid.aoi_id[k] = 0;
                                else
                                        data->avb_oid.aoi_id[k] =
                                            oid->aoi_id[j];
                        }
                        j--;
                        break;
                case AX_DATA_TYPE_IPADDRESS:
                        ipaddress = malloc(sizeof(*ipaddress));
                        if (ipaddress == NULL) {
                                agentx_log_axg_warn(axg,
                                    "Failed to bind ipaddress index");
                                agentx_varbind_error_type(axv,
                                    AX_PDU_ERROR_PROCESSINGERROR, 1);
                                return;
                        }
                        ipbytes = (unsigned char *)ipaddress;
                        for (k = 0; k < 4; k++, j++) {
                                if (j < oid->aoi_idlen && oid->aoi_id[j] > 255)
                                        overflow = 1;
                                if (overflow == 1)
                                        ipbytes[k] = 255;
                                else if (j >= oid->aoi_idlen || overflow == -1)
                                        ipbytes[k] = 0;
                                else
                                        ipbytes[k] = oid->aoi_id[j];
                        }
                        j--;
                        data->avb_ostring.aos_slen = sizeof(*ipaddress);
                        data->avb_ostring.aos_string =
                            (unsigned char *)ipaddress;
                        break;
                default:
#ifdef AX_DEBUG
                        agentx_log_axg_fatalx(axg,
                            "%s: unexpected index type", __func__);
#else
                        agentx_log_axg_warnx(axg,
                            "%s: unexpected index type", __func__);
                        agentx_varbind_error_type(axv,
                            AX_PDU_ERROR_PROCESSINGERROR, 1);
                        return;
#endif
                }
        }
        if (axv->axv_axg->axg_type == AX_PDU_TYPE_GET) {
                if (j != oid->aoi_idlen || overflow) {
                        agentx_varbind_nosuchinstance(axv);
                        return;
                }
        }

        if (overflow == 1) {
                axv->axv_include = 0;
        } else if (overflow == -1) {
                axv->axv_include = 1;
        } else if (j < oid->aoi_idlen)
                axv->axv_include = 0;
        else if (j > oid->aoi_idlen)
                axv->axv_include = 1;
        if (agentx_varbind_request(axv) == AGENTX_REQUEST_TYPE_GETNEXT &&
            !dynamic) {
                agentx_varbind_endofmibview(axv);
                return;
        }

        axo->axo_get(axv);
}

void
agentx_varbind_integer(struct agentx_varbind *axv, int32_t value)
{
        axv->axv_vb.avb_type = AX_DATA_TYPE_INTEGER;
        axv->axv_vb.avb_data.avb_int32 = value;

        agentx_varbind_finalize(axv);
}

void
agentx_varbind_string(struct agentx_varbind *axv, const char *value)
{
        agentx_varbind_nstring(axv, (const unsigned char *)value,
            strlen(value));
}

void
agentx_varbind_nstring(struct agentx_varbind *axv,
    const unsigned char *value, size_t slen)
{
        axv->axv_vb.avb_data.avb_ostring.aos_string = malloc(slen);
        if (axv->axv_vb.avb_data.avb_ostring.aos_string == NULL) {
                agentx_log_axg_warn(axv->axv_axg, "Couldn't bind string");
                agentx_varbind_error_type(axv,
                    AX_PDU_ERROR_PROCESSINGERROR, 1);
                return;
        }
        axv->axv_vb.avb_type = AX_DATA_TYPE_OCTETSTRING;
        memcpy(axv->axv_vb.avb_data.avb_ostring.aos_string, value, slen);
        axv->axv_vb.avb_data.avb_ostring.aos_slen = slen;

        agentx_varbind_finalize(axv);
}

void
agentx_varbind_printf(struct agentx_varbind *axv, const char *fmt, ...)
{
        va_list ap;
        int r;

        axv->axv_vb.avb_type = AX_DATA_TYPE_OCTETSTRING;
        va_start(ap, fmt);
        r = vasprintf((char **)&(axv->axv_vb.avb_data.avb_ostring.aos_string),
            fmt, ap);
        va_end(ap);
        if (r == -1) {
                axv->axv_vb.avb_data.avb_ostring.aos_string = NULL;
                agentx_log_axg_warn(axv->axv_axg, "Couldn't bind string");
                agentx_varbind_error_type(axv, AX_PDU_ERROR_PROCESSINGERROR, 1);
                return;
        }
        axv->axv_vb.avb_data.avb_ostring.aos_slen = r;

        agentx_varbind_finalize(axv);
}

void
agentx_varbind_null(struct agentx_varbind *axv)
{
        axv->axv_vb.avb_type = AX_DATA_TYPE_NULL;

        agentx_varbind_finalize(axv);
}

void
agentx_varbind_oid(struct agentx_varbind *axv, const uint32_t oid[],
    size_t oidlen)
{
        const char *errstr;

        axv->axv_vb.avb_type = AX_DATA_TYPE_OID;

        if (agentx_oidfill(&(axv->axv_vb.avb_data.avb_oid),
            oid, oidlen, &errstr) == -1) {
#ifdef AX_DEBUG
                agentx_log_axg_fatalx(axv->axv_axg, "%s: %s", __func__, errstr);
#else
                agentx_log_axg_warnx(axv->axv_axg, "%s: %s", __func__, errstr);
                agentx_varbind_error_type(axv, AX_PDU_ERROR_PROCESSINGERROR, 1);
                return;
#endif
        }

        agentx_varbind_finalize(axv);
}

void
agentx_varbind_object(struct agentx_varbind *axv,
    struct agentx_object *axo)
{
        agentx_varbind_oid(axv, axo->axo_oid.aoi_id,
            axo->axo_oid.aoi_idlen);
}

void
agentx_varbind_index(struct agentx_varbind *axv,
    struct agentx_index *axi)
{
        agentx_varbind_oid(axv, axi->axi_vb.avb_oid.aoi_id,
            axi->axi_vb.avb_oid.aoi_idlen);
}


void
agentx_varbind_ipaddress(struct agentx_varbind *axv,
    const struct in_addr *value)
{
        axv->axv_vb.avb_type = AX_DATA_TYPE_IPADDRESS;
        axv->axv_vb.avb_data.avb_ostring.aos_string = malloc(4);
        if (axv->axv_vb.avb_data.avb_ostring.aos_string == NULL) {
                agentx_log_axg_warn(axv->axv_axg, "Couldn't bind ipaddress");
                agentx_varbind_error_type(axv,
                    AX_PDU_ERROR_PROCESSINGERROR, 1);
                return;
        }
        memcpy(axv->axv_vb.avb_data.avb_ostring.aos_string, value, 4);
        axv->axv_vb.avb_data.avb_ostring.aos_slen = 4;

        agentx_varbind_finalize(axv);
}

void
agentx_varbind_counter32(struct agentx_varbind *axv, uint32_t value)
{
        axv->axv_vb.avb_type = AX_DATA_TYPE_COUNTER32;
        axv->axv_vb.avb_data.avb_uint32 = value;

        agentx_varbind_finalize(axv);
}

void
agentx_varbind_gauge32(struct agentx_varbind *axv, uint32_t value)
{
        axv->axv_vb.avb_type = AX_DATA_TYPE_GAUGE32;
        axv->axv_vb.avb_data.avb_uint32 = value;

        agentx_varbind_finalize(axv);
}

void
agentx_varbind_unsigned32(struct agentx_varbind *axv, uint32_t value)
{
        agentx_varbind_gauge32(axv, value);
}

void
agentx_varbind_timeticks(struct agentx_varbind *axv, uint32_t value)
{
        axv->axv_vb.avb_type = AX_DATA_TYPE_TIMETICKS;
        axv->axv_vb.avb_data.avb_uint32 = value;

        agentx_varbind_finalize(axv);
}

void
agentx_varbind_opaque(struct agentx_varbind *axv, const char *string,
    size_t strlen)
{
        axv->axv_vb.avb_type = AX_DATA_TYPE_OPAQUE;
        axv->axv_vb.avb_data.avb_ostring.aos_string = malloc(strlen);
        if (axv->axv_vb.avb_data.avb_ostring.aos_string == NULL) {
                agentx_log_axg_warn(axv->axv_axg, "Couldn't bind opaque");
                agentx_varbind_error_type(axv,
                    AX_PDU_ERROR_PROCESSINGERROR, 1);
                return;
        }
        memcpy(axv->axv_vb.avb_data.avb_ostring.aos_string, string, strlen);
        axv->axv_vb.avb_data.avb_ostring.aos_slen = strlen;

        agentx_varbind_finalize(axv);
}

void
agentx_varbind_counter64(struct agentx_varbind *axv, uint64_t value)
{
        axv->axv_vb.avb_type = AX_DATA_TYPE_COUNTER64;
        axv->axv_vb.avb_data.avb_uint64 = value;

        agentx_varbind_finalize(axv);
}

void
agentx_varbind_notfound(struct agentx_varbind *axv)
{
        if (axv->axv_indexlen == 0) {
#ifdef AX_DEBUG
                agentx_log_axg_fatalx(axv->axv_axg, "%s invalid call",
                    __func__);
#else
                agentx_log_axg_warnx(axv->axv_axg, "%s invalid call",
                    __func__);
                agentx_varbind_error_type(axv,
                    AX_PDU_ERROR_GENERR, 1);
#endif
        } else if (axv->axv_axg->axg_type == AX_PDU_TYPE_GET)
                agentx_varbind_nosuchinstance(axv);
        else
                agentx_varbind_endofmibview(axv);
}

void
agentx_varbind_error(struct agentx_varbind *axv)
{
        agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 1);
}

static void
agentx_varbind_error_type(struct agentx_varbind *axv,
    enum ax_pdu_error error, int done)
{
        if (axv->axv_error == AX_PDU_ERROR_NOERROR) {
                axv->axv_error = error;
        }

        if (done) {
                axv->axv_vb.avb_type = AX_DATA_TYPE_NULL;

                agentx_varbind_finalize(axv);
        }
}

static void
agentx_varbind_finalize(struct agentx_varbind *axv)
{
        struct agentx_get *axg = axv->axv_axg;
        struct ax_oid oid;
        union ax_data *data;
        size_t i, j;
        int cmp;

        if (axv->axv_error != AX_PDU_ERROR_NOERROR) {
                bcopy(&(axv->axv_start), &(axv->axv_vb.avb_oid),
                    sizeof(axv->axv_start));
                goto done;
        }
        bcopy(&(axv->axv_axo->axo_oid), &oid, sizeof(oid));
        if (axv->axv_indexlen == 0)
                ax_oid_add(&oid, 0);
        for (i = 0; i < axv->axv_indexlen; i++) {
                data = &(axv->axv_index[i].axv_idata);
                switch (axv->axv_index[i].axv_axi->axi_vb.avb_type) {
                case AX_DATA_TYPE_INTEGER:
                        if (ax_oid_add(&oid, data->avb_int32) == -1)
                                goto fail;
                        break;
                case AX_DATA_TYPE_OCTETSTRING:
                        if (!agentx_object_implied(axv->axv_axo,
                            axv->axv_index[i].axv_axi)) {
                                if (ax_oid_add(&oid,
                                    data->avb_ostring.aos_slen) == -1)
                                        goto fail;
                        }
                        for (j = 0; j < data->avb_ostring.aos_slen; j++) {
                                if (ax_oid_add(&oid,
                                    (uint8_t)data->avb_ostring.aos_string[j]) ==
                                    -1)
                                        goto fail;
                        }
                        break;
                case AX_DATA_TYPE_OID:
                        if (!agentx_object_implied(axv->axv_axo,
                            axv->axv_index[i].axv_axi)) {
                                if (ax_oid_add(&oid,
                                    data->avb_oid.aoi_idlen) == -1)
                                        goto fail;
                        }
                        for (j = 0; j < data->avb_oid.aoi_idlen; j++) {
                                if (ax_oid_add(&oid,
                                    data->avb_oid.aoi_id[j]) == -1)
                                        goto fail;
                        }
                        break;
                case AX_DATA_TYPE_IPADDRESS:
                        for (j = 0; j < 4; j++) {
                                if (ax_oid_add(&oid,
                                    data->avb_ostring.aos_string == NULL ? 0 :
                                    (uint8_t)data->avb_ostring.aos_string[j]) ==
                                    -1)
                                        goto fail;
                        }
                        break;
                default:
#ifdef AX_DEBUG
                        agentx_log_axg_fatalx(axg,
                            "%s: unsupported index type", __func__);
#else
                        bcopy(&(axv->axv_start), &(axv->axv_vb.avb_oid),
                            sizeof(axv->axv_start));
                        axv->axv_error = AX_PDU_ERROR_PROCESSINGERROR;
                        agentx_object_unlock(axv->axv_axo);
                        agentx_get_finalize(axv->axv_axg);
                        return;
#endif
                }
        }
        cmp = ax_oid_cmp(&oid, &(axv->axv_vb.avb_oid));
        switch (agentx_varbind_request(axv)) {
        case AGENTX_REQUEST_TYPE_GET:
                if (cmp != 0) {
#ifdef AX_DEBUG
                        agentx_log_axg_fatalx(axg, "index changed");
#else
                        agentx_log_axg_warnx(axg, "index changed");
                        bcopy(&(axv->axv_start), &(axv->axv_vb.avb_oid),
                            sizeof(axv->axv_start));
                        axv->axv_error = AX_PDU_ERROR_GENERR;
                        break;
#endif
                }
                break;
        case AGENTX_REQUEST_TYPE_GETNEXT:
                if (cmp <= 0) {
#ifdef AX_DEBUG
                        agentx_log_axg_fatalx(axg, "indices not incremented");
#else
                        agentx_log_axg_warnx(axg, "indices not incremented");
                        bcopy(&(axv->axv_start), &(axv->axv_vb.avb_oid),
                            sizeof(axv->axv_start));
                        axv->axv_error = AX_PDU_ERROR_GENERR;
                        break;
#endif
                }
                /* FALLTHROUGH */
        case AGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE:
                if (cmp < 0) {
#ifdef AX_DEBUG
                        agentx_log_axg_fatalx(axg, "index decremented");
#else
                        agentx_log_axg_warnx(axg, "index decremented");
                        bcopy(&(axv->axv_start), &(axv->axv_vb.avb_oid),
                            sizeof(axv->axv_start));
                        axv->axv_error = AX_PDU_ERROR_GENERR;
                        break;
#endif
                }
                if (axv->axv_end.aoi_idlen != 0 &&
                    ax_oid_cmp(&oid, &(axv->axv_end)) >= 0) {
                        agentx_varbind_endofmibview(axv);
                        return;
                }
                bcopy(&oid, &(axv->axv_vb.avb_oid), sizeof(oid));
        }
done:
        agentx_object_unlock(axv->axv_axo);
        agentx_get_finalize(axv->axv_axg);
        return;

fail:
        agentx_log_axg_warnx(axg, "oid too large");
        bcopy(&(axv->axv_start), &(axv->axv_vb.avb_oid),
            sizeof(axv->axv_start));
        axv->axv_error = AX_PDU_ERROR_GENERR;
        agentx_object_unlock(axv->axv_axo);
        agentx_get_finalize(axv->axv_axg);
}

static void
agentx_varbind_nosuchobject(struct agentx_varbind *axv)
{
        axv->axv_vb.avb_type = AX_DATA_TYPE_NOSUCHOBJECT;

        if (axv->axv_axo != NULL)
                agentx_object_unlock(axv->axv_axo);
        agentx_get_finalize(axv->axv_axg);
}

static void
agentx_varbind_nosuchinstance(struct agentx_varbind *axv)
{
        axv->axv_vb.avb_type = AX_DATA_TYPE_NOSUCHINSTANCE;

        if (axv->axv_axo != NULL)
                agentx_object_unlock(axv->axv_axo);
        agentx_get_finalize(axv->axv_axg);
}

static void
agentx_varbind_endofmibview(struct agentx_varbind *axv)
{
        struct agentx_object *axo;
        struct ax_varbind *vb;
        struct agentx_varbind_index *index;
        size_t i;

#ifdef AX_DEBUG
        if (axv->axv_axg->axg_type != AX_PDU_TYPE_GETNEXT &&
            axv->axv_axg->axg_type != AX_PDU_TYPE_GETBULK)
                agentx_log_axg_fatalx(axv->axv_axg,
                    "%s: invalid request type", __func__);
#endif

        if (axv->axv_axo != NULL &&
            (axo = RB_NEXT(axc_objects, &(axc->axc_objects),
            axv->axv_axo)) != NULL &&
            ax_oid_cmp(&(axo->axo_oid), &(axv->axv_end)) < 0) {
                bcopy(&(axo->axo_oid), &(axv->axv_vb.avb_oid),
                    sizeof(axo->axo_oid));
                axv->axv_include = 1;
                for (i = 0; i < axv->axv_indexlen; i++) {
                        index = &(axv->axv_index[i]);
                        vb = &(index->axv_axi->axi_vb);
                        if (vb->avb_type == AX_DATA_TYPE_OCTETSTRING ||
                            vb->avb_type == AX_DATA_TYPE_IPADDRESS)
                                free(index->axv_idata.avb_ostring.aos_string);
                }
                bzero(&(axv->axv_index), sizeof(axv->axv_index));
                agentx_object_unlock(axv->axv_axo);
                agentx_varbind_start(axv);
                return;
        }

        bcopy(&(axv->axv_start), &(axv->axv_vb.avb_oid),
            sizeof(axv->axv_start));
        axv->axv_vb.avb_type = AX_DATA_TYPE_ENDOFMIBVIEW;

        if (axv->axv_axo != NULL)
                agentx_object_unlock(axv->axv_axo);
        agentx_get_finalize(axv->axv_axg);
}

enum agentx_request_type
agentx_varbind_request(struct agentx_varbind *axv)
{
        if (axv->axv_axg->axg_type == AX_PDU_TYPE_GET)
                return AGENTX_REQUEST_TYPE_GET;
        if (axv->axv_include)
                return AGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE;
        return AGENTX_REQUEST_TYPE_GETNEXT;
}

struct agentx_object *
agentx_varbind_get_object(struct agentx_varbind *axv)
{
        return axv->axv_axo;
}

int32_t
agentx_varbind_get_index_integer(struct agentx_varbind *axv,
    struct agentx_index *axi)
{
        size_t i;

        if (axi->axi_vb.avb_type != AX_DATA_TYPE_INTEGER) {
#ifdef AX_DEBUG
                agentx_log_axg_fatalx(axv->axv_axg, "invalid index type");
#else
                agentx_log_axg_warnx(axv->axv_axg, "invalid index type");
                agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0);
                return 0;
#endif
        }

        for (i = 0; i < axv->axv_indexlen; i++) {
                if (axv->axv_index[i].axv_axi == axi)
                        return axv->axv_index[i].axv_idata.avb_int32;
        }
#ifdef AX_DEBUG
        agentx_log_axg_fatalx(axv->axv_axg, "invalid index");
#else
        agentx_log_axg_warnx(axv->axv_axg, "invalid index");
        agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0);
        return 0;
#endif
}

const unsigned char *
agentx_varbind_get_index_string(struct agentx_varbind *axv,
    struct agentx_index *axi, size_t *slen, int *implied)
{
        struct agentx_varbind_index *index;
        size_t i;

        if (axi->axi_vb.avb_type != AX_DATA_TYPE_OCTETSTRING) {
#ifdef AX_DEBUG
                agentx_log_axg_fatalx(axv->axv_axg, "invalid index type");
#else
                agentx_log_axg_warnx(axv->axv_axg, "invalid index type");
                agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0);
                *slen = 0;
                *implied = 0;
                return NULL;
#endif
        }

        for (i = 0; i < axv->axv_indexlen; i++) {
                if (axv->axv_index[i].axv_axi == axi) {
                        index = &(axv->axv_index[i]);
                        *slen = index->axv_idata.avb_ostring.aos_slen;
                        *implied = agentx_object_implied(axv->axv_axo, axi);
                        return index->axv_idata.avb_ostring.aos_string;
                }
        }

#ifdef AX_DEBUG
        agentx_log_axg_fatalx(axv->axv_axg, "invalid index");
#else
        agentx_log_axg_warnx(axv->axv_axg, "invalid index");
        agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0);
        *slen = 0;
        *implied = 0;
        return NULL;
#endif
}

const uint32_t *
agentx_varbind_get_index_oid(struct agentx_varbind *axv,
    struct agentx_index *axi, size_t *oidlen, int *implied)
{
        struct agentx_varbind_index *index;
        size_t i;

        if (axi->axi_vb.avb_type != AX_DATA_TYPE_OID) {
#ifdef AX_DEBUG
                agentx_log_axg_fatalx(axv->axv_axg, "invalid index type");
#else
                agentx_log_axg_warnx(axv->axv_axg, "invalid index type");
                agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0);
                *oidlen = 0;
                *implied = 0;
                return NULL;
#endif
        }

        for (i = 0; i < axv->axv_indexlen; i++) {
                if (axv->axv_index[i].axv_axi == axi) {
                        index = &(axv->axv_index[i]);
                        *oidlen = index->axv_idata.avb_oid.aoi_idlen;
                        *implied = agentx_object_implied(axv->axv_axo, axi);
                        return index->axv_idata.avb_oid.aoi_id;
                }
        }

#ifdef AX_DEBUG
        agentx_log_axg_fatalx(axv->axv_axg, "invalid index");
#else
        agentx_log_axg_warnx(axv->axv_axg, "invalid index");
        agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0);
        *oidlen = 0;
        *implied = 0;
        return NULL;
#endif
}

const struct in_addr *
agentx_varbind_get_index_ipaddress(struct agentx_varbind *axv,
    struct agentx_index *axi)
{
        static struct in_addr nuladdr = {0};
        struct agentx_varbind_index *index;
        size_t i;

        if (axi->axi_vb.avb_type != AX_DATA_TYPE_IPADDRESS) {
#ifdef AX_DEBUG
                agentx_log_axg_fatalx(axv->axv_axg, "invalid index type");
#else
                agentx_log_axg_warnx(axv->axv_axg, "invalid index type");
                agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0);
                return NULL;
#endif
        }

        for (i = 0; i < axv->axv_indexlen; i++) {
                if (axv->axv_index[i].axv_axi == axi) {
                        index = &(axv->axv_index[i]);
                        if (index->axv_idata.avb_ostring.aos_string == NULL)
                                return &nuladdr;
                        return (struct in_addr *)
                            index->axv_idata.avb_ostring.aos_string;
                }
        }

#ifdef AX_DEBUG
        agentx_log_axg_fatalx(axv->axv_axg, "invalid index");
#else
        agentx_log_axg_warnx(axv->axv_axg, "invalid index");
        agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0);
        return NULL;
#endif
}

void
agentx_varbind_set_index_integer(struct agentx_varbind *axv,
    struct agentx_index *axi, int32_t value)
{
        size_t i;

        if (axi->axi_vb.avb_type != AX_DATA_TYPE_INTEGER) {
#ifdef AX_DEBUG
                agentx_log_axg_fatalx(axv->axv_axg, "invalid index type");
#else
                agentx_log_axg_warnx(axv->axv_axg, "invalid index type");
                agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0);
                return;
#endif
        }

        if (value < 0) {
#ifdef AX_DEBUG
                agentx_log_axg_fatalx(axv->axv_axg, "invalid index value");
#else
                agentx_log_axg_warnx(axv->axv_axg, "invalid index value");
                agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0);
                return;
#endif
        }

        for (i = 0; i < axv->axv_indexlen; i++) {
                if (axv->axv_index[i].axv_axi == axi) {
                        if (axv->axv_axg->axg_type == AX_PDU_TYPE_GET &&
                            axv->axv_index[i].axv_idata.avb_int32 != value) {
#ifdef AX_DEBUG
                                agentx_log_axg_fatalx(axv->axv_axg,
                                    "can't change index on GET");
#else
                                agentx_log_axg_warnx(axv->axv_axg,
                                    "can't change index on GET");
                                agentx_varbind_error_type(axv,
                                    AX_PDU_ERROR_GENERR, 0);
                                return;
#endif
                        }
                        axv->axv_index[i].axv_idata.avb_int32 = value;
                        return;
                }
        }
#ifdef AX_DEBUG
        agentx_log_axg_fatalx(axv->axv_axg, "invalid index");
#else
        agentx_log_axg_warnx(axv->axv_axg, "invalid index");
        agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0);
#endif
}

void
agentx_varbind_set_index_string(struct agentx_varbind *axv,
    struct agentx_index *axi, const char *value)
{
        agentx_varbind_set_index_nstring(axv, axi,
            (const unsigned char *)value, strlen(value));
}

void
agentx_varbind_set_index_nstring(struct agentx_varbind *axv,
    struct agentx_index *axi, const unsigned char *value, size_t slen)
{
        struct ax_ostring *curvalue;
        unsigned char *nstring;
        size_t i;

        if (axi->axi_vb.avb_type != AX_DATA_TYPE_OCTETSTRING) {
#ifdef AX_DEBUG
                agentx_log_axg_fatalx(axv->axv_axg, "invalid index type");
#else
                agentx_log_axg_warnx(axv->axv_axg, "invalid index type");
                agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0);
                return;
#endif
        }

        for (i = 0; i < axv->axv_indexlen; i++) {
                if (axv->axv_index[i].axv_axi == axi) {
                        if (axi->axi_vb.avb_data.avb_ostring.aos_slen != 0 &&
                            axi->axi_vb.avb_data.avb_ostring.aos_slen != slen) {
#ifdef AX_DEBUG
                                agentx_log_axg_fatalx(axv->axv_axg,
                                    "invalid string length on explicit length "
                                    "string");
#else
                                agentx_log_axg_warnx(axv->axv_axg,
                                    "invalid string length on explicit length "
                                    "string");
                                agentx_varbind_error_type(axv,
                                    AX_PDU_ERROR_GENERR, 0);
                                return;
#endif
                        }
                        curvalue = &(axv->axv_index[i].axv_idata.avb_ostring);
                        if (axv->axv_axg->axg_type == AX_PDU_TYPE_GET &&
                            (curvalue->aos_slen != slen ||
                            memcmp(curvalue->aos_string, value, slen) != 0)) {
#ifdef AX_DEBUG
                                agentx_log_axg_fatalx(axv->axv_axg,
                                    "can't change index on GET");
#else
                                agentx_log_axg_warnx(axv->axv_axg,
                                    "can't change index on GET");
                                agentx_varbind_error_type(axv,
                                    AX_PDU_ERROR_GENERR, 0);
                                return;
#endif
                        }
                        if ((nstring = recallocarray(curvalue->aos_string,
                            curvalue->aos_slen + 1, slen + 1, 1)) == NULL) {
                                agentx_log_axg_warn(axv->axv_axg,
                                    "Failed to bind string index");
                                agentx_varbind_error_type(axv,
                                    AX_PDU_ERROR_PROCESSINGERROR, 0);
                                return;
                        }
                        curvalue->aos_string = nstring;
                        memcpy(nstring, value, slen);
                        curvalue->aos_slen = slen;
                        return;
                }
        }
#ifdef AX_DEBUG
        agentx_log_axg_fatalx(axv->axv_axg, "invalid index");
#else
        agentx_log_axg_warnx(axv->axv_axg, "invalid index");
        agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0);
#endif
}

void
agentx_varbind_set_index_oid(struct agentx_varbind *axv,
    struct agentx_index *axi, const uint32_t *value, size_t oidlen)
{
        struct ax_oid *curvalue, oid;
        const char *errstr;
        size_t i;

        if (axi->axi_vb.avb_type != AX_DATA_TYPE_OID) {
#ifdef AX_DEBUG
                agentx_log_axg_fatalx(axv->axv_axg, "invalid index type");
#else
                agentx_log_axg_warnx(axv->axv_axg, "invalid index type");
                agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0);
                return;
#endif
        }

        for (i = 0; i < axv->axv_indexlen; i++) {
                if (axv->axv_index[i].axv_axi == axi) {
                        if (axi->axi_vb.avb_data.avb_oid.aoi_idlen != 0 &&
                            axi->axi_vb.avb_data.avb_oid.aoi_idlen != oidlen) {
#ifdef AX_DEBUG
                                agentx_log_axg_fatalx(axv->axv_axg,
                                    "invalid oid length on explicit length "
                                    "oid");
#else
                                agentx_log_axg_warnx(axv->axv_axg,
                                    "invalid oid length on explicit length "
                                    "oid");
                                agentx_varbind_error_type(axv,
                                    AX_PDU_ERROR_GENERR, 0);
                                return;
#endif
                        }
                        curvalue = &(axv->axv_index[i].axv_idata.avb_oid);
                        if (agentx_oidfill(&oid, value,
                            oidlen, &errstr) == -1) {
#ifdef AX_DEBUG
                                agentx_log_axg_fatalx(axv->axv_axg, "%s: %s",
                                    __func__, errstr);
#else
                                agentx_log_axg_warnx(axv->axv_axg, "%s: %s",
                                     __func__, errstr);
                                agentx_varbind_error_type(axv,
                                     AX_PDU_ERROR_PROCESSINGERROR, 1);
                                return;
#endif
                        }

                        if (axv->axv_axg->axg_type == AX_PDU_TYPE_GET &&
                            ax_oid_cmp(&oid, curvalue) != 0) {
#ifdef AX_DEBUG
                                agentx_log_axg_fatalx(axv->axv_axg,
                                    "can't change index on GET");
#else
                                agentx_log_axg_warnx(axv->axv_axg,
                                    "can't change index on GET");
                                agentx_varbind_error_type(axv,
                                    AX_PDU_ERROR_GENERR, 0);
                                return;
#endif
                        }
                        
                        *curvalue = oid;
                        return;
                }
        }
#ifdef AX_DEBUG
        agentx_log_axg_fatalx(axv->axv_axg, "invalid index");
#else
        agentx_log_axg_warnx(axv->axv_axg, "invalid index");
        agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0);
#endif
}

void
agentx_varbind_set_index_object(struct agentx_varbind *axv,
    struct agentx_index *axi, struct agentx_object *axo)
{
        agentx_varbind_set_index_oid(axv, axi, axo->axo_oid.aoi_id,
            axo->axo_oid.aoi_idlen);
}

void
agentx_varbind_set_index_ipaddress(struct agentx_varbind *axv,
    struct agentx_index *axi, const struct in_addr *addr)
{
        struct ax_ostring *curvalue;
        size_t i;

        if (axi->axi_vb.avb_type != AX_DATA_TYPE_IPADDRESS) {
#ifdef AX_DEBUG
                agentx_log_axg_fatalx(axv->axv_axg, "invalid index type");
#else
                agentx_log_axg_warnx(axv->axv_axg, "invalid index type");
                agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0);
                return;
#endif
        }

        for (i = 0; i < axv->axv_indexlen; i++) {
                if (axv->axv_index[i].axv_axi == axi) {
                        curvalue = &(axv->axv_index[i].axv_idata.avb_ostring);
                        if (curvalue->aos_string == NULL)
                                curvalue->aos_string = calloc(1, sizeof(*addr));
                        if (curvalue->aos_string == NULL) {
                                agentx_log_axg_warn(axv->axv_axg,
                                    "Failed to bind ipaddress index");
                                agentx_varbind_error_type(axv,
                                    AX_PDU_ERROR_PROCESSINGERROR, 0);
                                return;
                        }
                        if (axv->axv_axg->axg_type == AX_PDU_TYPE_GET &&
                            memcmp(addr, curvalue->aos_string,
                            sizeof(*addr)) != 0) {
#ifdef AX_DEBUG
                                agentx_log_axg_fatalx(axv->axv_axg,
                                    "can't change index on GET");
#else
                                agentx_log_axg_warnx(axv->axv_axg,
                                    "can't change index on GET");
                                agentx_varbind_error_type(axv,
                                    AX_PDU_ERROR_GENERR, 0);
                                return;
#endif
                        }
                        bcopy(addr, curvalue->aos_string, sizeof(*addr));
                        return;
                }
        }
#ifdef AX_DEBUG
        agentx_log_axg_fatalx(axv->axv_axg, "invalid index");
#else
        agentx_log_axg_warnx(axv->axv_axg, "invalid index");
        agentx_varbind_error_type(axv, AX_PDU_ERROR_GENERR, 0);
#endif
}

static int
agentx_request(struct agentx *ax, uint32_t packetid,
    int (*cb)(struct ax_pdu *, void *), void *cookie)
{
        struct agentx_request *axr;

#ifdef AX_DEBUG
        if (ax->ax_ax->ax_wblen == 0)
                agentx_log_ax_fatalx(ax, "%s: no data to be written",
                    __func__);
#endif

        if ((axr = calloc(1, sizeof(*axr))) == NULL) {
                agentx_log_ax_warn(ax, "couldn't create request context");
                agentx_reset(ax);
                return -1;
        }

        axr->axr_packetid = packetid;
        axr->axr_cb = cb;
        axr->axr_cookie = cookie;
        if (RB_INSERT(ax_requests, &(ax->ax_requests), axr) != NULL) {
#ifdef AX_DEBUG
                agentx_log_ax_fatalx(ax, "%s: duplicate packetid", __func__);
#else
                agentx_log_ax_warnx(ax, "%s: duplicate packetid", __func__);
                free(axr);
                agentx_reset(ax);
                return -1;
#endif
        }

        agentx_wantwrite(ax, ax->ax_fd);
        return 0;
}

static int
agentx_request_cmp(struct agentx_request *r1,
    struct agentx_request *r2)
{
        return r1->axr_packetid < r2->axr_packetid ? -1 :
            r1->axr_packetid > r2->axr_packetid;
}

static int
agentx_strcat(char **dst, const char *src)
{
        char *tmp;
        size_t dstlen = 0, buflen = 0, srclen, nbuflen;

        if (*dst != NULL) {
                dstlen = strlen(*dst);
                buflen = ((dstlen / 512) + 1) * 512;
        }

        srclen = strlen(src);
        if (*dst == NULL || dstlen + srclen > buflen) {
                nbuflen = (((dstlen + srclen) / 512) + 1) * 512;
                tmp = recallocarray(*dst, buflen, nbuflen, sizeof(*tmp));
                if (tmp == NULL)
                        return -1;
                *dst = tmp;
                buflen = nbuflen;
        }

        (void)strlcat(*dst, src, buflen);
        return 0;
}

static int
agentx_oidfill(struct ax_oid *oid, const uint32_t oidval[], size_t oidlen,
    const char **errstr)
{
        size_t i;

        if (oidlen < AGENTX_OID_MIN_LEN) {
                *errstr = "oidlen < 2";
                errno = EINVAL;
                return -1;
        }
        if (oidlen > AGENTX_OID_MAX_LEN) {
                *errstr = "oidlen > 128";
                errno = EINVAL;
                return -1;
        }

        for (i = 0; i < oidlen; i++)
                oid->aoi_id[i] = oidval[i];
        oid->aoi_idlen = oidlen;
        return 0;
}

void
agentx_read(struct agentx *ax)
{
        struct agentx_session *axs;
        struct agentx_context *axc;
        struct agentx_request axr_search, *axr;
        struct ax_pdu *pdu;
        int error;

        if ((pdu = ax_recv(ax->ax_ax)) == NULL) {
                if (errno == EAGAIN)
                        return;
                agentx_log_ax_warn(ax, "lost connection");
                agentx_reset(ax);
                return;
        }

        TAILQ_FOREACH(axs, &(ax->ax_sessions), axs_ax_sessions) {
                if (axs->axs_id == pdu->ap_header.aph_sessionid)
                        break;
                if (axs->axs_cstate == AX_CSTATE_WAITOPEN &&
                    axs->axs_packetid == pdu->ap_header.aph_packetid)
                        break;
        }
        if (axs == NULL) {
                agentx_log_ax_warnx(ax, "received unexpected session: %d",
                    pdu->ap_header.aph_sessionid);
                ax_pdu_free(pdu);
                agentx_reset(ax);
                return;
        }
        TAILQ_FOREACH(axc, &(axs->axs_contexts), axc_axs_contexts) {
                if ((pdu->ap_header.aph_flags &
                    AX_PDU_FLAG_NON_DEFAULT_CONTEXT) == 0 &&
                    axc->axc_name_default == 1)
                        break;
                if (pdu->ap_header.aph_flags &
                    AX_PDU_FLAG_NON_DEFAULT_CONTEXT &&
                    axc->axc_name_default == 0 &&
                    pdu->ap_context.aos_slen == axc->axc_name.aos_slen &&
                    memcmp(pdu->ap_context.aos_string,
                    axc->axc_name.aos_string, axc->axc_name.aos_slen) == 0)
                        break;
        }
        if (pdu->ap_header.aph_type != AX_PDU_TYPE_RESPONSE) {
                if (axc == NULL) {
                        agentx_log_ax_warnx(ax, "%s: invalid context",
                            pdu->ap_context.aos_string);
                        ax_pdu_free(pdu);
                        agentx_reset(ax);
                        return;
                }
        }

        switch (pdu->ap_header.aph_type) {
        case AX_PDU_TYPE_GET:
        case AX_PDU_TYPE_GETNEXT:
        case AX_PDU_TYPE_GETBULK:
                agentx_get_start(axc, pdu);
                break;
        /* Add stubs for set functions */
        case AX_PDU_TYPE_TESTSET:
        case AX_PDU_TYPE_COMMITSET:
        case AX_PDU_TYPE_UNDOSET:
                if (pdu->ap_header.aph_type == AX_PDU_TYPE_TESTSET)
                        error = AX_PDU_ERROR_NOTWRITABLE;
                else if (pdu->ap_header.aph_type == AX_PDU_TYPE_COMMITSET)
                        error = AX_PDU_ERROR_COMMITFAILED;
                else
                        error = AX_PDU_ERROR_UNDOFAILED;

                agentx_log_axc_debug(axc, "unsupported call: %s",
                    ax_pdutype2string(pdu->ap_header.aph_type));
                if (ax_response(ax->ax_ax, axs->axs_id,
                    pdu->ap_header.aph_transactionid,
                    pdu->ap_header.aph_packetid,
                    0, error, 1, NULL, 0) == -1)
                        agentx_log_axc_warn(axc,
                            "transaction: %u packetid: %u: failed to send "
                            "reply", pdu->ap_header.aph_transactionid,
                            pdu->ap_header.aph_packetid);
                if (ax->ax_ax->ax_wblen > 0)
                        agentx_wantwrite(ax, ax->ax_fd);
                break;
        case AX_PDU_TYPE_CLEANUPSET:
                agentx_log_ax_debug(ax, "unsupported call: %s",
                    ax_pdutype2string(pdu->ap_header.aph_type));
                break;
        case AX_PDU_TYPE_RESPONSE:
                axr_search.axr_packetid = pdu->ap_header.aph_packetid;
                axr = RB_FIND(ax_requests, &(ax->ax_requests), &axr_search);
                if (axr == NULL) {
                        if (axc == NULL)
                                agentx_log_ax_warnx(ax, "received "
                                    "response on non-request");
                        else
                                agentx_log_axc_warnx(axc, "received "
                                    "response on non-request");
                        break;
                }
                if (axc != NULL && pdu->ap_payload.ap_response.ap_error == 0) {
                        axc->axc_sysuptime =
                            pdu->ap_payload.ap_response.ap_uptime;
                        (void) clock_gettime(CLOCK_MONOTONIC,
                            &(axc->axc_sysuptimespec));
                }
                RB_REMOVE(ax_requests, &(ax->ax_requests), axr);
                (void) axr->axr_cb(pdu, axr->axr_cookie);
                free(axr);
                break;
        default:
                if (axc == NULL)
                        agentx_log_ax_warnx(ax, "unsupported call: %s",
                            ax_pdutype2string(pdu->ap_header.aph_type));
                else
                        agentx_log_axc_warnx(axc, "unsupported call: %s",
                            ax_pdutype2string(pdu->ap_header.aph_type));
                agentx_reset(ax);
                break;
        }
        ax_pdu_free(pdu);
}

void
agentx_write(struct agentx *ax)
{
        ssize_t send;

        if ((send = ax_send(ax->ax_ax)) == -1) {
                if (errno == EAGAIN) {
                        agentx_wantwrite(ax, ax->ax_fd);
                        return;
                }
                agentx_log_ax_warn(ax, "lost connection");
                agentx_reset(ax);
                return;
        }
        if (send > 0)
                agentx_wantwrite(ax, ax->ax_fd);
}

RB_GENERATE_STATIC(ax_requests, agentx_request, axr_ax_requests,
    agentx_request_cmp)
RB_GENERATE_STATIC(axc_objects, agentx_object, axo_axc_objects,
    agentx_object_cmp)