diff options
Diffstat (limited to 'lib/libagentx')
-rw-r--r-- | lib/libagentx/Makefile | 31 | ||||
-rw-r--r-- | lib/libagentx/Symbols.list | 61 | ||||
-rw-r--r-- | lib/libagentx/agentx.c | 1359 | ||||
-rw-r--r-- | lib/libagentx/agentx.h | 232 | ||||
-rw-r--r-- | lib/libagentx/shlib_version | 2 | ||||
-rw-r--r-- | lib/libagentx/subagentx.3 | 650 | ||||
-rw-r--r-- | lib/libagentx/subagentx.c | 3994 | ||||
-rw-r--r-- | lib/libagentx/subagentx.h | 147 | ||||
-rw-r--r-- | lib/libagentx/subagentx_internal.h | 124 | ||||
-rw-r--r-- | lib/libagentx/subagentx_log.c | 316 |
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); + } +} |