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