snmpagent.c revision 150920
1122394Sharti/* 2122394Sharti * Copyright (c) 2001-2003 3122394Sharti * Fraunhofer Institute for Open Communication Systems (FhG Fokus). 4122394Sharti * All rights reserved. 5122394Sharti * 6122394Sharti * Author: Harti Brandt <harti@freebsd.org> 7133211Sharti * 8133211Sharti * Redistribution and use in source and binary forms, with or without 9133211Sharti * modification, are permitted provided that the following conditions 10133211Sharti * are met: 11133211Sharti * 1. Redistributions of source code must retain the above copyright 12133211Sharti * notice, this list of conditions and the following disclaimer. 13122394Sharti * 2. Redistributions in binary form must reproduce the above copyright 14122394Sharti * notice, this list of conditions and the following disclaimer in the 15122394Sharti * documentation and/or other materials provided with the distribution. 16133211Sharti * 17133211Sharti * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18133211Sharti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19133211Sharti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20133211Sharti * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 21133211Sharti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22133211Sharti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23133211Sharti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24133211Sharti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25133211Sharti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26133211Sharti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27133211Sharti * SUCH DAMAGE. 28122394Sharti * 29150920Sharti * $Begemot: bsnmp/lib/snmpagent.c,v 1.20 2005/10/04 11:21:33 brandt_h Exp $ 30122394Sharti * 31122394Sharti * SNMP Agent functions 32122394Sharti */ 33122394Sharti#include <sys/types.h> 34122394Sharti#include <sys/queue.h> 35122394Sharti#include <stdio.h> 36122394Sharti#include <stdlib.h> 37122394Sharti#include <stddef.h> 38122394Sharti#include <stdarg.h> 39150920Sharti#ifdef HAVE_STDINT_H 40133211Sharti#include <stdint.h> 41150920Sharti#elif defined(HAVE_INTTYPES_H) 42150920Sharti#include <inttypes.h> 43150920Sharti#endif 44122394Sharti#include <string.h> 45122394Sharti 46122394Sharti#include "asn1.h" 47122394Sharti#include "snmp.h" 48122394Sharti#include "snmppriv.h" 49122394Sharti#include "snmpagent.h" 50122394Sharti 51122394Shartistatic void snmp_debug_func(const char *fmt, ...); 52122394Sharti 53122394Shartivoid (*snmp_debug)(const char *fmt, ...) = snmp_debug_func; 54122394Sharti 55122394Shartistruct snmp_node *tree; 56122394Shartiu_int tree_size; 57122394Sharti 58122394Sharti/* 59122394Sharti * Structure to hold dependencies during SET processing 60122394Sharti * The last two members of this structure must be the 61122394Sharti * dependency visible by the user and the user data. 62122394Sharti */ 63122394Shartistruct depend { 64122394Sharti TAILQ_ENTRY(depend) link; 65122394Sharti size_t len; /* size of data part */ 66122394Sharti snmp_depop_t func; 67122394Sharti struct snmp_dependency dep; 68133211Sharti#if defined(__GNUC__) && __GNUC__ < 3 69133211Sharti u_char data[0]; 70133211Sharti#else 71122394Sharti u_char data[]; 72133211Sharti#endif 73122394Sharti}; 74122394ShartiTAILQ_HEAD(depend_list, depend); 75122394Sharti 76122394Sharti/* 77122394Sharti * Set context 78122394Sharti */ 79122394Shartistruct context { 80122394Sharti struct snmp_context ctx; 81122394Sharti struct depend_list dlist; 82122394Sharti const struct snmp_node *node[SNMP_MAX_BINDINGS]; 83122394Sharti struct snmp_scratch scratch[SNMP_MAX_BINDINGS]; 84122394Sharti struct depend *depend; 85122394Sharti}; 86122394Sharti 87122394Sharti#define TR(W) (snmp_trace & SNMP_TRACE_##W) 88122394Shartiu_int snmp_trace = 0; 89122394Sharti 90122394Shartistatic char oidbuf[ASN_OIDSTRLEN]; 91122394Sharti 92122394Sharti/* 93122394Sharti * Allocate a context 94122394Sharti */ 95122394Shartistruct snmp_context * 96122394Shartisnmp_init_context(void) 97122394Sharti{ 98122394Sharti struct context *context; 99122394Sharti 100122394Sharti if ((context = malloc(sizeof(*context))) == NULL) 101122394Sharti return (NULL); 102122394Sharti 103122394Sharti memset(context, 0, sizeof(*context)); 104122394Sharti TAILQ_INIT(&context->dlist); 105122394Sharti 106122394Sharti return (&context->ctx); 107122394Sharti} 108122394Sharti 109122394Sharti/* 110122394Sharti * Find a variable for SET/GET and the first GETBULK pass. 111122394Sharti * Return the node pointer. If the search fails, set the errp to 112122394Sharti * the correct SNMPv2 GET exception code. 113122394Sharti */ 114122394Shartistatic struct snmp_node * 115122394Shartifind_node(const struct snmp_value *value, enum snmp_syntax *errp) 116122394Sharti{ 117122394Sharti struct snmp_node *tp; 118122394Sharti 119122394Sharti if (TR(FIND)) 120122394Sharti snmp_debug("find: searching %s", 121122394Sharti asn_oid2str_r(&value->var, oidbuf)); 122122394Sharti 123122394Sharti /* 124122394Sharti * If we have an exact match (the entry in the table is a 125122394Sharti * sub-oid from the variable) we have found what we are for. 126122394Sharti * If the table oid is higher than the variable, there is no match. 127122394Sharti */ 128122394Sharti for (tp = tree; tp < tree + tree_size; tp++) { 129122394Sharti if (asn_is_suboid(&tp->oid, &value->var)) 130122394Sharti goto found; 131122394Sharti if (asn_compare_oid(&tp->oid, &value->var) >= 0) 132122394Sharti break; 133122394Sharti } 134122394Sharti 135122394Sharti if (TR(FIND)) 136122394Sharti snmp_debug("find: no match"); 137122394Sharti *errp = SNMP_SYNTAX_NOSUCHOBJECT; 138122394Sharti return (NULL); 139122394Sharti 140122394Sharti found: 141122394Sharti /* leafs must have a 0 instance identifier */ 142122394Sharti if (tp->type == SNMP_NODE_LEAF && 143122394Sharti (value->var.len != tp->oid.len + 1 || 144122394Sharti value->var.subs[tp->oid.len] != 0)) { 145122394Sharti if (TR(FIND)) 146122394Sharti snmp_debug("find: bad leaf index"); 147122394Sharti *errp = SNMP_SYNTAX_NOSUCHINSTANCE; 148122394Sharti return (NULL); 149122394Sharti } 150122394Sharti if (TR(FIND)) 151122394Sharti snmp_debug("find: found %s", 152122394Sharti asn_oid2str_r(&value->var, oidbuf)); 153122394Sharti return (tp); 154122394Sharti} 155122394Sharti 156122394Shartistatic struct snmp_node * 157122394Shartifind_subnode(const struct snmp_value *value) 158122394Sharti{ 159122394Sharti struct snmp_node *tp; 160122394Sharti 161122394Sharti for (tp = tree; tp < tree + tree_size; tp++) { 162122394Sharti if (asn_is_suboid(&value->var, &tp->oid)) 163122394Sharti return (tp); 164122394Sharti } 165122394Sharti return (NULL); 166122394Sharti} 167122394Sharti 168122394Sharti/* 169122394Sharti * Execute a GET operation. The tree is rooted at the global 'root'. 170122394Sharti * Build the response PDU on the fly. If the return code is SNMP_RET_ERR 171122394Sharti * the pdu error status and index will be set. 172122394Sharti */ 173122394Shartienum snmp_ret 174122394Shartisnmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b, 175122394Sharti struct snmp_pdu *resp, void *data) 176122394Sharti{ 177122394Sharti int ret; 178122394Sharti u_int i; 179122394Sharti struct snmp_node *tp; 180122394Sharti enum snmp_syntax except; 181122394Sharti struct context context; 182122394Sharti enum asn_err err; 183122394Sharti 184122394Sharti memset(&context, 0, sizeof(context)); 185122394Sharti context.ctx.data = data; 186122394Sharti 187122394Sharti memset(resp, 0, sizeof(*resp)); 188122394Sharti strcpy(resp->community, pdu->community); 189122394Sharti resp->version = pdu->version; 190122394Sharti resp->type = SNMP_PDU_RESPONSE; 191122394Sharti resp->request_id = pdu->request_id; 192122394Sharti resp->version = pdu->version; 193122394Sharti 194122394Sharti if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK) 195122394Sharti /* cannot even encode header - very bad */ 196122394Sharti return (SNMP_RET_IGN); 197122394Sharti 198122394Sharti for (i = 0; i < pdu->nbindings; i++) { 199122394Sharti resp->bindings[i].var = pdu->bindings[i].var; 200122394Sharti if ((tp = find_node(&pdu->bindings[i], &except)) == NULL) { 201122394Sharti if (pdu->version == SNMP_V1) { 202122394Sharti if (TR(GET)) 203122394Sharti snmp_debug("get: nosuchname"); 204122394Sharti pdu->error_status = SNMP_ERR_NOSUCHNAME; 205122394Sharti pdu->error_index = i + 1; 206122394Sharti snmp_pdu_free(resp); 207122394Sharti return (SNMP_RET_ERR); 208122394Sharti } 209122394Sharti if (TR(GET)) 210122394Sharti snmp_debug("get: exception %u", except); 211122394Sharti resp->bindings[i].syntax = except; 212122394Sharti 213122394Sharti } else { 214122394Sharti /* call the action to fetch the value. */ 215122394Sharti resp->bindings[i].syntax = tp->syntax; 216122394Sharti ret = (*tp->op)(&context.ctx, &resp->bindings[i], 217122394Sharti tp->oid.len, tp->index, SNMP_OP_GET); 218122394Sharti if (TR(GET)) 219122394Sharti snmp_debug("get: action returns %d", ret); 220122394Sharti 221122394Sharti if (ret == SNMP_ERR_NOSUCHNAME) { 222122394Sharti if (pdu->version == SNMP_V1) { 223122394Sharti pdu->error_status = SNMP_ERR_NOSUCHNAME; 224122394Sharti pdu->error_index = i + 1; 225122394Sharti snmp_pdu_free(resp); 226122394Sharti return (SNMP_RET_ERR); 227122394Sharti } 228122394Sharti if (TR(GET)) 229122394Sharti snmp_debug("get: exception noSuchInstance"); 230122394Sharti resp->bindings[i].syntax = SNMP_SYNTAX_NOSUCHINSTANCE; 231122394Sharti 232122394Sharti } else if (ret != SNMP_ERR_NOERROR) { 233122394Sharti pdu->error_status = SNMP_ERR_GENERR; 234122394Sharti pdu->error_index = i + 1; 235122394Sharti snmp_pdu_free(resp); 236122394Sharti return (SNMP_RET_ERR); 237122394Sharti } 238122394Sharti } 239122394Sharti resp->nbindings++; 240122394Sharti 241122394Sharti err = snmp_binding_encode(resp_b, &resp->bindings[i]); 242122394Sharti 243122394Sharti if (err == ASN_ERR_EOBUF) { 244122394Sharti pdu->error_status = SNMP_ERR_TOOBIG; 245122394Sharti pdu->error_index = 0; 246122394Sharti snmp_pdu_free(resp); 247122394Sharti return (SNMP_RET_ERR); 248122394Sharti } 249122394Sharti if (err != ASN_ERR_OK) { 250122394Sharti if (TR(GET)) 251122394Sharti snmp_debug("get: binding encoding: %u", err); 252122394Sharti pdu->error_status = SNMP_ERR_GENERR; 253122394Sharti pdu->error_index = i + 1; 254122394Sharti snmp_pdu_free(resp); 255122394Sharti return (SNMP_RET_ERR); 256122394Sharti } 257122394Sharti } 258122394Sharti 259122394Sharti return (snmp_fix_encoding(resp_b, resp)); 260122394Sharti} 261122394Sharti 262122394Shartistatic struct snmp_node * 263122394Shartinext_node(const struct snmp_value *value, int *pnext) 264122394Sharti{ 265122394Sharti struct snmp_node *tp; 266122394Sharti 267122394Sharti if (TR(FIND)) 268122394Sharti snmp_debug("next: searching %s", 269122394Sharti asn_oid2str_r(&value->var, oidbuf)); 270122394Sharti 271122394Sharti *pnext = 0; 272122394Sharti for (tp = tree; tp < tree + tree_size; tp++) { 273122394Sharti if (asn_is_suboid(&tp->oid, &value->var)) { 274122394Sharti /* the tree OID is a sub-oid of the requested OID. */ 275122394Sharti if (tp->type == SNMP_NODE_LEAF) { 276122394Sharti if (tp->oid.len == value->var.len) { 277122394Sharti /* request for scalar type */ 278122394Sharti if (TR(FIND)) 279122394Sharti snmp_debug("next: found scalar %s", 280122394Sharti asn_oid2str_r(&tp->oid, oidbuf)); 281122394Sharti return (tp); 282122394Sharti } 283122394Sharti /* try next */ 284122394Sharti } else { 285122394Sharti if (TR(FIND)) 286122394Sharti snmp_debug("next: found column %s", 287122394Sharti asn_oid2str_r(&tp->oid, oidbuf)); 288122394Sharti return (tp); 289122394Sharti } 290122394Sharti } else if (asn_is_suboid(&value->var, &tp->oid) || 291122394Sharti asn_compare_oid(&tp->oid, &value->var) >= 0) { 292122394Sharti if (TR(FIND)) 293122394Sharti snmp_debug("next: found %s", 294122394Sharti asn_oid2str_r(&tp->oid, oidbuf)); 295122394Sharti *pnext = 1; 296122394Sharti return (tp); 297122394Sharti } 298122394Sharti } 299122394Sharti 300122394Sharti if (TR(FIND)) 301122394Sharti snmp_debug("next: failed"); 302122394Sharti 303122394Sharti return (NULL); 304122394Sharti} 305122394Sharti 306122394Shartistatic enum snmp_ret 307122394Shartido_getnext(struct context *context, const struct snmp_value *inb, 308122394Sharti struct snmp_value *outb, struct snmp_pdu *pdu) 309122394Sharti{ 310122394Sharti const struct snmp_node *tp; 311122394Sharti int ret, next; 312122394Sharti 313122394Sharti if ((tp = next_node(inb, &next)) == NULL) 314122394Sharti goto eofMib; 315122394Sharti 316122394Sharti /* retain old variable if we are doing a GETNEXT on an exact 317122394Sharti * matched leaf only */ 318122394Sharti if (tp->type == SNMP_NODE_LEAF || next) 319122394Sharti outb->var = tp->oid; 320122394Sharti else 321122394Sharti outb->var = inb->var; 322122394Sharti 323122394Sharti for (;;) { 324122394Sharti outb->syntax = tp->syntax; 325122394Sharti if (tp->type == SNMP_NODE_LEAF) { 326122394Sharti /* make a GET operation */ 327122394Sharti outb->var.subs[outb->var.len++] = 0; 328122394Sharti ret = (*tp->op)(&context->ctx, outb, tp->oid.len, 329122394Sharti tp->index, SNMP_OP_GET); 330122394Sharti } else { 331122394Sharti /* make a GETNEXT */ 332122394Sharti ret = (*tp->op)(&context->ctx, outb, tp->oid.len, 333122394Sharti tp->index, SNMP_OP_GETNEXT); 334122394Sharti } 335122394Sharti if (ret != SNMP_ERR_NOSUCHNAME) { 336122394Sharti /* got something */ 337122394Sharti if (ret != SNMP_ERR_NOERROR && TR(GETNEXT)) 338122394Sharti snmp_debug("getnext: %s returns %u", 339122394Sharti asn_oid2str(&outb->var), ret); 340122394Sharti break; 341122394Sharti } 342122394Sharti 343122394Sharti /* object has no data - try next */ 344122394Sharti if (++tp == tree + tree_size) 345122394Sharti break; 346142810Sharti 347142810Sharti if (TR(GETNEXT)) 348142810Sharti snmp_debug("getnext: no data - avancing to %s", 349142810Sharti asn_oid2str(&tp->oid)); 350142810Sharti 351122394Sharti outb->var = tp->oid; 352122394Sharti } 353122394Sharti 354122394Sharti if (ret == SNMP_ERR_NOSUCHNAME) { 355122394Sharti eofMib: 356122394Sharti outb->var = inb->var; 357122394Sharti if (pdu->version == SNMP_V1) { 358122394Sharti pdu->error_status = SNMP_ERR_NOSUCHNAME; 359122394Sharti return (SNMP_RET_ERR); 360122394Sharti } 361122394Sharti outb->syntax = SNMP_SYNTAX_ENDOFMIBVIEW; 362122394Sharti 363122394Sharti } else if (ret != SNMP_ERR_NOERROR) { 364122394Sharti pdu->error_status = SNMP_ERR_GENERR; 365122394Sharti return (SNMP_RET_ERR); 366122394Sharti } 367122394Sharti return (SNMP_RET_OK); 368122394Sharti} 369122394Sharti 370122394Sharti 371122394Sharti/* 372122394Sharti * Execute a GETNEXT operation. The tree is rooted at the global 'root'. 373122394Sharti * Build the response PDU on the fly. The return is: 374122394Sharti */ 375122394Shartienum snmp_ret 376122394Shartisnmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b, 377122394Sharti struct snmp_pdu *resp, void *data) 378122394Sharti{ 379122394Sharti struct context context; 380122394Sharti u_int i; 381122394Sharti enum asn_err err; 382122394Sharti enum snmp_ret result; 383122394Sharti 384122394Sharti memset(&context, 0, sizeof(context)); 385122394Sharti context.ctx.data = data; 386122394Sharti 387122394Sharti memset(resp, 0, sizeof(*resp)); 388122394Sharti strcpy(resp->community, pdu->community); 389122394Sharti resp->type = SNMP_PDU_RESPONSE; 390122394Sharti resp->request_id = pdu->request_id; 391122394Sharti resp->version = pdu->version; 392122394Sharti 393122394Sharti if (snmp_pdu_encode_header(resp_b, resp)) 394122394Sharti return (SNMP_RET_IGN); 395122394Sharti 396122394Sharti for (i = 0; i < pdu->nbindings; i++) { 397122394Sharti result = do_getnext(&context, &pdu->bindings[i], 398122394Sharti &resp->bindings[i], pdu); 399122394Sharti 400122394Sharti if (result != SNMP_RET_OK) { 401122394Sharti pdu->error_index = i + 1; 402122394Sharti snmp_pdu_free(resp); 403122394Sharti return (result); 404122394Sharti } 405122394Sharti 406122394Sharti resp->nbindings++; 407122394Sharti 408122394Sharti err = snmp_binding_encode(resp_b, &resp->bindings[i]); 409122394Sharti 410122394Sharti if (err == ASN_ERR_EOBUF) { 411122394Sharti pdu->error_status = SNMP_ERR_TOOBIG; 412122394Sharti pdu->error_index = 0; 413122394Sharti snmp_pdu_free(resp); 414122394Sharti return (SNMP_RET_ERR); 415122394Sharti } 416122394Sharti if (err != ASN_ERR_OK) { 417122394Sharti if (TR(GET)) 418122394Sharti snmp_debug("getnext: binding encoding: %u", err); 419122394Sharti pdu->error_status = SNMP_ERR_GENERR; 420122394Sharti pdu->error_index = i + 1; 421122394Sharti snmp_pdu_free(resp); 422122394Sharti return (SNMP_RET_ERR); 423122394Sharti } 424122394Sharti } 425122394Sharti return (snmp_fix_encoding(resp_b, resp)); 426122394Sharti} 427122394Sharti 428122394Shartienum snmp_ret 429122394Shartisnmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b, 430122394Sharti struct snmp_pdu *resp, void *data) 431122394Sharti{ 432122394Sharti struct context context; 433122394Sharti u_int i; 434122394Sharti int cnt; 435122394Sharti u_int non_rep; 436122394Sharti int eomib; 437122394Sharti enum snmp_ret result; 438122394Sharti enum asn_err err; 439122394Sharti 440122394Sharti memset(&context, 0, sizeof(context)); 441122394Sharti context.ctx.data = data; 442122394Sharti 443122394Sharti memset(resp, 0, sizeof(*resp)); 444122394Sharti strcpy(resp->community, pdu->community); 445122394Sharti resp->version = pdu->version; 446122394Sharti resp->type = SNMP_PDU_RESPONSE; 447122394Sharti resp->request_id = pdu->request_id; 448122394Sharti resp->version = pdu->version; 449122394Sharti 450122394Sharti if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK) 451122394Sharti /* cannot even encode header - very bad */ 452122394Sharti return (SNMP_RET_IGN); 453122394Sharti 454122394Sharti if ((non_rep = pdu->error_status) > pdu->nbindings) 455122394Sharti non_rep = pdu->nbindings; 456122394Sharti 457122394Sharti /* non-repeaters */ 458122394Sharti for (i = 0; i < non_rep; i++) { 459122394Sharti result = do_getnext(&context, &pdu->bindings[i], 460122394Sharti &resp->bindings[resp->nbindings], pdu); 461122394Sharti 462122394Sharti if (result != SNMP_RET_OK) { 463122394Sharti pdu->error_index = i + 1; 464122394Sharti snmp_pdu_free(resp); 465122394Sharti return (result); 466122394Sharti } 467122394Sharti 468122394Sharti err = snmp_binding_encode(resp_b, 469122394Sharti &resp->bindings[resp->nbindings++]); 470122394Sharti 471122394Sharti if (err == ASN_ERR_EOBUF) 472122394Sharti goto done; 473122394Sharti 474122394Sharti if (err != ASN_ERR_OK) { 475122394Sharti if (TR(GET)) 476122394Sharti snmp_debug("getnext: binding encoding: %u", err); 477122394Sharti pdu->error_status = SNMP_ERR_GENERR; 478122394Sharti pdu->error_index = i + 1; 479122394Sharti snmp_pdu_free(resp); 480122394Sharti return (SNMP_RET_ERR); 481122394Sharti } 482122394Sharti } 483122394Sharti 484122394Sharti if (non_rep == pdu->nbindings) 485122394Sharti goto done; 486122394Sharti 487122394Sharti /* repeates */ 488122394Sharti for (cnt = 0; cnt < pdu->error_index; cnt++) { 489122394Sharti eomib = 1; 490122394Sharti for (i = non_rep; i < pdu->nbindings; i++) { 491122394Sharti if (cnt == 0) 492122394Sharti result = do_getnext(&context, &pdu->bindings[i], 493122394Sharti &resp->bindings[resp->nbindings], pdu); 494122394Sharti else 495122394Sharti result = do_getnext(&context, 496122394Sharti &resp->bindings[resp->nbindings - 497122394Sharti (pdu->nbindings - non_rep)], 498122394Sharti &resp->bindings[resp->nbindings], pdu); 499122394Sharti 500122394Sharti if (result != SNMP_RET_OK) { 501122394Sharti pdu->error_index = i + 1; 502122394Sharti snmp_pdu_free(resp); 503122394Sharti return (result); 504122394Sharti } 505122394Sharti if (resp->bindings[resp->nbindings].syntax != 506122394Sharti SNMP_SYNTAX_ENDOFMIBVIEW) 507122394Sharti eomib = 0; 508122394Sharti 509122394Sharti err = snmp_binding_encode(resp_b, 510122394Sharti &resp->bindings[resp->nbindings++]); 511122394Sharti 512122394Sharti if (err == ASN_ERR_EOBUF) 513122394Sharti goto done; 514122394Sharti 515122394Sharti if (err != ASN_ERR_OK) { 516122394Sharti if (TR(GET)) 517122394Sharti snmp_debug("getnext: binding encoding: %u", err); 518122394Sharti pdu->error_status = SNMP_ERR_GENERR; 519122394Sharti pdu->error_index = i + 1; 520122394Sharti snmp_pdu_free(resp); 521122394Sharti return (SNMP_RET_ERR); 522122394Sharti } 523122394Sharti } 524122394Sharti if (eomib) 525122394Sharti break; 526122394Sharti } 527122394Sharti 528122394Sharti done: 529122394Sharti return (snmp_fix_encoding(resp_b, resp)); 530122394Sharti} 531122394Sharti 532122394Sharti/* 533122394Sharti * Rollback a SET operation. Failed index is 'i'. 534122394Sharti */ 535122394Shartistatic void 536122394Shartirollback(struct context *context, struct snmp_pdu *pdu, u_int i) 537122394Sharti{ 538122394Sharti struct snmp_value *b; 539122394Sharti const struct snmp_node *np; 540122394Sharti int ret; 541122394Sharti 542122394Sharti while (i-- > 0) { 543122394Sharti b = &pdu->bindings[i]; 544122394Sharti np = context->node[i]; 545122394Sharti 546122394Sharti context->ctx.scratch = &context->scratch[i]; 547122394Sharti 548122394Sharti ret = (*np->op)(&context->ctx, b, np->oid.len, np->index, 549122394Sharti SNMP_OP_ROLLBACK); 550122394Sharti 551122394Sharti if (ret != SNMP_ERR_NOERROR) { 552122394Sharti snmp_error("set: rollback failed (%d) on variable %s " 553122394Sharti "index %u", ret, asn_oid2str(&b->var), i); 554122394Sharti if (pdu->version != SNMP_V1) { 555122394Sharti pdu->error_status = SNMP_ERR_UNDO_FAILED; 556122394Sharti pdu->error_index = 0; 557122394Sharti } 558122394Sharti } 559122394Sharti } 560122394Sharti} 561122394Sharti 562122394Sharti/* 563122394Sharti * Commit dependencies. 564122394Sharti */ 565122394Shartiint 566122394Shartisnmp_dep_commit(struct snmp_context *ctx) 567122394Sharti{ 568122394Sharti struct context *context = (struct context *)ctx; 569122394Sharti int ret; 570122394Sharti 571122394Sharti TAILQ_FOREACH(context->depend, &context->dlist, link) { 572122394Sharti ctx->dep = &context->depend->dep; 573122394Sharti 574122394Sharti if (TR(SET)) 575122394Sharti snmp_debug("set: dependency commit %s", 576122394Sharti asn_oid2str(&ctx->dep->obj)); 577122394Sharti 578122394Sharti ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT); 579122394Sharti 580122394Sharti if (ret != SNMP_ERR_NOERROR) { 581122394Sharti if (TR(SET)) 582122394Sharti snmp_debug("set: dependency failed %d", ret); 583122394Sharti return (ret); 584122394Sharti } 585122394Sharti } 586122394Sharti return (SNMP_ERR_NOERROR); 587122394Sharti} 588122394Sharti 589122394Sharti/* 590122394Sharti * Rollback dependencies 591122394Sharti */ 592122394Shartiint 593122394Shartisnmp_dep_rollback(struct snmp_context *ctx) 594122394Sharti{ 595122394Sharti struct context *context = (struct context *)ctx; 596122394Sharti int ret, ret1; 597122394Sharti char objbuf[ASN_OIDSTRLEN]; 598122394Sharti char idxbuf[ASN_OIDSTRLEN]; 599122394Sharti 600122394Sharti ret1 = SNMP_ERR_NOERROR; 601122394Sharti while ((context->depend = 602122394Sharti TAILQ_PREV(context->depend, depend_list, link)) != NULL) { 603122394Sharti ctx->dep = &context->depend->dep; 604122394Sharti 605122394Sharti if (TR(SET)) 606122394Sharti snmp_debug("set: dependency rollback %s", 607122394Sharti asn_oid2str(&ctx->dep->obj)); 608122394Sharti 609122394Sharti ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK); 610122394Sharti 611122394Sharti if (ret != SNMP_ERR_NOERROR) { 612122394Sharti snmp_debug("set: dep rollback returns %u: %s %s", ret, 613122394Sharti asn_oid2str_r(&ctx->dep->obj, objbuf), 614122394Sharti asn_oid2str_r(&ctx->dep->idx, idxbuf)); 615122394Sharti if (ret1 == SNMP_ERR_NOERROR) 616122394Sharti ret1 = ret; 617122394Sharti } 618122394Sharti } 619122394Sharti return (ret1); 620122394Sharti} 621122394Sharti 622128237Shartivoid 623128237Shartisnmp_dep_finish(struct snmp_context *ctx) 624128237Sharti{ 625128237Sharti struct context *context = (struct context *)ctx; 626128237Sharti struct depend *d; 627128237Sharti 628128237Sharti while ((d = TAILQ_FIRST(&context->dlist)) != NULL) { 629128237Sharti ctx->dep = &d->dep; 630128237Sharti (void)d->func(ctx, ctx->dep, SNMP_DEPOP_FINISH); 631128237Sharti TAILQ_REMOVE(&context->dlist, d, link); 632128237Sharti free(d); 633128237Sharti } 634128237Sharti} 635128237Sharti 636122394Sharti/* 637122394Sharti * Do a SET operation. 638122394Sharti */ 639122394Shartienum snmp_ret 640122394Shartisnmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b, 641122394Sharti struct snmp_pdu *resp, void *data) 642122394Sharti{ 643122394Sharti int ret; 644122394Sharti u_int i; 645122394Sharti enum asn_err asnerr; 646122394Sharti struct context context; 647122394Sharti const struct snmp_node *np; 648122394Sharti struct snmp_value *b; 649122394Sharti enum snmp_syntax except; 650122394Sharti 651122394Sharti memset(&context, 0, sizeof(context)); 652122394Sharti TAILQ_INIT(&context.dlist); 653122394Sharti context.ctx.data = data; 654122394Sharti 655122394Sharti memset(resp, 0, sizeof(*resp)); 656122394Sharti strcpy(resp->community, pdu->community); 657122394Sharti resp->type = SNMP_PDU_RESPONSE; 658122394Sharti resp->request_id = pdu->request_id; 659122394Sharti resp->version = pdu->version; 660122394Sharti 661122394Sharti if (snmp_pdu_encode_header(resp_b, resp)) 662122394Sharti return (SNMP_RET_IGN); 663122394Sharti 664122394Sharti /* 665122394Sharti * 1. Find all nodes, check that they are writeable and 666122394Sharti * that the syntax is ok, copy over the binding to the response. 667122394Sharti */ 668122394Sharti for (i = 0; i < pdu->nbindings; i++) { 669122394Sharti b = &pdu->bindings[i]; 670122394Sharti 671122394Sharti if ((np = context.node[i] = find_node(b, &except)) == NULL) { 672122394Sharti /* not found altogether or LEAF with wrong index */ 673122394Sharti if (TR(SET)) 674122394Sharti snmp_debug("set: node not found %s", 675122394Sharti asn_oid2str_r(&b->var, oidbuf)); 676122394Sharti if (pdu->version == SNMP_V1) { 677122394Sharti pdu->error_index = i + 1; 678122394Sharti pdu->error_status = SNMP_ERR_NOSUCHNAME; 679122394Sharti } else if ((np = find_subnode(b)) != NULL) { 680122394Sharti /* 2. intermediate object */ 681122394Sharti pdu->error_index = i + 1; 682122394Sharti pdu->error_status = SNMP_ERR_NOT_WRITEABLE; 683122394Sharti } else if (except == SNMP_SYNTAX_NOSUCHOBJECT) { 684122394Sharti pdu->error_index = i + 1; 685122394Sharti pdu->error_status = SNMP_ERR_NO_ACCESS; 686122394Sharti } else { 687122394Sharti pdu->error_index = i + 1; 688122394Sharti pdu->error_status = SNMP_ERR_NO_CREATION; 689122394Sharti } 690122394Sharti snmp_pdu_free(resp); 691122394Sharti return (SNMP_RET_ERR); 692122394Sharti } 693122394Sharti /* 694122394Sharti * 2. write/createable? 695122394Sharti * Can check this for leafs only, because in v2 we have 696122394Sharti * to differentiate between NOT_WRITEABLE and NO_CREATION 697122394Sharti * and only the action routine for COLUMNS knows, whether 698122394Sharti * a column exists. 699122394Sharti */ 700122394Sharti if (np->type == SNMP_NODE_LEAF && 701122394Sharti !(np->flags & SNMP_NODE_CANSET)) { 702122394Sharti if (pdu->version == SNMP_V1) { 703122394Sharti pdu->error_index = i + 1; 704122394Sharti pdu->error_status = SNMP_ERR_NOSUCHNAME; 705122394Sharti } else { 706122394Sharti pdu->error_index = i + 1; 707122394Sharti pdu->error_status = SNMP_ERR_NOT_WRITEABLE; 708122394Sharti } 709122394Sharti snmp_pdu_free(resp); 710122394Sharti return (SNMP_RET_ERR); 711122394Sharti } 712122394Sharti /* 713122394Sharti * 3. Ensure the right syntax 714122394Sharti */ 715122394Sharti if (np->syntax != b->syntax) { 716122394Sharti if (pdu->version == SNMP_V1) { 717122394Sharti pdu->error_index = i + 1; 718122394Sharti pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */ 719122394Sharti } else { 720122394Sharti pdu->error_index = i + 1; 721122394Sharti pdu->error_status = SNMP_ERR_WRONG_TYPE; 722122394Sharti } 723122394Sharti snmp_pdu_free(resp); 724122394Sharti return (SNMP_RET_ERR); 725122394Sharti } 726122394Sharti /* 727122394Sharti * 4. Copy binding 728122394Sharti */ 729122394Sharti if (snmp_value_copy(&resp->bindings[i], b)) { 730122394Sharti pdu->error_index = i + 1; 731122394Sharti pdu->error_status = SNMP_ERR_GENERR; 732122394Sharti snmp_pdu_free(resp); 733122394Sharti return (SNMP_RET_ERR); 734122394Sharti } 735122394Sharti asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]); 736122394Sharti if (asnerr == ASN_ERR_EOBUF) { 737122394Sharti pdu->error_index = i + 1; 738122394Sharti pdu->error_status = SNMP_ERR_TOOBIG; 739122394Sharti snmp_pdu_free(resp); 740122394Sharti return (SNMP_RET_ERR); 741122394Sharti } else if (asnerr != ASN_ERR_OK) { 742122394Sharti pdu->error_index = i + 1; 743122394Sharti pdu->error_status = SNMP_ERR_GENERR; 744122394Sharti snmp_pdu_free(resp); 745122394Sharti return (SNMP_RET_ERR); 746122394Sharti } 747122394Sharti resp->nbindings++; 748122394Sharti } 749122394Sharti 750128237Sharti context.ctx.code = SNMP_RET_OK; 751122394Sharti 752122394Sharti /* 753122394Sharti * 2. Call the SET method for each node. If a SET fails, rollback 754122394Sharti * everything. Map error codes depending on the version. 755122394Sharti */ 756122394Sharti for (i = 0; i < pdu->nbindings; i++) { 757122394Sharti b = &pdu->bindings[i]; 758122394Sharti np = context.node[i]; 759122394Sharti 760122394Sharti context.ctx.var_index = i + 1; 761122394Sharti context.ctx.scratch = &context.scratch[i]; 762122394Sharti 763122394Sharti ret = (*np->op)(&context.ctx, b, np->oid.len, np->index, 764122394Sharti SNMP_OP_SET); 765122394Sharti 766122394Sharti if (TR(SET)) 767122394Sharti snmp_debug("set: action %s returns %d", np->name, ret); 768122394Sharti 769122394Sharti if (pdu->version == SNMP_V1) { 770122394Sharti switch (ret) { 771122394Sharti case SNMP_ERR_NO_ACCESS: 772122394Sharti ret = SNMP_ERR_NOSUCHNAME; 773122394Sharti break; 774122394Sharti case SNMP_ERR_WRONG_TYPE: 775122394Sharti /* should no happen */ 776122394Sharti ret = SNMP_ERR_BADVALUE; 777122394Sharti break; 778122394Sharti case SNMP_ERR_WRONG_LENGTH: 779122394Sharti ret = SNMP_ERR_BADVALUE; 780122394Sharti break; 781122394Sharti case SNMP_ERR_WRONG_ENCODING: 782122394Sharti /* should not happen */ 783122394Sharti ret = SNMP_ERR_BADVALUE; 784122394Sharti break; 785122394Sharti case SNMP_ERR_WRONG_VALUE: 786122394Sharti ret = SNMP_ERR_BADVALUE; 787122394Sharti break; 788122394Sharti case SNMP_ERR_NO_CREATION: 789122394Sharti ret = SNMP_ERR_NOSUCHNAME; 790122394Sharti break; 791122394Sharti case SNMP_ERR_INCONS_VALUE: 792122394Sharti ret = SNMP_ERR_BADVALUE; 793122394Sharti break; 794122394Sharti case SNMP_ERR_RES_UNAVAIL: 795122394Sharti ret = SNMP_ERR_GENERR; 796122394Sharti break; 797122394Sharti case SNMP_ERR_COMMIT_FAILED: 798122394Sharti ret = SNMP_ERR_GENERR; 799122394Sharti break; 800122394Sharti case SNMP_ERR_UNDO_FAILED: 801122394Sharti ret = SNMP_ERR_GENERR; 802122394Sharti break; 803122394Sharti case SNMP_ERR_AUTH_ERR: 804122394Sharti /* should not happen */ 805122394Sharti ret = SNMP_ERR_GENERR; 806122394Sharti break; 807122394Sharti case SNMP_ERR_NOT_WRITEABLE: 808122394Sharti ret = SNMP_ERR_NOSUCHNAME; 809122394Sharti break; 810122394Sharti case SNMP_ERR_INCONS_NAME: 811122394Sharti ret = SNMP_ERR_BADVALUE; 812122394Sharti break; 813122394Sharti } 814122394Sharti } 815122394Sharti if (ret != SNMP_ERR_NOERROR) { 816122394Sharti pdu->error_index = i + 1; 817122394Sharti pdu->error_status = ret; 818122394Sharti 819122394Sharti rollback(&context, pdu, i); 820122394Sharti snmp_pdu_free(resp); 821122394Sharti 822128237Sharti context.ctx.code = SNMP_RET_ERR; 823122394Sharti 824122394Sharti goto errout; 825122394Sharti } 826122394Sharti } 827122394Sharti 828122394Sharti /* 829122394Sharti * 3. Call dependencies 830122394Sharti */ 831122394Sharti if (TR(SET)) 832122394Sharti snmp_debug("set: set operations ok"); 833122394Sharti 834122394Sharti if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) { 835122394Sharti pdu->error_status = ret; 836122394Sharti pdu->error_index = context.ctx.var_index; 837122394Sharti 838122394Sharti if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) { 839122394Sharti if (pdu->version != SNMP_V1) { 840122394Sharti pdu->error_status = SNMP_ERR_UNDO_FAILED; 841122394Sharti pdu->error_index = 0; 842122394Sharti } 843122394Sharti } 844122394Sharti rollback(&context, pdu, i); 845122394Sharti snmp_pdu_free(resp); 846122394Sharti 847128237Sharti context.ctx.code = SNMP_RET_ERR; 848122394Sharti 849122394Sharti goto errout; 850122394Sharti } 851122394Sharti 852122394Sharti /* 853122394Sharti * 4. Commit and copy values from the original packet to the response. 854122394Sharti * This is not the commit operation from RFC 1905 but rather an 855122394Sharti * 'FREE RESOURCES' operation. It shouldn't fail. 856122394Sharti */ 857122394Sharti if (TR(SET)) 858122394Sharti snmp_debug("set: commiting"); 859122394Sharti 860122394Sharti for (i = 0; i < pdu->nbindings; i++) { 861122394Sharti b = &resp->bindings[i]; 862122394Sharti np = context.node[i]; 863122394Sharti 864122394Sharti context.ctx.var_index = i + 1; 865122394Sharti context.ctx.scratch = &context.scratch[i]; 866122394Sharti 867122394Sharti ret = (*np->op)(&context.ctx, b, np->oid.len, np->index, 868122394Sharti SNMP_OP_COMMIT); 869122394Sharti 870122394Sharti if (ret != SNMP_ERR_NOERROR) 871122394Sharti snmp_error("set: commit failed (%d) on" 872122394Sharti " variable %s index %u", ret, 873122394Sharti asn_oid2str_r(&b->var, oidbuf), i); 874122394Sharti } 875122394Sharti 876122394Sharti if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { 877122394Sharti snmp_error("set: fix_encoding failed"); 878122394Sharti snmp_pdu_free(resp); 879128237Sharti context.ctx.code = SNMP_RET_IGN; 880122394Sharti } 881122394Sharti 882122394Sharti /* 883122394Sharti * Done 884122394Sharti */ 885122394Sharti errout: 886128237Sharti snmp_dep_finish(&context.ctx); 887122394Sharti 888122394Sharti if (TR(SET)) 889128237Sharti snmp_debug("set: returning %d", context.ctx.code); 890122394Sharti 891128237Sharti return (context.ctx.code); 892122394Sharti} 893122394Sharti/* 894122394Sharti * Lookup a dependency. If it doesn't exist, create one 895122394Sharti */ 896122394Shartistruct snmp_dependency * 897122394Shartisnmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj, 898122394Sharti const struct asn_oid *idx, size_t len, snmp_depop_t func) 899122394Sharti{ 900122394Sharti struct context *context; 901122394Sharti struct depend *d; 902122394Sharti 903122394Sharti context = (struct context *)(void *) 904122394Sharti ((char *)ctx - offsetof(struct context, ctx)); 905122394Sharti if (TR(DEPEND)) { 906122394Sharti snmp_debug("depend: looking for %s", asn_oid2str(obj)); 907122394Sharti if (idx) 908122394Sharti snmp_debug("depend: index is %s", asn_oid2str(idx)); 909122394Sharti } 910122394Sharti TAILQ_FOREACH(d, &context->dlist, link) 911122394Sharti if (asn_compare_oid(obj, &d->dep.obj) == 0 && 912122394Sharti ((idx == NULL && d->dep.idx.len == 0) || 913122394Sharti (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) { 914122394Sharti if(TR(DEPEND)) 915122394Sharti snmp_debug("depend: found"); 916122394Sharti return (&d->dep); 917122394Sharti } 918122394Sharti 919122394Sharti if(TR(DEPEND)) 920122394Sharti snmp_debug("depend: creating"); 921122394Sharti 922122394Sharti if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL) 923122394Sharti return (NULL); 924122394Sharti memset(&d->dep, 0, len); 925122394Sharti 926122394Sharti d->dep.obj = *obj; 927122394Sharti if (idx == NULL) 928122394Sharti d->dep.idx.len = 0; 929122394Sharti else 930122394Sharti d->dep.idx = *idx; 931122394Sharti d->len = len; 932122394Sharti d->func = func; 933122394Sharti 934122394Sharti TAILQ_INSERT_TAIL(&context->dlist, d, link); 935122394Sharti 936122394Sharti return (&d->dep); 937122394Sharti} 938122394Sharti 939122394Sharti/* 940122394Sharti * Make an error response from a PDU. We do this without decoding the 941122394Sharti * variable bindings. This means we can sent the junk back to a caller 942122394Sharti * that has sent us junk in the first place. 943122394Sharti */ 944122394Shartienum snmp_ret 945122394Shartisnmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b, 946122394Sharti struct asn_buf *resp_b) 947122394Sharti{ 948122394Sharti asn_len_t len; 949122394Sharti struct snmp_pdu resp; 950122394Sharti enum asn_err err; 951122394Sharti enum snmp_code code; 952122394Sharti 953122394Sharti memset(&resp, 0, sizeof(resp)); 954122394Sharti 955122394Sharti /* Message sequence */ 956122394Sharti if (asn_get_sequence(pdu_b, &len) != ASN_ERR_OK) 957122394Sharti return (SNMP_RET_IGN); 958122394Sharti if (pdu_b->asn_len < len) 959122394Sharti return (SNMP_RET_IGN); 960122394Sharti 961122394Sharti err = snmp_parse_message_hdr(pdu_b, &resp, &len); 962122394Sharti if (ASN_ERR_STOPPED(err)) 963122394Sharti return (SNMP_RET_IGN); 964122394Sharti if (pdu_b->asn_len < len) 965122394Sharti return (SNMP_RET_IGN); 966122394Sharti pdu_b->asn_len = len; 967122394Sharti 968122394Sharti err = snmp_parse_pdus_hdr(pdu_b, &resp, &len); 969122394Sharti if (ASN_ERR_STOPPED(err)) 970122394Sharti return (SNMP_RET_IGN); 971122394Sharti if (pdu_b->asn_len < len) 972122394Sharti return (SNMP_RET_IGN); 973122394Sharti pdu_b->asn_len = len; 974122394Sharti 975122394Sharti /* now we have the bindings left - construct new message */ 976122394Sharti resp.error_status = pdu->error_status; 977122394Sharti resp.error_index = pdu->error_index; 978122394Sharti resp.type = SNMP_PDU_RESPONSE; 979122394Sharti 980122394Sharti code = snmp_pdu_encode_header(resp_b, &resp); 981122394Sharti if (code != SNMP_CODE_OK) 982122394Sharti return (SNMP_RET_IGN); 983122394Sharti 984122394Sharti if (pdu_b->asn_len > resp_b->asn_len) 985122394Sharti /* too short */ 986122394Sharti return (SNMP_RET_IGN); 987122394Sharti (void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len); 988122394Sharti resp_b->asn_len -= pdu_b->asn_len; 989122394Sharti resp_b->asn_ptr += pdu_b->asn_len; 990122394Sharti 991122394Sharti code = snmp_fix_encoding(resp_b, &resp); 992122394Sharti if (code != SNMP_CODE_OK) 993122394Sharti return (SNMP_RET_IGN); 994122394Sharti 995122394Sharti return (SNMP_RET_OK); 996122394Sharti} 997122394Sharti 998122394Shartistatic void 999122394Shartisnmp_debug_func(const char *fmt, ...) 1000122394Sharti{ 1001122394Sharti va_list ap; 1002122394Sharti 1003122394Sharti va_start(ap, fmt); 1004122394Sharti vfprintf(stderr, fmt, ap); 1005122394Sharti va_end(ap); 1006122394Sharti fprintf(stderr, "\n"); 1007122394Sharti} 1008