summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorMartijn van Duren <martijn@cvs.openbsd.org>2022-08-23 08:56:22 +0000
committerMartijn van Duren <martijn@cvs.openbsd.org>2022-08-23 08:56:22 +0000
commitd22bfb695a028538e4f01a769177d2a1586e62c5 (patch)
tree5d2770194179e5d22e07ecea1d57250a06a6112f /usr.sbin
parentb1f93ce47bb6913e0760cc10338efd1d18776f2c (diff)
(Re)add support for agentx in snmpd
Current omissions in protocol support are notifications, index (de)allocation, and agent capabilities. Help testing sthen@ Feedback/tweaks/OK jmatthew@
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/snmpd/Makefile3
-rw-r--r--usr.sbin/snmpd/application.c13
-rw-r--r--usr.sbin/snmpd/application.h8
-rw-r--r--usr.sbin/snmpd/application_agentx.c879
-rw-r--r--usr.sbin/snmpd/ax.c1549
-rw-r--r--usr.sbin/snmpd/ax.h265
-rw-r--r--usr.sbin/snmpd/parse.y172
-rw-r--r--usr.sbin/snmpd/snmpd.conf.519
-rw-r--r--usr.sbin/snmpd/snmpd.h20
-rw-r--r--usr.sbin/snmpd/snmpe.c8
10 files changed, 2928 insertions, 8 deletions
diff --git a/usr.sbin/snmpd/Makefile b/usr.sbin/snmpd/Makefile
index f49829c0221..c112d9212f0 100644
--- a/usr.sbin/snmpd/Makefile
+++ b/usr.sbin/snmpd/Makefile
@@ -1,9 +1,10 @@
-# $OpenBSD: Makefile,v 1.19 2022/06/30 11:28:36 martijn Exp $
+# $OpenBSD: Makefile,v 1.20 2022/08/23 08:56:20 martijn Exp $
PROG= snmpd
MAN= snmpd.8 snmpd.conf.5
SRCS= parse.y log.c snmpe.c application.c application_legacy.c \
application_blocklist.c \
+ application_agentx.c ax.c \
mps.c trap.c mib.c smi.c kroute.c snmpd.c timer.c \
pf.c proc.c usm.c traphandler.c util.c
diff --git a/usr.sbin/snmpd/application.c b/usr.sbin/snmpd/application.c
index 0fb8a456255..335123f63e4 100644
--- a/usr.sbin/snmpd/application.c
+++ b/usr.sbin/snmpd/application.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: application.c,v 1.6 2022/06/30 11:28:36 martijn Exp $ */
+/* $OpenBSD: application.c,v 1.7 2022/08/23 08:56:20 martijn Exp $ */
/*
* Copyright (c) 2021 Martijn van Duren <martijn@openbsd.org>
@@ -146,10 +146,17 @@ RB_PROTOTYPE_STATIC(appl_requests, appl_request_downstream, ard_entry,
#define APPL_CONTEXT_NAME(ctx) (ctx->ac_name[0] == '\0' ? NULL : ctx->ac_name)
void
+appl(void)
+{
+ appl_agentx();
+}
+
+void
appl_init(void)
{
appl_blocklist_init();
appl_legacy_init();
+ appl_agentx_init();
}
void
@@ -159,6 +166,7 @@ appl_shutdown(void)
appl_blocklist_shutdown();
appl_legacy_shutdown();
+ appl_agentx_shutdown();
TAILQ_FOREACH_SAFE(ctx, &contexts, ac_entries, tctx) {
assert(RB_EMPTY(&(ctx->ac_regions)));
@@ -1122,10 +1130,13 @@ appl_response(struct appl_backend *backend, int32_t requestid,
log_warnx("Invalid error index");
invalid = 1;
}
+/* amavisd-snmp-subagent sets index to 1, no reason to crash over it. */
+#if PEDANTIC
if (error == APPL_ERROR_NOERROR && index != 0) {
log_warnx("error index with no error");
invalid = 1;
}
+#endif
if (vb == NULL && origvb != NULL) {
log_warnx("%s: Request %"PRIu32" returned less varbinds then "
"requested", backend->ab_name, requestid);
diff --git a/usr.sbin/snmpd/application.h b/usr.sbin/snmpd/application.h
index fd5b8146335..7f22af85fed 100644
--- a/usr.sbin/snmpd/application.h
+++ b/usr.sbin/snmpd/application.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: application.h,v 1.2 2022/06/30 11:28:36 martijn Exp $ */
+/* $OpenBSD: application.h,v 1.3 2022/08/23 08:56:20 martijn Exp $ */
/*
* Copyright (c) 2021 Martijn van Duren <martijn@openbsd.org>
@@ -117,6 +117,7 @@ struct appl_backend {
RB_HEAD(appl_requests, appl_request_downstream) ab_requests;
};
+void appl(void);
void appl_init(void);
void appl_shutdown(void);
enum appl_error appl_register(const char *, uint32_t, uint8_t, struct ber_oid *,
@@ -134,6 +135,11 @@ struct ber_element *appl_exception(enum appl_exception);
void appl_legacy_init(void);
void appl_legacy_shutdown(void);
+/* application_agentx.c */
+void appl_agentx(void);
+void appl_agentx_init(void);
+void appl_agentx_shutdown(void);
+
/* application_blocklist.c */
void appl_blocklist_init(void);
void appl_blocklist_shutdown(void);
diff --git a/usr.sbin/snmpd/application_agentx.c b/usr.sbin/snmpd/application_agentx.c
new file mode 100644
index 00000000000..5ac8b2d4852
--- /dev/null
+++ b/usr.sbin/snmpd/application_agentx.c
@@ -0,0 +1,879 @@
+/* $OpenBSD: application_agentx.c,v 1.1 2022/08/23 08:56:20 martijn Exp $ */
+/*
+ * Copyright (c) 2022 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/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <event.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "application.h"
+#include "ax.h"
+#include "log.h"
+#include "smi.h"
+#include "snmp.h"
+#include "snmpd.h"
+
+#define AGENTX_DEFAULTTIMEOUT 5
+
+struct appl_agentx_connection {
+ uint32_t conn_id;
+ struct ax *conn_ax;
+ struct event conn_rev;
+ struct event conn_wev;
+
+ TAILQ_HEAD(, appl_agentx_session) conn_sessions;
+ RB_ENTRY(appl_agentx_connection) conn_entry;
+};
+
+struct appl_agentx_session {
+ uint32_t sess_id;
+ struct appl_agentx_connection *sess_conn;
+ /*
+ * RFC 2741 section 7.1.1:
+ * All subsequent AgentX protocol operations initiated by the master
+ * agent for this session must use this byte ordering and set this bit
+ * accordingly.
+ */
+ enum ax_byte_order sess_byteorder;
+ uint8_t sess_timeout;
+ struct ax_oid sess_oid;
+ struct ax_ostring sess_descr;
+ struct appl_backend sess_backend;
+
+ RB_ENTRY(appl_agentx_session) sess_entry;
+ TAILQ_ENTRY(appl_agentx_session) sess_conn_entry;
+};
+
+void appl_agentx_listen(struct agentx_master *);
+void appl_agentx_accept(int, short, void *);
+void appl_agentx_free(struct appl_agentx_connection *);
+void appl_agentx_recv(int, short, void *);
+void appl_agentx_open(struct appl_agentx_connection *, struct ax_pdu *);
+void appl_agentx_close(struct appl_agentx_session *, struct ax_pdu *);
+void appl_agentx_forceclose(struct appl_backend *, enum appl_close_reason);
+void appl_agentx_session_free(struct appl_agentx_session *);
+void appl_agentx_register(struct appl_agentx_session *, struct ax_pdu *);
+void appl_agentx_unregister(struct appl_agentx_session *, struct ax_pdu *);
+void appl_agentx_get(struct appl_backend *, int32_t, int32_t, const char *,
+ struct appl_varbind *);
+void appl_agentx_getnext(struct appl_backend *, int32_t, int32_t, const char *,
+ struct appl_varbind *);
+void appl_agentx_response(struct appl_agentx_session *, struct ax_pdu *);
+void appl_agentx_send(int, short, void *);
+struct ber_oid *appl_agentx_oid2ber_oid(struct ax_oid *, struct ber_oid *);
+struct ber_element *appl_agentx_value2ber_element(struct ax_varbind *);
+struct ax_ostring *appl_agentx_string2ostring(const char *,
+ struct ax_ostring *);
+int appl_agentx_cmp(struct appl_agentx_connection *,
+ struct appl_agentx_connection *);
+int appl_agentx_session_cmp(struct appl_agentx_session *,
+ struct appl_agentx_session *);
+
+struct appl_backend_functions appl_agentx_functions = {
+ .ab_close = appl_agentx_forceclose,
+ .ab_get = appl_agentx_get,
+ .ab_getnext = appl_agentx_getnext,
+ .ab_getbulk = NULL, /* not properly supported in application.c and libagentx */
+};
+
+RB_HEAD(appl_agentx_conns, appl_agentx_connection) appl_agentx_conns =
+ RB_INITIALIZER(&appl_agentx_conns);
+RB_HEAD(appl_agentx_sessions, appl_agentx_session) appl_agentx_sessions =
+ RB_INITIALIZER(&appl_agentx_sessions);
+
+RB_PROTOTYPE_STATIC(appl_agentx_conns, appl_agentx_connection, conn_entry,
+ appl_agentx_cmp);
+RB_PROTOTYPE_STATIC(appl_agentx_sessions, appl_agentx_session, sess_entry,
+ appl_agentx_session_cmp);
+
+void
+appl_agentx(void)
+{
+ struct agentx_master *master;
+
+ TAILQ_FOREACH(master, &(snmpd_env->sc_agentx_masters), axm_entry)
+ appl_agentx_listen(master);
+}
+
+void
+appl_agentx_init(void)
+{
+ struct agentx_master *master;
+
+ TAILQ_FOREACH(master, &(snmpd_env->sc_agentx_masters), axm_entry) {
+ if (master->axm_fd == -1)
+ continue;
+ event_set(&(master->axm_ev), master->axm_fd,
+ EV_READ | EV_PERSIST, appl_agentx_accept, master);
+ event_add(&(master->axm_ev), NULL);
+ log_info("AgentX: listening on %s", master->axm_sun.sun_path);
+ }
+}
+void
+appl_agentx_listen(struct agentx_master *master)
+{
+ mode_t mask;
+
+ unlink(master->axm_sun.sun_path);
+
+ mask = umask(0777);
+ if ((master->axm_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1 ||
+ bind(master->axm_fd, (struct sockaddr *)&(master->axm_sun),
+ sizeof(master->axm_sun)) == -1 ||
+ listen(master->axm_fd, 5)) {
+ log_warn("Agentx: listen %s", master->axm_sun.sun_path);
+ umask(mask);
+ return;
+ }
+ umask(mask);
+ if (chown(master->axm_sun.sun_path, master->axm_owner,
+ master->axm_group) == -1) {
+ log_warn("AgentX: chown %s", master->axm_sun.sun_path);
+ goto fail;
+ }
+ if (chmod(master->axm_sun.sun_path, master->axm_mode) == -1) {
+ log_warn("AgentX: chmod %s", master->axm_sun.sun_path);
+ goto fail;
+ }
+ return;
+ fail:
+ close(master->axm_fd);
+ master->axm_fd = -1;
+}
+
+void
+appl_agentx_shutdown(void)
+{
+ struct appl_agentx_connection *conn, *tconn;
+
+ RB_FOREACH_SAFE(conn, appl_agentx_conns, &appl_agentx_conns, tconn)
+ appl_agentx_free(conn);
+}
+
+void
+appl_agentx_accept(int masterfd, short event, void *cookie)
+{
+ int fd;
+ struct agentx_master *master = cookie;
+ struct sockaddr_un sun;
+ socklen_t sunlen = sizeof(sun);
+ struct appl_agentx_connection *conn = NULL;
+
+ if ((fd = accept(masterfd, (struct sockaddr *)&sun, &sunlen)) == -1) {
+ log_warn("AgentX: accept %s", master->axm_sun.sun_path);
+ return;
+ }
+
+ if ((conn = malloc(sizeof(*conn))) == NULL) {
+ log_warn(NULL);
+ goto fail;
+ }
+
+ TAILQ_INIT(&(conn->conn_sessions));
+ if ((conn->conn_ax = ax_new(fd)) == NULL) {
+ log_warn(NULL);
+ goto fail;
+ }
+
+ do {
+ conn->conn_id = arc4random();
+ } while (RB_INSERT(appl_agentx_conns,
+ &appl_agentx_conns, conn) != NULL);
+
+ event_set(&(conn->conn_rev), fd, EV_READ | EV_PERSIST,
+ appl_agentx_recv, conn);
+ event_add(&(conn->conn_rev), NULL);
+ event_set(&(conn->conn_wev), fd, EV_WRITE, appl_agentx_send, conn);
+ log_info("AgentX(%d): new connection", conn->conn_id);
+
+ return;
+ fail:
+ close(fd);
+ free(conn);
+}
+
+void
+appl_agentx_free(struct appl_agentx_connection *conn)
+{
+ struct appl_agentx_session *session;
+
+ event_del(&(conn->conn_rev));
+ event_del(&(conn->conn_wev));
+
+ while ((session = TAILQ_FIRST(&(conn->conn_sessions))) != NULL) {
+ if (conn->conn_ax == NULL)
+ appl_agentx_session_free(session);
+ else
+ appl_agentx_forceclose(&(session->sess_backend),
+ APPL_CLOSE_REASONSHUTDOWN);
+ }
+
+ RB_REMOVE(appl_agentx_conns, &appl_agentx_conns, conn);
+ ax_free(conn->conn_ax);
+ free(conn);
+}
+
+void
+appl_agentx_recv(int fd, short event, void *cookie)
+{
+ struct appl_agentx_connection *conn = cookie;
+ struct appl_agentx_session *session;
+ struct ax_pdu *pdu;
+
+ if ((pdu = ax_recv(conn->conn_ax)) == NULL) {
+ if (errno == EAGAIN)
+ return;
+ log_warn("AgentX(%"PRIu32")", conn->conn_id);
+ /*
+ * Either the connection is dead, or we had garbage on the line.
+ * Both make sure we can't continue on this stream.
+ */
+ if (errno == ECONNRESET) {
+ ax_free(conn->conn_ax);
+ conn->conn_ax = NULL;
+ }
+ appl_agentx_free(conn);
+ return;
+ }
+
+ conn->conn_ax->ax_byteorder = pdu->ap_header.aph_flags &
+ AX_PDU_FLAG_NETWORK_BYTE_ORDER ?
+ AX_BYTE_ORDER_BE : AX_BYTE_ORDER_LE;
+ if (pdu->ap_header.aph_type != AX_PDU_TYPE_OPEN) {
+ /* Make sure we only look for connection-local sessions */
+ TAILQ_FOREACH(session, &(conn->conn_sessions),
+ sess_conn_entry) {
+ if (session->sess_id == pdu->ap_header.aph_sessionid)
+ break;
+ }
+ if (session == NULL) {
+ log_warnx("AgentX(%"PRIu32"): Session %"PRIu32" not "
+ "found for request", conn->conn_id,
+ pdu->ap_header.aph_sessionid);
+ ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
+ pdu->ap_header.aph_transactionid,
+ pdu->ap_header.aph_packetid,
+ &(pdu->ap_context), smi_getticks(),
+ APPL_ERROR_NOTOPEN, 0, NULL, 0);
+ appl_agentx_send(-1, EV_WRITE, conn);
+ goto fail;
+ }
+ /*
+ * RFC2741 section 7.1.1 bullet 4 is unclear on what byte order
+ * the response should be. My best guess is that it makes more
+ * sense that replies are in the same byte-order as what was
+ * requested.
+ * In practice we always have the same byte order as when we
+ * opened the session, so it's likely a non-issue, however, we
+ * can change to session byte order here.
+ */
+ }
+
+ switch (pdu->ap_header.aph_type) {
+ case AX_PDU_TYPE_OPEN:
+ appl_agentx_open(conn, pdu);
+ break;
+ case AX_PDU_TYPE_CLOSE:
+ appl_agentx_close(session, pdu);
+ break;
+ case AX_PDU_TYPE_REGISTER:
+ appl_agentx_register(session, pdu);
+ break;
+ case AX_PDU_TYPE_UNREGISTER:
+ appl_agentx_unregister(session, pdu);
+ break;
+ case AX_PDU_TYPE_GET:
+ case AX_PDU_TYPE_GETNEXT:
+ case AX_PDU_TYPE_GETBULK:
+ case AX_PDU_TYPE_TESTSET:
+ case AX_PDU_TYPE_COMMITSET:
+ case AX_PDU_TYPE_UNDOSET:
+ case AX_PDU_TYPE_CLEANUPSET:
+ appl_agentx_forceclose(&(session->sess_backend),
+ APPL_CLOSE_REASONPROTOCOLERROR);
+ if (!TAILQ_EMPTY(&(conn->conn_sessions)))
+ goto fail;
+
+ ax_pdu_free(pdu);
+ appl_agentx_free(conn);
+ return;
+ case AX_PDU_TYPE_NOTIFY:
+ log_warnx("%s: not supported",
+ ax_pdutype2string(pdu->ap_header.aph_type));
+ /*
+ * RFC 2741 section 7.1.10:
+ * Note that the master agent's successful response indicates
+ * the agentx-Notify-PDU was received and validated. It does
+ * not indicate that any particular notifications were actually
+ * generated or received by notification targets
+ */
+ /* XXX Not yet - FALLTHROUGH */
+ case AX_PDU_TYPE_PING:
+ ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
+ pdu->ap_header.aph_transactionid,
+ pdu->ap_header.aph_packetid, &(pdu->ap_context),
+ smi_getticks(), APPL_ERROR_NOERROR, 0, NULL, 0);
+ appl_agentx_send(-1, EV_WRITE, conn);
+ break;
+ case AX_PDU_TYPE_INDEXALLOCATE:
+ case AX_PDU_TYPE_INDEXDEALLOCATE:
+ log_warnx("%s: not supported",
+ ax_pdutype2string(pdu->ap_header.aph_type));
+ ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
+ pdu->ap_header.aph_transactionid,
+ pdu->ap_header.aph_packetid, &(pdu->ap_context),
+ smi_getticks(), APPL_ERROR_PROCESSINGERROR, 1,
+ pdu->ap_payload.ap_vbl.ap_varbind,
+ pdu->ap_payload.ap_vbl.ap_nvarbind);
+ appl_agentx_send(-1, EV_WRITE, conn);
+ break;
+ case AX_PDU_TYPE_ADDAGENTCAPS:
+ case AX_PDU_TYPE_REMOVEAGENTCAPS:
+ log_warnx("%s: not supported",
+ ax_pdutype2string(pdu->ap_header.aph_type));
+ ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
+ pdu->ap_header.aph_transactionid,
+ pdu->ap_header.aph_packetid, &(pdu->ap_context),
+ smi_getticks(), APPL_ERROR_PROCESSINGERROR, 1,
+ NULL, 0);
+ appl_agentx_send(-1, EV_WRITE, conn);
+ break;
+ case AX_PDU_TYPE_RESPONSE:
+ appl_agentx_response(session, pdu);
+ break;
+ }
+
+ fail:
+ ax_pdu_free(pdu);
+}
+
+void
+appl_agentx_open(struct appl_agentx_connection *conn, struct ax_pdu *pdu)
+{
+ struct appl_agentx_session *session;
+ struct ber_oid oid;
+ char oidbuf[1024];
+
+ if ((session = malloc(sizeof(*session))) == NULL) {
+ log_warn(NULL);
+ goto fail;
+ }
+ session->sess_descr.aos_string = NULL;
+
+ session->sess_conn = conn;
+ if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NETWORK_BYTE_ORDER)
+ session->sess_byteorder = AX_BYTE_ORDER_BE;
+ else
+ session->sess_byteorder = AX_BYTE_ORDER_LE;
+
+ /* RFC 2742 agentxSessionObjectID */
+ if (pdu->ap_payload.ap_open.ap_oid.aoi_idlen == 0) {
+ pdu->ap_payload.ap_open.ap_oid.aoi_id[0] = 0;
+ pdu->ap_payload.ap_open.ap_oid.aoi_id[1] = 0;
+ pdu->ap_payload.ap_open.ap_oid.aoi_idlen = 2;
+ } else if (pdu->ap_payload.ap_open.ap_oid.aoi_idlen == 1) {
+ log_warnx("AgentX(%"PRIu32"): Invalid oid: Open Failed",
+ conn->conn_id);
+ goto fail;
+ }
+ /* RFC 2742 agentxSessionDescr */
+ if (pdu->ap_payload.ap_open.ap_descr.aos_slen > 255) {
+ log_warnx("AgentX(%"PRIu32"): Invalid descr (too long): Open "
+ "Failed", conn->conn_id);
+ goto fail;
+ }
+ /*
+ * ax_ostring is always NUL-terminated, but doesn't scan for internal
+ * NUL-bytes. However, mbstowcs stops at NUL, which might be in the
+ * middle of the string.
+ */
+ if (strlen(pdu->ap_payload.ap_open.ap_descr.aos_string) !=
+ pdu->ap_payload.ap_open.ap_descr.aos_slen ||
+ mbstowcs(NULL,
+ pdu->ap_payload.ap_open.ap_descr.aos_string, 0) == (size_t)-1) {
+ log_warnx("AgentX(%"PRIu32"): Invalid descr (not UTF-8): "
+ "Open Failed", conn->conn_id);
+ goto fail;
+ }
+
+ session->sess_timeout = pdu->ap_payload.ap_open.ap_timeout;
+ session->sess_oid = pdu->ap_payload.ap_open.ap_oid;
+ session->sess_descr.aos_slen = pdu->ap_payload.ap_open.ap_descr.aos_slen;
+ if (pdu->ap_payload.ap_open.ap_descr.aos_string != NULL) {
+ session->sess_descr.aos_string =
+ strdup(pdu->ap_payload.ap_open.ap_descr.aos_string);
+ if (session->sess_descr.aos_string == NULL) {
+ log_warn("AgentX(%"PRIu32"): strdup: Open Failed",
+ conn->conn_id);
+ goto fail;
+ }
+ }
+
+ /* RFC 2742 agentxSessionIndex: chances of reuse, slim to none */
+ do {
+ session->sess_id = arc4random();
+ } while (RB_INSERT(appl_agentx_sessions,
+ &appl_agentx_sessions, session) != NULL);
+
+ if (asprintf(&(session->sess_backend.ab_name),
+ "AgentX(%"PRIu32"/%"PRIu32")",
+ conn->conn_id, session->sess_id) == -1) {
+ log_warn("AgentX(%d): asprintf: Open Failed",
+ conn->conn_id);
+ goto fail;
+ }
+ session->sess_backend.ab_cookie = session;
+ session->sess_backend.ab_retries = 0;
+ session->sess_backend.ab_fn = &appl_agentx_functions;
+ RB_INIT(&(session->sess_backend.ab_requests));
+ TAILQ_INSERT_TAIL(&(conn->conn_sessions), session, sess_conn_entry);
+
+ appl_agentx_oid2ber_oid(&(session->sess_oid), &oid);
+ smi_oid2string(&oid, oidbuf, sizeof(oidbuf), 0);
+ log_info("%s: %s %s: Open", session->sess_backend.ab_name, oidbuf,
+ session->sess_descr.aos_string);
+
+ ax_response(conn->conn_ax, session->sess_id, pdu->ap_header.aph_transactionid,
+ pdu->ap_header.aph_packetid, NULL, smi_getticks(), APPL_ERROR_NOERROR, 0,
+ NULL, 0);
+ appl_agentx_send(-1, EV_WRITE, conn);
+
+ return;
+ fail:
+ ax_response(conn->conn_ax, 0, pdu->ap_header.aph_transactionid,
+ pdu->ap_header.aph_packetid, NULL, 0, APPL_ERROR_OPENFAILED, 0,
+ NULL, 0);
+ appl_agentx_send(-1, EV_WRITE, conn);
+ if (session != NULL)
+ free(session->sess_descr.aos_string);
+ free(session);
+}
+
+void
+appl_agentx_close(struct appl_agentx_session *session, struct ax_pdu *pdu)
+{
+ struct appl_agentx_connection *conn = session->sess_conn;
+ char name[100];
+
+ strlcpy(name, session->sess_backend.ab_name, sizeof(name));
+ appl_agentx_session_free(session);
+ log_info("%s: Closed by subagent (%s)", name,
+ ax_closereason2string(pdu->ap_payload.ap_close.ap_reason));
+
+ ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
+ pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
+ &(pdu->ap_context), smi_getticks(), APPL_ERROR_NOERROR, 0, NULL, 0);
+ appl_agentx_send(-1, EV_WRITE, conn);
+}
+
+void
+appl_agentx_forceclose(struct appl_backend *backend,
+ enum appl_close_reason reason)
+{
+ struct appl_agentx_session *session = backend->ab_cookie;
+ char name[100];
+
+ session->sess_conn->conn_ax->ax_byteorder = session->sess_byteorder;
+ ax_close(session->sess_conn->conn_ax, session->sess_id,
+ (enum ax_close_reason) reason);
+ appl_agentx_send(-1, EV_WRITE, session->sess_conn);
+
+ strlcpy(name, session->sess_backend.ab_name, sizeof(name));
+ appl_agentx_session_free(session);
+ log_info("%s: Closed by snmpd (%s)", name,
+ ax_closereason2string((enum ax_close_reason)reason));
+}
+
+void
+appl_agentx_session_free(struct appl_agentx_session *session)
+{
+ struct appl_agentx_connection *conn = session->sess_conn;
+
+ appl_close(&(session->sess_backend));
+
+ RB_REMOVE(appl_agentx_sessions, &appl_agentx_sessions, session);
+ TAILQ_REMOVE(&(conn->conn_sessions), session, sess_conn_entry);
+
+ free(session->sess_backend.ab_name);
+ free(session->sess_descr.aos_string);
+ free(session);
+}
+
+void
+appl_agentx_register(struct appl_agentx_session *session, struct ax_pdu *pdu)
+{
+ uint32_t timeout;
+ struct ber_oid oid;
+ enum appl_error error;
+
+ timeout = pdu->ap_payload.ap_register.ap_timeout;
+ timeout = timeout != 0 ? timeout : session->sess_timeout != 0 ?
+ session->sess_timeout : AGENTX_DEFAULTTIMEOUT;
+ timeout *= 100;
+
+ if (appl_agentx_oid2ber_oid(
+ &(pdu->ap_payload.ap_register.ap_subtree), &oid) == NULL) {
+ log_warnx("%s: Failed to register: oid too small",
+ session->sess_backend.ab_name);
+ error = APPL_ERROR_PROCESSINGERROR;
+ goto fail;
+ }
+
+ error = appl_register(pdu->ap_context.aos_string, timeout,
+ pdu->ap_payload.ap_register.ap_priority, &oid,
+ pdu->ap_header.aph_flags & AX_PDU_FLAG_INSTANCE_REGISTRATION, 0,
+ pdu->ap_payload.ap_register.ap_range_subid,
+ pdu->ap_payload.ap_register.ap_upper_bound,
+ &(session->sess_backend));
+
+ fail:
+ ax_response(session->sess_conn->conn_ax, session->sess_id,
+ pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
+ &(pdu->ap_context), smi_getticks(), error, 0, NULL, 0);
+ appl_agentx_send(-1, EV_WRITE, session->sess_conn);
+}
+
+void
+appl_agentx_unregister(struct appl_agentx_session *session, struct ax_pdu *pdu)
+{
+ struct ber_oid oid;
+ enum appl_error error;
+
+ if (appl_agentx_oid2ber_oid(
+ &(pdu->ap_payload.ap_unregister.ap_subtree), &oid) == NULL) {
+ log_warnx("%s: Failed to unregister: oid too small",
+ session->sess_backend.ab_name);
+ error = APPL_ERROR_PROCESSINGERROR;
+ goto fail;
+ }
+
+ error = appl_unregister(pdu->ap_context.aos_string,
+ pdu->ap_payload.ap_unregister.ap_priority, &oid,
+ pdu->ap_payload.ap_unregister.ap_range_subid,
+ pdu->ap_payload.ap_unregister.ap_upper_bound,
+ &(session->sess_backend));
+
+ fail:
+ ax_response(session->sess_conn->conn_ax, session->sess_id,
+ pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
+ &(pdu->ap_context), smi_getticks(), error, 0, NULL, 0);
+ appl_agentx_send(-1, EV_WRITE, session->sess_conn);
+}
+
+#define AX_PDU_FLAG_INDEX (AX_PDU_FLAG_NEW_INDEX | AX_PDU_FLAG_ANY_INDEX)
+
+void
+appl_agentx_get(struct appl_backend *backend, int32_t transactionid,
+ int32_t requestid, const char *ctx, struct appl_varbind *vblist)
+{
+ struct appl_agentx_session *session = backend->ab_cookie;
+ struct ax_ostring *context, string;
+ struct appl_varbind *vb;
+ struct ax_searchrange *srl;
+ size_t i, j, nsr;
+
+ for (nsr = 0, vb = vblist; vb != NULL; vb = vb->av_next)
+ nsr++;
+
+ if ((srl = calloc(nsr, sizeof(*srl))) == NULL) {
+ log_warn(NULL);
+ appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist);
+ return;
+ }
+
+ for (i = 0, vb = vblist; i < nsr; i++, vb = vb->av_next) {
+ srl[i].asr_start.aoi_include = vb->av_include;
+ srl[i].asr_start.aoi_idlen = vb->av_oid.bo_n;
+ for (j = 0; j < vb->av_oid.bo_n; j++)
+ srl[i].asr_start.aoi_id[j] = vb->av_oid.bo_id[j];
+ srl[i].asr_stop.aoi_include = 0;
+ srl[i].asr_stop.aoi_idlen = 0;
+ }
+ if ((context = appl_agentx_string2ostring(ctx, &string)) == NULL) {
+ if (errno != 0) {
+ log_warn("Failed to convert context");
+ appl_response(backend, requestid,
+ APPL_ERROR_GENERR, 1, vblist);
+ free(srl);
+ return;
+ }
+ }
+
+ session->sess_conn->conn_ax->ax_byteorder = session->sess_byteorder;
+ if (ax_get(session->sess_conn->conn_ax, session->sess_id, transactionid,
+ requestid, context, srl, nsr) == -1)
+ appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist);
+ else
+ appl_agentx_send(-1, EV_WRITE, session->sess_conn);
+ free(srl);
+ if (context != NULL)
+ free(context->aos_string);
+}
+
+void
+appl_agentx_getnext(struct appl_backend *backend, int32_t transactionid,
+ int32_t requestid, const char *ctx, struct appl_varbind *vblist)
+{
+ struct appl_agentx_session *session = backend->ab_cookie;
+ struct ax_ostring *context, string;
+ struct appl_varbind *vb;
+ struct ax_searchrange *srl;
+ size_t i, j, nsr;
+
+ for (nsr = 0, vb = vblist; vb != NULL; vb = vb->av_next)
+ nsr++;
+
+ if ((srl = calloc(nsr, sizeof(*srl))) == NULL) {
+ log_warn(NULL);
+ appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist);
+ return;
+ }
+
+ for (i = 0, vb = vblist; i < nsr; i++, vb = vb->av_next) {
+ srl[i].asr_start.aoi_include = vb->av_include;
+ srl[i].asr_start.aoi_idlen = vb->av_oid.bo_n;
+ for (j = 0; j < vb->av_oid.bo_n; j++)
+ srl[i].asr_start.aoi_id[j] = vb->av_oid.bo_id[j];
+ srl[i].asr_stop.aoi_include = 0;
+ srl[i].asr_stop.aoi_idlen = vb->av_oid_end.bo_n;
+ for (j = 0; j < vb->av_oid.bo_n; j++)
+ srl[i].asr_stop.aoi_id[j] = vb->av_oid_end.bo_id[j];
+ }
+ if ((context = appl_agentx_string2ostring(ctx, &string)) == NULL) {
+ if (errno != 0) {
+ log_warn("Failed to convert context");
+ appl_response(backend, requestid,
+ APPL_ERROR_GENERR, 1, vblist);
+ free(srl);
+ return;
+ }
+ }
+
+ session->sess_conn->conn_ax->ax_byteorder = session->sess_byteorder;
+ if (ax_getnext(session->sess_conn->conn_ax, session->sess_id, transactionid,
+ requestid, context, srl, nsr) == -1)
+ appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist);
+ else
+ appl_agentx_send(-1, EV_WRITE, session->sess_conn);
+ free(srl);
+ if (context != NULL)
+ free(context->aos_string);
+}
+
+void
+appl_agentx_response(struct appl_agentx_session *session, struct ax_pdu *pdu)
+{
+ struct appl_varbind *response = NULL;
+ struct ax_varbind *vb;
+ enum appl_error error;
+ uint16_t index;
+ size_t i, nvarbind;
+
+ nvarbind = pdu->ap_payload.ap_response.ap_nvarbind;
+ if ((response = calloc(nvarbind, sizeof(*response))) == NULL) {
+ log_warn(NULL);
+ appl_response(&(session->sess_backend),
+ pdu->ap_header.aph_packetid,
+ APPL_ERROR_GENERR, 1, NULL);
+ return;
+ }
+
+ error = (enum appl_error)pdu->ap_payload.ap_response.ap_error;
+ index = pdu->ap_payload.ap_response.ap_index;
+ for (i = 0; i < nvarbind; i++) {
+ response[i].av_next = i + 1 == nvarbind ?
+ NULL : &(response[i + 1]);
+ vb = &(pdu->ap_payload.ap_response.ap_varbindlist[i]);
+
+ if (appl_agentx_oid2ber_oid(&(vb->avb_oid),
+ &(response[i].av_oid)) == NULL) {
+ log_warnx("%s: invalid oid",
+ session->sess_backend.ab_name);
+ if (error != APPL_ERROR_NOERROR) {
+ error = APPL_ERROR_GENERR;
+ index = i + 1;
+ }
+ continue;
+ }
+ response[i].av_value = appl_agentx_value2ber_element(vb);
+ if (response[i].av_value == NULL) {
+ log_warn("%s: Failed to parse response value",
+ session->sess_backend.ab_name);
+ if (error != APPL_ERROR_NOERROR) {
+ error = APPL_ERROR_GENERR;
+ index = i + 1;
+ }
+ }
+ }
+ appl_response(&(session->sess_backend), pdu->ap_header.aph_packetid,
+ error, index, response);
+ free(response);
+}
+
+void
+appl_agentx_send(int fd, short event, void *cookie)
+{
+ struct appl_agentx_connection *conn = cookie;
+
+ switch (ax_send(conn->conn_ax)) {
+ case -1:
+ if (errno == EAGAIN)
+ break;
+ log_warn("AgentX(%"PRIu32")", conn->conn_id);
+ ax_free(conn->conn_ax);
+ conn->conn_ax = NULL;
+ appl_agentx_free(conn);
+ return;
+ case 0:
+ return;
+ default:
+ break;
+ }
+ event_add(&(conn->conn_wev), NULL);
+}
+
+struct ber_oid *
+appl_agentx_oid2ber_oid(struct ax_oid *aoid, struct ber_oid *boid)
+{
+ size_t i;
+
+ if (aoid->aoi_idlen < BER_MIN_OID_LEN ||
+ aoid->aoi_idlen > BER_MAX_OID_LEN) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+
+ boid->bo_n = aoid->aoi_idlen;
+ for (i = 0; i < boid->bo_n; i++)
+ boid->bo_id[i] = aoid->aoi_id[i];
+ return boid;
+}
+
+struct ber_element *
+appl_agentx_value2ber_element(struct ax_varbind *vb)
+{
+ struct ber_oid oid;
+ struct ber_element *elm;
+
+ switch (vb->avb_type) {
+ case AX_DATA_TYPE_INTEGER:
+ return ober_add_integer(NULL, vb->avb_data.avb_int32);
+ case AX_DATA_TYPE_OCTETSTRING:
+ return ober_add_nstring(NULL,
+ vb->avb_data.avb_ostring.aos_string,
+ vb->avb_data.avb_ostring.aos_slen);
+ case AX_DATA_TYPE_NULL:
+ return ober_add_null(NULL);
+ case AX_DATA_TYPE_OID:
+ if (appl_agentx_oid2ber_oid(
+ &(vb->avb_data.avb_oid), &oid) == NULL)
+ return NULL;
+ return ober_add_oid(NULL, &oid);
+ case AX_DATA_TYPE_IPADDRESS:
+ if ((elm = ober_add_nstring(NULL,
+ vb->avb_data.avb_ostring.aos_string,
+ vb->avb_data.avb_ostring.aos_slen)) == NULL)
+ return NULL;
+ ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_IPADDR);
+ return elm;
+ case AX_DATA_TYPE_COUNTER32:
+ elm = ober_add_integer(NULL, vb->avb_data.avb_uint32);
+ if (elm == NULL)
+ return NULL;
+ ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
+ return elm;
+ case AX_DATA_TYPE_GAUGE32:
+ elm = ober_add_integer(NULL, vb->avb_data.avb_uint32);
+ if (elm == NULL)
+ return NULL;
+ ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_GAUGE32);
+ return elm;
+ case AX_DATA_TYPE_TIMETICKS:
+ elm = ober_add_integer(NULL, vb->avb_data.avb_uint32);
+ if (elm == NULL)
+ return NULL;
+ ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_TIMETICKS);
+ return elm;
+ case AX_DATA_TYPE_OPAQUE:
+ if ((elm = ober_add_nstring(NULL,
+ vb->avb_data.avb_ostring.aos_string,
+ vb->avb_data.avb_ostring.aos_slen)) == NULL)
+ return NULL;
+ ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_OPAQUE);
+ return elm;
+ case AX_DATA_TYPE_COUNTER64:
+ elm = ober_add_integer(NULL, vb->avb_data.avb_uint64);
+ if (elm == NULL)
+ return NULL;
+ ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_COUNTER64);
+ return elm;
+ case AX_DATA_TYPE_NOSUCHOBJECT:
+ return appl_exception(APPL_EXC_NOSUCHOBJECT);
+ case AX_DATA_TYPE_NOSUCHINSTANCE:
+ return appl_exception(APPL_EXC_NOSUCHINSTANCE);
+ case AX_DATA_TYPE_ENDOFMIBVIEW:
+ return appl_exception(APPL_EXC_ENDOFMIBVIEW);
+ default:
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+struct ax_ostring *
+appl_agentx_string2ostring(const char *str, struct ax_ostring *ostring)
+{
+ if (str == NULL) {
+ errno = 0;
+ return NULL;
+ }
+
+ ostring->aos_slen = strlen(str);
+ if ((ostring->aos_string = strdup(str)) == NULL)
+ return NULL;
+ return ostring;
+}
+
+int
+appl_agentx_cmp(struct appl_agentx_connection *conn1,
+ struct appl_agentx_connection *conn2)
+{
+ return conn1->conn_id < conn2->conn_id ? -1 :
+ conn1->conn_id > conn2->conn_id;
+}
+
+int
+appl_agentx_session_cmp(struct appl_agentx_session *sess1,
+ struct appl_agentx_session *sess2)
+{
+ return sess1->sess_id < sess2->sess_id ? -1 : sess1->sess_id > sess2->sess_id;
+}
+
+RB_GENERATE_STATIC(appl_agentx_conns, appl_agentx_connection, conn_entry,
+ appl_agentx_cmp);
+RB_GENERATE_STATIC(appl_agentx_sessions, appl_agentx_session, sess_entry,
+ appl_agentx_session_cmp);
diff --git a/usr.sbin/snmpd/ax.c b/usr.sbin/snmpd/ax.c
new file mode 100644
index 00000000000..63add683efe
--- /dev/null
+++ b/usr.sbin/snmpd/ax.c
@@ -0,0 +1,1549 @@
+/* $OpenBSD: ax.c,v 1.1 2022/08/23 08:56:20 martijn Exp $ */
+/*
+ * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <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 "ax.h"
+
+#define AX_PDU_HEADER 20
+
+static int ax_pdu_need(struct ax *, size_t);
+static int ax_pdu_header(struct ax *,
+ enum ax_pdu_type, uint8_t, uint32_t, uint32_t, uint32_t,
+ struct ax_ostring *);
+static uint32_t ax_packetid(struct ax *);
+static uint32_t ax_pdu_queue(struct ax *);
+static int ax_pdu_add_uint16(struct ax *, uint16_t);
+static int ax_pdu_add_uint32(struct ax *, uint32_t);
+static int ax_pdu_add_uint64(struct ax *, uint64_t);
+static int ax_pdu_add_oid(struct ax *, struct ax_oid *);
+static int ax_pdu_add_str(struct ax *, struct ax_ostring *);
+static int ax_pdu_add_varbindlist(struct ax *, struct ax_varbind *,
+ size_t);
+static int ax_pdu_add_searchrange(struct ax *, struct ax_searchrange *);
+static uint16_t ax_pdutoh16(struct ax_pdu_header *, uint8_t *);
+static uint32_t ax_pdutoh32(struct ax_pdu_header *, uint8_t *);
+static uint64_t ax_pdutoh64(struct ax_pdu_header *, uint8_t *);
+static ssize_t ax_pdutooid(struct ax_pdu_header *, struct ax_oid *,
+ uint8_t *, size_t);
+static ssize_t ax_pdutoostring(struct ax_pdu_header *,
+ struct ax_ostring *, uint8_t *, size_t);
+static ssize_t ax_pdutovarbind(struct ax_pdu_header *,
+ struct ax_varbind *, uint8_t *, size_t);
+
+struct ax *
+ax_new(int fd)
+{
+ struct ax *ax;
+
+ if (fd == -1) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if ((ax = calloc(1, sizeof(*ax))) == NULL)
+ return NULL;
+ ax->ax_fd = fd;
+ ax->ax_rbsize = 512;
+ if ((ax->ax_rbuf = malloc(ax->ax_rbsize)) == NULL)
+ goto fail;
+ ax->ax_byteorder = AX_BYTE_ORDER_NATIVE;
+
+ return ax;
+
+fail:
+ free(ax);
+ return NULL;
+}
+
+void
+ax_free(struct ax *ax)
+{
+ if (ax == NULL)
+ return;
+ close(ax->ax_fd);
+ free(ax->ax_rbuf);
+ free(ax->ax_wbuf);
+ free(ax->ax_packetids);
+ free(ax);
+}
+
+struct ax_pdu *
+ax_recv(struct ax *ax)
+{
+ struct ax_pdu *pdu;
+ struct ax_pdu_header header;
+ struct ax_pdu_response *response;
+ struct ax_varbind *varbind;
+ struct ax_pdu_searchrangelist *srl = NULL;
+ struct ax_pdu_varbindlist *vbl;
+ struct ax_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 < AX_PDU_HEADER) {
+ if ((nread = read(ax->ax_fd, ax->ax_rbuf + ax->ax_rblen,
+ AX_PDU_HEADER - ax->ax_rblen)) == 0) {
+ errno = ECONNRESET;
+ return NULL;
+ }
+ if (nread == -1)
+ return NULL;
+ ax->ax_rblen += nread;
+ if (ax->ax_rblen < AX_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 = ax_pdutoh32(&header, u8);
+ u8 += 4;
+ header.aph_transactionid = ax_pdutoh32(&header, u8);
+ u8 += 4;
+ header.aph_packetid = ax_pdutoh32(&header, u8);
+ u8 += 4;
+ header.aph_plength = ax_pdutoh32(&header, u8);
+
+ if (header.aph_version != 1) {
+ errno = EPROTO;
+ return NULL;
+ }
+ if (ax->ax_rblen < AX_PDU_HEADER + header.aph_plength) {
+ if (AX_PDU_HEADER + header.aph_plength > ax->ax_rbsize) {
+ rbsize = (((AX_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 - AX_PDU_HEADER));
+ if (nread == 0)
+ errno = ECONNRESET;
+ if (nread <= 0)
+ return NULL;
+ ax->ax_rblen += nread;
+ if (ax->ax_rblen < AX_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(AX_DEBUG) && defined(AX_DEBUG_VERBOSE)
+ {
+ char chars[4];
+ int print = 1;
+
+ fprintf(stderr, "received packet:\n");
+ for (i = 0; i < pdu->ap_header.aph_plength + AX_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) + AX_PDU_HEADER;
+ rawlen = pdu->ap_header.aph_plength;
+ if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NON_DEFAULT_CONTEXT) {
+ nread = ax_pdutoostring(&header, &(pdu->ap_context), u8,
+ rawlen);
+ if (nread == -1)
+ goto fail;
+ rawlen -= nread;
+ u8 += nread;
+ }
+
+ switch (pdu->ap_header.aph_type) {
+ case AX_PDU_TYPE_OPEN:
+ if (rawlen < 12) {
+ errno = EPROTO;
+ goto fail;
+ }
+ pdu->ap_payload.ap_open.ap_timeout = *u8;
+ rawlen -= 4;
+ u8 += 4;
+ if ((nread = ax_pdutooid(&header,
+ &(pdu->ap_payload.ap_open.ap_oid), u8, rawlen)) == -1)
+ goto fail;
+ rawlen -= nread;
+ u8 += nread;
+ if ((nread = ax_pdutoostring(&header,
+ &(pdu->ap_payload.ap_open.ap_descr), u8, rawlen)) == -1)
+ goto fail;
+ if (rawlen - nread != 0) {
+ errno = EPROTO;
+ goto fail;
+ }
+ break;
+ case AX_PDU_TYPE_CLOSE:
+ if (rawlen != 4) {
+ errno = EPROTO;
+ goto fail;
+ }
+ if (u8[0] != AX_CLOSE_OTHER &&
+ u8[0] != AX_CLOSEN_PARSEERROR &&
+ u8[0] != AX_CLOSE_PROTOCOLERROR &&
+ u8[0] != AX_CLOSE_TIMEOUTS &&
+ u8[0] != AX_CLOSE_SHUTDOWN &&
+ u8[0] != AX_CLOSE_BYMANAGER) {
+ errno = EPROTO;
+ goto fail;
+ }
+ pdu->ap_payload.ap_close.ap_reason = u8[0];
+ break;
+ case AX_PDU_TYPE_REGISTER:
+ if (rawlen < 8) {
+ errno = EPROTO;
+ goto fail;
+ }
+ pdu->ap_payload.ap_register.ap_timeout = *u8++;
+ pdu->ap_payload.ap_register.ap_priority = *u8++;
+ pdu->ap_payload.ap_register.ap_range_subid = *u8++;
+ u8++;
+ rawlen -= 4;
+ if ((nread = ax_pdutooid(&header,
+ &(pdu->ap_payload.ap_register.ap_subtree),
+ u8, rawlen)) == -1)
+ goto fail;
+ rawlen -= nread;
+ u8 += nread;
+ if (pdu->ap_payload.ap_register.ap_range_subid) {
+ if (rawlen != 4) {
+ errno = EPROTO;
+ goto fail;
+ }
+ pdu->ap_payload.ap_register.ap_upper_bound =
+ ax_pdutoh32(&header, u8);
+ rawlen -= 4;
+ }
+ if (rawlen != 0) {
+ errno = EPROTO;
+ goto fail;
+ }
+ break;
+ case AX_PDU_TYPE_UNREGISTER:
+ if (rawlen < 8) {
+ errno = EPROTO;
+ goto fail;
+ }
+ u8++;
+ pdu->ap_payload.ap_unregister.ap_priority = *u8++;
+ pdu->ap_payload.ap_unregister.ap_range_subid = *u8++;
+ u8++;
+ rawlen -= 4;
+ if ((nread = ax_pdutooid(&header,
+ &(pdu->ap_payload.ap_unregister.ap_subtree),
+ u8, rawlen)) == -1)
+ goto fail;
+ rawlen -= nread;
+ u8 += nread;
+ if (pdu->ap_payload.ap_unregister.ap_range_subid) {
+ if (rawlen != 4) {
+ errno = EPROTO;
+ goto fail;
+ }
+ pdu->ap_payload.ap_unregister.ap_upper_bound =
+ ax_pdutoh32(&header, u8);
+ rawlen -= 4;
+ }
+ if (rawlen != 0) {
+ errno = EPROTO;
+ goto fail;
+ }
+ break;
+ case AX_PDU_TYPE_GETBULK:
+ if (rawlen < 4) {
+ errno = EPROTO;
+ goto fail;
+ }
+ pdu->ap_payload.ap_getbulk.ap_nonrep =
+ ax_pdutoh16(&header, u8);
+ u8 += 2;
+ pdu->ap_payload.ap_getbulk.ap_maxrep =
+ ax_pdutoh16(&header, u8);
+ u8 += 2;
+ srl = &(pdu->ap_payload.ap_getbulk.ap_srl);
+ rawlen -= 4;
+ /* FALLTHROUGH */
+ case AX_PDU_TYPE_GET:
+ case AX_PDU_TYPE_GETNEXT:
+ if (pdu->ap_header.aph_type != AX_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 = ax_pdutooid(&header, &(sr->asr_start),
+ u8, rawlen)) == -1)
+ goto fail;
+ rawlen -= nread;
+ u8 += nread;
+ if ((nread = ax_pdutooid(&header, &(sr->asr_stop),
+ u8, rawlen)) == -1)
+ goto fail;
+ rawlen -= nread;
+ u8 += nread;
+ }
+ break;
+ case AX_PDU_TYPE_TESTSET:
+ case AX_PDU_TYPE_INDEXALLOCATE:
+ case AX_PDU_TYPE_INDEXDEALLOCATE:
+ case AX_PDU_TYPE_NOTIFY:
+ 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 = ax_pdutovarbind(&header,
+ &(vbl->ap_varbind[vbl->ap_nvarbind]), u8, rawlen);
+ if (nread == -1)
+ goto fail;
+ vbl->ap_nvarbind++;
+ u8 += nread;
+ rawlen -= nread;
+ }
+ break;
+ case AX_PDU_TYPE_COMMITSET:
+ case AX_PDU_TYPE_UNDOSET:
+ case AX_PDU_TYPE_CLEANUPSET:
+ case AX_PDU_TYPE_PING:
+ if (rawlen != 0) {
+ errno = EPROTO;
+ goto fail;
+ }
+ break;
+ case AX_PDU_TYPE_ADDAGENTCAPS:
+ nread = ax_pdutooid(&header,
+ &(pdu->ap_payload.ap_addagentcaps.ap_oid), u8, rawlen);
+ if (nread == -1)
+ goto fail;
+ rawlen -= nread;
+ u8 += nread;
+ nread = ax_pdutoostring(&header,
+ &(pdu->ap_payload.ap_addagentcaps.ap_descr), u8, rawlen);
+ if (nread == -1)
+ goto fail;
+ if (rawlen - nread != 0) {
+ errno = EPROTO;
+ goto fail;
+ }
+ break;
+ case AX_PDU_TYPE_REMOVEAGENTCAPS:
+ nread = ax_pdutooid(&header,
+ &(pdu->ap_payload.ap_removeagentcaps.ap_oid), u8, rawlen);
+ if (nread == -1)
+ goto fail;
+ if (rawlen - nread != 0) {
+ errno = EPROTO;
+ goto fail;
+ }
+ break;
+ case AX_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 = ax_pdutoh32(&header, u8);
+ u8 += 4;
+ response->ap_error = ax_pdutoh16(&header, u8);
+ u8 += 2;
+ response->ap_index = ax_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 = ax_pdutovarbind(&header,
+ &(response->ap_varbindlist[response->ap_nvarbind]),
+ u8, rawlen);
+ if (nread == -1)
+ goto fail;
+ response->ap_nvarbind++;
+ u8 += nread;
+ rawlen -= nread;
+ }
+ break;
+ default:
+ errno = EPROTO;
+ goto fail;
+ }
+
+ ax->ax_rblen = 0;
+
+ return pdu;
+fail:
+ ax_pdu_free(pdu);
+ return NULL;
+}
+
+static int
+ax_pdu_need(struct ax *ax, size_t need)
+{
+ uint8_t *wbuf;
+ size_t wbsize;
+
+ if (ax->ax_wbtlen + need >= ax->ax_wbsize) {
+ wbsize = (((ax->ax_wbtlen + need) / 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
+ax_send(struct ax *ax)
+{
+ ssize_t nwrite;
+
+ if (ax->ax_wblen != ax->ax_wbtlen) {
+ errno = EALREADY;
+ return -1;
+ }
+
+ if (ax->ax_wblen == 0)
+ return 0;
+
+#if defined(AX_DEBUG) && defined(AX_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
+ax_open(struct ax *ax, uint8_t timeout, struct ax_oid *oid,
+ struct ax_ostring *descr)
+{
+ if (ax_pdu_header(ax, AX_PDU_TYPE_OPEN, 0, 0, 0, 0,
+ NULL) == -1)
+ return 0;
+ ax_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 (ax_pdu_add_oid(ax, oid) == -1)
+ return 0;
+ if (ax_pdu_add_str(ax, descr) == -1)
+ return 0;
+
+ return ax_pdu_queue(ax);
+}
+
+uint32_t
+ax_close(struct ax *ax, uint32_t sessionid,
+ enum ax_close_reason reason)
+{
+ if (ax_pdu_header(ax, AX_PDU_TYPE_CLOSE, 0, sessionid, 0, 0,
+ NULL) == -1)
+ return 0;
+
+ if (ax_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 ax_pdu_queue(ax);
+}
+
+int
+ax_get(struct ax *ax, uint32_t sessionid, uint32_t transactionid,
+ uint32_t packetid, struct ax_ostring *context, struct ax_searchrange *srl,
+ size_t nsr)
+{
+ size_t i;
+
+ if (ax_pdu_header(ax, AX_PDU_TYPE_GET, 0, sessionid, transactionid,
+ packetid, context) == -1)
+ return -1;
+
+ for (i = 0; i < nsr; i++) {
+ if (ax_pdu_add_searchrange(ax, &(srl[i])) == -1)
+ return 0;
+ }
+
+ return ax_pdu_queue(ax);
+}
+
+int
+ax_getnext(struct ax *ax, uint32_t sessionid, uint32_t transactionid,
+ uint32_t packetid, struct ax_ostring *context, struct ax_searchrange *srl,
+ size_t nsr)
+{
+ size_t i;
+
+ if (ax_pdu_header(ax, AX_PDU_TYPE_GETNEXT, 0, sessionid, transactionid,
+ packetid, context) == -1)
+ return -1;
+
+ for (i = 0; i < nsr; i++) {
+ if (ax_pdu_add_searchrange(ax, &(srl[i])) == -1)
+ return 0;
+ }
+
+ return ax_pdu_queue(ax);
+}
+
+uint32_t
+ax_indexallocate(struct ax *ax, uint8_t flags, uint32_t sessionid,
+ struct ax_ostring *context, struct ax_varbind *vblist, size_t nvb)
+{
+ if (flags & ~(AX_PDU_FLAG_NEW_INDEX | AX_PDU_FLAG_ANY_INDEX)) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ if (ax_pdu_header(ax, AX_PDU_TYPE_INDEXALLOCATE, flags,
+ sessionid, 0, 0, context) == -1)
+ return 0;
+
+ if (ax_pdu_add_varbindlist(ax, vblist, nvb) == -1)
+ return 0;
+
+ return ax_pdu_queue(ax);
+}
+
+uint32_t
+ax_indexdeallocate(struct ax *ax, uint32_t sessionid,
+ struct ax_ostring *context, struct ax_varbind *vblist, size_t nvb)
+{
+ if (ax_pdu_header(ax, AX_PDU_TYPE_INDEXDEALLOCATE, 0,
+ sessionid, 0, 0, context) == -1)
+ return 0;
+
+ if (ax_pdu_add_varbindlist(ax, vblist, nvb) == -1)
+ return 0;
+
+ return ax_pdu_queue(ax);
+}
+
+uint32_t
+ax_addagentcaps(struct ax *ax, uint32_t sessionid,
+ struct ax_ostring *context, struct ax_oid *id,
+ struct ax_ostring *descr)
+{
+ if (ax_pdu_header(ax, AX_PDU_TYPE_ADDAGENTCAPS, 0,
+ sessionid, 0, 0, context) == -1)
+ return 0;
+ if (ax_pdu_add_oid(ax, id) == -1)
+ return 0;
+ if (ax_pdu_add_str(ax, descr) == -1)
+ return 0;
+
+ return ax_pdu_queue(ax);
+}
+
+uint32_t
+ax_removeagentcaps(struct ax *ax, uint32_t sessionid,
+ struct ax_ostring *context, struct ax_oid *id)
+{
+ if (ax_pdu_header(ax, AX_PDU_TYPE_REMOVEAGENTCAPS, 0,
+ sessionid, 0, 0, context) == -1)
+ return 0;
+ if (ax_pdu_add_oid(ax, id) == -1)
+ return 0;
+
+ return ax_pdu_queue(ax);
+
+}
+
+uint32_t
+ax_register(struct ax *ax, uint8_t flags, uint32_t sessionid,
+ struct ax_ostring *context, uint8_t timeout, uint8_t priority,
+ uint8_t range_subid, struct ax_oid *subtree, uint32_t upperbound)
+{
+ if (flags & ~(AX_PDU_FLAG_INSTANCE_REGISTRATION)) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ if (ax_pdu_header(ax, AX_PDU_TYPE_REGISTER, flags,
+ sessionid, 0, 0, context) == -1)
+ return 0;
+
+ if (ax_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 (ax_pdu_add_oid(ax, subtree) == -1)
+ return 0;
+ if (range_subid != 0) {
+ if (ax_pdu_add_uint32(ax, upperbound) == -1)
+ return 0;
+ }
+
+ return ax_pdu_queue(ax);
+}
+
+uint32_t
+ax_unregister(struct ax *ax, uint32_t sessionid,
+ struct ax_ostring *context, uint8_t priority, uint8_t range_subid,
+ struct ax_oid *subtree, uint32_t upperbound)
+{
+ if (ax_pdu_header(ax, AX_PDU_TYPE_UNREGISTER, 0,
+ sessionid, 0, 0, context) == -1)
+ return 0;
+
+ if (ax_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 (ax_pdu_add_oid(ax, subtree) == -1)
+ return 0;
+ if (range_subid != 0) {
+ if (ax_pdu_add_uint32(ax, upperbound) == -1)
+ return 0;
+ }
+
+ return ax_pdu_queue(ax);
+}
+
+int
+ax_response(struct ax *ax, uint32_t sessionid, uint32_t transactionid,
+ uint32_t packetid, struct ax_ostring *context, uint32_t sysuptime,
+ uint16_t error, uint16_t index, struct ax_varbind *vblist, size_t nvb)
+{
+ if (ax_pdu_header(ax, AX_PDU_TYPE_RESPONSE, 0, sessionid,
+ transactionid, packetid, context) == -1)
+ return -1;
+
+ if (ax_pdu_add_uint32(ax, sysuptime) == -1 ||
+ ax_pdu_add_uint16(ax, error) == -1 ||
+ ax_pdu_add_uint16(ax, index) == -1)
+ return -1;
+
+ if (ax_pdu_add_varbindlist(ax, vblist, nvb) == -1)
+ return -1;
+ if (ax_pdu_queue(ax) == 0)
+ return -1;
+ return 0;
+}
+
+void
+ax_pdu_free(struct ax_pdu *pdu)
+{
+ size_t i;
+ struct ax_pdu_response *response;
+ struct ax_pdu_varbindlist *vblist;
+
+ if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NON_DEFAULT_CONTEXT)
+ free(pdu->ap_context.aos_string);
+
+ switch (pdu->ap_header.aph_type) {
+ case AX_PDU_TYPE_OPEN:
+ free(pdu->ap_payload.ap_open.ap_descr.aos_string);
+ break;
+ case AX_PDU_TYPE_GET:
+ case AX_PDU_TYPE_GETNEXT:
+ case AX_PDU_TYPE_GETBULK:
+ free(pdu->ap_payload.ap_srl.ap_sr);
+ break;
+ case AX_PDU_TYPE_TESTSET:
+ case AX_PDU_TYPE_INDEXALLOCATE:
+ case AX_PDU_TYPE_INDEXDEALLOCATE:
+ vblist = &(pdu->ap_payload.ap_vbl);
+ for (i = 0; i < vblist->ap_nvarbind; i++)
+ ax_varbind_free(&(vblist->ap_varbind[i]));
+ free(vblist->ap_varbind);
+ break;
+ case AX_PDU_TYPE_RESPONSE:
+ response = &(pdu->ap_payload.ap_response);
+ for (i = 0; i < response->ap_nvarbind; i++)
+ ax_varbind_free(&(response->ap_varbindlist[i]));
+ free(response->ap_varbindlist);
+ break;
+ default:
+ break;
+ }
+ free(pdu);
+}
+
+void
+ax_varbind_free(struct ax_varbind *varbind)
+{
+ switch (varbind->avb_type) {
+ case AX_DATA_TYPE_OCTETSTRING:
+ case AX_DATA_TYPE_IPADDRESS:
+ case AX_DATA_TYPE_OPAQUE:
+ free(varbind->avb_data.avb_ostring.aos_string);
+ break;
+ default:
+ break;
+ }
+}
+
+const char *
+ax_error2string(enum ax_pdu_error error)
+{
+ static char buffer[64];
+ switch (error) {
+ case AX_PDU_ERROR_NOERROR:
+ return "No error";
+ case AX_PDU_ERROR_GENERR:
+ return "Generic error";
+ case AX_PDU_ERROR_NOACCESS:
+ return "No access";
+ case AX_PDU_ERROR_WRONGTYPE:
+ return "Wrong type";
+ case AX_PDU_ERROR_WRONGLENGTH:
+ return "Wrong length";
+ case AX_PDU_ERROR_WRONGENCODING:
+ return "Wrong encoding";
+ case AX_PDU_ERROR_WRONGVALUE:
+ return "Wrong value";
+ case AX_PDU_ERROR_NOCREATION:
+ return "No creation";
+ case AX_PDU_ERROR_INCONSISTENTVALUE:
+ return "Inconsistent value";
+ case AX_PDU_ERROR_RESOURCEUNAVAILABLE:
+ return "Resource unavailable";
+ case AX_PDU_ERROR_COMMITFAILED:
+ return "Commit failed";
+ case AX_PDU_ERROR_UNDOFAILED:
+ return "Undo failed";
+ case AX_PDU_ERROR_NOTWRITABLE:
+ return "Not writable";
+ case AX_PDU_ERROR_INCONSISTENTNAME:
+ return "Inconsistent name";
+ case AX_PDU_ERROR_OPENFAILED:
+ return "Open Failed";
+ case AX_PDU_ERROR_NOTOPEN:
+ return "Not open";
+ case AX_PDU_ERROR_INDEXWRONGTYPE:
+ return "Index wrong type";
+ case AX_PDU_ERROR_INDEXALREADYALLOCATED:
+ return "Index already allocated";
+ case AX_PDU_ERROR_INDEXNONEAVAILABLE:
+ return "Index none available";
+ case AX_PDU_ERROR_INDEXNOTALLOCATED:
+ return "Index not allocated";
+ case AX_PDU_ERROR_UNSUPPORTEDCONETXT:
+ return "Unsupported context";
+ case AX_PDU_ERROR_DUPLICATEREGISTRATION:
+ return "Duplicate registration";
+ case AX_PDU_ERROR_UNKNOWNREGISTRATION:
+ return "Unkown registration";
+ case AX_PDU_ERROR_UNKNOWNAGENTCAPS:
+ return "Unknown agent capabilities";
+ case AX_PDU_ERROR_PARSEERROR:
+ return "Parse error";
+ case AX_PDU_ERROR_REQUESTDENIED:
+ return "Request denied";
+ case AX_PDU_ERROR_PROCESSINGERROR:
+ return "Processing error";
+ }
+ snprintf(buffer, sizeof(buffer), "Unknown error: %d", error);
+ return buffer;
+}
+
+const char *
+ax_pdutype2string(enum ax_pdu_type type)
+{
+ static char buffer[64];
+ switch(type) {
+ case AX_PDU_TYPE_OPEN:
+ return "agentx-Open-PDU";
+ case AX_PDU_TYPE_CLOSE:
+ return "agentx-Close-PDU";
+ case AX_PDU_TYPE_REGISTER:
+ return "agentx-Register-PDU";
+ case AX_PDU_TYPE_UNREGISTER:
+ return "agentx-Unregister-PDU";
+ case AX_PDU_TYPE_GET:
+ return "agentx-Get-PDU";
+ case AX_PDU_TYPE_GETNEXT:
+ return "agentx-GetNext-PDU";
+ case AX_PDU_TYPE_GETBULK:
+ return "agentx-GetBulk-PDU";
+ case AX_PDU_TYPE_TESTSET:
+ return "agentx-TestSet-PDU";
+ case AX_PDU_TYPE_COMMITSET:
+ return "agentx-CommitSet-PDU";
+ case AX_PDU_TYPE_UNDOSET:
+ return "agentx-UndoSet-PDU";
+ case AX_PDU_TYPE_CLEANUPSET:
+ return "agentx-CleanupSet-PDU";
+ case AX_PDU_TYPE_NOTIFY:
+ return "agentx-Notify-PDU";
+ case AX_PDU_TYPE_PING:
+ return "agentx-Ping-PDU";
+ case AX_PDU_TYPE_INDEXALLOCATE:
+ return "agentx-IndexAllocate-PDU";
+ case AX_PDU_TYPE_INDEXDEALLOCATE:
+ return "agentx-IndexDeallocate-PDU";
+ case AX_PDU_TYPE_ADDAGENTCAPS:
+ return "agentx-AddAgentCaps-PDU";
+ case AX_PDU_TYPE_REMOVEAGENTCAPS:
+ return "agentx-RemoveAgentCaps-PDU";
+ case AX_PDU_TYPE_RESPONSE:
+ return "agentx-Response-PDU";
+ }
+ snprintf(buffer, sizeof(buffer), "Unknown type: %d", type);
+ return buffer;
+}
+
+const char *
+ax_closereason2string(enum ax_close_reason reason)
+{
+ static char buffer[64];
+
+ switch (reason) {
+ case AX_CLOSE_OTHER:
+ return "Undefined reason";
+ case AX_CLOSEN_PARSEERROR:
+ return "Too many AgentX parse errors from peer";
+ case AX_CLOSE_PROTOCOLERROR:
+ return "Too many AgentX protocol errors from peer";
+ case AX_CLOSE_TIMEOUTS:
+ return "Too many timeouts waiting for peer";
+ case AX_CLOSE_SHUTDOWN:
+ return "shutting down";
+ case AX_CLOSE_BYMANAGER:
+ return "Manager shuts down";
+ }
+ snprintf(buffer, sizeof(buffer), "Unknown reason: %d", reason);
+ return buffer;
+}
+
+const char *
+ax_oid2string(struct ax_oid *oid)
+{
+ return ax_oidrange2string(oid, 0, 0);
+}
+
+const char *
+ax_oidrange2string(struct ax_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 *
+ax_varbind2string(struct ax_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 AX_DATA_TYPE_INTEGER:
+ snprintf(buf, sizeof(buf), "%s: (int)%d",
+ ax_oid2string(&(vb->avb_oid)), vb->avb_data.avb_int32);
+ break;
+ case AX_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",
+ ax_oid2string(&(vb->avb_oid)), tmpbuf);
+ if (ret >= (int) sizeof(buf)) {
+ p = strrchr(buf, ' ');
+ strlcpy(p, "...", 4);
+ }
+ } else {
+ ret = snprintf(buf, sizeof(buf), "%s: (string)",
+ ax_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 AX_DATA_TYPE_NULL:
+ snprintf(buf, sizeof(buf), "%s: <null>",
+ ax_oid2string(&(vb->avb_oid)));
+ break;
+ case AX_DATA_TYPE_OID:
+ strlcpy(tmpbuf,
+ ax_oid2string(&(vb->avb_data.avb_oid)), sizeof(tmpbuf));
+ snprintf(buf, sizeof(buf), "%s: (oid)%s",
+ ax_oid2string(&(vb->avb_oid)), tmpbuf);
+ break;
+ case AX_DATA_TYPE_IPADDRESS:
+ if (vb->avb_data.avb_ostring.aos_slen != 4) {
+ snprintf(buf, sizeof(buf), "%s: (ipaddress)<invalid>",
+ ax_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",
+ ax_oid2string(&(vb->avb_oid)),
+ strerror(errno));
+ break;
+ }
+ snprintf(buf, sizeof(buf), "%s: (ipaddress)%s",
+ ax_oid2string(&(vb->avb_oid)), tmpbuf);
+ break;
+ case AX_DATA_TYPE_COUNTER32:
+ snprintf(buf, sizeof(buf), "%s: (counter32)%u",
+ ax_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32);
+ break;
+ case AX_DATA_TYPE_GAUGE32:
+ snprintf(buf, sizeof(buf), "%s: (gauge32)%u",
+ ax_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32);
+ break;
+ case AX_DATA_TYPE_TIMETICKS:
+ snprintf(buf, sizeof(buf), "%s: (timeticks)%u",
+ ax_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32);
+ break;
+ case AX_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",
+ ax_oid2string(&(vb->avb_oid)), tmpbuf);
+ if (ret >= (int) sizeof(buf)) {
+ p = strrchr(buf, ' ');
+ strlcpy(p, "...", 4);
+ }
+ break;
+ case AX_DATA_TYPE_COUNTER64:
+ snprintf(buf, sizeof(buf), "%s: (counter64)%"PRIu64,
+ ax_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint64);
+ break;
+ case AX_DATA_TYPE_NOSUCHOBJECT:
+ snprintf(buf, sizeof(buf), "%s: <noSuchObject>",
+ ax_oid2string(&(vb->avb_oid)));
+ break;
+ case AX_DATA_TYPE_NOSUCHINSTANCE:
+ snprintf(buf, sizeof(buf), "%s: <noSuchInstance>",
+ ax_oid2string(&(vb->avb_oid)));
+ break;
+ case AX_DATA_TYPE_ENDOFMIBVIEW:
+ snprintf(buf, sizeof(buf), "%s: <endOfMibView>",
+ ax_oid2string(&(vb->avb_oid)));
+ break;
+ }
+ return buf;
+}
+
+int
+ax_oid_cmp(struct ax_oid *o1, struct ax_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
+ax_oid_add(struct ax_oid *oid, uint32_t value)
+{
+ if (oid->aoi_idlen == AX_OID_MAX_LEN)
+ return -1;
+ oid->aoi_id[oid->aoi_idlen++] = value;
+ return 0;
+}
+
+static uint32_t
+ax_pdu_queue(struct ax *ax)
+{
+ struct ax_pdu_header header;
+ uint32_t packetid, plength;
+ size_t wbtlen = ax->ax_wbtlen;
+
+ header.aph_flags = ax->ax_byteorder == AX_BYTE_ORDER_BE ?
+ AX_PDU_FLAG_NETWORK_BYTE_ORDER : 0;
+ packetid = ax_pdutoh32(&header, &(ax->ax_wbuf[ax->ax_wblen + 12]));
+ plength = (ax->ax_wbtlen - ax->ax_wblen) - AX_PDU_HEADER;
+ ax->ax_wbtlen = ax->ax_wblen + 16;
+ (void)ax_pdu_add_uint32(ax, plength);
+
+ ax->ax_wblen = ax->ax_wbtlen = wbtlen;
+
+ return packetid;
+}
+
+static int
+ax_pdu_header(struct ax *ax, enum ax_pdu_type type, uint8_t flags,
+ uint32_t sessionid, uint32_t transactionid, uint32_t packetid,
+ struct ax_ostring *context)
+{
+ if (ax->ax_wblen != ax->ax_wbtlen) {
+ errno = EALREADY;
+ return -1;
+ }
+
+ if (ax_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 |= AX_PDU_FLAG_NON_DEFAULT_CONTEXT;
+ if (ax->ax_byteorder == AX_BYTE_ORDER_BE)
+ flags |= AX_PDU_FLAG_NETWORK_BYTE_ORDER;
+ ax->ax_wbuf[ax->ax_wbtlen++] = flags;
+ ax->ax_wbuf[ax->ax_wbtlen++] = 0;
+ if (packetid == 0)
+ packetid = ax_packetid(ax);
+ if (ax_pdu_add_uint32(ax, sessionid) == -1 ||
+ ax_pdu_add_uint32(ax, transactionid) == -1 ||
+ ax_pdu_add_uint32(ax, packetid) == -1 ||
+ ax_pdu_need(ax, 4) == -1)
+ return -1;
+ ax->ax_wbtlen += 4;
+ if (context != NULL) {
+ if (ax_pdu_add_str(ax, context) == -1)
+ return -1;
+ }
+
+ return 0;
+}
+
+static uint32_t
+ax_packetid(struct ax *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
+ax_pdu_add_uint16(struct ax *ax, uint16_t value)
+{
+ if (ax_pdu_need(ax, sizeof(value)) == -1)
+ return -1;
+
+ if (ax->ax_byteorder == AX_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
+ax_pdu_add_uint32(struct ax *ax, uint32_t value)
+{
+ if (ax_pdu_need(ax, sizeof(value)) == -1)
+ return -1;
+
+ if (ax->ax_byteorder == AX_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
+ax_pdu_add_uint64(struct ax *ax, uint64_t value)
+{
+ if (ax_pdu_need(ax, sizeof(value)) == -1)
+ return -1;
+
+ if (ax->ax_byteorder == AX_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
+ax_pdu_add_oid(struct ax *ax, struct ax_oid *oid)
+{
+ static struct ax_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 (ax_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++] = oid->aoi_include;
+ ax->ax_wbuf[ax->ax_wbtlen++] = 0;
+
+ for (; i < n_subid; i++) {
+ if (ax_pdu_add_uint32(ax, oid->aoi_id[i]) == -1)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+ax_pdu_add_str(struct ax *ax, struct ax_ostring *str)
+{
+ size_t length, zeroes;
+
+ if (ax_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 (ax_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
+ax_pdu_add_varbindlist(struct ax *ax,
+ struct ax_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 (ax_pdu_add_uint16(ax, temp) == -1 ||
+ ax_pdu_need(ax, 2))
+ return -1;
+ memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, 2);
+ ax->ax_wbtlen += 2;
+ if (ax_pdu_add_oid(ax, &(vblist[i].avb_oid)) == -1)
+ return -1;
+ switch (vblist[i].avb_type) {
+ case AX_DATA_TYPE_INTEGER:
+ if (ax_pdu_add_uint32(ax,
+ vblist[i].avb_data.avb_int32) == -1)
+ return -1;
+ break;
+ case AX_DATA_TYPE_COUNTER32:
+ case AX_DATA_TYPE_GAUGE32:
+ case AX_DATA_TYPE_TIMETICKS:
+ if (ax_pdu_add_uint32(ax,
+ vblist[i].avb_data.avb_uint32) == -1)
+ return -1;
+ break;
+ case AX_DATA_TYPE_COUNTER64:
+ if (ax_pdu_add_uint64(ax,
+ vblist[i].avb_data.avb_uint64) == -1)
+ return -1;
+ break;
+ case AX_DATA_TYPE_OCTETSTRING:
+ case AX_DATA_TYPE_IPADDRESS:
+ case AX_DATA_TYPE_OPAQUE:
+ if (ax_pdu_add_str(ax,
+ &(vblist[i].avb_data.avb_ostring)) == -1)
+ return -1;
+ break;
+ case AX_DATA_TYPE_OID:
+ if (ax_pdu_add_oid(ax,
+ &(vblist[i].avb_data.avb_oid)) == -1)
+ return -1;
+ break;
+ case AX_DATA_TYPE_NULL:
+ case AX_DATA_TYPE_NOSUCHOBJECT:
+ case AX_DATA_TYPE_NOSUCHINSTANCE:
+ case AX_DATA_TYPE_ENDOFMIBVIEW:
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int
+ax_pdu_add_searchrange(struct ax *ax, struct ax_searchrange *sr)
+{
+ if (ax_pdu_add_oid(ax, &(sr->asr_start)) == -1 ||
+ ax_pdu_add_oid(ax, &(sr->asr_stop)) == -1)
+ return -1;
+ return 0;
+}
+
+static uint16_t
+ax_pdutoh16(struct ax_pdu_header *header, uint8_t *buf)
+{
+ uint16_t value;
+
+ memcpy(&value, buf, sizeof(value));
+ if (header->aph_flags & AX_PDU_FLAG_NETWORK_BYTE_ORDER)
+ return be16toh(value);
+ return le16toh(value);
+}
+
+static uint32_t
+ax_pdutoh32(struct ax_pdu_header *header, uint8_t *buf)
+{
+ uint32_t value;
+
+ memcpy(&value, buf, sizeof(value));
+ if (header->aph_flags & AX_PDU_FLAG_NETWORK_BYTE_ORDER)
+ return be32toh(value);
+ return le32toh(value);
+}
+
+static uint64_t
+ax_pdutoh64(struct ax_pdu_header *header, uint8_t *buf)
+{
+ uint64_t value;
+
+ memcpy(&value, buf, sizeof(value));
+ if (header->aph_flags & AX_PDU_FLAG_NETWORK_BYTE_ORDER)
+ return be64toh(value);
+ return le64toh(value);
+}
+
+static ssize_t
+ax_pdutooid(struct ax_pdu_header *header, struct ax_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] = ax_pdutoh32(header, buf);
+
+ return nread;
+
+fail:
+ errno = EPROTO;
+ return -1;
+}
+
+static ssize_t
+ax_pdutoostring(struct ax_pdu_header *header,
+ struct ax_ostring *ostring, uint8_t *buf, size_t rawlen)
+{
+ ssize_t nread;
+
+ if (rawlen < 4)
+ goto fail;
+
+ ostring->aos_slen = ax_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
+ax_pdutovarbind(struct ax_pdu_header *header,
+ struct ax_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 = ax_pdutoh16(header, buf);
+
+ buf += 4;
+ rawlen -= 4;
+ nread = ax_pdutooid(header, &(varbind->avb_oid), buf, rawlen);
+ if (nread == -1)
+ return -1;
+ rread += nread;
+ buf += nread;
+ rawlen -= nread;
+
+ switch(varbind->avb_type) {
+ case AX_DATA_TYPE_INTEGER:
+ if (rawlen < 4)
+ goto fail;
+ varbind->avb_data.avb_int32 = ax_pdutoh32(header, buf);
+ return rread + 4;
+ case AX_DATA_TYPE_COUNTER32:
+ case AX_DATA_TYPE_GAUGE32:
+ case AX_DATA_TYPE_TIMETICKS:
+ if (rawlen < 4)
+ goto fail;
+ varbind->avb_data.avb_uint32 = ax_pdutoh32(header, buf);
+ return rread + 4;
+ case AX_DATA_TYPE_COUNTER64:
+ if (rawlen < 8)
+ goto fail;
+ varbind->avb_data.avb_uint64 = ax_pdutoh64(header, buf);
+ return rread + 8;
+ case AX_DATA_TYPE_OCTETSTRING:
+ case AX_DATA_TYPE_IPADDRESS:
+ case AX_DATA_TYPE_OPAQUE:
+ nread = ax_pdutoostring(header,
+ &(varbind->avb_data.avb_ostring), buf, rawlen);
+ if (nread == -1)
+ return -1;
+ return nread + rread;
+ case AX_DATA_TYPE_OID:
+ nread = ax_pdutooid(header, &(varbind->avb_data.avb_oid),
+ buf, rawlen);
+ if (nread == -1)
+ return -1;
+ return nread + rread;
+ case AX_DATA_TYPE_NULL:
+ case AX_DATA_TYPE_NOSUCHOBJECT:
+ case AX_DATA_TYPE_NOSUCHINSTANCE:
+ case AX_DATA_TYPE_ENDOFMIBVIEW:
+ return rread;
+ }
+
+fail:
+ errno = EPROTO;
+ return -1;
+}
diff --git a/usr.sbin/snmpd/ax.h b/usr.sbin/snmpd/ax.h
new file mode 100644
index 00000000000..3b2ea93ba99
--- /dev/null
+++ b/usr.sbin/snmpd/ax.h
@@ -0,0 +1,265 @@
+/* $OpenBSD: ax.h,v 1.1 2022/08/23 08:56:20 martijn Exp $ */
+/*
+ * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdint.h>
+
+#define AX_PDU_FLAG_INSTANCE_REGISTRATION (1 << 0)
+#define AX_PDU_FLAG_NEW_INDEX (1 << 1)
+#define AX_PDU_FLAG_ANY_INDEX (1 << 2)
+#define AX_PDU_FLAG_NON_DEFAULT_CONTEXT (1 << 3)
+#define AX_PDU_FLAG_NETWORK_BYTE_ORDER (1 << 4)
+
+#define AX_PRIORITY_DEFAULT 127
+
+enum ax_byte_order {
+ AX_BYTE_ORDER_BE,
+ AX_BYTE_ORDER_LE
+};
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define AX_BYTE_ORDER_NATIVE AX_BYTE_ORDER_BE
+#else
+#define AX_BYTE_ORDER_NATIVE AX_BYTE_ORDER_LE
+#endif
+
+enum ax_pdu_type {
+ AX_PDU_TYPE_OPEN = 1,
+ AX_PDU_TYPE_CLOSE = 2,
+ AX_PDU_TYPE_REGISTER = 3,
+ AX_PDU_TYPE_UNREGISTER = 4,
+ AX_PDU_TYPE_GET = 5,
+ AX_PDU_TYPE_GETNEXT = 6,
+ AX_PDU_TYPE_GETBULK = 7,
+ AX_PDU_TYPE_TESTSET = 8,
+ AX_PDU_TYPE_COMMITSET = 9,
+ AX_PDU_TYPE_UNDOSET = 10,
+ AX_PDU_TYPE_CLEANUPSET = 11,
+ AX_PDU_TYPE_NOTIFY = 12,
+ AX_PDU_TYPE_PING = 13,
+ AX_PDU_TYPE_INDEXALLOCATE = 14,
+ AX_PDU_TYPE_INDEXDEALLOCATE = 15,
+ AX_PDU_TYPE_ADDAGENTCAPS = 16,
+ AX_PDU_TYPE_REMOVEAGENTCAPS = 17,
+ AX_PDU_TYPE_RESPONSE = 18
+};
+
+enum ax_pdu_error {
+ AX_PDU_ERROR_NOERROR = 0,
+ AX_PDU_ERROR_GENERR = 5,
+ AX_PDU_ERROR_NOACCESS = 6,
+ AX_PDU_ERROR_WRONGTYPE = 7,
+ AX_PDU_ERROR_WRONGLENGTH = 8,
+ AX_PDU_ERROR_WRONGENCODING = 9,
+ AX_PDU_ERROR_WRONGVALUE = 10,
+ AX_PDU_ERROR_NOCREATION = 11,
+ AX_PDU_ERROR_INCONSISTENTVALUE = 12,
+ AX_PDU_ERROR_RESOURCEUNAVAILABLE = 13,
+ AX_PDU_ERROR_COMMITFAILED = 14,
+ AX_PDU_ERROR_UNDOFAILED = 15,
+ AX_PDU_ERROR_NOTWRITABLE = 17,
+ AX_PDU_ERROR_INCONSISTENTNAME = 18,
+ AX_PDU_ERROR_OPENFAILED = 256,
+ AX_PDU_ERROR_NOTOPEN = 257,
+ AX_PDU_ERROR_INDEXWRONGTYPE = 258,
+ AX_PDU_ERROR_INDEXALREADYALLOCATED = 259,
+ AX_PDU_ERROR_INDEXNONEAVAILABLE = 260,
+ AX_PDU_ERROR_INDEXNOTALLOCATED = 261,
+ AX_PDU_ERROR_UNSUPPORTEDCONETXT = 262,
+ AX_PDU_ERROR_DUPLICATEREGISTRATION = 263,
+ AX_PDU_ERROR_UNKNOWNREGISTRATION = 264,
+ AX_PDU_ERROR_UNKNOWNAGENTCAPS = 265,
+ AX_PDU_ERROR_PARSEERROR = 266,
+ AX_PDU_ERROR_REQUESTDENIED = 267,
+ AX_PDU_ERROR_PROCESSINGERROR = 268
+};
+
+enum ax_data_type {
+ AX_DATA_TYPE_INTEGER = 2,
+ AX_DATA_TYPE_OCTETSTRING = 4,
+ AX_DATA_TYPE_NULL = 5,
+ AX_DATA_TYPE_OID = 6,
+ AX_DATA_TYPE_IPADDRESS = 64,
+ AX_DATA_TYPE_COUNTER32 = 65,
+ AX_DATA_TYPE_GAUGE32 = 66,
+ AX_DATA_TYPE_TIMETICKS = 67,
+ AX_DATA_TYPE_OPAQUE = 68,
+ AX_DATA_TYPE_COUNTER64 = 70,
+ AX_DATA_TYPE_NOSUCHOBJECT = 128,
+ AX_DATA_TYPE_NOSUCHINSTANCE = 129,
+ AX_DATA_TYPE_ENDOFMIBVIEW = 130
+};
+
+enum ax_close_reason {
+ AX_CLOSE_OTHER = 1,
+ AX_CLOSEN_PARSEERROR = 2,
+ AX_CLOSE_PROTOCOLERROR = 3,
+ AX_CLOSE_TIMEOUTS = 4,
+ AX_CLOSE_SHUTDOWN = 5,
+ AX_CLOSE_BYMANAGER = 6
+};
+
+struct ax {
+ int ax_fd;
+ enum ax_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 AX_PRIMITIVE
+#define AX_PRIMITIVE
+
+#define AX_OID_MAX_LEN 128
+
+struct ax_oid {
+ uint8_t aoi_include;
+ uint32_t aoi_id[AX_OID_MAX_LEN];
+ size_t aoi_idlen;
+};
+
+struct ax_ostring {
+ unsigned char *aos_string;
+ uint32_t aos_slen;
+};
+#endif
+
+struct ax_searchrange {
+ struct ax_oid asr_start;
+ struct ax_oid asr_stop;
+};
+
+struct ax_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 ax_varbind {
+ enum ax_data_type avb_type;
+ struct ax_oid avb_oid;
+ union ax_data {
+ int32_t avb_int32;
+ uint32_t avb_uint32;
+ uint64_t avb_uint64;
+ struct ax_ostring avb_ostring;
+ struct ax_oid avb_oid;
+ } avb_data;
+};
+
+struct ax_pdu {
+ struct ax_pdu_header ap_header;
+ struct ax_ostring ap_context;
+ union {
+ struct ax_pdu_open {
+ uint8_t ap_timeout;
+ struct ax_oid ap_oid;
+ struct ax_ostring ap_descr;
+ } ap_open;
+ struct ax_pdu_close {
+ enum ax_close_reason ap_reason;
+ } ap_close;
+ struct ax_pdu_register {
+ uint8_t ap_timeout;
+ uint8_t ap_priority;
+ uint8_t ap_range_subid;
+ struct ax_oid ap_subtree;
+ uint32_t ap_upper_bound;
+ } ap_register;
+ struct ax_pdu_unregister {
+ uint8_t ap_priority;
+ uint8_t ap_range_subid;
+ struct ax_oid ap_subtree;
+ uint32_t ap_upper_bound;
+ } ap_unregister;
+ struct ax_pdu_searchrangelist {
+ size_t ap_nsr;
+ struct ax_searchrange *ap_sr;
+ } ap_srl;
+ struct ax_pdu_getbulk {
+ uint16_t ap_nonrep;
+ uint16_t ap_maxrep;
+ struct ax_pdu_searchrangelist ap_srl;
+ } ap_getbulk;
+ struct ax_pdu_varbindlist {
+ struct ax_varbind *ap_varbind;
+ size_t ap_nvarbind;
+ } ap_vbl;
+ struct ax_pdu_addagentcaps {
+ struct ax_oid ap_oid;
+ struct ax_ostring ap_descr;
+ } ap_addagentcaps;
+ struct ax_pdu_removeagentcaps {
+ struct ax_oid ap_oid;
+ } ap_removeagentcaps;
+ struct ax_pdu_response {
+ uint32_t ap_uptime;
+ enum ax_pdu_error ap_error;
+ uint16_t ap_index;
+ struct ax_varbind *ap_varbindlist;
+ size_t ap_nvarbind;
+ } ap_response;
+ } ap_payload;
+};
+
+struct ax *ax_new(int);
+void ax_free(struct ax *);
+struct ax_pdu *ax_recv(struct ax *);
+ssize_t ax_send(struct ax *);
+uint32_t ax_open(struct ax *, uint8_t, struct ax_oid *,
+ struct ax_ostring *);
+uint32_t ax_close(struct ax *, uint32_t, enum ax_close_reason);
+int ax_get(struct ax *, uint32_t, uint32_t, uint32_t, struct ax_ostring *,
+ struct ax_searchrange *, size_t);
+int ax_getnext(struct ax *, uint32_t, uint32_t, uint32_t, struct ax_ostring *,
+ struct ax_searchrange *, size_t);
+uint32_t ax_indexallocate(struct ax *, uint8_t, uint32_t,
+ struct ax_ostring *, struct ax_varbind *, size_t);
+uint32_t ax_indexdeallocate(struct ax *, uint32_t,
+ struct ax_ostring *, struct ax_varbind *, size_t);
+uint32_t ax_addagentcaps(struct ax *, uint32_t, struct ax_ostring *,
+ struct ax_oid *, struct ax_ostring *);
+uint32_t ax_removeagentcaps(struct ax *, uint32_t,
+ struct ax_ostring *, struct ax_oid *);
+uint32_t ax_register(struct ax *, uint8_t, uint32_t,
+ struct ax_ostring *, uint8_t, uint8_t, uint8_t, struct ax_oid *,
+ uint32_t);
+uint32_t ax_unregister(struct ax *, uint32_t, struct ax_ostring *,
+ uint8_t, uint8_t, struct ax_oid *, uint32_t);
+int ax_response(struct ax *, uint32_t, uint32_t, uint32_t,
+ struct ax_ostring *, uint32_t, uint16_t, uint16_t,
+ struct ax_varbind *, size_t);
+void ax_pdu_free(struct ax_pdu *);
+void ax_varbind_free(struct ax_varbind *);
+const char *ax_error2string(enum ax_pdu_error);
+const char *ax_pdutype2string(enum ax_pdu_type);
+const char *ax_oid2string(struct ax_oid *);
+const char *ax_oidrange2string(struct ax_oid *, uint8_t, uint32_t);
+const char *ax_varbind2string(struct ax_varbind *);
+const char *ax_closereason2string(enum ax_close_reason);
+int ax_oid_cmp(struct ax_oid *, struct ax_oid *);
+int ax_oid_add(struct ax_oid *, uint32_t);
diff --git a/usr.sbin/snmpd/parse.y b/usr.sbin/snmpd/parse.y
index 6c26f3a0f4e..f0562637e1a 100644
--- a/usr.sbin/snmpd/parse.y
+++ b/usr.sbin/snmpd/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.76 2022/06/30 11:53:07 martijn Exp $ */
+/* $OpenBSD: parse.y,v 1.77 2022/08/23 08:56:20 martijn Exp $ */
/*
* Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org>
@@ -41,6 +41,7 @@
#include <err.h>
#include <errno.h>
#include <event.h>
+#include <grp.h>
#include <inttypes.h>
#include <limits.h>
#include <stdint.h>
@@ -48,6 +49,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <netdb.h>
+#include <pwd.h>
#include <string.h>
#include <syslog.h>
@@ -111,6 +113,7 @@ typedef struct {
struct host *host;
struct timeval tv;
struct ber_oid *oid;
+ struct agentx_master ax;
struct {
int type;
void *data;
@@ -122,10 +125,17 @@ typedef struct {
int lineno;
} YYSTYPE;
+#define axm_opts axm_fd
+#define AXM_PATH 1 << 0
+#define AXM_OWNER 1 << 1
+#define AXM_GROUP 1 << 2
+#define AXM_MODE 1 << 3
+
%}
%token INCLUDE
%token LISTEN ON READ WRITE NOTIFY SNMPV1 SNMPV2 SNMPV3
+%token AGENTX PATH OWNER GROUP MODE
%token ENGINEID PEN OPENBSD IP4 IP6 MAC TEXT OCTETS AGENTID HOSTHASH
%token SYSTEM CONTACT DESCR LOCATION NAME OBJECTID SERVICES RTFILTER
%token READONLY READWRITE OCTETSTRING INTEGER COMMUNITY TRAP RECEIVER
@@ -141,6 +151,7 @@ typedef struct {
%type <v.oid> oid hostoid trapoid
%type <v.auth> auth
%type <v.enc> enc
+%type <v.ax> agentxopts agentxopt
%%
@@ -203,6 +214,53 @@ yesno : STRING {
;
main : LISTEN ON listen_udptcp
+ | AGENTX agentxopts {
+ struct agentx_master *master, *test;
+ struct group *grp;
+
+ if ((master = malloc(sizeof(*master))) == NULL) {
+ yyerror("malloc");
+ YYERROR;
+ }
+ master->axm_fd = -1;
+ master->axm_sun.sun_len = sizeof(master->axm_sun);
+ master->axm_sun.sun_family = AF_UNIX;
+ if ($2.axm_opts & AXM_PATH)
+ strlcpy(master->axm_sun.sun_path,
+ $2.axm_sun.sun_path,
+ sizeof(master->axm_sun.sun_path));
+ else
+ strlcpy(master->axm_sun.sun_path,
+ AGENTX_MASTER_PATH,
+ sizeof(master->axm_sun.sun_path));
+ master->axm_owner = $2.axm_opts & AXM_OWNER ?
+ $2.axm_owner : 0;
+ if ($2.axm_opts & AXM_GROUP)
+ master->axm_group = $2.axm_group;
+ else {
+ if ((grp = getgrnam(AGENTX_GROUP)) == NULL) {
+ yyerror("agentx: group %s not found",
+ AGENTX_GROUP);
+ YYERROR;
+ }
+ master->axm_group = grp->gr_gid;
+ }
+ master->axm_mode = $2.axm_opts & AXM_MODE ?
+ $2.axm_mode : 0660;
+
+ TAILQ_FOREACH(test, &conf->sc_agentx_masters,
+ axm_entry) {
+ if (strcmp(test->axm_sun.sun_path,
+ master->axm_sun.sun_path) == 0) {
+ yyerror("agentx: duplicate entry: %s",
+ test->axm_sun.sun_path);
+ YYERROR;
+ }
+ }
+
+ TAILQ_INSERT_TAIL(&conf->sc_agentx_masters, master,
+ axm_entry);
+ }
| engineid_local {
if (conf->sc_engineid_len != 0) {
yyerror("Redefinition of engineid");
@@ -408,6 +466,112 @@ port : /* empty */ {
}
;
+agentxopt : PATH STRING {
+ if ($2[0] != '/') {
+ yyerror("agentx path: must be absolute");
+ YYERROR;
+ }
+ if (strlcpy($$.axm_sun.sun_path, $2,
+ sizeof($$.axm_sun.sun_path)) >=
+ sizeof($$.axm_sun.sun_path)) {
+ yyerror("agentx path: too long");
+ YYERROR;
+ }
+ $$.axm_opts = AXM_PATH;
+ }
+ | OWNER NUMBER {
+ if ($2 > UID_MAX) {
+ yyerror("agentx owner: too large");
+ YYERROR;
+ }
+ $$.axm_owner = $2;
+ $$.axm_opts = AXM_OWNER;
+ }
+ | OWNER STRING {
+ struct passwd *pw;
+ const char *errstr;
+
+ $$.axm_owner = strtonum($2, 0, UID_MAX, &errstr);
+ if (errstr != NULL && errno == ERANGE) {
+ yyerror("agentx owner: %s", errstr);
+ YYERROR;
+ }
+ if ((pw = getpwnam($2)) == NULL) {
+ yyerror("agentx owner: user not found");
+ YYERROR;
+ }
+ $$.axm_owner = pw->pw_uid;
+ $$.axm_opts = AXM_OWNER;
+ }
+ | GROUP NUMBER {
+ if ($2 > GID_MAX) {
+ yyerror("agentx group: too large");
+ YYERROR;
+ }
+ $$.axm_group = $2;
+ $$.axm_opts = AXM_GROUP;
+ }
+ | GROUP STRING {
+ struct group *gr;
+ const char *errstr;
+
+ $$.axm_group = strtonum($2, 0, GID_MAX, &errstr);
+ if (errstr != NULL && errno == ERANGE) {
+ yyerror("agentx group: %s", errstr);
+ YYERROR;
+ }
+ if ((gr = getgrnam($2)) == NULL) {
+ yyerror("agentx group: group not found");
+ YYERROR;
+ }
+ $$.axm_group = gr->gr_gid;
+ $$.axm_opts = AXM_GROUP;
+ }
+ | MODE NUMBER {
+ long mode;
+ char *endptr, str[21];
+
+ snprintf(str, sizeof(str), "%lld", $2);
+ errno = 0;
+ mode = strtol(str, &endptr, 8);
+ if (errno != 0 || endptr[0] != '\0' ||
+ mode <= 0 || mode > 0777) {
+ yyerror("agentx mode: invalid");
+ YYERROR;
+ }
+ $$.axm_mode = mode;
+ $$.axm_opts = AXM_MODE;
+ }
+ ;
+
+agentxopts : /* empty */ {
+ bzero(&$$, sizeof($$));
+ }
+ | agentxopts agentxopt {
+ if ($$.axm_opts & $2.axm_opts) {
+ yyerror("agentx: duplicate option");
+ YYERROR;
+ }
+ switch ($2.axm_opts) {
+ case AXM_PATH:
+ strlcpy($$.axm_sun.sun_path,
+ $2.axm_sun.sun_path,
+ sizeof($$.axm_sun.sun_path));
+ break;
+ case AXM_OWNER:
+ $$.axm_owner = $2.axm_owner;
+ break;
+ case AXM_GROUP:
+ $$.axm_group = $2.axm_group;
+ break;
+ case AXM_MODE:
+ $$.axm_mode = $2.axm_mode;
+ break;
+ }
+ $$.axm_opts |= $2.axm_opts;
+ }
+ ;
+
enginefmt : IP4 STRING {
struct in_addr addr;
@@ -1024,6 +1188,7 @@ lookup(char *s)
/* this has to be sorted always */
static const struct keywords keywords[] = {
{ "agentid", AGENTID },
+ { "agentx", AGENTX },
{ "auth", AUTH },
{ "authkey", AUTHKEY },
{ "blocklist", BLOCKLIST },
@@ -1036,6 +1201,7 @@ lookup(char *s)
{ "engineid", ENGINEID },
{ "filter-pf-addresses", PFADDRFILTER },
{ "filter-routes", RTFILTER },
+ { "group", GROUP },
{ "handle", HANDLE },
{ "hosthash", HOSTHASH },
{ "include", INCLUDE },
@@ -1045,6 +1211,7 @@ lookup(char *s)
{ "listen", LISTEN },
{ "location", LOCATION },
{ "mac", MAC },
+ { "mode", MODE },
{ "name", NAME },
{ "none", NONE },
{ "notify", NOTIFY },
@@ -1052,6 +1219,8 @@ lookup(char *s)
{ "oid", OBJECTID },
{ "on", ON },
{ "openbsd", OPENBSD },
+ { "owner", OWNER },
+ { "path", PATH },
{ "pen", PEN },
{ "port", PORT },
{ "read", READ },
@@ -1448,6 +1617,7 @@ parse_config(const char *filename, u_int flags)
conf->sc_flags = flags;
conf->sc_confpath = filename;
TAILQ_INIT(&conf->sc_addresses);
+ TAILQ_INIT(&conf->sc_agentx_masters);
TAILQ_INIT(&conf->sc_trapreceivers);
conf->sc_min_seclevel = SNMP_MSGFLAG_AUTH | SNMP_MSGFLAG_PRIV;
diff --git a/usr.sbin/snmpd/snmpd.conf.5 b/usr.sbin/snmpd/snmpd.conf.5
index a415f0391d3..72730069f2f 100644
--- a/usr.sbin/snmpd/snmpd.conf.5
+++ b/usr.sbin/snmpd/snmpd.conf.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: snmpd.conf.5,v 1.60 2022/06/30 11:28:36 martijn Exp $
+.\" $OpenBSD: snmpd.conf.5,v 1.61 2022/08/23 08:56:21 martijn Exp $
.\"
.\" Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org>
.\"
@@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: June 30 2022 $
+.Dd $Mdocdate: August 23 2022 $
.Dt SNMPD.CONF 5
.Os
.Sh NAME
@@ -151,6 +151,21 @@ Having
set requires at least one
.Ic trap handle
statement.
+.It Ic agentx Oo Ic path Ar path Oc Oo Ic owner Ar owner Oc Oo group Ar group Oc Oo Ic mode Ar mode Oc
+Set up an agentx master socket at
+.Ar path
+and defaults to
+.Pa /var/agentx/master .
+Socket owner, group, and permissions can be set with
+.Ar owner ,
+.Ar group ,
+and
+.Ar mode
+respectively and defaults to root, _agentx, and 660.
+Multiple
+.Ic agentx
+statements are supported.
+Only unix sockets are supported.
.It Ic engineid Oo Ic pen Ar enterprise Oc Ar format
Set the snmp engineid, used for instance identification and key
generation for the
diff --git a/usr.sbin/snmpd/snmpd.h b/usr.sbin/snmpd/snmpd.h
index fa4d9aef015..faa26cd3f3a 100644
--- a/usr.sbin/snmpd/snmpd.h
+++ b/usr.sbin/snmpd/snmpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: snmpd.h,v 1.103 2022/06/30 11:28:36 martijn Exp $ */
+/* $OpenBSD: snmpd.h,v 1.104 2022/08/23 08:56:21 martijn Exp $ */
/*
* Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org>
@@ -21,6 +21,7 @@
#define SNMPD_H
#include <sys/tree.h>
+#include <sys/un.h>
#include <net/if.h>
#include <net/if_dl.h>
@@ -53,6 +54,9 @@
#define SNMP_PORT "161"
#define SNMPTRAP_PORT "162"
+#define AGENTX_MASTER_PATH "/var/agentx/master"
+#define AGENTX_GROUP "_agentx"
+
#define SNMPD_MAXSTRLEN 484
#define SNMPD_MAXCOMMUNITYLEN SNMPD_MAXSTRLEN
#define SNMPD_MAXVARBIND 0x7fffffff
@@ -502,6 +506,19 @@ struct address {
};
TAILQ_HEAD(addresslist, address);
+struct agentx_master {
+ int axm_fd;
+ struct sockaddr_un axm_sun;
+ uid_t axm_owner;
+ gid_t axm_group;
+ mode_t axm_mode;
+
+ struct event axm_ev;
+
+ TAILQ_ENTRY(agentx_master) axm_entry;
+};
+TAILQ_HEAD(axmasterlist, agentx_master);
+
#define ADDRESS_FLAG_READ 0x01
#define ADDRESS_FLAG_WRITE 0x02
#define ADDRESS_FLAG_NOTIFY 0x04
@@ -575,6 +592,7 @@ struct snmpd {
const char *sc_confpath;
struct addresslist sc_addresses;
+ struct axmasterlist sc_agentx_masters;
struct timeval sc_starttime;
u_int32_t sc_engine_boots;
diff --git a/usr.sbin/snmpd/snmpe.c b/usr.sbin/snmpd/snmpe.c
index 19c0f009941..3fa22de026f 100644
--- a/usr.sbin/snmpd/snmpe.c
+++ b/usr.sbin/snmpd/snmpe.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: snmpe.c,v 1.82 2022/01/19 11:00:56 martijn Exp $ */
+/* $OpenBSD: snmpe.c,v 1.83 2022/08/23 08:56:21 martijn Exp $ */
/*
* Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org>
@@ -33,6 +33,7 @@
#include <errno.h>
#include <event.h>
#include <fcntl.h>
+#include <locale.h>
#include <string.h>
#include <unistd.h>
#include <pwd.h>
@@ -74,6 +75,9 @@ snmpe(struct privsep *ps, struct privsep_proc *p)
struct oid *oid;
#endif
+ if ((setlocale(LC_CTYPE, "en_US.UTF-8")) == NULL)
+ fatal("setlocale(LC_CTYPE, \"en_US.UTF-8\")");
+
#ifdef DEBUG
for (oid = NULL; (oid = smi_foreach(oid, 0)) != NULL;) {
smi_oid2string(&oid->o_id, buf, sizeof(buf), 0);
@@ -81,6 +85,8 @@ snmpe(struct privsep *ps, struct privsep_proc *p)
}
#endif
+ appl();
+
/* bind SNMP UDP/TCP sockets */
TAILQ_FOREACH(h, &env->sc_addresses, entry)
if ((h->fd = snmpe_bind(h)) == -1)