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 168216294Ssyrinxstatic void 169216294Ssyrinxsnmp_pdu_create_response(struct snmp_pdu *pdu, struct snmp_pdu *resp) 170216294Ssyrinx{ 171216294Ssyrinx memset(resp, 0, sizeof(*resp)); 172216294Ssyrinx strcpy(resp->community, pdu->community); 173216294Ssyrinx resp->version = pdu->version; 174216294Ssyrinx resp->type = SNMP_PDU_RESPONSE; 175216294Ssyrinx resp->request_id = pdu->request_id; 176216294Ssyrinx resp->version = pdu->version; 177216294Ssyrinx 178216294Ssyrinx if (resp->version != SNMP_V3) 179216294Ssyrinx return; 180216294Ssyrinx 181216594Ssyrinx memcpy(&resp->engine, &pdu->engine, sizeof(pdu->engine)); 182216594Ssyrinx memcpy(&resp->user, &pdu->user, sizeof(pdu->user)); 183216594Ssyrinx snmp_pdu_init_secparams(resp); 184216294Ssyrinx resp->identifier = pdu->identifier; 185216294Ssyrinx resp->security_model = pdu->security_model; 186216294Ssyrinx resp->context_engine_len = pdu->context_engine_len; 187216294Ssyrinx memcpy(resp->context_engine, pdu->context_engine, 188216294Ssyrinx resp->context_engine_len); 189216294Ssyrinx strlcpy(resp->context_name, pdu->context_name, 190216294Ssyrinx sizeof(resp->context_name)); 191216294Ssyrinx} 192216294Ssyrinx 193122394Sharti/* 194122394Sharti * Execute a GET operation. The tree is rooted at the global 'root'. 195122394Sharti * Build the response PDU on the fly. If the return code is SNMP_RET_ERR 196122394Sharti * the pdu error status and index will be set. 197122394Sharti */ 198122394Shartienum snmp_ret 199122394Shartisnmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b, 200122394Sharti struct snmp_pdu *resp, void *data) 201122394Sharti{ 202122394Sharti int ret; 203122394Sharti u_int i; 204122394Sharti struct snmp_node *tp; 205122394Sharti enum snmp_syntax except; 206122394Sharti struct context context; 207122394Sharti enum asn_err err; 208122394Sharti 209122394Sharti memset(&context, 0, sizeof(context)); 210122394Sharti context.ctx.data = data; 211122394Sharti 212216294Ssyrinx snmp_pdu_create_response(pdu, resp); 213122394Sharti 214122394Sharti if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK) 215122394Sharti /* cannot even encode header - very bad */ 216122394Sharti return (SNMP_RET_IGN); 217122394Sharti 218122394Sharti for (i = 0; i < pdu->nbindings; i++) { 219122394Sharti resp->bindings[i].var = pdu->bindings[i].var; 220122394Sharti if ((tp = find_node(&pdu->bindings[i], &except)) == NULL) { 221122394Sharti if (pdu->version == SNMP_V1) { 222122394Sharti if (TR(GET)) 223122394Sharti snmp_debug("get: nosuchname"); 224122394Sharti pdu->error_status = SNMP_ERR_NOSUCHNAME; 225122394Sharti pdu->error_index = i + 1; 226122394Sharti snmp_pdu_free(resp); 227122394Sharti return (SNMP_RET_ERR); 228122394Sharti } 229122394Sharti if (TR(GET)) 230122394Sharti snmp_debug("get: exception %u", except); 231122394Sharti resp->bindings[i].syntax = except; 232122394Sharti 233122394Sharti } else { 234122394Sharti /* call the action to fetch the value. */ 235122394Sharti resp->bindings[i].syntax = tp->syntax; 236122394Sharti ret = (*tp->op)(&context.ctx, &resp->bindings[i], 237122394Sharti tp->oid.len, tp->index, SNMP_OP_GET); 238122394Sharti if (TR(GET)) 239122394Sharti snmp_debug("get: action returns %d", ret); 240122394Sharti 241122394Sharti if (ret == SNMP_ERR_NOSUCHNAME) { 242122394Sharti if (pdu->version == SNMP_V1) { 243122394Sharti pdu->error_status = SNMP_ERR_NOSUCHNAME; 244122394Sharti pdu->error_index = i + 1; 245122394Sharti snmp_pdu_free(resp); 246122394Sharti return (SNMP_RET_ERR); 247122394Sharti } 248122394Sharti if (TR(GET)) 249122394Sharti snmp_debug("get: exception noSuchInstance"); 250122394Sharti resp->bindings[i].syntax = SNMP_SYNTAX_NOSUCHINSTANCE; 251122394Sharti 252122394Sharti } else if (ret != SNMP_ERR_NOERROR) { 253122394Sharti pdu->error_status = SNMP_ERR_GENERR; 254122394Sharti pdu->error_index = i + 1; 255122394Sharti snmp_pdu_free(resp); 256122394Sharti return (SNMP_RET_ERR); 257122394Sharti } 258122394Sharti } 259122394Sharti resp->nbindings++; 260122394Sharti 261122394Sharti err = snmp_binding_encode(resp_b, &resp->bindings[i]); 262122394Sharti 263122394Sharti if (err == ASN_ERR_EOBUF) { 264122394Sharti pdu->error_status = SNMP_ERR_TOOBIG; 265122394Sharti pdu->error_index = 0; 266122394Sharti snmp_pdu_free(resp); 267122394Sharti return (SNMP_RET_ERR); 268122394Sharti } 269122394Sharti if (err != ASN_ERR_OK) { 270122394Sharti if (TR(GET)) 271122394Sharti snmp_debug("get: binding encoding: %u", err); 272122394Sharti pdu->error_status = SNMP_ERR_GENERR; 273122394Sharti pdu->error_index = i + 1; 274122394Sharti snmp_pdu_free(resp); 275122394Sharti return (SNMP_RET_ERR); 276122394Sharti } 277122394Sharti } 278122394Sharti 279122394Sharti return (snmp_fix_encoding(resp_b, resp)); 280122394Sharti} 281122394Sharti 282122394Shartistatic struct snmp_node * 283122394Shartinext_node(const struct snmp_value *value, int *pnext) 284122394Sharti{ 285122394Sharti struct snmp_node *tp; 286122394Sharti 287122394Sharti if (TR(FIND)) 288122394Sharti snmp_debug("next: searching %s", 289122394Sharti asn_oid2str_r(&value->var, oidbuf)); 290122394Sharti 291122394Sharti *pnext = 0; 292122394Sharti for (tp = tree; tp < tree + tree_size; tp++) { 293122394Sharti if (asn_is_suboid(&tp->oid, &value->var)) { 294122394Sharti /* the tree OID is a sub-oid of the requested OID. */ 295122394Sharti if (tp->type == SNMP_NODE_LEAF) { 296122394Sharti if (tp->oid.len == value->var.len) { 297122394Sharti /* request for scalar type */ 298122394Sharti if (TR(FIND)) 299122394Sharti snmp_debug("next: found scalar %s", 300122394Sharti asn_oid2str_r(&tp->oid, oidbuf)); 301122394Sharti return (tp); 302122394Sharti } 303122394Sharti /* try next */ 304122394Sharti } else { 305122394Sharti if (TR(FIND)) 306122394Sharti snmp_debug("next: found column %s", 307122394Sharti asn_oid2str_r(&tp->oid, oidbuf)); 308122394Sharti return (tp); 309122394Sharti } 310122394Sharti } else if (asn_is_suboid(&value->var, &tp->oid) || 311122394Sharti asn_compare_oid(&tp->oid, &value->var) >= 0) { 312122394Sharti if (TR(FIND)) 313122394Sharti snmp_debug("next: found %s", 314122394Sharti asn_oid2str_r(&tp->oid, oidbuf)); 315122394Sharti *pnext = 1; 316122394Sharti return (tp); 317122394Sharti } 318122394Sharti } 319122394Sharti 320122394Sharti if (TR(FIND)) 321122394Sharti snmp_debug("next: failed"); 322122394Sharti 323122394Sharti return (NULL); 324122394Sharti} 325122394Sharti 326122394Shartistatic enum snmp_ret 327122394Shartido_getnext(struct context *context, const struct snmp_value *inb, 328122394Sharti struct snmp_value *outb, struct snmp_pdu *pdu) 329122394Sharti{ 330122394Sharti const struct snmp_node *tp; 331122394Sharti int ret, next; 332122394Sharti 333122394Sharti if ((tp = next_node(inb, &next)) == NULL) 334122394Sharti goto eofMib; 335122394Sharti 336122394Sharti /* retain old variable if we are doing a GETNEXT on an exact 337122394Sharti * matched leaf only */ 338122394Sharti if (tp->type == SNMP_NODE_LEAF || next) 339122394Sharti outb->var = tp->oid; 340122394Sharti else 341122394Sharti outb->var = inb->var; 342122394Sharti 343122394Sharti for (;;) { 344122394Sharti outb->syntax = tp->syntax; 345122394Sharti if (tp->type == SNMP_NODE_LEAF) { 346122394Sharti /* make a GET operation */ 347122394Sharti outb->var.subs[outb->var.len++] = 0; 348122394Sharti ret = (*tp->op)(&context->ctx, outb, tp->oid.len, 349122394Sharti tp->index, SNMP_OP_GET); 350122394Sharti } else { 351122394Sharti /* make a GETNEXT */ 352122394Sharti ret = (*tp->op)(&context->ctx, outb, tp->oid.len, 353122394Sharti tp->index, SNMP_OP_GETNEXT); 354122394Sharti } 355122394Sharti if (ret != SNMP_ERR_NOSUCHNAME) { 356122394Sharti /* got something */ 357122394Sharti if (ret != SNMP_ERR_NOERROR && TR(GETNEXT)) 358122394Sharti snmp_debug("getnext: %s returns %u", 359122394Sharti asn_oid2str(&outb->var), ret); 360122394Sharti break; 361122394Sharti } 362122394Sharti 363122394Sharti /* object has no data - try next */ 364122394Sharti if (++tp == tree + tree_size) 365122394Sharti break; 366142810Sharti 367142810Sharti if (TR(GETNEXT)) 368142810Sharti snmp_debug("getnext: no data - avancing to %s", 369142810Sharti asn_oid2str(&tp->oid)); 370142810Sharti 371122394Sharti outb->var = tp->oid; 372122394Sharti } 373122394Sharti 374122394Sharti if (ret == SNMP_ERR_NOSUCHNAME) { 375122394Sharti eofMib: 376122394Sharti outb->var = inb->var; 377122394Sharti if (pdu->version == SNMP_V1) { 378122394Sharti pdu->error_status = SNMP_ERR_NOSUCHNAME; 379122394Sharti return (SNMP_RET_ERR); 380122394Sharti } 381122394Sharti outb->syntax = SNMP_SYNTAX_ENDOFMIBVIEW; 382122394Sharti 383122394Sharti } else if (ret != SNMP_ERR_NOERROR) { 384122394Sharti pdu->error_status = SNMP_ERR_GENERR; 385122394Sharti return (SNMP_RET_ERR); 386122394Sharti } 387122394Sharti return (SNMP_RET_OK); 388122394Sharti} 389122394Sharti 390122394Sharti 391122394Sharti/* 392122394Sharti * Execute a GETNEXT operation. The tree is rooted at the global 'root'. 393122394Sharti * Build the response PDU on the fly. The return is: 394122394Sharti */ 395122394Shartienum snmp_ret 396122394Shartisnmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b, 397122394Sharti struct snmp_pdu *resp, void *data) 398122394Sharti{ 399122394Sharti struct context context; 400122394Sharti u_int i; 401122394Sharti enum asn_err err; 402122394Sharti enum snmp_ret result; 403122394Sharti 404122394Sharti memset(&context, 0, sizeof(context)); 405122394Sharti context.ctx.data = data; 406122394Sharti 407216294Ssyrinx snmp_pdu_create_response(pdu, resp); 408122394Sharti 409122394Sharti if (snmp_pdu_encode_header(resp_b, resp)) 410122394Sharti return (SNMP_RET_IGN); 411122394Sharti 412122394Sharti for (i = 0; i < pdu->nbindings; i++) { 413122394Sharti result = do_getnext(&context, &pdu->bindings[i], 414122394Sharti &resp->bindings[i], pdu); 415122394Sharti 416122394Sharti if (result != SNMP_RET_OK) { 417122394Sharti pdu->error_index = i + 1; 418122394Sharti snmp_pdu_free(resp); 419122394Sharti return (result); 420122394Sharti } 421122394Sharti 422122394Sharti resp->nbindings++; 423122394Sharti 424122394Sharti err = snmp_binding_encode(resp_b, &resp->bindings[i]); 425122394Sharti 426122394Sharti if (err == ASN_ERR_EOBUF) { 427122394Sharti pdu->error_status = SNMP_ERR_TOOBIG; 428122394Sharti pdu->error_index = 0; 429122394Sharti snmp_pdu_free(resp); 430122394Sharti return (SNMP_RET_ERR); 431122394Sharti } 432122394Sharti if (err != ASN_ERR_OK) { 433122394Sharti if (TR(GET)) 434122394Sharti snmp_debug("getnext: binding encoding: %u", err); 435122394Sharti pdu->error_status = SNMP_ERR_GENERR; 436122394Sharti pdu->error_index = i + 1; 437122394Sharti snmp_pdu_free(resp); 438122394Sharti return (SNMP_RET_ERR); 439122394Sharti } 440122394Sharti } 441122394Sharti return (snmp_fix_encoding(resp_b, resp)); 442122394Sharti} 443122394Sharti 444122394Shartienum snmp_ret 445122394Shartisnmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b, 446122394Sharti struct snmp_pdu *resp, void *data) 447122394Sharti{ 448122394Sharti struct context context; 449122394Sharti u_int i; 450122394Sharti int cnt; 451122394Sharti u_int non_rep; 452122394Sharti int eomib; 453122394Sharti enum snmp_ret result; 454122394Sharti enum asn_err err; 455122394Sharti 456122394Sharti memset(&context, 0, sizeof(context)); 457122394Sharti context.ctx.data = data; 458122394Sharti 459216294Ssyrinx snmp_pdu_create_response(pdu, resp); 460122394Sharti 461122394Sharti if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK) 462122394Sharti /* cannot even encode header - very bad */ 463122394Sharti return (SNMP_RET_IGN); 464122394Sharti 465122394Sharti if ((non_rep = pdu->error_status) > pdu->nbindings) 466122394Sharti non_rep = pdu->nbindings; 467122394Sharti 468122394Sharti /* non-repeaters */ 469122394Sharti for (i = 0; i < non_rep; i++) { 470122394Sharti result = do_getnext(&context, &pdu->bindings[i], 471122394Sharti &resp->bindings[resp->nbindings], pdu); 472122394Sharti 473122394Sharti if (result != SNMP_RET_OK) { 474122394Sharti pdu->error_index = i + 1; 475122394Sharti snmp_pdu_free(resp); 476122394Sharti return (result); 477122394Sharti } 478122394Sharti 479122394Sharti err = snmp_binding_encode(resp_b, 480122394Sharti &resp->bindings[resp->nbindings++]); 481122394Sharti 482122394Sharti if (err == ASN_ERR_EOBUF) 483122394Sharti goto done; 484122394Sharti 485122394Sharti if (err != ASN_ERR_OK) { 486122394Sharti if (TR(GET)) 487122394Sharti snmp_debug("getnext: binding encoding: %u", err); 488122394Sharti pdu->error_status = SNMP_ERR_GENERR; 489122394Sharti pdu->error_index = i + 1; 490122394Sharti snmp_pdu_free(resp); 491122394Sharti return (SNMP_RET_ERR); 492122394Sharti } 493122394Sharti } 494122394Sharti 495122394Sharti if (non_rep == pdu->nbindings) 496122394Sharti goto done; 497122394Sharti 498122394Sharti /* repeates */ 499122394Sharti for (cnt = 0; cnt < pdu->error_index; cnt++) { 500122394Sharti eomib = 1; 501122394Sharti for (i = non_rep; i < pdu->nbindings; i++) { 502260638Sdelphij 503260638Sdelphij if (resp->nbindings == SNMP_MAX_BINDINGS) 504260638Sdelphij /* PDU is full */ 505260638Sdelphij goto done; 506260638Sdelphij 507122394Sharti if (cnt == 0) 508122394Sharti result = do_getnext(&context, &pdu->bindings[i], 509122394Sharti &resp->bindings[resp->nbindings], pdu); 510122394Sharti else 511122394Sharti result = do_getnext(&context, 512122394Sharti &resp->bindings[resp->nbindings - 513122394Sharti (pdu->nbindings - non_rep)], 514122394Sharti &resp->bindings[resp->nbindings], pdu); 515122394Sharti 516122394Sharti if (result != SNMP_RET_OK) { 517122394Sharti pdu->error_index = i + 1; 518122394Sharti snmp_pdu_free(resp); 519122394Sharti return (result); 520122394Sharti } 521122394Sharti if (resp->bindings[resp->nbindings].syntax != 522122394Sharti SNMP_SYNTAX_ENDOFMIBVIEW) 523122394Sharti eomib = 0; 524122394Sharti 525122394Sharti err = snmp_binding_encode(resp_b, 526122394Sharti &resp->bindings[resp->nbindings++]); 527122394Sharti 528122394Sharti if (err == ASN_ERR_EOBUF) 529122394Sharti goto done; 530122394Sharti 531122394Sharti if (err != ASN_ERR_OK) { 532122394Sharti if (TR(GET)) 533122394Sharti snmp_debug("getnext: binding encoding: %u", err); 534122394Sharti pdu->error_status = SNMP_ERR_GENERR; 535122394Sharti pdu->error_index = i + 1; 536122394Sharti snmp_pdu_free(resp); 537122394Sharti return (SNMP_RET_ERR); 538122394Sharti } 539122394Sharti } 540122394Sharti if (eomib) 541122394Sharti break; 542122394Sharti } 543122394Sharti 544122394Sharti done: 545122394Sharti return (snmp_fix_encoding(resp_b, resp)); 546122394Sharti} 547122394Sharti 548122394Sharti/* 549122394Sharti * Rollback a SET operation. Failed index is 'i'. 550122394Sharti */ 551122394Shartistatic void 552122394Shartirollback(struct context *context, struct snmp_pdu *pdu, u_int i) 553122394Sharti{ 554122394Sharti struct snmp_value *b; 555122394Sharti const struct snmp_node *np; 556122394Sharti int ret; 557122394Sharti 558122394Sharti while (i-- > 0) { 559122394Sharti b = &pdu->bindings[i]; 560122394Sharti np = context->node[i]; 561122394Sharti 562122394Sharti context->ctx.scratch = &context->scratch[i]; 563122394Sharti 564122394Sharti ret = (*np->op)(&context->ctx, b, np->oid.len, np->index, 565122394Sharti SNMP_OP_ROLLBACK); 566122394Sharti 567122394Sharti if (ret != SNMP_ERR_NOERROR) { 568122394Sharti snmp_error("set: rollback failed (%d) on variable %s " 569122394Sharti "index %u", ret, asn_oid2str(&b->var), i); 570122394Sharti if (pdu->version != SNMP_V1) { 571122394Sharti pdu->error_status = SNMP_ERR_UNDO_FAILED; 572122394Sharti pdu->error_index = 0; 573122394Sharti } 574122394Sharti } 575122394Sharti } 576122394Sharti} 577122394Sharti 578122394Sharti/* 579122394Sharti * Commit dependencies. 580122394Sharti */ 581122394Shartiint 582122394Shartisnmp_dep_commit(struct snmp_context *ctx) 583122394Sharti{ 584122394Sharti struct context *context = (struct context *)ctx; 585122394Sharti int ret; 586122394Sharti 587122394Sharti TAILQ_FOREACH(context->depend, &context->dlist, link) { 588122394Sharti ctx->dep = &context->depend->dep; 589122394Sharti 590122394Sharti if (TR(SET)) 591122394Sharti snmp_debug("set: dependency commit %s", 592122394Sharti asn_oid2str(&ctx->dep->obj)); 593122394Sharti 594122394Sharti ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT); 595122394Sharti 596122394Sharti if (ret != SNMP_ERR_NOERROR) { 597122394Sharti if (TR(SET)) 598122394Sharti snmp_debug("set: dependency failed %d", ret); 599122394Sharti return (ret); 600122394Sharti } 601122394Sharti } 602122394Sharti return (SNMP_ERR_NOERROR); 603122394Sharti} 604122394Sharti 605122394Sharti/* 606122394Sharti * Rollback dependencies 607122394Sharti */ 608122394Shartiint 609122394Shartisnmp_dep_rollback(struct snmp_context *ctx) 610122394Sharti{ 611122394Sharti struct context *context = (struct context *)ctx; 612122394Sharti int ret, ret1; 613122394Sharti char objbuf[ASN_OIDSTRLEN]; 614122394Sharti char idxbuf[ASN_OIDSTRLEN]; 615122394Sharti 616122394Sharti ret1 = SNMP_ERR_NOERROR; 617122394Sharti while ((context->depend = 618122394Sharti TAILQ_PREV(context->depend, depend_list, link)) != NULL) { 619122394Sharti ctx->dep = &context->depend->dep; 620122394Sharti 621122394Sharti if (TR(SET)) 622122394Sharti snmp_debug("set: dependency rollback %s", 623122394Sharti asn_oid2str(&ctx->dep->obj)); 624122394Sharti 625122394Sharti ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK); 626122394Sharti 627122394Sharti if (ret != SNMP_ERR_NOERROR) { 628122394Sharti snmp_debug("set: dep rollback returns %u: %s %s", ret, 629122394Sharti asn_oid2str_r(&ctx->dep->obj, objbuf), 630122394Sharti asn_oid2str_r(&ctx->dep->idx, idxbuf)); 631122394Sharti if (ret1 == SNMP_ERR_NOERROR) 632122394Sharti ret1 = ret; 633122394Sharti } 634122394Sharti } 635122394Sharti return (ret1); 636122394Sharti} 637122394Sharti 638128237Shartivoid 639128237Shartisnmp_dep_finish(struct snmp_context *ctx) 640128237Sharti{ 641128237Sharti struct context *context = (struct context *)ctx; 642128237Sharti struct depend *d; 643128237Sharti 644128237Sharti while ((d = TAILQ_FIRST(&context->dlist)) != NULL) { 645128237Sharti ctx->dep = &d->dep; 646128237Sharti (void)d->func(ctx, ctx->dep, SNMP_DEPOP_FINISH); 647128237Sharti TAILQ_REMOVE(&context->dlist, d, link); 648128237Sharti free(d); 649128237Sharti } 650128237Sharti} 651128237Sharti 652122394Sharti/* 653122394Sharti * Do a SET operation. 654122394Sharti */ 655122394Shartienum snmp_ret 656122394Shartisnmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b, 657122394Sharti struct snmp_pdu *resp, void *data) 658122394Sharti{ 659122394Sharti int ret; 660122394Sharti u_int i; 661122394Sharti enum asn_err asnerr; 662122394Sharti struct context context; 663122394Sharti const struct snmp_node *np; 664122394Sharti struct snmp_value *b; 665122394Sharti enum snmp_syntax except; 666122394Sharti 667122394Sharti memset(&context, 0, sizeof(context)); 668122394Sharti TAILQ_INIT(&context.dlist); 669122394Sharti context.ctx.data = data; 670122394Sharti 671216294Ssyrinx snmp_pdu_create_response(pdu, resp); 672122394Sharti 673122394Sharti if (snmp_pdu_encode_header(resp_b, resp)) 674122394Sharti return (SNMP_RET_IGN); 675122394Sharti 676122394Sharti /* 677122394Sharti * 1. Find all nodes, check that they are writeable and 678122394Sharti * that the syntax is ok, copy over the binding to the response. 679122394Sharti */ 680122394Sharti for (i = 0; i < pdu->nbindings; i++) { 681122394Sharti b = &pdu->bindings[i]; 682122394Sharti 683122394Sharti if ((np = context.node[i] = find_node(b, &except)) == NULL) { 684122394Sharti /* not found altogether or LEAF with wrong index */ 685122394Sharti if (TR(SET)) 686122394Sharti snmp_debug("set: node not found %s", 687122394Sharti asn_oid2str_r(&b->var, oidbuf)); 688122394Sharti if (pdu->version == SNMP_V1) { 689122394Sharti pdu->error_index = i + 1; 690122394Sharti pdu->error_status = SNMP_ERR_NOSUCHNAME; 691122394Sharti } else if ((np = find_subnode(b)) != NULL) { 692122394Sharti /* 2. intermediate object */ 693122394Sharti pdu->error_index = i + 1; 694122394Sharti pdu->error_status = SNMP_ERR_NOT_WRITEABLE; 695122394Sharti } else if (except == SNMP_SYNTAX_NOSUCHOBJECT) { 696122394Sharti pdu->error_index = i + 1; 697122394Sharti pdu->error_status = SNMP_ERR_NO_ACCESS; 698122394Sharti } else { 699122394Sharti pdu->error_index = i + 1; 700122394Sharti pdu->error_status = SNMP_ERR_NO_CREATION; 701122394Sharti } 702122394Sharti snmp_pdu_free(resp); 703122394Sharti return (SNMP_RET_ERR); 704122394Sharti } 705122394Sharti /* 706122394Sharti * 2. write/createable? 707122394Sharti * Can check this for leafs only, because in v2 we have 708122394Sharti * to differentiate between NOT_WRITEABLE and NO_CREATION 709122394Sharti * and only the action routine for COLUMNS knows, whether 710122394Sharti * a column exists. 711122394Sharti */ 712122394Sharti if (np->type == SNMP_NODE_LEAF && 713122394Sharti !(np->flags & SNMP_NODE_CANSET)) { 714122394Sharti if (pdu->version == SNMP_V1) { 715122394Sharti pdu->error_index = i + 1; 716122394Sharti pdu->error_status = SNMP_ERR_NOSUCHNAME; 717122394Sharti } else { 718122394Sharti pdu->error_index = i + 1; 719122394Sharti pdu->error_status = SNMP_ERR_NOT_WRITEABLE; 720122394Sharti } 721122394Sharti snmp_pdu_free(resp); 722122394Sharti return (SNMP_RET_ERR); 723122394Sharti } 724122394Sharti /* 725122394Sharti * 3. Ensure the right syntax 726122394Sharti */ 727122394Sharti if (np->syntax != b->syntax) { 728122394Sharti if (pdu->version == SNMP_V1) { 729122394Sharti pdu->error_index = i + 1; 730122394Sharti pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */ 731122394Sharti } else { 732122394Sharti pdu->error_index = i + 1; 733122394Sharti pdu->error_status = SNMP_ERR_WRONG_TYPE; 734122394Sharti } 735122394Sharti snmp_pdu_free(resp); 736122394Sharti return (SNMP_RET_ERR); 737122394Sharti } 738122394Sharti /* 739122394Sharti * 4. Copy binding 740122394Sharti */ 741122394Sharti if (snmp_value_copy(&resp->bindings[i], b)) { 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 asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]); 748122394Sharti if (asnerr == ASN_ERR_EOBUF) { 749122394Sharti pdu->error_index = i + 1; 750122394Sharti pdu->error_status = SNMP_ERR_TOOBIG; 751122394Sharti snmp_pdu_free(resp); 752122394Sharti return (SNMP_RET_ERR); 753122394Sharti } else if (asnerr != ASN_ERR_OK) { 754122394Sharti pdu->error_index = i + 1; 755122394Sharti pdu->error_status = SNMP_ERR_GENERR; 756122394Sharti snmp_pdu_free(resp); 757122394Sharti return (SNMP_RET_ERR); 758122394Sharti } 759122394Sharti resp->nbindings++; 760122394Sharti } 761122394Sharti 762128237Sharti context.ctx.code = SNMP_RET_OK; 763122394Sharti 764122394Sharti /* 765122394Sharti * 2. Call the SET method for each node. If a SET fails, rollback 766122394Sharti * everything. Map error codes depending on the version. 767122394Sharti */ 768122394Sharti for (i = 0; i < pdu->nbindings; i++) { 769122394Sharti b = &pdu->bindings[i]; 770122394Sharti np = context.node[i]; 771122394Sharti 772122394Sharti context.ctx.var_index = i + 1; 773122394Sharti context.ctx.scratch = &context.scratch[i]; 774122394Sharti 775122394Sharti ret = (*np->op)(&context.ctx, b, np->oid.len, np->index, 776122394Sharti SNMP_OP_SET); 777122394Sharti 778122394Sharti if (TR(SET)) 779122394Sharti snmp_debug("set: action %s returns %d", np->name, ret); 780122394Sharti 781122394Sharti if (pdu->version == SNMP_V1) { 782122394Sharti switch (ret) { 783122394Sharti case SNMP_ERR_NO_ACCESS: 784122394Sharti ret = SNMP_ERR_NOSUCHNAME; 785122394Sharti break; 786122394Sharti case SNMP_ERR_WRONG_TYPE: 787122394Sharti /* should no happen */ 788122394Sharti ret = SNMP_ERR_BADVALUE; 789122394Sharti break; 790122394Sharti case SNMP_ERR_WRONG_LENGTH: 791122394Sharti ret = SNMP_ERR_BADVALUE; 792122394Sharti break; 793122394Sharti case SNMP_ERR_WRONG_ENCODING: 794122394Sharti /* should not happen */ 795122394Sharti ret = SNMP_ERR_BADVALUE; 796122394Sharti break; 797122394Sharti case SNMP_ERR_WRONG_VALUE: 798122394Sharti ret = SNMP_ERR_BADVALUE; 799122394Sharti break; 800122394Sharti case SNMP_ERR_NO_CREATION: 801122394Sharti ret = SNMP_ERR_NOSUCHNAME; 802122394Sharti break; 803122394Sharti case SNMP_ERR_INCONS_VALUE: 804122394Sharti ret = SNMP_ERR_BADVALUE; 805122394Sharti break; 806122394Sharti case SNMP_ERR_RES_UNAVAIL: 807122394Sharti ret = SNMP_ERR_GENERR; 808122394Sharti break; 809122394Sharti case SNMP_ERR_COMMIT_FAILED: 810122394Sharti ret = SNMP_ERR_GENERR; 811122394Sharti break; 812122394Sharti case SNMP_ERR_UNDO_FAILED: 813122394Sharti ret = SNMP_ERR_GENERR; 814122394Sharti break; 815122394Sharti case SNMP_ERR_AUTH_ERR: 816122394Sharti /* should not happen */ 817122394Sharti ret = SNMP_ERR_GENERR; 818122394Sharti break; 819122394Sharti case SNMP_ERR_NOT_WRITEABLE: 820122394Sharti ret = SNMP_ERR_NOSUCHNAME; 821122394Sharti break; 822122394Sharti case SNMP_ERR_INCONS_NAME: 823122394Sharti ret = SNMP_ERR_BADVALUE; 824122394Sharti break; 825122394Sharti } 826122394Sharti } 827122394Sharti if (ret != SNMP_ERR_NOERROR) { 828122394Sharti pdu->error_index = i + 1; 829122394Sharti pdu->error_status = ret; 830122394Sharti 831122394Sharti rollback(&context, pdu, i); 832122394Sharti snmp_pdu_free(resp); 833122394Sharti 834128237Sharti context.ctx.code = SNMP_RET_ERR; 835122394Sharti 836122394Sharti goto errout; 837122394Sharti } 838122394Sharti } 839122394Sharti 840122394Sharti /* 841122394Sharti * 3. Call dependencies 842122394Sharti */ 843122394Sharti if (TR(SET)) 844122394Sharti snmp_debug("set: set operations ok"); 845122394Sharti 846122394Sharti if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) { 847122394Sharti pdu->error_status = ret; 848122394Sharti pdu->error_index = context.ctx.var_index; 849122394Sharti 850122394Sharti if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) { 851122394Sharti if (pdu->version != SNMP_V1) { 852122394Sharti pdu->error_status = SNMP_ERR_UNDO_FAILED; 853122394Sharti pdu->error_index = 0; 854122394Sharti } 855122394Sharti } 856122394Sharti rollback(&context, pdu, i); 857122394Sharti snmp_pdu_free(resp); 858122394Sharti 859128237Sharti context.ctx.code = SNMP_RET_ERR; 860122394Sharti 861122394Sharti goto errout; 862122394Sharti } 863122394Sharti 864122394Sharti /* 865122394Sharti * 4. Commit and copy values from the original packet to the response. 866122394Sharti * This is not the commit operation from RFC 1905 but rather an 867122394Sharti * 'FREE RESOURCES' operation. It shouldn't fail. 868122394Sharti */ 869122394Sharti if (TR(SET)) 870122394Sharti snmp_debug("set: commiting"); 871122394Sharti 872122394Sharti for (i = 0; i < pdu->nbindings; i++) { 873122394Sharti b = &resp->bindings[i]; 874122394Sharti np = context.node[i]; 875122394Sharti 876122394Sharti context.ctx.var_index = i + 1; 877122394Sharti context.ctx.scratch = &context.scratch[i]; 878122394Sharti 879122394Sharti ret = (*np->op)(&context.ctx, b, np->oid.len, np->index, 880122394Sharti SNMP_OP_COMMIT); 881122394Sharti 882122394Sharti if (ret != SNMP_ERR_NOERROR) 883122394Sharti snmp_error("set: commit failed (%d) on" 884122394Sharti " variable %s index %u", ret, 885122394Sharti asn_oid2str_r(&b->var, oidbuf), i); 886122394Sharti } 887122394Sharti 888122394Sharti if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { 889122394Sharti snmp_error("set: fix_encoding failed"); 890122394Sharti snmp_pdu_free(resp); 891128237Sharti context.ctx.code = SNMP_RET_IGN; 892122394Sharti } 893122394Sharti 894122394Sharti /* 895122394Sharti * Done 896122394Sharti */ 897122394Sharti errout: 898128237Sharti snmp_dep_finish(&context.ctx); 899122394Sharti 900122394Sharti if (TR(SET)) 901128237Sharti snmp_debug("set: returning %d", context.ctx.code); 902122394Sharti 903128237Sharti return (context.ctx.code); 904122394Sharti} 905122394Sharti/* 906122394Sharti * Lookup a dependency. If it doesn't exist, create one 907122394Sharti */ 908122394Shartistruct snmp_dependency * 909122394Shartisnmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj, 910122394Sharti const struct asn_oid *idx, size_t len, snmp_depop_t func) 911122394Sharti{ 912122394Sharti struct context *context; 913122394Sharti struct depend *d; 914122394Sharti 915122394Sharti context = (struct context *)(void *) 916122394Sharti ((char *)ctx - offsetof(struct context, ctx)); 917122394Sharti if (TR(DEPEND)) { 918122394Sharti snmp_debug("depend: looking for %s", asn_oid2str(obj)); 919122394Sharti if (idx) 920122394Sharti snmp_debug("depend: index is %s", asn_oid2str(idx)); 921122394Sharti } 922122394Sharti TAILQ_FOREACH(d, &context->dlist, link) 923122394Sharti if (asn_compare_oid(obj, &d->dep.obj) == 0 && 924122394Sharti ((idx == NULL && d->dep.idx.len == 0) || 925122394Sharti (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) { 926122394Sharti if(TR(DEPEND)) 927122394Sharti snmp_debug("depend: found"); 928122394Sharti return (&d->dep); 929122394Sharti } 930122394Sharti 931122394Sharti if(TR(DEPEND)) 932122394Sharti snmp_debug("depend: creating"); 933122394Sharti 934122394Sharti if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL) 935122394Sharti return (NULL); 936122394Sharti memset(&d->dep, 0, len); 937122394Sharti 938122394Sharti d->dep.obj = *obj; 939122394Sharti if (idx == NULL) 940122394Sharti d->dep.idx.len = 0; 941122394Sharti else 942122394Sharti d->dep.idx = *idx; 943122394Sharti d->len = len; 944122394Sharti d->func = func; 945122394Sharti 946122394Sharti TAILQ_INSERT_TAIL(&context->dlist, d, link); 947122394Sharti 948122394Sharti return (&d->dep); 949122394Sharti} 950122394Sharti 951122394Sharti/* 952122394Sharti * Make an error response from a PDU. We do this without decoding the 953122394Sharti * variable bindings. This means we can sent the junk back to a caller 954122394Sharti * that has sent us junk in the first place. 955122394Sharti */ 956122394Shartienum snmp_ret 957122394Shartisnmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b, 958122394Sharti struct asn_buf *resp_b) 959122394Sharti{ 960122394Sharti asn_len_t len; 961122394Sharti struct snmp_pdu resp; 962122394Sharti enum asn_err err; 963122394Sharti enum snmp_code code; 964122394Sharti 965122394Sharti memset(&resp, 0, sizeof(resp)); 966216294Ssyrinx if ((code = snmp_pdu_decode_header(pdu_b, &resp)) != SNMP_CODE_OK) 967122394Sharti return (SNMP_RET_IGN); 968122394Sharti 969122394Sharti if (pdu_b->asn_len < len) 970122394Sharti return (SNMP_RET_IGN); 971122394Sharti pdu_b->asn_len = len; 972122394Sharti 973122394Sharti err = snmp_parse_pdus_hdr(pdu_b, &resp, &len); 974122394Sharti if (ASN_ERR_STOPPED(err)) 975122394Sharti return (SNMP_RET_IGN); 976122394Sharti if (pdu_b->asn_len < len) 977122394Sharti return (SNMP_RET_IGN); 978122394Sharti pdu_b->asn_len = len; 979122394Sharti 980122394Sharti /* now we have the bindings left - construct new message */ 981122394Sharti resp.error_status = pdu->error_status; 982122394Sharti resp.error_index = pdu->error_index; 983122394Sharti resp.type = SNMP_PDU_RESPONSE; 984122394Sharti 985122394Sharti code = snmp_pdu_encode_header(resp_b, &resp); 986122394Sharti if (code != SNMP_CODE_OK) 987122394Sharti return (SNMP_RET_IGN); 988122394Sharti 989122394Sharti if (pdu_b->asn_len > resp_b->asn_len) 990122394Sharti /* too short */ 991122394Sharti return (SNMP_RET_IGN); 992122394Sharti (void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len); 993122394Sharti resp_b->asn_len -= pdu_b->asn_len; 994122394Sharti resp_b->asn_ptr += pdu_b->asn_len; 995122394Sharti 996122394Sharti code = snmp_fix_encoding(resp_b, &resp); 997122394Sharti if (code != SNMP_CODE_OK) 998122394Sharti return (SNMP_RET_IGN); 999122394Sharti 1000122394Sharti return (SNMP_RET_OK); 1001122394Sharti} 1002122394Sharti 1003122394Shartistatic void 1004122394Shartisnmp_debug_func(const char *fmt, ...) 1005122394Sharti{ 1006122394Sharti va_list ap; 1007122394Sharti 1008122394Sharti va_start(ap, fmt); 1009122394Sharti vfprintf(stderr, fmt, ap); 1010122394Sharti va_end(ap); 1011122394Sharti fprintf(stderr, "\n"); 1012122394Sharti} 1013