snmpagent.c revision 260640
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 51static void snmp_debug_func(const char *fmt, ...); 52 53void (*snmp_debug)(const char *fmt, ...) = snmp_debug_func; 54 55struct snmp_node *tree; 56u_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 */ 63struct 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}; 74TAILQ_HEAD(depend_list, depend); 75 76/* 77 * Set context 78 */ 79struct 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) 88u_int snmp_trace = 0; 89 90static char oidbuf[ASN_OIDSTRLEN]; 91 92/* 93 * Allocate a context 94 */ 95struct snmp_context * 96snmp_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 */ 114static struct snmp_node * 115find_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 156static struct snmp_node * 157find_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 168static void 169snmp_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 */ 198enum snmp_ret 199snmp_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 282static struct snmp_node * 283next_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 326static enum snmp_ret 327do_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 */ 395enum snmp_ret 396snmp_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 444enum snmp_ret 445snmp_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 503 if (resp->nbindings == SNMP_MAX_BINDINGS) 504 /* PDU is full */ 505 goto done; 506 507 if (cnt == 0) 508 result = do_getnext(&context, &pdu->bindings[i], 509 &resp->bindings[resp->nbindings], pdu); 510 else 511 result = do_getnext(&context, 512 &resp->bindings[resp->nbindings - 513 (pdu->nbindings - non_rep)], 514 &resp->bindings[resp->nbindings], pdu); 515 516 if (result != SNMP_RET_OK) { 517 pdu->error_index = i + 1; 518 snmp_pdu_free(resp); 519 return (result); 520 } 521 if (resp->bindings[resp->nbindings].syntax != 522 SNMP_SYNTAX_ENDOFMIBVIEW) 523 eomib = 0; 524 525 err = snmp_binding_encode(resp_b, 526 &resp->bindings[resp->nbindings++]); 527 528 if (err == ASN_ERR_EOBUF) 529 goto done; 530 531 if (err != ASN_ERR_OK) { 532 if (TR(GET)) 533 snmp_debug("getnext: binding encoding: %u", err); 534 pdu->error_status = SNMP_ERR_GENERR; 535 pdu->error_index = i + 1; 536 snmp_pdu_free(resp); 537 return (SNMP_RET_ERR); 538 } 539 } 540 if (eomib) 541 break; 542 } 543 544 done: 545 return (snmp_fix_encoding(resp_b, resp)); 546} 547 548/* 549 * Rollback a SET operation. Failed index is 'i'. 550 */ 551static void 552rollback(struct context *context, struct snmp_pdu *pdu, u_int i) 553{ 554 struct snmp_value *b; 555 const struct snmp_node *np; 556 int ret; 557 558 while (i-- > 0) { 559 b = &pdu->bindings[i]; 560 np = context->node[i]; 561 562 context->ctx.scratch = &context->scratch[i]; 563 564 ret = (*np->op)(&context->ctx, b, np->oid.len, np->index, 565 SNMP_OP_ROLLBACK); 566 567 if (ret != SNMP_ERR_NOERROR) { 568 snmp_error("set: rollback failed (%d) on variable %s " 569 "index %u", ret, asn_oid2str(&b->var), i); 570 if (pdu->version != SNMP_V1) { 571 pdu->error_status = SNMP_ERR_UNDO_FAILED; 572 pdu->error_index = 0; 573 } 574 } 575 } 576} 577 578/* 579 * Commit dependencies. 580 */ 581int 582snmp_dep_commit(struct snmp_context *ctx) 583{ 584 struct context *context = (struct context *)ctx; 585 int ret; 586 587 TAILQ_FOREACH(context->depend, &context->dlist, link) { 588 ctx->dep = &context->depend->dep; 589 590 if (TR(SET)) 591 snmp_debug("set: dependency commit %s", 592 asn_oid2str(&ctx->dep->obj)); 593 594 ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT); 595 596 if (ret != SNMP_ERR_NOERROR) { 597 if (TR(SET)) 598 snmp_debug("set: dependency failed %d", ret); 599 return (ret); 600 } 601 } 602 return (SNMP_ERR_NOERROR); 603} 604 605/* 606 * Rollback dependencies 607 */ 608int 609snmp_dep_rollback(struct snmp_context *ctx) 610{ 611 struct context *context = (struct context *)ctx; 612 int ret, ret1; 613 char objbuf[ASN_OIDSTRLEN]; 614 char idxbuf[ASN_OIDSTRLEN]; 615 616 ret1 = SNMP_ERR_NOERROR; 617 while ((context->depend = 618 TAILQ_PREV(context->depend, depend_list, link)) != NULL) { 619 ctx->dep = &context->depend->dep; 620 621 if (TR(SET)) 622 snmp_debug("set: dependency rollback %s", 623 asn_oid2str(&ctx->dep->obj)); 624 625 ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK); 626 627 if (ret != SNMP_ERR_NOERROR) { 628 snmp_debug("set: dep rollback returns %u: %s %s", ret, 629 asn_oid2str_r(&ctx->dep->obj, objbuf), 630 asn_oid2str_r(&ctx->dep->idx, idxbuf)); 631 if (ret1 == SNMP_ERR_NOERROR) 632 ret1 = ret; 633 } 634 } 635 return (ret1); 636} 637 638void 639snmp_dep_finish(struct snmp_context *ctx) 640{ 641 struct context *context = (struct context *)ctx; 642 struct depend *d; 643 644 while ((d = TAILQ_FIRST(&context->dlist)) != NULL) { 645 ctx->dep = &d->dep; 646 (void)d->func(ctx, ctx->dep, SNMP_DEPOP_FINISH); 647 TAILQ_REMOVE(&context->dlist, d, link); 648 free(d); 649 } 650} 651 652/* 653 * Do a SET operation. 654 */ 655enum snmp_ret 656snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b, 657 struct snmp_pdu *resp, void *data) 658{ 659 int ret; 660 u_int i; 661 enum asn_err asnerr; 662 struct context context; 663 const struct snmp_node *np; 664 struct snmp_value *b; 665 enum snmp_syntax except; 666 667 memset(&context, 0, sizeof(context)); 668 TAILQ_INIT(&context.dlist); 669 context.ctx.data = data; 670 671 snmp_pdu_create_response(pdu, resp); 672 673 if (snmp_pdu_encode_header(resp_b, resp)) 674 return (SNMP_RET_IGN); 675 676 /* 677 * 1. Find all nodes, check that they are writeable and 678 * that the syntax is ok, copy over the binding to the response. 679 */ 680 for (i = 0; i < pdu->nbindings; i++) { 681 b = &pdu->bindings[i]; 682 683 if ((np = context.node[i] = find_node(b, &except)) == NULL) { 684 /* not found altogether or LEAF with wrong index */ 685 if (TR(SET)) 686 snmp_debug("set: node not found %s", 687 asn_oid2str_r(&b->var, oidbuf)); 688 if (pdu->version == SNMP_V1) { 689 pdu->error_index = i + 1; 690 pdu->error_status = SNMP_ERR_NOSUCHNAME; 691 } else if ((np = find_subnode(b)) != NULL) { 692 /* 2. intermediate object */ 693 pdu->error_index = i + 1; 694 pdu->error_status = SNMP_ERR_NOT_WRITEABLE; 695 } else if (except == SNMP_SYNTAX_NOSUCHOBJECT) { 696 pdu->error_index = i + 1; 697 pdu->error_status = SNMP_ERR_NO_ACCESS; 698 } else { 699 pdu->error_index = i + 1; 700 pdu->error_status = SNMP_ERR_NO_CREATION; 701 } 702 snmp_pdu_free(resp); 703 return (SNMP_RET_ERR); 704 } 705 /* 706 * 2. write/createable? 707 * Can check this for leafs only, because in v2 we have 708 * to differentiate between NOT_WRITEABLE and NO_CREATION 709 * and only the action routine for COLUMNS knows, whether 710 * a column exists. 711 */ 712 if (np->type == SNMP_NODE_LEAF && 713 !(np->flags & SNMP_NODE_CANSET)) { 714 if (pdu->version == SNMP_V1) { 715 pdu->error_index = i + 1; 716 pdu->error_status = SNMP_ERR_NOSUCHNAME; 717 } else { 718 pdu->error_index = i + 1; 719 pdu->error_status = SNMP_ERR_NOT_WRITEABLE; 720 } 721 snmp_pdu_free(resp); 722 return (SNMP_RET_ERR); 723 } 724 /* 725 * 3. Ensure the right syntax 726 */ 727 if (np->syntax != b->syntax) { 728 if (pdu->version == SNMP_V1) { 729 pdu->error_index = i + 1; 730 pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */ 731 } else { 732 pdu->error_index = i + 1; 733 pdu->error_status = SNMP_ERR_WRONG_TYPE; 734 } 735 snmp_pdu_free(resp); 736 return (SNMP_RET_ERR); 737 } 738 /* 739 * 4. Copy binding 740 */ 741 if (snmp_value_copy(&resp->bindings[i], b)) { 742 pdu->error_index = i + 1; 743 pdu->error_status = SNMP_ERR_GENERR; 744 snmp_pdu_free(resp); 745 return (SNMP_RET_ERR); 746 } 747 asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]); 748 if (asnerr == ASN_ERR_EOBUF) { 749 pdu->error_index = i + 1; 750 pdu->error_status = SNMP_ERR_TOOBIG; 751 snmp_pdu_free(resp); 752 return (SNMP_RET_ERR); 753 } else if (asnerr != ASN_ERR_OK) { 754 pdu->error_index = i + 1; 755 pdu->error_status = SNMP_ERR_GENERR; 756 snmp_pdu_free(resp); 757 return (SNMP_RET_ERR); 758 } 759 resp->nbindings++; 760 } 761 762 context.ctx.code = SNMP_RET_OK; 763 764 /* 765 * 2. Call the SET method for each node. If a SET fails, rollback 766 * everything. Map error codes depending on the version. 767 */ 768 for (i = 0; i < pdu->nbindings; i++) { 769 b = &pdu->bindings[i]; 770 np = context.node[i]; 771 772 context.ctx.var_index = i + 1; 773 context.ctx.scratch = &context.scratch[i]; 774 775 ret = (*np->op)(&context.ctx, b, np->oid.len, np->index, 776 SNMP_OP_SET); 777 778 if (TR(SET)) 779 snmp_debug("set: action %s returns %d", np->name, ret); 780 781 if (pdu->version == SNMP_V1) { 782 switch (ret) { 783 case SNMP_ERR_NO_ACCESS: 784 ret = SNMP_ERR_NOSUCHNAME; 785 break; 786 case SNMP_ERR_WRONG_TYPE: 787 /* should no happen */ 788 ret = SNMP_ERR_BADVALUE; 789 break; 790 case SNMP_ERR_WRONG_LENGTH: 791 ret = SNMP_ERR_BADVALUE; 792 break; 793 case SNMP_ERR_WRONG_ENCODING: 794 /* should not happen */ 795 ret = SNMP_ERR_BADVALUE; 796 break; 797 case SNMP_ERR_WRONG_VALUE: 798 ret = SNMP_ERR_BADVALUE; 799 break; 800 case SNMP_ERR_NO_CREATION: 801 ret = SNMP_ERR_NOSUCHNAME; 802 break; 803 case SNMP_ERR_INCONS_VALUE: 804 ret = SNMP_ERR_BADVALUE; 805 break; 806 case SNMP_ERR_RES_UNAVAIL: 807 ret = SNMP_ERR_GENERR; 808 break; 809 case SNMP_ERR_COMMIT_FAILED: 810 ret = SNMP_ERR_GENERR; 811 break; 812 case SNMP_ERR_UNDO_FAILED: 813 ret = SNMP_ERR_GENERR; 814 break; 815 case SNMP_ERR_AUTH_ERR: 816 /* should not happen */ 817 ret = SNMP_ERR_GENERR; 818 break; 819 case SNMP_ERR_NOT_WRITEABLE: 820 ret = SNMP_ERR_NOSUCHNAME; 821 break; 822 case SNMP_ERR_INCONS_NAME: 823 ret = SNMP_ERR_BADVALUE; 824 break; 825 } 826 } 827 if (ret != SNMP_ERR_NOERROR) { 828 pdu->error_index = i + 1; 829 pdu->error_status = ret; 830 831 rollback(&context, pdu, i); 832 snmp_pdu_free(resp); 833 834 context.ctx.code = SNMP_RET_ERR; 835 836 goto errout; 837 } 838 } 839 840 /* 841 * 3. Call dependencies 842 */ 843 if (TR(SET)) 844 snmp_debug("set: set operations ok"); 845 846 if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) { 847 pdu->error_status = ret; 848 pdu->error_index = context.ctx.var_index; 849 850 if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) { 851 if (pdu->version != SNMP_V1) { 852 pdu->error_status = SNMP_ERR_UNDO_FAILED; 853 pdu->error_index = 0; 854 } 855 } 856 rollback(&context, pdu, i); 857 snmp_pdu_free(resp); 858 859 context.ctx.code = SNMP_RET_ERR; 860 861 goto errout; 862 } 863 864 /* 865 * 4. Commit and copy values from the original packet to the response. 866 * This is not the commit operation from RFC 1905 but rather an 867 * 'FREE RESOURCES' operation. It shouldn't fail. 868 */ 869 if (TR(SET)) 870 snmp_debug("set: commiting"); 871 872 for (i = 0; i < pdu->nbindings; i++) { 873 b = &resp->bindings[i]; 874 np = context.node[i]; 875 876 context.ctx.var_index = i + 1; 877 context.ctx.scratch = &context.scratch[i]; 878 879 ret = (*np->op)(&context.ctx, b, np->oid.len, np->index, 880 SNMP_OP_COMMIT); 881 882 if (ret != SNMP_ERR_NOERROR) 883 snmp_error("set: commit failed (%d) on" 884 " variable %s index %u", ret, 885 asn_oid2str_r(&b->var, oidbuf), i); 886 } 887 888 if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { 889 snmp_error("set: fix_encoding failed"); 890 snmp_pdu_free(resp); 891 context.ctx.code = SNMP_RET_IGN; 892 } 893 894 /* 895 * Done 896 */ 897 errout: 898 snmp_dep_finish(&context.ctx); 899 900 if (TR(SET)) 901 snmp_debug("set: returning %d", context.ctx.code); 902 903 return (context.ctx.code); 904} 905/* 906 * Lookup a dependency. If it doesn't exist, create one 907 */ 908struct snmp_dependency * 909snmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj, 910 const struct asn_oid *idx, size_t len, snmp_depop_t func) 911{ 912 struct context *context; 913 struct depend *d; 914 915 context = (struct context *)(void *) 916 ((char *)ctx - offsetof(struct context, ctx)); 917 if (TR(DEPEND)) { 918 snmp_debug("depend: looking for %s", asn_oid2str(obj)); 919 if (idx) 920 snmp_debug("depend: index is %s", asn_oid2str(idx)); 921 } 922 TAILQ_FOREACH(d, &context->dlist, link) 923 if (asn_compare_oid(obj, &d->dep.obj) == 0 && 924 ((idx == NULL && d->dep.idx.len == 0) || 925 (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) { 926 if(TR(DEPEND)) 927 snmp_debug("depend: found"); 928 return (&d->dep); 929 } 930 931 if(TR(DEPEND)) 932 snmp_debug("depend: creating"); 933 934 if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL) 935 return (NULL); 936 memset(&d->dep, 0, len); 937 938 d->dep.obj = *obj; 939 if (idx == NULL) 940 d->dep.idx.len = 0; 941 else 942 d->dep.idx = *idx; 943 d->len = len; 944 d->func = func; 945 946 TAILQ_INSERT_TAIL(&context->dlist, d, link); 947 948 return (&d->dep); 949} 950 951/* 952 * Make an error response from a PDU. We do this without decoding the 953 * variable bindings. This means we can sent the junk back to a caller 954 * that has sent us junk in the first place. 955 */ 956enum snmp_ret 957snmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b, 958 struct asn_buf *resp_b) 959{ 960 asn_len_t len; 961 struct snmp_pdu resp; 962 enum asn_err err; 963 enum snmp_code code; 964 965 memset(&resp, 0, sizeof(resp)); 966 if ((code = snmp_pdu_decode_header(pdu_b, &resp)) != SNMP_CODE_OK) 967 return (SNMP_RET_IGN); 968 969 if (pdu_b->asn_len < len) 970 return (SNMP_RET_IGN); 971 pdu_b->asn_len = len; 972 973 err = snmp_parse_pdus_hdr(pdu_b, &resp, &len); 974 if (ASN_ERR_STOPPED(err)) 975 return (SNMP_RET_IGN); 976 if (pdu_b->asn_len < len) 977 return (SNMP_RET_IGN); 978 pdu_b->asn_len = len; 979 980 /* now we have the bindings left - construct new message */ 981 resp.error_status = pdu->error_status; 982 resp.error_index = pdu->error_index; 983 resp.type = SNMP_PDU_RESPONSE; 984 985 code = snmp_pdu_encode_header(resp_b, &resp); 986 if (code != SNMP_CODE_OK) 987 return (SNMP_RET_IGN); 988 989 if (pdu_b->asn_len > resp_b->asn_len) 990 /* too short */ 991 return (SNMP_RET_IGN); 992 (void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len); 993 resp_b->asn_len -= pdu_b->asn_len; 994 resp_b->asn_ptr += pdu_b->asn_len; 995 996 code = snmp_fix_encoding(resp_b, &resp); 997 if (code != SNMP_CODE_OK) 998 return (SNMP_RET_IGN); 999 1000 return (SNMP_RET_OK); 1001} 1002 1003static void 1004snmp_debug_func(const char *fmt, ...) 1005{ 1006 va_list ap; 1007 1008 va_start(ap, fmt); 1009 vfprintf(stderr, fmt, ap); 1010 va_end(ap); 1011 fprintf(stderr, "\n"); 1012} 1013