1*f0bcdb5cSmartijn /* $OpenBSD: application_agentx.c,v 1.16 2024/02/06 12:44:27 martijn Exp $ */
24100cc5fSmartijn /*
34100cc5fSmartijn * Copyright (c) 2022 Martijn van Duren <martijn@openbsd.org>
44100cc5fSmartijn *
54100cc5fSmartijn * Permission to use, copy, modify, and distribute this software for any
64100cc5fSmartijn * purpose with or without fee is hereby granted, provided that the above
74100cc5fSmartijn * copyright notice and this permission notice appear in all copies.
84100cc5fSmartijn *
94100cc5fSmartijn * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
104100cc5fSmartijn * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
114100cc5fSmartijn * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
124100cc5fSmartijn * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
134100cc5fSmartijn * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
144100cc5fSmartijn * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
154100cc5fSmartijn * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
164100cc5fSmartijn */
174100cc5fSmartijn
184100cc5fSmartijn #include <sys/queue.h>
194100cc5fSmartijn #include <sys/socket.h>
204100cc5fSmartijn #include <sys/stat.h>
21a9292d2aSmartijn #include <sys/tree.h>
224100cc5fSmartijn #include <sys/un.h>
234100cc5fSmartijn
24a9292d2aSmartijn #include <ber.h>
254100cc5fSmartijn #include <errno.h>
264100cc5fSmartijn #include <event.h>
274100cc5fSmartijn #include <inttypes.h>
28a9292d2aSmartijn #include <stdint.h>
294100cc5fSmartijn #include <stdio.h>
30a9292d2aSmartijn #include <stdlib.h>
314100cc5fSmartijn #include <string.h>
324100cc5fSmartijn #include <unistd.h>
334100cc5fSmartijn
344100cc5fSmartijn #include "application.h"
354100cc5fSmartijn #include "ax.h"
364100cc5fSmartijn #include "log.h"
37*f0bcdb5cSmartijn #include "mib.h"
384100cc5fSmartijn #include "smi.h"
394100cc5fSmartijn #include "snmp.h"
404100cc5fSmartijn #include "snmpd.h"
414100cc5fSmartijn
424100cc5fSmartijn #define AGENTX_DEFAULTTIMEOUT 5
434100cc5fSmartijn
444100cc5fSmartijn struct appl_agentx_connection {
454100cc5fSmartijn uint32_t conn_id;
461985d3ebSmartijn /*
471985d3ebSmartijn * A backend has several overruling properties:
481985d3ebSmartijn * - If it exits, snmpd crashes
491985d3ebSmartijn * - All registrations are priority 1
501985d3ebSmartijn * - All registrations own the subtree.
511985d3ebSmartijn */
521985d3ebSmartijn int conn_backend;
534100cc5fSmartijn struct ax *conn_ax;
544100cc5fSmartijn struct event conn_rev;
554100cc5fSmartijn struct event conn_wev;
564100cc5fSmartijn
574100cc5fSmartijn TAILQ_HEAD(, appl_agentx_session) conn_sessions;
584100cc5fSmartijn RB_ENTRY(appl_agentx_connection) conn_entry;
594100cc5fSmartijn };
604100cc5fSmartijn
614100cc5fSmartijn struct appl_agentx_session {
624100cc5fSmartijn uint32_t sess_id;
634100cc5fSmartijn struct appl_agentx_connection *sess_conn;
644100cc5fSmartijn /*
654100cc5fSmartijn * RFC 2741 section 7.1.1:
664100cc5fSmartijn * All subsequent AgentX protocol operations initiated by the master
674100cc5fSmartijn * agent for this session must use this byte ordering and set this bit
684100cc5fSmartijn * accordingly.
694100cc5fSmartijn */
704100cc5fSmartijn enum ax_byte_order sess_byteorder;
714100cc5fSmartijn uint8_t sess_timeout;
724100cc5fSmartijn struct ax_oid sess_oid;
734100cc5fSmartijn struct ax_ostring sess_descr;
744100cc5fSmartijn struct appl_backend sess_backend;
754100cc5fSmartijn
764100cc5fSmartijn RB_ENTRY(appl_agentx_session) sess_entry;
774100cc5fSmartijn TAILQ_ENTRY(appl_agentx_session) sess_conn_entry;
784100cc5fSmartijn };
794100cc5fSmartijn
804100cc5fSmartijn void appl_agentx_listen(struct agentx_master *);
814100cc5fSmartijn void appl_agentx_accept(int, short, void *);
82273d3fb0Smartijn void appl_agentx_free(struct appl_agentx_connection *, enum appl_close_reason);
834100cc5fSmartijn void appl_agentx_recv(int, short, void *);
844100cc5fSmartijn void appl_agentx_open(struct appl_agentx_connection *, struct ax_pdu *);
854100cc5fSmartijn void appl_agentx_close(struct appl_agentx_session *, struct ax_pdu *);
864100cc5fSmartijn void appl_agentx_forceclose(struct appl_backend *, enum appl_close_reason);
874100cc5fSmartijn void appl_agentx_session_free(struct appl_agentx_session *);
884100cc5fSmartijn void appl_agentx_register(struct appl_agentx_session *, struct ax_pdu *);
894100cc5fSmartijn void appl_agentx_unregister(struct appl_agentx_session *, struct ax_pdu *);
904100cc5fSmartijn void appl_agentx_get(struct appl_backend *, int32_t, int32_t, const char *,
914100cc5fSmartijn struct appl_varbind *);
924100cc5fSmartijn void appl_agentx_getnext(struct appl_backend *, int32_t, int32_t, const char *,
934100cc5fSmartijn struct appl_varbind *);
949bbbacf6Smartijn void appl_agentx_addagentcaps(struct appl_agentx_session *, struct ax_pdu *);
959bbbacf6Smartijn void appl_agentx_removeagentcaps(struct appl_agentx_session *, struct ax_pdu *);
964100cc5fSmartijn void appl_agentx_response(struct appl_agentx_session *, struct ax_pdu *);
974100cc5fSmartijn void appl_agentx_send(int, short, void *);
984100cc5fSmartijn struct ber_oid *appl_agentx_oid2ber_oid(struct ax_oid *, struct ber_oid *);
994100cc5fSmartijn struct ber_element *appl_agentx_value2ber_element(struct ax_varbind *);
1004100cc5fSmartijn struct ax_ostring *appl_agentx_string2ostring(const char *,
1014100cc5fSmartijn struct ax_ostring *);
1024100cc5fSmartijn int appl_agentx_cmp(struct appl_agentx_connection *,
1034100cc5fSmartijn struct appl_agentx_connection *);
1044100cc5fSmartijn int appl_agentx_session_cmp(struct appl_agentx_session *,
1054100cc5fSmartijn struct appl_agentx_session *);
1064100cc5fSmartijn
1074100cc5fSmartijn struct appl_backend_functions appl_agentx_functions = {
1084100cc5fSmartijn .ab_close = appl_agentx_forceclose,
1094100cc5fSmartijn .ab_get = appl_agentx_get,
1104100cc5fSmartijn .ab_getnext = appl_agentx_getnext,
1114100cc5fSmartijn .ab_getbulk = NULL, /* not properly supported in application.c and libagentx */
1124100cc5fSmartijn };
1134100cc5fSmartijn
1144100cc5fSmartijn RB_HEAD(appl_agentx_conns, appl_agentx_connection) appl_agentx_conns =
1154100cc5fSmartijn RB_INITIALIZER(&appl_agentx_conns);
1164100cc5fSmartijn RB_HEAD(appl_agentx_sessions, appl_agentx_session) appl_agentx_sessions =
1174100cc5fSmartijn RB_INITIALIZER(&appl_agentx_sessions);
1184100cc5fSmartijn
1194100cc5fSmartijn RB_PROTOTYPE_STATIC(appl_agentx_conns, appl_agentx_connection, conn_entry,
1204100cc5fSmartijn appl_agentx_cmp);
1214100cc5fSmartijn RB_PROTOTYPE_STATIC(appl_agentx_sessions, appl_agentx_session, sess_entry,
1224100cc5fSmartijn appl_agentx_session_cmp);
1234100cc5fSmartijn
1244100cc5fSmartijn void
appl_agentx(void)1254100cc5fSmartijn appl_agentx(void)
1264100cc5fSmartijn {
1274100cc5fSmartijn struct agentx_master *master;
1284100cc5fSmartijn
1294100cc5fSmartijn TAILQ_FOREACH(master, &(snmpd_env->sc_agentx_masters), axm_entry)
1304100cc5fSmartijn appl_agentx_listen(master);
1314100cc5fSmartijn }
1324100cc5fSmartijn
1334100cc5fSmartijn void
appl_agentx_init(void)1344100cc5fSmartijn appl_agentx_init(void)
1354100cc5fSmartijn {
1364100cc5fSmartijn struct agentx_master *master;
1374100cc5fSmartijn
1384100cc5fSmartijn TAILQ_FOREACH(master, &(snmpd_env->sc_agentx_masters), axm_entry) {
1394100cc5fSmartijn if (master->axm_fd == -1)
1404100cc5fSmartijn continue;
1414100cc5fSmartijn event_set(&(master->axm_ev), master->axm_fd,
1424100cc5fSmartijn EV_READ | EV_PERSIST, appl_agentx_accept, master);
1434100cc5fSmartijn event_add(&(master->axm_ev), NULL);
1444100cc5fSmartijn log_info("AgentX: listening on %s", master->axm_sun.sun_path);
1454100cc5fSmartijn }
1464100cc5fSmartijn }
1474100cc5fSmartijn void
appl_agentx_listen(struct agentx_master * master)1484100cc5fSmartijn appl_agentx_listen(struct agentx_master *master)
1494100cc5fSmartijn {
1504100cc5fSmartijn mode_t mask;
1514100cc5fSmartijn
1524100cc5fSmartijn unlink(master->axm_sun.sun_path);
1534100cc5fSmartijn
1544100cc5fSmartijn mask = umask(0777);
1554100cc5fSmartijn if ((master->axm_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1 ||
1564100cc5fSmartijn bind(master->axm_fd, (struct sockaddr *)&(master->axm_sun),
1574100cc5fSmartijn sizeof(master->axm_sun)) == -1 ||
1584100cc5fSmartijn listen(master->axm_fd, 5)) {
15969acb4a3Smartijn log_warn("AgentX: listen %s", master->axm_sun.sun_path);
1604100cc5fSmartijn umask(mask);
1614100cc5fSmartijn return;
1624100cc5fSmartijn }
1634100cc5fSmartijn umask(mask);
1644100cc5fSmartijn if (chown(master->axm_sun.sun_path, master->axm_owner,
1654100cc5fSmartijn master->axm_group) == -1) {
1664100cc5fSmartijn log_warn("AgentX: chown %s", master->axm_sun.sun_path);
1674100cc5fSmartijn goto fail;
1684100cc5fSmartijn }
1694100cc5fSmartijn if (chmod(master->axm_sun.sun_path, master->axm_mode) == -1) {
1704100cc5fSmartijn log_warn("AgentX: chmod %s", master->axm_sun.sun_path);
1714100cc5fSmartijn goto fail;
1724100cc5fSmartijn }
1734100cc5fSmartijn return;
1744100cc5fSmartijn fail:
1754100cc5fSmartijn close(master->axm_fd);
1764100cc5fSmartijn master->axm_fd = -1;
1774100cc5fSmartijn }
1784100cc5fSmartijn
1794100cc5fSmartijn void
appl_agentx_shutdown(void)1804100cc5fSmartijn appl_agentx_shutdown(void)
1814100cc5fSmartijn {
1824100cc5fSmartijn struct appl_agentx_connection *conn, *tconn;
1834100cc5fSmartijn
1844100cc5fSmartijn RB_FOREACH_SAFE(conn, appl_agentx_conns, &appl_agentx_conns, tconn)
185273d3fb0Smartijn appl_agentx_free(conn, APPL_CLOSE_REASONSHUTDOWN);
1864100cc5fSmartijn }
1874100cc5fSmartijn
1884100cc5fSmartijn void
appl_agentx_accept(int masterfd,short event,void * cookie)1894100cc5fSmartijn appl_agentx_accept(int masterfd, short event, void *cookie)
1904100cc5fSmartijn {
1914100cc5fSmartijn int fd;
1924100cc5fSmartijn struct agentx_master *master = cookie;
1934100cc5fSmartijn struct sockaddr_un sun;
1944100cc5fSmartijn socklen_t sunlen = sizeof(sun);
1954100cc5fSmartijn struct appl_agentx_connection *conn = NULL;
1964100cc5fSmartijn
1974100cc5fSmartijn if ((fd = accept(masterfd, (struct sockaddr *)&sun, &sunlen)) == -1) {
1984100cc5fSmartijn log_warn("AgentX: accept %s", master->axm_sun.sun_path);
1994100cc5fSmartijn return;
2004100cc5fSmartijn }
2014100cc5fSmartijn
2024100cc5fSmartijn if ((conn = malloc(sizeof(*conn))) == NULL) {
2034100cc5fSmartijn log_warn(NULL);
2044100cc5fSmartijn goto fail;
2054100cc5fSmartijn }
2064100cc5fSmartijn
2071985d3ebSmartijn conn->conn_backend = 0;
2084100cc5fSmartijn TAILQ_INIT(&(conn->conn_sessions));
2094100cc5fSmartijn if ((conn->conn_ax = ax_new(fd)) == NULL) {
2104100cc5fSmartijn log_warn(NULL);
2114100cc5fSmartijn goto fail;
2124100cc5fSmartijn }
2134100cc5fSmartijn
2144100cc5fSmartijn do {
2154100cc5fSmartijn conn->conn_id = arc4random();
2164100cc5fSmartijn } while (RB_INSERT(appl_agentx_conns,
2174100cc5fSmartijn &appl_agentx_conns, conn) != NULL);
2184100cc5fSmartijn
2194100cc5fSmartijn event_set(&(conn->conn_rev), fd, EV_READ | EV_PERSIST,
2204100cc5fSmartijn appl_agentx_recv, conn);
2214100cc5fSmartijn event_add(&(conn->conn_rev), NULL);
2224100cc5fSmartijn event_set(&(conn->conn_wev), fd, EV_WRITE, appl_agentx_send, conn);
22369acb4a3Smartijn log_info("AgentX(%"PRIu32"): new connection", conn->conn_id);
2244100cc5fSmartijn
2254100cc5fSmartijn return;
2264100cc5fSmartijn fail:
2274100cc5fSmartijn close(fd);
2284100cc5fSmartijn free(conn);
2294100cc5fSmartijn }
2304100cc5fSmartijn
2314100cc5fSmartijn void
appl_agentx_backend(int fd)2321985d3ebSmartijn appl_agentx_backend(int fd)
2331985d3ebSmartijn {
2341985d3ebSmartijn struct appl_agentx_connection *conn;
2351985d3ebSmartijn
2361985d3ebSmartijn if ((conn = malloc(sizeof(*conn))) == NULL)
2371985d3ebSmartijn fatal(NULL);
2381985d3ebSmartijn
2391985d3ebSmartijn conn->conn_backend = 1;
2401985d3ebSmartijn TAILQ_INIT(&(conn->conn_sessions));
2411985d3ebSmartijn if ((conn->conn_ax = ax_new(fd)) == NULL)
2421985d3ebSmartijn fatal("ax_new");
2431985d3ebSmartijn
2441985d3ebSmartijn do {
2451985d3ebSmartijn conn->conn_id = arc4random();
2461985d3ebSmartijn } while (RB_INSERT(appl_agentx_conns,
2471985d3ebSmartijn &appl_agentx_conns, conn) != NULL);
2481985d3ebSmartijn
2491985d3ebSmartijn event_set(&(conn->conn_rev), fd, EV_READ | EV_PERSIST,
2501985d3ebSmartijn appl_agentx_recv, conn);
2511985d3ebSmartijn event_add(&(conn->conn_rev), NULL);
2521985d3ebSmartijn event_set(&(conn->conn_wev), fd, EV_WRITE, appl_agentx_send, conn);
2531985d3ebSmartijn }
2541985d3ebSmartijn
2551985d3ebSmartijn void
appl_agentx_free(struct appl_agentx_connection * conn,enum appl_close_reason reason)256273d3fb0Smartijn appl_agentx_free(struct appl_agentx_connection *conn,
257273d3fb0Smartijn enum appl_close_reason reason)
2584100cc5fSmartijn {
2594100cc5fSmartijn struct appl_agentx_session *session;
2604100cc5fSmartijn
2614100cc5fSmartijn while ((session = TAILQ_FIRST(&(conn->conn_sessions))) != NULL) {
2624100cc5fSmartijn if (conn->conn_ax == NULL)
2634100cc5fSmartijn appl_agentx_session_free(session);
2644100cc5fSmartijn else
2654100cc5fSmartijn appl_agentx_forceclose(&(session->sess_backend),
266273d3fb0Smartijn reason);
2674100cc5fSmartijn }
2684100cc5fSmartijn
269eb6ea173Smartijn event_del(&(conn->conn_rev));
270eb6ea173Smartijn event_del(&(conn->conn_wev));
271eb6ea173Smartijn
2724100cc5fSmartijn RB_REMOVE(appl_agentx_conns, &appl_agentx_conns, conn);
273eb6ea173Smartijn if (conn->conn_ax != NULL)
274eb6ea173Smartijn (void)ax_send(conn->conn_ax);
2754100cc5fSmartijn ax_free(conn->conn_ax);
2761985d3ebSmartijn if (conn->conn_backend)
2771985d3ebSmartijn fatalx("AgentX(%"PRIu32"): disappeared unexpected",
2781985d3ebSmartijn conn->conn_id);
2794100cc5fSmartijn free(conn);
2804100cc5fSmartijn }
2814100cc5fSmartijn
2824100cc5fSmartijn void
appl_agentx_recv(int fd,short event,void * cookie)2834100cc5fSmartijn appl_agentx_recv(int fd, short event, void *cookie)
2844100cc5fSmartijn {
2854100cc5fSmartijn struct appl_agentx_connection *conn = cookie;
28669014b58Smartijn struct appl_agentx_session *session = NULL;
2874100cc5fSmartijn struct ax_pdu *pdu;
28869014b58Smartijn enum appl_error error;
28969014b58Smartijn char name[100];
2904100cc5fSmartijn
29169014b58Smartijn snprintf(name, sizeof(name), "AgentX(%"PRIu32")", conn->conn_id);
2924100cc5fSmartijn if ((pdu = ax_recv(conn->conn_ax)) == NULL) {
2934100cc5fSmartijn if (errno == EAGAIN)
2944100cc5fSmartijn return;
29569014b58Smartijn log_warn("%s", name);
2964100cc5fSmartijn /*
2974100cc5fSmartijn * Either the connection is dead, or we had garbage on the line.
2984100cc5fSmartijn * Both make sure we can't continue on this stream.
2994100cc5fSmartijn */
3004100cc5fSmartijn if (errno == ECONNRESET) {
3014100cc5fSmartijn ax_free(conn->conn_ax);
3024100cc5fSmartijn conn->conn_ax = NULL;
3034100cc5fSmartijn }
304273d3fb0Smartijn appl_agentx_free(conn, errno == EPROTO ?
305273d3fb0Smartijn APPL_CLOSE_REASONPROTOCOLERROR : APPL_CLOSE_REASONOTHER);
3064100cc5fSmartijn return;
3074100cc5fSmartijn }
3084100cc5fSmartijn
3094100cc5fSmartijn conn->conn_ax->ax_byteorder = pdu->ap_header.aph_flags &
3104100cc5fSmartijn AX_PDU_FLAG_NETWORK_BYTE_ORDER ?
3114100cc5fSmartijn AX_BYTE_ORDER_BE : AX_BYTE_ORDER_LE;
3124100cc5fSmartijn if (pdu->ap_header.aph_type != AX_PDU_TYPE_OPEN) {
3134100cc5fSmartijn /* Make sure we only look for connection-local sessions */
3144100cc5fSmartijn TAILQ_FOREACH(session, &(conn->conn_sessions),
3154100cc5fSmartijn sess_conn_entry) {
3164100cc5fSmartijn if (session->sess_id == pdu->ap_header.aph_sessionid)
3174100cc5fSmartijn break;
3184100cc5fSmartijn }
3194100cc5fSmartijn if (session == NULL) {
32069014b58Smartijn log_warnx("%s: Session %"PRIu32" not found for request",
32169014b58Smartijn name, pdu->ap_header.aph_sessionid);
32269014b58Smartijn error = APPL_ERROR_NOTOPEN;
3234100cc5fSmartijn goto fail;
3244100cc5fSmartijn }
32569014b58Smartijn strlcpy(name, session->sess_backend.ab_name, sizeof(name));
3264100cc5fSmartijn /*
3274100cc5fSmartijn * RFC2741 section 7.1.1 bullet 4 is unclear on what byte order
3284100cc5fSmartijn * the response should be. My best guess is that it makes more
3294100cc5fSmartijn * sense that replies are in the same byte-order as what was
3304100cc5fSmartijn * requested.
3314100cc5fSmartijn * In practice we always have the same byte order as when we
3324100cc5fSmartijn * opened the session, so it's likely a non-issue, however, we
3334100cc5fSmartijn * can change to session byte order here.
3344100cc5fSmartijn */
3354100cc5fSmartijn }
3364100cc5fSmartijn
33769014b58Smartijn if (pdu->ap_header.aph_flags & AX_PDU_FLAG_INSTANCE_REGISTRATION) {
33869014b58Smartijn if (pdu->ap_header.aph_type != AX_PDU_TYPE_REGISTER) {
33969014b58Smartijn log_warnx("%s: %s: Invalid INSTANCE_REGISTRATION flag",
34069014b58Smartijn name, ax_pdutype2string(pdu->ap_header.aph_flags));
34169014b58Smartijn error = APPL_ERROR_PARSEERROR;
34269014b58Smartijn goto fail;
34369014b58Smartijn }
34469014b58Smartijn }
34569014b58Smartijn if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NEW_INDEX) {
34669014b58Smartijn if (pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXALLOCATE &&
34769014b58Smartijn pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXDEALLOCATE) {
34869014b58Smartijn log_warnx("%s: %s: Invalid NEW_INDEX flag", name,
34969014b58Smartijn ax_pdutype2string(pdu->ap_header.aph_flags));
35069014b58Smartijn error = APPL_ERROR_PARSEERROR;
35169014b58Smartijn goto fail;
35269014b58Smartijn }
35369014b58Smartijn }
35469014b58Smartijn if (pdu->ap_header.aph_flags & AX_PDU_FLAG_ANY_INDEX) {
35569014b58Smartijn if (pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXALLOCATE &&
35669014b58Smartijn pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXDEALLOCATE) {
35769014b58Smartijn log_warnx("%s: %s: Invalid ANY_INDEX flag", name,
35869014b58Smartijn ax_pdutype2string(pdu->ap_header.aph_flags));
35969014b58Smartijn error = APPL_ERROR_PARSEERROR;
36069014b58Smartijn goto fail;
36169014b58Smartijn }
36269014b58Smartijn }
36369014b58Smartijn if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NON_DEFAULT_CONTEXT) {
36469014b58Smartijn if (pdu->ap_header.aph_type != AX_PDU_TYPE_REGISTER &&
36569014b58Smartijn pdu->ap_header.aph_type != AX_PDU_TYPE_UNREGISTER &&
36669014b58Smartijn pdu->ap_header.aph_type != AX_PDU_TYPE_ADDAGENTCAPS &&
36769014b58Smartijn pdu->ap_header.aph_type != AX_PDU_TYPE_REMOVEAGENTCAPS &&
36869014b58Smartijn pdu->ap_header.aph_type != AX_PDU_TYPE_GET &&
36969014b58Smartijn pdu->ap_header.aph_type != AX_PDU_TYPE_GETNEXT &&
37069014b58Smartijn pdu->ap_header.aph_type != AX_PDU_TYPE_GETBULK &&
37169014b58Smartijn pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXALLOCATE &&
37269014b58Smartijn pdu->ap_header.aph_type != AX_PDU_TYPE_INDEXDEALLOCATE &&
37369014b58Smartijn pdu->ap_header.aph_type != AX_PDU_TYPE_NOTIFY &&
37469014b58Smartijn pdu->ap_header.aph_type != AX_PDU_TYPE_TESTSET &&
37569014b58Smartijn pdu->ap_header.aph_type != AX_PDU_TYPE_PING) {
37669014b58Smartijn log_warnx("%s: %s: Invalid NON_DEFAULT_CONTEXT flag",
37769014b58Smartijn name, ax_pdutype2string(pdu->ap_header.aph_flags));
37869014b58Smartijn error = APPL_ERROR_PARSEERROR;
37969014b58Smartijn goto fail;
38069014b58Smartijn }
381253352b6Smartijn if (appl_context(pdu->ap_context.aos_string, 0) == NULL) {
382253352b6Smartijn log_warnx("%s: %s: Unsupported context",
383253352b6Smartijn name, ax_pdutype2string(pdu->ap_header.aph_flags));
384253352b6Smartijn error = APPL_ERROR_UNSUPPORTEDCONTEXT;
385253352b6Smartijn goto fail;
386253352b6Smartijn }
38769014b58Smartijn }
3884100cc5fSmartijn switch (pdu->ap_header.aph_type) {
3894100cc5fSmartijn case AX_PDU_TYPE_OPEN:
3904100cc5fSmartijn appl_agentx_open(conn, pdu);
3914100cc5fSmartijn break;
3924100cc5fSmartijn case AX_PDU_TYPE_CLOSE:
3934100cc5fSmartijn appl_agentx_close(session, pdu);
3944100cc5fSmartijn break;
3954100cc5fSmartijn case AX_PDU_TYPE_REGISTER:
3964100cc5fSmartijn appl_agentx_register(session, pdu);
3974100cc5fSmartijn break;
3984100cc5fSmartijn case AX_PDU_TYPE_UNREGISTER:
3994100cc5fSmartijn appl_agentx_unregister(session, pdu);
4004100cc5fSmartijn break;
4014100cc5fSmartijn case AX_PDU_TYPE_GET:
4024100cc5fSmartijn case AX_PDU_TYPE_GETNEXT:
4034100cc5fSmartijn case AX_PDU_TYPE_GETBULK:
4044100cc5fSmartijn case AX_PDU_TYPE_TESTSET:
4054100cc5fSmartijn case AX_PDU_TYPE_COMMITSET:
4064100cc5fSmartijn case AX_PDU_TYPE_UNDOSET:
4074100cc5fSmartijn case AX_PDU_TYPE_CLEANUPSET:
40869014b58Smartijn log_warnx("%s: %s: Not an adminsitrative message", name,
40969014b58Smartijn ax_pdutype2string(pdu->ap_header.aph_type));
41069014b58Smartijn error = APPL_ERROR_PARSEERROR;
4114100cc5fSmartijn goto fail;
4124100cc5fSmartijn case AX_PDU_TYPE_NOTIFY:
41369014b58Smartijn log_warnx("%s: %s: not supported", name,
4144100cc5fSmartijn ax_pdutype2string(pdu->ap_header.aph_type));
4154100cc5fSmartijn /*
4164100cc5fSmartijn * RFC 2741 section 7.1.10:
4174100cc5fSmartijn * Note that the master agent's successful response indicates
4184100cc5fSmartijn * the agentx-Notify-PDU was received and validated. It does
4194100cc5fSmartijn * not indicate that any particular notifications were actually
4204100cc5fSmartijn * generated or received by notification targets
4214100cc5fSmartijn */
4224100cc5fSmartijn /* XXX Not yet - FALLTHROUGH */
4234100cc5fSmartijn case AX_PDU_TYPE_PING:
4244100cc5fSmartijn ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
4254100cc5fSmartijn pdu->ap_header.aph_transactionid,
4266f5d9364Smartijn pdu->ap_header.aph_packetid, smi_getticks(),
4276f5d9364Smartijn APPL_ERROR_NOERROR, 0, NULL, 0);
428eb6ea173Smartijn event_add(&(conn->conn_wev), NULL);
4294100cc5fSmartijn break;
4304100cc5fSmartijn case AX_PDU_TYPE_INDEXALLOCATE:
4314100cc5fSmartijn case AX_PDU_TYPE_INDEXDEALLOCATE:
43269014b58Smartijn log_warnx("%s: %s: not supported", name,
4334100cc5fSmartijn ax_pdutype2string(pdu->ap_header.aph_type));
4344100cc5fSmartijn ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
4354100cc5fSmartijn pdu->ap_header.aph_transactionid,
4366f5d9364Smartijn pdu->ap_header.aph_packetid, smi_getticks(),
4376f5d9364Smartijn APPL_ERROR_PROCESSINGERROR, 1,
4384100cc5fSmartijn pdu->ap_payload.ap_vbl.ap_varbind,
4394100cc5fSmartijn pdu->ap_payload.ap_vbl.ap_nvarbind);
440eb6ea173Smartijn event_add(&(conn->conn_wev), NULL);
4414100cc5fSmartijn break;
4424100cc5fSmartijn case AX_PDU_TYPE_ADDAGENTCAPS:
4439bbbacf6Smartijn appl_agentx_addagentcaps(session, pdu);
4449bbbacf6Smartijn break;
4454100cc5fSmartijn case AX_PDU_TYPE_REMOVEAGENTCAPS:
4469bbbacf6Smartijn appl_agentx_removeagentcaps(session, pdu);
4479bbbacf6Smartijn break;
4484100cc5fSmartijn case AX_PDU_TYPE_RESPONSE:
4494100cc5fSmartijn appl_agentx_response(session, pdu);
4504100cc5fSmartijn break;
4514100cc5fSmartijn }
4524100cc5fSmartijn
4534100cc5fSmartijn ax_pdu_free(pdu);
45469014b58Smartijn return;
45569014b58Smartijn fail:
45669014b58Smartijn ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
45769014b58Smartijn pdu->ap_header.aph_transactionid,
45869014b58Smartijn pdu->ap_header.aph_packetid, smi_getticks(),
45969014b58Smartijn error, 0, NULL, 0);
460eb6ea173Smartijn event_add(&(conn->conn_wev), NULL);
46169014b58Smartijn ax_pdu_free(pdu);
46269014b58Smartijn
46369014b58Smartijn if (session == NULL || error != APPL_ERROR_PARSEERROR)
46469014b58Smartijn return;
46569014b58Smartijn
46669014b58Smartijn appl_agentx_forceclose(&(session->sess_backend),
46769014b58Smartijn APPL_CLOSE_REASONPARSEERROR);
46869014b58Smartijn if (TAILQ_EMPTY(&(conn->conn_sessions)))
469273d3fb0Smartijn appl_agentx_free(conn, APPL_CLOSE_REASONOTHER);
4704100cc5fSmartijn }
4714100cc5fSmartijn
4724100cc5fSmartijn void
appl_agentx_open(struct appl_agentx_connection * conn,struct ax_pdu * pdu)4734100cc5fSmartijn appl_agentx_open(struct appl_agentx_connection *conn, struct ax_pdu *pdu)
4744100cc5fSmartijn {
4754100cc5fSmartijn struct appl_agentx_session *session;
4764100cc5fSmartijn struct ber_oid oid;
4774100cc5fSmartijn char oidbuf[1024];
478d2fe463cSmartijn enum appl_error error = APPL_ERROR_NOERROR;
4794100cc5fSmartijn
4804100cc5fSmartijn if ((session = malloc(sizeof(*session))) == NULL) {
4814100cc5fSmartijn log_warn(NULL);
482d2fe463cSmartijn error = APPL_ERROR_OPENFAILED;
4834100cc5fSmartijn goto fail;
4844100cc5fSmartijn }
4854100cc5fSmartijn session->sess_descr.aos_string = NULL;
4864100cc5fSmartijn
4874100cc5fSmartijn session->sess_conn = conn;
4884100cc5fSmartijn if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NETWORK_BYTE_ORDER)
4894100cc5fSmartijn session->sess_byteorder = AX_BYTE_ORDER_BE;
4904100cc5fSmartijn else
4914100cc5fSmartijn session->sess_byteorder = AX_BYTE_ORDER_LE;
4924100cc5fSmartijn
4934100cc5fSmartijn /* RFC 2742 agentxSessionObjectID */
4944100cc5fSmartijn if (pdu->ap_payload.ap_open.ap_oid.aoi_idlen == 0) {
4954100cc5fSmartijn pdu->ap_payload.ap_open.ap_oid.aoi_id[0] = 0;
4964100cc5fSmartijn pdu->ap_payload.ap_open.ap_oid.aoi_id[1] = 0;
4974100cc5fSmartijn pdu->ap_payload.ap_open.ap_oid.aoi_idlen = 2;
4984100cc5fSmartijn } else if (pdu->ap_payload.ap_open.ap_oid.aoi_idlen == 1) {
4994100cc5fSmartijn log_warnx("AgentX(%"PRIu32"): Invalid oid: Open Failed",
5004100cc5fSmartijn conn->conn_id);
501d2fe463cSmartijn error = APPL_ERROR_PARSEERROR;
5024100cc5fSmartijn goto fail;
5034100cc5fSmartijn }
5044100cc5fSmartijn /* RFC 2742 agentxSessionDescr */
5054100cc5fSmartijn if (pdu->ap_payload.ap_open.ap_descr.aos_slen > 255) {
5064100cc5fSmartijn log_warnx("AgentX(%"PRIu32"): Invalid descr (too long): Open "
5074100cc5fSmartijn "Failed", conn->conn_id);
508d2fe463cSmartijn error = APPL_ERROR_PARSEERROR;
5094100cc5fSmartijn goto fail;
5104100cc5fSmartijn }
5114100cc5fSmartijn /*
5124100cc5fSmartijn * ax_ostring is always NUL-terminated, but doesn't scan for internal
5134100cc5fSmartijn * NUL-bytes. However, mbstowcs stops at NUL, which might be in the
5144100cc5fSmartijn * middle of the string.
5154100cc5fSmartijn */
5164100cc5fSmartijn if (strlen(pdu->ap_payload.ap_open.ap_descr.aos_string) !=
5174100cc5fSmartijn pdu->ap_payload.ap_open.ap_descr.aos_slen ||
5184100cc5fSmartijn mbstowcs(NULL,
5194100cc5fSmartijn pdu->ap_payload.ap_open.ap_descr.aos_string, 0) == (size_t)-1) {
5204100cc5fSmartijn log_warnx("AgentX(%"PRIu32"): Invalid descr (not UTF-8): "
5214100cc5fSmartijn "Open Failed", conn->conn_id);
522d2fe463cSmartijn error = APPL_ERROR_PARSEERROR;
5234100cc5fSmartijn goto fail;
5244100cc5fSmartijn }
5254100cc5fSmartijn
5264100cc5fSmartijn session->sess_timeout = pdu->ap_payload.ap_open.ap_timeout;
5274100cc5fSmartijn session->sess_oid = pdu->ap_payload.ap_open.ap_oid;
5284100cc5fSmartijn session->sess_descr.aos_slen = pdu->ap_payload.ap_open.ap_descr.aos_slen;
5294100cc5fSmartijn if (pdu->ap_payload.ap_open.ap_descr.aos_string != NULL) {
5304100cc5fSmartijn session->sess_descr.aos_string =
5314100cc5fSmartijn strdup(pdu->ap_payload.ap_open.ap_descr.aos_string);
5324100cc5fSmartijn if (session->sess_descr.aos_string == NULL) {
5334100cc5fSmartijn log_warn("AgentX(%"PRIu32"): strdup: Open Failed",
5344100cc5fSmartijn conn->conn_id);
535d2fe463cSmartijn error = APPL_ERROR_OPENFAILED;
5364100cc5fSmartijn goto fail;
5374100cc5fSmartijn }
5384100cc5fSmartijn }
5394100cc5fSmartijn
5404100cc5fSmartijn /* RFC 2742 agentxSessionIndex: chances of reuse, slim to none */
5414100cc5fSmartijn do {
5424100cc5fSmartijn session->sess_id = arc4random();
5434100cc5fSmartijn } while (RB_INSERT(appl_agentx_sessions,
5444100cc5fSmartijn &appl_agentx_sessions, session) != NULL);
5454100cc5fSmartijn
5464100cc5fSmartijn if (asprintf(&(session->sess_backend.ab_name),
5474100cc5fSmartijn "AgentX(%"PRIu32"/%"PRIu32")",
5484100cc5fSmartijn conn->conn_id, session->sess_id) == -1) {
54969acb4a3Smartijn log_warn("AgentX(%"PRIu32"): asprintf: Open Failed",
5504100cc5fSmartijn conn->conn_id);
551d2fe463cSmartijn error = APPL_ERROR_OPENFAILED;
5524100cc5fSmartijn goto fail;
5534100cc5fSmartijn }
5544100cc5fSmartijn session->sess_backend.ab_cookie = session;
5554100cc5fSmartijn session->sess_backend.ab_retries = 0;
5564100cc5fSmartijn session->sess_backend.ab_fn = &appl_agentx_functions;
557752950f9Smartijn session->sess_backend.ab_range = 1;
5584100cc5fSmartijn RB_INIT(&(session->sess_backend.ab_requests));
5594100cc5fSmartijn TAILQ_INSERT_TAIL(&(conn->conn_sessions), session, sess_conn_entry);
5604100cc5fSmartijn
5614100cc5fSmartijn appl_agentx_oid2ber_oid(&(session->sess_oid), &oid);
562*f0bcdb5cSmartijn mib_oid2string(&oid, oidbuf, sizeof(oidbuf), snmpd_env->sc_oidfmt);
5634100cc5fSmartijn log_info("%s: %s %s: Open", session->sess_backend.ab_name, oidbuf,
5644100cc5fSmartijn session->sess_descr.aos_string);
5654100cc5fSmartijn
5666f5d9364Smartijn ax_response(conn->conn_ax, session->sess_id,
5676f5d9364Smartijn pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
5686f5d9364Smartijn smi_getticks(), APPL_ERROR_NOERROR, 0, NULL, 0);
569eb6ea173Smartijn event_add(&(conn->conn_wev), NULL);
5704100cc5fSmartijn
5714100cc5fSmartijn return;
5724100cc5fSmartijn fail:
5734100cc5fSmartijn ax_response(conn->conn_ax, 0, pdu->ap_header.aph_transactionid,
574d2fe463cSmartijn pdu->ap_header.aph_packetid, 0, error, 0, NULL, 0);
575eb6ea173Smartijn event_add(&(conn->conn_wev), NULL);
5764100cc5fSmartijn if (session != NULL)
5774100cc5fSmartijn free(session->sess_descr.aos_string);
5784100cc5fSmartijn free(session);
5794100cc5fSmartijn }
5804100cc5fSmartijn
5814100cc5fSmartijn void
appl_agentx_close(struct appl_agentx_session * session,struct ax_pdu * pdu)5824100cc5fSmartijn appl_agentx_close(struct appl_agentx_session *session, struct ax_pdu *pdu)
5834100cc5fSmartijn {
5844100cc5fSmartijn struct appl_agentx_connection *conn = session->sess_conn;
5854100cc5fSmartijn char name[100];
58689417623Smartijn enum appl_error error = APPL_ERROR_NOERROR;
5874100cc5fSmartijn
5884100cc5fSmartijn strlcpy(name, session->sess_backend.ab_name, sizeof(name));
58989417623Smartijn if (pdu->ap_payload.ap_close.ap_reason == AX_CLOSE_BYMANAGER) {
59089417623Smartijn log_warnx("%s: Invalid close reason", name);
59189417623Smartijn error = APPL_ERROR_PARSEERROR;
59289417623Smartijn } else {
5934100cc5fSmartijn appl_agentx_session_free(session);
5944100cc5fSmartijn log_info("%s: Closed by subagent (%s)", name,
5954100cc5fSmartijn ax_closereason2string(pdu->ap_payload.ap_close.ap_reason));
59689417623Smartijn }
5974100cc5fSmartijn
5984100cc5fSmartijn ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
5994100cc5fSmartijn pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
60089417623Smartijn smi_getticks(), error, 0, NULL, 0);
601eb6ea173Smartijn event_add(&(conn->conn_wev), NULL);
60289417623Smartijn if (error == APPL_ERROR_NOERROR)
60389417623Smartijn return;
60489417623Smartijn
60589417623Smartijn appl_agentx_forceclose(&(session->sess_backend),
60689417623Smartijn APPL_CLOSE_REASONPARSEERROR);
60789417623Smartijn if (TAILQ_EMPTY(&(conn->conn_sessions)))
60889417623Smartijn appl_agentx_free(conn, APPL_CLOSE_REASONOTHER);
6094100cc5fSmartijn }
6104100cc5fSmartijn
6114100cc5fSmartijn void
appl_agentx_forceclose(struct appl_backend * backend,enum appl_close_reason reason)6124100cc5fSmartijn appl_agentx_forceclose(struct appl_backend *backend,
6134100cc5fSmartijn enum appl_close_reason reason)
6144100cc5fSmartijn {
6154100cc5fSmartijn struct appl_agentx_session *session = backend->ab_cookie;
6164100cc5fSmartijn char name[100];
6174100cc5fSmartijn
6184100cc5fSmartijn session->sess_conn->conn_ax->ax_byteorder = session->sess_byteorder;
6194100cc5fSmartijn ax_close(session->sess_conn->conn_ax, session->sess_id,
6204100cc5fSmartijn (enum ax_close_reason) reason);
621eb6ea173Smartijn event_add(&(session->sess_conn->conn_wev), NULL);
6224100cc5fSmartijn
6234100cc5fSmartijn strlcpy(name, session->sess_backend.ab_name, sizeof(name));
6244100cc5fSmartijn appl_agentx_session_free(session);
6254100cc5fSmartijn log_info("%s: Closed by snmpd (%s)", name,
6264100cc5fSmartijn ax_closereason2string((enum ax_close_reason)reason));
6274100cc5fSmartijn }
6284100cc5fSmartijn
6294100cc5fSmartijn void
appl_agentx_session_free(struct appl_agentx_session * session)6304100cc5fSmartijn appl_agentx_session_free(struct appl_agentx_session *session)
6314100cc5fSmartijn {
6324100cc5fSmartijn struct appl_agentx_connection *conn = session->sess_conn;
6334100cc5fSmartijn
6344100cc5fSmartijn appl_close(&(session->sess_backend));
6354100cc5fSmartijn
6364100cc5fSmartijn RB_REMOVE(appl_agentx_sessions, &appl_agentx_sessions, session);
6374100cc5fSmartijn TAILQ_REMOVE(&(conn->conn_sessions), session, sess_conn_entry);
6384100cc5fSmartijn
6394100cc5fSmartijn free(session->sess_backend.ab_name);
6404100cc5fSmartijn free(session->sess_descr.aos_string);
6414100cc5fSmartijn free(session);
6424100cc5fSmartijn }
6434100cc5fSmartijn
6444100cc5fSmartijn void
appl_agentx_register(struct appl_agentx_session * session,struct ax_pdu * pdu)6454100cc5fSmartijn appl_agentx_register(struct appl_agentx_session *session, struct ax_pdu *pdu)
6464100cc5fSmartijn {
6474100cc5fSmartijn uint32_t timeout;
6484100cc5fSmartijn struct ber_oid oid;
6494100cc5fSmartijn enum appl_error error;
6501985d3ebSmartijn int subtree = 0;
6514100cc5fSmartijn
6524100cc5fSmartijn timeout = pdu->ap_payload.ap_register.ap_timeout;
6534100cc5fSmartijn timeout = timeout != 0 ? timeout : session->sess_timeout != 0 ?
6544100cc5fSmartijn session->sess_timeout : AGENTX_DEFAULTTIMEOUT;
6554100cc5fSmartijn timeout *= 100;
6564100cc5fSmartijn
6571985d3ebSmartijn if (session->sess_conn->conn_backend) {
6581985d3ebSmartijn pdu->ap_payload.ap_register.ap_priority = 1;
6591985d3ebSmartijn subtree = 1;
6601985d3ebSmartijn }
6614100cc5fSmartijn if (appl_agentx_oid2ber_oid(
6624100cc5fSmartijn &(pdu->ap_payload.ap_register.ap_subtree), &oid) == NULL) {
6634100cc5fSmartijn log_warnx("%s: Failed to register: oid too small",
6644100cc5fSmartijn session->sess_backend.ab_name);
6654100cc5fSmartijn error = APPL_ERROR_PROCESSINGERROR;
6664100cc5fSmartijn goto fail;
6674100cc5fSmartijn }
6684100cc5fSmartijn
6694100cc5fSmartijn error = appl_register(pdu->ap_context.aos_string, timeout,
6704100cc5fSmartijn pdu->ap_payload.ap_register.ap_priority, &oid,
6711985d3ebSmartijn pdu->ap_header.aph_flags & AX_PDU_FLAG_INSTANCE_REGISTRATION,
6721985d3ebSmartijn subtree, pdu->ap_payload.ap_register.ap_range_subid,
6734100cc5fSmartijn pdu->ap_payload.ap_register.ap_upper_bound,
6744100cc5fSmartijn &(session->sess_backend));
6754100cc5fSmartijn
6764100cc5fSmartijn fail:
6774100cc5fSmartijn ax_response(session->sess_conn->conn_ax, session->sess_id,
6784100cc5fSmartijn pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
6796f5d9364Smartijn smi_getticks(), error, 0, NULL, 0);
680eb6ea173Smartijn event_add(&(session->sess_conn->conn_wev), NULL);
6814100cc5fSmartijn }
6824100cc5fSmartijn
6834100cc5fSmartijn void
appl_agentx_unregister(struct appl_agentx_session * session,struct ax_pdu * pdu)6844100cc5fSmartijn appl_agentx_unregister(struct appl_agentx_session *session, struct ax_pdu *pdu)
6854100cc5fSmartijn {
6864100cc5fSmartijn struct ber_oid oid;
6874100cc5fSmartijn enum appl_error error;
6884100cc5fSmartijn
6894100cc5fSmartijn if (appl_agentx_oid2ber_oid(
6904100cc5fSmartijn &(pdu->ap_payload.ap_unregister.ap_subtree), &oid) == NULL) {
6914100cc5fSmartijn log_warnx("%s: Failed to unregister: oid too small",
6924100cc5fSmartijn session->sess_backend.ab_name);
6934100cc5fSmartijn error = APPL_ERROR_PROCESSINGERROR;
6944100cc5fSmartijn goto fail;
6954100cc5fSmartijn }
6964100cc5fSmartijn
6974100cc5fSmartijn error = appl_unregister(pdu->ap_context.aos_string,
6984100cc5fSmartijn pdu->ap_payload.ap_unregister.ap_priority, &oid,
6994100cc5fSmartijn pdu->ap_payload.ap_unregister.ap_range_subid,
7004100cc5fSmartijn pdu->ap_payload.ap_unregister.ap_upper_bound,
7014100cc5fSmartijn &(session->sess_backend));
7024100cc5fSmartijn
7034100cc5fSmartijn fail:
7044100cc5fSmartijn ax_response(session->sess_conn->conn_ax, session->sess_id,
7054100cc5fSmartijn pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
7066f5d9364Smartijn smi_getticks(), error, 0, NULL, 0);
707eb6ea173Smartijn event_add(&(session->sess_conn->conn_wev), NULL);
7084100cc5fSmartijn }
7094100cc5fSmartijn
7104100cc5fSmartijn #define AX_PDU_FLAG_INDEX (AX_PDU_FLAG_NEW_INDEX | AX_PDU_FLAG_ANY_INDEX)
7114100cc5fSmartijn
7124100cc5fSmartijn void
appl_agentx_get(struct appl_backend * backend,int32_t transactionid,int32_t requestid,const char * ctx,struct appl_varbind * vblist)7134100cc5fSmartijn appl_agentx_get(struct appl_backend *backend, int32_t transactionid,
7144100cc5fSmartijn int32_t requestid, const char *ctx, struct appl_varbind *vblist)
7154100cc5fSmartijn {
7164100cc5fSmartijn struct appl_agentx_session *session = backend->ab_cookie;
7174100cc5fSmartijn struct ax_ostring *context, string;
7184100cc5fSmartijn struct appl_varbind *vb;
7194100cc5fSmartijn struct ax_searchrange *srl;
7204100cc5fSmartijn size_t i, j, nsr;
7214100cc5fSmartijn
722fe6f1a99Smartijn if (session->sess_conn->conn_ax == NULL)
723fe6f1a99Smartijn return;
724fe6f1a99Smartijn
7254100cc5fSmartijn for (nsr = 0, vb = vblist; vb != NULL; vb = vb->av_next)
7264100cc5fSmartijn nsr++;
7274100cc5fSmartijn
7284100cc5fSmartijn if ((srl = calloc(nsr, sizeof(*srl))) == NULL) {
7294100cc5fSmartijn log_warn(NULL);
7304100cc5fSmartijn appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist);
7314100cc5fSmartijn return;
7324100cc5fSmartijn }
7334100cc5fSmartijn
7344100cc5fSmartijn for (i = 0, vb = vblist; i < nsr; i++, vb = vb->av_next) {
7354100cc5fSmartijn srl[i].asr_start.aoi_include = vb->av_include;
7364100cc5fSmartijn srl[i].asr_start.aoi_idlen = vb->av_oid.bo_n;
7374100cc5fSmartijn for (j = 0; j < vb->av_oid.bo_n; j++)
7384100cc5fSmartijn srl[i].asr_start.aoi_id[j] = vb->av_oid.bo_id[j];
7394100cc5fSmartijn srl[i].asr_stop.aoi_include = 0;
7404100cc5fSmartijn srl[i].asr_stop.aoi_idlen = 0;
7414100cc5fSmartijn }
7424100cc5fSmartijn if ((context = appl_agentx_string2ostring(ctx, &string)) == NULL) {
7434100cc5fSmartijn if (errno != 0) {
7444100cc5fSmartijn log_warn("Failed to convert context");
7454100cc5fSmartijn appl_response(backend, requestid,
7464100cc5fSmartijn APPL_ERROR_GENERR, 1, vblist);
7474100cc5fSmartijn free(srl);
7484100cc5fSmartijn return;
7494100cc5fSmartijn }
7504100cc5fSmartijn }
7514100cc5fSmartijn
7524100cc5fSmartijn session->sess_conn->conn_ax->ax_byteorder = session->sess_byteorder;
7534100cc5fSmartijn if (ax_get(session->sess_conn->conn_ax, session->sess_id, transactionid,
7544100cc5fSmartijn requestid, context, srl, nsr) == -1)
7554100cc5fSmartijn appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist);
7564100cc5fSmartijn else
757eb6ea173Smartijn event_add(&(session->sess_conn->conn_wev), NULL);
7584100cc5fSmartijn free(srl);
7594100cc5fSmartijn if (context != NULL)
7604100cc5fSmartijn free(context->aos_string);
7614100cc5fSmartijn }
7624100cc5fSmartijn
7634100cc5fSmartijn void
appl_agentx_getnext(struct appl_backend * backend,int32_t transactionid,int32_t requestid,const char * ctx,struct appl_varbind * vblist)7644100cc5fSmartijn appl_agentx_getnext(struct appl_backend *backend, int32_t transactionid,
7654100cc5fSmartijn int32_t requestid, const char *ctx, struct appl_varbind *vblist)
7664100cc5fSmartijn {
7674100cc5fSmartijn struct appl_agentx_session *session = backend->ab_cookie;
7684100cc5fSmartijn struct ax_ostring *context, string;
7694100cc5fSmartijn struct appl_varbind *vb;
7704100cc5fSmartijn struct ax_searchrange *srl;
7714100cc5fSmartijn size_t i, j, nsr;
7724100cc5fSmartijn
773fe6f1a99Smartijn if (session->sess_conn->conn_ax == NULL)
774fe6f1a99Smartijn return;
775fe6f1a99Smartijn
7764100cc5fSmartijn for (nsr = 0, vb = vblist; vb != NULL; vb = vb->av_next)
7774100cc5fSmartijn nsr++;
7784100cc5fSmartijn
7794100cc5fSmartijn if ((srl = calloc(nsr, sizeof(*srl))) == NULL) {
7804100cc5fSmartijn log_warn(NULL);
7814100cc5fSmartijn appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist);
7824100cc5fSmartijn return;
7834100cc5fSmartijn }
7844100cc5fSmartijn
7854100cc5fSmartijn for (i = 0, vb = vblist; i < nsr; i++, vb = vb->av_next) {
7864100cc5fSmartijn srl[i].asr_start.aoi_include = vb->av_include;
7874100cc5fSmartijn srl[i].asr_start.aoi_idlen = vb->av_oid.bo_n;
7884100cc5fSmartijn for (j = 0; j < vb->av_oid.bo_n; j++)
7894100cc5fSmartijn srl[i].asr_start.aoi_id[j] = vb->av_oid.bo_id[j];
7904100cc5fSmartijn srl[i].asr_stop.aoi_include = 0;
7914100cc5fSmartijn srl[i].asr_stop.aoi_idlen = vb->av_oid_end.bo_n;
7920601d079Smartijn for (j = 0; j < vb->av_oid_end.bo_n; j++)
7934100cc5fSmartijn srl[i].asr_stop.aoi_id[j] = vb->av_oid_end.bo_id[j];
7944100cc5fSmartijn }
7954100cc5fSmartijn if ((context = appl_agentx_string2ostring(ctx, &string)) == NULL) {
7964100cc5fSmartijn if (errno != 0) {
7974100cc5fSmartijn log_warn("Failed to convert context");
7984100cc5fSmartijn appl_response(backend, requestid,
7994100cc5fSmartijn APPL_ERROR_GENERR, 1, vblist);
8004100cc5fSmartijn free(srl);
8014100cc5fSmartijn return;
8024100cc5fSmartijn }
8034100cc5fSmartijn }
8044100cc5fSmartijn
8054100cc5fSmartijn session->sess_conn->conn_ax->ax_byteorder = session->sess_byteorder;
8064100cc5fSmartijn if (ax_getnext(session->sess_conn->conn_ax, session->sess_id, transactionid,
8074100cc5fSmartijn requestid, context, srl, nsr) == -1)
8084100cc5fSmartijn appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist);
8094100cc5fSmartijn else
810eb6ea173Smartijn event_add(&(session->sess_conn->conn_wev), NULL);
8114100cc5fSmartijn free(srl);
8124100cc5fSmartijn if (context != NULL)
8134100cc5fSmartijn free(context->aos_string);
8144100cc5fSmartijn }
8154100cc5fSmartijn
8164100cc5fSmartijn void
appl_agentx_addagentcaps(struct appl_agentx_session * session,struct ax_pdu * pdu)8179bbbacf6Smartijn appl_agentx_addagentcaps(struct appl_agentx_session *session,
8189bbbacf6Smartijn struct ax_pdu *pdu)
8199bbbacf6Smartijn {
8209bbbacf6Smartijn struct ber_oid oid;
8219bbbacf6Smartijn enum appl_error error;
8229bbbacf6Smartijn
8239bbbacf6Smartijn if (appl_agentx_oid2ber_oid(&(pdu->ap_payload.ap_addagentcaps.ap_oid),
8249bbbacf6Smartijn &oid) == NULL) {
8259bbbacf6Smartijn log_warnx("%s: Failed to add agent capabilities: oid too small",
8269bbbacf6Smartijn session->sess_backend.ab_name);
8279bbbacf6Smartijn error = APPL_ERROR_PARSEERROR;
8289bbbacf6Smartijn goto fail;
8299bbbacf6Smartijn }
8309bbbacf6Smartijn
8319bbbacf6Smartijn error = appl_addagentcaps(pdu->ap_context.aos_string, &oid,
8329bbbacf6Smartijn pdu->ap_payload.ap_addagentcaps.ap_descr.aos_string,
8339bbbacf6Smartijn &(session->sess_backend));
8349bbbacf6Smartijn
8359bbbacf6Smartijn fail:
8369bbbacf6Smartijn ax_response(session->sess_conn->conn_ax, session->sess_id,
8379bbbacf6Smartijn pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
8389bbbacf6Smartijn smi_getticks(), error, 0, NULL, 0);
8399bbbacf6Smartijn event_add(&(session->sess_conn->conn_wev), NULL);
8409bbbacf6Smartijn }
8419bbbacf6Smartijn
8429bbbacf6Smartijn void
appl_agentx_removeagentcaps(struct appl_agentx_session * session,struct ax_pdu * pdu)8439bbbacf6Smartijn appl_agentx_removeagentcaps(struct appl_agentx_session *session,
8449bbbacf6Smartijn struct ax_pdu *pdu)
8459bbbacf6Smartijn {
8469bbbacf6Smartijn struct ber_oid oid;
8479bbbacf6Smartijn enum appl_error error;
8489bbbacf6Smartijn
8499bbbacf6Smartijn if (appl_agentx_oid2ber_oid(&(pdu->ap_payload.ap_addagentcaps.ap_oid),
8509bbbacf6Smartijn &oid) == NULL) {
8519bbbacf6Smartijn log_warnx("%s: Failed to remove agent capabilities: "
8529bbbacf6Smartijn "oid too small", session->sess_backend.ab_name);
8539bbbacf6Smartijn error = APPL_ERROR_PARSEERROR;
8549bbbacf6Smartijn goto fail;
8559bbbacf6Smartijn }
8569bbbacf6Smartijn
8579bbbacf6Smartijn error = appl_removeagentcaps(pdu->ap_context.aos_string, &oid,
8589bbbacf6Smartijn &(session->sess_backend));
8599bbbacf6Smartijn
8609bbbacf6Smartijn fail:
8619bbbacf6Smartijn ax_response(session->sess_conn->conn_ax, session->sess_id,
8629bbbacf6Smartijn pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
8639bbbacf6Smartijn smi_getticks(), error, 0, NULL, 0);
8649bbbacf6Smartijn event_add(&(session->sess_conn->conn_wev), NULL);
8659bbbacf6Smartijn }
8669bbbacf6Smartijn
8679bbbacf6Smartijn void
appl_agentx_response(struct appl_agentx_session * session,struct ax_pdu * pdu)8684100cc5fSmartijn appl_agentx_response(struct appl_agentx_session *session, struct ax_pdu *pdu)
8694100cc5fSmartijn {
8704100cc5fSmartijn struct appl_varbind *response = NULL;
8714100cc5fSmartijn struct ax_varbind *vb;
8724100cc5fSmartijn enum appl_error error;
8734100cc5fSmartijn uint16_t index;
8744100cc5fSmartijn size_t i, nvarbind;
8754100cc5fSmartijn
8764100cc5fSmartijn nvarbind = pdu->ap_payload.ap_response.ap_nvarbind;
8774100cc5fSmartijn if ((response = calloc(nvarbind, sizeof(*response))) == NULL) {
8784100cc5fSmartijn log_warn(NULL);
8794100cc5fSmartijn appl_response(&(session->sess_backend),
8804100cc5fSmartijn pdu->ap_header.aph_packetid,
8814100cc5fSmartijn APPL_ERROR_GENERR, 1, NULL);
8824100cc5fSmartijn return;
8834100cc5fSmartijn }
8844100cc5fSmartijn
8854100cc5fSmartijn error = (enum appl_error)pdu->ap_payload.ap_response.ap_error;
8864100cc5fSmartijn index = pdu->ap_payload.ap_response.ap_index;
8874100cc5fSmartijn for (i = 0; i < nvarbind; i++) {
8884100cc5fSmartijn response[i].av_next = i + 1 == nvarbind ?
8894100cc5fSmartijn NULL : &(response[i + 1]);
8904100cc5fSmartijn vb = &(pdu->ap_payload.ap_response.ap_varbindlist[i]);
8914100cc5fSmartijn
8924100cc5fSmartijn if (appl_agentx_oid2ber_oid(&(vb->avb_oid),
8934100cc5fSmartijn &(response[i].av_oid)) == NULL) {
8944100cc5fSmartijn log_warnx("%s: invalid oid",
8954100cc5fSmartijn session->sess_backend.ab_name);
8964100cc5fSmartijn if (error != APPL_ERROR_NOERROR) {
8974100cc5fSmartijn error = APPL_ERROR_GENERR;
8984100cc5fSmartijn index = i + 1;
8994100cc5fSmartijn }
9004100cc5fSmartijn continue;
9014100cc5fSmartijn }
9024100cc5fSmartijn response[i].av_value = appl_agentx_value2ber_element(vb);
9034100cc5fSmartijn if (response[i].av_value == NULL) {
9044100cc5fSmartijn log_warn("%s: Failed to parse response value",
9054100cc5fSmartijn session->sess_backend.ab_name);
9064100cc5fSmartijn if (error != APPL_ERROR_NOERROR) {
9074100cc5fSmartijn error = APPL_ERROR_GENERR;
9084100cc5fSmartijn index = i + 1;
9094100cc5fSmartijn }
9104100cc5fSmartijn }
9114100cc5fSmartijn }
9124100cc5fSmartijn appl_response(&(session->sess_backend), pdu->ap_header.aph_packetid,
9134100cc5fSmartijn error, index, response);
9144100cc5fSmartijn free(response);
9154100cc5fSmartijn }
9164100cc5fSmartijn
9174100cc5fSmartijn void
appl_agentx_send(int fd,short event,void * cookie)9184100cc5fSmartijn appl_agentx_send(int fd, short event, void *cookie)
9194100cc5fSmartijn {
9204100cc5fSmartijn struct appl_agentx_connection *conn = cookie;
9214100cc5fSmartijn
9224100cc5fSmartijn switch (ax_send(conn->conn_ax)) {
9234100cc5fSmartijn case -1:
9244100cc5fSmartijn if (errno == EAGAIN)
9254100cc5fSmartijn break;
9264100cc5fSmartijn log_warn("AgentX(%"PRIu32")", conn->conn_id);
9274100cc5fSmartijn ax_free(conn->conn_ax);
9284100cc5fSmartijn conn->conn_ax = NULL;
929273d3fb0Smartijn appl_agentx_free(conn, APPL_CLOSE_REASONOTHER);
9304100cc5fSmartijn return;
9314100cc5fSmartijn case 0:
9324100cc5fSmartijn return;
9334100cc5fSmartijn default:
9344100cc5fSmartijn break;
9354100cc5fSmartijn }
9364100cc5fSmartijn event_add(&(conn->conn_wev), NULL);
9374100cc5fSmartijn }
9384100cc5fSmartijn
9394100cc5fSmartijn struct ber_oid *
appl_agentx_oid2ber_oid(struct ax_oid * aoid,struct ber_oid * boid)9404100cc5fSmartijn appl_agentx_oid2ber_oid(struct ax_oid *aoid, struct ber_oid *boid)
9414100cc5fSmartijn {
9424100cc5fSmartijn size_t i;
9434100cc5fSmartijn
9444100cc5fSmartijn if (aoid->aoi_idlen < BER_MIN_OID_LEN ||
9454100cc5fSmartijn aoid->aoi_idlen > BER_MAX_OID_LEN) {
9464100cc5fSmartijn errno = EINVAL;
9474100cc5fSmartijn return NULL;
9484100cc5fSmartijn }
9494100cc5fSmartijn
9504100cc5fSmartijn
9514100cc5fSmartijn boid->bo_n = aoid->aoi_idlen;
9524100cc5fSmartijn for (i = 0; i < boid->bo_n; i++)
9534100cc5fSmartijn boid->bo_id[i] = aoid->aoi_id[i];
9544100cc5fSmartijn return boid;
9554100cc5fSmartijn }
9564100cc5fSmartijn
9574100cc5fSmartijn struct ber_element *
appl_agentx_value2ber_element(struct ax_varbind * vb)9584100cc5fSmartijn appl_agentx_value2ber_element(struct ax_varbind *vb)
9594100cc5fSmartijn {
9604100cc5fSmartijn struct ber_oid oid;
9614100cc5fSmartijn struct ber_element *elm;
9624100cc5fSmartijn
9634100cc5fSmartijn switch (vb->avb_type) {
9644100cc5fSmartijn case AX_DATA_TYPE_INTEGER:
9654100cc5fSmartijn return ober_add_integer(NULL, vb->avb_data.avb_int32);
9664100cc5fSmartijn case AX_DATA_TYPE_OCTETSTRING:
9674100cc5fSmartijn return ober_add_nstring(NULL,
9684100cc5fSmartijn vb->avb_data.avb_ostring.aos_string,
9694100cc5fSmartijn vb->avb_data.avb_ostring.aos_slen);
9704100cc5fSmartijn case AX_DATA_TYPE_NULL:
9714100cc5fSmartijn return ober_add_null(NULL);
9724100cc5fSmartijn case AX_DATA_TYPE_OID:
9734100cc5fSmartijn if (appl_agentx_oid2ber_oid(
9744100cc5fSmartijn &(vb->avb_data.avb_oid), &oid) == NULL)
9754100cc5fSmartijn return NULL;
9764100cc5fSmartijn return ober_add_oid(NULL, &oid);
9774100cc5fSmartijn case AX_DATA_TYPE_IPADDRESS:
9784100cc5fSmartijn if ((elm = ober_add_nstring(NULL,
9794100cc5fSmartijn vb->avb_data.avb_ostring.aos_string,
9804100cc5fSmartijn vb->avb_data.avb_ostring.aos_slen)) == NULL)
9814100cc5fSmartijn return NULL;
9824100cc5fSmartijn ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_IPADDR);
9834100cc5fSmartijn return elm;
9844100cc5fSmartijn case AX_DATA_TYPE_COUNTER32:
9854100cc5fSmartijn elm = ober_add_integer(NULL, vb->avb_data.avb_uint32);
9864100cc5fSmartijn if (elm == NULL)
9874100cc5fSmartijn return NULL;
9884100cc5fSmartijn ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
9894100cc5fSmartijn return elm;
9904100cc5fSmartijn case AX_DATA_TYPE_GAUGE32:
9914100cc5fSmartijn elm = ober_add_integer(NULL, vb->avb_data.avb_uint32);
9924100cc5fSmartijn if (elm == NULL)
9934100cc5fSmartijn return NULL;
9944100cc5fSmartijn ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_GAUGE32);
9954100cc5fSmartijn return elm;
9964100cc5fSmartijn case AX_DATA_TYPE_TIMETICKS:
9974100cc5fSmartijn elm = ober_add_integer(NULL, vb->avb_data.avb_uint32);
9984100cc5fSmartijn if (elm == NULL)
9994100cc5fSmartijn return NULL;
10004100cc5fSmartijn ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_TIMETICKS);
10014100cc5fSmartijn return elm;
10024100cc5fSmartijn case AX_DATA_TYPE_OPAQUE:
10034100cc5fSmartijn if ((elm = ober_add_nstring(NULL,
10044100cc5fSmartijn vb->avb_data.avb_ostring.aos_string,
10054100cc5fSmartijn vb->avb_data.avb_ostring.aos_slen)) == NULL)
10064100cc5fSmartijn return NULL;
10074100cc5fSmartijn ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_OPAQUE);
10084100cc5fSmartijn return elm;
10094100cc5fSmartijn case AX_DATA_TYPE_COUNTER64:
10104100cc5fSmartijn elm = ober_add_integer(NULL, vb->avb_data.avb_uint64);
10114100cc5fSmartijn if (elm == NULL)
10124100cc5fSmartijn return NULL;
10134100cc5fSmartijn ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_COUNTER64);
10144100cc5fSmartijn return elm;
10154100cc5fSmartijn case AX_DATA_TYPE_NOSUCHOBJECT:
10164100cc5fSmartijn return appl_exception(APPL_EXC_NOSUCHOBJECT);
10174100cc5fSmartijn case AX_DATA_TYPE_NOSUCHINSTANCE:
10184100cc5fSmartijn return appl_exception(APPL_EXC_NOSUCHINSTANCE);
10194100cc5fSmartijn case AX_DATA_TYPE_ENDOFMIBVIEW:
10204100cc5fSmartijn return appl_exception(APPL_EXC_ENDOFMIBVIEW);
10214100cc5fSmartijn default:
10224100cc5fSmartijn errno = EINVAL;
10234100cc5fSmartijn return NULL;
10244100cc5fSmartijn }
10254100cc5fSmartijn }
10264100cc5fSmartijn
10274100cc5fSmartijn struct ax_ostring *
appl_agentx_string2ostring(const char * str,struct ax_ostring * ostring)10284100cc5fSmartijn appl_agentx_string2ostring(const char *str, struct ax_ostring *ostring)
10294100cc5fSmartijn {
10304100cc5fSmartijn if (str == NULL) {
10314100cc5fSmartijn errno = 0;
10324100cc5fSmartijn return NULL;
10334100cc5fSmartijn }
10344100cc5fSmartijn
10354100cc5fSmartijn ostring->aos_slen = strlen(str);
10364100cc5fSmartijn if ((ostring->aos_string = strdup(str)) == NULL)
10374100cc5fSmartijn return NULL;
10384100cc5fSmartijn return ostring;
10394100cc5fSmartijn }
10404100cc5fSmartijn
10414100cc5fSmartijn int
appl_agentx_cmp(struct appl_agentx_connection * conn1,struct appl_agentx_connection * conn2)10424100cc5fSmartijn appl_agentx_cmp(struct appl_agentx_connection *conn1,
10434100cc5fSmartijn struct appl_agentx_connection *conn2)
10444100cc5fSmartijn {
10454100cc5fSmartijn return conn1->conn_id < conn2->conn_id ? -1 :
10464100cc5fSmartijn conn1->conn_id > conn2->conn_id;
10474100cc5fSmartijn }
10484100cc5fSmartijn
10494100cc5fSmartijn int
appl_agentx_session_cmp(struct appl_agentx_session * sess1,struct appl_agentx_session * sess2)10504100cc5fSmartijn appl_agentx_session_cmp(struct appl_agentx_session *sess1,
10514100cc5fSmartijn struct appl_agentx_session *sess2)
10524100cc5fSmartijn {
10534100cc5fSmartijn return sess1->sess_id < sess2->sess_id ? -1 : sess1->sess_id > sess2->sess_id;
10544100cc5fSmartijn }
10554100cc5fSmartijn
10564100cc5fSmartijn RB_GENERATE_STATIC(appl_agentx_conns, appl_agentx_connection, conn_entry,
10574100cc5fSmartijn appl_agentx_cmp);
10584100cc5fSmartijn RB_GENERATE_STATIC(appl_agentx_sessions, appl_agentx_session, sess_entry,
10594100cc5fSmartijn appl_agentx_session_cmp);
1060