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