summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMartijn van Duren <martijn@cvs.openbsd.org>2020-09-16 10:48:53 +0000
committerMartijn van Duren <martijn@cvs.openbsd.org>2020-09-16 10:48:53 +0000
commitefc5c04aef96e37947ea4975e3e899e2e685a354 (patch)
tree8bce956f9b4121d3a15fd546f72840d5cba80e4a /lib
parenta6dffad470bfbb25f6f4cf8b1636119ea23955bb (diff)
Import libagentx.
This is the same code as I committed earlier to relayd with some fixes by bluhm@. Not yet linked to the build so developers can return home before things explode. To be used by other daemons soon(tm). OK bluhm@
Diffstat (limited to 'lib')
-rw-r--r--lib/libagentx/Makefile31
-rw-r--r--lib/libagentx/Symbols.list61
-rw-r--r--lib/libagentx/agentx.c1359
-rw-r--r--lib/libagentx/agentx.h232
-rw-r--r--lib/libagentx/shlib_version2
-rw-r--r--lib/libagentx/subagentx.3650
-rw-r--r--lib/libagentx/subagentx.c3994
-rw-r--r--lib/libagentx/subagentx.h147
-rw-r--r--lib/libagentx/subagentx_internal.h124
-rw-r--r--lib/libagentx/subagentx_log.c316
10 files changed, 6916 insertions, 0 deletions
diff --git a/lib/libagentx/Makefile b/lib/libagentx/Makefile
new file mode 100644
index 00000000000..82c255f9786
--- /dev/null
+++ b/lib/libagentx/Makefile
@@ -0,0 +1,31 @@
+# $OpenBSD: Makefile,v 1.1 2020/09/16 10:48:52 martijn Exp $
+
+LIB= agentx
+
+SRCS= agentx.c subagentx.c subagentx_log.c
+HDRS= subagentx.h
+MAN= subagentx.3
+
+CFLAGS+= -Wall -I${.CURDIR}
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
+CFLAGS+= -Wmissing-declarations
+CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
+CFLAGS+= -Wsign-compare
+
+CLEANFILES= ${VERSION_SCRIPT}
+
+VERSION_SCRIPT= Symbols.map
+SYMBOL_LIST= ${.CURDIR}/Symbols.list
+
+includes:
+ @cd ${.CURDIR}; for i in ${HDRS}; do \
+ cmp -s $$i ${DESTDIR}/usr/include/$$i || \
+ ${INSTALL} ${INSTALL_COPY} -m 444 -o $(BINOWN) -g $(BINGRP) $$i \
+ ${DESTDIR}/usr/include; done
+
+${VERSION_SCRIPT}: ${SYMBOL_LIST}
+ { printf '{\n\tglobal:\n'; \
+ sed '/^[._a-zA-Z]/s/$$/;/; s/^/ /' ${SYMBOL_LIST}; \
+ printf '\n\tlocal:\n\t\t*;\n};\n'; } >$@.tmp && mv $@.tmp $@
+
+.include <bsd.lib.mk>
diff --git a/lib/libagentx/Symbols.list b/lib/libagentx/Symbols.list
new file mode 100644
index 00000000000..b31c7b0d95f
--- /dev/null
+++ b/lib/libagentx/Symbols.list
@@ -0,0 +1,61 @@
+subagentx_log_fatal
+subagentx_log_warn
+subagentx_log_info
+subagentx_log_debug
+subagentx
+subagentx_connect
+subagentx_read
+subagentx_write
+subagentx_wantwrite
+subagentx_free
+subagentx_session
+subagentx_session_free
+subagentx_context
+subagentx_context_object_find
+subagentx_context_object_nfind
+subagentx_context_uptime
+subagentx_context_free
+subagentx_agentcaps
+subagentx_agentcaps_free
+subagentx_region
+subagentx_region_free
+subagentx_index_integer_new
+subagentx_index_integer_any
+subagentx_index_integer_value
+subagentx_index_integer_dynamic
+subagentx_index_string_dynamic
+subagentx_index_nstring_dynamic
+subagentx_index_oid_dynamic
+subagentx_index_noid_dynamic
+subagentx_index_ipaddress_dynamic
+subagentx_index_free
+subagentx_object
+subagentx_object_free
+subagentx_varbind_integer
+subagentx_varbind_string
+subagentx_varbind_nstring
+subagentx_varbind_printf
+subagentx_varbind_null
+subagentx_varbind_oid
+subagentx_varbind_object
+subagentx_varbind_index
+subagentx_varbind_ipaddress
+subagentx_varbind_counter32
+subagentx_varbind_gauge32
+subagentx_varbind_timeticks
+subagentx_varbind_opaque
+subagentx_varbind_counter64
+subagentx_varbind_notfound
+subagentx_varbind_error
+subagentx_varbind_request
+subagentx_varbind_get_object
+subagentx_varbind_get_index_integer
+subagentx_varbind_get_index_string
+subagentx_varbind_get_index_oid
+subagentx_varbind_get_index_ipaddress
+subagentx_varbind_set_index_integer
+subagentx_varbind_set_index_string
+subagentx_varbind_set_index_nstring
+subagentx_varbind_set_index_oid
+subagentx_varbind_set_index_object
+subagentx_varbind_set_index_ipaddress
diff --git a/lib/libagentx/agentx.c b/lib/libagentx/agentx.c
new file mode 100644
index 00000000000..91e7aefc615
--- /dev/null
+++ b/lib/libagentx/agentx.c
@@ -0,0 +1,1359 @@
+/*
+ * 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 <sys/socket.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <endian.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include "agentx.h"
+
+#define AGENTX_PDU_HEADER 20
+
+static int agentx_pdu_need(struct agentx *, size_t);
+static int agentx_pdu_header(struct agentx *,
+ enum agentx_pdu_type, uint8_t, uint32_t, uint32_t, uint32_t,
+ struct agentx_ostring *);
+static uint32_t agentx_packetid(struct agentx *);
+static uint32_t agentx_pdu_queue(struct agentx *);
+static int agentx_pdu_add_uint16(struct agentx *, uint16_t);
+static int agentx_pdu_add_uint32(struct agentx *, uint32_t);
+static int agentx_pdu_add_uint64(struct agentx *, uint64_t);
+static int agentx_pdu_add_oid(struct agentx *, struct agentx_oid *, int);
+static int agentx_pdu_add_str(struct agentx *, struct agentx_ostring *);
+static int agentx_pdu_add_varbindlist( struct agentx *, struct agentx_varbind *,
+ size_t);
+static uint16_t agentx_pdutoh16(struct agentx_pdu_header *, uint8_t *);
+static uint32_t agentx_pdutoh32(struct agentx_pdu_header *, uint8_t *);
+static uint64_t agentx_pdutoh64(struct agentx_pdu_header *, uint8_t *);
+static ssize_t agentx_pdutooid(struct agentx_pdu_header *, struct agentx_oid *,
+ uint8_t *, size_t);
+static ssize_t agentx_pdutoostring(struct agentx_pdu_header *,
+ struct agentx_ostring *, uint8_t *, size_t);
+static ssize_t agentx_pdutovarbind(struct agentx_pdu_header *,
+ struct agentx_varbind *, uint8_t *, size_t);
+
+struct agentx *
+agentx_new(int fd)
+{
+ struct agentx *ax;
+
+ if (fd == -1) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if ((ax = calloc(1, sizeof(*ax))) == NULL)
+ return NULL;
+ ax->ax_fd = fd;
+ if ((ax->ax_rbuf = malloc(512)) == NULL)
+ goto fail;
+ ax->ax_rbsize = 512;
+ ax->ax_byteorder = AGENTX_BYTE_ORDER_NATIVE;
+
+ return ax;
+
+fail:
+ free(ax);
+ return NULL;
+}
+
+void
+agentx_free(struct agentx *ax)
+{
+ if (ax == NULL)
+ return;
+ close(ax->ax_fd);
+ free(ax->ax_rbuf);
+ free(ax->ax_wbuf);
+ free(ax->ax_packetids);
+ free(ax);
+}
+
+struct agentx_pdu *
+agentx_recv(struct agentx *ax)
+{
+ struct agentx_pdu *pdu;
+ struct agentx_pdu_header header;
+ struct agentx_pdu_response *response;
+ struct agentx_varbind *varbind;
+ struct agentx_pdu_searchrangelist *srl;
+ struct agentx_pdu_varbindlist *vbl;
+ struct agentx_searchrange *sr;
+ size_t rbsize, packetidx = 0, i, rawlen;
+ ssize_t nread;
+ uint8_t *u8;
+ uint8_t *rbuf;
+ int found;
+
+ /* Only read a single packet at a time to make sure libevent triggers */
+ if (ax->ax_rblen < AGENTX_PDU_HEADER) {
+ if ((nread = read(ax->ax_fd, ax->ax_rbuf + ax->ax_rblen,
+ AGENTX_PDU_HEADER - ax->ax_rblen)) == 0) {
+ errno = ECONNRESET;
+ return NULL;
+ }
+ if (nread == -1)
+ return NULL;
+ ax->ax_rblen += nread;
+ if (ax->ax_rblen < AGENTX_PDU_HEADER) {
+ errno = EAGAIN;
+ return NULL;
+ }
+ }
+ u8 = ax->ax_rbuf;
+ header.aph_version = *u8++;
+ header.aph_type = *u8++;
+ header.aph_flags = *u8++;
+ u8++;
+ header.aph_sessionid = agentx_pdutoh32(&header, u8);
+ u8 += 4;
+ header.aph_transactionid = agentx_pdutoh32(&header, u8);
+ u8 += 4;
+ header.aph_packetid = agentx_pdutoh32(&header, u8);
+ u8 += 4;
+ header.aph_plength = agentx_pdutoh32(&header, u8);
+
+ if (header.aph_version != 1) {
+ errno = EPROTO;
+ return NULL;
+ }
+ if (ax->ax_rblen < AGENTX_PDU_HEADER + header.aph_plength) {
+ if (AGENTX_PDU_HEADER + header.aph_plength > ax->ax_rbsize) {
+ rbsize = (((AGENTX_PDU_HEADER + header.aph_plength)
+ / 512) + 1) * 512;
+ if ((rbuf = recallocarray(ax->ax_rbuf, ax->ax_rbsize,
+ rbsize, sizeof(*rbuf))) == NULL)
+ return NULL;
+ ax->ax_rbsize = rbsize;
+ ax->ax_rbuf = rbuf;
+ }
+ nread = read(ax->ax_fd, ax->ax_rbuf + ax->ax_rblen,
+ header.aph_plength - (ax->ax_rblen - AGENTX_PDU_HEADER));
+ if (nread == 0)
+ errno = ECONNRESET;
+ if (nread <= 0)
+ return NULL;
+ ax->ax_rblen += nread;
+ if (ax->ax_rblen < AGENTX_PDU_HEADER + header.aph_plength) {
+ errno = EAGAIN;
+ return NULL;
+ }
+ }
+
+ if ((pdu = calloc(1, sizeof(*pdu))) == NULL)
+ return NULL;
+
+ memcpy(&(pdu->ap_header), &header, sizeof(header));
+
+#if defined(AGENTX_DEBUG) && defined(AGENTX_DEBUG_VERBOSE)
+ {
+ char chars[4];
+ int print = 1;
+
+ fprintf(stderr, "received packet:\n");
+ for (i = 0; i < pdu->ap_header.aph_plength + AGENTX_PDU_HEADER;
+ i++) {
+ fprintf(stderr, "%02hhx ", ax->ax_rbuf[i]);
+ chars[i % 4] = ax->ax_rbuf[i];
+ if (!isprint(ax->ax_rbuf[i]))
+ print = 0;
+ if (i % 4 == 3) {
+ if (print)
+ fprintf(stderr, "%.4s", chars);
+ fprintf(stderr, "\n");
+ print = 1;
+ }
+ }
+ }
+#endif
+
+ u8 = (ax->ax_rbuf) + AGENTX_PDU_HEADER;
+ rawlen = pdu->ap_header.aph_plength;
+ if (pdu->ap_header.aph_flags & AGENTX_PDU_FLAG_NON_DEFAULT_CONTEXT) {
+ nread = agentx_pdutoostring(&header, &(pdu->ap_context), u8,
+ rawlen);
+ if (nread == -1)
+ goto fail;
+ rawlen -= nread;
+ u8 += nread;
+ }
+
+ switch (pdu->ap_header.aph_type) {
+ case AGENTX_PDU_TYPE_GETBULK:
+ if (rawlen < 4) {
+ errno = EPROTO;
+ goto fail;
+ }
+ pdu->ap_payload.ap_getbulk.ap_nonrep =
+ agentx_pdutoh16(&header, u8);
+ u8 += 2;
+ pdu->ap_payload.ap_getbulk.ap_maxrep =
+ agentx_pdutoh16(&header, u8);
+ u8 += 2;
+ srl = &(pdu->ap_payload.ap_getbulk.ap_srl);
+ rawlen -= 4;
+ /* FALLTHROUGH */
+ case AGENTX_PDU_TYPE_GET:
+ case AGENTX_PDU_TYPE_GETNEXT:
+ if (pdu->ap_header.aph_type != AGENTX_PDU_TYPE_GETBULK)
+ srl = &(pdu->ap_payload.ap_srl);
+ while (rawlen > 0 ) {
+ srl->ap_nsr++;
+ sr = reallocarray(srl->ap_sr, srl->ap_nsr, sizeof(*sr));
+ if (sr == NULL)
+ goto fail;
+ srl->ap_sr = sr;
+ sr += (srl->ap_nsr - 1);
+ if ((nread = agentx_pdutooid(&header, &(sr->asr_start),
+ u8, rawlen)) == -1)
+ goto fail;
+ rawlen -= nread;
+ u8 += nread;
+ if ((nread = agentx_pdutooid(&header, &(sr->asr_stop),
+ u8, rawlen)) == -1)
+ goto fail;
+ rawlen -= nread;
+ u8 += nread;
+ }
+ break;
+ case AGENTX_PDU_TYPE_TESTSET:
+ vbl = &(pdu->ap_payload.ap_vbl);
+ while (rawlen > 0) {
+ varbind = recallocarray(vbl->ap_varbind,
+ vbl->ap_nvarbind, vbl->ap_nvarbind + 1,
+ sizeof(*(vbl->ap_varbind)));
+ if (varbind == NULL)
+ goto fail;
+ vbl->ap_varbind = varbind;
+ nread = agentx_pdutovarbind(&header,
+ &(vbl->ap_varbind[vbl->ap_nvarbind]), u8, rawlen);
+ if (nread == -1)
+ goto fail;
+ vbl->ap_nvarbind++;
+ u8 += nread;
+ rawlen -= nread;
+ }
+ break;
+ case AGENTX_PDU_TYPE_COMMITSET:
+ case AGENTX_PDU_TYPE_UNDOSET:
+ case AGENTX_PDU_TYPE_CLEANUPSET:
+ if (rawlen != 0) {
+ errno = EPROTO;
+ goto fail;
+ }
+ break;
+ case AGENTX_PDU_TYPE_RESPONSE:
+ if (ax->ax_packetids != NULL) {
+ found = 0;
+ for (i = 0; ax->ax_packetids[i] != 0; i++) {
+ if (ax->ax_packetids[i] ==
+ pdu->ap_header.aph_packetid) {
+ packetidx = i;
+ found = 1;
+ }
+ }
+ if (found) {
+ ax->ax_packetids[packetidx] =
+ ax->ax_packetids[i - 1];
+ ax->ax_packetids[i - 1] = 0;
+ } else {
+ errno = EPROTO;
+ goto fail;
+ }
+ }
+ if (rawlen < 8) {
+ errno = EPROTO;
+ goto fail;
+ }
+ response = &(pdu->ap_payload.ap_response);
+ response->ap_uptime = agentx_pdutoh32(&header, u8);
+ u8 += 4;
+ response->ap_error = agentx_pdutoh16(&header, u8);
+ u8 += 2;
+ response->ap_index = agentx_pdutoh16(&header, u8);
+ u8 += 2;
+ rawlen -= 8;
+ while (rawlen > 0) {
+ varbind = recallocarray(response->ap_varbindlist,
+ response->ap_nvarbind, response->ap_nvarbind + 1,
+ sizeof(*(response->ap_varbindlist)));
+ if (varbind == NULL)
+ goto fail;
+ response->ap_varbindlist = varbind;
+ nread = agentx_pdutovarbind(&header,
+ &(response->ap_varbindlist[response->ap_nvarbind]),
+ u8, rawlen);
+ if (nread == -1)
+ goto fail;
+ response->ap_nvarbind++;
+ u8 += nread;
+ rawlen -= nread;
+ }
+ break;
+ default:
+ pdu->ap_payload.ap_raw = malloc(pdu->ap_header.aph_plength);
+ if (pdu->ap_payload.ap_raw == NULL)
+ goto fail;
+ memcpy(pdu->ap_payload.ap_raw, ax->ax_rbuf + AGENTX_PDU_HEADER,
+ pdu->ap_header.aph_plength);
+ break;
+ }
+
+ ax->ax_rblen = 0;
+
+ return pdu;
+fail:
+ agentx_pdu_free(pdu);
+ return NULL;
+}
+
+static int
+agentx_pdu_need(struct agentx *ax, size_t need)
+{
+ uint8_t *wbuf;
+ size_t wbsize;
+
+ if (ax->ax_wbtlen >= ax->ax_wbsize) {
+ wbsize = ((ax->ax_wbtlen / 512) + 1) * 512;
+ wbuf = recallocarray(ax->ax_wbuf, ax->ax_wbsize, wbsize, 1);
+ if (wbuf == NULL) {
+ ax->ax_wbtlen = ax->ax_wblen;
+ return -1;
+ }
+ ax->ax_wbsize = wbsize;
+ ax->ax_wbuf = wbuf;
+ }
+
+ return 0;
+}
+
+ssize_t
+agentx_send(struct agentx *ax)
+{
+ ssize_t nwrite;
+
+ if (ax->ax_wblen != ax->ax_wbtlen) {
+ errno = EALREADY;
+ return -1;
+ }
+
+ if (ax->ax_wblen == 0)
+ return 0;
+
+#if defined(AGENTX_DEBUG) && defined(AGENTX_DEBUG_VERBOSE)
+ {
+ size_t i;
+ char chars[4];
+ int print = 1;
+
+ fprintf(stderr, "sending packet:\n");
+ for (i = 0; i < ax->ax_wblen; i++) {
+ fprintf(stderr, "%02hhx ", ax->ax_wbuf[i]);
+ chars[i % 4] = ax->ax_wbuf[i];
+ if (!isprint(ax->ax_wbuf[i]))
+ print = 0;
+ if (i % 4 == 3) {
+ if (print)
+ fprintf(stderr, "%.4s", chars);
+ fprintf(stderr, "\n");
+ print = 1;
+ }
+ }
+ }
+#endif
+
+ if ((nwrite = send(ax->ax_fd, ax->ax_wbuf, ax->ax_wblen,
+ MSG_NOSIGNAL | MSG_DONTWAIT)) == -1)
+ return -1;
+
+ memmove(ax->ax_wbuf, ax->ax_wbuf + nwrite, ax->ax_wblen - nwrite);
+ ax->ax_wblen -= nwrite;
+ ax->ax_wbtlen = ax->ax_wblen;
+
+ return ax->ax_wblen;
+}
+
+uint32_t
+agentx_open(struct agentx *ax, uint8_t timeout, struct agentx_oid *oid,
+ struct agentx_ostring *descr)
+{
+ if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_OPEN, 0, 0, 0, 0,
+ NULL) == -1)
+ return 0;
+ agentx_pdu_need(ax, 4);
+ ax->ax_wbuf[ax->ax_wbtlen++] = timeout;
+ memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, 3);
+ ax->ax_wbtlen += 3;
+ if (agentx_pdu_add_oid(ax, oid, 0) == -1)
+ return 0;
+ if (agentx_pdu_add_str(ax, descr) == -1)
+ return 0;
+
+ return agentx_pdu_queue(ax);
+}
+
+uint32_t
+agentx_close(struct agentx *ax, uint32_t sessionid,
+ enum agentx_close_reason reason)
+{
+ if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_CLOSE, 0, sessionid, 0, 0,
+ NULL) == -1)
+ return 0;
+
+ if (agentx_pdu_need(ax, 4) == -1)
+ return 0;
+ ax->ax_wbuf[ax->ax_wbtlen++] = (uint8_t)reason;
+ memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, 3);
+ ax->ax_wbtlen += 3;
+
+ return agentx_pdu_queue(ax);
+}
+
+uint32_t
+agentx_indexallocate(struct agentx *ax, uint8_t flags, uint32_t sessionid,
+ struct agentx_ostring *context, struct agentx_varbind *vblist, size_t nvb)
+{
+ if (flags & ~(AGENTX_PDU_FLAG_NEW_INDEX | AGENTX_PDU_FLAG_ANY_INDEX)) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_INDEXALLOCATE, flags,
+ sessionid, 0, 0, context) == -1)
+ return 0;
+
+ if (agentx_pdu_add_varbindlist(ax, vblist, nvb) == -1)
+ return 0;
+
+ return agentx_pdu_queue(ax);
+}
+
+uint32_t
+agentx_indexdeallocate(struct agentx *ax, uint32_t sessionid,
+ struct agentx_ostring *context, struct agentx_varbind *vblist, size_t nvb)
+{
+ if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_INDEXDEALLOCATE, 0,
+ sessionid, 0, 0, context) == -1)
+ return 0;
+
+ if (agentx_pdu_add_varbindlist(ax, vblist, nvb) == -1)
+ return 0;
+
+ return agentx_pdu_queue(ax);
+}
+
+uint32_t
+agentx_addagentcaps(struct agentx *ax, uint32_t sessionid,
+ struct agentx_ostring *context, struct agentx_oid *id,
+ struct agentx_ostring *descr)
+{
+ if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_ADDAGENTCAPS, 0,
+ sessionid, 0, 0, context) == -1)
+ return 0;
+ if (agentx_pdu_add_oid(ax, id, 0) == -1)
+ return 0;
+ if (agentx_pdu_add_str(ax, descr) == -1)
+ return 0;
+
+ return agentx_pdu_queue(ax);
+}
+
+uint32_t
+agentx_removeagentcaps(struct agentx *ax, uint32_t sessionid,
+ struct agentx_ostring *context, struct agentx_oid *id)
+{
+ if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_REMOVEAGENTCAPS, 0,
+ sessionid, 0, 0, context) == -1)
+ return 0;
+ if (agentx_pdu_add_oid(ax, id, 0) == -1)
+ return 0;
+
+ return agentx_pdu_queue(ax);
+
+}
+
+uint32_t
+agentx_register(struct agentx *ax, uint8_t flags, uint32_t sessionid,
+ struct agentx_ostring *context, uint8_t timeout, uint8_t priority,
+ uint8_t range_subid, struct agentx_oid *subtree, uint32_t upperbound)
+{
+ if (flags & ~(AGENTX_PDU_FLAG_INSTANCE_REGISTRATION)) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_REGISTER, flags,
+ sessionid, 0, 0, context) == -1)
+ return 0;
+
+ if (agentx_pdu_need(ax, 4) == -1)
+ return 0;
+ ax->ax_wbuf[ax->ax_wbtlen++] = timeout;
+ ax->ax_wbuf[ax->ax_wbtlen++] = priority;
+ ax->ax_wbuf[ax->ax_wbtlen++] = range_subid;
+ ax->ax_wbuf[ax->ax_wbtlen++] = 0;
+ if (agentx_pdu_add_oid(ax, subtree, 0) == -1)
+ return 0;
+ if (range_subid != 0) {
+ if (agentx_pdu_add_uint32(ax, upperbound) == -1)
+ return 0;
+ }
+
+ return agentx_pdu_queue(ax);
+}
+
+uint32_t
+agentx_unregister(struct agentx *ax, uint32_t sessionid,
+ struct agentx_ostring *context, uint8_t priority, uint8_t range_subid,
+ struct agentx_oid *subtree, uint32_t upperbound)
+{
+ if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_UNREGISTER, 0,
+ sessionid, 0, 0, context) == -1)
+ return 0;
+
+ if (agentx_pdu_need(ax, 4) == -1)
+ return 0;
+ ax->ax_wbuf[ax->ax_wbtlen++] = 0;
+ ax->ax_wbuf[ax->ax_wbtlen++] = priority;
+ ax->ax_wbuf[ax->ax_wbtlen++] = range_subid;
+ ax->ax_wbuf[ax->ax_wbtlen++] = 0;
+ if (agentx_pdu_add_oid(ax, subtree, 0) == -1)
+ return 0;
+ if (range_subid != 0) {
+ if (agentx_pdu_add_uint32(ax, upperbound) == -1)
+ return 0;
+ }
+
+ return agentx_pdu_queue(ax);
+}
+
+int
+agentx_response(struct agentx *ax, uint32_t sessionid, uint32_t transactionid,
+ uint32_t packetid, struct agentx_ostring *context, uint32_t sysuptime,
+ uint16_t error, uint16_t index, struct agentx_varbind *vblist, size_t nvb)
+{
+ if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_RESPONSE, 0, sessionid,
+ transactionid, packetid, context) == -1)
+ return -1;
+
+ if (agentx_pdu_add_uint32(ax, sysuptime) == -1 ||
+ agentx_pdu_add_uint16(ax, error) == -1 ||
+ agentx_pdu_add_uint16(ax, index) == -1)
+ return -1;
+
+ if (agentx_pdu_add_varbindlist(ax, vblist, nvb) == -1)
+ return -1;
+ if (agentx_pdu_queue(ax) == 0)
+ return -1;
+ return 0;
+}
+
+void
+agentx_pdu_free(struct agentx_pdu *pdu)
+{
+ size_t i;
+ struct agentx_pdu_response *response;
+
+ if (pdu->ap_header.aph_flags & AGENTX_PDU_FLAG_NON_DEFAULT_CONTEXT)
+ free(pdu->ap_context.aos_string);
+
+ switch (pdu->ap_header.aph_type) {
+ case AGENTX_PDU_TYPE_GET:
+ case AGENTX_PDU_TYPE_GETNEXT:
+ case AGENTX_PDU_TYPE_GETBULK:
+ free(pdu->ap_payload.ap_srl.ap_sr);
+ break;
+ case AGENTX_PDU_TYPE_RESPONSE:
+ response = &(pdu->ap_payload.ap_response);
+ for (i = 0; i < response->ap_nvarbind; i++)
+ agentx_varbind_free(&(response->ap_varbindlist[i]));
+ free(response->ap_varbindlist);
+ break;
+ default:
+ free(pdu->ap_payload.ap_raw);
+ break;
+ }
+ free(pdu);
+}
+
+void
+agentx_varbind_free(struct agentx_varbind *varbind)
+{
+ switch (varbind->avb_type) {
+ case AGENTX_DATA_TYPE_OCTETSTRING:
+ case AGENTX_DATA_TYPE_IPADDRESS:
+ case AGENTX_DATA_TYPE_OPAQUE:
+ free(varbind->avb_data.avb_ostring.aos_string);
+ break;
+ default:
+ break;
+ }
+}
+
+const char *
+agentx_error2string(enum agentx_pdu_error error)
+{
+ static char buffer[64];
+ switch (error) {
+ case AGENTX_PDU_ERROR_NOERROR:
+ return "No error";
+ case AGENTX_PDU_ERROR_GENERR:
+ return "Generic error";
+ case AGENTX_PDU_ERROR_NOACCESS:
+ return "No access";
+ case AGENTX_PDU_ERROR_WRONGTYPE:
+ return "Wrong type";
+ case AGENTX_PDU_ERROR_WRONGLENGTH:
+ return "Wrong length";
+ case AGENTX_PDU_ERROR_WRONGENCODING:
+ return "Wrong encoding";
+ case AGENTX_PDU_ERROR_WRONGVALUE:
+ return "Wrong value";
+ case AGENTX_PDU_ERROR_NOCREATION:
+ return "No creation";
+ case AGENTX_PDU_ERROR_INCONSISTENTVALUE:
+ return "Inconsistent value";
+ case AGENTX_PDU_ERROR_RESOURCEUNAVAILABLE:
+ return "Resource unavailable";
+ case AGENTX_PDU_ERROR_COMMITFAILED:
+ return "Commit failed";
+ case AGENTX_PDU_ERROR_UNDOFAILED:
+ return "Undo failed";
+ case AGENTX_PDU_ERROR_NOTWRITABLE:
+ return "Not writable";
+ case AGENTX_PDU_ERROR_INCONSISTENTNAME:
+ return "Inconsistent name";
+ case AGENTX_PDU_ERROR_OPENFAILED:
+ return "Open Failed";
+ case AGENTX_PDU_ERROR_NOTOPEN:
+ return "Not open";
+ case AGENTX_PDU_ERROR_INDEXWRONGTYPE:
+ return "Index wrong type";
+ case AGENTX_PDU_ERROR_INDEXALREADYALLOCATED:
+ return "Index already allocated";
+ case AGENTX_PDU_ERROR_INDEXNONEAVAILABLE:
+ return "Index none available";
+ case AGENTX_PDU_ERROR_INDEXNOTALLOCATED:
+ return "Index not allocated";
+ case AGENTX_PDU_ERROR_UNSUPPORTEDCONETXT:
+ return "Unsupported context";
+ case AGENTX_PDU_ERROR_DUPLICATEREGISTRATION:
+ return "Duplicate registration";
+ case AGENTX_PDU_ERROR_UNKNOWNREGISTRATION:
+ return "Unkown registration";
+ case AGENTX_PDU_ERROR_UNKNOWNAGENTCAPS:
+ return "Unknown agent capabilities";
+ case AGENTX_PDU_ERROR_PARSEERROR:
+ return "Parse error";
+ case AGENTX_PDU_ERROR_REQUESTDENIED:
+ return "Request denied";
+ case AGENTX_PDU_ERROR_PROCESSINGERROR:
+ return "Processing error";
+ }
+ snprintf(buffer, sizeof(buffer), "Unknown error: %d", error);
+ return buffer;
+}
+
+const char *
+agentx_pdutype2string(enum agentx_pdu_type type)
+{
+ static char buffer[64];
+ switch(type) {
+ case AGENTX_PDU_TYPE_OPEN:
+ return "agentx-Open-PDU";
+ case AGENTX_PDU_TYPE_CLOSE:
+ return "agentx-Close-PDU";
+ case AGENTX_PDU_TYPE_REGISTER:
+ return "agentx-Register-PDU";
+ case AGENTX_PDU_TYPE_UNREGISTER:
+ return "agentx-Unregister-PDU";
+ case AGENTX_PDU_TYPE_GET:
+ return "agentx-Get-PDU";
+ case AGENTX_PDU_TYPE_GETNEXT:
+ return "agentx-GetNext-PDU";
+ case AGENTX_PDU_TYPE_GETBULK:
+ return "agentx-GetBulk-PDU";
+ case AGENTX_PDU_TYPE_TESTSET:
+ return "agentx-TestSet-PDU";
+ case AGENTX_PDU_TYPE_COMMITSET:
+ return "agentx-CommitSet-PDU";
+ case AGENTX_PDU_TYPE_UNDOSET:
+ return "agentx-UndoSet-PDU";
+ case AGENTX_PDU_TYPE_CLEANUPSET:
+ return "agentx-CleanupSet-PDU";
+ case AGENTX_PDU_TYPE_NOTIFY:
+ return "agentx-Notify-PDU";
+ case AGENTX_PDU_TYPE_PING:
+ return "agentx-Ping-PDU";
+ case AGENTX_PDU_TYPE_INDEXALLOCATE:
+ return "agentx-IndexAllocate-PDU";
+ case AGENTX_PDU_TYPE_INDEXDEALLOCATE:
+ return "agentx-IndexDeallocate-PDU";
+ case AGENTX_PDU_TYPE_ADDAGENTCAPS:
+ return "agentx-AddAgentCaps-PDU";
+ case AGENTX_PDU_TYPE_REMOVEAGENTCAPS:
+ return "agentx-RemoveAgentCaps-PDU";
+ case AGENTX_PDU_TYPE_RESPONSE:
+ return "agentx-Response-PDU";
+ }
+ snprintf(buffer, sizeof(buffer), "Unknown type: %d", type);
+ return buffer;
+}
+
+const char *
+agentx_closereason2string(enum agentx_close_reason reason)
+{
+ static char buffer[64];
+
+ switch (reason) {
+ case AGENTX_CLOSE_OTHER:
+ return "Undefined reason";
+ case AGENTX_CLOSEN_PARSEERROR:
+ return "Too many AgentX parse errors from peer";
+ case AGENTX_CLOSE_PROTOCOLERROR:
+ return "Too many AgentX protocol errors from peer";
+ case AGENTX_CLOSE_TIMEOUTS:
+ return "Too many timeouts waiting for peer";
+ case AGENTX_CLOSE_SHUTDOWN:
+ return "shutting down";
+ case AGENTX_CLOSE_BYMANAGER:
+ return "Manager shuts down";
+ }
+ snprintf(buffer, sizeof(buffer), "Unknown reason: %d", reason);
+ return buffer;
+}
+
+const char *
+agentx_oid2string(struct agentx_oid *oid)
+{
+ return agentx_oidrange2string(oid, 0, 0);
+}
+
+const char *
+agentx_oidrange2string(struct agentx_oid *oid, uint8_t range_subid,
+ uint32_t upperbound)
+{
+ static char buf[1024];
+ char *p;
+ size_t i, rest;
+ int ret;
+
+ rest = sizeof(buf);
+ p = buf;
+ for (i = 0; i < oid->aoi_idlen; i++) {
+ if (range_subid != 0 && range_subid - 1 == (uint8_t)i)
+ ret = snprintf(p, rest, ".[%u-%u]", oid->aoi_id[i],
+ upperbound);
+ else
+ ret = snprintf(p, rest, ".%u", oid->aoi_id[i]);
+ if ((size_t) ret >= rest) {
+ snprintf(buf, sizeof(buf), "Couldn't parse oid");
+ return buf;
+ }
+ p += ret;
+ rest -= (size_t) ret;
+ }
+ return buf;
+}
+
+const char *
+agentx_varbind2string(struct agentx_varbind *vb)
+{
+ static char buf[1024];
+ char tmpbuf[1024];
+ size_t i, bufleft;
+ int ishex = 0;
+ char *p;
+ int ret;
+
+ switch (vb->avb_type) {
+ case AGENTX_DATA_TYPE_INTEGER:
+ snprintf(buf, sizeof(buf), "%s: (int)%u",
+ agentx_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32);
+ break;
+ case AGENTX_DATA_TYPE_OCTETSTRING:
+ for (i = 0;
+ i < vb->avb_data.avb_ostring.aos_slen && !ishex; i++) {
+ if (!isprint(vb->avb_data.avb_ostring.aos_string[i]))
+ ishex = 1;
+ }
+ if (ishex) {
+ p = tmpbuf;
+ bufleft = sizeof(tmpbuf);
+ for (i = 0;
+ i < vb->avb_data.avb_ostring.aos_slen; i++) {
+ ret = snprintf(p, bufleft, " %02hhX",
+ vb->avb_data.avb_ostring.aos_string[i]);
+ if (ret >= (int) bufleft) {
+ p = strrchr(p, ' ');
+ strlcpy(p, "...", 4);
+ break;
+ }
+ p += 3;
+ bufleft -= 3;
+ }
+ ret = snprintf(buf, sizeof(buf), "%s: (hex-string)%s",
+ agentx_oid2string(&(vb->avb_oid)), tmpbuf);
+ if (ret >= (int) sizeof(buf)) {
+ p = strrchr(buf, ' ');
+ strlcpy(p, "...", 4);
+ }
+ } else {
+ ret = snprintf(buf, sizeof(buf), "%s: (string)",
+ agentx_oid2string(&(vb->avb_oid)));
+ if (ret >= (int) sizeof(buf)) {
+ snprintf(buf, sizeof(buf), "<too large OID>: "
+ "(string)<too large string>");
+ break;
+ }
+ p = buf + ret;
+ bufleft = (int) sizeof(buf) - ret;
+ if (snprintf(p, bufleft, "%.*s",
+ vb->avb_data.avb_ostring.aos_slen,
+ vb->avb_data.avb_ostring.aos_string) >=
+ (int) bufleft) {
+ p = buf + sizeof(buf) - 4;
+ strlcpy(p, "...", 4);
+ }
+ }
+ break;
+ case AGENTX_DATA_TYPE_NULL:
+ snprintf(buf, sizeof(buf), "%s: <null>",
+ agentx_oid2string(&(vb->avb_oid)));
+ break;
+ case AGENTX_DATA_TYPE_OID:
+ strlcpy(tmpbuf,
+ agentx_oid2string(&(vb->avb_data.avb_oid)), sizeof(tmpbuf));
+ snprintf(buf, sizeof(buf), "%s: (oid)%s",
+ agentx_oid2string(&(vb->avb_oid)), tmpbuf);
+ break;
+ case AGENTX_DATA_TYPE_IPADDRESS:
+ if (vb->avb_data.avb_ostring.aos_slen != 4) {
+ snprintf(buf, sizeof(buf), "%s: (ipaddress)<invalid>",
+ agentx_oid2string(&(vb->avb_oid)));
+ break;
+ }
+ if (inet_ntop(PF_INET, vb->avb_data.avb_ostring.aos_string,
+ tmpbuf, sizeof(tmpbuf)) == NULL) {
+ snprintf(buf, sizeof(buf), "%s: (ipaddress)"
+ "<unparseable>: %s",
+ agentx_oid2string(&(vb->avb_oid)),
+ strerror(errno));
+ break;
+ }
+ snprintf(buf, sizeof(buf), "%s: (ipaddress)%s",
+ agentx_oid2string(&(vb->avb_oid)), tmpbuf);
+ break;
+ case AGENTX_DATA_TYPE_COUNTER32:
+ snprintf(buf, sizeof(buf), "%s: (counter32)%u",
+ agentx_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32);
+ break;
+ case AGENTX_DATA_TYPE_GAUGE32:
+ snprintf(buf, sizeof(buf), "%s: (gauge32)%u",
+ agentx_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32);
+ break;
+ case AGENTX_DATA_TYPE_TIMETICKS:
+ snprintf(buf, sizeof(buf), "%s: (timeticks)%u",
+ agentx_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32);
+ break;
+ case AGENTX_DATA_TYPE_OPAQUE:
+ p = tmpbuf;
+ bufleft = sizeof(tmpbuf);
+ for (i = 0;
+ i < vb->avb_data.avb_ostring.aos_slen; i++) {
+ ret = snprintf(p, bufleft, " %02hhX",
+ vb->avb_data.avb_ostring.aos_string[i]);
+ if (ret >= (int) bufleft) {
+ p = strrchr(p, ' ');
+ strlcpy(p, "...", 4);
+ break;
+ }
+ p += 3;
+ bufleft -= 3;
+ }
+ ret = snprintf(buf, sizeof(buf), "%s: (opaque)%s",
+ agentx_oid2string(&(vb->avb_oid)), tmpbuf);
+ if (ret >= (int) sizeof(buf)) {
+ p = strrchr(buf, ' ');
+ strlcpy(p, "...", 4);
+ }
+ break;
+ case AGENTX_DATA_TYPE_COUNTER64:
+ snprintf(buf, sizeof(buf), "%s: (counter64)%"PRIu64,
+ agentx_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint64);
+ break;
+ case AGENTX_DATA_TYPE_NOSUCHOBJECT:
+ snprintf(buf, sizeof(buf), "%s: <noSuchObject>",
+ agentx_oid2string(&(vb->avb_oid)));
+ break;
+ case AGENTX_DATA_TYPE_NOSUCHINSTANCE:
+ snprintf(buf, sizeof(buf), "%s: <noSuchInstance>",
+ agentx_oid2string(&(vb->avb_oid)));
+ break;
+ case AGENTX_DATA_TYPE_ENDOFMIBVIEW:
+ snprintf(buf, sizeof(buf), "%s: <endOfMibView>",
+ agentx_oid2string(&(vb->avb_oid)));
+ break;
+ }
+ return buf;
+}
+
+int
+agentx_oid_cmp(struct agentx_oid *o1, struct agentx_oid *o2)
+{
+ size_t i, min;
+
+ min = o1->aoi_idlen < o2->aoi_idlen ? o1->aoi_idlen : o2->aoi_idlen;
+ for (i = 0; i < min; i++) {
+ if (o1->aoi_id[i] < o2->aoi_id[i])
+ return -1;
+ if (o1->aoi_id[i] > o2->aoi_id[i])
+ return 1;
+ }
+ /* o1 is parent of o2 */
+ if (o1->aoi_idlen < o2->aoi_idlen)
+ return -2;
+ /* o1 is child of o2 */
+ if (o1->aoi_idlen > o2->aoi_idlen)
+ return 2;
+ return 0;
+}
+
+int
+agentx_oid_add(struct agentx_oid *oid, uint32_t value)
+{
+ if (oid->aoi_idlen == AGENTX_OID_MAX_LEN)
+ return -1;
+ oid->aoi_id[oid->aoi_idlen++] = value;
+ return 0;
+}
+
+static uint32_t
+agentx_pdu_queue(struct agentx *ax)
+{
+ struct agentx_pdu_header header;
+ uint32_t packetid, plength;
+ size_t wbtlen = ax->ax_wbtlen;
+
+ header.aph_flags = ax->ax_byteorder == AGENTX_BYTE_ORDER_BE ?
+ AGENTX_PDU_FLAG_NETWORK_BYTE_ORDER : 0;
+ packetid = agentx_pdutoh32(&header, &(ax->ax_wbuf[ax->ax_wblen + 12]));
+ plength = (ax->ax_wbtlen - ax->ax_wblen) - AGENTX_PDU_HEADER;
+ ax->ax_wbtlen = ax->ax_wblen + 16;
+ (void)agentx_pdu_add_uint32(ax, plength);
+
+ ax->ax_wblen = ax->ax_wbtlen = wbtlen;
+
+ return packetid;
+}
+
+static int
+agentx_pdu_header(struct agentx *ax, enum agentx_pdu_type type, uint8_t flags,
+ uint32_t sessionid, uint32_t transactionid, uint32_t packetid,
+ struct agentx_ostring *context)
+{
+ if (ax->ax_wblen != ax->ax_wbtlen) {
+ errno = EALREADY;
+ return -1;
+ }
+
+ ax->ax_wbtlen = ax->ax_wblen;
+ if (agentx_pdu_need(ax, 4) == -1)
+ return -1;
+ ax->ax_wbuf[ax->ax_wbtlen++] = 1;
+ ax->ax_wbuf[ax->ax_wbtlen++] = (uint8_t) type;
+ if (context != NULL)
+ flags |= AGENTX_PDU_FLAG_NON_DEFAULT_CONTEXT;
+ if (ax->ax_byteorder == AGENTX_BYTE_ORDER_BE)
+ flags |= AGENTX_PDU_FLAG_NETWORK_BYTE_ORDER;
+ ax->ax_wbuf[ax->ax_wbtlen++] = flags;
+ ax->ax_wbuf[ax->ax_wbtlen++] = 0;
+ if (packetid == 0)
+ packetid = agentx_packetid(ax);
+ if (agentx_pdu_add_uint32(ax, sessionid) == -1 ||
+ agentx_pdu_add_uint32(ax, transactionid) == -1 ||
+ agentx_pdu_add_uint32(ax, packetid) == -1 ||
+ agentx_pdu_need(ax, 4) == -1)
+ return -1;
+ ax->ax_wbtlen += 4;
+ if (context != NULL) {
+ if (agentx_pdu_add_str(ax, context) == -1)
+ return -1;
+ }
+
+ return 0;
+}
+
+static uint32_t
+agentx_packetid(struct agentx *ax)
+{
+ uint32_t packetid, *packetids;
+ size_t npackets = 0, i;
+ int found;
+
+ if (ax->ax_packetids != NULL) {
+ for (npackets = 0; ax->ax_packetids[npackets] != 0; npackets++)
+ continue;
+ }
+ if (ax->ax_packetidsize == 0 || npackets == ax->ax_packetidsize - 1) {
+ packetids = recallocarray(ax->ax_packetids, ax->ax_packetidsize,
+ ax->ax_packetidsize + 25, sizeof(*packetids));
+ if (packetids == NULL)
+ return 0;
+ ax->ax_packetidsize += 25;
+ ax->ax_packetids = packetids;
+ }
+ do {
+ found = 0;
+ packetid = arc4random();
+ for (i = 0; ax->ax_packetids[i] != 0; i++) {
+ if (ax->ax_packetids[i] == packetid) {
+ found = 1;
+ break;
+ }
+ }
+ } while (packetid == 0 || found);
+ ax->ax_packetids[npackets] = packetid;
+
+ return packetid;
+}
+
+static int
+agentx_pdu_add_uint16(struct agentx *ax, uint16_t value)
+{
+ if (agentx_pdu_need(ax, sizeof(value)) == -1)
+ return -1;
+
+ if (ax->ax_byteorder == AGENTX_BYTE_ORDER_BE)
+ value = htobe16(value);
+ else
+ value = htole16(value);
+ memcpy(ax->ax_wbuf + ax->ax_wbtlen, &value, sizeof(value));
+ ax->ax_wbtlen += sizeof(value);
+ return 0;
+}
+
+static int
+agentx_pdu_add_uint32(struct agentx *ax, uint32_t value)
+{
+ if (agentx_pdu_need(ax, sizeof(value)) == -1)
+ return -1;
+
+ if (ax->ax_byteorder == AGENTX_BYTE_ORDER_BE)
+ value = htobe32(value);
+ else
+ value = htole32(value);
+ memcpy(ax->ax_wbuf + ax->ax_wbtlen, &value, sizeof(value));
+ ax->ax_wbtlen += sizeof(value);
+ return 0;
+}
+
+static int
+agentx_pdu_add_uint64(struct agentx *ax, uint64_t value)
+{
+ if (agentx_pdu_need(ax, sizeof(value)) == -1)
+ return -1;
+
+ if (ax->ax_byteorder == AGENTX_BYTE_ORDER_BE)
+ value = htobe64(value);
+ else
+ value = htole64(value);
+ memcpy(ax->ax_wbuf + ax->ax_wbtlen, &value, sizeof(value));
+ ax->ax_wbtlen += sizeof(value);
+ return 0;
+}
+
+
+static int
+agentx_pdu_add_oid(struct agentx *ax, struct agentx_oid *oid, int include)
+{
+ static struct agentx_oid nulloid = {0};
+ uint8_t prefix = 0, n_subid, i = 0;
+
+ n_subid = oid->aoi_idlen;
+
+ if (oid == NULL)
+ oid = &nulloid;
+
+ if (oid->aoi_idlen > 4 &&
+ oid->aoi_id[0] == 1 && oid->aoi_id[1] == 3 &&
+ oid->aoi_id[2] == 6 && oid->aoi_id[3] == 1 &&
+ oid->aoi_id[4] <= UINT8_MAX) {
+ prefix = oid->aoi_id[4];
+ i = 5;
+ }
+
+ if (agentx_pdu_need(ax, 4) == -1)
+ return -1;
+ ax->ax_wbuf[ax->ax_wbtlen++] = n_subid - i;
+ ax->ax_wbuf[ax->ax_wbtlen++] = prefix;
+ ax->ax_wbuf[ax->ax_wbtlen++] = !!include;
+ ax->ax_wbuf[ax->ax_wbtlen++] = 0;
+
+ for (; i < n_subid; i++) {
+ if (agentx_pdu_add_uint32(ax, oid->aoi_id[i]) == -1)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+agentx_pdu_add_str(struct agentx *ax, struct agentx_ostring *str)
+{
+ size_t length, zeroes;
+
+ if (agentx_pdu_add_uint32(ax, str->aos_slen) == -1)
+ return -1;
+
+ if ((zeroes = (4 - (str->aos_slen % 4))) == 4)
+ zeroes = 0;
+ length = str->aos_slen + zeroes;
+ if (agentx_pdu_need(ax, length) == -1)
+ return -1;
+
+ memcpy(&(ax->ax_wbuf[ax->ax_wbtlen]), str->aos_string, str->aos_slen);
+ ax->ax_wbtlen += str->aos_slen;
+ memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, zeroes);
+ ax->ax_wbtlen += zeroes;
+ return 0;
+}
+
+static int
+agentx_pdu_add_varbindlist(struct agentx *ax,
+ struct agentx_varbind *vblist, size_t nvb)
+{
+ size_t i;
+ uint16_t temp;
+
+ for (i = 0; i < nvb; i++) {
+ temp = (uint16_t) vblist[i].avb_type;
+ if (agentx_pdu_add_uint16(ax, temp) == -1 ||
+ agentx_pdu_need(ax, 2))
+ return -1;
+ memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, 2);
+ ax->ax_wbtlen += 2;
+ if (agentx_pdu_add_oid(ax, &(vblist[i].avb_oid), 0) == -1)
+ return -1;
+ switch (vblist[i].avb_type) {
+ case AGENTX_DATA_TYPE_INTEGER:
+ case AGENTX_DATA_TYPE_COUNTER32:
+ case AGENTX_DATA_TYPE_GAUGE32:
+ case AGENTX_DATA_TYPE_TIMETICKS:
+ if (agentx_pdu_add_uint32(ax,
+ vblist[i].avb_data.avb_uint32) == -1)
+ return -1;
+ break;
+ case AGENTX_DATA_TYPE_COUNTER64:
+ if (agentx_pdu_add_uint64(ax,
+ vblist[i].avb_data.avb_uint64) == -1)
+ return -1;
+ break;
+ case AGENTX_DATA_TYPE_OCTETSTRING:
+ case AGENTX_DATA_TYPE_IPADDRESS:
+ case AGENTX_DATA_TYPE_OPAQUE:
+ if (agentx_pdu_add_str(ax,
+ &(vblist[i].avb_data.avb_ostring)) == -1)
+ return -1;
+ break;
+ case AGENTX_DATA_TYPE_OID:
+ if (agentx_pdu_add_oid(ax,
+ &(vblist[i].avb_data.avb_oid), 1) == -1)
+ return -1;
+ break;
+ case AGENTX_DATA_TYPE_NULL:
+ case AGENTX_DATA_TYPE_NOSUCHOBJECT:
+ case AGENTX_DATA_TYPE_NOSUCHINSTANCE:
+ case AGENTX_DATA_TYPE_ENDOFMIBVIEW:
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static uint16_t
+agentx_pdutoh16(struct agentx_pdu_header *header, uint8_t *buf)
+{
+ uint16_t value;
+
+ memcpy(&value, buf, sizeof(value));
+ if (header->aph_flags & AGENTX_PDU_FLAG_NETWORK_BYTE_ORDER)
+ return be16toh(value);
+ return le16toh(value);
+}
+
+static uint32_t
+agentx_pdutoh32(struct agentx_pdu_header *header, uint8_t *buf)
+{
+ uint32_t value;
+
+ memcpy(&value, buf, sizeof(value));
+ if (header->aph_flags & AGENTX_PDU_FLAG_NETWORK_BYTE_ORDER)
+ return be32toh(value);
+ return le32toh(value);
+}
+
+static uint64_t
+agentx_pdutoh64(struct agentx_pdu_header *header, uint8_t *buf)
+{
+ uint64_t value;
+
+ memcpy(&value, buf, sizeof(value));
+ if (header->aph_flags & AGENTX_PDU_FLAG_NETWORK_BYTE_ORDER)
+ return be64toh(value);
+ return le64toh(value);
+}
+
+static ssize_t
+agentx_pdutooid(struct agentx_pdu_header *header, struct agentx_oid *oid,
+ uint8_t *buf, size_t rawlen)
+{
+ size_t i = 0;
+ ssize_t nread;
+
+ if (rawlen < 4)
+ goto fail;
+ rawlen -= 4;
+ nread = 4;
+ oid->aoi_idlen = *buf++;
+ if (rawlen < (oid->aoi_idlen * 4))
+ goto fail;
+ nread += oid->aoi_idlen * 4;
+ if (*buf != 0) {
+ oid->aoi_id[0] = 1;
+ oid->aoi_id[1] = 3;
+ oid->aoi_id[2] = 6;
+ oid->aoi_id[3] = 1;
+ oid->aoi_id[4] = *buf;
+ oid->aoi_idlen += 5;
+ i = 5;
+ }
+ buf++;
+ oid->aoi_include = *buf;
+ for (buf += 2; i < oid->aoi_idlen; i++, buf += 4)
+ oid->aoi_id[i] = agentx_pdutoh32(header, buf);
+
+ return nread;
+
+fail:
+ errno = EPROTO;
+ return -1;
+}
+
+static ssize_t
+agentx_pdutoostring(struct agentx_pdu_header *header,
+ struct agentx_ostring *ostring, uint8_t *buf, size_t rawlen)
+{
+ ssize_t nread;
+
+ if (rawlen < 4)
+ goto fail;
+
+ ostring->aos_slen = agentx_pdutoh32(header, buf);
+ rawlen -= 4;
+ buf += 4;
+ if (ostring->aos_slen > rawlen)
+ goto fail;
+ if ((ostring->aos_string = malloc(ostring->aos_slen + 1)) == NULL)
+ return -1;
+ memcpy(ostring->aos_string, buf, ostring->aos_slen);
+ ostring->aos_string[ostring->aos_slen] = '\0';
+
+ nread = 4 + ostring->aos_slen;
+ if (ostring->aos_slen % 4 != 0)
+ nread += 4 - (ostring->aos_slen % 4);
+
+ return nread;
+
+fail:
+ errno = EPROTO;
+ return -1;
+}
+
+static ssize_t
+agentx_pdutovarbind(struct agentx_pdu_header *header,
+ struct agentx_varbind *varbind, uint8_t *buf, size_t rawlen)
+{
+ ssize_t nread, rread = 4;
+
+ if (rawlen == 0)
+ return 0;
+
+ if (rawlen < 8)
+ goto fail;
+ varbind->avb_type = agentx_pdutoh16(header, buf);
+
+ buf += 4;
+ rawlen -= 4;
+ nread = agentx_pdutooid(header, &(varbind->avb_oid), buf, rawlen);
+ if (nread == -1)
+ return -1;
+ rread += nread;
+ buf += nread;
+ rawlen -= nread;
+
+ switch(varbind->avb_type) {
+ case AGENTX_DATA_TYPE_INTEGER:
+ case AGENTX_DATA_TYPE_COUNTER32:
+ case AGENTX_DATA_TYPE_GAUGE32:
+ case AGENTX_DATA_TYPE_TIMETICKS:
+ if (rawlen < 4)
+ goto fail;
+ varbind->avb_data.avb_uint32 = agentx_pdutoh32(header, buf);
+ return rread + 4;
+ case AGENTX_DATA_TYPE_COUNTER64:
+ if (rawlen < 8)
+ goto fail;
+ varbind->avb_data.avb_uint64 = agentx_pdutoh64(header, buf);
+ return rread + 8;
+ case AGENTX_DATA_TYPE_OCTETSTRING:
+ case AGENTX_DATA_TYPE_IPADDRESS:
+ case AGENTX_DATA_TYPE_OPAQUE:
+ nread = agentx_pdutoostring(header,
+ &(varbind->avb_data.avb_ostring), buf, rawlen);
+ if (nread == -1)
+ return -1;
+ return nread + rread;
+ case AGENTX_DATA_TYPE_OID:
+ nread = agentx_pdutooid(header, &(varbind->avb_data.avb_oid),
+ buf, rawlen);
+ if (nread == -1)
+ return -1;
+ return nread + rread;
+ case AGENTX_DATA_TYPE_NULL:
+ case AGENTX_DATA_TYPE_NOSUCHOBJECT:
+ case AGENTX_DATA_TYPE_NOSUCHINSTANCE:
+ case AGENTX_DATA_TYPE_ENDOFMIBVIEW:
+ return rread;
+ }
+
+fail:
+ errno = EPROTO;
+ return -1;
+}
diff --git a/lib/libagentx/agentx.h b/lib/libagentx/agentx.h
new file mode 100644
index 00000000000..2300c79117a
--- /dev/null
+++ b/lib/libagentx/agentx.h
@@ -0,0 +1,232 @@
+/*
+ * 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 <stdint.h>
+
+#define AGENTX_PDU_FLAG_INSTANCE_REGISTRATION (1 << 0)
+#define AGENTX_PDU_FLAG_NEW_INDEX (1 << 1)
+#define AGENTX_PDU_FLAG_ANY_INDEX (1 << 2)
+#define AGENTX_PDU_FLAG_NON_DEFAULT_CONTEXT (1 << 3)
+#define AGENTX_PDU_FLAG_NETWORK_BYTE_ORDER (1 << 4)
+
+#define AGENTX_PRIORITY_DEFAULT 127
+
+enum agentx_byte_order {
+ AGENTX_BYTE_ORDER_BE,
+ AGENTX_BYTE_ORDER_LE
+};
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define AGENTX_BYTE_ORDER_NATIVE AGENTX_BYTE_ORDER_BE
+#else
+#define AGENTX_BYTE_ORDER_NATIVE AGENTX_BYTE_ORDER_LE
+#endif
+
+enum agentx_pdu_type {
+ AGENTX_PDU_TYPE_OPEN = 1,
+ AGENTX_PDU_TYPE_CLOSE = 2,
+ AGENTX_PDU_TYPE_REGISTER = 3,
+ AGENTX_PDU_TYPE_UNREGISTER = 4,
+ AGENTX_PDU_TYPE_GET = 5,
+ AGENTX_PDU_TYPE_GETNEXT = 6,
+ AGENTX_PDU_TYPE_GETBULK = 7,
+ AGENTX_PDU_TYPE_TESTSET = 8,
+ AGENTX_PDU_TYPE_COMMITSET = 9,
+ AGENTX_PDU_TYPE_UNDOSET = 10,
+ AGENTX_PDU_TYPE_CLEANUPSET = 11,
+ AGENTX_PDU_TYPE_NOTIFY = 12,
+ AGENTX_PDU_TYPE_PING = 13,
+ AGENTX_PDU_TYPE_INDEXALLOCATE = 14,
+ AGENTX_PDU_TYPE_INDEXDEALLOCATE = 15,
+ AGENTX_PDU_TYPE_ADDAGENTCAPS = 16,
+ AGENTX_PDU_TYPE_REMOVEAGENTCAPS = 17,
+ AGENTX_PDU_TYPE_RESPONSE = 18
+};
+
+enum agentx_pdu_error {
+ AGENTX_PDU_ERROR_NOERROR = 0,
+ AGENTX_PDU_ERROR_GENERR = 5,
+ AGENTX_PDU_ERROR_NOACCESS = 6,
+ AGENTX_PDU_ERROR_WRONGTYPE = 7,
+ AGENTX_PDU_ERROR_WRONGLENGTH = 8,
+ AGENTX_PDU_ERROR_WRONGENCODING = 9,
+ AGENTX_PDU_ERROR_WRONGVALUE = 10,
+ AGENTX_PDU_ERROR_NOCREATION = 11,
+ AGENTX_PDU_ERROR_INCONSISTENTVALUE = 12,
+ AGENTX_PDU_ERROR_RESOURCEUNAVAILABLE = 13,
+ AGENTX_PDU_ERROR_COMMITFAILED = 14,
+ AGENTX_PDU_ERROR_UNDOFAILED = 15,
+ AGENTX_PDU_ERROR_NOTWRITABLE = 17,
+ AGENTX_PDU_ERROR_INCONSISTENTNAME = 18,
+ AGENTX_PDU_ERROR_OPENFAILED = 256,
+ AGENTX_PDU_ERROR_NOTOPEN = 257,
+ AGENTX_PDU_ERROR_INDEXWRONGTYPE = 258,
+ AGENTX_PDU_ERROR_INDEXALREADYALLOCATED = 259,
+ AGENTX_PDU_ERROR_INDEXNONEAVAILABLE = 260,
+ AGENTX_PDU_ERROR_INDEXNOTALLOCATED = 261,
+ AGENTX_PDU_ERROR_UNSUPPORTEDCONETXT = 262,
+ AGENTX_PDU_ERROR_DUPLICATEREGISTRATION = 263,
+ AGENTX_PDU_ERROR_UNKNOWNREGISTRATION = 264,
+ AGENTX_PDU_ERROR_UNKNOWNAGENTCAPS = 265,
+ AGENTX_PDU_ERROR_PARSEERROR = 266,
+ AGENTX_PDU_ERROR_REQUESTDENIED = 267,
+ AGENTX_PDU_ERROR_PROCESSINGERROR = 268
+};
+
+enum agentx_data_type {
+ AGENTX_DATA_TYPE_INTEGER = 2,
+ AGENTX_DATA_TYPE_OCTETSTRING = 4,
+ AGENTX_DATA_TYPE_NULL = 5,
+ AGENTX_DATA_TYPE_OID = 6,
+ AGENTX_DATA_TYPE_IPADDRESS = 64,
+ AGENTX_DATA_TYPE_COUNTER32 = 65,
+ AGENTX_DATA_TYPE_GAUGE32 = 66,
+ AGENTX_DATA_TYPE_TIMETICKS = 67,
+ AGENTX_DATA_TYPE_OPAQUE = 68,
+ AGENTX_DATA_TYPE_COUNTER64 = 70,
+ AGENTX_DATA_TYPE_NOSUCHOBJECT = 128,
+ AGENTX_DATA_TYPE_NOSUCHINSTANCE = 129,
+ AGENTX_DATA_TYPE_ENDOFMIBVIEW = 130
+};
+
+enum agentx_close_reason {
+ AGENTX_CLOSE_OTHER = 1,
+ AGENTX_CLOSEN_PARSEERROR = 2,
+ AGENTX_CLOSE_PROTOCOLERROR = 3,
+ AGENTX_CLOSE_TIMEOUTS = 4,
+ AGENTX_CLOSE_SHUTDOWN = 5,
+ AGENTX_CLOSE_BYMANAGER = 6
+};
+
+struct agentx {
+ int ax_fd;
+ enum agentx_byte_order ax_byteorder;
+ uint8_t *ax_rbuf;
+ size_t ax_rblen;
+ size_t ax_rbsize;
+ uint8_t *ax_wbuf;
+ size_t ax_wblen;
+ size_t ax_wbtlen;
+ size_t ax_wbsize;
+ uint32_t *ax_packetids;
+ size_t ax_packetidsize;
+};
+
+#ifndef AGENTX_PRIMITIVE
+#define AGENTX_PRIMITIVE
+
+#define AGENTX_OID_MAX_LEN 128
+
+struct agentx_oid {
+ uint8_t aoi_include;
+ uint32_t aoi_id[AGENTX_OID_MAX_LEN];
+ size_t aoi_idlen;
+};
+
+struct agentx_ostring {
+ unsigned char *aos_string;
+ uint32_t aos_slen;
+};
+#endif
+
+struct agentx_searchrange {
+ struct agentx_oid asr_start;
+ struct agentx_oid asr_stop;
+};
+
+struct agentx_pdu_header {
+ uint8_t aph_version;
+ uint8_t aph_type;
+ uint8_t aph_flags;
+ uint8_t aph_reserved;
+ uint32_t aph_sessionid;
+ uint32_t aph_transactionid;
+ uint32_t aph_packetid;
+ uint32_t aph_plength;
+};
+
+struct agentx_varbind {
+ enum agentx_data_type avb_type;
+ struct agentx_oid avb_oid;
+ union agentx_data {
+ uint32_t avb_uint32;
+ uint64_t avb_uint64;
+ struct agentx_ostring avb_ostring;
+ struct agentx_oid avb_oid;
+ } avb_data;
+};
+
+struct agentx_pdu {
+ struct agentx_pdu_header ap_header;
+ struct agentx_ostring ap_context;
+ union {
+ struct agentx_pdu_searchrangelist {
+ size_t ap_nsr;
+ struct agentx_searchrange *ap_sr;
+ } ap_srl;
+ struct agentx_pdu_getbulk {
+ uint16_t ap_nonrep;
+ uint16_t ap_maxrep;
+ struct agentx_pdu_searchrangelist ap_srl;
+ } ap_getbulk;
+ struct agentx_pdu_varbindlist {
+ struct agentx_varbind *ap_varbind;
+ size_t ap_nvarbind;
+ } ap_vbl;
+ struct agentx_pdu_response {
+ uint32_t ap_uptime;
+ enum agentx_pdu_error ap_error;
+ uint16_t ap_index;
+ struct agentx_varbind *ap_varbindlist;
+ size_t ap_nvarbind;
+ } ap_response;
+ void *ap_raw;
+ } ap_payload;
+};
+
+struct agentx *agentx_new(int);
+void agentx_free(struct agentx *);
+struct agentx_pdu *agentx_recv(struct agentx *);
+ssize_t agentx_send(struct agentx *);
+uint32_t agentx_open(struct agentx *, uint8_t, struct agentx_oid *,
+ struct agentx_ostring *);
+uint32_t agentx_close(struct agentx *, uint32_t, enum agentx_close_reason);
+uint32_t agentx_indexallocate(struct agentx *, uint8_t, uint32_t,
+ struct agentx_ostring *, struct agentx_varbind *, size_t);
+uint32_t agentx_indexdeallocate(struct agentx *, uint32_t,
+ struct agentx_ostring *, struct agentx_varbind *, size_t);
+uint32_t agentx_addagentcaps(struct agentx *, uint32_t, struct agentx_ostring *,
+ struct agentx_oid *, struct agentx_ostring *);
+uint32_t agentx_removeagentcaps(struct agentx *, uint32_t,
+ struct agentx_ostring *, struct agentx_oid *);
+uint32_t agentx_register(struct agentx *, uint8_t, uint32_t,
+ struct agentx_ostring *, uint8_t, uint8_t, uint8_t, struct agentx_oid *,
+ uint32_t);
+uint32_t agentx_unregister(struct agentx *, uint32_t, struct agentx_ostring *,
+ uint8_t, uint8_t, struct agentx_oid *, uint32_t);
+int agentx_response(struct agentx *, uint32_t, uint32_t, uint32_t,
+ struct agentx_ostring *, uint32_t, uint16_t, uint16_t,
+ struct agentx_varbind *, size_t);
+void agentx_pdu_free(struct agentx_pdu *);
+void agentx_varbind_free(struct agentx_varbind *);
+const char *agentx_error2string(enum agentx_pdu_error);
+const char *agentx_pdutype2string(enum agentx_pdu_type);
+const char *agentx_oid2string(struct agentx_oid *);
+const char *agentx_oidrange2string(struct agentx_oid *, uint8_t, uint32_t);
+const char *agentx_varbind2string(struct agentx_varbind *);
+const char *agentx_closereason2string(enum agentx_close_reason);
+int agentx_oid_cmp(struct agentx_oid *, struct agentx_oid *);
+int agentx_oid_add(struct agentx_oid *, uint32_t);
diff --git a/lib/libagentx/shlib_version b/lib/libagentx/shlib_version
new file mode 100644
index 00000000000..97c9f92d6b8
--- /dev/null
+++ b/lib/libagentx/shlib_version
@@ -0,0 +1,2 @@
+major=0
+minor=0
diff --git a/lib/libagentx/subagentx.3 b/lib/libagentx/subagentx.3
new file mode 100644
index 00000000000..ea21dcf1e55
--- /dev/null
+++ b/lib/libagentx/subagentx.3
@@ -0,0 +1,650 @@
+.\" $OpenBSD: subagentx.3,v 1.1 2020/09/16 10:48:52 martijn Exp $
+.\"
+.\" Copyright (c) 2020 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.
+.\"
+.Dd $Mdocdate: September 16 2020 $
+.Dt SUBAGENTX 3
+.Os
+.Sh NAME
+.Nm subagentx_log_fatal ,
+.Nm subagentx_log_warn ,
+.Nm subagentx_log_info ,
+.Nm subagentx_log_debug ,
+.Nm subagentx ,
+.Nm subagentx_connect ,
+.Nm subagentx_read ,
+.Nm subagentx_write ,
+.Nm subagentx_wantwrite ,
+.Nm subagentx_free ,
+.Nm subagentx_session ,
+.Nm subagentx_session_free ,
+.Nm subagentx_context ,
+.Nm subagentx_context_object_find ,
+.Nm subagentx_context_object_nfind ,
+.Nm subagentx_context_uptime ,
+.Nm subagentx_context_free ,
+.Nm subagentx_region ,
+.Nm subagentx_region_free ,
+.Nm subagentx_agentcaps ,
+.Nm subagentx_agentcaps_free ,
+.Nm subagentx_index_integer_new ,
+.Nm subagentx_index_integer_any ,
+.Nm subagentx_index_integer_value ,
+.Nm subagentx_index_integer_dynamic ,
+.Nm subagentx_index_string_dynamic ,
+.Nm subagentx_index_nstring_dynamic ,
+.Nm subagentx_index_oid_dynamic ,
+.Nm subagentx_index_noid_dynamic ,
+.Nm subagentx_index_ipaddress_dynamic ,
+.Nm subagentx_index_free ,
+.Nm subagentx_object ,
+.Nm subagentx_object_free ,
+.Nm subagentx_varbind_integer ,
+.Nm subagentx_varbind_string ,
+.Nm subagentx_varbind_nstring ,
+.Nm subagentx_varbind_printf ,
+.Nm subagentx_varbind_null ,
+.Nm subagentx_varbind_oid ,
+.Nm subagentx_varbind_object ,
+.Nm subagentx_varbind_index ,
+.Nm subagentx_varbind_ipaddress ,
+.Nm subagentx_varbind_counter32 ,
+.Nm subagentx_varbind_gauge32 ,
+.Nm subagentx_varbind_timeticks ,
+.Nm subagentx_varbind_opaque ,
+.Nm subagentx_varbind_counter64 ,
+.Nm subagentx_varbind_notfound ,
+.Nm subagentx_varbind_error ,
+.Nm subagentx_varbind_request ,
+.Nm subagentx_varbind_get_index_integer ,
+.Nm subagentx_varbind_get_index_string ,
+.Nm subagentx_varbind_get_index_oid ,
+.Nm subagentx_varbind_get_index_ipaddress ,
+.Nm subagentx_varbind_set_index_integer ,
+.Nm subagentx_varbind_set_index_string ,
+.Nm subagentx_varbind_set_index_nstring ,
+.Nm subagentx_varbind_set_index_oid ,
+.Nm subagentx_varbind_set_index_object ,
+.Nm subagentx_varbind_set_index_ipaddress
+.Nd manage an interface to an agentx master
+.Sh SYNOPSIS
+.In subagentx.h
+.Ft extern void
+.Fn (*subagentx_log_fatal) "const char *fmt" ...
+.Ft extern void
+.Fn (*subagentx_log_warn) "const char *fmt" ...
+.Ft extern void
+.Fn (*subagentx_log_info) "const char *fmt" ...
+.Ft extern void
+.Fn (*subagentx_log_debug) "const char *fmt" ...
+.Ft struct subagentx *
+.Fn subagentx "void (*nofd)(struct subagentx *, void *, int)" "void *cookie"
+.Ft void
+.Fn subagentx_connect "struct subagentx *sa" "int fd"
+.Ft void
+.Fn subagentx_read "struct subagentx *sa"
+.Ft void
+.Fn subagentx_write "struct subagentx *sa"
+.Ft extern void
+.Fn (*subagentx_wantwrite) "struct subagentx *sa" "int fd"
+.Ft void
+.Fn subagentx_free "struct subagentx *sa"
+.Ft struct subagentx_session *
+.Fo subagentx_session
+.Fa "struct subagentx *sa" "uint8_t timeout" "uint32_t oid[]" "size_t oidlen"
+.Fa "const char *descr"
+.Fc
+.Ft void
+.Fn subagentx_session_free "struct subagentx_session *sas"
+.Ft struct subagentx_context *
+.Fn subagentx_context "struct subagentx_session *sas" "const char *name"
+.Ft uint32_t
+.Ft struct subagentx_object *
+.Fo subagentx_context_object_find
+.Fa "struct subagentx_context *sac" "const uint32_t oid[]" "size_t oidlen"
+.Fa "int active" "int instance"
+.Fc
+.Ft struct subagentx_object *
+.Fo subagentx_context_object_nfind
+.Fa "struct subagentx_context *" "const uint32_t oid[]" "size_t oidlen"
+.Fa "int active" "int inclusive"
+.Fc
+.Ft uint32_t
+.Fn subagentx_context_uptime "struct subagentx_context *sac"
+.Ft void
+.Fn subagentx_context_free "struct subagentx_context *sac"
+.Ft struct subagentx_agentcaps *
+.Fo subagentx_agentcaps
+.Fa "struct subagentx_context *sac" "uint32_t oid[]" "size_t oidlen"
+.Fa "const char *descr"
+.Fc
+.Ft void
+.Fn subagentx_agentcaps_free "struct subagentx_agentcaps *saa"
+.Ft struct subagentx_region *
+.Fo subagentx_region
+.Fa "struct subagentx_context *sac" "uint8_t timeout" "uint32_t oid[]"
+.Fa "size_t oidlen"
+.Fc
+.Ft void
+.Fn subagentx_region_free "struct subagentx_region *sar"
+.Ft struct subagentx_index *
+.Fo subagentx_index_integer_new
+.Fa "struct subagentx_region *sar" "uint32_t oid[]" "size_t oidlen"
+.Fc
+.Ft struct subagentx_index *
+.Fo subagentx_index_integer_any
+.Fa "struct subagentx_region *sar" "uint32_t oid[]" "size_t oidlen"
+.Fc
+.Ft struct subagentx_index *
+.Fo subagentx_index_integer_value
+.Fa "struct subagentx_region *sar" "uint32_t oid[]" "size_t oidlen"
+.Fa "uint32_t value"
+.Fc
+.Ft struct subagentx_index *
+.Fo subagentx_index_integer_dynamic
+.Fa "struct subagentx_region *sar" "uint32_t oid[] "size_t oidlen"
+.Fc
+.Ft struct subagentx_index *
+.Fo subagentx_index_string_dynamic
+.Fa "struct subagentx_region *sar" "uint32_t oid[]" "size_t oidlen"
+.Fc
+.Ft struct subagentx_index *
+.Fo subagentx_index_nstring_dynamic
+.Fa "struct subagentx_region *sar" "uint32_t oid[]" "size_t oidlen"
+.Fa "size_t slen"
+.Fc
+.Ft struct subagentx_index *
+.Fo subagentx_index_oid_dynamic
+.Fa "struct subagentx_region *sar "uint32_t oid[]" "size_t oidlen"
+.Fc
+.Ft struct subagentx_index *
+.Fo subagentx_index_noid_dynamic
+.Fa "struct subagentx_region *sar" "uint32_t oid[]" "size_t oidlen"
+.Fa "size_t vlen"
+.Fc
+.Ft struct subagentx_index *
+.Fo subagentx_index_ipaddress_dynamic
+.Fa "struct subagentx_region *sar" "uint32_t oid[]" "size_t oidlen"
+.Fc
+.Ft struct subagentx_index *
+.Fo subagentx_index_dynamic
+.Fa "struct subagentx_region *sar" "uint32_t oid[]" "size_t oidlen"
+.Fc
+.Ft void
+.Fn subagentx_index_free "struct subagentx_index *sai"
+.Ft struct subagentx_object *
+.Fo subagentx_object
+.Fa "struct subagentx_region *sar" "uint32_t oid[]" "size_t oidlen"
+.Fa "struct subagentx_index *index[]" "size_t indexlen" "int implied"
+.Fa "void (*getcb)(struct subagentx_varbind *)"
+.Fc
+.Ft void
+.Fn subagentx_object_free "struct subagentx_object *sao"
+.Ft void
+.Fn subagentx_varbind_integer "struct subagentx_varbind *sav" "uint32_t value"
+.Ft void
+.Fn subagentx_varbind_string "struct subagentx_varbind *sav" "const char *value"
+.Ft void
+.Fo subagentx_varbind_nstring
+.Fa "struct subagentx_varbind *sav" "const char *value" "size_t slen"
+.Fc
+.Ft void
+.Fo subagentx_varbind_printf
+.Fa "struct subagentx_varbind *sav" "const char *fmt" ...
+.Fc
+.Ft void
+.Fn subagentx_varbind_null "struct subagentx_varbind *sav"
+.Ft void
+.Fo subagentx_varbind_oid
+.Fa "struct subagentx_varbind *sav" "const uint32_t oid[]" "size_t oidlen"
+.Fc
+.Ft void
+.Fo subagentx_varbind_object
+.Fa "struct subagentx_varbind *sav" "struct subagentx_object *sao"
+.Fc
+.Ft void
+.Fo subagentx_varbind_index
+.Fa "struct subagentx_varbind *sav" "struct subagentx_index *sai"
+.Fc
+.Ft void
+.Fo subagentx_varbind_ipaddress
+.Fa "struct subagentx_varbind *sav" "const struct in_addr *addr"
+.Fc
+.Ft void
+.Fn subagentx_varbind_counter32 "struct subagentx_varbind *sav" "uint32_t value"
+.Ft void
+.Fn subagentx_varbind_gauge32 "struct subagentx_varbind *sav" "uint32_t value"
+.Ft void
+.Fo subagentx_varbind_timeticks
+.Fa "struct subagentx_varbind *sav" "uint32_t value"
+.Fc
+.Ft void
+.Fo subagentx_varbind_opaque
+.Fa "struct subagentx_varbind *sav" "const char *value" "size_t slen"
+.Fc
+.Ft void
+.Fn subagentx_varbind_counter64 "struct subagentx_varbind *sav" "uint64_t value"
+.Ft void
+.Fn subagentx_varbind_notfound "struct subagentx_varbind *sav"
+.Ft void
+.Fn subagentx_varbind_error "struct subagentx_varbind *sav"
+.Ft enum subagentx_request_type
+.Fn subagentx_varbind_request "struct subagentx_varbind *sav"
+.Ft uint32_t
+.Fo subagentx_varbind_get_index_integer
+.Fa "struct subagentx_varbind *sav" "struct subagentx_index *sai"
+.Fc
+.Ft const unsigned char *
+.Fo subagentx_varbind_get_index_string
+.Fa "struct subagentx_varbind *sav" "struct subagentx_index *sai" "size_t *slen"
+.Fa "int *implied"
+.Fc
+.Ft const uint32_t *
+.Fo subagentx_varbind_get_index_oid
+.Fa "struct subagentx_varbind *sav" "struct subagentx_index *sai"
+.Fa "size_t *oidlen" "int *implied"
+.Fc
+.Ft const struct in_addr *
+.Fo subagentx_varbind_get_index_ipaddress
+.Fa "struct subagentx_varbind *sav" "struct subagentx_index *sai"
+.Fc
+.Ft void
+.Fo subagentx_varbind_set_index_integer
+.Fa "struct subagentx_varbind *sav" "struct subagentx_index *sai"
+.Fa "uint32_t value"
+.Fc
+.Ft void
+.Fo subagentx_varbind_set_index_string
+.Fa "struct subagentx_varbind *sav" "struct subagentx_index *sai"
+.Fa "const unsigned char *value"
+.Fc
+.Ft void
+.Fo subagentx_varbind_set_index_nstring
+.Fa "struct subagentx_varbind *sav" "struct subagentx_index *sai"
+.Fa "const unsigned char *value" "size_t slen"
+.Fc
+.Ft void
+.Fo subagentx_varbind_set_index_oid
+.Fa "struct subagentx_varbind *sav" "struct subagentx_index *sai"
+.Fa "const uint32_t *oid" "size_t oidlen"
+.Fc
+.Ft void
+.Fo subagentx_varbind_set_index_object
+.Fa "struct subagentx_varbind *sav" "struct subagentx_index *sai"
+.Fa "struct subagentx_object *sao"
+.Fc
+.Ft void
+.Fo subagentx_varbind_set_index_ipaddress
+.Fa "struct subagentx_varbind *sav" "struct subagentx_index *sai"
+.Fa "const struct in_addr *addr"
+.Fc
+.Bd -literal
+enum subagentx_request_type {
+ SUBAGENTX_REQUEST_TYPE_GET,
+ SUBAGENTX_REQUEST_TYPE_GETNEXT,
+ SUBAGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE
+};
+.Ed
+.Fd #define SUBAGENTX_AGENTX_MASTER "/var/agentx/master"
+.Fd #define SUBAGENTX_OID_MAX_LEN 128
+.Fd #define SUBAGENTX_OID_INDEX_MAX_LEN 10
+.Fd #define SUBAGENTX_OID(...)
+.Fd #define SUBAGENTX_MIB2 1, 3, 6, 1, 2, 1
+.Fd #define SUBAGENTX_ENTERPRISES 1, 3, 6, 1, 4, 1
+.Sh DESCRIPTION
+The
+.Nm subagentx
+functions allow an application to describe their MIB layout and provide an
+.Fa fd
+based interface to control the internal agentx state.
+.Nm subagentx
+is not thread safe.
+.Ss DESCRIBING THE MIB
+.Nm subagentx
+is a framework to abstract away the agentx protocol from the application.
+For the framework to report information to the administrator, the
+.Fn subagentx_log_fatal ,
+.Fn subagentx_log_warn ,
+.Fn subagentx_log_info
+and
+.Fn subagentx_log_debug
+functions must be set.
+.Pp
+When
+.Fa sa
+is created by
+.Fn subagentx
+or when
+.Fa sa
+detects that there is no connection to the agentx master it calls out to
+.Fa nofd
+with itself,
+.Fa cookie
+and an integer
+.Fa close
+as arguments.
+If
+.Fa close
+is not set
+.Fn nofd
+is expected to set up a new
+.Fa fd
+to the agentx master.
+This one can usually be found at
+.Dv SUBAGENTX_AGENTX_MASTER .
+This
+.Fa fd
+can be returned to
+.Fa sa
+at any moment via
+.Nm subagentx_connect ,
+but must always be done as a result of a call to
+.Fn nofd .
+Once
+.Nm subagentx_connect
+has been called the application is responsible for retrieving data when available
+on
+.Fa fd
+by calling
+.Fn subagentx_read .
+If nonblocking writes are desirable the
+.Nm subagentx_wantwrite
+pointer can be set to an application function and will be called as soon as
+there's data available to be written out.
+Once
+.Fa fd
+is ready for write the function
+.Nm subagentx_write
+should be called.
+.Pp
+.Fa sa
+can be freed via
+.Fn subagentx_free .
+It will close all active sessions and free all derived objects.
+Once freed no new objects can be derived from the freed objects.
+Once all sessions are closed it will call out to
+.Fn nofd
+with
+.Fa close
+set, indicating that the application can clean up any context related to
+.Fa sa .
+.Pp
+On top of the
+.Fa sa
+connection a
+.Nm subagentx_session
+must be set up.
+Normally there's only a single session per
+.Fa sa .
+The
+.Fa timeout
+argument specifies the maximum time in seconds the master should wait for a
+reply before determining we're gone.
+If set to 0 the agentx master determines the timeout.
+The
+.Fa oid
+and
+.Fa oidlen
+combination identifies the subagent and will be visible through the
+agentxSessionObjectID object on the agentx master.
+The
+.Fa descr
+is a short displaystring description of the agent and will be visiable through
+the agentxSessionDescr object on the agentx master.
+.Pp
+The
+.Nm subagentx_context
+is the SNMPv3 context in which the objects operate and is built on top of
+subagentx_session
+.Fa sas .
+If the default context is requested
+.Fa name
+must be NULL.
+.Pp
+.Fn subagentx_agentcaps
+registers an entry in the agentx master's sysORTable.
+The
+.Fa oid ,
+.Fa oidlen
+combination should point to an AGENT-CAPABILITIES object which describes the
+capabilities of the subagent.
+.Fa descr
+should be a textual description of the capabilities.
+If no AGENT-CAPABILITIES object is defined this function can be omitted.
+.Pp
+A
+.Nm subagentx_region
+indicates a region inside the object-tree for which get- and set-requests will
+be queried.
+If the OID has already been claimed by another subagent it will try to claim it
+on a lower priority.
+The
+.Fa timeout
+parameter overrules its
+.Nm subagentx_session
+counterpart.
+.Pp
+For objects in a table one or more
+.Ft subagentx_index
+elements must be supplied.
+.Nm subagentx_index_integer_new ,
+.Nm subagentx_index_integer_any
+and
+.Nm subagentx_index_integer_value
+register an integer index at the agentx master.
+Of these
+.Nm subagentx_index_integer_new
+registers a new, previously unused, index;
+.Nm subagentx_index_integer_any
+registers the first available index;
+and
+.Nm subagentx_index_integer_value
+tries to register a specific value.
+If the registration of an index fails an error will be logged and all objects
+using it will remain disabled.
+The OID where the index should be registered is documented by the MIB.
+These registered indices are usually used for tables where multiple subagents
+are registered.
+.Pp
+For dynamic indices the subagentx_index_*_dynamic functions can be used, based
+on the data type of the object.
+The data type should match the data type in the MIB at the
+.Fa oid
+object.
+Indices of data type string or oid with a fixed length should be created via
+.Fn subagentx_index_nstring_dynamic
+and
+.Fn subagentx_index_noid_dynamic
+respectively.
+.Pp
+.Nm subagentx_object
+is an object as described in the MIB.
+For scalar objects
+.Pq without indices
+the final zero must be omitted.
+For table entries a list of 1 or more indices must be added via
+.Fa index
+and
+.Fa indexlen .
+The list of indices must match the INDEX list on the ENTRY object in the MIB.
+The total length of the OID, including indices, can't be more than
+.Dv SUBAGENTX_OID_MAX_LEN
+and indexlen can't be more than
+.Dv SUBAGENTX_OID_INDEX_MAX_LEN .
+If
+.Fa implied
+is set the final index must be of type OID or string and will omit the leading
+length indicator.
+This value must only be set if specified in the MIB.
+.Fn getcb
+will be called for each varbind in a GET, GETNEXT or GETBULK request that
+matches the object.
+.Ss HANDLING GET REQUESTS
+A call to
+.Fn getcb
+must eventually result in a call to one of the following functions:
+.Bl -tag -width subagentx_varbind_counter32()
+.It Fn subagentx_varbind_integer
+Set the return value to an uint32_t value.
+.It Fn subagentx_varbind_string
+A C string wrapper around
+.Fn subagentx_varbind_nstring .
+.It Fn subagentx_varbind_nstring
+Set the return value to an octetstring.
+.It Fn subagentx_varbind_printf
+A printf wrapper around
+.Fn subagentx_varbind_nstring .
+.It Fn subagentx_varbind_null
+Set the return value to null.
+.It Fn subagentx_varbind_oid
+Set the return value to an OID value.
+.It Fn subagentx_varbind_object
+An subagentx_object wrapper around
+.Fn subagentx_varbind_oid .
+.It Fn subagentx_varbind_index
+An subagentx_index wrapper around
+.Fn subagentx_varbind_oid .
+.It Fn subagentx_varbind_ipaddress
+Set the return value to ipaddress.
+.It Fn subagentx_varbind_counter32
+Set the return value to an uint32_t of type counter32.
+.It Fn subagentx_varbind_gauge32
+Set the return value to an uint32_t of type gauge32.
+.It Fn subagentx_varbind_timeticks
+Set the return value to an uint32_t of type timeticks.
+.It Fn subagentx_varbind_opaque
+Set the return value to an opaque value.
+.It Fn subagentx_varbind_counter64
+Set the return value to an uint64_t of type counter64.
+.It Fn subagentx_varbind_notfound
+When the request is of type GET return an nosuchinstance error.
+When the request is of type GETNEXT or GETBULK return an endofmibview error.
+On endofmibview the next object is queried.
+This function can only be called on objects that contain one or more *_dynamic
+indices.
+.It Fn subagentx_varbind_error
+Returns a GENERR error to the client.
+.El
+.Pp
+For objects containing *_dynamic indices the following support functions are to
+be used:
+.Bl -tag -width subagentx_varbind_get_index_ipaddress()
+.It Fn subagentx_varbind_request
+Returns whether the request is of type GET, GETNEXT or GETNEXTINCLUSIVE.
+.It Fn subagentx_varbind_get_index_integer
+Retrieve a single uint32_t index value.
+.It Fn subagentx_varbind_get_index_string
+Retrieve an octetstring index value.
+.Fa slen
+is the length of the string and
+.Fa implied
+indicates if the next value for this index should be length sorted before
+alphabetically sorted.
+.It Fn subagentx_varbind_get_index_oid
+Retrieve an oid index value.
+.Fa oidlen
+is the length of the oid and
+.Fa implied
+indicates if the next value for this index should be length sorted before
+alphabetically sorted.
+.It Fn subagentx_varbind_get_index_ipaddress
+Retrieve an ipaddress index value.
+.It Fn subagentx_varbind_set_index_integer
+Sets a single uint32_t index value.
+.It Fn subagentx_varbind_set_index_string
+A C string wrapper around
+.Fn subagentx_varbind_set_index_nstring .
+.It Fn subagentx_varbind_set_index_nstring
+Set an octetstring index value.
+.It Fn subagentx_varbind_set_index_oid
+Set an oid index value.
+.It Fn subagentx_varbind_set_index_object
+A subagentx_object wrapper around
+.Fn subagentx_varbind_set_index_oid .
+.It Fn subagentx_varbind_set_index_ipaddress
+Set an ipaddress index value.
+.El
+.Pp
+For these functions
+.Fa sai
+must be part of the object the request is performed on.
+The function type must also match the data type of
+.Fa sai .
+.Pp
+Other functions that can retrieve information from the agentx context are:
+.Bl -tag -width subagentx_context_object_nfind()
+.It Fn subagentx_context_object_find
+Find a subagentx_object created inside subagentx_context
+.Fa sac
+based on
+.Fa oid
+and
+.Fa oidlen .
+If
+.Fa active
+is set the object must be reachable from the agentx master, else NULL is
+returned.
+If
+.Fa oid
+can be an instance, find its parent object.
+.It Fn subagentx_context_object_nfind
+Find the next subagentx_object created inside subagentx_context
+.Fa sac
+based on
+.Fa oid
+and
+.Fa oidlen .
+If
+.Fa active
+is set the object must be reachable from the agentx master, else NULL is
+returned.
+If
+.Fa inclusive
+is set the object returned may also exactly match
+.Fa oid .
+.It Fn subagentx_context_uptime
+Returns the sysuptime in seconds for
+.Fa sac
+in timeticks.
+.El
+.Sh SEE ALSO
+.Xr snmp 1 ,
+.Xr snmpd 8
+.Sh STANDARDS
+.Rs
+.%A M. Daniele
+.%A B. Wijnen
+.%A M. Ellison, Ed.
+.%A D. Francisco. Ed.
+.%D January 2000
+.%R RFC 2741
+.%T Agent Extensibility (AgentX) Protocol Version 1
+.Re
+.Pp
+.Rs
+.%A L. Heintz
+.%A S. Gudur
+.%A M. Ellison, Ed.
+.%D January 2000
+.%R RFC 2742
+.%T Definitions of Managed Objects for Extensible SNMP Agents
+.Re
+.Sh HISTORY
+The
+.Nm subagentx
+API first appeared in
+.Ox 6.8 .
+.Sh AUTHORS
+.An Martijn van Duren Aq Mt martijn@openbsd.org
diff --git a/lib/libagentx/subagentx.c b/lib/libagentx/subagentx.c
new file mode 100644
index 00000000000..6c392a1cb63
--- /dev/null
+++ b/lib/libagentx/subagentx.c
@@ -0,0 +1,3994 @@
+/*
+ * 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 "subagentx_internal.h"
+#include "subagentx.h"
+
+enum subagentx_index_type {
+ SAI_TYPE_NEW,
+ SAI_TYPE_ANY,
+ SAI_TYPE_VALUE,
+ SAI_TYPE_DYNAMIC
+};
+
+#define SUBAGENTX_CONTEXT_CTX(sac) (sac->sac_name_default ? NULL : \
+ &(sac->sac_name))
+
+struct subagentx_agentcaps {
+ struct subagentx_context *saa_sac;
+ struct agentx_oid saa_oid;
+ struct agentx_ostring saa_descr;
+ enum subagentx_cstate saa_cstate;
+ enum subagentx_dstate saa_dstate;
+ TAILQ_ENTRY(subagentx_agentcaps) saa_sac_agentcaps;
+};
+
+struct subagentx_region {
+ struct subagentx_context *sar_sac;
+ struct agentx_oid sar_oid;
+ uint8_t sar_timeout;
+ uint8_t sar_priority;
+ enum subagentx_cstate sar_cstate;
+ enum subagentx_dstate sar_dstate;
+ TAILQ_HEAD(, subagentx_index) sar_indices;
+ TAILQ_HEAD(, subagentx_object) sar_objects;
+ TAILQ_ENTRY(subagentx_region) sar_sac_regions;
+};
+
+struct subagentx_index {
+ struct subagentx_region *sai_sar;
+ enum subagentx_index_type sai_type;
+ struct agentx_varbind sai_vb;
+ struct subagentx_object **sai_object;
+ size_t sai_objectlen;
+ size_t sai_objectsize;
+ enum subagentx_cstate sai_cstate;
+ enum subagentx_dstate sai_dstate;
+ TAILQ_ENTRY(subagentx_index) sai_sar_indices;
+};
+
+struct subagentx_object {
+ struct subagentx_region *sao_sar;
+ struct agentx_oid sao_oid;
+ struct subagentx_index *sao_index[SUBAGENTX_OID_INDEX_MAX_LEN];
+ size_t sao_indexlen;
+ int sao_implied;
+ uint8_t sao_timeout;
+ /* Prevent freeing object while in use by get and set requesets */
+ uint32_t sao_lock;
+ void (*sao_get)(struct subagentx_varbind *);
+ enum subagentx_cstate sao_cstate;
+ enum subagentx_dstate sao_dstate;
+ RB_ENTRY(subagentx_object) sao_sac_objects;
+ TAILQ_ENTRY(subagentx_object) sao_sar_objects;
+};
+
+struct subagentx_varbind {
+ struct subagentx_get *sav_sag;
+ struct subagentx_object *sav_sao;
+ struct subagentx_varbind_index {
+ struct subagentx_index *sav_sai;
+ union agentx_data sav_idata;
+ uint8_t sav_idatacomplete;
+ } sav_index[SUBAGENTX_OID_INDEX_MAX_LEN];
+ size_t sav_indexlen;
+ int sav_initialized;
+ int sav_include;
+ struct agentx_varbind sav_vb;
+ struct agentx_oid sav_start;
+ struct agentx_oid sav_end;
+ enum agentx_pdu_error sav_error;
+};
+
+#define SUBAGENTX_GET_CTX(sag) (sag->sag_context_default ? NULL : \
+ &(sag->sag_context))
+struct subagentx_request {
+ uint32_t sar_packetid;
+ int (*sar_cb)(struct agentx_pdu *, void *);
+ void *sar_cookie;
+ RB_ENTRY(subagentx_request) sar_sa_requests;
+};
+
+static void subagentx_start(struct subagentx *);
+static void subagentx_finalize(struct subagentx *, int);
+static void subagentx_wantwritenow(struct subagentx *, int);
+void (*subagentx_wantwrite)(struct subagentx *, int) =
+ subagentx_wantwritenow;
+static void subagentx_reset(struct subagentx *);
+static void subagentx_free_finalize(struct subagentx *);
+static int subagentx_session_start(struct subagentx_session *);
+static int subagentx_session_finalize(struct agentx_pdu *, void *);
+static int subagentx_session_close(struct subagentx_session *,
+ enum agentx_close_reason);
+static int subagentx_session_close_finalize(struct agentx_pdu *, void *);
+static void subagentx_session_free_finalize(struct subagentx_session *);
+static void subagentx_session_reset(struct subagentx_session *);
+static void subagentx_context_start(struct subagentx_context *);
+static void subagentx_context_free_finalize(struct subagentx_context *);
+static void subagentx_context_reset(struct subagentx_context *);
+static int subagentx_agentcaps_start(struct subagentx_agentcaps *);
+static int subagentx_agentcaps_finalize(struct agentx_pdu *, void *);
+static int subagentx_agentcaps_close(struct subagentx_agentcaps *);
+static int subagentx_agentcaps_close_finalize(struct agentx_pdu *, void *);
+static void subagentx_agentcaps_free_finalize(struct subagentx_agentcaps *);
+static void subagentx_agentcaps_reset(struct subagentx_agentcaps *);
+static int subagentx_region_start(struct subagentx_region *);
+static int subagentx_region_finalize(struct agentx_pdu *, void *);
+static int subagentx_region_close(struct subagentx_region *);
+static int subagentx_region_close_finalize(struct agentx_pdu *, void *);
+static void subagentx_region_free_finalize(struct subagentx_region *);
+static void subagentx_region_reset(struct subagentx_region *);
+static struct subagentx_index *subagentx_index(struct subagentx_region *,
+ struct agentx_varbind *, enum subagentx_index_type);
+static int subagentx_index_start(struct subagentx_index *);
+static int subagentx_index_finalize(struct agentx_pdu *, void *);
+static void subagentx_index_free_finalize(struct subagentx_index *);
+static void subagentx_index_reset(struct subagentx_index *);
+static int subagentx_index_close(struct subagentx_index *);
+static int subagentx_index_close_finalize(struct agentx_pdu *, void *);
+static int subagentx_object_start(struct subagentx_object *);
+static int subagentx_object_finalize(struct agentx_pdu *, void *);
+static int subagentx_object_lock(struct subagentx_object *);
+static void subagentx_object_unlock(struct subagentx_object *);
+static int subagentx_object_close(struct subagentx_object *);
+static int subagentx_object_close_finalize(struct agentx_pdu *, void *);
+static void subagentx_object_free_finalize(struct subagentx_object *);
+static void subagentx_object_reset(struct subagentx_object *);
+static int subagentx_object_cmp(struct subagentx_object *,
+ struct subagentx_object *);
+static void subagentx_get_start(struct subagentx_context *,
+ struct agentx_pdu *);
+static void subagentx_get_finalize(struct subagentx_get *);
+static void subagentx_get_free(struct subagentx_get *);
+static void subagentx_varbind_start(struct subagentx_varbind *);
+static void subagentx_varbind_finalize(struct subagentx_varbind *);
+static void subagentx_varbind_nosuchobject(struct subagentx_varbind *);
+static void subagentx_varbind_nosuchinstance(struct subagentx_varbind *);
+static void subagentx_varbind_endofmibview(struct subagentx_varbind *);
+static void subagentx_varbind_error_type(struct subagentx_varbind *,
+ enum agentx_pdu_error, int);
+static int subagentx_request(struct subagentx *, uint32_t,
+ int (*)(struct agentx_pdu *, void *), void *);
+static int subagentx_request_cmp(struct subagentx_request *,
+ struct subagentx_request *);
+static int subagentx_strcat(char **, const char *);
+
+RB_PROTOTYPE_STATIC(sa_requests, subagentx_request, sar_sa_requests,
+ subagentx_request_cmp)
+RB_PROTOTYPE_STATIC(sac_objects, subagentx_object, sao_sac_objects,
+ subagentx_object_cmp)
+
+struct subagentx *
+subagentx(void (*nofd)(struct subagentx *, void *, int), void *cookie)
+{
+ struct subagentx *sa;
+
+ if ((sa = calloc(1, sizeof(*sa))) == NULL)
+ return NULL;
+
+ sa->sa_nofd = nofd;
+ sa->sa_cookie = cookie;
+ sa->sa_fd = -1;
+ sa->sa_cstate = SA_CSTATE_CLOSE;
+ sa->sa_dstate = SA_DSTATE_OPEN;
+ TAILQ_INIT(&(sa->sa_sessions));
+ TAILQ_INIT(&(sa->sa_getreqs));
+ RB_INIT(&(sa->sa_requests));
+
+ subagentx_start(sa);
+
+ return sa;
+}
+
+/*
+ * subagentx_finalize is not a suitable name for a public API,
+ * but use it internally for consistency
+ */
+void
+subagentx_connect(struct subagentx *sa, int fd)
+{
+ subagentx_finalize(sa, fd);
+}
+
+static void
+subagentx_start(struct subagentx *sa)
+{
+#ifdef AGENTX_DEBUG
+ if (sa->sa_cstate != SA_CSTATE_CLOSE ||
+ sa->sa_dstate != SA_DSTATE_OPEN)
+ subagentx_log_sa_fatalx(sa, "%s: unexpected connect", __func__);
+#endif
+ sa->sa_cstate = SA_CSTATE_WAITOPEN;
+ sa->sa_nofd(sa, sa->sa_cookie, 0);
+}
+
+static void
+subagentx_finalize(struct subagentx *sa, int fd)
+{
+ struct subagentx_session *sas;
+
+ if (sa->sa_cstate != SA_CSTATE_WAITOPEN) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sa_fatalx(sa, "%s: subagentx unexpected connect",
+ __func__);
+#else
+ subagentx_log_sa_warnx(sa,
+ "%s: subagentx unexpected connect: ignoring", __func__);
+ return;
+#endif
+ }
+ if ((sa->sa_ax = agentx_new(fd)) == NULL) {
+ subagentx_log_sa_warn(sa, "failed to initialize");
+ close(fd);
+ subagentx_reset(sa);
+ return;
+ }
+
+ subagentx_log_sa_info(sa, "new connection: %d", fd);
+
+ sa->sa_fd = fd;
+ sa->sa_cstate = SA_CSTATE_OPEN;
+
+ TAILQ_FOREACH(sas, &(sa->sa_sessions), sas_sa_sessions) {
+ if (subagentx_session_start(sas) == -1)
+ break;
+ }
+}
+
+static void
+subagentx_wantwritenow(struct subagentx *sa, int fd)
+{
+ subagentx_write(sa);
+}
+
+static void
+subagentx_reset(struct subagentx *sa)
+{
+ struct subagentx_session *sas, *tsas;
+ struct subagentx_request *sar;
+ struct subagentx_get *sag;
+
+ agentx_free(sa->sa_ax);
+ sa->sa_ax = NULL;
+ sa->sa_fd = -1;
+
+ sa->sa_cstate = SA_CSTATE_CLOSE;
+
+ while ((sar = RB_MIN(sa_requests, &(sa->sa_requests))) != NULL) {
+ RB_REMOVE(sa_requests, &(sa->sa_requests), sar);
+ free(sar);
+ }
+ TAILQ_FOREACH_SAFE(sas, &(sa->sa_sessions), sas_sa_sessions, tsas)
+ subagentx_session_reset(sas);
+ while (!TAILQ_EMPTY(&(sa->sa_getreqs))) {
+ sag = TAILQ_FIRST(&(sa->sa_getreqs));
+ sag->sag_sac = NULL;
+ TAILQ_REMOVE(&(sa->sa_getreqs), sag, sag_sa_getreqs);
+ }
+
+ if (sa->sa_dstate == SA_DSTATE_CLOSE) {
+ subagentx_free_finalize(sa);
+ return;
+ }
+
+ subagentx_start(sa);
+}
+
+void
+subagentx_free(struct subagentx *sa)
+{
+ struct subagentx_session *sas, *tsas;
+
+ if (sa == NULL)
+ return;
+
+ if (sa->sa_dstate == SA_DSTATE_CLOSE) {
+/* Malloc throws abort on invalid pointers as well */
+ subagentx_log_sa_fatalx(sa, "%s: double free", __func__);
+ }
+ sa->sa_dstate = SA_DSTATE_CLOSE;
+
+ if (!TAILQ_EMPTY(&(sa->sa_sessions))) {
+ TAILQ_FOREACH_SAFE(sas, &(sa->sa_sessions), sas_sa_sessions,
+ tsas) {
+ if (sas->sas_dstate != SA_DSTATE_CLOSE)
+ subagentx_session_free(sas);
+ }
+ } else
+ subagentx_free_finalize(sa);
+}
+
+static void
+subagentx_free_finalize(struct subagentx *sa)
+{
+#ifdef AGENTX_DEBUG
+ if (sa->sa_dstate != SA_DSTATE_CLOSE)
+ subagentx_log_sa_fatalx(sa, "%s: subagentx not closing",
+ __func__);
+ if (!TAILQ_EMPTY(&(sa->sa_sessions)))
+ subagentx_log_sa_fatalx(sa, "%s: subagentx still has sessions",
+ __func__);
+ if (!RB_EMPTY(&(sa->sa_requests)))
+ subagentx_log_sa_fatalx(sa,
+ "%s: subagentx still has pending requests", __func__);
+#endif
+
+ agentx_free(sa->sa_ax);
+ sa->sa_nofd(sa, sa->sa_cookie, 1);
+ free(sa);
+}
+
+struct subagentx_session *
+subagentx_session(struct subagentx *sa, uint32_t oid[],
+ size_t oidlen, const char *descr, uint8_t timeout)
+{
+ struct subagentx_session *sas;
+ size_t i;
+
+ if (oidlen > SUBAGENTX_OID_MAX_LEN) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sa_fatalx(sa, "%s: oidlen > %d", __func__,
+ SUBAGENTX_OID_MAX_LEN);
+#else
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+ if ((sas = calloc(1, sizeof(*sas))) == NULL)
+ return NULL;
+
+ sas->sas_sa = sa;
+ sas->sas_timeout = timeout;
+ for (i = 0; i < oidlen; i++)
+ sas->sas_oid.aoi_id[i] = oid[i];
+ sas->sas_oid.aoi_idlen = oidlen;
+ sas->sas_descr.aos_string = (unsigned char *)strdup(descr);
+ if (sas->sas_descr.aos_string == NULL) {
+ free(sas);
+ return NULL;
+ }
+ sas->sas_descr.aos_slen = strlen(descr);
+ sas->sas_cstate = SA_CSTATE_CLOSE;
+ sas->sas_dstate = SA_DSTATE_OPEN;
+ TAILQ_INIT(&(sas->sas_contexts));
+ TAILQ_INSERT_HEAD(&(sa->sa_sessions), sas, sas_sa_sessions);
+
+ if (sa->sa_cstate == SA_CSTATE_OPEN)
+ (void) subagentx_session_start(sas);
+
+ return sas;
+}
+
+static int
+subagentx_session_start(struct subagentx_session *sas)
+{
+ struct subagentx *sa = sas->sas_sa;
+ uint32_t packetid;
+
+#ifdef AGENTX_DEBUG
+ if (sa->sa_cstate != SA_CSTATE_OPEN ||
+ sas->sas_cstate != SA_CSTATE_CLOSE ||
+ sas->sas_dstate != SA_DSTATE_OPEN)
+ subagentx_log_sa_fatalx(sa, "%s: unexpected session open",
+ __func__);
+#endif
+ packetid = agentx_open(sa->sa_ax, sas->sas_timeout, &(sas->sas_oid),
+ &(sas->sas_descr));
+ if (packetid == 0) {
+ subagentx_log_sa_warn(sa, "couldn't generate %s",
+ agentx_pdutype2string(AGENTX_PDU_TYPE_OPEN));
+ subagentx_reset(sa);
+ return -1;
+ }
+ sas->sas_packetid = packetid;
+ subagentx_log_sa_info(sa, "opening session");
+ sas->sas_cstate = SA_CSTATE_WAITOPEN;
+ return subagentx_request(sa, packetid, subagentx_session_finalize, sas);
+}
+
+static int
+subagentx_session_finalize(struct agentx_pdu *pdu, void *cookie)
+{
+ struct subagentx_session *sas = cookie;
+ struct subagentx *sa = sas->sas_sa;
+ struct subagentx_context *sac;
+
+#ifdef AGENTX_DEBUG
+ if (sas->sas_cstate != SA_CSTATE_WAITOPEN)
+ subagentx_log_sa_fatalx(sa, "%s: not expecting new session",
+ __func__);
+#endif
+
+ if (pdu->ap_payload.ap_response.ap_error != AGENTX_PDU_ERROR_NOERROR) {
+ subagentx_log_sa_warnx(sa, "failed to open session: %s",
+ agentx_error2string(pdu->ap_payload.ap_response.ap_error));
+ subagentx_reset(sa);
+ return -1;
+ }
+
+ sas->sas_id = pdu->ap_header.aph_sessionid;
+ sas->sas_cstate = SA_CSTATE_OPEN;
+
+ if (sas->sas_dstate == SA_DSTATE_CLOSE) {
+ subagentx_session_close(sas, AGENTX_CLOSE_SHUTDOWN);
+ return 0;
+ }
+
+ subagentx_log_sas_info(sas, "open");
+
+ TAILQ_FOREACH(sac, &(sas->sas_contexts), sac_sas_contexts)
+ subagentx_context_start(sac);
+ return 0;
+}
+
+static int
+subagentx_session_close(struct subagentx_session *sas,
+ enum agentx_close_reason reason)
+{
+ struct subagentx *sa = sas->sas_sa;
+ uint32_t packetid;
+
+#ifdef AGENTX_DEBUG
+ if (sas->sas_cstate != SA_CSTATE_OPEN)
+ subagentx_log_sa_fatalx(sa, "%s: unexpected session close",
+ __func__);
+#endif
+ if ((packetid = agentx_close(sa->sa_ax, sas->sas_id, reason)) == 0) {
+ subagentx_log_sas_warn(sas, "couldn't generate %s",
+ agentx_pdutype2string(AGENTX_PDU_TYPE_CLOSE));
+ subagentx_reset(sa);
+ return -1;
+ }
+
+ subagentx_log_sas_info(sas, "closing session: %s",
+ agentx_closereason2string(reason));
+
+ sas->sas_cstate = SA_CSTATE_WAITCLOSE;
+ return subagentx_request(sa, packetid, subagentx_session_close_finalize,
+ sas);
+}
+
+static int
+subagentx_session_close_finalize(struct agentx_pdu *pdu, void *cookie)
+{
+ struct subagentx_session *sas = cookie;
+ struct subagentx *sa = sas->sas_sa;
+ struct subagentx_context *sac, *tsac;
+
+#ifdef AGENTX_DEBUG
+ if (sas->sas_cstate != SA_CSTATE_WAITCLOSE)
+ subagentx_log_sas_fatalx(sas, "%s: not expecting session close",
+ __func__);
+#endif
+
+ if (pdu->ap_payload.ap_response.ap_error != AGENTX_PDU_ERROR_NOERROR) {
+ subagentx_log_sas_warnx(sas, "failed to close session: %s",
+ agentx_error2string(pdu->ap_payload.ap_response.ap_error));
+ subagentx_reset(sa);
+ return -1;
+ }
+
+ sas->sas_cstate = SA_CSTATE_CLOSE;
+
+ subagentx_log_sas_info(sas, "closed");
+
+ TAILQ_FOREACH_SAFE(sac, &(sas->sas_contexts), sac_sas_contexts, tsac)
+ subagentx_context_reset(sac);
+
+ if (sas->sas_dstate == SA_DSTATE_CLOSE)
+ subagentx_session_free_finalize(sas);
+ else {
+ if (sa->sa_cstate == SA_CSTATE_OPEN)
+ if (subagentx_session_start(sas) == -1)
+ return -1;
+ }
+ return 0;
+}
+
+void
+subagentx_session_free(struct subagentx_session *sas)
+{
+ struct subagentx_context *sac, *tsac;
+
+ if (sas == NULL)
+ return;
+
+ if (sas->sas_dstate == SA_DSTATE_CLOSE)
+ subagentx_log_sas_fatalx(sas, "%s: double free", __func__);
+
+ sas->sas_dstate = SA_DSTATE_CLOSE;
+
+ if (sas->sas_cstate == SA_CSTATE_OPEN)
+ (void) subagentx_session_close(sas, AGENTX_CLOSE_SHUTDOWN);
+
+ TAILQ_FOREACH_SAFE(sac, &(sas->sas_contexts), sac_sas_contexts, tsac) {
+ if (sac->sac_dstate != SA_DSTATE_CLOSE)
+ subagentx_context_free(sac);
+ }
+
+ if (sas->sas_cstate == SA_CSTATE_CLOSE)
+ subagentx_session_free_finalize(sas);
+}
+
+static void
+subagentx_session_free_finalize(struct subagentx_session *sas)
+{
+ struct subagentx *sa = sas->sas_sa;
+
+#ifdef AGENTX_DEBUG
+ if (sas->sas_cstate != SA_CSTATE_CLOSE)
+ subagentx_log_sas_fatalx(sas, "%s: free without closing",
+ __func__);
+ if (!TAILQ_EMPTY(&(sas->sas_contexts)))
+ subagentx_log_sas_fatalx(sas,
+ "%s: subagentx still has contexts", __func__);
+#endif
+
+ TAILQ_REMOVE(&(sa->sa_sessions), sas, sas_sa_sessions);
+ free(sas->sas_descr.aos_string);
+ free(sas);
+
+ if (TAILQ_EMPTY(&(sa->sa_sessions)) && sa->sa_dstate == SA_DSTATE_CLOSE)
+ subagentx_free_finalize(sa);
+}
+
+static void
+subagentx_session_reset(struct subagentx_session *sas)
+{
+ struct subagentx_context *sac, *tsac;
+
+ sas->sas_cstate = SA_CSTATE_CLOSE;
+
+ TAILQ_FOREACH_SAFE(sac, &(sas->sas_contexts), sac_sas_contexts, tsac)
+ subagentx_context_reset(sac);
+
+ if (sas->sas_dstate == SA_DSTATE_CLOSE)
+ subagentx_session_free_finalize(sas);
+}
+
+struct subagentx_context *
+subagentx_context(struct subagentx_session *sas, const char *name)
+{
+ struct subagentx_context *sac;
+
+ if (sas->sas_dstate == SA_DSTATE_CLOSE)
+ subagentx_log_sas_fatalx(sas, "%s: use after free", __func__);
+
+ if ((sac = calloc(1, sizeof(*sac))) == NULL)
+ return NULL;
+
+ sac->sac_sas = sas;
+ sac->sac_name_default = (name == NULL);
+ if (name != NULL) {
+ sac->sac_name.aos_string = (unsigned char *)strdup(name);
+ if (sac->sac_name.aos_string == NULL) {
+ free(sac);
+ return NULL;
+ }
+ sac->sac_name.aos_slen = strlen(name);
+ }
+ sac->sac_cstate = sas->sas_cstate == SA_CSTATE_OPEN ?
+ SA_CSTATE_OPEN : SA_CSTATE_CLOSE;
+ sac->sac_dstate = SA_DSTATE_OPEN;
+ TAILQ_INIT(&(sac->sac_agentcaps));
+ TAILQ_INIT(&(sac->sac_regions));
+
+ TAILQ_INSERT_HEAD(&(sas->sas_contexts), sac, sac_sas_contexts);
+
+ return sac;
+}
+
+static void
+subagentx_context_start(struct subagentx_context *sac)
+{
+ struct subagentx_agentcaps *saa;
+ struct subagentx_region *sar;
+
+#ifdef AGENTX_DEBUG
+ if (sac->sac_cstate != SA_CSTATE_CLOSE)
+ subagentx_log_sac_fatalx(sac, "%s: unexpected context start",
+ __func__);
+#endif
+ sac->sac_cstate = SA_CSTATE_OPEN;
+
+ TAILQ_FOREACH(saa, &(sac->sac_agentcaps), saa_sac_agentcaps) {
+ if (subagentx_agentcaps_start(saa) == -1)
+ return;
+ }
+ TAILQ_FOREACH(sar, &(sac->sac_regions), sar_sac_regions) {
+ if (subagentx_region_start(sar) == -1)
+ return;
+ }
+}
+
+uint32_t
+subagentx_context_uptime(struct subagentx_context *sac)
+{
+ struct timespec cur, res;
+
+ if (sac->sac_sysuptimespec.tv_sec == 0 &&
+ sac->sac_sysuptimespec.tv_nsec == 0)
+ return 0;
+
+ (void) clock_gettime(CLOCK_MONOTONIC, &cur);
+
+ timespecsub(&cur, &(sac->sac_sysuptimespec), &res);
+
+ return sac->sac_sysuptime +
+ (uint32_t) ((res.tv_sec * 100) + (res.tv_nsec / 10000000));
+}
+
+struct subagentx_object *
+subagentx_context_object_find(struct subagentx_context *sac,
+ const uint32_t oid[], size_t oidlen, int active, int instance)
+{
+ struct subagentx_object *sao, sao_search;
+ size_t i;
+
+ for (i = 0; i < oidlen; i++)
+ sao_search.sao_oid.aoi_id[i] = oid[i];
+ sao_search.sao_oid.aoi_idlen = oidlen;
+
+ sao = RB_FIND(sac_objects, &(sac->sac_objects), &sao_search);
+ while (sao == NULL && !instance && sao_search.sao_oid.aoi_idlen > 0) {
+ sao = RB_FIND(sac_objects, &(sac->sac_objects), &sao_search);
+ sao_search.sao_oid.aoi_idlen--;
+ }
+ if (active && sao != NULL && sao->sao_cstate != SA_CSTATE_OPEN)
+ return NULL;
+ return sao;
+}
+
+struct subagentx_object *
+subagentx_context_object_nfind(struct subagentx_context *sac,
+ const uint32_t oid[], size_t oidlen, int active, int inclusive)
+{
+ struct subagentx_object *sao, sao_search;
+ size_t i;
+
+ for (i = 0; i < oidlen; i++)
+ sao_search.sao_oid.aoi_id[i] = oid[i];
+ sao_search.sao_oid.aoi_idlen = oidlen;
+
+ sao = RB_NFIND(sac_objects, &(sac->sac_objects), &sao_search);
+ if (!inclusive && sao != NULL &&
+ agentx_oid_cmp(&(sao_search.sao_oid), &(sao->sao_oid)) <= 0) {
+ sao = RB_NEXT(sac_objects, &(sac->sac_objects), sao);
+ }
+
+ while (active && sao != NULL && sao->sao_cstate != SA_CSTATE_OPEN)
+ sao = RB_NEXT(sac_objects, &(sac->sac_objects), sao);
+ return sao;
+}
+
+void
+subagentx_context_free(struct subagentx_context *sac)
+{
+ struct subagentx_agentcaps *saa, *tsaa;
+ struct subagentx_region *sar, *tsar;
+
+ if (sac == NULL)
+ return;
+
+#ifdef AGENTX_DEBUG
+ if (sac->sac_dstate == SA_DSTATE_CLOSE)
+ subagentx_log_sac_fatalx(sac, "%s: double free", __func__);
+#endif
+ sac->sac_dstate = SA_DSTATE_CLOSE;
+
+ TAILQ_FOREACH_SAFE(saa, &(sac->sac_agentcaps), saa_sac_agentcaps,
+ tsaa) {
+ if (saa->saa_dstate != SA_DSTATE_CLOSE)
+ subagentx_agentcaps_free(saa);
+ }
+ TAILQ_FOREACH_SAFE(sar, &(sac->sac_regions), sar_sac_regions, tsar) {
+ if (sar->sar_dstate != SA_DSTATE_CLOSE)
+ subagentx_region_free(sar);
+ }
+}
+
+static void
+subagentx_context_free_finalize(struct subagentx_context *sac)
+{
+ struct subagentx_session *sas = sac->sac_sas;
+
+#ifdef AGENTX_DEBUG
+ if (sac->sac_dstate != SA_DSTATE_CLOSE)
+ subagentx_log_sac_fatalx(sac, "%s: unexpected context free",
+ __func__);
+#endif
+ if (!TAILQ_EMPTY(&(sac->sac_regions)) ||
+ !TAILQ_EMPTY(&(sac->sac_agentcaps)))
+ return;
+ TAILQ_REMOVE(&(sas->sas_contexts), sac, sac_sas_contexts);
+ free(sac->sac_name.aos_string);
+ free(sac);
+}
+
+static void
+subagentx_context_reset(struct subagentx_context *sac)
+{
+ struct subagentx_agentcaps *saa, *tsaa;
+ struct subagentx_region *sar, *tsar;
+
+ sac->sac_cstate = SA_CSTATE_CLOSE;
+ sac->sac_sysuptimespec.tv_sec = 0;
+ sac->sac_sysuptimespec.tv_nsec = 0;
+
+ TAILQ_FOREACH_SAFE(saa, &(sac->sac_agentcaps), saa_sac_agentcaps, tsaa)
+ subagentx_agentcaps_reset(saa);
+ TAILQ_FOREACH_SAFE(sar, &(sac->sac_regions), sar_sac_regions, tsar)
+ subagentx_region_reset(sar);
+
+ if (sac->sac_dstate == SA_DSTATE_CLOSE)
+ subagentx_context_free_finalize(sac);
+}
+
+struct subagentx_agentcaps *
+subagentx_agentcaps(struct subagentx_context *sac, uint32_t oid[],
+ size_t oidlen, const char *descr)
+{
+ struct subagentx_agentcaps *saa;
+ size_t i;
+
+ if (sac->sac_dstate == SA_DSTATE_CLOSE)
+ subagentx_log_sac_fatalx(sac, "%s: use after free", __func__);
+
+ if ((saa = calloc(1, sizeof(*saa))) == NULL)
+ return NULL;
+
+ saa->saa_sac = sac;
+ for (i = 0; i < oidlen; i++)
+ saa->saa_oid.aoi_id[i] = oid[i];
+ saa->saa_oid.aoi_idlen = oidlen;
+ saa->saa_descr.aos_string = (unsigned char *)strdup(descr);
+ if (saa->saa_descr.aos_string == NULL) {
+ free(saa);
+ return NULL;
+ }
+ saa->saa_descr.aos_slen = strlen(descr);
+ saa->saa_cstate = SA_CSTATE_CLOSE;
+ saa->saa_dstate = SA_DSTATE_OPEN;
+
+ TAILQ_INSERT_TAIL(&(sac->sac_agentcaps), saa, saa_sac_agentcaps);
+
+ if (sac->sac_cstate == SA_CSTATE_OPEN)
+ subagentx_agentcaps_start(saa);
+
+ return saa;
+}
+
+static int
+subagentx_agentcaps_start(struct subagentx_agentcaps *saa)
+{
+ struct subagentx_context *sac = saa->saa_sac;
+ struct subagentx_session *sas = sac->sac_sas;
+ struct subagentx *sa = sas->sas_sa;
+ uint32_t packetid;
+
+#ifdef AGENTX_DEBUG
+ if (sac->sac_cstate != SA_CSTATE_OPEN ||
+ saa->saa_cstate != SA_CSTATE_CLOSE ||
+ saa->saa_dstate != SA_DSTATE_OPEN)
+ subagentx_log_sac_fatalx(sac,
+ "%s: unexpected region registration", __func__);
+#endif
+
+ packetid = agentx_addagentcaps(sa->sa_ax, sas->sas_id,
+ SUBAGENTX_CONTEXT_CTX(sac), &(saa->saa_oid), &(saa->saa_descr));
+ if (packetid == 0) {
+ subagentx_log_sac_warn(sac, "couldn't generate %s",
+ agentx_pdutype2string(AGENTX_PDU_TYPE_ADDAGENTCAPS));
+ subagentx_reset(sa);
+ return -1;
+ }
+ subagentx_log_sac_info(sac, "agentcaps %s: opening",
+ agentx_oid2string(&(saa->saa_oid)));
+ saa->saa_cstate = SA_CSTATE_WAITOPEN;
+ return subagentx_request(sa, packetid, subagentx_agentcaps_finalize,
+ saa);
+}
+
+static int
+subagentx_agentcaps_finalize(struct agentx_pdu *pdu, void *cookie)
+{
+ struct subagentx_agentcaps *saa = cookie;
+ struct subagentx_context *sac = saa->saa_sac;
+
+#ifdef AGENTX_DEBUG
+ if (saa->saa_cstate != SA_CSTATE_WAITOPEN)
+ subagentx_log_sac_fatalx(sac,
+ "%s: not expecting agentcaps open", __func__);
+#endif
+
+ if (pdu->ap_payload.ap_response.ap_error != AGENTX_PDU_ERROR_NOERROR) {
+ /* Agentcaps failing is nothing too serious */
+ subagentx_log_sac_warn(sac, "agentcaps %s: %s",
+ agentx_oid2string(&(saa->saa_oid)),
+ agentx_error2string(pdu->ap_payload.ap_response.ap_error));
+ saa->saa_cstate = SA_CSTATE_CLOSE;
+ return 0;
+ }
+
+ saa->saa_cstate = SA_CSTATE_OPEN;
+
+ subagentx_log_sac_info(sac, "agentcaps %s: open",
+ agentx_oid2string(&(saa->saa_oid)));
+
+ if (saa->saa_dstate == SA_DSTATE_CLOSE)
+ subagentx_agentcaps_close(saa);
+
+ return 0;
+}
+
+static int
+subagentx_agentcaps_close(struct subagentx_agentcaps *saa)
+{
+ struct subagentx_context *sac = saa->saa_sac;
+ struct subagentx_session *sas = sac->sac_sas;
+ struct subagentx *sa = sas->sas_sa;
+ uint32_t packetid;
+
+#ifdef AGENTX_DEBUG
+ if (saa->saa_cstate != SA_CSTATE_OPEN)
+ subagentx_log_sac_fatalx(sac, "%s: unexpected agentcaps close",
+ __func__);
+#endif
+
+ saa->saa_cstate = SA_CSTATE_WAITCLOSE;
+ if (sas->sas_cstate == SA_CSTATE_WAITCLOSE)
+ return 0;
+
+ packetid = agentx_removeagentcaps(sa->sa_ax, sas->sas_id,
+ SUBAGENTX_CONTEXT_CTX(sac), &(saa->saa_oid));
+ if (packetid == 0) {
+ subagentx_log_sac_warn(sac, "couldn't generate %s",
+ agentx_pdutype2string(AGENTX_PDU_TYPE_REMOVEAGENTCAPS));
+ subagentx_reset(sa);
+ return -1;
+ }
+ subagentx_log_sac_info(sac, "agentcaps %s: closing",
+ agentx_oid2string(&(saa->saa_oid)));
+ return subagentx_request(sa, packetid,
+ subagentx_agentcaps_close_finalize, saa);
+}
+
+static int
+subagentx_agentcaps_close_finalize(struct agentx_pdu *pdu, void *cookie)
+{
+ struct subagentx_agentcaps *saa = cookie;
+ struct subagentx_context *sac = saa->saa_sac;
+ struct subagentx_session *sas = sac->sac_sas;
+ struct subagentx *sa = sas->sas_sa;
+
+#ifdef AGENTX_DEBUG
+ if (saa->saa_cstate != SA_CSTATE_WAITCLOSE)
+ subagentx_log_sac_fatalx(sac, "%s: unexpected agentcaps close",
+ __func__);
+#endif
+
+ if (pdu->ap_payload.ap_response.ap_error != AGENTX_PDU_ERROR_NOERROR) {
+ subagentx_log_sac_warnx(sac, "agentcaps %s: %s",
+ agentx_oid2string(&(saa->saa_oid)),
+ agentx_error2string(pdu->ap_payload.ap_response.ap_error));
+ subagentx_reset(sa);
+ return -1;
+ }
+
+ saa->saa_cstate = SA_CSTATE_CLOSE;
+
+ subagentx_log_sac_info(sac, "agentcaps %s: closed",
+ agentx_oid2string(&(saa->saa_oid)));
+
+ if (saa->saa_dstate == SA_DSTATE_CLOSE) {
+ subagentx_agentcaps_free_finalize(saa);
+ return 0;
+ } else {
+ if (sac->sac_cstate == SA_CSTATE_OPEN) {
+ if (subagentx_agentcaps_start(saa) == -1)
+ return -1;
+ }
+ }
+ return 0;
+}
+
+void
+subagentx_agentcaps_free(struct subagentx_agentcaps *saa)
+{
+ if (saa == NULL)
+ return;
+
+ if (saa->saa_dstate == SA_DSTATE_CLOSE)
+ subagentx_log_sac_fatalx(saa->saa_sac, "%s: double free",
+ __func__);
+
+ saa->saa_dstate = SA_DSTATE_CLOSE;
+
+ if (saa->saa_cstate == SA_CSTATE_OPEN) {
+ if (subagentx_agentcaps_close(saa) == -1)
+ return;
+ }
+
+ if (saa->saa_cstate == SA_CSTATE_CLOSE)
+ subagentx_agentcaps_free_finalize(saa);
+}
+
+static void
+subagentx_agentcaps_free_finalize(struct subagentx_agentcaps *saa)
+{
+ struct subagentx_context *sac = saa->saa_sac;
+
+#ifdef AGENTX_DEBUG
+ if (saa->saa_dstate != SA_DSTATE_CLOSE ||
+ saa->saa_cstate != SA_CSTATE_CLOSE)
+ subagentx_log_sac_fatalx(sac, "%s: unexpected free", __func__);
+#endif
+
+ TAILQ_REMOVE(&(sac->sac_agentcaps), saa, saa_sac_agentcaps);
+ free(saa->saa_descr.aos_string);
+ free(saa);
+
+ if (sac->sac_dstate == SA_DSTATE_CLOSE)
+ subagentx_context_free_finalize(sac);
+}
+
+static void
+subagentx_agentcaps_reset(struct subagentx_agentcaps *saa)
+{
+ saa->saa_cstate = SA_CSTATE_CLOSE;
+
+ if (saa->saa_dstate == SA_DSTATE_CLOSE)
+ subagentx_agentcaps_free_finalize(saa);
+}
+
+struct subagentx_region *
+subagentx_region(struct subagentx_context *sac, uint32_t oid[],
+ size_t oidlen, uint8_t timeout)
+{
+ struct subagentx_region *sar;
+ struct agentx_oid tmpoid;
+ size_t i;
+
+ if (sac->sac_dstate == SA_DSTATE_CLOSE)
+ subagentx_log_sac_fatalx(sac, "%s: use after free", __func__);
+ if (oidlen < 1) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sac, "%s: oidlen == 0", __func__);
+#else
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+ if (oidlen > SUBAGENTX_OID_MAX_LEN) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sac, "%s: oidlen > %d", __func__,
+ SUBAGENTX_OID_MAX_LEN);
+#else
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+
+ for (i = 0; i < oidlen; i++)
+ tmpoid.aoi_id[i] = oid[i];
+ tmpoid.aoi_idlen = oidlen;
+ TAILQ_FOREACH(sar, &(sac->sac_regions), sar_sac_regions) {
+ if (agentx_oid_cmp(&(sar->sar_oid), &tmpoid) == 0) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sac,
+ "%s: duplicate region registration", __func__);
+#else
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+ }
+
+ if ((sar = calloc(1, sizeof(*sar))) == NULL)
+ return NULL;
+
+ sar->sar_sac = sac;
+ sar->sar_timeout = timeout;
+ sar->sar_priority = AGENTX_PRIORITY_DEFAULT;
+ bcopy(&tmpoid, &(sar->sar_oid), sizeof(sar->sar_oid));
+ sar->sar_cstate = SA_CSTATE_CLOSE;
+ sar->sar_dstate = SA_DSTATE_OPEN;
+ TAILQ_INIT(&(sar->sar_indices));
+ TAILQ_INIT(&(sar->sar_objects));
+
+ TAILQ_INSERT_HEAD(&(sac->sac_regions), sar, sar_sac_regions);
+
+ if (sac->sac_cstate == SA_CSTATE_OPEN)
+ (void) subagentx_region_start(sar);
+
+ return sar;
+}
+
+static int
+subagentx_region_start(struct subagentx_region *sar)
+{
+ struct subagentx_context *sac = sar->sar_sac;
+ struct subagentx_session *sas = sac->sac_sas;
+ struct subagentx *sa = sas->sas_sa;
+ uint32_t packetid;
+
+#ifdef AGENTX_DEBUG
+ if (sac->sac_cstate != SA_CSTATE_OPEN ||
+ sar->sar_cstate != SA_CSTATE_CLOSE ||
+ sar->sar_dstate != SA_DSTATE_OPEN)
+ subagentx_log_sac_fatalx(sac,
+ "%s: unexpected region registration", __func__);
+#endif
+
+ packetid = agentx_register(sa->sa_ax, 0, sas->sas_id,
+ SUBAGENTX_CONTEXT_CTX(sac), sar->sar_timeout, sar->sar_priority,
+ 0, &(sar->sar_oid), 0);
+ if (packetid == 0) {
+ subagentx_log_sac_warn(sac, "couldn't generate %s",
+ agentx_pdutype2string(AGENTX_PDU_TYPE_REGISTER));
+ subagentx_reset(sa);
+ return -1;
+ }
+ subagentx_log_sac_info(sac, "region %s: opening",
+ agentx_oid2string(&(sar->sar_oid)));
+ sar->sar_cstate = SA_CSTATE_WAITOPEN;
+ return subagentx_request(sa, packetid, subagentx_region_finalize, sar);
+}
+
+static int
+subagentx_region_finalize(struct agentx_pdu *pdu, void *cookie)
+{
+ struct subagentx_region *sar = cookie;
+ struct subagentx_context *sac = sar->sar_sac;
+ struct subagentx_session *sas = sac->sac_sas;
+ struct subagentx *sa = sas->sas_sa;
+ struct subagentx_index *sai;
+ struct subagentx_object *sao;
+
+#ifdef AGENTX_DEBUG
+ if (sar->sar_cstate != SA_CSTATE_WAITOPEN)
+ subagentx_log_sac_fatalx(sac, "%s: not expecting region open",
+ __func__);
+#endif
+
+ if (pdu->ap_payload.ap_response.ap_error == AGENTX_PDU_ERROR_NOERROR) {
+ sar->sar_cstate = SA_CSTATE_OPEN;
+ subagentx_log_sac_info(sac, "region %s: open",
+ agentx_oid2string(&(sar->sar_oid)));
+ } else if (pdu->ap_payload.ap_response.ap_error ==
+ AGENTX_PDU_ERROR_DUPLICATEREGISTRATION) {
+ sar->sar_cstate = SA_CSTATE_CLOSE;
+ /* Try at lower priority: first come first serve */
+ if ((++sar->sar_priority) != 0) {
+ subagentx_log_sac_warnx(sac, "region %s: duplicate, "
+ "reducing priority",
+ agentx_oid2string(&(sar->sar_oid)));
+ return subagentx_region_start(sar);
+ }
+ subagentx_log_sac_info(sac, "region %s: duplicate, can't "
+ "reduce priority, ignoring",
+ agentx_oid2string(&(sar->sar_oid)));
+ } else if (pdu->ap_payload.ap_response.ap_error ==
+ AGENTX_PDU_ERROR_REQUESTDENIED) {
+ sar->sar_cstate = SA_CSTATE_CLOSE;
+ subagentx_log_sac_warnx(sac, "region %s: %s",
+ agentx_oid2string(&(sar->sar_oid)),
+ agentx_error2string(pdu->ap_payload.ap_response.ap_error));
+ /*
+ * If we can't register a region, related objects are useless.
+ * But no need to retry.
+ */
+ return 0;
+ } else {
+ subagentx_log_sac_info(sac, "region %s: %s",
+ agentx_oid2string(&(sar->sar_oid)),
+ agentx_error2string(pdu->ap_payload.ap_response.ap_error));
+ subagentx_reset(sa);
+ return -1;
+ }
+
+ if (sar->sar_dstate == SA_DSTATE_CLOSE) {
+ if (subagentx_region_close(sar) == -1)
+ return -1;
+ } else {
+ TAILQ_FOREACH(sai, &(sar->sar_indices), sai_sar_indices) {
+ if (subagentx_index_start(sai) == -1)
+ return -1;
+ }
+ TAILQ_FOREACH(sao, &(sar->sar_objects), sao_sar_objects) {
+ if (subagentx_object_start(sao) == -1)
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int
+subagentx_region_close(struct subagentx_region *sar)
+{
+ struct subagentx_context *sac = sar->sar_sac;
+ struct subagentx_session *sas = sac->sac_sas;
+ struct subagentx *sa = sas->sas_sa;
+ uint32_t packetid;
+
+#ifdef AGENTX_DEBUG
+ if (sar->sar_cstate != SA_CSTATE_OPEN)
+ subagentx_log_sac_fatalx(sac, "%s: unexpected region close",
+ __func__);
+#endif
+
+ sar->sar_cstate = SA_CSTATE_WAITCLOSE;
+ if (sas->sas_cstate == SA_CSTATE_WAITCLOSE)
+ return 0;
+
+ packetid = agentx_unregister(sa->sa_ax, sas->sas_id,
+ SUBAGENTX_CONTEXT_CTX(sac), sar->sar_priority, 0, &(sar->sar_oid),
+ 0);
+ if (packetid == 0) {
+ subagentx_log_sac_warn(sac, "couldn't generate %s",
+ agentx_pdutype2string(AGENTX_PDU_TYPE_UNREGISTER));
+ subagentx_reset(sa);
+ return -1;
+ }
+ subagentx_log_sac_info(sac, "region %s: closing",
+ agentx_oid2string(&(sar->sar_oid)));
+ return subagentx_request(sa, packetid, subagentx_region_close_finalize,
+ sar);
+}
+
+static int
+subagentx_region_close_finalize(struct agentx_pdu *pdu, void *cookie)
+{
+ struct subagentx_region *sar = cookie;
+ struct subagentx_context *sac = sar->sar_sac;
+ struct subagentx_session *sas = sac->sac_sas;
+ struct subagentx *sa = sas->sas_sa;
+
+#ifdef AGENTX_DEBUG
+ if (sar->sar_cstate != SA_CSTATE_WAITCLOSE)
+ subagentx_log_sac_fatalx(sac, "%s: unexpected region close",
+ __func__);
+#endif
+
+ if (pdu->ap_payload.ap_response.ap_error != AGENTX_PDU_ERROR_NOERROR) {
+ subagentx_log_sac_warnx(sac, "closing %s: %s",
+ agentx_oid2string(&(sar->sar_oid)),
+ agentx_error2string(pdu->ap_payload.ap_response.ap_error));
+ subagentx_reset(sa);
+ return -1;
+ }
+
+ sar->sar_priority = AGENTX_PRIORITY_DEFAULT;
+ sar->sar_cstate = SA_CSTATE_CLOSE;
+
+ subagentx_log_sac_info(sac, "region %s: closed",
+ agentx_oid2string(&(sar->sar_oid)));
+
+ if (sar->sar_dstate == SA_DSTATE_CLOSE) {
+ subagentx_region_free_finalize(sar);
+ return 0;
+ } else {
+ if (sac->sac_cstate == SA_CSTATE_OPEN) {
+ if (subagentx_region_start(sar) == -1)
+ return -1;
+ }
+ }
+ return 0;
+}
+
+void
+subagentx_region_free(struct subagentx_region *sar)
+{
+ struct subagentx_index *sai, *tsai;
+ struct subagentx_object *sao, *tsao;
+
+ if (sar == NULL)
+ return;
+
+ if (sar->sar_dstate == SA_DSTATE_CLOSE)
+ subagentx_log_sac_fatalx(sar->sar_sac, "%s: double free",
+ __func__);
+
+ sar->sar_dstate = SA_DSTATE_CLOSE;
+
+ TAILQ_FOREACH_SAFE(sai, &(sar->sar_indices), sai_sar_indices, tsai) {
+ if (sai->sai_dstate != SA_DSTATE_CLOSE)
+ subagentx_index_free(sai);
+ }
+
+ TAILQ_FOREACH_SAFE(sao, &(sar->sar_objects), sao_sar_objects, tsao) {
+ if (sao->sao_dstate != SA_DSTATE_CLOSE)
+ subagentx_object_free(sao);
+ }
+
+ if (sar->sar_cstate == SA_CSTATE_OPEN) {
+ if (subagentx_region_close(sar) == -1)
+ return;
+ }
+
+ if (sar->sar_cstate == SA_CSTATE_CLOSE)
+ subagentx_region_free_finalize(sar);
+}
+
+static void
+subagentx_region_free_finalize(struct subagentx_region *sar)
+{
+ struct subagentx_context *sac = sar->sar_sac;
+
+#ifdef AGENTX_DEBUG
+ if (sar->sar_dstate != SA_DSTATE_CLOSE)
+ subagentx_log_sac_fatalx(sac, "%s: unexpected free", __func__);
+#endif
+
+ if (!TAILQ_EMPTY(&(sar->sar_indices)) ||
+ !TAILQ_EMPTY(&(sar->sar_objects)))
+ return;
+
+ if (sar->sar_cstate != SA_CSTATE_CLOSE)
+ return;
+
+ TAILQ_REMOVE(&(sac->sac_regions), sar, sar_sac_regions);
+ free(sar);
+
+ if (sac->sac_dstate == SA_DSTATE_CLOSE)
+ subagentx_context_free_finalize(sac);
+}
+
+static void
+subagentx_region_reset(struct subagentx_region *sar)
+{
+ struct subagentx_index *sai, *tsai;
+ struct subagentx_object *sao, *tsao;
+
+ sar->sar_cstate = SA_CSTATE_CLOSE;
+ sar->sar_priority = AGENTX_PRIORITY_DEFAULT;
+
+ TAILQ_FOREACH_SAFE(sai, &(sar->sar_indices), sai_sar_indices, tsai)
+ subagentx_index_reset(sai);
+ TAILQ_FOREACH_SAFE(sao, &(sar->sar_objects), sao_sar_objects, tsao)
+ subagentx_object_reset(sao);
+
+ if (sar->sar_dstate == SA_DSTATE_CLOSE)
+ subagentx_region_free_finalize(sar);
+}
+
+struct subagentx_index *
+subagentx_index_integer_new(struct subagentx_region *sar, uint32_t oid[],
+ size_t oidlen)
+{
+ struct agentx_varbind vb;
+ size_t i;
+
+ if (oidlen > SUBAGENTX_OID_MAX_LEN) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d",
+ __func__, SUBAGENTX_OID_MAX_LEN);
+#else
+ subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d",
+ __func__, SUBAGENTX_OID_MAX_LEN);
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+
+ vb.avb_type = AGENTX_DATA_TYPE_INTEGER;
+ for (i = 0; i < oidlen; i++)
+ vb.avb_oid.aoi_id[i] = oid[i];
+ vb.avb_oid.aoi_idlen = oidlen;
+ vb.avb_data.avb_uint32 = 0;
+
+ return subagentx_index(sar, &vb, SAI_TYPE_NEW);
+}
+
+struct subagentx_index *
+subagentx_index_integer_any(struct subagentx_region *sar, uint32_t oid[],
+ size_t oidlen)
+{
+ struct agentx_varbind vb;
+ size_t i;
+
+ if (oidlen > SUBAGENTX_OID_MAX_LEN) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d",
+ __func__, SUBAGENTX_OID_MAX_LEN);
+#else
+ subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d",
+ __func__, SUBAGENTX_OID_MAX_LEN);
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+
+ vb.avb_type = AGENTX_DATA_TYPE_INTEGER;
+ for (i = 0; i < oidlen; i++)
+ vb.avb_oid.aoi_id[i] = oid[i];
+ vb.avb_oid.aoi_idlen = oidlen;
+ vb.avb_data.avb_uint32 = 0;
+
+ return subagentx_index(sar, &vb, SAI_TYPE_ANY);
+}
+
+struct subagentx_index *
+subagentx_index_integer_value(struct subagentx_region *sar, uint32_t oid[],
+ size_t oidlen, uint32_t value)
+{
+ struct agentx_varbind vb;
+ size_t i;
+
+ if (oidlen > SUBAGENTX_OID_MAX_LEN) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d",
+ __func__, SUBAGENTX_OID_MAX_LEN);
+#else
+ subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d",
+ __func__, SUBAGENTX_OID_MAX_LEN);
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+
+ vb.avb_type = AGENTX_DATA_TYPE_INTEGER;
+ for (i = 0; i < oidlen; i++)
+ vb.avb_oid.aoi_id[i] = oid[i];
+ vb.avb_oid.aoi_idlen = oidlen;
+ vb.avb_data.avb_uint32 = value;
+
+ return subagentx_index(sar, &vb, SAI_TYPE_VALUE);
+}
+
+struct subagentx_index *
+subagentx_index_integer_dynamic(struct subagentx_region *sar, uint32_t oid[],
+ size_t oidlen)
+{
+ struct agentx_varbind vb;
+ size_t i;
+
+ if (oidlen > SUBAGENTX_OID_MAX_LEN) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d",
+ __func__, SUBAGENTX_OID_MAX_LEN);
+#else
+ subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d",
+ __func__, SUBAGENTX_OID_MAX_LEN);
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+
+ vb.avb_type = AGENTX_DATA_TYPE_INTEGER;
+ for (i = 0; i < oidlen; i++)
+ vb.avb_oid.aoi_id[i] = oid[i];
+ vb.avb_oid.aoi_idlen = oidlen;
+
+ return subagentx_index(sar, &vb, SAI_TYPE_DYNAMIC);
+}
+
+struct subagentx_index *
+subagentx_index_string_dynamic(struct subagentx_region *sar, uint32_t oid[],
+ size_t oidlen)
+{
+ struct agentx_varbind vb;
+ size_t i;
+
+ if (oidlen > SUBAGENTX_OID_MAX_LEN) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d",
+ __func__, SUBAGENTX_OID_MAX_LEN);
+#else
+ subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d",
+ __func__, SUBAGENTX_OID_MAX_LEN);
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+
+ vb.avb_type = AGENTX_DATA_TYPE_OCTETSTRING;
+ for (i = 0; i < oidlen; i++)
+ vb.avb_oid.aoi_id[i] = oid[i];
+ vb.avb_oid.aoi_idlen = oidlen;
+ vb.avb_data.avb_ostring.aos_slen = 0;
+ vb.avb_data.avb_ostring.aos_string = NULL;
+
+ return subagentx_index(sar, &vb, SAI_TYPE_DYNAMIC);
+}
+
+struct subagentx_index *
+subagentx_index_nstring_dynamic(struct subagentx_region *sar, uint32_t oid[],
+ size_t oidlen, size_t vlen)
+{
+ struct agentx_varbind vb;
+ size_t i;
+
+ if (oidlen > SUBAGENTX_OID_MAX_LEN) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d",
+ __func__, SUBAGENTX_OID_MAX_LEN);
+#else
+ subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d",
+ __func__, SUBAGENTX_OID_MAX_LEN);
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+ if (vlen == 0 || vlen > SUBAGENTX_OID_MAX_LEN) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sar->sar_sac, "%s: invalid string "
+ "length: %zu\n", __func__, vlen);
+#else
+ subagentx_log_sac_warnx(sar->sar_sac, "%s: invalid string "
+ "length: %zu\n", __func__, vlen);
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+
+ vb.avb_type = AGENTX_DATA_TYPE_OCTETSTRING;
+ for (i = 0; i < oidlen; i++)
+ vb.avb_oid.aoi_id[i] = oid[i];
+ vb.avb_oid.aoi_idlen = oidlen;
+ vb.avb_data.avb_ostring.aos_slen = vlen;
+ vb.avb_data.avb_ostring.aos_string = NULL;
+
+ return subagentx_index(sar, &vb, SAI_TYPE_DYNAMIC);
+}
+
+struct subagentx_index *
+subagentx_index_oid_dynamic(struct subagentx_region *sar, uint32_t oid[],
+ size_t oidlen)
+{
+ struct agentx_varbind vb;
+ size_t i;
+
+ if (oidlen > SUBAGENTX_OID_MAX_LEN) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d",
+ __func__, SUBAGENTX_OID_MAX_LEN);
+#else
+ subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d",
+ __func__, SUBAGENTX_OID_MAX_LEN);
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+
+ vb.avb_type = AGENTX_DATA_TYPE_OID;
+ for (i = 0; i < oidlen; i++)
+ vb.avb_oid.aoi_id[i] = oid[i];
+ vb.avb_oid.aoi_idlen = oidlen;
+ vb.avb_data.avb_oid.aoi_idlen = 0;
+
+ return subagentx_index(sar, &vb, SAI_TYPE_DYNAMIC);
+}
+
+struct subagentx_index *
+subagentx_index_noid_dynamic(struct subagentx_region *sar, uint32_t oid[],
+ size_t oidlen, size_t vlen)
+{
+ struct agentx_varbind vb;
+ size_t i;
+
+ if (oidlen > SUBAGENTX_OID_MAX_LEN) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d",
+ __func__, SUBAGENTX_OID_MAX_LEN);
+#else
+ subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d",
+ __func__, SUBAGENTX_OID_MAX_LEN);
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+ if (vlen == 0 || vlen > SUBAGENTX_OID_MAX_LEN) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sar->sar_sac, "%s: invalid string "
+ "length: %zu\n", __func__, vlen);
+#else
+ subagentx_log_sac_warnx(sar->sar_sac, "%s: invalid string "
+ "length: %zu\n", __func__, vlen);
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+
+ vb.avb_type = AGENTX_DATA_TYPE_OID;
+ for (i = 0; i < oidlen; i++)
+ vb.avb_oid.aoi_id[i] = oid[i];
+ vb.avb_oid.aoi_idlen = oidlen;
+ vb.avb_data.avb_oid.aoi_idlen = vlen;
+
+ return subagentx_index(sar, &vb, SAI_TYPE_DYNAMIC);
+}
+
+struct subagentx_index *
+subagentx_index_ipaddress_dynamic(struct subagentx_region *sar, uint32_t oid[],
+ size_t oidlen)
+{
+ struct agentx_varbind vb;
+ size_t i;
+
+ if (oidlen > SUBAGENTX_OID_MAX_LEN) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d",
+ __func__, SUBAGENTX_OID_MAX_LEN);
+#else
+ subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d",
+ __func__, SUBAGENTX_OID_MAX_LEN);
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+
+ vb.avb_type = AGENTX_DATA_TYPE_IPADDRESS;
+ for (i = 0; i < oidlen; i++)
+ vb.avb_oid.aoi_id[i] = oid[i];
+ vb.avb_data.avb_ostring.aos_string = NULL;
+ vb.avb_oid.aoi_idlen = oidlen;
+
+ return subagentx_index(sar, &vb, SAI_TYPE_DYNAMIC);
+}
+
+static struct subagentx_index *
+subagentx_index(struct subagentx_region *sar, struct agentx_varbind *vb,
+ enum subagentx_index_type type)
+{
+ struct subagentx_index *sai;
+
+ if (sar->sar_dstate == SA_DSTATE_CLOSE)
+ subagentx_log_sac_fatalx(sar->sar_sac, "%s: use after free",
+ __func__);
+ if (agentx_oid_cmp(&(sar->sar_oid), &(vb->avb_oid)) != -2) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sar->sar_sac, "%s: oid is not child "
+ "of region %s", __func__,
+ agentx_oid2string(&(vb->avb_oid)));
+#else
+ subagentx_log_sac_warnx(sar->sar_sac, "%s: oid is not child of "
+ "region %s", __func__, agentx_oid2string(&(vb->avb_oid)));
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+
+ if ((sai = calloc(1, sizeof(*sai))) == NULL)
+ return NULL;
+
+ sai->sai_sar = sar;
+ sai->sai_type = type;
+ bcopy(vb, &(sai->sai_vb), sizeof(*vb));
+ sai->sai_cstate = SA_CSTATE_CLOSE;
+ sai->sai_dstate = SA_DSTATE_OPEN;
+ TAILQ_INSERT_HEAD(&(sar->sar_indices), sai, sai_sar_indices);
+
+ if (sar->sar_cstate == SA_CSTATE_OPEN)
+ subagentx_index_start(sai);
+
+ return sai;
+}
+
+static int
+subagentx_index_start(struct subagentx_index *sai)
+{
+ struct subagentx_region *sar = sai->sai_sar;
+ struct subagentx_context *sac = sar->sar_sac;
+ struct subagentx_session *sas = sac->sac_sas;
+ struct subagentx *sa = sas->sas_sa;
+ uint32_t packetid;
+ int flags = 0;
+
+#ifdef AGENTX_DEBUG
+ if (sar->sar_cstate != SA_CSTATE_OPEN ||
+ sai->sai_cstate != SA_CSTATE_CLOSE ||
+ sai->sai_dstate != SA_DSTATE_OPEN)
+ subagentx_log_sac_fatalx(sac, "%s: unexpected index allocation",
+ __func__);
+#endif
+
+ sai->sai_cstate = SA_CSTATE_WAITOPEN;
+
+ if (sai->sai_type == SAI_TYPE_NEW)
+ flags = AGENTX_PDU_FLAG_NEW_INDEX;
+ else if (sai->sai_type == SAI_TYPE_ANY)
+ flags = AGENTX_PDU_FLAG_ANY_INDEX;
+ else if (sai->sai_type == SAI_TYPE_DYNAMIC) {
+ subagentx_index_finalize(NULL, sai);
+ return 0;
+ }
+
+ /* We might be able to bundle, but if we fail we'd have to reorganise */
+ packetid = agentx_indexallocate(sa->sa_ax, flags, sas->sas_id,
+ SUBAGENTX_CONTEXT_CTX(sac), &(sai->sai_vb), 1);
+ if (packetid == 0) {
+ subagentx_log_sac_warn(sac, "couldn't generate %s",
+ agentx_pdutype2string(AGENTX_PDU_TYPE_INDEXDEALLOCATE));
+ subagentx_reset(sa);
+ return -1;
+ }
+ if (sai->sai_type == SAI_TYPE_VALUE)
+ subagentx_log_sac_info(sac, "index %s: allocating '%u'",
+ agentx_oid2string(&(sai->sai_vb.avb_oid)),
+ sai->sai_vb.avb_data.avb_uint32);
+ else if (sai->sai_type == SAI_TYPE_ANY)
+ subagentx_log_sac_info(sac, "index %s: allocating any index",
+ agentx_oid2string(&(sai->sai_vb.avb_oid)));
+ else if (sai->sai_type == SAI_TYPE_NEW)
+ subagentx_log_sac_info(sac, "index %s: allocating new index",
+ agentx_oid2string(&(sai->sai_vb.avb_oid)));
+
+ return subagentx_request(sa, packetid, subagentx_index_finalize, sai);
+}
+
+static int
+subagentx_index_finalize(struct agentx_pdu *pdu, void *cookie)
+{
+ struct subagentx_index *sai = cookie;
+ struct subagentx_region *sar = sai->sai_sar;
+ struct subagentx_context *sac = sar->sar_sac;
+ struct subagentx_session *sas = sac->sac_sas;
+ struct subagentx *sa = sas->sas_sa;
+ struct agentx_pdu_response *resp;
+ size_t i;
+
+#ifdef AGENTX_DEBUG
+ if (sai->sai_cstate != SA_CSTATE_WAITOPEN)
+ subagentx_log_sac_fatalx(sac,
+ "%s: not expecting index allocate", __func__);
+#endif
+ if (sai->sai_type == SAI_TYPE_DYNAMIC) {
+ sai->sai_cstate = SA_CSTATE_OPEN;
+ return 0;
+ }
+
+ resp = &(pdu->ap_payload.ap_response);
+ if (resp->ap_error != AGENTX_PDU_ERROR_NOERROR) {
+ sai->sai_cstate = SA_CSTATE_CLOSE;
+ subagentx_log_sac_warnx(sac, "index %s: %s",
+ agentx_oid2string(&(sar->sar_oid)),
+ agentx_error2string(resp->ap_error));
+ return 0;
+ }
+ sai->sai_cstate = SA_CSTATE_OPEN;
+ if (resp->ap_nvarbind != 1) {
+ subagentx_log_sac_warnx(sac, "index %s: unexpected number of "
+ "indices", agentx_oid2string(&(sar->sar_oid)));
+ subagentx_reset(sa);
+ return -1;
+ }
+ if (resp->ap_varbindlist[0].avb_type != sai->sai_vb.avb_type) {
+ subagentx_log_sac_warnx(sac, "index %s: unexpected index type",
+ agentx_oid2string(&(sar->sar_oid)));
+ subagentx_reset(sa);
+ return -1;
+ }
+ if (agentx_oid_cmp(&(resp->ap_varbindlist[0].avb_oid),
+ &(sai->sai_vb.avb_oid)) != 0) {
+ subagentx_log_sac_warnx(sac, "index %s: unexpected oid",
+ agentx_oid2string(&(sar->sar_oid)));
+ subagentx_reset(sa);
+ return -1;
+ }
+
+ switch (sai->sai_vb.avb_type) {
+ case AGENTX_DATA_TYPE_INTEGER:
+ if (sai->sai_type == SAI_TYPE_NEW ||
+ sai->sai_type == SAI_TYPE_ANY)
+ sai->sai_vb.avb_data.avb_uint32 =
+ resp->ap_varbindlist[0].avb_data.avb_uint32;
+ else if (sai->sai_vb.avb_data.avb_uint32 !=
+ resp->ap_varbindlist[0].avb_data.avb_uint32) {
+ subagentx_log_sac_warnx(sac, "index %s: unexpected "
+ "index value", agentx_oid2string(&(sar->sar_oid)));
+ subagentx_reset(sa);
+ return -1;
+ }
+ subagentx_log_sac_info(sac, "index %s: allocated '%u'",
+ agentx_oid2string(&(sai->sai_vb.avb_oid)),
+ sai->sai_vb.avb_data.avb_uint32);
+ break;
+ default:
+ subagentx_log_sac_fatalx(sac, "%s: Unsupported index type",
+ __func__);
+ }
+
+ if (sai->sai_dstate == SA_DSTATE_CLOSE)
+ return subagentx_index_close(sai);
+
+ /* TODO Make use of range_subid register */
+ for (i = 0; i < sai->sai_objectlen; i++) {
+ if (sai->sai_object[i]->sao_dstate == SA_DSTATE_OPEN) {
+ if (subagentx_object_start(sai->sai_object[i]) == -1)
+ return -1;
+ }
+ }
+ return 0;
+}
+
+void
+subagentx_index_free(struct subagentx_index *sai)
+{
+ size_t i;
+
+ if (sai == NULL)
+ return;
+
+ if (sai->sai_dstate == SA_DSTATE_CLOSE)
+ subagentx_log_sac_fatalx(sai->sai_sar->sar_sac,
+ "%s: double free", __func__);
+
+ sai->sai_dstate = SA_DSTATE_CLOSE;
+
+ /* TODO Do a range_subid unregister before freeing */
+ for (i = 0; i < sai->sai_objectlen; i++) {
+ if (sai->sai_object[i]->sao_dstate != SA_DSTATE_CLOSE)
+ subagentx_object_free(sai->sai_object[i]);
+ }
+
+ if (sai->sai_cstate == SA_CSTATE_OPEN)
+ (void) subagentx_index_close(sai);
+ else if (sai->sai_cstate == SA_CSTATE_CLOSE)
+ subagentx_index_free_finalize(sai);
+}
+
+static void
+subagentx_index_free_finalize(struct subagentx_index *sai)
+{
+ struct subagentx_region *sar = sai->sai_sar;
+
+#ifdef AGENTX_DEBUG
+ if (sai->sai_dstate != SA_DSTATE_CLOSE)
+ subagentx_log_sac_fatalx(sar->sar_sac, "%s: unexpected free",
+ __func__);
+ if (sai->sai_cstate != SA_CSTATE_CLOSE)
+ subagentx_log_sac_fatalx(sar->sar_sac,
+ "%s: free without deallocating", __func__);
+#endif
+
+ if (sai->sai_objectlen != 0)
+ return;
+
+ TAILQ_REMOVE(&(sar->sar_indices), sai, sai_sar_indices);
+ agentx_varbind_free(&(sai->sai_vb));
+ free(sai->sai_object);
+ free(sai);
+ if (sar->sar_dstate == SA_DSTATE_CLOSE)
+ subagentx_region_free_finalize(sar);
+}
+
+static void
+subagentx_index_reset(struct subagentx_index *sai)
+{
+ sai->sai_cstate = SA_CSTATE_CLOSE;
+
+ if (sai->sai_dstate == SA_DSTATE_CLOSE)
+ subagentx_index_free_finalize(sai);
+}
+
+static int
+subagentx_index_close(struct subagentx_index *sai)
+{
+ struct subagentx_region *sar = sai->sai_sar;
+ struct subagentx_context *sac = sar->sar_sac;
+ struct subagentx_session *sas = sac->sac_sas;
+ struct subagentx *sa = sas->sas_sa;
+ uint32_t packetid;
+
+#ifdef AGENTX_DEBUG
+ if (sai->sai_cstate != SA_CSTATE_OPEN)
+ subagentx_log_sac_fatalx(sac,
+ "%s: unexpected index deallocation", __func__);
+#endif
+
+ sai->sai_cstate = SA_CSTATE_WAITCLOSE;
+ if (sas->sas_cstate == SA_CSTATE_WAITCLOSE)
+ return 0;
+
+ /* We might be able to bundle, but if we fail we'd have to reorganise */
+ packetid = agentx_indexdeallocate(sa->sa_ax, sas->sas_id,
+ SUBAGENTX_CONTEXT_CTX(sac), &(sai->sai_vb), 1);
+ if (packetid == 0) {
+ subagentx_log_sac_warn(sac, "couldn't generate %s",
+ agentx_pdutype2string(AGENTX_PDU_TYPE_INDEXDEALLOCATE));
+ subagentx_reset(sa);
+ return -1;
+ }
+ subagentx_log_sac_info(sac, "index %s: deallocating",
+ agentx_oid2string(&(sai->sai_vb.avb_oid)));
+ return subagentx_request(sa, packetid, subagentx_index_close_finalize,
+ sai);
+}
+
+static int
+subagentx_index_close_finalize(struct agentx_pdu *pdu, void *cookie)
+{
+ struct subagentx_index *sai = cookie;
+ struct subagentx_region *sar = sai->sai_sar;
+ struct subagentx_context *sac = sar->sar_sac;
+ struct subagentx_session *sas = sac->sac_sas;
+ struct subagentx *sa = sas->sas_sa;
+ struct agentx_pdu_response *resp = &(pdu->ap_payload.ap_response);
+
+#ifdef AGENTX_DEBUG
+ if (sai->sai_cstate != SA_CSTATE_WAITCLOSE)
+ subagentx_log_sac_fatalx(sac, "%s: unexpected indexdeallocate",
+ __func__);
+#endif
+
+ if (pdu->ap_payload.ap_response.ap_error != AGENTX_PDU_ERROR_NOERROR) {
+ subagentx_log_sac_warnx(sac,
+ "index %s: couldn't deallocate: %s",
+ agentx_oid2string(&(sai->sai_vb.avb_oid)),
+ agentx_error2string(resp->ap_error));
+ subagentx_reset(sa);
+ return -1;
+ }
+
+ if (resp->ap_nvarbind != 1) {
+ subagentx_log_sac_warnx(sac,
+ "index %s: unexpected number of indices",
+ agentx_oid2string(&(sar->sar_oid)));
+ subagentx_reset(sa);
+ return -1;
+ }
+ if (resp->ap_varbindlist[0].avb_type != sai->sai_vb.avb_type) {
+ subagentx_log_sac_warnx(sac, "index %s: unexpected index type",
+ agentx_oid2string(&(sar->sar_oid)));
+ subagentx_reset(sa);
+ return -1;
+ }
+ if (agentx_oid_cmp(&(resp->ap_varbindlist[0].avb_oid),
+ &(sai->sai_vb.avb_oid)) != 0) {
+ subagentx_log_sac_warnx(sac, "index %s: unexpected oid",
+ agentx_oid2string(&(sar->sar_oid)));
+ subagentx_reset(sa);
+ return -1;
+ }
+ switch (sai->sai_vb.avb_type) {
+ case AGENTX_DATA_TYPE_INTEGER:
+ if (sai->sai_vb.avb_data.avb_uint32 !=
+ resp->ap_varbindlist[0].avb_data.avb_uint32) {
+ subagentx_log_sac_warnx(sac,
+ "index %s: unexpected index value",
+ agentx_oid2string(&(sar->sar_oid)));
+ subagentx_reset(sa);
+ return -1;
+ }
+ break;
+ default:
+ subagentx_log_sac_fatalx(sac, "%s: Unsupported index type",
+ __func__);
+ }
+
+ sai->sai_cstate = SA_CSTATE_CLOSE;
+
+ subagentx_log_sac_info(sac, "index %s: deallocated",
+ agentx_oid2string(&(sai->sai_vb.avb_oid)));
+
+ if (sai->sai_dstate == SA_DSTATE_CLOSE) {
+ subagentx_index_free_finalize(sai);
+ } else if (sar->sar_cstate == SA_CSTATE_OPEN) {
+ if (subagentx_index_start(sai) == -1)
+ return -1;
+ }
+ return 0;
+}
+
+struct subagentx_object *
+subagentx_object(struct subagentx_region *sar, uint32_t oid[], size_t oidlen,
+ struct subagentx_index *sai[], size_t sailen, int implied,
+ void (*get)(struct subagentx_varbind *))
+{
+ struct subagentx_object *sao, **tsao, sao_search;
+ struct subagentx_index *lsai;
+ int ready = 1;
+ size_t i, j;
+
+ if (sar->sar_dstate == SA_DSTATE_CLOSE)
+ subagentx_log_sac_fatalx(sar->sar_sac, "%s: use after free",
+ __func__);
+ if (oidlen < 1) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen == 0",
+ __func__);
+#else
+ subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen == 0",
+ __func__);
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+ if (oidlen > SUBAGENTX_OID_MAX_LEN) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d",
+ __func__, SUBAGENTX_OID_MAX_LEN);
+#else
+ subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d",
+ __func__, SUBAGENTX_OID_MAX_LEN);
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+ if (sailen > SUBAGENTX_OID_INDEX_MAX_LEN) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sar->sar_sac, "%s: indexlen > %d",
+ __func__, SUBAGENTX_OID_INDEX_MAX_LEN);
+#else
+ subagentx_log_sac_warnx(sar->sar_sac, "%s: indexlen > %d",
+ __func__, SUBAGENTX_OID_INDEX_MAX_LEN);
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+
+ for (i = 0; i < oidlen; i++)
+ sao_search.sao_oid.aoi_id[i] = oid[i];
+ sao_search.sao_oid.aoi_idlen = oidlen;
+
+ do {
+ if (RB_FIND(sac_objects, &(sar->sar_sac->sac_objects),
+ &sao_search) != NULL) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sar->sar_sac, "%s: invalid "
+ "parent child object relationship", __func__);
+#else
+ subagentx_log_sac_warnx(sar->sar_sac, "%s: invalid "
+ "parent child object relationship", __func__);
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+ sao_search.sao_oid.aoi_idlen--;
+ } while (sao_search.sao_oid.aoi_idlen > 0);
+ sao_search.sao_oid.aoi_idlen = oidlen;
+ sao = RB_NFIND(sac_objects, &(sar->sar_sac->sac_objects), &sao_search);
+ if (sao != NULL &&
+ agentx_oid_cmp(&(sao->sao_oid), &(sao_search.sao_oid)) == 2) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sar->sar_sac, "%s: invalid parent "
+ "child object relationship", __func__);
+#else
+ subagentx_log_sac_warnx(sar->sar_sac, "%s: invalid parent "
+ "child object relationship", __func__);
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+ if (implied == 1) {
+ lsai = sai[sailen - 1];
+ if (lsai->sai_vb.avb_type == AGENTX_DATA_TYPE_OCTETSTRING) {
+ if (lsai->sai_vb.avb_data.avb_ostring.aos_slen != 0) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sar->sar_sac,
+ "%s: implied can only be used on strings "
+ "of dynamic length", __func__);
+#else
+ subagentx_log_sac_warnx(sar->sar_sac,
+ "%s: implied can only be used on strings "
+ "of dynamic length", __func__);
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+ } else if (lsai->sai_vb.avb_type == AGENTX_DATA_TYPE_OID) {
+ if (lsai->sai_vb.avb_data.avb_oid.aoi_idlen != 0) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sar->sar_sac,
+ "%s: implied can only be used on oids of "
+ "dynamic length", __func__);
+#else
+ subagentx_log_sac_warnx(sar->sar_sac,
+ "%s: implied can only be used on oids of "
+ "dynamic length", __func__);
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+ } else {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sar->sar_sac, "%s: implied "
+ "can only be set on oid and string indices",
+ __func__);
+#else
+ subagentx_log_sac_warnx(sar->sar_sac, "%s: implied can "
+ "only be set on oid and string indices", __func__);
+ errno = EINVAL;
+ return NULL;
+#endif
+ }
+ }
+
+ ready = sar->sar_cstate == SA_CSTATE_OPEN;
+ if ((sao = calloc(1, sizeof(*sao))) == NULL)
+ return NULL;
+ sao->sao_sar = sar;
+ bcopy(&(sao_search.sao_oid), &(sao->sao_oid), sizeof(sao->sao_oid));
+ for (i = 0; i < sailen; i++) {
+ sao->sao_index[i] = sai[i];
+ if (sai[i]->sai_objectlen == sai[i]->sai_objectsize) {
+ tsao = recallocarray(sai[i]->sai_object,
+ sai[i]->sai_objectlen, sai[i]->sai_objectlen + 1,
+ sizeof(*sai[i]->sai_object));
+ if (tsao == NULL) {
+ free(sao);
+ return NULL;
+ }
+ sai[i]->sai_object = tsao;
+ sai[i]->sai_objectsize = sai[i]->sai_objectlen + 1;
+ }
+ for (j = 0; j < sai[i]->sai_objectlen; j++) {
+ if (agentx_oid_cmp(&(sao->sao_oid),
+ &(sai[i]->sai_object[j]->sao_oid)) < 0) {
+ memmove(&(sai[i]->sai_object[j + 1]),
+ &(sai[i]->sai_object[j]),
+ sizeof(*(sai[i]->sai_object)) *
+ (sai[i]->sai_objectlen - j));
+ break;
+ }
+ }
+ sai[i]->sai_object[j] = sao;
+ sai[i]->sai_objectlen++;
+ if (sai[i]->sai_cstate != SA_CSTATE_OPEN)
+ ready = 0;
+ }
+ sao->sao_indexlen = sailen;
+ sao->sao_implied = implied;
+ sao->sao_timeout = 0;
+ sao->sao_lock = 0;
+ sao->sao_get = get;
+ sao->sao_cstate = SA_CSTATE_CLOSE;
+ sao->sao_dstate = SA_DSTATE_OPEN;
+
+ TAILQ_INSERT_TAIL(&(sar->sar_objects), sao, sao_sar_objects);
+ RB_INSERT(sac_objects, &(sar->sar_sac->sac_objects), sao);
+
+ if (ready)
+ subagentx_object_start(sao);
+
+ return sao;
+}
+
+static int
+subagentx_object_start(struct subagentx_object *sao)
+{
+ struct subagentx_region *sar = sao->sao_sar;
+ struct subagentx_context *sac = sar->sar_sac;
+ struct subagentx_session *sas = sac->sac_sas;
+ struct subagentx *sa = sas->sas_sa;
+ struct agentx_oid oid;
+ char oids[1024];
+ size_t i;
+ int needregister = 0;
+ uint32_t packetid;
+ uint8_t flags = AGENTX_PDU_FLAG_INSTANCE_REGISTRATION;
+
+#ifdef AGENTX_DEBUG
+ if (sar->sar_cstate != SA_CSTATE_OPEN ||
+ sao->sao_cstate != SA_CSTATE_CLOSE ||
+ sao->sao_dstate != SA_DSTATE_OPEN)
+ subagentx_log_sac_fatalx(sac,
+ "%s: unexpected object registration", __func__);
+#endif
+
+ if (sao->sao_timeout != 0)
+ needregister = 1;
+ for (i = 0; i < sao->sao_indexlen; i++) {
+ if (sao->sao_index[i]->sai_cstate != SA_CSTATE_OPEN)
+ return 0;
+ if (sao->sao_index[i]->sai_type != SAI_TYPE_DYNAMIC)
+ needregister = 1;
+ }
+ if (!needregister) {
+ sao->sao_cstate = SA_CSTATE_WAITOPEN;
+ subagentx_object_finalize(NULL, sao);
+ return 0;
+ }
+
+ bcopy(&(sao->sao_oid), &(oid), sizeof(oid));
+ for (i = 0; i < sao->sao_indexlen; i++) {
+ if (sao->sao_index[i]->sai_type == SAI_TYPE_DYNAMIC) {
+ flags = 0;
+ break;
+ }
+#ifdef AGENTX_DEBUG
+ if (sao->sao_index[i]->sai_vb.avb_type !=
+ AGENTX_DATA_TYPE_INTEGER)
+ subagentx_log_sac_fatalx(sac,
+ "%s: Unsupported allocated index type", __func__);
+#endif
+ oid.aoi_id[oid.aoi_idlen++] =
+ sao->sao_index[i]->sai_vb.avb_data.avb_uint32;
+ }
+ packetid = agentx_register(sa->sa_ax, flags, sas->sas_id,
+ SUBAGENTX_CONTEXT_CTX(sac), sao->sao_timeout,
+ AGENTX_PRIORITY_DEFAULT, 0, &oid, 0);
+ if (packetid == 0) {
+ subagentx_log_sac_warn(sac, "couldn't generate %s",
+ agentx_pdutype2string(AGENTX_PDU_TYPE_REGISTER));
+ subagentx_reset(sa);
+ return -1;
+ }
+ strlcpy(oids, agentx_oid2string(&(sao->sao_oid)), sizeof(oids));
+ subagentx_log_sac_info(sac, "object %s (%s %s): opening",
+ oids, flags ? "instance" : "region", agentx_oid2string(&(oid)));
+ sao->sao_cstate = SA_CSTATE_WAITOPEN;
+ return subagentx_request(sa, packetid, subagentx_object_finalize, sao);
+}
+
+static int
+subagentx_object_finalize(struct agentx_pdu *pdu, void *cookie)
+{
+ struct subagentx_object *sao = cookie;
+ struct subagentx_context *sac = sao->sao_sar->sar_sac;
+ struct agentx_oid oid;
+ char oids[1024];
+ size_t i;
+ uint8_t flags = 1;
+
+#ifdef AGENTX_DEBUG
+ if (sao->sao_cstate != SA_CSTATE_WAITOPEN)
+ subagentx_log_sac_fatalx(sac, "%s: not expecting object open",
+ __func__);
+#endif
+
+ if (pdu == NULL) {
+ sao->sao_cstate = SA_CSTATE_OPEN;
+ return 0;
+ }
+
+ bcopy(&(sao->sao_oid), &oid, sizeof(oid));
+ for (i = 0; i < sao->sao_indexlen; i++) {
+ if (sao->sao_index[i]->sai_type == SAI_TYPE_DYNAMIC) {
+ flags = 0;
+ break;
+ }
+#ifdef AGENTX_DEBUG
+ if (sao->sao_index[i]->sai_vb.avb_type !=
+ AGENTX_DATA_TYPE_INTEGER)
+ subagentx_log_sac_fatalx(sac,
+ "%s: Unsupported allocated index type", __func__);
+#endif
+
+ oid.aoi_id[oid.aoi_idlen++] =
+ sao->sao_index[i]->sai_vb.avb_data.avb_uint32;
+ }
+ strlcpy(oids, agentx_oid2string(&(sao->sao_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 != AGENTX_PDU_ERROR_NOERROR) {
+ sao->sao_cstate = SA_CSTATE_CLOSE;
+ subagentx_log_sac_info(sac, "object %s (%s %s): %s",
+ oids, flags ? "instance" : "region", agentx_oid2string(&oid),
+ agentx_error2string(pdu->ap_payload.ap_response.ap_error));
+ if (sao->sao_dstate == SA_DSTATE_CLOSE)
+ return subagentx_object_close_finalize(NULL, sao);
+ return 0;
+ }
+ sao->sao_cstate = SA_CSTATE_OPEN;
+ subagentx_log_sac_info(sac, "object %s (%s %s): open", oids,
+ flags ? "instance" : "region", agentx_oid2string(&oid));
+
+ if (sao->sao_dstate == SA_DSTATE_CLOSE)
+ return subagentx_object_close(sao);
+
+ return 0;
+}
+
+static int
+subagentx_object_lock(struct subagentx_object *sao)
+{
+ if (sao->sao_lock == UINT32_MAX) {
+ subagentx_log_sac_warnx(sao->sao_sar->sar_sac,
+ "%s: sao_lock == %u", __func__, UINT32_MAX);
+ return -1;
+ }
+ sao->sao_lock++;
+ return 0;
+}
+
+static void
+subagentx_object_unlock(struct subagentx_object *sao)
+{
+#ifdef AGENTX_DEBUG
+ if (sao->sao_lock == 0)
+ subagentx_log_sac_fatalx(sao->sao_sar->sar_sac,
+ "%s: sao_lock == 0", __func__);
+#endif
+ sao->sao_lock--;
+ if (sao->sao_lock == 0 && sao->sao_dstate == SA_DSTATE_CLOSE &&
+ sao->sao_cstate == SA_CSTATE_CLOSE)
+ subagentx_object_free_finalize(sao);
+}
+
+static int
+subagentx_object_close(struct subagentx_object *sao)
+{
+ struct subagentx_context *sac = sao->sao_sar->sar_sac;
+ struct subagentx_session *sas = sac->sac_sas;
+ struct subagentx *sa = sas->sas_sa;
+ struct agentx_oid oid;
+ char oids[1024];
+ size_t i;
+ int needclose = 0;
+ uint32_t packetid;
+ uint8_t flags = 1;
+
+#ifdef AGENTX_DEBUG
+ if (sao->sao_cstate != SA_CSTATE_OPEN)
+ subagentx_log_sac_fatalx(sac, "%s: unexpected object close",
+ __func__);
+#endif
+
+ for (i = 0; i < sao->sao_indexlen; i++) {
+#ifdef AGENTX_DEBUG
+ if (sao->sao_index[i]->sai_cstate != SA_CSTATE_OPEN)
+ subagentx_log_sac_fatalx(sac,
+ "%s: Object open while index closed", __func__);
+#endif
+ if (sao->sao_index[i]->sai_type != SAI_TYPE_DYNAMIC)
+ needclose = 1;
+ }
+ sao->sao_cstate = SA_CSTATE_WAITCLOSE;
+ if (sas->sas_cstate == SA_CSTATE_WAITCLOSE)
+ return 0;
+ if (!needclose) {
+ subagentx_object_close_finalize(NULL, sao);
+ return 0;
+ }
+
+ bcopy(&(sao->sao_oid), &(oid), sizeof(oid));
+ for (i = 0; i < sao->sao_indexlen; i++) {
+ if (sao->sao_index[i]->sai_type == SAI_TYPE_DYNAMIC) {
+ flags = 0;
+ break;
+ }
+#ifdef AGENTX_DEBUG
+ if (sao->sao_index[i]->sai_vb.avb_type !=
+ AGENTX_DATA_TYPE_INTEGER)
+ subagentx_log_sac_fatalx(sac,
+ "%s: Unsupported allocated index type", __func__);
+#endif
+ oid.aoi_id[oid.aoi_idlen++] =
+ sao->sao_index[i]->sai_vb.avb_data.avb_uint32;
+ }
+ packetid = agentx_unregister(sa->sa_ax, sas->sas_id,
+ SUBAGENTX_CONTEXT_CTX(sac), AGENTX_PRIORITY_DEFAULT, 0, &oid, 0);
+ if (packetid == 0) {
+ subagentx_log_sac_warn(sac, "couldn't generate %s",
+ agentx_pdutype2string(AGENTX_PDU_TYPE_UNREGISTER));
+ subagentx_reset(sa);
+ return -1;
+ }
+ strlcpy(oids, agentx_oid2string(&(sao->sao_oid)), sizeof(oids));
+ subagentx_log_sac_info(sac, "object %s (%s %s): closing",
+ oids, flags ? "instance" : "region", agentx_oid2string(&(oid)));
+ return subagentx_request(sa, packetid, subagentx_object_close_finalize,
+ sao);
+}
+
+static int
+subagentx_object_close_finalize(struct agentx_pdu *pdu, void *cookie)
+{
+ struct subagentx_object *sao = cookie;
+ struct subagentx_region *sar = sao->sao_sar;
+ struct subagentx_context *sac = sar->sar_sac;
+ struct subagentx_session *sas = sac->sac_sas;
+ struct subagentx *sa = sas->sas_sa;
+ struct agentx_oid oid;
+ char oids[1024];
+ uint8_t flags = 1;
+ size_t i;
+
+#ifdef AGENTX_DEBUG
+ if (sao->sao_cstate != SA_CSTATE_WAITCLOSE)
+ subagentx_log_sac_fatalx(sac,
+ "%s: unexpected object unregister", __func__);
+#endif
+
+ if (pdu != NULL) {
+ bcopy(&(sao->sao_oid), &(oid), sizeof(oid));
+ for (i = 0; i < sao->sao_indexlen; i++) {
+ if (sao->sao_index[i]->sai_type == SAI_TYPE_DYNAMIC) {
+ flags = 0;
+ break;
+ }
+#ifdef AGENTX_DEBUG
+ if (sao->sao_index[i]->sai_vb.avb_type !=
+ AGENTX_DATA_TYPE_INTEGER)
+ subagentx_log_sac_fatalx(sac,
+ "%s: Unsupported allocated index type",
+ __func__);
+#endif
+ oid.aoi_id[oid.aoi_idlen++] =
+ sao->sao_index[i]->sai_vb.avb_data.avb_uint32;
+ }
+ strlcpy(oids, agentx_oid2string(&(sao->sao_oid)), sizeof(oids));
+ if (pdu->ap_payload.ap_response.ap_error !=
+ AGENTX_PDU_ERROR_NOERROR) {
+ subagentx_log_sac_warnx(sac,
+ "closing object %s (%s %s): %s", oids,
+ flags ? "instance" : "region",
+ agentx_oid2string(&oid), agentx_error2string(
+ pdu->ap_payload.ap_response.ap_error));
+ subagentx_reset(sa);
+ return -1;
+ }
+ subagentx_log_sac_info(sac, "object %s (%s %s): closed", oids,
+ flags ? "instance" : "region", agentx_oid2string(&oid));
+ }
+
+ if (sao->sao_dstate == SA_DSTATE_CLOSE)
+ subagentx_object_free_finalize(sao);
+ else {
+ if (sar->sar_cstate == SA_CSTATE_OPEN)
+ if (subagentx_object_start(sao) == -1)
+ return -1;
+ }
+
+ return 0;
+}
+
+void
+subagentx_object_free(struct subagentx_object *sao)
+{
+ if (sao == NULL)
+ return;
+
+ if (sao->sao_dstate == SA_DSTATE_CLOSE)
+ subagentx_log_sac_fatalx(sao->sao_sar->sar_sac,
+ "%s: double free", __func__);
+
+ sao->sao_dstate = SA_DSTATE_CLOSE;
+
+ if (sao->sao_cstate == SA_CSTATE_OPEN) {
+ if (subagentx_object_close(sao) == -1)
+ return;
+ }
+ if (sao->sao_cstate == SA_CSTATE_CLOSE)
+ subagentx_object_free_finalize(sao);
+}
+
+static void
+subagentx_object_free_finalize(struct subagentx_object *sao)
+{
+#ifdef AGENTX_DEBUG
+ struct subagentx *sa = sao->sao_sar->sar_sac->sac_sas->sas_sa;
+#endif
+ size_t i, j;
+ int found;
+
+#ifdef AGENTX_DEBUG
+ if (sao->sao_dstate != SA_DSTATE_CLOSE)
+ subagentx_log_sac_fatalx(sao->sao_sar->sar_sac,
+ "%s: unexpected free", __func__);
+#endif
+
+ if (sao->sao_lock != 0) {
+#ifdef AGENTX_DEBUG
+ if (TAILQ_EMPTY(&(sa->sa_getreqs)))
+ subagentx_log_sac_fatalx(sao->sao_sar->sar_sac,
+ "%s: %s sao_lock == %u", __func__,
+ agentx_oid2string(&(sao->sao_oid)), sao->sao_lock);
+#endif
+ return;
+ }
+
+ RB_REMOVE(sac_objects, &(sao->sao_sar->sar_sac->sac_objects), sao);
+ TAILQ_REMOVE(&(sao->sao_sar->sar_objects), sao, sao_sar_objects);
+
+ for (i = 0; i < sao->sao_indexlen; i++) {
+ found = 0;
+ for (j = 0; j < sao->sao_index[i]->sai_objectlen; j++) {
+ if (sao->sao_index[i]->sai_object[j] == sao)
+ found = 1;
+ if (found && j + 1 != sao->sao_index[i]->sai_objectlen)
+ sao->sao_index[i]->sai_object[j] =
+ sao->sao_index[i]->sai_object[j + 1];
+ }
+#ifdef AGENTX_DEBUG
+ if (!found)
+ subagentx_log_sac_fatalx(sao->sao_sar->sar_sac,
+ "%s: object not found in index", __func__);
+#endif
+ sao->sao_index[i]->sai_objectlen--;
+ if (sao->sao_index[i]->sai_dstate == SA_DSTATE_CLOSE)
+ subagentx_index_free_finalize(sao->sao_index[i]);
+ }
+
+ free(sao);
+}
+
+static void
+subagentx_object_reset(struct subagentx_object *sao)
+{
+ sao->sao_cstate = SA_CSTATE_CLOSE;
+
+ if (sao->sao_dstate == SA_DSTATE_CLOSE)
+ subagentx_object_free_finalize(sao);
+}
+
+static int
+subagentx_object_cmp(struct subagentx_object *o1, struct subagentx_object *o2)
+{
+ return agentx_oid_cmp(&(o1->sao_oid), &(o2->sao_oid));
+}
+
+static int
+subagentx_object_implied(struct subagentx_object *sao,
+ struct subagentx_index *sai)
+{
+ size_t i = 0;
+
+ for (i = 0; i < sao->sao_indexlen; i++) {
+ if (sao->sao_index[i] == sai) {
+ if (sai->sai_vb.avb_data.avb_ostring.aos_slen != 0)
+ return 1;
+ else if (i == sao->sao_indexlen - 1)
+ return sao->sao_implied;
+ return 0;
+ }
+ }
+#ifdef AGENTX_DEBUG
+ subagentx_log_sac_fatalx(sao->sao_sar->sar_sac, "%s: unsupported index",
+ __func__);
+#endif
+ return 0;
+}
+
+static void
+subagentx_get_start(struct subagentx_context *sac, struct agentx_pdu *pdu)
+{
+ struct subagentx_session *sas = sac->sac_sas;
+ struct subagentx *sa = sas->sas_sa;
+ struct subagentx_get *sag, tsag;
+ struct agentx_pdu_searchrangelist *srl;
+ char *logmsg = NULL;
+ size_t i, j;
+ int fail = 0;
+
+ if ((sag = calloc(1, sizeof(*sag))) == NULL) {
+ tsag.sag_sessionid = pdu->ap_header.aph_sessionid;
+ tsag.sag_transactionid = pdu->ap_header.aph_transactionid;
+ tsag.sag_packetid = pdu->ap_header.aph_packetid;
+ tsag.sag_context_default = sac->sac_name_default;
+ tsag.sag_fd = sac->sac_sas->sas_sa->sa_fd;
+ subagentx_log_sag_warn(&tsag, "Couldn't parse request");
+ subagentx_reset(sa);
+ return;
+ }
+
+ sag->sag_sessionid = pdu->ap_header.aph_sessionid;
+ sag->sag_transactionid = pdu->ap_header.aph_transactionid;
+ sag->sag_packetid = pdu->ap_header.aph_packetid;
+ sag->sag_context_default = sac->sac_name_default;
+ sag->sag_fd = sac->sac_sas->sas_sa->sa_fd;
+ if (!sac->sac_name_default) {
+ sag->sag_context.aos_string =
+ (unsigned char *)strdup((char *)sac->sac_name.aos_string);
+ if (sag->sag_context.aos_string == NULL) {
+ subagentx_log_sag_warn(sag, "Couldn't parse request");
+ free(sag);
+ subagentx_reset(sa);
+ return;
+ }
+ }
+ sag->sag_context.aos_slen = sac->sac_name.aos_slen;
+ sag->sag_type = pdu->ap_header.aph_type;
+ sag->sag_sac = sac;
+ TAILQ_INSERT_TAIL(&(sa->sa_getreqs), sag, sag_sa_getreqs);
+ if (sag->sag_type == AGENTX_PDU_TYPE_GET ||
+ sag->sag_type == AGENTX_PDU_TYPE_GETNEXT) {
+ srl = &(pdu->ap_payload.ap_srl);
+ sag->sag_nvarbind = srl->ap_nsr;
+ } else {
+ sag->sag_nonrep = pdu->ap_payload.ap_getbulk.ap_nonrep;
+ sag->sag_maxrep = pdu->ap_payload.ap_getbulk.ap_maxrep;
+ srl = &(pdu->ap_payload.ap_getbulk.ap_srl);
+ sag->sag_nvarbind = ((srl->ap_nsr - sag->sag_nonrep) *
+ sag->sag_maxrep) + sag->sag_nonrep;
+ }
+
+ if ((sag->sag_varbind = calloc(sag->sag_nvarbind,
+ sizeof(*(sag->sag_varbind)))) == NULL) {
+ subagentx_log_sag_warn(sag, "Couldn't parse request");
+ subagentx_get_free(sag);
+ subagentx_reset(sa);
+ 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 < sag->sag_nonrep ||
+ sag->sag_type != AGENTX_PDU_TYPE_GETBULK)
+ j = i;
+ else if (sag->sag_maxrep == 0)
+ break;
+ else
+ j = (sag->sag_maxrep * i) + sag->sag_nonrep;
+ bcopy(&(srl->ap_sr[i].asr_start),
+ &(sag->sag_varbind[j].sav_vb.avb_oid),
+ sizeof(srl->ap_sr[i].asr_start));
+ bcopy(&(srl->ap_sr[i].asr_start),
+ &(sag->sag_varbind[j].sav_start),
+ sizeof(srl->ap_sr[i].asr_start));
+ bcopy(&(srl->ap_sr[i].asr_stop),
+ &(sag->sag_varbind[j].sav_end),
+ sizeof(srl->ap_sr[i].asr_stop));
+ sag->sag_varbind[j].sav_initialized = 1;
+ sag->sag_varbind[j].sav_sag = sag;
+ sag->sag_varbind[j].sav_include =
+ srl->ap_sr[i].asr_start.aoi_include;
+ if (j == 0)
+ fail |= subagentx_strcat(&logmsg, " {");
+ else
+ fail |= subagentx_strcat(&logmsg, ",{");
+ fail |= subagentx_strcat(&logmsg,
+ agentx_oid2string(&(srl->ap_sr[i].asr_start)));
+ if (srl->ap_sr[i].asr_start.aoi_include)
+ fail |= subagentx_strcat(&logmsg, " (inclusive)");
+ if (srl->ap_sr[i].asr_stop.aoi_idlen != 0) {
+ fail |= subagentx_strcat(&logmsg, " - ");
+ fail |= subagentx_strcat(&logmsg,
+ agentx_oid2string(&(srl->ap_sr[i].asr_stop)));
+ }
+ fail |= subagentx_strcat(&logmsg, "}");
+ if (fail) {
+ subagentx_log_sag_warn(sag, "Couldn't parse request");
+ free(logmsg);
+ subagentx_get_free(sag);
+ subagentx_reset(sa);
+ return;
+ }
+ }
+
+ subagentx_log_sag_debug(sag, "%s:%s",
+ agentx_pdutype2string(sag->sag_type), logmsg);
+ free(logmsg);
+
+ for (i = 0; i < srl->ap_nsr; i++) {
+ if (i < sag->sag_nonrep ||
+ sag->sag_type != AGENTX_PDU_TYPE_GETBULK)
+ j = i;
+ else if (sag->sag_maxrep == 0)
+ break;
+ else
+ j = (sag->sag_maxrep * i) + sag->sag_nonrep;
+ subagentx_varbind_start(&(sag->sag_varbind[j]));
+ }
+}
+
+static void
+subagentx_get_finalize(struct subagentx_get *sag)
+{
+ struct subagentx_context *sac = sag->sag_sac;
+ struct subagentx_session *sas = sac->sac_sas;
+ struct subagentx *sa = sas->sas_sa;
+ size_t i, j, nvarbind = 0;
+ uint16_t error = 0, index = 0;
+ struct agentx_varbind *vbl;
+ char *logmsg = NULL;
+ int fail = 0;
+
+ for (i = 0; i < sag->sag_nvarbind; i++) {
+ if (sag->sag_varbind[i].sav_initialized) {
+ if (sag->sag_varbind[i].sav_vb.avb_type == 0)
+ return;
+ nvarbind++;
+ }
+ }
+
+ if (sag->sag_sac == NULL) {
+ subagentx_get_free(sag);
+ return;
+ }
+
+ if ((vbl = calloc(nvarbind, sizeof(*vbl))) == NULL) {
+ subagentx_log_sag_warn(sag, "Couldn't parse request");
+ subagentx_get_free(sag);
+ subagentx_reset(sa);
+ return;
+ }
+ for (i = 0, j = 0; i < sag->sag_nvarbind; i++) {
+ if (sag->sag_varbind[i].sav_initialized) {
+ memcpy(&(vbl[j]), &(sag->sag_varbind[i].sav_vb),
+ sizeof(*vbl));
+ if (error == 0 && sag->sag_varbind[i].sav_error !=
+ AGENTX_PDU_ERROR_NOERROR) {
+ error = sag->sag_varbind[i].sav_error;
+ index = j + 1;
+ }
+ if (j == 0)
+ fail |= subagentx_strcat(&logmsg, " {");
+ else
+ fail |= subagentx_strcat(&logmsg, ",{");
+ fail |= subagentx_strcat(&logmsg,
+ agentx_varbind2string(&(vbl[j])));
+ if (sag->sag_varbind[i].sav_error !=
+ AGENTX_PDU_ERROR_NOERROR) {
+ fail |= subagentx_strcat(&logmsg, "(");
+ fail |= subagentx_strcat(&logmsg,
+ agentx_error2string(
+ sag->sag_varbind[i].sav_error));
+ fail |= subagentx_strcat(&logmsg, ")");
+ }
+ fail |= subagentx_strcat(&logmsg, "}");
+ if (fail) {
+ subagentx_log_sag_warn(sag,
+ "Couldn't parse request");
+ free(logmsg);
+ subagentx_get_free(sag);
+ return;
+ }
+ j++;
+ }
+ }
+ subagentx_log_sag_debug(sag, "response:%s", logmsg);
+ free(logmsg);
+
+ if (agentx_response(sa->sa_ax, sas->sas_id, sag->sag_transactionid,
+ sag->sag_packetid, SUBAGENTX_CONTEXT_CTX(sac), 0, error, index,
+ vbl, nvarbind) == -1) {
+ subagentx_log_sag_warn(sag, "Couldn't parse request");
+ subagentx_reset(sa);
+ } else
+ subagentx_wantwrite(sa, sa->sa_fd);
+ free(vbl);
+ subagentx_get_free(sag);
+}
+
+void
+subagentx_get_free(struct subagentx_get *sag)
+{
+ struct subagentx_varbind *sav;
+ struct subagentx_object *sao;
+ struct subagentx *sa = sag->sag_sac->sac_sas->sas_sa;
+ struct subagentx_varbind_index *index;
+ size_t i, j;
+
+ if (sag->sag_sac != NULL)
+ TAILQ_REMOVE(&(sa->sa_getreqs), sag, sag_sa_getreqs);
+
+ for (i = 0; i < sag->sag_nvarbind; i++) {
+ sav = &(sag->sag_varbind[i]);
+ for (j = 0; sav->sav_sao != NULL &&
+ j < sav->sav_sao->sao_indexlen; j++) {
+ sao = sav->sav_sao;
+ index = &(sav->sav_index[j]);
+ if (sao->sao_index[j]->sai_vb.avb_type ==
+ AGENTX_DATA_TYPE_OCTETSTRING)
+ free(index->sav_idata.avb_ostring.aos_string);
+ }
+ agentx_varbind_free(&(sag->sag_varbind[i].sav_vb));
+ }
+
+ free(sag->sag_context.aos_string);
+ free(sag->sag_varbind);
+ free(sag);
+}
+
+static void
+subagentx_varbind_start(struct subagentx_varbind *sav)
+{
+ struct subagentx_get *sag = sav->sav_sag;
+ struct subagentx_context *sac = sag->sag_sac;
+ struct subagentx_object *sao, sao_search;
+ struct subagentx_varbind_index *index;
+ struct subagentx_index *sai;
+ struct agentx_oid *oid;
+ union agentx_data *data;
+ struct in_addr *ipaddress;
+ unsigned char *ipbytes;
+ size_t i, j, k;
+ int overflow = 0, dynamic;
+
+#ifdef AGENTX_DEBUG
+ if (!sav->sav_initialized)
+ subagentx_log_sag_fatalx(sav->sav_sag,
+ "%s: sav_initialized not set", __func__);
+#endif
+
+ bcopy(&(sav->sav_vb.avb_oid), &(sao_search.sao_oid),
+ sizeof(sao_search.sao_oid));
+
+ do {
+ sao = RB_FIND(sac_objects, &(sac->sac_objects), &sao_search);
+ if (sao_search.sao_oid.aoi_idlen > 0)
+ sao_search.sao_oid.aoi_idlen--;
+ } while (sao == NULL && sao_search.sao_oid.aoi_idlen > 0);
+ if (sao == NULL || sao->sao_cstate != SA_CSTATE_OPEN) {
+ sav->sav_include = 1;
+ if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET) {
+ subagentx_varbind_nosuchobject(sav);
+ return;
+ }
+ bcopy(&(sav->sav_vb.avb_oid), &(sao_search.sao_oid),
+ sizeof(sao_search.sao_oid));
+ sao = RB_NFIND(sac_objects, &(sac->sac_objects), &sao_search);
+getnext:
+ while (sao != NULL && sao->sao_cstate != SA_CSTATE_OPEN)
+ sao = RB_NEXT(sac_objects, &(sac->sac_objects), sao);
+ if (sao == NULL) {
+ subagentx_varbind_endofmibview(sav);
+ return;
+ }
+ bcopy(&(sao->sao_oid), &(sav->sav_vb.avb_oid),
+ sizeof(sao->sao_oid));
+ }
+ sav->sav_sao = sao;
+ sav->sav_indexlen = sao->sao_indexlen;
+ if (subagentx_object_lock(sao) == -1) {
+ subagentx_varbind_error_type(sav,
+ AGENTX_PDU_ERROR_PROCESSINGERROR, 1);
+ return;
+ }
+
+ oid = &(sav->sav_vb.avb_oid);
+ if (sao->sao_indexlen == 0) {
+ if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET) {
+ if (oid->aoi_idlen != sao->sao_oid.aoi_idlen + 1 ||
+ oid->aoi_id[oid->aoi_idlen - 1] != 0) {
+ subagentx_varbind_nosuchinstance(sav);
+ return;
+ }
+ } else {
+ if (oid->aoi_idlen == sao->sao_oid.aoi_idlen) {
+ oid->aoi_id[oid->aoi_idlen++] = 0;
+ sav->sav_include = 1;
+ } else {
+ sav->sav_sao = NULL;
+ subagentx_object_unlock(sao);
+ sao = RB_NEXT(sac_objects, &(sac->sac_objects),
+ sao);
+ goto getnext;
+ }
+ }
+ }
+ j = sao->sao_oid.aoi_idlen;
+/*
+ * We can't trust what the client gives us, so sometimes we need to map it to
+ * index type.
+ * - AGENTX_PDU_TYPE_GET: we always return AGENTX_DATA_TYPE_NOSUCHINSTANCE
+ * - AGENTX_PDU_TYPE_GETNEXT:
+ * - Missing OID digits to match indices will result in the indices to be NUL-
+ * initialized and the request type will be set to
+ * SUBAGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE
+ * - An overflow can happen on AGENTX_DATA_TYPE_OCTETSTRING and
+ * AGENTX_DATA_TYPE_IPADDRESS. This results in request type being set to
+ * SUBAGENTX_REQUEST_TYPE_GETNEXT and will set the index to its maximum
+ * value:
+ * - AGENTX_DATA_TYPE_INTEGER: UINT32_MAX
+ * - AGENTX_DATA_TYPE_OCTETSTRING: aos_slen = UINT32_MAX and
+ * aos_string = NULL
+ * - AGENTX_DATA_TYPE_OID: aoi_idlen = UINT32_MAX and aoi_id[x] = UINT32_MAX
+ * - AGENTX_DATA_TYPE_IPADDRESS: 255.255.255.255
+ */
+ for (dynamic = 0, i = 0; i < sao->sao_indexlen; i++) {
+ index = &(sav->sav_index[i]);
+ index->sav_sai = sao->sao_index[i];
+ data = &(index->sav_idata);
+ if (sao->sao_index[i]->sai_type == SAI_TYPE_DYNAMIC)
+ dynamic = 1;
+ if (j >= sav->sav_vb.avb_oid.aoi_idlen && !overflow &&
+ sao->sao_index[i]->sai_type == SAI_TYPE_DYNAMIC)
+ continue;
+ switch (sao->sao_index[i]->sai_vb.avb_type) {
+ case AGENTX_DATA_TYPE_INTEGER:
+/* Dynamic index: normal copy paste */
+ if (sao->sao_index[i]->sai_type == SAI_TYPE_DYNAMIC) {
+ data->avb_uint32 = overflow ?
+ UINT32_MAX : sav->sav_vb.avb_oid.aoi_id[j];
+ j++;
+ index->sav_idatacomplete = 1;
+ break;
+ }
+ sai = sao->sao_index[i];
+/* With a GET-request we need an exact match */
+ if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET) {
+ if (sai->sai_vb.avb_data.avb_uint32 !=
+ sav->sav_vb.avb_oid.aoi_id[j]) {
+ subagentx_varbind_nosuchinstance(sav);
+ return;
+ }
+ index->sav_idatacomplete = 1;
+ j++;
+ break;
+ }
+/* A higher value automatically moves us to the next value */
+ if (overflow ||
+ sav->sav_vb.avb_oid.aoi_id[j] >
+ sai->sai_vb.avb_data.avb_uint32) {
+/* If we're !dynamic up until now the rest of the oid doesn't matter */
+ if (!dynamic) {
+ subagentx_varbind_endofmibview(sav);
+ return;
+ }
+/*
+ * Else we just pick the max value and make sure we don't return
+ * SUBAGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE
+ */
+ data->avb_uint32 = UINT32_MAX;
+ index->sav_idatacomplete = 1;
+ overflow = 1;
+ j++;
+ break;
+/*
+ * A lower value automatically moves to the set value and counts as a short oid
+ */
+ } else if (sav->sav_vb.avb_oid.aoi_id[j] <
+ sai->sai_vb.avb_data.avb_uint32) {
+ data->avb_uint32 =
+ sai->sai_vb.avb_data.avb_uint32;
+ j = sav->sav_vb.avb_oid.aoi_idlen;
+ break;
+ }
+/* Normal match, except we already matched overflow at higher value */
+ data->avb_uint32 = sav->sav_vb.avb_oid.aoi_id[j];
+ j++;
+ index->sav_idatacomplete = 1;
+ break;
+ case AGENTX_DATA_TYPE_OCTETSTRING:
+ if (!subagentx_object_implied(sao, index->sav_sai)) {
+ if (overflow || sav->sav_vb.avb_oid.aoi_id[j] >
+ SUBAGENTX_OID_MAX_LEN -
+ sav->sav_vb.avb_oid.aoi_idlen) {
+ overflow = 1;
+ data->avb_ostring.aos_slen = UINT32_MAX;
+ index->sav_idatacomplete = 1;
+ continue;
+ }
+ data->avb_ostring.aos_slen =
+ sav->sav_vb.avb_oid.aoi_id[j++];
+ } else {
+ if (overflow) {
+ data->avb_ostring.aos_slen = UINT32_MAX;
+ index->sav_idatacomplete = 1;
+ continue;
+ }
+ data->avb_ostring.aos_slen =
+ sav->sav_vb.avb_oid.aoi_idlen - j;
+ }
+ data->avb_ostring.aos_string =
+ calloc(data->avb_ostring.aos_slen + 1, 1);
+ if (data->avb_ostring.aos_string == NULL) {
+ subagentx_log_sag_warn(sag,
+ "Failed to bind string index");
+ subagentx_varbind_error_type(sav,
+ AGENTX_PDU_ERROR_PROCESSINGERROR, 1);
+ return;
+ }
+ for (k = 0; k < data->avb_ostring.aos_slen; k++, j++) {
+ if (!overflow &&
+ j == sav->sav_vb.avb_oid.aoi_idlen)
+ break;
+
+ if (sav->sav_vb.avb_oid.aoi_id[j] > 255)
+ overflow = 1;
+
+ data->avb_ostring.aos_string[k] = overflow ?
+ 0xff : sav->sav_vb.avb_oid.aoi_id[j];
+ }
+ if (k == data->avb_ostring.aos_slen)
+ index->sav_idatacomplete = 1;
+ break;
+ case AGENTX_DATA_TYPE_OID:
+ if (!subagentx_object_implied(sao, index->sav_sai)) {
+ if (overflow || sav->sav_vb.avb_oid.aoi_id[j] >
+ SUBAGENTX_OID_MAX_LEN -
+ sav->sav_vb.avb_oid.aoi_idlen) {
+ overflow = 1;
+ data->avb_oid.aoi_idlen = UINT32_MAX;
+ index->sav_idatacomplete = 1;
+ continue;
+ }
+ data->avb_oid.aoi_idlen =
+ sav->sav_vb.avb_oid.aoi_id[j++];
+ } else {
+ if (overflow) {
+ data->avb_oid.aoi_idlen = UINT32_MAX;
+ index->sav_idatacomplete = 1;
+ continue;
+ }
+ data->avb_oid.aoi_idlen =
+ sav->sav_vb.avb_oid.aoi_idlen - j;
+ }
+ for (k = 0; k < data->avb_oid.aoi_idlen; k++, j++) {
+ if (!overflow &&
+ j == sav->sav_vb.avb_oid.aoi_idlen) {
+ data->avb_oid.aoi_id[k] = 0;
+ continue;
+ }
+ data->avb_oid.aoi_id[k] = overflow ?
+ UINT32_MAX : sav->sav_vb.avb_oid.aoi_id[j];
+ }
+ if (j <= sav->sav_vb.avb_oid.aoi_idlen)
+ index->sav_idatacomplete = 1;
+ break;
+ case AGENTX_DATA_TYPE_IPADDRESS:
+ ipaddress = calloc(1, sizeof(*ipaddress));
+ if (ipaddress == NULL) {
+ subagentx_log_sag_warn(sag,
+ "Failed to bind ipaddress index");
+ subagentx_varbind_error_type(sav,
+ AGENTX_PDU_ERROR_PROCESSINGERROR, 1);
+ return;
+ }
+ ipbytes = (unsigned char *)ipaddress;
+ for (k = 0; k < 4; k++, j++) {
+ if (!overflow &&
+ j == sav->sav_vb.avb_oid.aoi_idlen)
+ break;
+
+ if (sav->sav_vb.avb_oid.aoi_id[j] > 255)
+ overflow = 1;
+
+ ipbytes[k] = overflow ? 255 :
+ sav->sav_vb.avb_oid.aoi_id[j];
+ }
+ if (j <= sav->sav_vb.avb_oid.aoi_idlen)
+ index->sav_idatacomplete = 1;
+ data->avb_ostring.aos_slen = sizeof(*ipaddress);
+ data->avb_ostring.aos_string =
+ (unsigned char *)ipaddress;
+ break;
+ default:
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sag,
+ "%s: unexpected index type", __func__);
+#else
+ subagentx_log_sag_warnx(sag,
+ "%s: unexpected index type", __func__);
+ subagentx_varbind_error_type(sav,
+ AGENTX_PDU_ERROR_PROCESSINGERROR, 1);
+ return;
+#endif
+ }
+ }
+ if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET) {
+ if ((sao->sao_indexlen > 0 &&
+ !sav->sav_index[sao->sao_indexlen - 1].sav_idatacomplete) ||
+ j != sav->sav_vb.avb_oid.aoi_idlen || overflow) {
+ subagentx_varbind_nosuchinstance(sav);
+ return;
+ }
+ }
+
+ if (overflow || j > sav->sav_vb.avb_oid.aoi_idlen)
+ sav->sav_include = 0;
+
+/*
+ * SUBAGENTX_REQUEST_TYPE_GETNEXT request can !dynamic objects can just move to
+ * the next object
+ */
+ if (subagentx_varbind_request(sav) == SUBAGENTX_REQUEST_TYPE_GETNEXT &&
+ !dynamic) {
+ subagentx_varbind_endofmibview(sav);
+ return;
+ }
+
+ sao->sao_get(sav);
+}
+
+void
+subagentx_varbind_integer(struct subagentx_varbind *sav, uint32_t value)
+{
+ sav->sav_vb.avb_type = AGENTX_DATA_TYPE_INTEGER;
+ sav->sav_vb.avb_data.avb_uint32 = value;
+
+ subagentx_varbind_finalize(sav);
+}
+
+void
+subagentx_varbind_string(struct subagentx_varbind *sav, const char *value)
+{
+ subagentx_varbind_nstring(sav, (const unsigned char *)value,
+ strlen(value));
+}
+
+void
+subagentx_varbind_nstring(struct subagentx_varbind *sav,
+ const unsigned char *value, size_t slen)
+{
+ sav->sav_vb.avb_data.avb_ostring.aos_string = malloc(slen);
+ if (sav->sav_vb.avb_data.avb_ostring.aos_string == NULL) {
+ subagentx_log_sag_warn(sav->sav_sag, "Couldn't bind string");
+ subagentx_varbind_error_type(sav,
+ AGENTX_PDU_ERROR_PROCESSINGERROR, 1);
+ return;
+ }
+ sav->sav_vb.avb_type = AGENTX_DATA_TYPE_OCTETSTRING;
+ memcpy(sav->sav_vb.avb_data.avb_ostring.aos_string, value, slen);
+ sav->sav_vb.avb_data.avb_ostring.aos_slen = slen;
+
+ subagentx_varbind_finalize(sav);
+}
+
+void
+subagentx_varbind_printf(struct subagentx_varbind *sav, const char *fmt, ...)
+{
+ va_list ap;
+ int r;
+
+ sav->sav_vb.avb_type = AGENTX_DATA_TYPE_OCTETSTRING;
+ va_start(ap, fmt);
+ r = vasprintf((char **)&(sav->sav_vb.avb_data.avb_ostring.aos_string),
+ fmt, ap);
+ va_end(ap);
+ if (r == -1) {
+ sav->sav_vb.avb_data.avb_ostring.aos_string = NULL;
+ subagentx_log_sag_warn(sav->sav_sag, "Couldn't bind string");
+ subagentx_varbind_error_type(sav,
+ AGENTX_PDU_ERROR_PROCESSINGERROR, 1);
+ return;
+ }
+ sav->sav_vb.avb_data.avb_ostring.aos_slen = r;
+
+ subagentx_varbind_finalize(sav);
+}
+
+void
+subagentx_varbind_null(struct subagentx_varbind *sav)
+{
+ sav->sav_vb.avb_type = AGENTX_DATA_TYPE_NULL;
+
+ subagentx_varbind_finalize(sav);
+}
+
+void
+subagentx_varbind_oid(struct subagentx_varbind *sav, const uint32_t oid[],
+ size_t oidlen)
+{
+ size_t i;
+
+ sav->sav_vb.avb_type = AGENTX_DATA_TYPE_OID;
+
+ for (i = 0; i < oidlen; i++)
+ sav->sav_vb.avb_data.avb_oid.aoi_id[i] = oid[i];
+ sav->sav_vb.avb_data.avb_oid.aoi_idlen = oidlen;
+
+ subagentx_varbind_finalize(sav);
+}
+
+void
+subagentx_varbind_object(struct subagentx_varbind *sav,
+ struct subagentx_object *sao)
+{
+ subagentx_varbind_oid(sav, sao->sao_oid.aoi_id,
+ sao->sao_oid.aoi_idlen);
+}
+
+void
+subagentx_varbind_index(struct subagentx_varbind *sav,
+ struct subagentx_index *sai)
+{
+ subagentx_varbind_oid(sav, sai->sai_vb.avb_oid.aoi_id,
+ sai->sai_vb.avb_oid.aoi_idlen);
+}
+
+
+void
+subagentx_varbind_ipaddress(struct subagentx_varbind *sav,
+ const struct in_addr *value)
+{
+ sav->sav_vb.avb_type = AGENTX_DATA_TYPE_IPADDRESS;
+ sav->sav_vb.avb_data.avb_ostring.aos_string = malloc(4);
+ if (sav->sav_vb.avb_data.avb_ostring.aos_string == NULL) {
+ subagentx_log_sag_warn(sav->sav_sag, "Couldn't bind ipaddress");
+ subagentx_varbind_error_type(sav,
+ AGENTX_PDU_ERROR_PROCESSINGERROR, 1);
+ return;
+ }
+ memcpy(sav->sav_vb.avb_data.avb_ostring.aos_string, value, 4);
+ sav->sav_vb.avb_data.avb_ostring.aos_slen = 4;
+
+ subagentx_varbind_finalize(sav);
+}
+
+void
+subagentx_varbind_counter32(struct subagentx_varbind *sav, uint32_t value)
+{
+ sav->sav_vb.avb_type = AGENTX_DATA_TYPE_COUNTER32;
+ sav->sav_vb.avb_data.avb_uint32 = value;
+
+ subagentx_varbind_finalize(sav);
+}
+
+void
+subagentx_varbind_gauge32(struct subagentx_varbind *sav, uint32_t value)
+{
+ sav->sav_vb.avb_type = AGENTX_DATA_TYPE_GAUGE32;
+ sav->sav_vb.avb_data.avb_uint32 = value;
+
+ subagentx_varbind_finalize(sav);
+}
+
+void
+subagentx_varbind_timeticks(struct subagentx_varbind *sav, uint32_t value)
+{
+ sav->sav_vb.avb_type = AGENTX_DATA_TYPE_TIMETICKS;
+ sav->sav_vb.avb_data.avb_uint32 = value;
+
+ subagentx_varbind_finalize(sav);
+}
+
+void
+subagentx_varbind_opaque(struct subagentx_varbind *sav, const char *string,
+ size_t strlen)
+{
+ sav->sav_vb.avb_type = AGENTX_DATA_TYPE_OPAQUE;
+ sav->sav_vb.avb_data.avb_ostring.aos_string = malloc(strlen);
+ if (sav->sav_vb.avb_data.avb_ostring.aos_string == NULL) {
+ subagentx_log_sag_warn(sav->sav_sag, "Couldn't bind opaque");
+ subagentx_varbind_error_type(sav,
+ AGENTX_PDU_ERROR_PROCESSINGERROR, 1);
+ return;
+ }
+ memcpy(sav->sav_vb.avb_data.avb_ostring.aos_string, string, strlen);
+ sav->sav_vb.avb_data.avb_ostring.aos_slen = strlen;
+
+ subagentx_varbind_finalize(sav);
+}
+
+void
+subagentx_varbind_counter64(struct subagentx_varbind *sav, uint64_t value)
+{
+ sav->sav_vb.avb_type = AGENTX_DATA_TYPE_COUNTER64;
+ sav->sav_vb.avb_data.avb_uint64 = value;
+
+ subagentx_varbind_finalize(sav);
+}
+
+void
+subagentx_varbind_notfound(struct subagentx_varbind *sav)
+{
+ if (sav->sav_indexlen == 0) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag, "%s invalid call",
+ __func__);
+#else
+ subagentx_log_sag_warnx(sav->sav_sag, "%s invalid call",
+ __func__);
+ subagentx_varbind_error_type(sav,
+ AGENTX_PDU_ERROR_GENERR, 1);
+#endif
+ } else if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET)
+ subagentx_varbind_nosuchinstance(sav);
+ else
+ subagentx_varbind_endofmibview(sav);
+}
+
+void
+subagentx_varbind_error(struct subagentx_varbind *sav)
+{
+ subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 1);
+}
+
+static void
+subagentx_varbind_error_type(struct subagentx_varbind *sav,
+ enum agentx_pdu_error error, int done)
+{
+ if (sav->sav_error == AGENTX_PDU_ERROR_NOERROR) {
+ sav->sav_error = error;
+ }
+
+ if (done) {
+ sav->sav_vb.avb_type = AGENTX_DATA_TYPE_NULL;
+
+ subagentx_varbind_finalize(sav);
+ }
+}
+
+static void
+subagentx_varbind_finalize(struct subagentx_varbind *sav)
+{
+ struct subagentx_get *sag = sav->sav_sag;
+ struct agentx_oid oid;
+ union agentx_data *data;
+ size_t i, j;
+ int cmp;
+
+ if (sav->sav_error != AGENTX_PDU_ERROR_NOERROR) {
+ bcopy(&(sav->sav_start), &(sav->sav_vb.avb_oid),
+ sizeof(sav->sav_start));
+ goto done;
+ }
+ bcopy(&(sav->sav_sao->sao_oid), &oid, sizeof(oid));
+ if (sav->sav_indexlen == 0)
+ agentx_oid_add(&oid, 0);
+ for (i = 0; i < sav->sav_indexlen; i++) {
+ data = &(sav->sav_index[i].sav_idata);
+ switch (sav->sav_index[i].sav_sai->sai_vb.avb_type) {
+ case AGENTX_DATA_TYPE_INTEGER:
+ if (agentx_oid_add(&oid, data->avb_uint32) == -1)
+ goto fail;
+ break;
+ case AGENTX_DATA_TYPE_OCTETSTRING:
+ if (!subagentx_object_implied(sav->sav_sao,
+ sav->sav_index[i].sav_sai)) {
+ if (agentx_oid_add(&oid,
+ data->avb_ostring.aos_slen) == -1)
+ goto fail;
+ }
+ for (j = 0; j < data->avb_ostring.aos_slen; j++) {
+ if (agentx_oid_add(&oid,
+ (uint8_t)data->avb_ostring.aos_string[j]) ==
+ -1)
+ goto fail;
+ }
+ break;
+ case AGENTX_DATA_TYPE_OID:
+ if (!subagentx_object_implied(sav->sav_sao,
+ sav->sav_index[i].sav_sai)) {
+ if (agentx_oid_add(&oid,
+ data->avb_oid.aoi_idlen) == -1)
+ goto fail;
+ }
+ for (j = 0; j < data->avb_oid.aoi_idlen; j++) {
+ if (agentx_oid_add(&oid,
+ data->avb_oid.aoi_id[j]) == -1)
+ goto fail;
+ }
+ break;
+ case AGENTX_DATA_TYPE_IPADDRESS:
+ for (j = 0; j < 4; j++) {
+ if (agentx_oid_add(&oid,
+ data->avb_ostring.aos_string == NULL ? 0 :
+ (uint8_t)data->avb_ostring.aos_string[j]) ==
+ -1)
+ goto fail;
+ }
+ break;
+ default:
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sag,
+ "%s: unsupported index type", __func__);
+#else
+ bcopy(&(sav->sav_start), &(sav->sav_vb.avb_oid),
+ sizeof(sav->sav_start));
+ sav->sav_error = AGENTX_PDU_ERROR_PROCESSINGERROR;
+ subagentx_object_unlock(sav->sav_sao);
+ subagentx_get_finalize(sav->sav_sag);
+ return;
+#endif
+ }
+ }
+ cmp = agentx_oid_cmp(&(sav->sav_vb.avb_oid), &oid);
+ if ((subagentx_varbind_request(sav) == SUBAGENTX_REQUEST_TYPE_GETNEXT &&
+ cmp >= 0) || cmp > 0) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sag, "indices not incremented");
+#else
+ subagentx_log_sag_warnx(sag, "indices not incremented");
+ bcopy(&(sav->sav_start), &(sav->sav_vb.avb_oid),
+ sizeof(sav->sav_start));
+ sav->sav_error = AGENTX_PDU_ERROR_GENERR;
+#endif
+ } else
+ bcopy(&oid, &(sav->sav_vb.avb_oid), sizeof(oid));
+done:
+ subagentx_object_unlock(sav->sav_sao);
+ subagentx_get_finalize(sav->sav_sag);
+ return;
+
+fail:
+ subagentx_log_sag_warnx(sag, "oid too large");
+ bcopy(&(sav->sav_start), &(sav->sav_vb.avb_oid),
+ sizeof(sav->sav_start));
+ sav->sav_error = AGENTX_PDU_ERROR_GENERR;
+ subagentx_object_unlock(sav->sav_sao);
+ subagentx_get_finalize(sav->sav_sag);
+
+}
+
+static void
+subagentx_varbind_nosuchobject(struct subagentx_varbind *sav)
+{
+ sav->sav_vb.avb_type = AGENTX_DATA_TYPE_NOSUCHOBJECT;
+
+ if (sav->sav_sao != NULL)
+ subagentx_object_unlock(sav->sav_sao);
+ subagentx_get_finalize(sav->sav_sag);
+}
+
+static void
+subagentx_varbind_nosuchinstance(struct subagentx_varbind *sav)
+{
+ sav->sav_vb.avb_type = AGENTX_DATA_TYPE_NOSUCHINSTANCE;
+
+ if (sav->sav_sao != NULL)
+ subagentx_object_unlock(sav->sav_sao);
+ subagentx_get_finalize(sav->sav_sag);
+}
+
+static void
+subagentx_varbind_endofmibview(struct subagentx_varbind *sav)
+{
+ struct subagentx_object *sao;
+ struct agentx_varbind *vb;
+ size_t i;
+
+#ifdef AGENTX_DEBUG
+ if (sav->sav_sag->sag_type != AGENTX_PDU_TYPE_GETNEXT &&
+ sav->sav_sag->sag_type != AGENTX_PDU_TYPE_GETBULK)
+ subagentx_log_sag_fatalx(sav->sav_sag,
+ "%s: invalid request type", __func__);
+#endif
+
+ if (sav->sav_sao != NULL &&
+ (sao = RB_NEXT(sac_objects, &(sac->sac_objects),
+ sav->sav_sao)) != NULL &&
+ agentx_oid_cmp(&(sao->sao_oid), &(sav->sav_end)) < 0) {
+ bcopy(&(sao->sao_oid), &(sav->sav_vb.avb_oid),
+ sizeof(sao->sao_oid));
+ sav->sav_include = 1;
+ for (i = 0; i < sav->sav_indexlen; i++) {
+ vb = &(sav->sav_index[i].sav_sai->sai_vb);
+ if (vb->avb_type == AGENTX_DATA_TYPE_OCTETSTRING ||
+ vb->avb_type == AGENTX_DATA_TYPE_IPADDRESS)
+ free(vb->avb_data.avb_ostring.aos_string);
+ }
+ bzero(&(sav->sav_index), sizeof(sav->sav_index));
+ subagentx_object_unlock(sav->sav_sao);
+ subagentx_varbind_start(sav);
+ return;
+ }
+
+ sav->sav_vb.avb_type = AGENTX_DATA_TYPE_ENDOFMIBVIEW;
+
+ if (sav->sav_sao != NULL)
+ subagentx_object_unlock(sav->sav_sao);
+ subagentx_get_finalize(sav->sav_sag);
+}
+
+enum subagentx_request_type
+subagentx_varbind_request(struct subagentx_varbind *sav)
+{
+ if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET)
+ return SUBAGENTX_REQUEST_TYPE_GET;
+ if (sav->sav_include ||
+ (sav->sav_indexlen > 0 &&
+ !sav->sav_index[sav->sav_indexlen - 1].sav_idatacomplete))
+ return SUBAGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE;
+ return SUBAGENTX_REQUEST_TYPE_GETNEXT;
+}
+
+struct subagentx_object *
+subagentx_varbind_get_object(struct subagentx_varbind *sav)
+{
+ return sav->sav_sao;
+}
+
+uint32_t
+subagentx_varbind_get_index_integer(struct subagentx_varbind *sav,
+ struct subagentx_index *sai)
+{
+ size_t i;
+
+ if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_INTEGER) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type");
+#else
+ subagentx_log_sag_warnx(sav->sav_sag, "invalid index type");
+ subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0);
+ return 0;
+#endif
+ }
+
+ for (i = 0; i < sav->sav_indexlen; i++) {
+ if (sav->sav_index[i].sav_sai == sai)
+ return sav->sav_index[i].sav_idata.avb_uint32;
+ }
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag, "invalid index");
+#else
+ subagentx_log_sag_warnx(sav->sav_sag, "invalid index");
+ subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0);
+ return 0;
+#endif
+}
+
+const unsigned char *
+subagentx_varbind_get_index_string(struct subagentx_varbind *sav,
+ struct subagentx_index *sai, size_t *slen, int *implied)
+{
+ struct subagentx_varbind_index *index;
+ size_t i;
+
+ if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_OCTETSTRING) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type");
+#else
+ subagentx_log_sag_warnx(sav->sav_sag, "invalid index type");
+ subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0);
+ *slen = 0;
+ *implied = 0;
+ return NULL;
+#endif
+ }
+
+ for (i = 0; i < sav->sav_indexlen; i++) {
+ if (sav->sav_index[i].sav_sai == sai) {
+ index = &(sav->sav_index[i]);
+ *slen = index->sav_idata.avb_ostring.aos_slen;
+ *implied = subagentx_object_implied(sav->sav_sao, sai);
+ return index->sav_idata.avb_ostring.aos_string;
+ }
+ }
+
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag, "invalid index");
+#else
+ subagentx_log_sag_warnx(sav->sav_sag, "invalid index");
+ subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0);
+ *slen = 0;
+ *implied = 0;
+ return NULL;
+#endif
+}
+
+const uint32_t *
+subagentx_varbind_get_index_oid(struct subagentx_varbind *sav,
+ struct subagentx_index *sai, size_t *oidlen, int *implied)
+{
+ struct subagentx_varbind_index *index;
+ size_t i;
+
+ if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_OID) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type");
+#else
+ subagentx_log_sag_warnx(sav->sav_sag, "invalid index type");
+ subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0);
+ *oidlen = 0;
+ *implied = 0;
+ return NULL;
+#endif
+ }
+
+ for (i = 0; i < sav->sav_indexlen; i++) {
+ if (sav->sav_index[i].sav_sai == sai) {
+ index = &(sav->sav_index[i]);
+ *oidlen = index->sav_idata.avb_oid.aoi_idlen;
+ *implied = subagentx_object_implied(sav->sav_sao, sai);
+ return index->sav_idata.avb_oid.aoi_id;
+ }
+ }
+
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag, "invalid index");
+#else
+ subagentx_log_sag_warnx(sav->sav_sag, "invalid index");
+ subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0);
+ *oidlen = 0;
+ *implied = 0;
+ return NULL;
+#endif
+}
+
+const struct in_addr *
+subagentx_varbind_get_index_ipaddress(struct subagentx_varbind *sav,
+ struct subagentx_index *sai)
+{
+ static struct in_addr nuladdr = {0};
+ struct subagentx_varbind_index *index;
+ size_t i;
+
+ if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_IPADDRESS) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type");
+#else
+ subagentx_log_sag_warnx(sav->sav_sag, "invalid index type");
+ subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0);
+ return NULL;
+#endif
+ }
+
+ for (i = 0; i < sav->sav_indexlen; i++) {
+ if (sav->sav_index[i].sav_sai == sai) {
+ index = &(sav->sav_index[i]);
+ if (index->sav_idata.avb_ostring.aos_string == NULL)
+ return &nuladdr;
+ return (struct in_addr *)
+ index->sav_idata.avb_ostring.aos_string;
+ }
+ }
+
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag, "invalid index");
+#else
+ subagentx_log_sag_warnx(sav->sav_sag, "invalid index");
+ subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0);
+ return NULL;
+#endif
+}
+
+void
+subagentx_varbind_set_index_integer(struct subagentx_varbind *sav,
+ struct subagentx_index *sai, uint32_t value)
+{
+ size_t i;
+
+ if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_INTEGER) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type");
+#else
+ subagentx_log_sag_warnx(sav->sav_sag, "invalid index type");
+ subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0);
+ return;
+#endif
+ }
+
+ for (i = 0; i < sav->sav_indexlen; i++) {
+ if (sav->sav_index[i].sav_sai == sai) {
+ if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET &&
+ sav->sav_index[i].sav_idata.avb_uint32 != value) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag,
+ "can't change index on GET");
+#else
+ subagentx_log_sag_warnx(sav->sav_sag,
+ "can't change index on GET");
+ subagentx_varbind_error_type(sav,
+ AGENTX_PDU_ERROR_GENERR, 0);
+ return;
+#endif
+ }
+ sav->sav_index[i].sav_idata.avb_uint32 = value;
+ return;
+ }
+ }
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag, "invalid index");
+#else
+ subagentx_log_sag_warnx(sav->sav_sag, "invalid index");
+ subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0);
+#endif
+}
+
+void
+subagentx_varbind_set_index_string(struct subagentx_varbind *sav,
+ struct subagentx_index *sai, const char *value)
+{
+ subagentx_varbind_set_index_nstring(sav, sai,
+ (const unsigned char *)value, strlen(value));
+}
+
+void
+subagentx_varbind_set_index_nstring(struct subagentx_varbind *sav,
+ struct subagentx_index *sai, const unsigned char *value, size_t slen)
+{
+ struct agentx_ostring *curvalue;
+ unsigned char *nstring;
+ size_t i;
+
+ if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_OCTETSTRING) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type");
+#else
+ subagentx_log_sag_warnx(sav->sav_sag, "invalid index type");
+ subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0);
+ return;
+#endif
+ }
+
+ for (i = 0; i < sav->sav_indexlen; i++) {
+ if (sav->sav_index[i].sav_sai == sai) {
+ if (sai->sai_vb.avb_data.avb_ostring.aos_slen != 0 &&
+ sai->sai_vb.avb_data.avb_ostring.aos_slen != slen) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag,
+ "invalid string length on explicit length "
+ "string");
+#else
+ subagentx_log_sag_warnx(sav->sav_sag,
+ "invalid string length on explicit length "
+ "string");
+ subagentx_varbind_error_type(sav,
+ AGENTX_PDU_ERROR_GENERR, 0);
+ return;
+#endif
+ }
+ curvalue = &(sav->sav_index[i].sav_idata.avb_ostring);
+ if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET &&
+ (curvalue->aos_slen != slen ||
+ memcmp(curvalue->aos_string, value, slen) != 0)) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag,
+ "can't change index on GET");
+#else
+ subagentx_log_sag_warnx(sav->sav_sag,
+ "can't change index on GET");
+ subagentx_varbind_error_type(sav,
+ AGENTX_PDU_ERROR_GENERR, 0);
+ return;
+#endif
+ }
+ if ((nstring = recallocarray(curvalue->aos_string,
+ curvalue->aos_slen + 1, slen + 1, 1)) == NULL) {
+ subagentx_log_sag_warn(sav->sav_sag,
+ "Failed to bind string index");
+ subagentx_varbind_error_type(sav,
+ AGENTX_PDU_ERROR_PROCESSINGERROR, 0);
+ return;
+ }
+ curvalue->aos_string = nstring;
+ memcpy(nstring, value, slen);
+ curvalue->aos_slen = slen;
+ return;
+ }
+ }
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag, "invalid index");
+#else
+ subagentx_log_sag_warnx(sav->sav_sag, "invalid index");
+ subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0);
+#endif
+}
+
+void
+subagentx_varbind_set_index_oid(struct subagentx_varbind *sav,
+ struct subagentx_index *sai, const uint32_t *value, size_t oidlen)
+{
+ struct agentx_oid *curvalue, oid;
+ size_t i;
+
+ if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_OID) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type");
+#else
+ subagentx_log_sag_warnx(sav->sav_sag, "invalid index type");
+ subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0);
+ return;
+#endif
+ }
+
+ for (i = 0; i < sav->sav_indexlen; i++) {
+ if (sav->sav_index[i].sav_sai == sai) {
+ if (sai->sai_vb.avb_data.avb_oid.aoi_idlen != 0 &&
+ sai->sai_vb.avb_data.avb_oid.aoi_idlen != oidlen) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag,
+ "invalid oid length on explicit length "
+ "oid");
+#else
+ subagentx_log_sag_warnx(sav->sav_sag,
+ "invalid oid length on explicit length "
+ "oid");
+ subagentx_varbind_error_type(sav,
+ AGENTX_PDU_ERROR_GENERR, 0);
+ return;
+#endif
+ }
+ curvalue = &(sav->sav_index[i].sav_idata.avb_oid);
+ for (i = 0; i < oidlen; i++)
+ oid.aoi_id[i] = value[i];
+ oid.aoi_idlen = oidlen;
+ if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET &&
+ agentx_oid_cmp(&oid, curvalue) != 0) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag,
+ "can't change index on GET");
+#else
+ subagentx_log_sag_warnx(sav->sav_sag,
+ "can't change index on GET");
+ subagentx_varbind_error_type(sav,
+ AGENTX_PDU_ERROR_GENERR, 0);
+ return;
+#endif
+ }
+ for (i = 0; i < oidlen; i++)
+ curvalue->aoi_id[i] = value[i];
+ curvalue->aoi_idlen = oidlen;
+ return;
+ }
+ }
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag, "invalid index");
+#else
+ subagentx_log_sag_warnx(sav->sav_sag, "invalid index");
+ subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0);
+#endif
+}
+
+void
+subagentx_varbind_set_index_object(struct subagentx_varbind *sav,
+ struct subagentx_index *sai, struct subagentx_object *sao)
+{
+ subagentx_varbind_set_index_oid(sav, sai, sao->sao_oid.aoi_id,
+ sao->sao_oid.aoi_idlen);
+}
+
+void
+subagentx_varbind_set_index_ipaddress(struct subagentx_varbind *sav,
+ struct subagentx_index *sai, const struct in_addr *addr)
+{
+ struct agentx_ostring *curvalue;
+ size_t i;
+
+ if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_IPADDRESS) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type");
+#else
+ subagentx_log_sag_warnx(sav->sav_sag, "invalid index type");
+ subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0);
+ return;
+#endif
+ }
+
+ for (i = 0; i < sav->sav_indexlen; i++) {
+ if (sav->sav_index[i].sav_sai == sai) {
+ curvalue = &(sav->sav_index[i].sav_idata.avb_ostring);
+ if (curvalue->aos_string == NULL)
+ curvalue->aos_string = calloc(1, sizeof(*addr));
+ if (curvalue->aos_string == NULL) {
+ subagentx_log_sag_warn(sav->sav_sag,
+ "Failed to bind ipaddress index");
+ subagentx_varbind_error_type(sav,
+ AGENTX_PDU_ERROR_PROCESSINGERROR, 0);
+ return;
+ }
+ if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET &&
+ memcmp(addr, curvalue->aos_string,
+ sizeof(*addr)) != 0) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag,
+ "can't change index on GET");
+#else
+ subagentx_log_sag_warnx(sav->sav_sag,
+ "can't change index on GET");
+ subagentx_varbind_error_type(sav,
+ AGENTX_PDU_ERROR_GENERR, 0);
+ return;
+#endif
+ }
+ bcopy(addr, curvalue->aos_string, sizeof(*addr));
+ return;
+ }
+ }
+#ifdef AGENTX_DEBUG
+ subagentx_log_sag_fatalx(sav->sav_sag, "invalid index");
+#else
+ subagentx_log_sag_warnx(sav->sav_sag, "invalid index");
+ subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0);
+#endif
+}
+
+static int
+subagentx_request(struct subagentx *sa, uint32_t packetid,
+ int (*cb)(struct agentx_pdu *, void *), void *cookie)
+{
+ struct subagentx_request *sar;
+
+#ifdef AGENTX_DEBUG
+ if (sa->sa_ax->ax_wblen == 0)
+ subagentx_log_sa_fatalx(sa, "%s: no data to be written",
+ __func__);
+#endif
+
+ if ((sar = calloc(1, sizeof(*sar))) == NULL) {
+ subagentx_log_sa_warn(sa, "couldn't create request context");
+ subagentx_reset(sa);
+ return -1;
+ }
+
+ sar->sar_packetid = packetid;
+ sar->sar_cb = cb;
+ sar->sar_cookie = cookie;
+ if (RB_INSERT(sa_requests, &(sa->sa_requests), sar) != NULL) {
+#ifdef AGENTX_DEBUG
+ subagentx_log_sa_fatalx(sa, "%s: duplicate packetid", __func__);
+#else
+ subagentx_log_sa_warnx(sa, "%s: duplicate packetid", __func__);
+ free(sar);
+ subagentx_reset(sa);
+ return -1;
+#endif
+ }
+
+ subagentx_wantwrite(sa, sa->sa_fd);
+ return 0;
+}
+
+static int
+subagentx_request_cmp(struct subagentx_request *r1,
+ struct subagentx_request *r2)
+{
+ return r1->sar_packetid < r2->sar_packetid ? -1 :
+ r1->sar_packetid > r2->sar_packetid;
+}
+
+static int
+subagentx_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;
+}
+
+void
+subagentx_read(struct subagentx *sa)
+{
+ struct subagentx_session *sas;
+ struct subagentx_context *sac;
+ struct subagentx_request sar_search, *sar;
+ struct agentx_pdu *pdu;
+ int error;
+
+ if ((pdu = agentx_recv(sa->sa_ax)) == NULL) {
+ if (errno == EAGAIN)
+ return;
+ subagentx_log_sa_warn(sa, "lost connection");
+ subagentx_reset(sa);
+ return;
+ }
+
+ TAILQ_FOREACH(sas, &(sa->sa_sessions), sas_sa_sessions) {
+ if (sas->sas_id == pdu->ap_header.aph_sessionid)
+ break;
+ if (sas->sas_cstate == SA_CSTATE_WAITOPEN &&
+ sas->sas_packetid == pdu->ap_header.aph_packetid)
+ break;
+ }
+ if (sas == NULL) {
+ subagentx_log_sa_warnx(sa, "received unexpected session: %d",
+ pdu->ap_header.aph_sessionid);
+ agentx_pdu_free(pdu);
+ subagentx_reset(sa);
+ return;
+ }
+ TAILQ_FOREACH(sac, &(sas->sas_contexts), sac_sas_contexts) {
+ if ((pdu->ap_header.aph_flags &
+ AGENTX_PDU_FLAG_NON_DEFAULT_CONTEXT) == 0 &&
+ sac->sac_name_default == 1)
+ break;
+ if (pdu->ap_header.aph_flags &
+ AGENTX_PDU_FLAG_NON_DEFAULT_CONTEXT &&
+ sac->sac_name_default == 0 &&
+ pdu->ap_context.aos_slen == sac->sac_name.aos_slen &&
+ memcmp(pdu->ap_context.aos_string,
+ sac->sac_name.aos_string, sac->sac_name.aos_slen) == 0)
+ break;
+ }
+ if (pdu->ap_header.aph_type != AGENTX_PDU_TYPE_RESPONSE) {
+ if (sac == NULL) {
+ subagentx_log_sa_warnx(sa, "%s: invalid context",
+ pdu->ap_context.aos_string);
+ agentx_pdu_free(pdu);
+ subagentx_reset(sa);
+ return;
+ }
+ }
+
+ switch (pdu->ap_header.aph_type) {
+ case AGENTX_PDU_TYPE_GET:
+ case AGENTX_PDU_TYPE_GETNEXT:
+ case AGENTX_PDU_TYPE_GETBULK:
+ subagentx_get_start(sac, pdu);
+ break;
+ /* Add stubs for set functions */
+ case AGENTX_PDU_TYPE_TESTSET:
+ case AGENTX_PDU_TYPE_COMMITSET:
+ case AGENTX_PDU_TYPE_UNDOSET:
+ if (pdu->ap_header.aph_type == AGENTX_PDU_TYPE_TESTSET)
+ error = AGENTX_PDU_ERROR_NOTWRITABLE;
+ else if (pdu->ap_header.aph_type == AGENTX_PDU_TYPE_COMMITSET)
+ error = AGENTX_PDU_ERROR_COMMITFAILED;
+ else
+ error = AGENTX_PDU_ERROR_UNDOFAILED;
+
+ subagentx_log_sac_debug(sac, "unsupported call: %s",
+ agentx_pdutype2string(pdu->ap_header.aph_type));
+ if (agentx_response(sa->sa_ax, sas->sas_id,
+ pdu->ap_header.aph_transactionid,
+ pdu->ap_header.aph_packetid,
+ sac == NULL ? NULL : SUBAGENTX_CONTEXT_CTX(sac),
+ 0, error, 1, NULL, 0) == -1)
+ subagentx_log_sac_warn(sac,
+ "transaction: %u packetid: %u: failed to send "
+ "reply", pdu->ap_header.aph_transactionid,
+ pdu->ap_header.aph_packetid);
+ if (sa->sa_ax->ax_wblen > 0)
+ subagentx_wantwrite(sa, sa->sa_fd);
+ break;
+ case AGENTX_PDU_TYPE_CLEANUPSET:
+ subagentx_log_sa_debug(sa, "unsupported call: %s",
+ agentx_pdutype2string(pdu->ap_header.aph_type));
+ break;
+ case AGENTX_PDU_TYPE_RESPONSE:
+ sar_search.sar_packetid = pdu->ap_header.aph_packetid;
+ sar = RB_FIND(sa_requests, &(sa->sa_requests), &sar_search);
+ if (sar == NULL) {
+ if (sac == NULL)
+ subagentx_log_sa_warnx(sa, "received "
+ "response on non-request");
+ else
+ subagentx_log_sac_warnx(sac, "received "
+ "response on non-request");
+ break;
+ }
+ if (sac != NULL && pdu->ap_payload.ap_response.ap_error == 0) {
+ sac->sac_sysuptime =
+ pdu->ap_payload.ap_response.ap_uptime;
+ (void) clock_gettime(CLOCK_MONOTONIC,
+ &(sac->sac_sysuptimespec));
+ }
+ RB_REMOVE(sa_requests, &(sa->sa_requests), sar);
+ (void) sar->sar_cb(pdu, sar->sar_cookie);
+ free(sar);
+ break;
+ default:
+ if (sac == NULL)
+ subagentx_log_sa_warnx(sa, "unsupported call: %s",
+ agentx_pdutype2string(pdu->ap_header.aph_type));
+ else
+ subagentx_log_sac_warnx(sac, "unsupported call: %s",
+ agentx_pdutype2string(pdu->ap_header.aph_type));
+ subagentx_reset(sa);
+ break;
+ }
+ agentx_pdu_free(pdu);
+}
+
+void
+subagentx_write(struct subagentx *sa)
+{
+ ssize_t send;
+
+ if ((send = agentx_send(sa->sa_ax)) == -1) {
+ if (errno == EAGAIN) {
+ subagentx_wantwrite(sa, sa->sa_fd);
+ return;
+ }
+ subagentx_log_sa_warn(sa, "lost connection");
+ subagentx_reset(sa);
+ return;
+ }
+ if (send > 0)
+ subagentx_wantwrite(sa, sa->sa_fd);
+}
+
+RB_GENERATE_STATIC(sa_requests, subagentx_request, sar_sa_requests,
+ subagentx_request_cmp)
+RB_GENERATE_STATIC(sac_objects, subagentx_object, sao_sac_objects,
+ subagentx_object_cmp)
diff --git a/lib/libagentx/subagentx.h b/lib/libagentx/subagentx.h
new file mode 100644
index 00000000000..418b86a78c1
--- /dev/null
+++ b/lib/libagentx/subagentx.h
@@ -0,0 +1,147 @@
+/*
+ * 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 <stdint.h>
+#include <stddef.h>
+
+struct subagentx;
+struct subagentx_session;
+struct subagentx_context;
+struct subagentx_agentcaps;
+struct subagentx_region;
+struct subagentx_index;
+struct subagentx_object;
+struct subagentx_varbind;
+
+enum subagentx_request_type {
+ SUBAGENTX_REQUEST_TYPE_GET,
+ SUBAGENTX_REQUEST_TYPE_GETNEXT,
+ SUBAGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE
+};
+
+#define SUBAGENTX_AGENTX_MASTER "/var/agentx/master"
+#define SUBAGENTX_OID_MAX_LEN 128
+#define SUBAGENTX_OID_INDEX_MAX_LEN 10
+#define SUBAGENTX_MIB2 1, 3, 6, 1, 2, 1
+#define SUBAGENTX_ENTERPRISES 1, 3, 6, 1, 4, 1
+#define SUBAGENTX_OID(...) (uint32_t []) { __VA_ARGS__ }, \
+ (sizeof((uint32_t []) { __VA_ARGS__ }) / sizeof(uint32_t))
+
+extern void (*subagentx_log_fatal)(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+extern void (*subagentx_log_warn)(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+extern void (*subagentx_log_info)(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+extern void (*subagentx_log_debug)(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)));
+
+struct subagentx *subagentx(void (*)(struct subagentx *, void *, int), void *);
+void subagentx_connect(struct subagentx *, int);
+void subagentx_read(struct subagentx *);
+void subagentx_write(struct subagentx *);
+extern void (*subagentx_wantwrite)(struct subagentx *, int);
+void subagentx_free(struct subagentx *);
+struct subagentx_session *subagentx_session(struct subagentx *,
+ uint32_t[], size_t, const char *, uint8_t);
+void subagentx_session_free(struct subagentx_session *);
+struct subagentx_context *subagentx_context(struct subagentx_session *,
+ const char *);
+struct subagentx_object *subagentx_context_object_find(
+ struct subagentx_context *, const uint32_t[], size_t, int, int);
+struct subagentx_object *subagentx_context_object_nfind(
+ struct subagentx_context *, const uint32_t[], size_t, int, int);
+uint32_t subagentx_context_uptime(struct subagentx_context *);
+void subagentx_context_free(struct subagentx_context *);
+struct subagentx_agentcaps *subagentx_agentcaps(struct subagentx_context *,
+ uint32_t[], size_t, const char *);
+void subagentx_agentcaps_free(struct subagentx_agentcaps *);
+struct subagentx_region *subagentx_region(struct subagentx_context *,
+ uint32_t[], size_t, uint8_t);
+void subagentx_region_free(struct subagentx_region *);
+struct subagentx_index *subagentx_index_integer_new(struct subagentx_region *,
+ uint32_t[], size_t);
+struct subagentx_index *subagentx_index_integer_any(struct subagentx_region *,
+ uint32_t[], size_t);
+struct subagentx_index *subagentx_index_integer_value(struct subagentx_region *,
+ uint32_t[], size_t, uint32_t);
+struct subagentx_index *subagentx_index_integer_dynamic(
+ struct subagentx_region *, uint32_t[], size_t);
+struct subagentx_index *subagentx_index_string_dynamic(
+ struct subagentx_region *, uint32_t[], size_t);
+struct subagentx_index *subagentx_index_nstring_dynamic(
+ struct subagentx_region *, uint32_t[], size_t, size_t);
+struct subagentx_index *subagentx_index_oid_dynamic(struct subagentx_region *,
+ uint32_t[], size_t);
+struct subagentx_index *subagentx_index_noid_dynamic(struct subagentx_region *,
+ uint32_t[], size_t, size_t);
+struct subagentx_index *subagentx_index_ipaddress_dynamic(
+ struct subagentx_region *, uint32_t[], size_t);
+void subagentx_index_free(struct subagentx_index *);
+struct subagentx_object *subagentx_object(struct subagentx_region *, uint32_t[],
+ size_t, struct subagentx_index *[], size_t, int,
+ void (*)(struct subagentx_varbind *));
+void subagentx_object_free(struct subagentx_object *);
+
+void subagentx_varbind_integer(struct subagentx_varbind *, uint32_t);
+void subagentx_varbind_string(struct subagentx_varbind *, const char *);
+void subagentx_varbind_nstring(struct subagentx_varbind *,
+ const unsigned char *, size_t);
+void subagentx_varbind_printf(struct subagentx_varbind *, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
+void subagentx_varbind_null(struct subagentx_varbind *);
+void subagentx_varbind_oid(struct subagentx_varbind *, const uint32_t[],
+ size_t);
+void subagentx_varbind_object(struct subagentx_varbind *,
+ struct subagentx_object *);
+void subagentx_varbind_index(struct subagentx_varbind *,
+ struct subagentx_index *);
+void subagentx_varbind_ipaddress(struct subagentx_varbind *,
+ const struct in_addr *);
+void subagentx_varbind_counter32(struct subagentx_varbind *, uint32_t);
+void subagentx_varbind_gauge32(struct subagentx_varbind *, uint32_t);
+void subagentx_varbind_timeticks(struct subagentx_varbind *, uint32_t);
+void subagentx_varbind_opaque(struct subagentx_varbind *, const char *, size_t);
+void subagentx_varbind_counter64(struct subagentx_varbind *, uint64_t);
+void subagentx_varbind_notfound(struct subagentx_varbind *);
+void subagentx_varbind_error(struct subagentx_varbind *);
+
+enum subagentx_request_type subagentx_varbind_request(
+ struct subagentx_varbind *);
+struct subagentx_object *
+ subagentx_varbind_get_object(struct subagentx_varbind *);
+uint32_t subagentx_varbind_get_index_integer(struct subagentx_varbind *,
+ struct subagentx_index *);
+const unsigned char *subagentx_varbind_get_index_string(
+ struct subagentx_varbind *, struct subagentx_index *, size_t *, int *);
+const uint32_t *subagentx_varbind_get_index_oid(struct subagentx_varbind *,
+ struct subagentx_index *, size_t *, int *);
+const struct in_addr *subagentx_varbind_get_index_ipaddress(
+ struct subagentx_varbind *, struct subagentx_index *);
+void subagentx_varbind_set_index_integer(struct subagentx_varbind *,
+ struct subagentx_index *, uint32_t);
+void subagentx_varbind_set_index_string(struct subagentx_varbind *,
+ struct subagentx_index *, const char *);
+void subagentx_varbind_set_index_nstring(struct subagentx_varbind *,
+ struct subagentx_index *, const unsigned char *, size_t);
+void subagentx_varbind_set_index_oid(struct subagentx_varbind *,
+ struct subagentx_index *, const uint32_t *, size_t);
+void subagentx_varbind_set_index_object(struct subagentx_varbind *,
+ struct subagentx_index *, struct subagentx_object *);
+void subagentx_varbind_set_index_ipaddress(struct subagentx_varbind *,
+ struct subagentx_index *, const struct in_addr *);
diff --git a/lib/libagentx/subagentx_internal.h b/lib/libagentx/subagentx_internal.h
new file mode 100644
index 00000000000..4dafcf373a4
--- /dev/null
+++ b/lib/libagentx/subagentx_internal.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2020 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 <sys/queue.h>
+#include <sys/time.h>
+#include <sys/tree.h>
+
+#include "agentx.h"
+
+enum subagentx_cstate { /* Current state */
+ SA_CSTATE_CLOSE, /* Closed */
+ SA_CSTATE_WAITOPEN, /* Connection requested */
+ SA_CSTATE_OPEN, /* Open */
+ SA_CSTATE_WAITCLOSE /* Close requested */
+};
+
+enum subagentx_dstate { /* Desired state */
+ SA_DSTATE_OPEN, /* Open */
+ SA_DSTATE_CLOSE /* Close/free */
+};
+
+struct subagentx {
+ void (*sa_nofd)(struct subagentx *, void *, int);
+ void *sa_cookie;
+ int sa_fd;
+ enum subagentx_cstate sa_cstate;
+ enum subagentx_dstate sa_dstate;
+ struct agentx *sa_ax;
+ TAILQ_HEAD(, subagentx_session) sa_sessions;
+ TAILQ_HEAD(, subagentx_get) sa_getreqs;
+ RB_HEAD(sa_requests, subagentx_request) sa_requests;
+};
+
+struct subagentx_session {
+ struct subagentx *sas_sa;
+ uint32_t sas_id;
+ uint32_t sas_timeout;
+ struct agentx_oid sas_oid;
+ struct agentx_ostring sas_descr;
+ enum subagentx_cstate sas_cstate;
+ enum subagentx_dstate sas_dstate;
+ uint32_t sas_packetid;
+ TAILQ_HEAD(, subagentx_context) sas_contexts;
+ TAILQ_ENTRY(subagentx_session) sas_sa_sessions;
+};
+
+struct subagentx_context {
+ struct subagentx_session *sac_sas;
+ int sac_name_default;
+ struct agentx_ostring sac_name;
+ uint32_t sac_sysuptime;
+ struct timespec sac_sysuptimespec;
+ enum subagentx_cstate sac_cstate;
+ enum subagentx_dstate sac_dstate;
+ TAILQ_HEAD(, subagentx_agentcaps) sac_agentcaps;
+ TAILQ_HEAD(, subagentx_region) sac_regions;
+ RB_HEAD(sac_objects, subagentx_object) sac_objects;
+ TAILQ_ENTRY(subagentx_context) sac_sas_contexts;
+};
+
+struct subagentx_get {
+ struct subagentx_context *sag_sac;
+ int sag_fd; /* Only used for logging */
+ uint32_t sag_sessionid;
+ uint32_t sag_transactionid;
+ uint32_t sag_packetid;
+ int sag_context_default;
+ struct agentx_ostring sag_context;
+ enum agentx_pdu_type sag_type;
+ uint16_t sag_nonrep;
+ uint16_t sag_maxrep;
+ size_t sag_nvarbind;
+ struct subagentx_varbind *sag_varbind;
+ TAILQ_ENTRY(subagentx_get) sag_sa_getreqs;
+};
+
+__dead void subagentx_log_sa_fatalx(struct subagentx *, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
+void subagentx_log_sa_warn(struct subagentx *, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
+void subagentx_log_sa_warnx(struct subagentx *, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
+void subagentx_log_sa_info(struct subagentx *, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
+void subagentx_log_sa_debug(struct subagentx *, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
+__dead void subagentx_log_sas_fatalx(struct subagentx_session *, const char *,
+ ...) __attribute__((__format__ (printf, 2, 3)));
+void subagentx_log_sas_warnx(struct subagentx_session *, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
+void subagentx_log_sas_warn(struct subagentx_session *, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
+void subagentx_log_sas_info(struct subagentx_session *, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
+__dead void subagentx_log_sac_fatalx(struct subagentx_context *, const char *,
+ ...) __attribute__((__format__ (printf, 2, 3)));
+void subagentx_log_sac_warnx(struct subagentx_context *, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
+void subagentx_log_sac_warn(struct subagentx_context *, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
+void subagentx_log_sac_info(struct subagentx_context *, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
+void subagentx_log_sac_debug(struct subagentx_context *, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
+__dead void subagentx_log_sag_fatalx(struct subagentx_get *, const char *,
+ ...) __attribute__((__format__ (printf, 2, 3)));
+void subagentx_log_sag_warnx(struct subagentx_get *, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
+void subagentx_log_sag_warn(struct subagentx_get *, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
+void subagentx_log_sag_debug(struct subagentx_get *, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
diff --git a/lib/libagentx/subagentx_log.c b/lib/libagentx/subagentx_log.c
new file mode 100644
index 00000000000..d9dcca8e856
--- /dev/null
+++ b/lib/libagentx/subagentx_log.c
@@ -0,0 +1,316 @@
+/*
+ * Copyright (c) 2020 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 <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "subagentx_internal.h"
+
+#define SUBAGENTX_CONTEXT_NAME(sac) (sac->sac_name_default ? "<default>" : \
+ (char *)sac->sac_name.aos_string)
+#define SUBAGENTX_GET_CTXNAME(sag) (sag->sag_context_default ? "<default>" : \
+ (char *)sag->sag_context.aos_string)
+
+enum subagentx_log_type {
+ SUBAGENTX_LOG_TYPE_FATAL,
+ SUBAGENTX_LOG_TYPE_WARN,
+ SUBAGENTX_LOG_TYPE_INFO,
+ SUBAGENTX_LOG_TYPE_DEBUG
+};
+
+void (*subagentx_log_fatal)(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2))) = NULL;
+void (*subagentx_log_warn)(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2))) = NULL;
+void (*subagentx_log_info)(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2))) = NULL;
+void (*subagentx_log_debug)(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2))) = NULL;
+
+
+static void
+subagentx_log_do(enum subagentx_log_type, const char *, va_list, int,
+ struct subagentx *, struct subagentx_session *, struct subagentx_context *,
+ struct subagentx_get *);
+
+void
+subagentx_log_sa_fatalx(struct subagentx *sa, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ subagentx_log_do(SUBAGENTX_LOG_TYPE_FATAL, fmt, ap, 0, sa, NULL, NULL,
+ NULL);
+ va_end(ap);
+ abort();
+}
+
+void
+subagentx_log_sa_warn(struct subagentx *sa, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 1, sa, NULL, NULL,
+ NULL);
+ va_end(ap);
+}
+
+void
+subagentx_log_sa_warnx(struct subagentx *sa, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 0, sa, NULL, NULL,
+ NULL);
+ va_end(ap);
+}
+
+void
+subagentx_log_sa_info(struct subagentx *sa, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ subagentx_log_do(SUBAGENTX_LOG_TYPE_INFO, fmt, ap, 0, sa, NULL, NULL,
+ NULL);
+ va_end(ap);
+}
+
+void
+subagentx_log_sa_debug(struct subagentx *sa, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ subagentx_log_do(SUBAGENTX_LOG_TYPE_DEBUG, fmt, ap, 0, sa, NULL, NULL,
+ NULL);
+ va_end(ap);
+}
+
+void
+subagentx_log_sas_fatalx(struct subagentx_session *sas, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ subagentx_log_do(SUBAGENTX_LOG_TYPE_FATAL, fmt, ap, 0, NULL, sas, NULL,
+ NULL);
+ va_end(ap);
+ abort();
+}
+
+void
+subagentx_log_sas_warnx(struct subagentx_session *sas, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 0, NULL, sas, NULL,
+ NULL);
+ va_end(ap);
+}
+
+void
+subagentx_log_sas_warn(struct subagentx_session *sas, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 1, NULL, sas, NULL,
+ NULL);
+ va_end(ap);
+}
+
+void
+subagentx_log_sas_info(struct subagentx_session *sas, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ subagentx_log_do(SUBAGENTX_LOG_TYPE_INFO, fmt, ap, 0, NULL, sas, NULL,
+ NULL);
+ va_end(ap);
+}
+
+void
+subagentx_log_sac_fatalx(struct subagentx_context *sac, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ subagentx_log_do(SUBAGENTX_LOG_TYPE_FATAL, fmt, ap, 0, NULL, NULL, sac,
+ NULL);
+ va_end(ap);
+ abort();
+}
+
+void
+subagentx_log_sac_warnx(struct subagentx_context *sac, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 0, NULL, NULL, sac,
+ NULL);
+ va_end(ap);
+}
+
+void
+subagentx_log_sac_warn(struct subagentx_context *sac, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 1, NULL, NULL, sac,
+ NULL);
+ va_end(ap);
+}
+
+void
+subagentx_log_sac_info(struct subagentx_context *sac, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ subagentx_log_do(SUBAGENTX_LOG_TYPE_INFO, fmt, ap, 0, NULL, NULL, sac,
+ NULL);
+ va_end(ap);
+}
+
+void
+subagentx_log_sac_debug(struct subagentx_context *sac, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ subagentx_log_do(SUBAGENTX_LOG_TYPE_DEBUG, fmt, ap, 0, NULL, NULL, sac,
+ NULL);
+ va_end(ap);
+}
+
+void
+subagentx_log_sag_fatalx(struct subagentx_get *sag, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ subagentx_log_do(SUBAGENTX_LOG_TYPE_FATAL, fmt, ap, 0, NULL, NULL, NULL,
+ sag);
+ va_end(ap);
+ abort();
+}
+
+void
+subagentx_log_sag_warnx(struct subagentx_get *sag, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 0, NULL, NULL, NULL,
+ sag);
+ va_end(ap);
+}
+
+void
+subagentx_log_sag_warn(struct subagentx_get *sag, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 1, NULL, NULL, NULL,
+ sag);
+ va_end(ap);
+}
+
+void
+subagentx_log_sag_debug(struct subagentx_get *sag, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ subagentx_log_do(SUBAGENTX_LOG_TYPE_DEBUG, fmt, ap, 0, NULL, NULL, NULL,
+ sag);
+ va_end(ap);
+}
+
+static void
+subagentx_log_do(enum subagentx_log_type type, const char *fmt, va_list ap,
+ int useerrno, struct subagentx *sa, struct subagentx_session *sas,
+ struct subagentx_context *sac, struct subagentx_get *sag)
+{
+ void (*subagentx_log)(const char *, ...);
+ char buf[1500];
+
+ if (type == SUBAGENTX_LOG_TYPE_FATAL)
+ subagentx_log = subagentx_log_fatal;
+ else if (type == SUBAGENTX_LOG_TYPE_WARN)
+ subagentx_log = subagentx_log_warn;
+ else if (type == SUBAGENTX_LOG_TYPE_INFO)
+ subagentx_log = subagentx_log_info;
+ else
+ subagentx_log = subagentx_log_debug;
+ if (subagentx_log == NULL)
+ return;
+
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+
+ if (sag != NULL) {
+ if (useerrno)
+ subagentx_log("[fd:%d sess:%u ctx:%s trid:%u pid:%u]: "
+ "%s: %s", sag->sag_fd, sag->sag_sessionid,
+ SUBAGENTX_GET_CTXNAME(sag), sag->sag_transactionid,
+ sag->sag_packetid, buf, strerror(errno));
+ else
+ subagentx_log("[fd:%d sess:%u ctx:%s trid:%u pid:%u]: "
+ "%s", sag->sag_fd, sag->sag_sessionid,
+ SUBAGENTX_GET_CTXNAME(sag), sag->sag_transactionid,
+ sag->sag_packetid, buf);
+ } else if (sac != NULL) {
+ sas = sac->sac_sas;
+ sa = sas->sas_sa;
+ if (useerrno)
+ subagentx_log("[fd:%d sess:%u ctx:%s]: %s: %s",
+ sa->sa_fd, sas->sas_id, SUBAGENTX_CONTEXT_NAME(sac),
+ buf, strerror(errno));
+ else
+ subagentx_log("[fd:%d sess:%u ctx:%s]: %s", sa->sa_fd,
+ sas->sas_id, SUBAGENTX_CONTEXT_NAME(sac), buf);
+ } else if (sas != NULL) {
+ sa = sas->sas_sa;
+ if (useerrno)
+ subagentx_log("[fd:%d sess:%u]: %s: %s", sa->sa_fd,
+ sas->sas_id, buf, strerror(errno));
+ else
+ subagentx_log("[fd:%d sess:%u]: %s", sa->sa_fd,
+ sas->sas_id, buf);
+ } else if (sa->sa_fd == -1) {
+ if (useerrno)
+ subagentx_log("%s: %s", buf, strerror(errno));
+ else
+ subagentx_log("%s", buf);
+ } else {
+ if (useerrno)
+ subagentx_log("[fd:%d]: %s: %s", sa->sa_fd, buf,
+ strerror(errno));
+ else
+ subagentx_log("[fd:%d]: %s", sa->sa_fd, buf);
+ }
+}