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