xref: /openbsd-src/usr.sbin/snmpd/application_agentx.c (revision 3374c67d44f9b75b98444cbf63020f777792342e)
1 /*	$OpenBSD: application_agentx.c,v 1.4 2022/09/01 14:34:17 martijn Exp $ */
2 /*
3  * Copyright (c) 2022 Martijn van Duren <martijn@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/queue.h>
19 #include <sys/socket.h>
20 #include <sys/stat.h>
21 #include <sys/time.h>
22 #include <sys/types.h>
23 #include <sys/un.h>
24 
25 #include <errno.h>
26 #include <event.h>
27 #include <inttypes.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <unistd.h>
32 
33 #include "application.h"
34 #include "ax.h"
35 #include "log.h"
36 #include "smi.h"
37 #include "snmp.h"
38 #include "snmpd.h"
39 
40 #define AGENTX_DEFAULTTIMEOUT 5
41 
42 struct appl_agentx_connection {
43 	uint32_t conn_id;
44 	/*
45 	 * A backend has several overruling properties:
46 	 * - If it exits, snmpd crashes
47 	 * - All registrations are priority 1
48 	 * - All registrations own the subtree.
49 	 */
50 	int conn_backend;
51 	struct ax *conn_ax;
52 	struct event conn_rev;
53 	struct event conn_wev;
54 
55 	TAILQ_HEAD(, appl_agentx_session) conn_sessions;
56 	RB_ENTRY(appl_agentx_connection) conn_entry;
57 };
58 
59 struct appl_agentx_session {
60 	uint32_t sess_id;
61 	struct appl_agentx_connection *sess_conn;
62 	/*
63 	 * RFC 2741 section 7.1.1:
64 	 * All subsequent AgentX protocol operations initiated by the master
65 	 * agent for this session must use this byte ordering and set this bit
66 	 * accordingly.
67 	 */
68 	enum ax_byte_order sess_byteorder;
69 	uint8_t sess_timeout;
70 	struct ax_oid sess_oid;
71 	struct ax_ostring sess_descr;
72 	struct appl_backend sess_backend;
73 
74 	RB_ENTRY(appl_agentx_session) sess_entry;
75 	TAILQ_ENTRY(appl_agentx_session) sess_conn_entry;
76 };
77 
78 void appl_agentx_listen(struct agentx_master *);
79 void appl_agentx_accept(int, short, void *);
80 void appl_agentx_free(struct appl_agentx_connection *);
81 void appl_agentx_recv(int, short, void *);
82 void appl_agentx_open(struct appl_agentx_connection *, struct ax_pdu *);
83 void appl_agentx_close(struct appl_agentx_session *, struct ax_pdu *);
84 void appl_agentx_forceclose(struct appl_backend *, enum appl_close_reason);
85 void appl_agentx_session_free(struct appl_agentx_session *);
86 void appl_agentx_register(struct appl_agentx_session *, struct ax_pdu *);
87 void appl_agentx_unregister(struct appl_agentx_session *, struct ax_pdu *);
88 void appl_agentx_get(struct appl_backend *, int32_t, int32_t, const char *,
89     struct appl_varbind *);
90 void appl_agentx_getnext(struct appl_backend *, int32_t, int32_t, const char *,
91     struct appl_varbind *);
92 void appl_agentx_response(struct appl_agentx_session *, struct ax_pdu *);
93 void appl_agentx_send(int, short, void *);
94 struct ber_oid *appl_agentx_oid2ber_oid(struct ax_oid *, struct ber_oid *);
95 struct ber_element *appl_agentx_value2ber_element(struct ax_varbind *);
96 struct ax_ostring *appl_agentx_string2ostring(const char *,
97     struct ax_ostring *);
98 int appl_agentx_cmp(struct appl_agentx_connection *,
99     struct appl_agentx_connection *);
100 int appl_agentx_session_cmp(struct appl_agentx_session *,
101     struct appl_agentx_session *);
102 
103 struct appl_backend_functions appl_agentx_functions = {
104 	.ab_close = appl_agentx_forceclose,
105 	.ab_get = appl_agentx_get,
106 	.ab_getnext = appl_agentx_getnext,
107 	.ab_getbulk = NULL, /* not properly supported in application.c and libagentx */
108 };
109 
110 RB_HEAD(appl_agentx_conns, appl_agentx_connection) appl_agentx_conns =
111     RB_INITIALIZER(&appl_agentx_conns);
112 RB_HEAD(appl_agentx_sessions, appl_agentx_session) appl_agentx_sessions =
113     RB_INITIALIZER(&appl_agentx_sessions);
114 
115 RB_PROTOTYPE_STATIC(appl_agentx_conns, appl_agentx_connection, conn_entry,
116     appl_agentx_cmp);
117 RB_PROTOTYPE_STATIC(appl_agentx_sessions, appl_agentx_session, sess_entry,
118     appl_agentx_session_cmp);
119 
120 void
121 appl_agentx(void)
122 {
123 	struct agentx_master *master;
124 
125 	TAILQ_FOREACH(master, &(snmpd_env->sc_agentx_masters), axm_entry)
126 		appl_agentx_listen(master);
127 }
128 
129 void
130 appl_agentx_init(void)
131 {
132 	struct agentx_master *master;
133 
134 	TAILQ_FOREACH(master, &(snmpd_env->sc_agentx_masters), axm_entry) {
135 		if (master->axm_fd == -1)
136 			continue;
137 		event_set(&(master->axm_ev), master->axm_fd,
138 		    EV_READ | EV_PERSIST, appl_agentx_accept, master);
139 		event_add(&(master->axm_ev), NULL);
140 		log_info("AgentX: listening on %s", master->axm_sun.sun_path);
141 	}
142 }
143 void
144 appl_agentx_listen(struct agentx_master *master)
145 {
146 	mode_t mask;
147 
148 	unlink(master->axm_sun.sun_path);
149 
150 	mask = umask(0777);
151 	if ((master->axm_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1 ||
152 	    bind(master->axm_fd, (struct sockaddr *)&(master->axm_sun),
153 	    sizeof(master->axm_sun)) == -1 ||
154 	    listen(master->axm_fd, 5)) {
155 		log_warn("AgentX: listen %s", master->axm_sun.sun_path);
156 		umask(mask);
157 		return;
158 	}
159 	umask(mask);
160 	if (chown(master->axm_sun.sun_path, master->axm_owner,
161 	    master->axm_group) == -1) {
162 		log_warn("AgentX: chown %s", master->axm_sun.sun_path);
163 		goto fail;
164 	}
165 	if (chmod(master->axm_sun.sun_path, master->axm_mode) == -1) {
166 		log_warn("AgentX: chmod %s", master->axm_sun.sun_path);
167 		goto fail;
168 	}
169 	return;
170  fail:
171 	close(master->axm_fd);
172 	master->axm_fd = -1;
173 }
174 
175 void
176 appl_agentx_shutdown(void)
177 {
178 	struct appl_agentx_connection *conn, *tconn;
179 
180 	RB_FOREACH_SAFE(conn, appl_agentx_conns, &appl_agentx_conns, tconn)
181 		appl_agentx_free(conn);
182 }
183 
184 void
185 appl_agentx_accept(int masterfd, short event, void *cookie)
186 {
187 	int fd;
188 	struct agentx_master *master = cookie;
189 	struct sockaddr_un sun;
190 	socklen_t sunlen = sizeof(sun);
191 	struct appl_agentx_connection *conn = NULL;
192 
193 	if ((fd = accept(masterfd, (struct sockaddr *)&sun, &sunlen)) == -1) {
194 		log_warn("AgentX: accept %s", master->axm_sun.sun_path);
195 		return;
196 	}
197 
198 	if ((conn = malloc(sizeof(*conn))) == NULL) {
199 		log_warn(NULL);
200 		goto fail;
201 	}
202 
203 	conn->conn_backend = 0;
204 	TAILQ_INIT(&(conn->conn_sessions));
205 	if ((conn->conn_ax = ax_new(fd)) == NULL) {
206 		log_warn(NULL);
207 		goto fail;
208 	}
209 
210 	do {
211 		conn->conn_id = arc4random();
212 	} while (RB_INSERT(appl_agentx_conns,
213 	    &appl_agentx_conns, conn) != NULL);
214 
215 	event_set(&(conn->conn_rev), fd, EV_READ | EV_PERSIST,
216 	    appl_agentx_recv, conn);
217 	event_add(&(conn->conn_rev), NULL);
218 	event_set(&(conn->conn_wev), fd, EV_WRITE, appl_agentx_send, conn);
219 	log_info("AgentX(%"PRIu32"): new connection", conn->conn_id);
220 
221 	return;
222  fail:
223 	close(fd);
224 	free(conn);
225 }
226 
227 void
228 appl_agentx_backend(int fd)
229 {
230 	struct appl_agentx_connection *conn;
231 
232 	if ((conn = malloc(sizeof(*conn))) == NULL)
233 		fatal(NULL);
234 
235 	conn->conn_backend = 1;
236 	TAILQ_INIT(&(conn->conn_sessions));
237 	if ((conn->conn_ax = ax_new(fd)) == NULL)
238 		fatal("ax_new");
239 
240 	do {
241 		conn->conn_id = arc4random();
242 	} while (RB_INSERT(appl_agentx_conns,
243 	    &appl_agentx_conns, conn) != NULL);
244 
245 	event_set(&(conn->conn_rev), fd, EV_READ | EV_PERSIST,
246 	    appl_agentx_recv, conn);
247 	event_add(&(conn->conn_rev), NULL);
248 	event_set(&(conn->conn_wev), fd, EV_WRITE, appl_agentx_send, conn);
249 }
250 
251 void
252 appl_agentx_free(struct appl_agentx_connection *conn)
253 {
254 	struct appl_agentx_session *session;
255 
256 	event_del(&(conn->conn_rev));
257 	event_del(&(conn->conn_wev));
258 
259 	while ((session = TAILQ_FIRST(&(conn->conn_sessions))) != NULL) {
260 		if (conn->conn_ax == NULL)
261 			appl_agentx_session_free(session);
262 		else
263 			appl_agentx_forceclose(&(session->sess_backend),
264 			    APPL_CLOSE_REASONSHUTDOWN);
265 	}
266 
267 	RB_REMOVE(appl_agentx_conns, &appl_agentx_conns, conn);
268 	ax_free(conn->conn_ax);
269 	if (conn->conn_backend)
270 		fatalx("AgentX(%"PRIu32"): disappeared unexpected",
271 		    conn->conn_id);
272 	free(conn);
273 }
274 
275 void
276 appl_agentx_recv(int fd, short event, void *cookie)
277 {
278 	struct appl_agentx_connection *conn = cookie;
279 	struct appl_agentx_session *session;
280 	struct ax_pdu *pdu;
281 
282 	if ((pdu = ax_recv(conn->conn_ax)) == NULL) {
283 		if (errno == EAGAIN)
284 			return;
285 		log_warn("AgentX(%"PRIu32")", conn->conn_id);
286 		/*
287 		 * Either the connection is dead, or we had garbage on the line.
288 		 * Both make sure we can't continue on this stream.
289 		 */
290 		if (errno == ECONNRESET) {
291 			ax_free(conn->conn_ax);
292 			conn->conn_ax = NULL;
293 		}
294 		appl_agentx_free(conn);
295 		return;
296 	}
297 
298 	conn->conn_ax->ax_byteorder = pdu->ap_header.aph_flags &
299 	    AX_PDU_FLAG_NETWORK_BYTE_ORDER ?
300 	    AX_BYTE_ORDER_BE : AX_BYTE_ORDER_LE;
301 	if (pdu->ap_header.aph_type != AX_PDU_TYPE_OPEN) {
302 		/* Make sure we only look for connection-local sessions */
303 		TAILQ_FOREACH(session, &(conn->conn_sessions),
304 		    sess_conn_entry) {
305 			if (session->sess_id == pdu->ap_header.aph_sessionid)
306 				break;
307 		}
308 		if (session == NULL) {
309 			log_warnx("AgentX(%"PRIu32"): Session %"PRIu32" not "
310 			    "found for request", conn->conn_id,
311 			    pdu->ap_header.aph_sessionid);
312 			ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
313 			    pdu->ap_header.aph_transactionid,
314 			    pdu->ap_header.aph_packetid,
315 			    &(pdu->ap_context), smi_getticks(),
316 			    APPL_ERROR_NOTOPEN, 0, NULL, 0);
317 			appl_agentx_send(-1, EV_WRITE, conn);
318 			goto fail;
319 		}
320 		/*
321 		 * RFC2741 section 7.1.1 bullet 4 is unclear on what byte order
322 		 * the response should be. My best guess is that it makes more
323 		 * sense that replies are in the same byte-order as what was
324 		 * requested.
325 		 * In practice we always have the same byte order as when we
326 		 * opened the session, so it's likely a non-issue, however, we
327 		 * can change to session byte order here.
328 		 */
329 	}
330 
331 	switch (pdu->ap_header.aph_type) {
332 	case AX_PDU_TYPE_OPEN:
333 		appl_agentx_open(conn, pdu);
334 		break;
335 	case AX_PDU_TYPE_CLOSE:
336 		appl_agentx_close(session, pdu);
337 		break;
338 	case AX_PDU_TYPE_REGISTER:
339 		appl_agentx_register(session, pdu);
340 		break;
341 	case AX_PDU_TYPE_UNREGISTER:
342 		appl_agentx_unregister(session, pdu);
343 		break;
344 	case AX_PDU_TYPE_GET:
345 	case AX_PDU_TYPE_GETNEXT:
346 	case AX_PDU_TYPE_GETBULK:
347 	case AX_PDU_TYPE_TESTSET:
348 	case AX_PDU_TYPE_COMMITSET:
349 	case AX_PDU_TYPE_UNDOSET:
350 	case AX_PDU_TYPE_CLEANUPSET:
351 		appl_agentx_forceclose(&(session->sess_backend),
352 		    APPL_CLOSE_REASONPROTOCOLERROR);
353 		if (!TAILQ_EMPTY(&(conn->conn_sessions)))
354 			goto fail;
355 
356 		ax_pdu_free(pdu);
357 		appl_agentx_free(conn);
358 		return;
359 	case AX_PDU_TYPE_NOTIFY:
360 		log_warnx("%s: not supported",
361 		    ax_pdutype2string(pdu->ap_header.aph_type));
362 		/*
363 		 * RFC 2741 section 7.1.10:
364 		 * Note that the master agent's successful response indicates
365 		 * the agentx-Notify-PDU was received and validated.  It does
366 		 * not indicate that any particular notifications were actually
367 		 * generated or received by notification targets
368 		 */
369 		/* XXX Not yet - FALLTHROUGH */
370 	case AX_PDU_TYPE_PING:
371 		ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
372 		    pdu->ap_header.aph_transactionid,
373 		    pdu->ap_header.aph_packetid, &(pdu->ap_context),
374 		    smi_getticks(), APPL_ERROR_NOERROR, 0, NULL, 0);
375 		appl_agentx_send(-1, EV_WRITE, conn);
376 		break;
377 	case AX_PDU_TYPE_INDEXALLOCATE:
378 	case AX_PDU_TYPE_INDEXDEALLOCATE:
379 		log_warnx("%s: not supported",
380 		    ax_pdutype2string(pdu->ap_header.aph_type));
381 		ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
382 		    pdu->ap_header.aph_transactionid,
383 		    pdu->ap_header.aph_packetid, &(pdu->ap_context),
384 		    smi_getticks(), APPL_ERROR_PROCESSINGERROR, 1,
385 		    pdu->ap_payload.ap_vbl.ap_varbind,
386 		    pdu->ap_payload.ap_vbl.ap_nvarbind);
387 		appl_agentx_send(-1, EV_WRITE, conn);
388 		break;
389 	case AX_PDU_TYPE_ADDAGENTCAPS:
390 	case AX_PDU_TYPE_REMOVEAGENTCAPS:
391 		log_warnx("%s: not supported",
392 		    ax_pdutype2string(pdu->ap_header.aph_type));
393 		ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
394 		    pdu->ap_header.aph_transactionid,
395 		    pdu->ap_header.aph_packetid, &(pdu->ap_context),
396 		    smi_getticks(), APPL_ERROR_PROCESSINGERROR, 1,
397 		    NULL, 0);
398 		appl_agentx_send(-1, EV_WRITE, conn);
399 		break;
400 	case AX_PDU_TYPE_RESPONSE:
401 		appl_agentx_response(session, pdu);
402 		break;
403 	}
404 
405  fail:
406 	ax_pdu_free(pdu);
407 }
408 
409 void
410 appl_agentx_open(struct appl_agentx_connection *conn, struct ax_pdu *pdu)
411 {
412 	struct appl_agentx_session *session;
413 	struct ber_oid oid;
414 	char oidbuf[1024];
415 
416 	if ((session = malloc(sizeof(*session))) == NULL) {
417 		log_warn(NULL);
418 		goto fail;
419 	}
420 	session->sess_descr.aos_string = NULL;
421 
422 	session->sess_conn = conn;
423 	if (pdu->ap_header.aph_flags & AX_PDU_FLAG_NETWORK_BYTE_ORDER)
424 		session->sess_byteorder = AX_BYTE_ORDER_BE;
425 	else
426 		session->sess_byteorder = AX_BYTE_ORDER_LE;
427 
428 	/* RFC 2742 agentxSessionObjectID */
429 	if (pdu->ap_payload.ap_open.ap_oid.aoi_idlen == 0) {
430 		pdu->ap_payload.ap_open.ap_oid.aoi_id[0] = 0;
431 		pdu->ap_payload.ap_open.ap_oid.aoi_id[1] = 0;
432 		pdu->ap_payload.ap_open.ap_oid.aoi_idlen = 2;
433 	} else if (pdu->ap_payload.ap_open.ap_oid.aoi_idlen == 1) {
434 		log_warnx("AgentX(%"PRIu32"): Invalid oid: Open Failed",
435 		    conn->conn_id);
436 		goto fail;
437 	}
438 	/* RFC 2742 agentxSessionDescr */
439 	if (pdu->ap_payload.ap_open.ap_descr.aos_slen > 255) {
440 		log_warnx("AgentX(%"PRIu32"): Invalid descr (too long): Open "
441 		    "Failed", conn->conn_id);
442 		goto fail;
443 	}
444 	/*
445 	 * ax_ostring is always NUL-terminated, but doesn't scan for internal
446 	 * NUL-bytes. However, mbstowcs stops at NUL, which might be in the
447 	 * middle of the string.
448 	 */
449 	if (strlen(pdu->ap_payload.ap_open.ap_descr.aos_string) !=
450 	    pdu->ap_payload.ap_open.ap_descr.aos_slen ||
451 	    mbstowcs(NULL,
452 	    pdu->ap_payload.ap_open.ap_descr.aos_string, 0) == (size_t)-1) {
453 		log_warnx("AgentX(%"PRIu32"): Invalid descr (not UTF-8): "
454 		    "Open Failed", conn->conn_id);
455 		goto fail;
456 	}
457 
458 	session->sess_timeout = pdu->ap_payload.ap_open.ap_timeout;
459 	session->sess_oid = pdu->ap_payload.ap_open.ap_oid;
460 	session->sess_descr.aos_slen = pdu->ap_payload.ap_open.ap_descr.aos_slen;
461 	if (pdu->ap_payload.ap_open.ap_descr.aos_string != NULL) {
462 		session->sess_descr.aos_string =
463 		    strdup(pdu->ap_payload.ap_open.ap_descr.aos_string);
464 		if (session->sess_descr.aos_string == NULL) {
465 			log_warn("AgentX(%"PRIu32"): strdup: Open Failed",
466 			    conn->conn_id);
467 			goto fail;
468 		}
469 	}
470 
471 	/* RFC 2742 agentxSessionIndex: chances of reuse, slim to none */
472 	do {
473 		session->sess_id = arc4random();
474 	} while (RB_INSERT(appl_agentx_sessions,
475 	    &appl_agentx_sessions, session) != NULL);
476 
477 	if (asprintf(&(session->sess_backend.ab_name),
478 	    "AgentX(%"PRIu32"/%"PRIu32")",
479 	    conn->conn_id, session->sess_id) == -1) {
480 		log_warn("AgentX(%"PRIu32"): asprintf: Open Failed",
481 		    conn->conn_id);
482 		goto fail;
483 	}
484 	session->sess_backend.ab_cookie = session;
485 	session->sess_backend.ab_retries = 0;
486 	session->sess_backend.ab_fn = &appl_agentx_functions;
487 	RB_INIT(&(session->sess_backend.ab_requests));
488 	TAILQ_INSERT_TAIL(&(conn->conn_sessions), session, sess_conn_entry);
489 
490 	appl_agentx_oid2ber_oid(&(session->sess_oid), &oid);
491 	smi_oid2string(&oid, oidbuf, sizeof(oidbuf), 0);
492 	log_info("%s: %s %s: Open", session->sess_backend.ab_name, oidbuf,
493 	    session->sess_descr.aos_string);
494 
495 	ax_response(conn->conn_ax, session->sess_id, pdu->ap_header.aph_transactionid,
496 	    pdu->ap_header.aph_packetid, NULL, smi_getticks(), APPL_ERROR_NOERROR, 0,
497 	    NULL, 0);
498 	appl_agentx_send(-1, EV_WRITE, conn);
499 
500 	return;
501  fail:
502 	ax_response(conn->conn_ax, 0, pdu->ap_header.aph_transactionid,
503 	    pdu->ap_header.aph_packetid, NULL, 0, APPL_ERROR_OPENFAILED, 0,
504 	    NULL, 0);
505 	appl_agentx_send(-1, EV_WRITE, conn);
506 	if (session != NULL)
507 		free(session->sess_descr.aos_string);
508 	free(session);
509 }
510 
511 void
512 appl_agentx_close(struct appl_agentx_session *session, struct ax_pdu *pdu)
513 {
514 	struct appl_agentx_connection *conn = session->sess_conn;
515 	char name[100];
516 
517 	strlcpy(name, session->sess_backend.ab_name, sizeof(name));
518 	appl_agentx_session_free(session);
519 	log_info("%s: Closed by subagent (%s)", name,
520 	    ax_closereason2string(pdu->ap_payload.ap_close.ap_reason));
521 
522 	ax_response(conn->conn_ax, pdu->ap_header.aph_sessionid,
523 	    pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
524 	    &(pdu->ap_context), smi_getticks(), APPL_ERROR_NOERROR, 0, NULL, 0);
525 	appl_agentx_send(-1, EV_WRITE, conn);
526 }
527 
528 void
529 appl_agentx_forceclose(struct appl_backend *backend,
530     enum appl_close_reason reason)
531 {
532 	struct appl_agentx_session *session = backend->ab_cookie;
533 	char name[100];
534 
535 	session->sess_conn->conn_ax->ax_byteorder = session->sess_byteorder;
536 	ax_close(session->sess_conn->conn_ax, session->sess_id,
537 	    (enum ax_close_reason) reason);
538 	appl_agentx_send(-1, EV_WRITE, session->sess_conn);
539 
540 	strlcpy(name, session->sess_backend.ab_name, sizeof(name));
541 	appl_agentx_session_free(session);
542 	log_info("%s: Closed by snmpd (%s)", name,
543 	    ax_closereason2string((enum ax_close_reason)reason));
544 }
545 
546 void
547 appl_agentx_session_free(struct appl_agentx_session *session)
548 {
549 	struct appl_agentx_connection *conn = session->sess_conn;
550 
551 	appl_close(&(session->sess_backend));
552 
553 	RB_REMOVE(appl_agentx_sessions, &appl_agentx_sessions, session);
554 	TAILQ_REMOVE(&(conn->conn_sessions), session, sess_conn_entry);
555 
556 	free(session->sess_backend.ab_name);
557 	free(session->sess_descr.aos_string);
558 	free(session);
559 }
560 
561 void
562 appl_agentx_register(struct appl_agentx_session *session, struct ax_pdu *pdu)
563 {
564 	uint32_t timeout;
565 	struct ber_oid oid;
566 	enum appl_error error;
567 	int subtree = 0;
568 
569 	timeout = pdu->ap_payload.ap_register.ap_timeout;
570 	timeout = timeout != 0 ? timeout : session->sess_timeout != 0 ?
571 	    session->sess_timeout : AGENTX_DEFAULTTIMEOUT;
572 	timeout *= 100;
573 
574 	if (session->sess_conn->conn_backend) {
575 		pdu->ap_payload.ap_register.ap_priority = 1;
576 		subtree = 1;
577 	}
578 	if (appl_agentx_oid2ber_oid(
579 	    &(pdu->ap_payload.ap_register.ap_subtree), &oid) == NULL) {
580 		log_warnx("%s: Failed to register: oid too small",
581 		    session->sess_backend.ab_name);
582 		error = APPL_ERROR_PROCESSINGERROR;
583 		goto fail;
584 	}
585 
586 	error = appl_register(pdu->ap_context.aos_string, timeout,
587 	    pdu->ap_payload.ap_register.ap_priority, &oid,
588 	    pdu->ap_header.aph_flags & AX_PDU_FLAG_INSTANCE_REGISTRATION,
589 	    subtree, pdu->ap_payload.ap_register.ap_range_subid,
590 	    pdu->ap_payload.ap_register.ap_upper_bound,
591 	    &(session->sess_backend));
592 
593  fail:
594 	ax_response(session->sess_conn->conn_ax, session->sess_id,
595 	    pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
596 	    &(pdu->ap_context), smi_getticks(), error, 0, NULL, 0);
597 	appl_agentx_send(-1, EV_WRITE, session->sess_conn);
598 }
599 
600 void
601 appl_agentx_unregister(struct appl_agentx_session *session, struct ax_pdu *pdu)
602 {
603 	struct ber_oid oid;
604 	enum appl_error error;
605 
606 	if (appl_agentx_oid2ber_oid(
607 	    &(pdu->ap_payload.ap_unregister.ap_subtree), &oid) == NULL) {
608 		log_warnx("%s: Failed to unregister: oid too small",
609 		    session->sess_backend.ab_name);
610 		error = APPL_ERROR_PROCESSINGERROR;
611 		goto fail;
612 	}
613 
614 	error = appl_unregister(pdu->ap_context.aos_string,
615 	    pdu->ap_payload.ap_unregister.ap_priority, &oid,
616 	    pdu->ap_payload.ap_unregister.ap_range_subid,
617 	    pdu->ap_payload.ap_unregister.ap_upper_bound,
618 	    &(session->sess_backend));
619 
620  fail:
621 	ax_response(session->sess_conn->conn_ax, session->sess_id,
622 	    pdu->ap_header.aph_transactionid, pdu->ap_header.aph_packetid,
623 	    &(pdu->ap_context), smi_getticks(), error, 0, NULL, 0);
624 	appl_agentx_send(-1, EV_WRITE, session->sess_conn);
625 }
626 
627 #define AX_PDU_FLAG_INDEX (AX_PDU_FLAG_NEW_INDEX | AX_PDU_FLAG_ANY_INDEX)
628 
629 void
630 appl_agentx_get(struct appl_backend *backend, int32_t transactionid,
631     int32_t requestid, const char *ctx, struct appl_varbind *vblist)
632 {
633 	struct appl_agentx_session *session = backend->ab_cookie;
634 	struct ax_ostring *context, string;
635 	struct appl_varbind *vb;
636 	struct ax_searchrange *srl;
637 	size_t i, j, nsr;
638 
639 	for (nsr = 0, vb = vblist; vb != NULL; vb = vb->av_next)
640 		nsr++;
641 
642 	if ((srl = calloc(nsr, sizeof(*srl))) == NULL) {
643 		log_warn(NULL);
644 		appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist);
645 		return;
646 	}
647 
648 	for (i = 0, vb = vblist; i < nsr; i++, vb = vb->av_next) {
649 		srl[i].asr_start.aoi_include = vb->av_include;
650 		srl[i].asr_start.aoi_idlen = vb->av_oid.bo_n;
651 		for (j = 0; j < vb->av_oid.bo_n; j++)
652 			srl[i].asr_start.aoi_id[j] = vb->av_oid.bo_id[j];
653 		srl[i].asr_stop.aoi_include = 0;
654 		srl[i].asr_stop.aoi_idlen = 0;
655 	}
656 	if ((context = appl_agentx_string2ostring(ctx, &string)) == NULL) {
657 		if (errno != 0) {
658 			log_warn("Failed to convert context");
659 			appl_response(backend, requestid,
660 			    APPL_ERROR_GENERR, 1, vblist);
661 			free(srl);
662 			return;
663 		}
664 	}
665 
666 	session->sess_conn->conn_ax->ax_byteorder = session->sess_byteorder;
667 	if (ax_get(session->sess_conn->conn_ax, session->sess_id, transactionid,
668 	    requestid, context, srl, nsr) == -1)
669 		appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist);
670 	else
671 		appl_agentx_send(-1, EV_WRITE, session->sess_conn);
672 	free(srl);
673 	if (context != NULL)
674 		free(context->aos_string);
675 }
676 
677 void
678 appl_agentx_getnext(struct appl_backend *backend, int32_t transactionid,
679     int32_t requestid, const char *ctx, struct appl_varbind *vblist)
680 {
681 	struct appl_agentx_session *session = backend->ab_cookie;
682 	struct ax_ostring *context, string;
683 	struct appl_varbind *vb;
684 	struct ax_searchrange *srl;
685 	size_t i, j, nsr;
686 
687 	for (nsr = 0, vb = vblist; vb != NULL; vb = vb->av_next)
688 		nsr++;
689 
690 	if ((srl = calloc(nsr, sizeof(*srl))) == NULL) {
691 		log_warn(NULL);
692 		appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist);
693 		return;
694 	}
695 
696 	for (i = 0, vb = vblist; i < nsr; i++, vb = vb->av_next) {
697 		srl[i].asr_start.aoi_include = vb->av_include;
698 		srl[i].asr_start.aoi_idlen = vb->av_oid.bo_n;
699 		for (j = 0; j < vb->av_oid.bo_n; j++)
700 			srl[i].asr_start.aoi_id[j] = vb->av_oid.bo_id[j];
701 		srl[i].asr_stop.aoi_include = 0;
702 		srl[i].asr_stop.aoi_idlen = vb->av_oid_end.bo_n;
703 		for (j = 0; j < vb->av_oid_end.bo_n; j++)
704 			srl[i].asr_stop.aoi_id[j] = vb->av_oid_end.bo_id[j];
705 	}
706 	if ((context = appl_agentx_string2ostring(ctx, &string)) == NULL) {
707 		if (errno != 0) {
708 			log_warn("Failed to convert context");
709 			appl_response(backend, requestid,
710 			    APPL_ERROR_GENERR, 1, vblist);
711 			free(srl);
712 			return;
713 		}
714 	}
715 
716 	session->sess_conn->conn_ax->ax_byteorder = session->sess_byteorder;
717 	if (ax_getnext(session->sess_conn->conn_ax, session->sess_id, transactionid,
718 	    requestid, context, srl, nsr) == -1)
719 		appl_response(backend, requestid, APPL_ERROR_GENERR, 1, vblist);
720 	else
721 		appl_agentx_send(-1, EV_WRITE, session->sess_conn);
722 	free(srl);
723 	if (context != NULL)
724 		free(context->aos_string);
725 }
726 
727 void
728 appl_agentx_response(struct appl_agentx_session *session, struct ax_pdu *pdu)
729 {
730 	struct appl_varbind *response = NULL;
731 	struct ax_varbind *vb;
732 	enum appl_error error;
733 	uint16_t index;
734 	size_t i, nvarbind;
735 
736 	nvarbind = pdu->ap_payload.ap_response.ap_nvarbind;
737 	if ((response = calloc(nvarbind, sizeof(*response))) == NULL) {
738 		log_warn(NULL);
739 		appl_response(&(session->sess_backend),
740 		    pdu->ap_header.aph_packetid,
741 		    APPL_ERROR_GENERR, 1, NULL);
742 		return;
743 	}
744 
745 	error = (enum appl_error)pdu->ap_payload.ap_response.ap_error;
746 	index = pdu->ap_payload.ap_response.ap_index;
747 	for (i = 0; i < nvarbind; i++) {
748 		response[i].av_next = i + 1 == nvarbind ?
749 		    NULL : &(response[i + 1]);
750 		vb = &(pdu->ap_payload.ap_response.ap_varbindlist[i]);
751 
752 		if (appl_agentx_oid2ber_oid(&(vb->avb_oid),
753 		    &(response[i].av_oid)) == NULL) {
754 			log_warnx("%s: invalid oid",
755 			    session->sess_backend.ab_name);
756 			if (error != APPL_ERROR_NOERROR) {
757 				error = APPL_ERROR_GENERR;
758 				index = i + 1;
759 			}
760 			continue;
761 		}
762 		response[i].av_value = appl_agentx_value2ber_element(vb);
763 		if (response[i].av_value == NULL) {
764 			log_warn("%s: Failed to parse response value",
765 			    session->sess_backend.ab_name);
766 			if (error != APPL_ERROR_NOERROR) {
767 				error = APPL_ERROR_GENERR;
768 				index = i + 1;
769 			}
770 		}
771 	}
772 	appl_response(&(session->sess_backend), pdu->ap_header.aph_packetid,
773 	    error, index, response);
774 	free(response);
775 }
776 
777 void
778 appl_agentx_send(int fd, short event, void *cookie)
779 {
780 	struct appl_agentx_connection *conn = cookie;
781 
782 	switch (ax_send(conn->conn_ax)) {
783 	case -1:
784 		if (errno == EAGAIN)
785 			break;
786 		log_warn("AgentX(%"PRIu32")", conn->conn_id);
787 		ax_free(conn->conn_ax);
788 		conn->conn_ax = NULL;
789 		appl_agentx_free(conn);
790 		return;
791 	case 0:
792 		return;
793 	default:
794 		break;
795 	}
796 	event_add(&(conn->conn_wev), NULL);
797 }
798 
799 struct ber_oid *
800 appl_agentx_oid2ber_oid(struct ax_oid *aoid, struct ber_oid *boid)
801 {
802 	size_t i;
803 
804 	if (aoid->aoi_idlen < BER_MIN_OID_LEN ||
805 	    aoid->aoi_idlen > BER_MAX_OID_LEN) {
806 		errno = EINVAL;
807 		return NULL;
808 	}
809 
810 
811 	boid->bo_n = aoid->aoi_idlen;
812 	for (i = 0; i < boid->bo_n; i++)
813 		boid->bo_id[i] = aoid->aoi_id[i];
814 	return boid;
815 }
816 
817 struct ber_element *
818 appl_agentx_value2ber_element(struct ax_varbind *vb)
819 {
820 	struct ber_oid oid;
821 	struct ber_element *elm;
822 
823 	switch (vb->avb_type) {
824 	case AX_DATA_TYPE_INTEGER:
825 		return ober_add_integer(NULL, vb->avb_data.avb_int32);
826 	case AX_DATA_TYPE_OCTETSTRING:
827 		return ober_add_nstring(NULL,
828 		    vb->avb_data.avb_ostring.aos_string,
829 		    vb->avb_data.avb_ostring.aos_slen);
830 	case AX_DATA_TYPE_NULL:
831 		return ober_add_null(NULL);
832 	case AX_DATA_TYPE_OID:
833 		if (appl_agentx_oid2ber_oid(
834 		    &(vb->avb_data.avb_oid), &oid) == NULL)
835 			return NULL;
836 		return ober_add_oid(NULL, &oid);
837 	case AX_DATA_TYPE_IPADDRESS:
838 		if ((elm = ober_add_nstring(NULL,
839 		    vb->avb_data.avb_ostring.aos_string,
840 		    vb->avb_data.avb_ostring.aos_slen)) == NULL)
841 			return NULL;
842 		ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_IPADDR);
843 		return elm;
844 	case AX_DATA_TYPE_COUNTER32:
845 		elm = ober_add_integer(NULL, vb->avb_data.avb_uint32);
846 		if (elm == NULL)
847 			return NULL;
848 		ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_COUNTER32);
849 		return elm;
850 	case AX_DATA_TYPE_GAUGE32:
851 		elm = ober_add_integer(NULL, vb->avb_data.avb_uint32);
852 		if (elm == NULL)
853 			return NULL;
854 		ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_GAUGE32);
855 		return elm;
856 	case AX_DATA_TYPE_TIMETICKS:
857 		elm = ober_add_integer(NULL, vb->avb_data.avb_uint32);
858 		if (elm == NULL)
859 			return NULL;
860 		ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_TIMETICKS);
861 		return elm;
862 	case AX_DATA_TYPE_OPAQUE:
863 		if ((elm = ober_add_nstring(NULL,
864 		    vb->avb_data.avb_ostring.aos_string,
865 		    vb->avb_data.avb_ostring.aos_slen)) == NULL)
866 			return NULL;
867 		ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_OPAQUE);
868 		return elm;
869 	case AX_DATA_TYPE_COUNTER64:
870 		elm = ober_add_integer(NULL, vb->avb_data.avb_uint64);
871 		if (elm == NULL)
872 			return NULL;
873 		ober_set_header(elm, BER_CLASS_APPLICATION, SNMP_T_COUNTER64);
874 		return elm;
875 	case AX_DATA_TYPE_NOSUCHOBJECT:
876 		return appl_exception(APPL_EXC_NOSUCHOBJECT);
877 	case AX_DATA_TYPE_NOSUCHINSTANCE:
878 		return appl_exception(APPL_EXC_NOSUCHINSTANCE);
879 	case AX_DATA_TYPE_ENDOFMIBVIEW:
880 		return appl_exception(APPL_EXC_ENDOFMIBVIEW);
881 	default:
882 		errno = EINVAL;
883 		return NULL;
884 	}
885 }
886 
887 struct ax_ostring *
888 appl_agentx_string2ostring(const char *str, struct ax_ostring *ostring)
889 {
890 	if (str == NULL) {
891 		errno = 0;
892 		return NULL;
893 	}
894 
895 	ostring->aos_slen = strlen(str);
896 	if ((ostring->aos_string = strdup(str)) == NULL)
897 		return NULL;
898 	return ostring;
899 }
900 
901 int
902 appl_agentx_cmp(struct appl_agentx_connection *conn1,
903     struct appl_agentx_connection *conn2)
904 {
905 	return conn1->conn_id < conn2->conn_id ? -1 :
906 	    conn1->conn_id > conn2->conn_id;
907 }
908 
909 int
910 appl_agentx_session_cmp(struct appl_agentx_session *sess1,
911     struct appl_agentx_session *sess2)
912 {
913 	return sess1->sess_id < sess2->sess_id ? -1 : sess1->sess_id > sess2->sess_id;
914 }
915 
916 RB_GENERATE_STATIC(appl_agentx_conns, appl_agentx_connection, conn_entry,
917     appl_agentx_cmp);
918 RB_GENERATE_STATIC(appl_agentx_sessions, appl_agentx_session, sess_entry,
919     appl_agentx_session_cmp);
920