1 /* 2 * Copyright (c) 2001-2003 3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4 * All rights reserved. 5 * 6 * Author: Harti Brandt <harti@freebsd.org> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $Begemot: bsnmp/lib/snmpagent.c,v 1.20 2005/10/04 11:21:33 brandt_h Exp $ 30 * 31 * SNMP Agent functions 32 */ 33 #include <sys/types.h> 34 #include <sys/queue.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <stddef.h> 38 #include <stdarg.h> 39 #ifdef HAVE_STDINT_H 40 #include <stdint.h> 41 #elif defined(HAVE_INTTYPES_H) 42 #include <inttypes.h> 43 #endif 44 #include <string.h> 45 46 #include "asn1.h" 47 #include "snmp.h" 48 #include "snmppriv.h" 49 #include "snmpagent.h" 50 51 static void snmp_debug_func(const char *fmt, ...); 52 53 void (*snmp_debug)(const char *fmt, ...) = snmp_debug_func; 54 55 struct snmp_node *tree; 56 u_int tree_size; 57 58 /* 59 * Structure to hold dependencies during SET processing 60 * The last two members of this structure must be the 61 * dependency visible by the user and the user data. 62 */ 63 struct depend { 64 TAILQ_ENTRY(depend) link; 65 size_t len; /* size of data part */ 66 snmp_depop_t func; 67 struct snmp_dependency dep; 68 #if defined(__GNUC__) && __GNUC__ < 3 69 u_char data[0]; 70 #else 71 u_char data[]; 72 #endif 73 }; 74 TAILQ_HEAD(depend_list, depend); 75 76 /* 77 * Set context 78 */ 79 struct context { 80 struct snmp_context ctx; 81 struct depend_list dlist; 82 const struct snmp_node *node[SNMP_MAX_BINDINGS]; 83 struct snmp_scratch scratch[SNMP_MAX_BINDINGS]; 84 struct depend *depend; 85 }; 86 87 #define TR(W) (snmp_trace & SNMP_TRACE_##W) 88 u_int snmp_trace = 0; 89 90 static char oidbuf[ASN_OIDSTRLEN]; 91 92 /* 93 * Allocate a context 94 */ 95 struct snmp_context * 96 snmp_init_context(void) 97 { 98 struct context *context; 99 100 if ((context = malloc(sizeof(*context))) == NULL) 101 return (NULL); 102 103 memset(context, 0, sizeof(*context)); 104 TAILQ_INIT(&context->dlist); 105 106 return (&context->ctx); 107 } 108 109 /* 110 * Find a variable for SET/GET and the first GETBULK pass. 111 * Return the node pointer. If the search fails, set the errp to 112 * the correct SNMPv2 GET exception code. 113 */ 114 static struct snmp_node * 115 find_node(const struct snmp_value *value, enum snmp_syntax *errp) 116 { 117 struct snmp_node *tp; 118 119 if (TR(FIND)) 120 snmp_debug("find: searching %s", 121 asn_oid2str_r(&value->var, oidbuf)); 122 123 /* 124 * If we have an exact match (the entry in the table is a 125 * sub-oid from the variable) we have found what we are for. 126 * If the table oid is higher than the variable, there is no match. 127 */ 128 for (tp = tree; tp < tree + tree_size; tp++) { 129 if (asn_is_suboid(&tp->oid, &value->var)) 130 goto found; 131 if (asn_compare_oid(&tp->oid, &value->var) >= 0) 132 break; 133 } 134 135 if (TR(FIND)) 136 snmp_debug("find: no match"); 137 *errp = SNMP_SYNTAX_NOSUCHOBJECT; 138 return (NULL); 139 140 found: 141 /* leafs must have a 0 instance identifier */ 142 if (tp->type == SNMP_NODE_LEAF && 143 (value->var.len != tp->oid.len + 1 || 144 value->var.subs[tp->oid.len] != 0)) { 145 if (TR(FIND)) 146 snmp_debug("find: bad leaf index"); 147 *errp = SNMP_SYNTAX_NOSUCHINSTANCE; 148 return (NULL); 149 } 150 if (TR(FIND)) 151 snmp_debug("find: found %s", 152 asn_oid2str_r(&value->var, oidbuf)); 153 return (tp); 154 } 155 156 static struct snmp_node * 157 find_subnode(const struct snmp_value *value) 158 { 159 struct snmp_node *tp; 160 161 for (tp = tree; tp < tree + tree_size; tp++) { 162 if (asn_is_suboid(&value->var, &tp->oid)) 163 return (tp); 164 } 165 return (NULL); 166 } 167 168 static void 169 snmp_pdu_create_response(struct snmp_pdu *pdu, struct snmp_pdu *resp) 170 { 171 memset(resp, 0, sizeof(*resp)); 172 strcpy(resp->community, pdu->community); 173 resp->version = pdu->version; 174 resp->type = SNMP_PDU_RESPONSE; 175 resp->request_id = pdu->request_id; 176 resp->version = pdu->version; 177 178 if (resp->version != SNMP_V3) 179 return; 180 181 memcpy(&resp->engine, &pdu->engine, sizeof(pdu->engine)); 182 memcpy(&resp->user, &pdu->user, sizeof(pdu->user)); 183 snmp_pdu_init_secparams(resp); 184 resp->identifier = pdu->identifier; 185 resp->security_model = pdu->security_model; 186 resp->context_engine_len = pdu->context_engine_len; 187 memcpy(resp->context_engine, pdu->context_engine, 188 resp->context_engine_len); 189 strlcpy(resp->context_name, pdu->context_name, 190 sizeof(resp->context_name)); 191 } 192 193 /* 194 * Execute a GET operation. The tree is rooted at the global 'root'. 195 * Build the response PDU on the fly. If the return code is SNMP_RET_ERR 196 * the pdu error status and index will be set. 197 */ 198 enum snmp_ret 199 snmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b, 200 struct snmp_pdu *resp, void *data) 201 { 202 int ret; 203 u_int i; 204 struct snmp_node *tp; 205 enum snmp_syntax except; 206 struct context context; 207 enum asn_err err; 208 209 memset(&context, 0, sizeof(context)); 210 context.ctx.data = data; 211 212 snmp_pdu_create_response(pdu, resp); 213 214 if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK) 215 /* cannot even encode header - very bad */ 216 return (SNMP_RET_IGN); 217 218 for (i = 0; i < pdu->nbindings; i++) { 219 resp->bindings[i].var = pdu->bindings[i].var; 220 if ((tp = find_node(&pdu->bindings[i], &except)) == NULL) { 221 if (pdu->version == SNMP_V1) { 222 if (TR(GET)) 223 snmp_debug("get: nosuchname"); 224 pdu->error_status = SNMP_ERR_NOSUCHNAME; 225 pdu->error_index = i + 1; 226 snmp_pdu_free(resp); 227 return (SNMP_RET_ERR); 228 } 229 if (TR(GET)) 230 snmp_debug("get: exception %u", except); 231 resp->bindings[i].syntax = except; 232 233 } else { 234 /* call the action to fetch the value. */ 235 resp->bindings[i].syntax = tp->syntax; 236 ret = (*tp->op)(&context.ctx, &resp->bindings[i], 237 tp->oid.len, tp->index, SNMP_OP_GET); 238 if (TR(GET)) 239 snmp_debug("get: action returns %d", ret); 240 241 if (ret == SNMP_ERR_NOSUCHNAME) { 242 if (pdu->version == SNMP_V1) { 243 pdu->error_status = SNMP_ERR_NOSUCHNAME; 244 pdu->error_index = i + 1; 245 snmp_pdu_free(resp); 246 return (SNMP_RET_ERR); 247 } 248 if (TR(GET)) 249 snmp_debug("get: exception noSuchInstance"); 250 resp->bindings[i].syntax = SNMP_SYNTAX_NOSUCHINSTANCE; 251 252 } else if (ret != SNMP_ERR_NOERROR) { 253 pdu->error_status = SNMP_ERR_GENERR; 254 pdu->error_index = i + 1; 255 snmp_pdu_free(resp); 256 return (SNMP_RET_ERR); 257 } 258 } 259 resp->nbindings++; 260 261 err = snmp_binding_encode(resp_b, &resp->bindings[i]); 262 263 if (err == ASN_ERR_EOBUF) { 264 pdu->error_status = SNMP_ERR_TOOBIG; 265 pdu->error_index = 0; 266 snmp_pdu_free(resp); 267 return (SNMP_RET_ERR); 268 } 269 if (err != ASN_ERR_OK) { 270 if (TR(GET)) 271 snmp_debug("get: binding encoding: %u", err); 272 pdu->error_status = SNMP_ERR_GENERR; 273 pdu->error_index = i + 1; 274 snmp_pdu_free(resp); 275 return (SNMP_RET_ERR); 276 } 277 } 278 279 return (snmp_fix_encoding(resp_b, resp)); 280 } 281 282 static struct snmp_node * 283 next_node(const struct snmp_value *value, int *pnext) 284 { 285 struct snmp_node *tp; 286 287 if (TR(FIND)) 288 snmp_debug("next: searching %s", 289 asn_oid2str_r(&value->var, oidbuf)); 290 291 *pnext = 0; 292 for (tp = tree; tp < tree + tree_size; tp++) { 293 if (asn_is_suboid(&tp->oid, &value->var)) { 294 /* the tree OID is a sub-oid of the requested OID. */ 295 if (tp->type == SNMP_NODE_LEAF) { 296 if (tp->oid.len == value->var.len) { 297 /* request for scalar type */ 298 if (TR(FIND)) 299 snmp_debug("next: found scalar %s", 300 asn_oid2str_r(&tp->oid, oidbuf)); 301 return (tp); 302 } 303 /* try next */ 304 } else { 305 if (TR(FIND)) 306 snmp_debug("next: found column %s", 307 asn_oid2str_r(&tp->oid, oidbuf)); 308 return (tp); 309 } 310 } else if (asn_is_suboid(&value->var, &tp->oid) || 311 asn_compare_oid(&tp->oid, &value->var) >= 0) { 312 if (TR(FIND)) 313 snmp_debug("next: found %s", 314 asn_oid2str_r(&tp->oid, oidbuf)); 315 *pnext = 1; 316 return (tp); 317 } 318 } 319 320 if (TR(FIND)) 321 snmp_debug("next: failed"); 322 323 return (NULL); 324 } 325 326 static enum snmp_ret 327 do_getnext(struct context *context, const struct snmp_value *inb, 328 struct snmp_value *outb, struct snmp_pdu *pdu) 329 { 330 const struct snmp_node *tp; 331 int ret, next; 332 333 if ((tp = next_node(inb, &next)) == NULL) 334 goto eofMib; 335 336 /* retain old variable if we are doing a GETNEXT on an exact 337 * matched leaf only */ 338 if (tp->type == SNMP_NODE_LEAF || next) 339 outb->var = tp->oid; 340 else 341 outb->var = inb->var; 342 343 for (;;) { 344 outb->syntax = tp->syntax; 345 if (tp->type == SNMP_NODE_LEAF) { 346 /* make a GET operation */ 347 outb->var.subs[outb->var.len++] = 0; 348 ret = (*tp->op)(&context->ctx, outb, tp->oid.len, 349 tp->index, SNMP_OP_GET); 350 } else { 351 /* make a GETNEXT */ 352 ret = (*tp->op)(&context->ctx, outb, tp->oid.len, 353 tp->index, SNMP_OP_GETNEXT); 354 } 355 if (ret != SNMP_ERR_NOSUCHNAME) { 356 /* got something */ 357 if (ret != SNMP_ERR_NOERROR && TR(GETNEXT)) 358 snmp_debug("getnext: %s returns %u", 359 asn_oid2str(&outb->var), ret); 360 break; 361 } 362 363 /* object has no data - try next */ 364 if (++tp == tree + tree_size) 365 break; 366 367 if (TR(GETNEXT)) 368 snmp_debug("getnext: no data - avancing to %s", 369 asn_oid2str(&tp->oid)); 370 371 outb->var = tp->oid; 372 } 373 374 if (ret == SNMP_ERR_NOSUCHNAME) { 375 eofMib: 376 outb->var = inb->var; 377 if (pdu->version == SNMP_V1) { 378 pdu->error_status = SNMP_ERR_NOSUCHNAME; 379 return (SNMP_RET_ERR); 380 } 381 outb->syntax = SNMP_SYNTAX_ENDOFMIBVIEW; 382 383 } else if (ret != SNMP_ERR_NOERROR) { 384 pdu->error_status = SNMP_ERR_GENERR; 385 return (SNMP_RET_ERR); 386 } 387 return (SNMP_RET_OK); 388 } 389 390 391 /* 392 * Execute a GETNEXT operation. The tree is rooted at the global 'root'. 393 * Build the response PDU on the fly. The return is: 394 */ 395 enum snmp_ret 396 snmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b, 397 struct snmp_pdu *resp, void *data) 398 { 399 struct context context; 400 u_int i; 401 enum asn_err err; 402 enum snmp_ret result; 403 404 memset(&context, 0, sizeof(context)); 405 context.ctx.data = data; 406 407 snmp_pdu_create_response(pdu, resp); 408 409 if (snmp_pdu_encode_header(resp_b, resp)) 410 return (SNMP_RET_IGN); 411 412 for (i = 0; i < pdu->nbindings; i++) { 413 result = do_getnext(&context, &pdu->bindings[i], 414 &resp->bindings[i], pdu); 415 416 if (result != SNMP_RET_OK) { 417 pdu->error_index = i + 1; 418 snmp_pdu_free(resp); 419 return (result); 420 } 421 422 resp->nbindings++; 423 424 err = snmp_binding_encode(resp_b, &resp->bindings[i]); 425 426 if (err == ASN_ERR_EOBUF) { 427 pdu->error_status = SNMP_ERR_TOOBIG; 428 pdu->error_index = 0; 429 snmp_pdu_free(resp); 430 return (SNMP_RET_ERR); 431 } 432 if (err != ASN_ERR_OK) { 433 if (TR(GET)) 434 snmp_debug("getnext: binding encoding: %u", err); 435 pdu->error_status = SNMP_ERR_GENERR; 436 pdu->error_index = i + 1; 437 snmp_pdu_free(resp); 438 return (SNMP_RET_ERR); 439 } 440 } 441 return (snmp_fix_encoding(resp_b, resp)); 442 } 443 444 enum snmp_ret 445 snmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b, 446 struct snmp_pdu *resp, void *data) 447 { 448 struct context context; 449 u_int i; 450 int cnt; 451 u_int non_rep; 452 int eomib; 453 enum snmp_ret result; 454 enum asn_err err; 455 456 memset(&context, 0, sizeof(context)); 457 context.ctx.data = data; 458 459 snmp_pdu_create_response(pdu, resp); 460 461 if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK) 462 /* cannot even encode header - very bad */ 463 return (SNMP_RET_IGN); 464 465 if ((non_rep = pdu->error_status) > pdu->nbindings) 466 non_rep = pdu->nbindings; 467 468 /* non-repeaters */ 469 for (i = 0; i < non_rep; i++) { 470 result = do_getnext(&context, &pdu->bindings[i], 471 &resp->bindings[resp->nbindings], pdu); 472 473 if (result != SNMP_RET_OK) { 474 pdu->error_index = i + 1; 475 snmp_pdu_free(resp); 476 return (result); 477 } 478 479 err = snmp_binding_encode(resp_b, 480 &resp->bindings[resp->nbindings++]); 481 482 if (err == ASN_ERR_EOBUF) 483 goto done; 484 485 if (err != ASN_ERR_OK) { 486 if (TR(GET)) 487 snmp_debug("getnext: binding encoding: %u", err); 488 pdu->error_status = SNMP_ERR_GENERR; 489 pdu->error_index = i + 1; 490 snmp_pdu_free(resp); 491 return (SNMP_RET_ERR); 492 } 493 } 494 495 if (non_rep == pdu->nbindings) 496 goto done; 497 498 /* repeates */ 499 for (cnt = 0; cnt < pdu->error_index; cnt++) { 500 eomib = 1; 501 for (i = non_rep; i < pdu->nbindings; i++) { 502 if (cnt == 0) 503 result = do_getnext(&context, &pdu->bindings[i], 504 &resp->bindings[resp->nbindings], pdu); 505 else 506 result = do_getnext(&context, 507 &resp->bindings[resp->nbindings - 508 (pdu->nbindings - non_rep)], 509 &resp->bindings[resp->nbindings], pdu); 510 511 if (result != SNMP_RET_OK) { 512 pdu->error_index = i + 1; 513 snmp_pdu_free(resp); 514 return (result); 515 } 516 if (resp->bindings[resp->nbindings].syntax != 517 SNMP_SYNTAX_ENDOFMIBVIEW) 518 eomib = 0; 519 520 err = snmp_binding_encode(resp_b, 521 &resp->bindings[resp->nbindings++]); 522 523 if (err == ASN_ERR_EOBUF) 524 goto done; 525 526 if (err != ASN_ERR_OK) { 527 if (TR(GET)) 528 snmp_debug("getnext: binding encoding: %u", err); 529 pdu->error_status = SNMP_ERR_GENERR; 530 pdu->error_index = i + 1; 531 snmp_pdu_free(resp); 532 return (SNMP_RET_ERR); 533 } 534 } 535 if (eomib) 536 break; 537 } 538 539 done: 540 return (snmp_fix_encoding(resp_b, resp)); 541 } 542 543 /* 544 * Rollback a SET operation. Failed index is 'i'. 545 */ 546 static void 547 rollback(struct context *context, struct snmp_pdu *pdu, u_int i) 548 { 549 struct snmp_value *b; 550 const struct snmp_node *np; 551 int ret; 552 553 while (i-- > 0) { 554 b = &pdu->bindings[i]; 555 np = context->node[i]; 556 557 context->ctx.scratch = &context->scratch[i]; 558 559 ret = (*np->op)(&context->ctx, b, np->oid.len, np->index, 560 SNMP_OP_ROLLBACK); 561 562 if (ret != SNMP_ERR_NOERROR) { 563 snmp_error("set: rollback failed (%d) on variable %s " 564 "index %u", ret, asn_oid2str(&b->var), i); 565 if (pdu->version != SNMP_V1) { 566 pdu->error_status = SNMP_ERR_UNDO_FAILED; 567 pdu->error_index = 0; 568 } 569 } 570 } 571 } 572 573 /* 574 * Commit dependencies. 575 */ 576 int 577 snmp_dep_commit(struct snmp_context *ctx) 578 { 579 struct context *context = (struct context *)ctx; 580 int ret; 581 582 TAILQ_FOREACH(context->depend, &context->dlist, link) { 583 ctx->dep = &context->depend->dep; 584 585 if (TR(SET)) 586 snmp_debug("set: dependency commit %s", 587 asn_oid2str(&ctx->dep->obj)); 588 589 ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT); 590 591 if (ret != SNMP_ERR_NOERROR) { 592 if (TR(SET)) 593 snmp_debug("set: dependency failed %d", ret); 594 return (ret); 595 } 596 } 597 return (SNMP_ERR_NOERROR); 598 } 599 600 /* 601 * Rollback dependencies 602 */ 603 int 604 snmp_dep_rollback(struct snmp_context *ctx) 605 { 606 struct context *context = (struct context *)ctx; 607 int ret, ret1; 608 char objbuf[ASN_OIDSTRLEN]; 609 char idxbuf[ASN_OIDSTRLEN]; 610 611 ret1 = SNMP_ERR_NOERROR; 612 while ((context->depend = 613 TAILQ_PREV(context->depend, depend_list, link)) != NULL) { 614 ctx->dep = &context->depend->dep; 615 616 if (TR(SET)) 617 snmp_debug("set: dependency rollback %s", 618 asn_oid2str(&ctx->dep->obj)); 619 620 ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK); 621 622 if (ret != SNMP_ERR_NOERROR) { 623 snmp_debug("set: dep rollback returns %u: %s %s", ret, 624 asn_oid2str_r(&ctx->dep->obj, objbuf), 625 asn_oid2str_r(&ctx->dep->idx, idxbuf)); 626 if (ret1 == SNMP_ERR_NOERROR) 627 ret1 = ret; 628 } 629 } 630 return (ret1); 631 } 632 633 void 634 snmp_dep_finish(struct snmp_context *ctx) 635 { 636 struct context *context = (struct context *)ctx; 637 struct depend *d; 638 639 while ((d = TAILQ_FIRST(&context->dlist)) != NULL) { 640 ctx->dep = &d->dep; 641 (void)d->func(ctx, ctx->dep, SNMP_DEPOP_FINISH); 642 TAILQ_REMOVE(&context->dlist, d, link); 643 free(d); 644 } 645 } 646 647 /* 648 * Do a SET operation. 649 */ 650 enum snmp_ret 651 snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b, 652 struct snmp_pdu *resp, void *data) 653 { 654 int ret; 655 u_int i; 656 enum asn_err asnerr; 657 struct context context; 658 const struct snmp_node *np; 659 struct snmp_value *b; 660 enum snmp_syntax except; 661 662 memset(&context, 0, sizeof(context)); 663 TAILQ_INIT(&context.dlist); 664 context.ctx.data = data; 665 666 snmp_pdu_create_response(pdu, resp); 667 668 if (snmp_pdu_encode_header(resp_b, resp)) 669 return (SNMP_RET_IGN); 670 671 /* 672 * 1. Find all nodes, check that they are writeable and 673 * that the syntax is ok, copy over the binding to the response. 674 */ 675 for (i = 0; i < pdu->nbindings; i++) { 676 b = &pdu->bindings[i]; 677 678 if ((np = context.node[i] = find_node(b, &except)) == NULL) { 679 /* not found altogether or LEAF with wrong index */ 680 if (TR(SET)) 681 snmp_debug("set: node not found %s", 682 asn_oid2str_r(&b->var, oidbuf)); 683 if (pdu->version == SNMP_V1) { 684 pdu->error_index = i + 1; 685 pdu->error_status = SNMP_ERR_NOSUCHNAME; 686 } else if ((np = find_subnode(b)) != NULL) { 687 /* 2. intermediate object */ 688 pdu->error_index = i + 1; 689 pdu->error_status = SNMP_ERR_NOT_WRITEABLE; 690 } else if (except == SNMP_SYNTAX_NOSUCHOBJECT) { 691 pdu->error_index = i + 1; 692 pdu->error_status = SNMP_ERR_NO_ACCESS; 693 } else { 694 pdu->error_index = i + 1; 695 pdu->error_status = SNMP_ERR_NO_CREATION; 696 } 697 snmp_pdu_free(resp); 698 return (SNMP_RET_ERR); 699 } 700 /* 701 * 2. write/createable? 702 * Can check this for leafs only, because in v2 we have 703 * to differentiate between NOT_WRITEABLE and NO_CREATION 704 * and only the action routine for COLUMNS knows, whether 705 * a column exists. 706 */ 707 if (np->type == SNMP_NODE_LEAF && 708 !(np->flags & SNMP_NODE_CANSET)) { 709 if (pdu->version == SNMP_V1) { 710 pdu->error_index = i + 1; 711 pdu->error_status = SNMP_ERR_NOSUCHNAME; 712 } else { 713 pdu->error_index = i + 1; 714 pdu->error_status = SNMP_ERR_NOT_WRITEABLE; 715 } 716 snmp_pdu_free(resp); 717 return (SNMP_RET_ERR); 718 } 719 /* 720 * 3. Ensure the right syntax 721 */ 722 if (np->syntax != b->syntax) { 723 if (pdu->version == SNMP_V1) { 724 pdu->error_index = i + 1; 725 pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */ 726 } else { 727 pdu->error_index = i + 1; 728 pdu->error_status = SNMP_ERR_WRONG_TYPE; 729 } 730 snmp_pdu_free(resp); 731 return (SNMP_RET_ERR); 732 } 733 /* 734 * 4. Copy binding 735 */ 736 if (snmp_value_copy(&resp->bindings[i], b)) { 737 pdu->error_index = i + 1; 738 pdu->error_status = SNMP_ERR_GENERR; 739 snmp_pdu_free(resp); 740 return (SNMP_RET_ERR); 741 } 742 asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]); 743 if (asnerr == ASN_ERR_EOBUF) { 744 pdu->error_index = i + 1; 745 pdu->error_status = SNMP_ERR_TOOBIG; 746 snmp_pdu_free(resp); 747 return (SNMP_RET_ERR); 748 } else if (asnerr != ASN_ERR_OK) { 749 pdu->error_index = i + 1; 750 pdu->error_status = SNMP_ERR_GENERR; 751 snmp_pdu_free(resp); 752 return (SNMP_RET_ERR); 753 } 754 resp->nbindings++; 755 } 756 757 context.ctx.code = SNMP_RET_OK; 758 759 /* 760 * 2. Call the SET method for each node. If a SET fails, rollback 761 * everything. Map error codes depending on the version. 762 */ 763 for (i = 0; i < pdu->nbindings; i++) { 764 b = &pdu->bindings[i]; 765 np = context.node[i]; 766 767 context.ctx.var_index = i + 1; 768 context.ctx.scratch = &context.scratch[i]; 769 770 ret = (*np->op)(&context.ctx, b, np->oid.len, np->index, 771 SNMP_OP_SET); 772 773 if (TR(SET)) 774 snmp_debug("set: action %s returns %d", np->name, ret); 775 776 if (pdu->version == SNMP_V1) { 777 switch (ret) { 778 case SNMP_ERR_NO_ACCESS: 779 ret = SNMP_ERR_NOSUCHNAME; 780 break; 781 case SNMP_ERR_WRONG_TYPE: 782 /* should no happen */ 783 ret = SNMP_ERR_BADVALUE; 784 break; 785 case SNMP_ERR_WRONG_LENGTH: 786 ret = SNMP_ERR_BADVALUE; 787 break; 788 case SNMP_ERR_WRONG_ENCODING: 789 /* should not happen */ 790 ret = SNMP_ERR_BADVALUE; 791 break; 792 case SNMP_ERR_WRONG_VALUE: 793 ret = SNMP_ERR_BADVALUE; 794 break; 795 case SNMP_ERR_NO_CREATION: 796 ret = SNMP_ERR_NOSUCHNAME; 797 break; 798 case SNMP_ERR_INCONS_VALUE: 799 ret = SNMP_ERR_BADVALUE; 800 break; 801 case SNMP_ERR_RES_UNAVAIL: 802 ret = SNMP_ERR_GENERR; 803 break; 804 case SNMP_ERR_COMMIT_FAILED: 805 ret = SNMP_ERR_GENERR; 806 break; 807 case SNMP_ERR_UNDO_FAILED: 808 ret = SNMP_ERR_GENERR; 809 break; 810 case SNMP_ERR_AUTH_ERR: 811 /* should not happen */ 812 ret = SNMP_ERR_GENERR; 813 break; 814 case SNMP_ERR_NOT_WRITEABLE: 815 ret = SNMP_ERR_NOSUCHNAME; 816 break; 817 case SNMP_ERR_INCONS_NAME: 818 ret = SNMP_ERR_BADVALUE; 819 break; 820 } 821 } 822 if (ret != SNMP_ERR_NOERROR) { 823 pdu->error_index = i + 1; 824 pdu->error_status = ret; 825 826 rollback(&context, pdu, i); 827 snmp_pdu_free(resp); 828 829 context.ctx.code = SNMP_RET_ERR; 830 831 goto errout; 832 } 833 } 834 835 /* 836 * 3. Call dependencies 837 */ 838 if (TR(SET)) 839 snmp_debug("set: set operations ok"); 840 841 if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) { 842 pdu->error_status = ret; 843 pdu->error_index = context.ctx.var_index; 844 845 if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) { 846 if (pdu->version != SNMP_V1) { 847 pdu->error_status = SNMP_ERR_UNDO_FAILED; 848 pdu->error_index = 0; 849 } 850 } 851 rollback(&context, pdu, i); 852 snmp_pdu_free(resp); 853 854 context.ctx.code = SNMP_RET_ERR; 855 856 goto errout; 857 } 858 859 /* 860 * 4. Commit and copy values from the original packet to the response. 861 * This is not the commit operation from RFC 1905 but rather an 862 * 'FREE RESOURCES' operation. It shouldn't fail. 863 */ 864 if (TR(SET)) 865 snmp_debug("set: commiting"); 866 867 for (i = 0; i < pdu->nbindings; i++) { 868 b = &resp->bindings[i]; 869 np = context.node[i]; 870 871 context.ctx.var_index = i + 1; 872 context.ctx.scratch = &context.scratch[i]; 873 874 ret = (*np->op)(&context.ctx, b, np->oid.len, np->index, 875 SNMP_OP_COMMIT); 876 877 if (ret != SNMP_ERR_NOERROR) 878 snmp_error("set: commit failed (%d) on" 879 " variable %s index %u", ret, 880 asn_oid2str_r(&b->var, oidbuf), i); 881 } 882 883 if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { 884 snmp_error("set: fix_encoding failed"); 885 snmp_pdu_free(resp); 886 context.ctx.code = SNMP_RET_IGN; 887 } 888 889 /* 890 * Done 891 */ 892 errout: 893 snmp_dep_finish(&context.ctx); 894 895 if (TR(SET)) 896 snmp_debug("set: returning %d", context.ctx.code); 897 898 return (context.ctx.code); 899 } 900 /* 901 * Lookup a dependency. If it doesn't exist, create one 902 */ 903 struct snmp_dependency * 904 snmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj, 905 const struct asn_oid *idx, size_t len, snmp_depop_t func) 906 { 907 struct context *context; 908 struct depend *d; 909 910 context = (struct context *)(void *) 911 ((char *)ctx - offsetof(struct context, ctx)); 912 if (TR(DEPEND)) { 913 snmp_debug("depend: looking for %s", asn_oid2str(obj)); 914 if (idx) 915 snmp_debug("depend: index is %s", asn_oid2str(idx)); 916 } 917 TAILQ_FOREACH(d, &context->dlist, link) 918 if (asn_compare_oid(obj, &d->dep.obj) == 0 && 919 ((idx == NULL && d->dep.idx.len == 0) || 920 (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) { 921 if(TR(DEPEND)) 922 snmp_debug("depend: found"); 923 return (&d->dep); 924 } 925 926 if(TR(DEPEND)) 927 snmp_debug("depend: creating"); 928 929 if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL) 930 return (NULL); 931 memset(&d->dep, 0, len); 932 933 d->dep.obj = *obj; 934 if (idx == NULL) 935 d->dep.idx.len = 0; 936 else 937 d->dep.idx = *idx; 938 d->len = len; 939 d->func = func; 940 941 TAILQ_INSERT_TAIL(&context->dlist, d, link); 942 943 return (&d->dep); 944 } 945 946 /* 947 * Make an error response from a PDU. We do this without decoding the 948 * variable bindings. This means we can sent the junk back to a caller 949 * that has sent us junk in the first place. 950 */ 951 enum snmp_ret 952 snmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b, 953 struct asn_buf *resp_b) 954 { 955 asn_len_t len; 956 struct snmp_pdu resp; 957 enum asn_err err; 958 enum snmp_code code; 959 960 memset(&resp, 0, sizeof(resp)); 961 if ((code = snmp_pdu_decode_header(pdu_b, &resp)) != SNMP_CODE_OK) 962 return (SNMP_RET_IGN); 963 964 if (pdu_b->asn_len < len) 965 return (SNMP_RET_IGN); 966 pdu_b->asn_len = len; 967 968 err = snmp_parse_pdus_hdr(pdu_b, &resp, &len); 969 if (ASN_ERR_STOPPED(err)) 970 return (SNMP_RET_IGN); 971 if (pdu_b->asn_len < len) 972 return (SNMP_RET_IGN); 973 pdu_b->asn_len = len; 974 975 /* now we have the bindings left - construct new message */ 976 resp.error_status = pdu->error_status; 977 resp.error_index = pdu->error_index; 978 resp.type = SNMP_PDU_RESPONSE; 979 980 code = snmp_pdu_encode_header(resp_b, &resp); 981 if (code != SNMP_CODE_OK) 982 return (SNMP_RET_IGN); 983 984 if (pdu_b->asn_len > resp_b->asn_len) 985 /* too short */ 986 return (SNMP_RET_IGN); 987 (void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len); 988 resp_b->asn_len -= pdu_b->asn_len; 989 resp_b->asn_ptr += pdu_b->asn_len; 990 991 code = snmp_fix_encoding(resp_b, &resp); 992 if (code != SNMP_CODE_OK) 993 return (SNMP_RET_IGN); 994 995 return (SNMP_RET_OK); 996 } 997 998 static void 999 snmp_debug_func(const char *fmt, ...) 1000 { 1001 va_list ap; 1002 1003 va_start(ap, fmt); 1004 vfprintf(stderr, fmt, ap); 1005 va_end(ap); 1006 fprintf(stderr, "\n"); 1007 } 1008