/contrib/bsnmp/lib/snmpagent.c
https://bitbucket.org/freebsd/freebsd-head/ · C · 1007 lines · 734 code · 137 blank · 136 comment · 210 complexity · c4bfcddf86eaffd49a688897ca739a45 MD5 · raw file
- /*
- * Copyright (c) 2001-2003
- * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
- * All rights reserved.
- *
- * Author: Harti Brandt <harti@freebsd.org>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $Begemot: bsnmp/lib/snmpagent.c,v 1.20 2005/10/04 11:21:33 brandt_h Exp $
- *
- * SNMP Agent functions
- */
- #include <sys/types.h>
- #include <sys/queue.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <stddef.h>
- #include <stdarg.h>
- #ifdef HAVE_STDINT_H
- #include <stdint.h>
- #elif defined(HAVE_INTTYPES_H)
- #include <inttypes.h>
- #endif
- #include <string.h>
- #include "asn1.h"
- #include "snmp.h"
- #include "snmppriv.h"
- #include "snmpagent.h"
- static void snmp_debug_func(const char *fmt, ...);
- void (*snmp_debug)(const char *fmt, ...) = snmp_debug_func;
- struct snmp_node *tree;
- u_int tree_size;
- /*
- * Structure to hold dependencies during SET processing
- * The last two members of this structure must be the
- * dependency visible by the user and the user data.
- */
- struct depend {
- TAILQ_ENTRY(depend) link;
- size_t len; /* size of data part */
- snmp_depop_t func;
- struct snmp_dependency dep;
- #if defined(__GNUC__) && __GNUC__ < 3
- u_char data[0];
- #else
- u_char data[];
- #endif
- };
- TAILQ_HEAD(depend_list, depend);
- /*
- * Set context
- */
- struct context {
- struct snmp_context ctx;
- struct depend_list dlist;
- const struct snmp_node *node[SNMP_MAX_BINDINGS];
- struct snmp_scratch scratch[SNMP_MAX_BINDINGS];
- struct depend *depend;
- };
- #define TR(W) (snmp_trace & SNMP_TRACE_##W)
- u_int snmp_trace = 0;
- static char oidbuf[ASN_OIDSTRLEN];
- /*
- * Allocate a context
- */
- struct snmp_context *
- snmp_init_context(void)
- {
- struct context *context;
- if ((context = malloc(sizeof(*context))) == NULL)
- return (NULL);
- memset(context, 0, sizeof(*context));
- TAILQ_INIT(&context->dlist);
- return (&context->ctx);
- }
- /*
- * Find a variable for SET/GET and the first GETBULK pass.
- * Return the node pointer. If the search fails, set the errp to
- * the correct SNMPv2 GET exception code.
- */
- static struct snmp_node *
- find_node(const struct snmp_value *value, enum snmp_syntax *errp)
- {
- struct snmp_node *tp;
- if (TR(FIND))
- snmp_debug("find: searching %s",
- asn_oid2str_r(&value->var, oidbuf));
- /*
- * If we have an exact match (the entry in the table is a
- * sub-oid from the variable) we have found what we are for.
- * If the table oid is higher than the variable, there is no match.
- */
- for (tp = tree; tp < tree + tree_size; tp++) {
- if (asn_is_suboid(&tp->oid, &value->var))
- goto found;
- if (asn_compare_oid(&tp->oid, &value->var) >= 0)
- break;
- }
- if (TR(FIND))
- snmp_debug("find: no match");
- *errp = SNMP_SYNTAX_NOSUCHOBJECT;
- return (NULL);
- found:
- /* leafs must have a 0 instance identifier */
- if (tp->type == SNMP_NODE_LEAF &&
- (value->var.len != tp->oid.len + 1 ||
- value->var.subs[tp->oid.len] != 0)) {
- if (TR(FIND))
- snmp_debug("find: bad leaf index");
- *errp = SNMP_SYNTAX_NOSUCHINSTANCE;
- return (NULL);
- }
- if (TR(FIND))
- snmp_debug("find: found %s",
- asn_oid2str_r(&value->var, oidbuf));
- return (tp);
- }
- static struct snmp_node *
- find_subnode(const struct snmp_value *value)
- {
- struct snmp_node *tp;
- for (tp = tree; tp < tree + tree_size; tp++) {
- if (asn_is_suboid(&value->var, &tp->oid))
- return (tp);
- }
- return (NULL);
- }
- static void
- snmp_pdu_create_response(struct snmp_pdu *pdu, struct snmp_pdu *resp)
- {
- memset(resp, 0, sizeof(*resp));
- strcpy(resp->community, pdu->community);
- resp->version = pdu->version;
- resp->type = SNMP_PDU_RESPONSE;
- resp->request_id = pdu->request_id;
- resp->version = pdu->version;
- if (resp->version != SNMP_V3)
- return;
- memcpy(&resp->engine, &pdu->engine, sizeof(pdu->engine));
- memcpy(&resp->user, &pdu->user, sizeof(pdu->user));
- snmp_pdu_init_secparams(resp);
- resp->identifier = pdu->identifier;
- resp->security_model = pdu->security_model;
- resp->context_engine_len = pdu->context_engine_len;
- memcpy(resp->context_engine, pdu->context_engine,
- resp->context_engine_len);
- strlcpy(resp->context_name, pdu->context_name,
- sizeof(resp->context_name));
- }
- /*
- * Execute a GET operation. The tree is rooted at the global 'root'.
- * Build the response PDU on the fly. If the return code is SNMP_RET_ERR
- * the pdu error status and index will be set.
- */
- enum snmp_ret
- snmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b,
- struct snmp_pdu *resp, void *data)
- {
- int ret;
- u_int i;
- struct snmp_node *tp;
- enum snmp_syntax except;
- struct context context;
- enum asn_err err;
- memset(&context, 0, sizeof(context));
- context.ctx.data = data;
- snmp_pdu_create_response(pdu, resp);
- if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
- /* cannot even encode header - very bad */
- return (SNMP_RET_IGN);
- for (i = 0; i < pdu->nbindings; i++) {
- resp->bindings[i].var = pdu->bindings[i].var;
- if ((tp = find_node(&pdu->bindings[i], &except)) == NULL) {
- if (pdu->version == SNMP_V1) {
- if (TR(GET))
- snmp_debug("get: nosuchname");
- pdu->error_status = SNMP_ERR_NOSUCHNAME;
- pdu->error_index = i + 1;
- snmp_pdu_free(resp);
- return (SNMP_RET_ERR);
- }
- if (TR(GET))
- snmp_debug("get: exception %u", except);
- resp->bindings[i].syntax = except;
- } else {
- /* call the action to fetch the value. */
- resp->bindings[i].syntax = tp->syntax;
- ret = (*tp->op)(&context.ctx, &resp->bindings[i],
- tp->oid.len, tp->index, SNMP_OP_GET);
- if (TR(GET))
- snmp_debug("get: action returns %d", ret);
- if (ret == SNMP_ERR_NOSUCHNAME) {
- if (pdu->version == SNMP_V1) {
- pdu->error_status = SNMP_ERR_NOSUCHNAME;
- pdu->error_index = i + 1;
- snmp_pdu_free(resp);
- return (SNMP_RET_ERR);
- }
- if (TR(GET))
- snmp_debug("get: exception noSuchInstance");
- resp->bindings[i].syntax = SNMP_SYNTAX_NOSUCHINSTANCE;
- } else if (ret != SNMP_ERR_NOERROR) {
- pdu->error_status = SNMP_ERR_GENERR;
- pdu->error_index = i + 1;
- snmp_pdu_free(resp);
- return (SNMP_RET_ERR);
- }
- }
- resp->nbindings++;
- err = snmp_binding_encode(resp_b, &resp->bindings[i]);
- if (err == ASN_ERR_EOBUF) {
- pdu->error_status = SNMP_ERR_TOOBIG;
- pdu->error_index = 0;
- snmp_pdu_free(resp);
- return (SNMP_RET_ERR);
- }
- if (err != ASN_ERR_OK) {
- if (TR(GET))
- snmp_debug("get: binding encoding: %u", err);
- pdu->error_status = SNMP_ERR_GENERR;
- pdu->error_index = i + 1;
- snmp_pdu_free(resp);
- return (SNMP_RET_ERR);
- }
- }
- return (snmp_fix_encoding(resp_b, resp));
- }
- static struct snmp_node *
- next_node(const struct snmp_value *value, int *pnext)
- {
- struct snmp_node *tp;
- if (TR(FIND))
- snmp_debug("next: searching %s",
- asn_oid2str_r(&value->var, oidbuf));
- *pnext = 0;
- for (tp = tree; tp < tree + tree_size; tp++) {
- if (asn_is_suboid(&tp->oid, &value->var)) {
- /* the tree OID is a sub-oid of the requested OID. */
- if (tp->type == SNMP_NODE_LEAF) {
- if (tp->oid.len == value->var.len) {
- /* request for scalar type */
- if (TR(FIND))
- snmp_debug("next: found scalar %s",
- asn_oid2str_r(&tp->oid, oidbuf));
- return (tp);
- }
- /* try next */
- } else {
- if (TR(FIND))
- snmp_debug("next: found column %s",
- asn_oid2str_r(&tp->oid, oidbuf));
- return (tp);
- }
- } else if (asn_is_suboid(&value->var, &tp->oid) ||
- asn_compare_oid(&tp->oid, &value->var) >= 0) {
- if (TR(FIND))
- snmp_debug("next: found %s",
- asn_oid2str_r(&tp->oid, oidbuf));
- *pnext = 1;
- return (tp);
- }
- }
- if (TR(FIND))
- snmp_debug("next: failed");
- return (NULL);
- }
- static enum snmp_ret
- do_getnext(struct context *context, const struct snmp_value *inb,
- struct snmp_value *outb, struct snmp_pdu *pdu)
- {
- const struct snmp_node *tp;
- int ret, next;
- if ((tp = next_node(inb, &next)) == NULL)
- goto eofMib;
- /* retain old variable if we are doing a GETNEXT on an exact
- * matched leaf only */
- if (tp->type == SNMP_NODE_LEAF || next)
- outb->var = tp->oid;
- else
- outb->var = inb->var;
- for (;;) {
- outb->syntax = tp->syntax;
- if (tp->type == SNMP_NODE_LEAF) {
- /* make a GET operation */
- outb->var.subs[outb->var.len++] = 0;
- ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
- tp->index, SNMP_OP_GET);
- } else {
- /* make a GETNEXT */
- ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
- tp->index, SNMP_OP_GETNEXT);
- }
- if (ret != SNMP_ERR_NOSUCHNAME) {
- /* got something */
- if (ret != SNMP_ERR_NOERROR && TR(GETNEXT))
- snmp_debug("getnext: %s returns %u",
- asn_oid2str(&outb->var), ret);
- break;
- }
- /* object has no data - try next */
- if (++tp == tree + tree_size)
- break;
- if (TR(GETNEXT))
- snmp_debug("getnext: no data - avancing to %s",
- asn_oid2str(&tp->oid));
- outb->var = tp->oid;
- }
- if (ret == SNMP_ERR_NOSUCHNAME) {
- eofMib:
- outb->var = inb->var;
- if (pdu->version == SNMP_V1) {
- pdu->error_status = SNMP_ERR_NOSUCHNAME;
- return (SNMP_RET_ERR);
- }
- outb->syntax = SNMP_SYNTAX_ENDOFMIBVIEW;
- } else if (ret != SNMP_ERR_NOERROR) {
- pdu->error_status = SNMP_ERR_GENERR;
- return (SNMP_RET_ERR);
- }
- return (SNMP_RET_OK);
- }
- /*
- * Execute a GETNEXT operation. The tree is rooted at the global 'root'.
- * Build the response PDU on the fly. The return is:
- */
- enum snmp_ret
- snmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b,
- struct snmp_pdu *resp, void *data)
- {
- struct context context;
- u_int i;
- enum asn_err err;
- enum snmp_ret result;
- memset(&context, 0, sizeof(context));
- context.ctx.data = data;
- snmp_pdu_create_response(pdu, resp);
- if (snmp_pdu_encode_header(resp_b, resp))
- return (SNMP_RET_IGN);
- for (i = 0; i < pdu->nbindings; i++) {
- result = do_getnext(&context, &pdu->bindings[i],
- &resp->bindings[i], pdu);
- if (result != SNMP_RET_OK) {
- pdu->error_index = i + 1;
- snmp_pdu_free(resp);
- return (result);
- }
- resp->nbindings++;
- err = snmp_binding_encode(resp_b, &resp->bindings[i]);
- if (err == ASN_ERR_EOBUF) {
- pdu->error_status = SNMP_ERR_TOOBIG;
- pdu->error_index = 0;
- snmp_pdu_free(resp);
- return (SNMP_RET_ERR);
- }
- if (err != ASN_ERR_OK) {
- if (TR(GET))
- snmp_debug("getnext: binding encoding: %u", err);
- pdu->error_status = SNMP_ERR_GENERR;
- pdu->error_index = i + 1;
- snmp_pdu_free(resp);
- return (SNMP_RET_ERR);
- }
- }
- return (snmp_fix_encoding(resp_b, resp));
- }
- enum snmp_ret
- snmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b,
- struct snmp_pdu *resp, void *data)
- {
- struct context context;
- u_int i;
- int cnt;
- u_int non_rep;
- int eomib;
- enum snmp_ret result;
- enum asn_err err;
- memset(&context, 0, sizeof(context));
- context.ctx.data = data;
- snmp_pdu_create_response(pdu, resp);
- if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
- /* cannot even encode header - very bad */
- return (SNMP_RET_IGN);
- if ((non_rep = pdu->error_status) > pdu->nbindings)
- non_rep = pdu->nbindings;
- /* non-repeaters */
- for (i = 0; i < non_rep; i++) {
- result = do_getnext(&context, &pdu->bindings[i],
- &resp->bindings[resp->nbindings], pdu);
- if (result != SNMP_RET_OK) {
- pdu->error_index = i + 1;
- snmp_pdu_free(resp);
- return (result);
- }
- err = snmp_binding_encode(resp_b,
- &resp->bindings[resp->nbindings++]);
- if (err == ASN_ERR_EOBUF)
- goto done;
- if (err != ASN_ERR_OK) {
- if (TR(GET))
- snmp_debug("getnext: binding encoding: %u", err);
- pdu->error_status = SNMP_ERR_GENERR;
- pdu->error_index = i + 1;
- snmp_pdu_free(resp);
- return (SNMP_RET_ERR);
- }
- }
- if (non_rep == pdu->nbindings)
- goto done;
- /* repeates */
- for (cnt = 0; cnt < pdu->error_index; cnt++) {
- eomib = 1;
- for (i = non_rep; i < pdu->nbindings; i++) {
- if (cnt == 0)
- result = do_getnext(&context, &pdu->bindings[i],
- &resp->bindings[resp->nbindings], pdu);
- else
- result = do_getnext(&context,
- &resp->bindings[resp->nbindings -
- (pdu->nbindings - non_rep)],
- &resp->bindings[resp->nbindings], pdu);
- if (result != SNMP_RET_OK) {
- pdu->error_index = i + 1;
- snmp_pdu_free(resp);
- return (result);
- }
- if (resp->bindings[resp->nbindings].syntax !=
- SNMP_SYNTAX_ENDOFMIBVIEW)
- eomib = 0;
- err = snmp_binding_encode(resp_b,
- &resp->bindings[resp->nbindings++]);
- if (err == ASN_ERR_EOBUF)
- goto done;
- if (err != ASN_ERR_OK) {
- if (TR(GET))
- snmp_debug("getnext: binding encoding: %u", err);
- pdu->error_status = SNMP_ERR_GENERR;
- pdu->error_index = i + 1;
- snmp_pdu_free(resp);
- return (SNMP_RET_ERR);
- }
- }
- if (eomib)
- break;
- }
- done:
- return (snmp_fix_encoding(resp_b, resp));
- }
- /*
- * Rollback a SET operation. Failed index is 'i'.
- */
- static void
- rollback(struct context *context, struct snmp_pdu *pdu, u_int i)
- {
- struct snmp_value *b;
- const struct snmp_node *np;
- int ret;
- while (i-- > 0) {
- b = &pdu->bindings[i];
- np = context->node[i];
- context->ctx.scratch = &context->scratch[i];
- ret = (*np->op)(&context->ctx, b, np->oid.len, np->index,
- SNMP_OP_ROLLBACK);
- if (ret != SNMP_ERR_NOERROR) {
- snmp_error("set: rollback failed (%d) on variable %s "
- "index %u", ret, asn_oid2str(&b->var), i);
- if (pdu->version != SNMP_V1) {
- pdu->error_status = SNMP_ERR_UNDO_FAILED;
- pdu->error_index = 0;
- }
- }
- }
- }
- /*
- * Commit dependencies.
- */
- int
- snmp_dep_commit(struct snmp_context *ctx)
- {
- struct context *context = (struct context *)ctx;
- int ret;
- TAILQ_FOREACH(context->depend, &context->dlist, link) {
- ctx->dep = &context->depend->dep;
- if (TR(SET))
- snmp_debug("set: dependency commit %s",
- asn_oid2str(&ctx->dep->obj));
- ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT);
- if (ret != SNMP_ERR_NOERROR) {
- if (TR(SET))
- snmp_debug("set: dependency failed %d", ret);
- return (ret);
- }
- }
- return (SNMP_ERR_NOERROR);
- }
- /*
- * Rollback dependencies
- */
- int
- snmp_dep_rollback(struct snmp_context *ctx)
- {
- struct context *context = (struct context *)ctx;
- int ret, ret1;
- char objbuf[ASN_OIDSTRLEN];
- char idxbuf[ASN_OIDSTRLEN];
- ret1 = SNMP_ERR_NOERROR;
- while ((context->depend =
- TAILQ_PREV(context->depend, depend_list, link)) != NULL) {
- ctx->dep = &context->depend->dep;
- if (TR(SET))
- snmp_debug("set: dependency rollback %s",
- asn_oid2str(&ctx->dep->obj));
- ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK);
- if (ret != SNMP_ERR_NOERROR) {
- snmp_debug("set: dep rollback returns %u: %s %s", ret,
- asn_oid2str_r(&ctx->dep->obj, objbuf),
- asn_oid2str_r(&ctx->dep->idx, idxbuf));
- if (ret1 == SNMP_ERR_NOERROR)
- ret1 = ret;
- }
- }
- return (ret1);
- }
- void
- snmp_dep_finish(struct snmp_context *ctx)
- {
- struct context *context = (struct context *)ctx;
- struct depend *d;
- while ((d = TAILQ_FIRST(&context->dlist)) != NULL) {
- ctx->dep = &d->dep;
- (void)d->func(ctx, ctx->dep, SNMP_DEPOP_FINISH);
- TAILQ_REMOVE(&context->dlist, d, link);
- free(d);
- }
- }
- /*
- * Do a SET operation.
- */
- enum snmp_ret
- snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b,
- struct snmp_pdu *resp, void *data)
- {
- int ret;
- u_int i;
- enum asn_err asnerr;
- struct context context;
- const struct snmp_node *np;
- struct snmp_value *b;
- enum snmp_syntax except;
- memset(&context, 0, sizeof(context));
- TAILQ_INIT(&context.dlist);
- context.ctx.data = data;
- snmp_pdu_create_response(pdu, resp);
- if (snmp_pdu_encode_header(resp_b, resp))
- return (SNMP_RET_IGN);
- /*
- * 1. Find all nodes, check that they are writeable and
- * that the syntax is ok, copy over the binding to the response.
- */
- for (i = 0; i < pdu->nbindings; i++) {
- b = &pdu->bindings[i];
- if ((np = context.node[i] = find_node(b, &except)) == NULL) {
- /* not found altogether or LEAF with wrong index */
- if (TR(SET))
- snmp_debug("set: node not found %s",
- asn_oid2str_r(&b->var, oidbuf));
- if (pdu->version == SNMP_V1) {
- pdu->error_index = i + 1;
- pdu->error_status = SNMP_ERR_NOSUCHNAME;
- } else if ((np = find_subnode(b)) != NULL) {
- /* 2. intermediate object */
- pdu->error_index = i + 1;
- pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
- } else if (except == SNMP_SYNTAX_NOSUCHOBJECT) {
- pdu->error_index = i + 1;
- pdu->error_status = SNMP_ERR_NO_ACCESS;
- } else {
- pdu->error_index = i + 1;
- pdu->error_status = SNMP_ERR_NO_CREATION;
- }
- snmp_pdu_free(resp);
- return (SNMP_RET_ERR);
- }
- /*
- * 2. write/createable?
- * Can check this for leafs only, because in v2 we have
- * to differentiate between NOT_WRITEABLE and NO_CREATION
- * and only the action routine for COLUMNS knows, whether
- * a column exists.
- */
- if (np->type == SNMP_NODE_LEAF &&
- !(np->flags & SNMP_NODE_CANSET)) {
- if (pdu->version == SNMP_V1) {
- pdu->error_index = i + 1;
- pdu->error_status = SNMP_ERR_NOSUCHNAME;
- } else {
- pdu->error_index = i + 1;
- pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
- }
- snmp_pdu_free(resp);
- return (SNMP_RET_ERR);
- }
- /*
- * 3. Ensure the right syntax
- */
- if (np->syntax != b->syntax) {
- if (pdu->version == SNMP_V1) {
- pdu->error_index = i + 1;
- pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */
- } else {
- pdu->error_index = i + 1;
- pdu->error_status = SNMP_ERR_WRONG_TYPE;
- }
- snmp_pdu_free(resp);
- return (SNMP_RET_ERR);
- }
- /*
- * 4. Copy binding
- */
- if (snmp_value_copy(&resp->bindings[i], b)) {
- pdu->error_index = i + 1;
- pdu->error_status = SNMP_ERR_GENERR;
- snmp_pdu_free(resp);
- return (SNMP_RET_ERR);
- }
- asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]);
- if (asnerr == ASN_ERR_EOBUF) {
- pdu->error_index = i + 1;
- pdu->error_status = SNMP_ERR_TOOBIG;
- snmp_pdu_free(resp);
- return (SNMP_RET_ERR);
- } else if (asnerr != ASN_ERR_OK) {
- pdu->error_index = i + 1;
- pdu->error_status = SNMP_ERR_GENERR;
- snmp_pdu_free(resp);
- return (SNMP_RET_ERR);
- }
- resp->nbindings++;
- }
- context.ctx.code = SNMP_RET_OK;
- /*
- * 2. Call the SET method for each node. If a SET fails, rollback
- * everything. Map error codes depending on the version.
- */
- for (i = 0; i < pdu->nbindings; i++) {
- b = &pdu->bindings[i];
- np = context.node[i];
- context.ctx.var_index = i + 1;
- context.ctx.scratch = &context.scratch[i];
- ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
- SNMP_OP_SET);
- if (TR(SET))
- snmp_debug("set: action %s returns %d", np->name, ret);
- if (pdu->version == SNMP_V1) {
- switch (ret) {
- case SNMP_ERR_NO_ACCESS:
- ret = SNMP_ERR_NOSUCHNAME;
- break;
- case SNMP_ERR_WRONG_TYPE:
- /* should no happen */
- ret = SNMP_ERR_BADVALUE;
- break;
- case SNMP_ERR_WRONG_LENGTH:
- ret = SNMP_ERR_BADVALUE;
- break;
- case SNMP_ERR_WRONG_ENCODING:
- /* should not happen */
- ret = SNMP_ERR_BADVALUE;
- break;
- case SNMP_ERR_WRONG_VALUE:
- ret = SNMP_ERR_BADVALUE;
- break;
- case SNMP_ERR_NO_CREATION:
- ret = SNMP_ERR_NOSUCHNAME;
- break;
- case SNMP_ERR_INCONS_VALUE:
- ret = SNMP_ERR_BADVALUE;
- break;
- case SNMP_ERR_RES_UNAVAIL:
- ret = SNMP_ERR_GENERR;
- break;
- case SNMP_ERR_COMMIT_FAILED:
- ret = SNMP_ERR_GENERR;
- break;
- case SNMP_ERR_UNDO_FAILED:
- ret = SNMP_ERR_GENERR;
- break;
- case SNMP_ERR_AUTH_ERR:
- /* should not happen */
- ret = SNMP_ERR_GENERR;
- break;
- case SNMP_ERR_NOT_WRITEABLE:
- ret = SNMP_ERR_NOSUCHNAME;
- break;
- case SNMP_ERR_INCONS_NAME:
- ret = SNMP_ERR_BADVALUE;
- break;
- }
- }
- if (ret != SNMP_ERR_NOERROR) {
- pdu->error_index = i + 1;
- pdu->error_status = ret;
- rollback(&context, pdu, i);
- snmp_pdu_free(resp);
- context.ctx.code = SNMP_RET_ERR;
- goto errout;
- }
- }
- /*
- * 3. Call dependencies
- */
- if (TR(SET))
- snmp_debug("set: set operations ok");
- if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) {
- pdu->error_status = ret;
- pdu->error_index = context.ctx.var_index;
- if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) {
- if (pdu->version != SNMP_V1) {
- pdu->error_status = SNMP_ERR_UNDO_FAILED;
- pdu->error_index = 0;
- }
- }
- rollback(&context, pdu, i);
- snmp_pdu_free(resp);
- context.ctx.code = SNMP_RET_ERR;
- goto errout;
- }
- /*
- * 4. Commit and copy values from the original packet to the response.
- * This is not the commit operation from RFC 1905 but rather an
- * 'FREE RESOURCES' operation. It shouldn't fail.
- */
- if (TR(SET))
- snmp_debug("set: commiting");
- for (i = 0; i < pdu->nbindings; i++) {
- b = &resp->bindings[i];
- np = context.node[i];
- context.ctx.var_index = i + 1;
- context.ctx.scratch = &context.scratch[i];
- ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
- SNMP_OP_COMMIT);
- if (ret != SNMP_ERR_NOERROR)
- snmp_error("set: commit failed (%d) on"
- " variable %s index %u", ret,
- asn_oid2str_r(&b->var, oidbuf), i);
- }
- if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
- snmp_error("set: fix_encoding failed");
- snmp_pdu_free(resp);
- context.ctx.code = SNMP_RET_IGN;
- }
- /*
- * Done
- */
- errout:
- snmp_dep_finish(&context.ctx);
- if (TR(SET))
- snmp_debug("set: returning %d", context.ctx.code);
- return (context.ctx.code);
- }
- /*
- * Lookup a dependency. If it doesn't exist, create one
- */
- struct snmp_dependency *
- snmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj,
- const struct asn_oid *idx, size_t len, snmp_depop_t func)
- {
- struct context *context;
- struct depend *d;
- context = (struct context *)(void *)
- ((char *)ctx - offsetof(struct context, ctx));
- if (TR(DEPEND)) {
- snmp_debug("depend: looking for %s", asn_oid2str(obj));
- if (idx)
- snmp_debug("depend: index is %s", asn_oid2str(idx));
- }
- TAILQ_FOREACH(d, &context->dlist, link)
- if (asn_compare_oid(obj, &d->dep.obj) == 0 &&
- ((idx == NULL && d->dep.idx.len == 0) ||
- (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) {
- if(TR(DEPEND))
- snmp_debug("depend: found");
- return (&d->dep);
- }
- if(TR(DEPEND))
- snmp_debug("depend: creating");
- if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL)
- return (NULL);
- memset(&d->dep, 0, len);
- d->dep.obj = *obj;
- if (idx == NULL)
- d->dep.idx.len = 0;
- else
- d->dep.idx = *idx;
- d->len = len;
- d->func = func;
- TAILQ_INSERT_TAIL(&context->dlist, d, link);
- return (&d->dep);
- }
- /*
- * Make an error response from a PDU. We do this without decoding the
- * variable bindings. This means we can sent the junk back to a caller
- * that has sent us junk in the first place.
- */
- enum snmp_ret
- snmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b,
- struct asn_buf *resp_b)
- {
- asn_len_t len;
- struct snmp_pdu resp;
- enum asn_err err;
- enum snmp_code code;
- memset(&resp, 0, sizeof(resp));
- if ((code = snmp_pdu_decode_header(pdu_b, &resp)) != SNMP_CODE_OK)
- return (SNMP_RET_IGN);
- if (pdu_b->asn_len < len)
- return (SNMP_RET_IGN);
- pdu_b->asn_len = len;
- err = snmp_parse_pdus_hdr(pdu_b, &resp, &len);
- if (ASN_ERR_STOPPED(err))
- return (SNMP_RET_IGN);
- if (pdu_b->asn_len < len)
- return (SNMP_RET_IGN);
- pdu_b->asn_len = len;
- /* now we have the bindings left - construct new message */
- resp.error_status = pdu->error_status;
- resp.error_index = pdu->error_index;
- resp.type = SNMP_PDU_RESPONSE;
- code = snmp_pdu_encode_header(resp_b, &resp);
- if (code != SNMP_CODE_OK)
- return (SNMP_RET_IGN);
- if (pdu_b->asn_len > resp_b->asn_len)
- /* too short */
- return (SNMP_RET_IGN);
- (void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len);
- resp_b->asn_len -= pdu_b->asn_len;
- resp_b->asn_ptr += pdu_b->asn_len;
- code = snmp_fix_encoding(resp_b, &resp);
- if (code != SNMP_CODE_OK)
- return (SNMP_RET_IGN);
- return (SNMP_RET_OK);
- }
- static void
- snmp_debug_func(const char *fmt, ...)
- {
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fprintf(stderr, "\n");
- }