src/lib/libagentx/agentx.c

4146 lines
109 KiB
C

/* $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 requests */
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)