[BACK]Return to agentx.c CVS log [TXT][DIR] Up to [local] / src / lib / libagentx

File: [local] / src / lib / libagentx / agentx.c (download)

Revision 1.24, Sun Oct 29 11:10:07 2023 UTC (7 months, 1 week ago) by martijn
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, HEAD
Changes since 1.23: +45 -11 lines

Add a test to the end of agentx_varbind_finalize(), after completing the
full OID, that checks if we're >= searchrange.end. If so, just make it
an endOfMIBView.

OK tb@

/*	$OpenBSD: agentx.c,v 1.24 2023/10/29 11:10:07 martijn 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
			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);
		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 = axa->axa_axc->axc_axs->axs_ax;
	int axfree;

	if (axa == NULL)
		return;

	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)