1 /* $OpenBSD: application.c,v 1.17 2022/12/20 20:04:55 martijn Exp $ */ 2 3 /* 4 * Copyright (c) 2021 Martijn van Duren <martijn@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/queue.h> 20 #include <sys/tree.h> 21 22 #include <assert.h> 23 #include <errno.h> 24 #include <event.h> 25 #include <inttypes.h> 26 #include <stdlib.h> 27 #include <stdio.h> 28 #include <string.h> 29 30 #include "application.h" 31 #include "log.h" 32 #include "smi.h" 33 #include "snmp.h" 34 #include "snmpe.h" 35 #include "mib.h" 36 37 TAILQ_HEAD(, appl_context) contexts = TAILQ_HEAD_INITIALIZER(contexts); 38 39 struct appl_context { 40 char ac_name[APPL_CONTEXTNAME_MAX + 1]; 41 42 RB_HEAD(appl_regions, appl_region) ac_regions; 43 44 TAILQ_ENTRY(appl_context) ac_entries; 45 }; 46 47 struct appl_region { 48 struct ber_oid ar_oid; 49 uint8_t ar_priority; 50 int32_t ar_timeout; 51 int ar_instance; 52 int ar_subtree; /* Claim entire subtree */ 53 struct appl_backend *ar_backend; 54 struct appl_region *ar_next; /* Sorted by priority */ 55 56 RB_ENTRY(appl_region) ar_entry; 57 }; 58 59 struct appl_request_upstream { 60 struct appl_context *aru_ctx; 61 struct snmp_message *aru_statereference; 62 int32_t aru_requestid; /* upstream requestid */ 63 int32_t aru_transactionid; /* RFC 2741 section 6.1 */ 64 uint16_t aru_nonrepeaters; 65 uint16_t aru_maxrepetitions; 66 struct appl_varbind_internal *aru_vblist; 67 size_t aru_varbindlen; 68 enum appl_error aru_error; 69 int16_t aru_index; 70 int aru_locked; /* Prevent recursion through appl_request_send */ 71 72 enum snmp_version aru_pduversion; 73 struct ber_element *aru_pdu; /* Original requested pdu */ 74 }; 75 76 struct appl_request_downstream { 77 struct appl_request_upstream *ard_request; 78 struct appl_backend *ard_backend; 79 enum snmp_pdutype ard_requesttype; 80 uint16_t ard_nonrepeaters; 81 uint16_t ard_maxrepetitions; 82 int32_t ard_requestid; 83 uint8_t ard_retries; 84 85 struct appl_varbind_internal *ard_vblist; 86 struct event ard_timer; 87 88 RB_ENTRY(appl_request_downstream) ard_entry; 89 }; 90 91 enum appl_varbind_state { 92 APPL_VBSTATE_MUSTFILL, 93 APPL_VBSTATE_NEW, 94 APPL_VBSTATE_PENDING, 95 APPL_VBSTATE_DONE 96 }; 97 98 struct appl_varbind_internal { 99 enum appl_varbind_state avi_state; 100 struct appl_varbind avi_varbind; 101 struct appl_region *avi_region; 102 int16_t avi_index; 103 struct appl_request_upstream *avi_request_upstream; 104 struct appl_request_downstream *avi_request_downstream; 105 struct appl_varbind_internal *avi_next; 106 struct appl_varbind_internal *avi_sub; 107 }; 108 109 /* SNMP-TARGET-MIB (RFC 3413) */ 110 struct snmp_target_mib { 111 uint32_t snmp_unavailablecontexts; 112 uint32_t snmp_unknowncontexts; 113 } snmp_target_mib; 114 115 enum appl_error appl_region(struct appl_context *, uint32_t, uint8_t, 116 struct ber_oid *, int, int, struct appl_backend *); 117 void appl_region_free(struct appl_context *, struct appl_region *); 118 struct appl_region *appl_region_find(struct appl_context *, 119 const struct ber_oid *); 120 struct appl_region *appl_region_next(struct appl_context *, 121 struct ber_oid *, struct appl_region *); 122 void appl_request_upstream_free(struct appl_request_upstream *); 123 void appl_request_downstream_free(struct appl_request_downstream *); 124 void appl_request_upstream_resolve(struct appl_request_upstream *); 125 void appl_request_downstream_send(struct appl_request_downstream *); 126 void appl_request_downstream_timeout(int, short, void *); 127 void appl_request_upstream_reply(struct appl_request_upstream *); 128 int appl_varbind_valid(struct appl_varbind *, struct appl_varbind *, int, int, 129 int, const char **); 130 int appl_varbind_backend(struct appl_varbind_internal *); 131 void appl_varbind_error(struct appl_varbind_internal *, enum appl_error); 132 void appl_report(struct snmp_message *, int32_t, struct ber_oid *, 133 struct ber_element *); 134 void appl_pdu_log(struct appl_backend *, enum snmp_pdutype, int32_t, uint16_t, 135 uint16_t, struct appl_varbind *); 136 void ober_oid_nextsibling(struct ber_oid *); 137 138 int appl_region_cmp(struct appl_region *, struct appl_region *); 139 int appl_request_cmp(struct appl_request_downstream *, 140 struct appl_request_downstream *); 141 142 RB_PROTOTYPE_STATIC(appl_regions, appl_region, ar_entry, appl_region_cmp); 143 RB_PROTOTYPE_STATIC(appl_requests, appl_request_downstream, ard_entry, 144 appl_request_cmp); 145 146 #define APPL_CONTEXT_NAME(ctx) (ctx->ac_name[0] == '\0' ? NULL : ctx->ac_name) 147 148 void 149 appl(void) 150 { 151 appl_agentx(); 152 } 153 154 void 155 appl_init(void) 156 { 157 appl_blocklist_init(); 158 appl_legacy_init(); 159 appl_agentx_init(); 160 } 161 162 void 163 appl_shutdown(void) 164 { 165 struct appl_context *ctx, *tctx; 166 167 appl_blocklist_shutdown(); 168 appl_legacy_shutdown(); 169 appl_agentx_shutdown(); 170 171 TAILQ_FOREACH_SAFE(ctx, &contexts, ac_entries, tctx) { 172 assert(RB_EMPTY(&(ctx->ac_regions))); 173 TAILQ_REMOVE(&contexts, ctx, ac_entries); 174 free(ctx); 175 } 176 } 177 178 static struct appl_context * 179 appl_context(const char *name, int create) 180 { 181 struct appl_context *ctx; 182 183 if (name == NULL) 184 name = ""; 185 186 if (strlen(name) > APPL_CONTEXTNAME_MAX) { 187 errno = EINVAL; 188 return NULL; 189 } 190 191 TAILQ_FOREACH(ctx, &contexts, ac_entries) { 192 if (strcmp(name, ctx->ac_name) == 0) 193 return ctx; 194 } 195 196 /* Always allow the default namespace */ 197 if (!create && name[0] != '\0') { 198 errno = ENOENT; 199 return NULL; 200 } 201 202 if ((ctx = malloc(sizeof(*ctx))) == NULL) 203 return NULL; 204 205 strlcpy(ctx->ac_name, name, sizeof(ctx->ac_name)); 206 RB_INIT(&(ctx->ac_regions)); 207 208 TAILQ_INSERT_TAIL(&contexts, ctx, ac_entries); 209 return ctx; 210 } 211 212 enum appl_error 213 appl_region(struct appl_context *ctx, uint32_t timeout, uint8_t priority, 214 struct ber_oid *oid, int instance, int subtree, 215 struct appl_backend *backend) 216 { 217 struct appl_region *region = NULL, *nregion; 218 char oidbuf[1024], regionbuf[1024], subidbuf[11]; 219 size_t i; 220 221 /* Don't use smi_oid2string, because appl_register can't use it */ 222 oidbuf[0] = '\0'; 223 for (i = 0; i < oid->bo_n; i++) { 224 if (i != 0) 225 strlcat(oidbuf, ".", sizeof(oidbuf)); 226 snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, 227 oid->bo_id[i]); 228 strlcat(oidbuf, subidbuf, sizeof(oidbuf)); 229 } 230 231 /* 232 * Don't allow overlap when subtree flag is set. 233 * This allows us to keep control of certain regions like system. 234 */ 235 region = appl_region_find(ctx, oid); 236 if (region != NULL && region->ar_subtree && 237 region->ar_backend != backend) 238 goto overlap; 239 240 if ((nregion = malloc(sizeof(*nregion))) == NULL) { 241 log_warn("%s: Can't register %s: Processing error", 242 backend->ab_name, oidbuf); 243 return APPL_ERROR_PROCESSINGERROR; 244 } 245 nregion->ar_oid = *oid; 246 nregion->ar_priority = priority; 247 nregion->ar_timeout = timeout; 248 nregion->ar_instance = instance; 249 nregion->ar_subtree = subtree; 250 nregion->ar_backend = backend; 251 nregion->ar_next = NULL; 252 253 region = RB_INSERT(appl_regions, &(ctx->ac_regions), nregion); 254 if (region == NULL) 255 return APPL_ERROR_NOERROR; 256 257 if (region->ar_priority == priority) 258 goto duplicate; 259 if (region->ar_priority > priority) { 260 RB_REMOVE(appl_regions, &(ctx->ac_regions), region); 261 RB_INSERT(appl_regions, &(ctx->ac_regions), nregion); 262 nregion->ar_next = region; 263 return APPL_ERROR_NOERROR; 264 } 265 266 while (region->ar_next != NULL && 267 region->ar_next->ar_priority < priority) 268 region = region->ar_next; 269 if (region->ar_next != NULL && region->ar_next->ar_priority == priority) 270 goto duplicate; 271 nregion->ar_next = region->ar_next; 272 region->ar_next = nregion; 273 274 return APPL_ERROR_NOERROR; 275 duplicate: 276 free(nregion); 277 log_info("%s: %s priority %"PRId8": Duplicate registration", 278 backend->ab_name, oidbuf, priority); 279 return APPL_ERROR_DUPLICATEREGISTRATION; 280 overlap: 281 regionbuf[0] = '\0'; 282 for (i = 0; i < region->ar_oid.bo_n; i++) { 283 if (i != 0) 284 strlcat(regionbuf, ".", sizeof(regionbuf)); 285 snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, 286 region->ar_oid.bo_id[i]); 287 strlcat(regionbuf, subidbuf, sizeof(regionbuf)); 288 } 289 log_info("%s: %s overlaps with %s: Request denied", 290 backend->ab_name, oidbuf, regionbuf); 291 return APPL_ERROR_REQUESTDENIED; 292 } 293 294 /* Name from RFC 2741 section 6.2.3 */ 295 enum appl_error 296 appl_register(const char *ctxname, uint32_t timeout, uint8_t priority, 297 struct ber_oid *oid, int instance, int subtree, uint8_t range_subid, 298 uint32_t upper_bound, struct appl_backend *backend) 299 { 300 struct appl_context *ctx; 301 struct appl_region *region, search; 302 char oidbuf[1024], subidbuf[11]; 303 enum appl_error error; 304 size_t i; 305 uint32_t lower_bound; 306 307 oidbuf[0] = '\0'; 308 /* smi_oid2string can't do ranges */ 309 for (i = 0; i < oid->bo_n; i++) { 310 snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, oid->bo_id[i]); 311 if (i != 0) 312 strlcat(oidbuf, ".", sizeof(oidbuf)); 313 if (range_subid == i + 1) { 314 strlcat(oidbuf, "[", sizeof(oidbuf)); 315 strlcat(oidbuf, subidbuf, sizeof(oidbuf)); 316 strlcat(oidbuf, "-", sizeof(oidbuf)); 317 snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, 318 upper_bound); 319 strlcat(oidbuf, subidbuf, sizeof(oidbuf)); 320 strlcat(oidbuf, "]", sizeof(oidbuf)); 321 } else 322 strlcat(oidbuf, subidbuf, sizeof(oidbuf)); 323 } 324 325 if (ctxname == NULL) 326 ctxname = ""; 327 log_info("%s: Registering %s%s context(%s) priority(%"PRIu8") " 328 "timeout(%"PRIu32".%02us)", backend->ab_name, oidbuf, 329 instance ? "(instance)" : "", ctxname, priority, 330 timeout/100, timeout % 100); 331 332 if ((ctx = appl_context(ctxname, 0)) == NULL) { 333 if (errno == ENOMEM) { 334 log_warn("%s: Can't register %s: Processing error", 335 backend->ab_name, oidbuf); 336 return APPL_ERROR_PROCESSINGERROR; 337 } 338 log_info("%s: Can't register %s: Unsupported context \"%s\"", 339 backend->ab_name, oidbuf, ctxname); 340 return APPL_ERROR_UNSUPPORTEDCONTEXT; 341 } 342 /* Default timeouts should be handled by backend */ 343 if (timeout == 0) 344 fatalx("%s: Timeout can't be 0", __func__); 345 if (priority == 0) { 346 log_warnx("%s: Can't register %s: priority can't be 0", 347 backend->ab_name, oidbuf); 348 return APPL_ERROR_PARSEERROR; 349 } 350 if (range_subid > oid->bo_n) { 351 log_warnx("%s: Can't register %s: range_subid too large", 352 backend->ab_name, oidbuf); 353 return APPL_ERROR_PARSEERROR; 354 } 355 if (range_subid != 0 && oid->bo_id[range_subid] >= upper_bound) { 356 log_warnx("%s: Can't register %s: upper bound smaller or equal " 357 "to range_subid", backend->ab_name, oidbuf); 358 return APPL_ERROR_PARSEERROR; 359 } 360 if (range_subid != 0) 361 lower_bound = oid->bo_id[range_subid]; 362 363 if (range_subid == 0) 364 return appl_region(ctx, timeout, priority, oid, instance, 365 subtree, backend); 366 367 do { 368 if ((error = appl_region(ctx, timeout, priority, oid, instance, 369 subtree, backend)) != APPL_ERROR_NOERROR) 370 goto fail; 371 } while (oid->bo_id[range_subid] != upper_bound); 372 if ((error = appl_region(ctx, timeout, priority, oid, instance, subtree, 373 backend)) != APPL_ERROR_NOERROR) 374 goto fail; 375 376 return APPL_ERROR_NOERROR; 377 fail: 378 search.ar_oid = *oid; 379 if (search.ar_oid.bo_id[range_subid] == lower_bound) 380 return error; 381 382 for (search.ar_oid.bo_id[range_subid]--; 383 search.ar_oid.bo_id[range_subid] != lower_bound; 384 search.ar_oid.bo_id[range_subid]--) { 385 region = RB_FIND(appl_regions, &(ctx->ac_regions), &search); 386 while (region->ar_priority != priority) 387 region = region->ar_next; 388 appl_region_free(ctx, region); 389 } 390 region = RB_FIND(appl_regions, &(ctx->ac_regions), &search); 391 while (region->ar_priority != priority) 392 region = region->ar_next; 393 appl_region_free(ctx, region); 394 return error; 395 } 396 397 /* Name from RFC 2741 section 6.2.4 */ 398 enum appl_error 399 appl_unregister(const char *ctxname, uint8_t priority, struct ber_oid *oid, 400 uint8_t range_subid, uint32_t upper_bound, struct appl_backend *backend) 401 { 402 struct appl_region *region, search; 403 struct appl_context *ctx; 404 char oidbuf[1024], subidbuf[11]; 405 size_t i; 406 407 oidbuf[0] = '\0'; 408 for (i = 0; i < oid->bo_n; i++) { 409 snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, oid->bo_id[i]); 410 if (i != 0) 411 strlcat(oidbuf, ".", sizeof(oidbuf)); 412 if (range_subid == i + 1) { 413 strlcat(oidbuf, "[", sizeof(oidbuf)); 414 strlcat(oidbuf, subidbuf, sizeof(oidbuf)); 415 strlcat(oidbuf, "-", sizeof(oidbuf)); 416 snprintf(subidbuf, sizeof(subidbuf), "%"PRIu32, 417 upper_bound); 418 strlcat(oidbuf, subidbuf, sizeof(oidbuf)); 419 strlcat(oidbuf, "]", sizeof(oidbuf)); 420 } else 421 strlcat(oidbuf, subidbuf, sizeof(oidbuf)); 422 } 423 424 if (ctxname == NULL) 425 ctxname = ""; 426 log_info("%s: Unregistering %s context(%s) priority(%"PRIu8")", 427 backend->ab_name, oidbuf,ctxname, priority); 428 429 if ((ctx = appl_context(ctxname, 0)) == NULL) { 430 if (errno == ENOMEM) { 431 log_warn("%s: Can't unregister %s: Processing error", 432 backend->ab_name, oidbuf); 433 return APPL_ERROR_PROCESSINGERROR; 434 } 435 log_info("%s: Can't unregister %s: Unsupported context \"%s\"", 436 backend->ab_name, oidbuf, ctxname); 437 return APPL_ERROR_UNSUPPORTEDCONTEXT; 438 } 439 440 if (priority == 0) { 441 log_warnx("%s: Can't unregister %s: priority can't be 0", 442 backend->ab_name, oidbuf); 443 return APPL_ERROR_PARSEERROR; 444 } 445 446 if (range_subid > oid->bo_n) { 447 log_warnx("%s: Can't unregiser %s: range_subid too large", 448 backend->ab_name, oidbuf); 449 return APPL_ERROR_PARSEERROR; 450 } 451 if (range_subid != 0 && oid->bo_id[range_subid] >= upper_bound) { 452 log_warnx("%s: Can't unregister %s: upper bound smaller or " 453 "equal to range_subid", backend->ab_name, oidbuf); 454 return APPL_ERROR_PARSEERROR; 455 } 456 457 search.ar_oid = *oid; 458 while (range_subid != 0 && 459 search.ar_oid.bo_id[range_subid] != upper_bound) { 460 region = RB_FIND(appl_regions, &(ctx->ac_regions), &search); 461 while (region != NULL && region->ar_priority < priority) 462 region = region->ar_next; 463 if (region == NULL || region->ar_priority != priority) { 464 log_warnx("%s: Can't unregister %s: region not found", 465 backend->ab_name, oidbuf); 466 return APPL_ERROR_UNKNOWNREGISTRATION; 467 } 468 if (region->ar_backend != backend) { 469 log_warnx("%s: Can't unregister %s: region not owned " 470 "by backend", backend->ab_name, oidbuf); 471 return APPL_ERROR_UNKNOWNREGISTRATION; 472 } 473 } 474 region = RB_FIND(appl_regions, &(ctx->ac_regions), &search); 475 while (region != NULL && region->ar_priority < priority) 476 region = region->ar_next; 477 if (region == NULL || region->ar_priority != priority) { 478 log_warnx("%s: Can't unregister %s: region not found", 479 backend->ab_name, oidbuf); 480 return APPL_ERROR_UNKNOWNREGISTRATION; 481 } 482 if (region->ar_backend != backend) { 483 log_warnx("%s: Can't unregister %s: region not owned " 484 "by backend", backend->ab_name, oidbuf); 485 return APPL_ERROR_UNKNOWNREGISTRATION; 486 } 487 488 search.ar_oid = *oid; 489 while (range_subid != 0 && 490 search.ar_oid.bo_id[range_subid] != upper_bound) { 491 region = RB_FIND(appl_regions, &(ctx->ac_regions), &search); 492 while (region != NULL && region->ar_priority != priority) 493 region = region->ar_next; 494 appl_region_free(ctx, region); 495 } 496 region = RB_FIND(appl_regions, &(ctx->ac_regions), &search); 497 while (region != NULL && region->ar_priority != priority) 498 region = region->ar_next; 499 appl_region_free(ctx, region); 500 501 return APPL_ERROR_NOERROR; 502 } 503 504 void 505 appl_region_free(struct appl_context *ctx, struct appl_region *region) 506 { 507 struct appl_region *pregion; 508 509 pregion = RB_FIND(appl_regions, &(ctx->ac_regions), region); 510 511 if (pregion == region) { 512 RB_REMOVE(appl_regions, &(ctx->ac_regions), region); 513 if (region->ar_next != NULL) 514 RB_INSERT(appl_regions, &(ctx->ac_regions), 515 region->ar_next); 516 } else { 517 while (pregion->ar_next != region) 518 pregion = pregion->ar_next; 519 pregion->ar_next = region->ar_next; 520 } 521 522 free(region); 523 } 524 525 /* backend is owned by the sub-application, just release application.c stuff */ 526 void 527 appl_close(struct appl_backend *backend) 528 { 529 struct appl_context *ctx; 530 struct appl_region *region, *tregion, *nregion; 531 struct appl_request_downstream *request, *trequest; 532 533 TAILQ_FOREACH(ctx, &contexts, ac_entries) { 534 RB_FOREACH_SAFE(region, appl_regions, 535 &(ctx->ac_regions), tregion) { 536 while (region != NULL) { 537 nregion = region->ar_next; 538 if (region->ar_backend == backend) 539 appl_region_free(ctx, region); 540 region = nregion; 541 } 542 } 543 } 544 545 RB_FOREACH_SAFE(request, appl_requests, 546 &(backend->ab_requests), trequest) 547 appl_request_downstream_free(request); 548 } 549 550 struct appl_region * 551 appl_region_find(struct appl_context *ctx, 552 const struct ber_oid *oid) 553 { 554 struct appl_region *region, search; 555 556 search.ar_oid = *oid; 557 while (search.ar_oid.bo_n > 0) { 558 region = RB_FIND(appl_regions, &(ctx->ac_regions), &search); 559 if (region != NULL) 560 return region; 561 search.ar_oid.bo_n--; 562 } 563 return NULL; 564 } 565 566 struct appl_region * 567 appl_region_next(struct appl_context *ctx, struct ber_oid *oid, 568 struct appl_region *cregion) 569 { 570 struct appl_region search, *nregion, *pregion; 571 int cmp; 572 573 search.ar_oid = *oid; 574 nregion = RB_NFIND(appl_regions, &(ctx->ac_regions), &search); 575 576 if (cregion == nregion) 577 nregion = RB_NEXT(appl_regions, &(ctx->ac_regions), nregion); 578 /* Past last element in tree, we might still have a parent */ 579 if (nregion == NULL) { 580 search.ar_oid = cregion->ar_oid; 581 search.ar_oid.bo_n--; 582 return appl_region_find(ctx, &(search.ar_oid)); 583 } 584 cmp = appl_region_cmp(cregion, nregion); 585 if (cmp >= 0) 586 fatalx("%s: wrong OID order", __func__); 587 /* Direct descendant */ 588 if (cmp == -2) 589 return nregion; 590 591 /* cmp == -1 */ 592 search.ar_oid = cregion->ar_oid; 593 /* Find direct next sibling */ 594 ober_oid_nextsibling(&(search.ar_oid)); 595 if (ober_oid_cmp(&(nregion->ar_oid), &(search.ar_oid)) == 0) 596 return nregion; 597 /* Sibling gaps go to parent, or end end at border */ 598 search.ar_oid = cregion->ar_oid; 599 search.ar_oid.bo_n--; 600 pregion = appl_region_find(ctx, &(search.ar_oid)); 601 602 return pregion != NULL ? pregion : nregion; 603 } 604 605 /* Name from RFC 3413 section 3.2 */ 606 void 607 appl_processpdu(struct snmp_message *statereference, const char *ctxname, 608 enum snmp_version pduversion, struct ber_element *pdu) 609 { 610 struct appl_context *ctx; 611 struct appl_request_upstream *ureq; 612 struct ber_oid oid; 613 struct ber_element *value, *varbind, *varbindlist; 614 long long nonrepeaters, maxrepetitions; 615 static uint32_t transactionid; 616 int32_t requestid; 617 size_t i, varbindlen = 0, repeaterlen; 618 619 /* pdu must be ASN.1 validated in snmpe.c */ 620 (void) ober_scanf_elements(pdu, "{diie", &requestid, &nonrepeaters, 621 &maxrepetitions, &varbindlist); 622 623 /* RFC 3413, section 3.2, processPDU, item 5, final bullet */ 624 if ((ctx = appl_context(ctxname, 0)) == NULL) { 625 oid = BER_OID(MIB_snmpUnknownContexts, 0); 626 snmp_target_mib.snmp_unknowncontexts++; 627 if ((value = ober_add_integer(NULL, 628 snmp_target_mib.snmp_unknowncontexts)) == NULL) 629 fatal("ober_add_integer"); 630 appl_report(statereference, requestid, &oid, value); 631 return; 632 } 633 634 if ((ureq = malloc(sizeof(*ureq))) == NULL) 635 fatal("malloc"); 636 637 ureq->aru_ctx = ctx; 638 ureq->aru_statereference = statereference; 639 ureq->aru_transactionid = transactionid++; 640 ureq->aru_requestid = requestid; 641 ureq->aru_error = APPL_ERROR_NOERROR; 642 ureq->aru_index = 0; 643 ureq->aru_nonrepeaters = nonrepeaters; 644 ureq->aru_maxrepetitions = maxrepetitions; 645 ureq->aru_varbindlen = 0; 646 ureq->aru_locked = 0; 647 ureq->aru_pduversion = pduversion; 648 ureq->aru_pdu = pdu; 649 650 varbind = varbindlist->be_sub; 651 for (; varbind != NULL; varbind = varbind->be_next) 652 varbindlen++; 653 654 repeaterlen = varbindlen - nonrepeaters; 655 if (pdu->be_type == SNMP_C_GETBULKREQ) 656 ureq->aru_varbindlen = nonrepeaters + 657 (repeaterlen * maxrepetitions); 658 else 659 ureq->aru_varbindlen = varbindlen; 660 if ((ureq->aru_vblist = calloc(ureq->aru_varbindlen, 661 sizeof(*ureq->aru_vblist))) == NULL) 662 fatal("malloc"); 663 664 varbind = varbindlist->be_sub; 665 /* Use aru_varbindlen in case maxrepetitions == 0 */ 666 for (i = 0; i < ureq->aru_varbindlen; i++) { 667 ureq->aru_vblist[i].avi_request_upstream = ureq; 668 ureq->aru_vblist[i].avi_index = i + 1; 669 ureq->aru_vblist[i].avi_state = APPL_VBSTATE_NEW; 670 /* This can only happen with bulkreq */ 671 if (varbind == NULL) { 672 ureq->aru_vblist[i - repeaterlen].avi_sub = 673 &(ureq->aru_vblist[i]); 674 ureq->aru_vblist[i].avi_state = APPL_VBSTATE_MUSTFILL; 675 continue; 676 } 677 ober_get_oid(varbind->be_sub, 678 &(ureq->aru_vblist[i].avi_varbind.av_oid)); 679 if (i + 1 < ureq->aru_varbindlen) { 680 ureq->aru_vblist[i].avi_next = 681 &(ureq->aru_vblist[i + 1]); 682 ureq->aru_vblist[i].avi_varbind.av_next = 683 &(ureq->aru_vblist[i + 1].avi_varbind); 684 } else { 685 ureq->aru_vblist[i].avi_next = NULL; 686 ureq->aru_vblist[i].avi_varbind.av_next = NULL; 687 } 688 varbind = varbind->be_next; 689 } 690 691 appl_pdu_log(NULL, pdu->be_type, requestid, nonrepeaters, 692 maxrepetitions, &(ureq->aru_vblist[0].avi_varbind)); 693 694 appl_request_upstream_resolve(ureq); 695 } 696 697 void 698 appl_request_upstream_free(struct appl_request_upstream *ureq) 699 { 700 size_t i; 701 struct appl_varbind_internal *vb; 702 703 if (ureq == NULL) 704 return; 705 706 for (i = 0; i < ureq->aru_varbindlen && ureq->aru_vblist != NULL; i++) { 707 vb = &(ureq->aru_vblist[i]); 708 ober_free_elements(vb->avi_varbind.av_value); 709 appl_request_downstream_free(vb->avi_request_downstream); 710 } 711 free(ureq->aru_vblist); 712 713 assert(ureq->aru_statereference == NULL); 714 715 free(ureq); 716 } 717 718 void 719 appl_request_downstream_free(struct appl_request_downstream *dreq) 720 { 721 struct appl_varbind_internal *vb; 722 int retry = 0; 723 724 if (dreq == NULL) 725 return; 726 727 RB_REMOVE(appl_requests, &(dreq->ard_backend->ab_requests), dreq); 728 evtimer_del(&(dreq->ard_timer)); 729 730 for (vb = dreq->ard_vblist; vb != NULL; vb = vb->avi_next) { 731 vb->avi_request_downstream = NULL; 732 if (vb->avi_state == APPL_VBSTATE_PENDING) { 733 vb->avi_state = APPL_VBSTATE_NEW; 734 retry = 1; 735 } 736 } 737 738 if (retry) 739 appl_request_upstream_resolve(dreq->ard_request); 740 free(dreq); 741 } 742 743 void 744 appl_request_upstream_resolve(struct appl_request_upstream *ureq) 745 { 746 struct appl_varbind_internal *vb, *lvb, *tvb; 747 struct appl_request_downstream *dreq; 748 struct appl_region *region, *lregion; 749 struct timeval tv; 750 int done; 751 size_t i; 752 int32_t maxrepetitions; 753 int32_t timeout; 754 755 if (ureq->aru_locked) 756 return; 757 ureq->aru_locked = 1; 758 759 if (ureq->aru_pdu->be_type == SNMP_C_SETREQ) { 760 ureq->aru_error = APPL_ERROR_NOTWRITABLE; 761 ureq->aru_index = 1; 762 appl_request_upstream_reply(ureq); 763 return; 764 } 765 766 next: 767 dreq = NULL; 768 lvb = NULL; 769 done = 1; 770 timeout = 0; 771 772 if (ureq->aru_error != APPL_ERROR_NOERROR) { 773 appl_request_upstream_reply(ureq); 774 return; 775 } 776 for (i = 0; i < ureq->aru_varbindlen; i++) { 777 vb = &(ureq->aru_vblist[i]); 778 779 switch (vb->avi_state) { 780 case APPL_VBSTATE_MUSTFILL: 781 case APPL_VBSTATE_PENDING: 782 done = 0; 783 continue; 784 case APPL_VBSTATE_DONE: 785 continue; 786 case APPL_VBSTATE_NEW: 787 break; 788 } 789 if (appl_varbind_backend(vb) == -1) 790 fatal("appl_varbind_backend"); 791 if (vb->avi_state != APPL_VBSTATE_DONE) 792 done = 0; 793 } 794 795 for (i = 0; i < ureq->aru_varbindlen; i++) { 796 vb = &(ureq->aru_vblist[i]); 797 798 if (vb->avi_state != APPL_VBSTATE_NEW) 799 continue; 800 801 vb = &(ureq->aru_vblist[i]); 802 region = vb->avi_region; 803 lregion = lvb != NULL ? lvb->avi_region : NULL; 804 if (lvb != NULL && region->ar_backend != lregion->ar_backend) 805 continue; 806 807 vb->avi_varbind.av_next = NULL; 808 vb->avi_next = NULL; 809 tvb = vb; 810 for (maxrepetitions = 0; tvb != NULL; tvb = tvb->avi_sub) 811 maxrepetitions++; 812 if (dreq == NULL) { 813 if ((dreq = malloc(sizeof(*dreq))) == NULL) 814 fatal("malloc"); 815 816 dreq->ard_request = ureq; 817 dreq->ard_vblist = vb; 818 dreq->ard_backend = vb->avi_region->ar_backend; 819 dreq->ard_retries = dreq->ard_backend->ab_retries; 820 dreq->ard_requesttype = ureq->aru_pdu->be_type; 821 /* 822 * We don't yet fully handle bulkrequest responses. 823 * It's completely valid to map onto getrequest. 824 * maxrepetitions calculated in preparation of support. 825 */ 826 if (dreq->ard_requesttype == SNMP_C_GETBULKREQ && 827 dreq->ard_backend->ab_fn->ab_getbulk == NULL) 828 dreq->ard_requesttype = SNMP_C_GETNEXTREQ; 829 /* 830 * If first varbind is nonrepeater, set maxrepetitions 831 * to 0, so that the next varbind with 832 * maxrepetitions > 1 determines length. 833 */ 834 if (maxrepetitions == 1) { 835 dreq->ard_maxrepetitions = 0; 836 dreq->ard_nonrepeaters = 1; 837 } else { 838 dreq->ard_maxrepetitions = maxrepetitions; 839 dreq->ard_nonrepeaters = 0; 840 } 841 do { 842 dreq->ard_requestid = arc4random(); 843 } while (RB_INSERT(appl_requests, 844 &(dreq->ard_backend->ab_requests), dreq) != NULL); 845 lvb = vb; 846 /* avi_sub isn't set on !bulkrequest, so we always enter here */ 847 } else if (maxrepetitions == 1) { 848 dreq->ard_nonrepeaters++; 849 vb->avi_varbind.av_next = 850 &(dreq->ard_vblist->avi_varbind); 851 vb->avi_next = dreq->ard_vblist; 852 dreq->ard_vblist = vb; 853 } else { 854 lvb->avi_varbind.av_next = &(vb->avi_varbind); 855 lvb->avi_next = vb; 856 /* RFC 2741 section 7.2.1.3: 857 * The value of g.max_repetitions in the GetBulk-PDU may 858 * be less than (but not greater than) the value in the 859 * original request PDU. 860 */ 861 if (dreq->ard_maxrepetitions > maxrepetitions || 862 dreq->ard_maxrepetitions == 0) 863 dreq->ard_maxrepetitions = maxrepetitions; 864 lvb = vb; 865 } 866 vb->avi_request_downstream = dreq; 867 vb->avi_state = APPL_VBSTATE_PENDING; 868 if (region->ar_timeout > timeout) 869 timeout = region->ar_timeout; 870 } 871 872 if (dreq == NULL) { 873 ureq->aru_locked = 0; 874 if (done) 875 appl_request_upstream_reply(ureq); 876 return; 877 } 878 879 tv.tv_sec = timeout / 100; 880 tv.tv_usec = (timeout % 100) * 10000; 881 evtimer_set(&(dreq->ard_timer), appl_request_downstream_timeout, dreq); 882 evtimer_add(&(dreq->ard_timer), &tv); 883 884 appl_request_downstream_send(dreq); 885 goto next; 886 } 887 888 void 889 appl_request_downstream_send(struct appl_request_downstream *dreq) 890 { 891 892 appl_pdu_log(dreq->ard_backend, dreq->ard_requesttype, 893 dreq->ard_requestid, 0, 0, &(dreq->ard_vblist->avi_varbind)); 894 895 if (dreq->ard_requesttype == SNMP_C_GETREQ) { 896 dreq->ard_backend->ab_fn->ab_get(dreq->ard_backend, 897 dreq->ard_request->aru_transactionid, 898 dreq->ard_requestid, 899 APPL_CONTEXT_NAME(dreq->ard_request->aru_ctx), 900 &(dreq->ard_vblist->avi_varbind)); 901 } else if (dreq->ard_requesttype == SNMP_C_GETNEXTREQ) { 902 dreq->ard_backend->ab_fn->ab_getnext(dreq->ard_backend, 903 dreq->ard_request->aru_transactionid, 904 dreq->ard_requestid, 905 APPL_CONTEXT_NAME(dreq->ard_request->aru_ctx), 906 &(dreq->ard_vblist->avi_varbind)); 907 } 908 } 909 910 void 911 appl_request_downstream_timeout(__unused int fd, __unused short event, 912 void *cookie) 913 { 914 struct appl_request_downstream *dreq = cookie; 915 916 log_info("%s: %"PRIu32" timed out%s", 917 dreq->ard_backend->ab_name, dreq->ard_requestid, 918 dreq->ard_retries > 0 ? ": retrying" : ""); 919 if (dreq->ard_retries > 0) { 920 dreq->ard_retries--; 921 appl_request_downstream_send(dreq); 922 } else 923 appl_response(dreq->ard_backend, dreq->ard_requestid, 924 APPL_ERROR_GENERR, 1, &(dreq->ard_vblist->avi_varbind)); 925 } 926 927 void 928 appl_request_upstream_reply(struct appl_request_upstream *ureq) 929 { 930 struct ber_element *varbindlist = NULL, *varbind = NULL, *value; 931 struct appl_varbind_internal *vb; 932 size_t i, repvarbinds, varbindlen; 933 ssize_t match = -1; 934 935 varbindlen = ureq->aru_varbindlen; 936 937 if (ureq->aru_pduversion == SNMP_V1) { 938 /* RFC 3584 section 4.2.2.2 Map exceptions */ 939 for (i = 0; i < varbindlen; i++) { 940 vb = &(ureq->aru_vblist[i]); 941 value = vb->avi_varbind.av_value; 942 if (value != NULL && 943 value->be_class == BER_CLASS_CONTEXT) 944 appl_varbind_error(vb, APPL_ERROR_NOSUCHNAME); 945 } 946 /* RFC 3584 section 4.4 Map errors */ 947 switch (ureq->aru_error) { 948 case APPL_ERROR_WRONGVALUE: 949 case APPL_ERROR_WRONGENCODING: 950 case APPL_ERROR_WRONGTYPE: 951 case APPL_ERROR_WRONGLENGTH: 952 case APPL_ERROR_INCONSISTENTVALUE: 953 ureq->aru_error = APPL_ERROR_BADVALUE; 954 break; 955 case APPL_ERROR_NOACCESS: 956 case APPL_ERROR_NOTWRITABLE: 957 case APPL_ERROR_NOCREATION: 958 case APPL_ERROR_INCONSISTENTNAME: 959 case APPL_ERROR_AUTHORIZATIONERROR: 960 ureq->aru_error = APPL_ERROR_NOSUCHNAME; 961 break; 962 case APPL_ERROR_RESOURCEUNAVAILABLE: 963 case APPL_ERROR_COMMITFAILED: 964 case APPL_ERROR_UNDOFAILED: 965 ureq->aru_error = APPL_ERROR_GENERR; 966 break; 967 default: 968 break; 969 } 970 } 971 /* RFC 3416 section 4.2.{1,2,3} reset original varbinds */ 972 if (ureq->aru_error != APPL_ERROR_NOERROR) { 973 ober_scanf_elements(ureq->aru_pdu, "{SSS{e", &varbind); 974 for (varbindlen = 0; varbind != NULL; 975 varbindlen++, varbind = varbind->be_next) { 976 vb = &(ureq->aru_vblist[varbindlen]); 977 ober_get_oid(varbind->be_sub, 978 &(vb->avi_varbind.av_oid)); 979 ober_free_elements(vb->avi_varbind.av_value); 980 vb->avi_varbind.av_value = ober_add_null(NULL);; 981 } 982 /* RFC 3416 section 4.2.3: Strip excessive EOMV */ 983 } else if (ureq->aru_pdu->be_type == SNMP_C_GETBULKREQ) { 984 repvarbinds = (ureq->aru_varbindlen - ureq->aru_nonrepeaters) / 985 ureq->aru_maxrepetitions; 986 for (i = ureq->aru_nonrepeaters; 987 i < ureq->aru_varbindlen - repvarbinds; i++) { 988 value = ureq->aru_vblist[i].avi_varbind.av_value; 989 if ((i - ureq->aru_nonrepeaters) % repvarbinds == 0 && 990 value->be_class == BER_CLASS_CONTEXT && 991 value->be_type == APPL_EXC_ENDOFMIBVIEW) { 992 if (match != -1) 993 break; 994 match = i; 995 } 996 if (value->be_class != BER_CLASS_CONTEXT || 997 value->be_type != APPL_EXC_ENDOFMIBVIEW) 998 match = -1; 999 } 1000 if (match != -1) 1001 varbindlen = match + repvarbinds; 1002 } 1003 1004 for (i = 0; i < varbindlen; i++) { 1005 vb = &(ureq->aru_vblist[i]); 1006 vb->avi_varbind.av_next = 1007 &(ureq->aru_vblist[i + 1].avi_varbind); 1008 } 1009 1010 ureq->aru_vblist[i - 1].avi_varbind.av_next = NULL; 1011 appl_pdu_log(NULL, SNMP_C_RESPONSE, ureq->aru_requestid, 1012 ureq->aru_error, ureq->aru_index, 1013 &(ureq->aru_vblist[0].avi_varbind)); 1014 1015 for (i = 0; i < varbindlen; i++) { 1016 varbind = ober_printf_elements(varbind, "{Oe}", 1017 &(ureq->aru_vblist[i].avi_varbind.av_oid), 1018 ureq->aru_vblist[i].avi_varbind.av_value); 1019 ureq->aru_vblist[i].avi_varbind.av_value = NULL; 1020 if (varbind == NULL) 1021 fatal("ober_printf_elements"); 1022 if (varbindlist == NULL) 1023 varbindlist = varbind; 1024 } 1025 1026 snmpe_send(ureq->aru_statereference, SNMP_C_RESPONSE, 1027 ureq->aru_requestid, ureq->aru_error, ureq->aru_index, varbindlist); 1028 ureq->aru_statereference = NULL; 1029 appl_request_upstream_free(ureq); 1030 } 1031 1032 /* Name from RFC 2741 section 6.2.16 */ 1033 void 1034 appl_response(struct appl_backend *backend, int32_t requestid, 1035 enum appl_error error, int16_t index, struct appl_varbind *vblist) 1036 { 1037 struct appl_request_downstream *dreq, search; 1038 struct appl_request_upstream *ureq = NULL; 1039 const char *errstr; 1040 char oidbuf[1024]; 1041 enum snmp_pdutype pdutype; 1042 struct appl_varbind *vb; 1043 struct appl_varbind_internal *origvb = NULL; 1044 int invalid = 0; 1045 int next = 0, eomv; 1046 int32_t i; 1047 1048 appl_pdu_log(backend, SNMP_C_RESPONSE, requestid, error, index, vblist); 1049 1050 search.ard_requestid = requestid; 1051 dreq = RB_FIND(appl_requests, &(backend->ab_requests), &search); 1052 if (dreq == NULL) { 1053 log_debug("%s: %"PRIu32" not outstanding", 1054 backend->ab_name, requestid); 1055 /* Continue to verify validity */ 1056 } else { 1057 ureq = dreq->ard_request; 1058 pdutype = ureq->aru_pdu->be_type; 1059 next = pdutype == SNMP_C_GETNEXTREQ || 1060 pdutype == SNMP_C_GETBULKREQ; 1061 origvb = dreq->ard_vblist; 1062 } 1063 1064 vb = vblist; 1065 for (i = 1; vb != NULL; vb = vb->av_next, i++) { 1066 if (!appl_varbind_valid(vb, origvb == NULL ? 1067 NULL : &(origvb->avi_varbind), next, 1068 error != APPL_ERROR_NOERROR, backend->ab_range, &errstr)) { 1069 smi_oid2string(&(vb->av_oid), oidbuf, 1070 sizeof(oidbuf), 0); 1071 log_warnx("%s: %"PRIu32" %s: %s", 1072 backend->ab_name, requestid, oidbuf, errstr); 1073 invalid = 1; 1074 } 1075 /* Transfer av_value */ 1076 if (origvb != NULL) { 1077 if (error != APPL_ERROR_NOERROR && i == index) 1078 appl_varbind_error(origvb, error); 1079 origvb->avi_state = APPL_VBSTATE_DONE; 1080 origvb->avi_varbind.av_oid = vb->av_oid; 1081 1082 eomv = vb->av_value != NULL && 1083 vb->av_value->be_class == BER_CLASS_CONTEXT && 1084 vb->av_value->be_type == APPL_EXC_ENDOFMIBVIEW; 1085 /* 1086 * Treat results past av_oid_end for backends that 1087 * don't support searchranges as EOMV 1088 */ 1089 eomv |= !backend->ab_range && next && 1090 ober_oid_cmp(&(vb->av_oid), 1091 &(origvb->avi_varbind.av_oid_end)) > 0; 1092 /* RFC 3584 section 4.2.2.1 */ 1093 if (ureq->aru_pduversion == SNMP_V1 && 1094 vb->av_value != NULL && 1095 vb->av_value->be_class == BER_CLASS_APPLICATION && 1096 vb->av_value->be_type == SNMP_COUNTER64) { 1097 if (next) 1098 eomv = 1; 1099 else 1100 appl_varbind_error(origvb, 1101 APPL_ERROR_NOSUCHNAME); 1102 } 1103 1104 if (eomv) { 1105 ober_free_elements(vb->av_value); 1106 origvb->avi_varbind.av_oid = 1107 origvb->avi_varbind.av_oid_end; 1108 origvb->avi_varbind.av_include = 1; 1109 vb->av_value = NULL; 1110 origvb->avi_state = APPL_VBSTATE_NEW; 1111 } 1112 origvb->avi_varbind.av_value = vb->av_value; 1113 if (origvb->avi_varbind.av_next == NULL && 1114 vb->av_next != NULL) { 1115 log_warnx("%s: Request %"PRIu32" returned more " 1116 "varbinds then requested", 1117 backend->ab_name, requestid); 1118 invalid = 1; 1119 } 1120 if (origvb->avi_sub != NULL && 1121 origvb->avi_state == APPL_VBSTATE_DONE) { 1122 origvb->avi_sub->avi_varbind.av_oid = 1123 origvb->avi_varbind.av_oid; 1124 origvb->avi_sub->avi_state = APPL_VBSTATE_NEW; 1125 } 1126 origvb = origvb->avi_next; 1127 } else { 1128 ober_free_elements(vb->av_value); 1129 vb->av_value = NULL; 1130 } 1131 } 1132 if (error != APPL_ERROR_NOERROR && (index <= 0 || index >= i)) { 1133 log_warnx("Invalid error index"); 1134 invalid = 1; 1135 } 1136 /* amavisd-snmp-subagent sets index to 1, no reason to crash over it. */ 1137 #if PEDANTIC 1138 if (error == APPL_ERROR_NOERROR && index != 0) { 1139 log_warnx("error index with no error"); 1140 invalid = 1; 1141 } 1142 #endif 1143 if (vb == NULL && origvb != NULL) { 1144 log_warnx("%s: Request %"PRIu32" returned less varbinds then " 1145 "requested", backend->ab_name, requestid); 1146 invalid = 1; 1147 } 1148 1149 if (dreq != NULL) { 1150 if (invalid) 1151 appl_varbind_error(dreq->ard_vblist, APPL_ERROR_GENERR); 1152 appl_request_downstream_free(dreq); 1153 } 1154 1155 if (invalid && backend->ab_fn->ab_close != NULL) { 1156 log_warnx("%s: Closing: Too many parse errors", 1157 backend->ab_name); 1158 backend->ab_fn->ab_close(backend, APPL_CLOSE_REASONPARSEERROR); 1159 } 1160 1161 if (ureq != NULL) 1162 appl_request_upstream_resolve(ureq); 1163 } 1164 1165 int 1166 appl_varbind_valid(struct appl_varbind *varbind, struct appl_varbind *request, 1167 int next, int null, int range, const char **errstr) 1168 { 1169 int cmp; 1170 int eomv = 0; 1171 1172 if (varbind->av_value == NULL) { 1173 if (!null) { 1174 *errstr = "missing value"; 1175 return 0; 1176 } 1177 return 1; 1178 } 1179 if (varbind->av_value->be_class == BER_CLASS_UNIVERSAL) { 1180 switch (varbind->av_value->be_type) { 1181 case BER_TYPE_NULL: 1182 if (null) 1183 break; 1184 *errstr = "not expecting null value"; 1185 return 0; 1186 case BER_TYPE_INTEGER: 1187 case BER_TYPE_OCTETSTRING: 1188 case BER_TYPE_OBJECT: 1189 if (!null) 1190 break; 1191 /* FALLTHROUGH */ 1192 default: 1193 *errstr = "invalid value"; 1194 return 0; 1195 } 1196 } else if (varbind->av_value->be_class == BER_CLASS_APPLICATION) { 1197 switch (varbind->av_value->be_type) { 1198 case SNMP_T_IPADDR: 1199 case SNMP_T_COUNTER32: 1200 case SNMP_T_GAUGE32: 1201 case SNMP_T_TIMETICKS: 1202 case SNMP_T_OPAQUE: 1203 case SNMP_T_COUNTER64: 1204 if (!null) 1205 break; 1206 /* FALLTHROUGH */ 1207 default: 1208 *errstr = "expecting null value"; 1209 return 0; 1210 } 1211 } else if (varbind->av_value->be_class == BER_CLASS_CONTEXT) { 1212 switch (varbind->av_value->be_type) { 1213 case APPL_EXC_NOSUCHOBJECT: 1214 if (next && request != NULL) { 1215 *errstr = "Unexpected noSuchObject"; 1216 return 0; 1217 } 1218 /* FALLTHROUGH */ 1219 case APPL_EXC_NOSUCHINSTANCE: 1220 if (null) { 1221 *errstr = "expecting null value"; 1222 return 0; 1223 } 1224 if (next && request != NULL) { 1225 *errstr = "Unexpected noSuchInstance"; 1226 return 0; 1227 } 1228 break; 1229 case APPL_EXC_ENDOFMIBVIEW: 1230 if (null) { 1231 *errstr = "expecting null value"; 1232 return 0; 1233 } 1234 if (!next && request != NULL) { 1235 *errstr = "Unexpected endOfMibView"; 1236 return 0; 1237 } 1238 eomv = 1; 1239 break; 1240 default: 1241 *errstr = "invalid exception"; 1242 return 0; 1243 } 1244 } else { 1245 *errstr = "invalid value"; 1246 return 0; 1247 } 1248 1249 if (request == NULL) 1250 return 1; 1251 1252 cmp = ober_oid_cmp(&(request->av_oid), &(varbind->av_oid)); 1253 if (next && !eomv) { 1254 if (request->av_include) { 1255 if (cmp > 0) { 1256 *errstr = "oid not incrementing"; 1257 return 0; 1258 } 1259 } else { 1260 if (cmp >= 0) { 1261 *errstr = "oid not incrementing"; 1262 return 0; 1263 } 1264 } 1265 if (range && ober_oid_cmp(&(varbind->av_oid), 1266 &(request->av_oid_end)) > 0) { 1267 *errstr = "end oid not honoured"; 1268 return 0; 1269 } 1270 } else { 1271 if (cmp != 0) { 1272 *errstr = "oids not equal"; 1273 return 0; 1274 } 1275 } 1276 return 1; 1277 } 1278 1279 int 1280 appl_varbind_backend(struct appl_varbind_internal *ivb) 1281 { 1282 struct appl_request_upstream *ureq = ivb->avi_request_upstream; 1283 struct appl_region search, *region, *pregion; 1284 struct appl_varbind *vb = &(ivb->avi_varbind); 1285 struct ber_oid oid, nextsibling; 1286 int next, cmp; 1287 1288 next = ureq->aru_pdu->be_type == SNMP_C_GETNEXTREQ || 1289 ureq->aru_pdu->be_type == SNMP_C_GETBULKREQ; 1290 1291 region = appl_region_find(ureq->aru_ctx, &(vb->av_oid)); 1292 if (region == NULL) { 1293 if (!next) { 1294 vb->av_value = appl_exception(APPL_EXC_NOSUCHOBJECT); 1295 ivb->avi_state = APPL_VBSTATE_DONE; 1296 if (vb->av_value == NULL) 1297 return -1; 1298 return 0; 1299 } 1300 search.ar_oid = vb->av_oid; 1301 region = RB_NFIND(appl_regions, 1302 &(ureq->aru_ctx->ac_regions), &search); 1303 if (region == NULL) 1304 goto eomv; 1305 vb->av_oid = region->ar_oid; 1306 vb->av_include = 1; 1307 } 1308 cmp = ober_oid_cmp(&(region->ar_oid), &(vb->av_oid)); 1309 if (cmp == -2) { 1310 if (region->ar_instance) { 1311 if (!next) { 1312 vb->av_value = 1313 appl_exception(APPL_EXC_NOSUCHINSTANCE); 1314 ivb->avi_state = APPL_VBSTATE_DONE; 1315 if (vb->av_value == NULL) 1316 return -1; 1317 return 0; 1318 } 1319 if ((region = appl_region_next(ureq->aru_ctx, 1320 &(vb->av_oid), region)) == NULL) 1321 goto eomv; 1322 vb->av_oid = region->ar_oid; 1323 vb->av_include = 1; 1324 } 1325 } else if (cmp == 0) { 1326 if (region->ar_instance && next && !vb->av_include) { 1327 if ((region = appl_region_next(ureq->aru_ctx, 1328 &(vb->av_oid), region)) == NULL) 1329 goto eomv; 1330 vb->av_oid = region->ar_oid; 1331 vb->av_include = 1; 1332 } 1333 } 1334 ivb->avi_region = region; 1335 if (next) { 1336 oid = vb->av_oid; 1337 /* 1338 * For the searchrange end we only want contiguous regions. 1339 * This means directly connecting, or overlapping with the same 1340 * backend. 1341 */ 1342 do { 1343 pregion = region; 1344 region = appl_region_next(ureq->aru_ctx, &oid, pregion); 1345 if (region == NULL) { 1346 oid = pregion->ar_oid; 1347 ober_oid_nextsibling(&oid); 1348 break; 1349 } 1350 cmp = ober_oid_cmp(&(region->ar_oid), &oid); 1351 if (cmp == 2) 1352 oid = region->ar_oid; 1353 else if (cmp == 1) { 1354 /* Break out if we find a gap */ 1355 nextsibling = pregion->ar_oid; 1356 ober_oid_nextsibling(&nextsibling); 1357 if (ober_oid_cmp(&(region->ar_oid), 1358 &nextsibling) != 0) { 1359 oid = pregion->ar_oid; 1360 ober_oid_nextsibling(&oid); 1361 break; 1362 } 1363 oid = region->ar_oid; 1364 } else if (cmp == -2) { 1365 oid = pregion->ar_oid; 1366 ober_oid_nextsibling(&oid); 1367 } else 1368 fatalx("We can't stop/move back on getnext"); 1369 } while (region->ar_backend == pregion->ar_backend); 1370 vb->av_oid_end = oid; 1371 } 1372 return 0; 1373 1374 eomv: 1375 do { 1376 ivb->avi_varbind.av_value = 1377 appl_exception(APPL_EXC_ENDOFMIBVIEW); 1378 ivb->avi_state = APPL_VBSTATE_DONE; 1379 if (ivb->avi_varbind.av_value == NULL) 1380 return -1; 1381 if (ivb->avi_sub != NULL) 1382 ivb->avi_sub->avi_varbind.av_oid = 1383 ivb->avi_varbind.av_oid; 1384 ivb = ivb->avi_sub; 1385 } while (ivb != NULL); 1386 1387 return 0; 1388 } 1389 1390 void 1391 appl_varbind_error(struct appl_varbind_internal *avi, enum appl_error error) 1392 { 1393 struct appl_request_upstream *ureq = avi->avi_request_upstream; 1394 1395 if (ureq->aru_error == APPL_ERROR_GENERR) 1396 return; 1397 if (ureq->aru_error != APPL_ERROR_NOERROR && error != APPL_ERROR_GENERR) 1398 return; 1399 ureq->aru_error = error; 1400 ureq->aru_index = avi->avi_index; 1401 } 1402 1403 void 1404 appl_report(struct snmp_message *msg, int32_t requestid, struct ber_oid *oid, 1405 struct ber_element *value) 1406 { 1407 struct ber_element *varbind; 1408 1409 varbind = ober_printf_elements(NULL, "{Oe}", oid, value); 1410 if (varbind == NULL) { 1411 log_warn("%"PRId32": ober_printf_elements", requestid); 1412 ober_free_elements(value); 1413 snmp_msgfree(msg); 1414 return; 1415 } 1416 1417 snmpe_send(msg, SNMP_C_REPORT, requestid, 0, 0, varbind); 1418 } 1419 1420 struct ber_element * 1421 appl_exception(enum appl_exception type) 1422 { 1423 struct ber_element *value; 1424 1425 if ((value = ober_add_null(NULL)) == NULL) { 1426 log_warn("malloc"); 1427 return NULL; 1428 } 1429 ober_set_header(value, BER_CLASS_CONTEXT, type); 1430 1431 return value; 1432 } 1433 1434 void 1435 appl_pdu_log(struct appl_backend *backend, enum snmp_pdutype pdutype, 1436 int32_t requestid, uint16_t error, uint16_t index, 1437 struct appl_varbind *vblist) 1438 { 1439 struct appl_varbind *vb; 1440 char buf[1024], oidbuf[1024], *str; 1441 int next; 1442 1443 if (log_getverbose() < 2) 1444 return; 1445 1446 next = (pdutype == SNMP_C_GETNEXTREQ || pdutype == SNMP_C_GETBULKREQ); 1447 1448 buf[0] = '\0'; 1449 for (vb = vblist; vb != NULL; vb = vb->av_next) { 1450 strlcat(buf, "{", sizeof(buf)); 1451 strlcat(buf, smi_oid2string(&(vb->av_oid), oidbuf, 1452 sizeof(oidbuf), 0), sizeof(buf)); 1453 if (next) { 1454 if (vb->av_include) 1455 strlcat(buf, "(incl)", sizeof(buf)); 1456 if (vb->av_oid_end.bo_n > 0) { 1457 strlcat(buf, "-", sizeof(buf)); 1458 strlcat(buf, smi_oid2string(&(vb->av_oid_end), 1459 oidbuf, sizeof(oidbuf), 0), sizeof(buf)); 1460 } 1461 } 1462 strlcat(buf, ":", sizeof(buf)); 1463 if (vb->av_value != NULL) { 1464 str = smi_print_element(vb->av_value); 1465 strlcat(buf, str == NULL ? "???" : str, sizeof(buf)); 1466 free(str); 1467 } else 1468 strlcat(buf, "null", sizeof(buf)); 1469 strlcat(buf, "}", sizeof(buf)); 1470 } 1471 log_debug("%s%s%s{%"PRId32", %"PRIu16", %"PRIu16", {%s}}", 1472 backend != NULL ? backend->ab_name : "", 1473 backend != NULL ? ": " : "", 1474 snmpe_pdutype2string(pdutype), requestid, error, index, buf); 1475 } 1476 1477 void 1478 ober_oid_nextsibling(struct ber_oid *oid) 1479 { 1480 while (oid->bo_n > 0) { 1481 oid->bo_id[oid->bo_n - 1]++; 1482 /* Overflow check */ 1483 if (oid->bo_id[oid->bo_n - 1] != 0) 1484 return; 1485 oid->bo_n--; 1486 } 1487 } 1488 1489 int 1490 appl_region_cmp(struct appl_region *r1, struct appl_region *r2) 1491 { 1492 return ober_oid_cmp(&(r1->ar_oid), &(r2->ar_oid)); 1493 } 1494 1495 int 1496 appl_request_cmp(struct appl_request_downstream *r1, 1497 struct appl_request_downstream *r2) 1498 { 1499 return r1->ard_requestid < r2->ard_requestid ? -1 : 1500 r1->ard_requestid > r2->ard_requestid; 1501 } 1502 1503 RB_GENERATE_STATIC(appl_regions, appl_region, ar_entry, appl_region_cmp); 1504 RB_GENERATE_STATIC(appl_requests, appl_request_downstream, ard_entry, 1505 appl_request_cmp); 1506