Edit

IABSD.fr/src/lib/libagentx/agentx.c

Branch :

  • Show log

    Commit

  • Author : jsg
    Date : 2025-12-08 10:22:19
    Hash : a5d3afcc
    Message : fix memory leaks in error paths found with cppcheck, ok martijn@

  • 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)