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