2 * Copyright (c) 2001-2003
3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
6 * Author: Harti Brandt <harti@freebsd.org>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * $Begemot: bsnmp/lib/snmpagent.c,v 1.20 2005/10/04 11:21:33 brandt_h Exp $
31 * SNMP Agent functions
33 #include <sys/types.h>
34 #include <sys/queue.h>
41 #elif defined(HAVE_INTTYPES_H)
49 #include "snmpagent.h"
51 static void snmp_debug_func(const char *fmt, ...);
53 void (*snmp_debug)(const char *fmt, ...) = snmp_debug_func;
55 struct snmp_node *tree;
59 * Structure to hold dependencies during SET processing
60 * The last two members of this structure must be the
61 * dependency visible by the user and the user data.
64 TAILQ_ENTRY(depend) link;
65 size_t len; /* size of data part */
67 struct snmp_dependency dep;
68 #if defined(__GNUC__) && __GNUC__ < 3
74 TAILQ_HEAD(depend_list, depend);
80 struct snmp_context ctx;
81 struct depend_list dlist;
82 const struct snmp_node *node[SNMP_MAX_BINDINGS];
83 struct snmp_scratch scratch[SNMP_MAX_BINDINGS];
84 struct depend *depend;
87 #define TR(W) (snmp_trace & SNMP_TRACE_##W)
90 static char oidbuf[ASN_OIDSTRLEN];
96 snmp_init_context(void)
98 struct context *context;
100 if ((context = malloc(sizeof(*context))) == NULL)
103 memset(context, 0, sizeof(*context));
104 TAILQ_INIT(&context->dlist);
106 return (&context->ctx);
110 * Find a variable for SET/GET and the first GETBULK pass.
111 * Return the node pointer. If the search fails, set the errp to
112 * the correct SNMPv2 GET exception code.
114 static struct snmp_node *
115 find_node(const struct snmp_value *value, enum snmp_syntax *errp)
117 struct snmp_node *tp;
120 snmp_debug("find: searching %s",
121 asn_oid2str_r(&value->var, oidbuf));
124 * If we have an exact match (the entry in the table is a
125 * sub-oid from the variable) we have found what we are for.
126 * If the table oid is higher than the variable, there is no match.
128 for (tp = tree; tp < tree + tree_size; tp++) {
129 if (asn_is_suboid(&tp->oid, &value->var))
131 if (asn_compare_oid(&tp->oid, &value->var) >= 0)
136 snmp_debug("find: no match");
137 *errp = SNMP_SYNTAX_NOSUCHOBJECT;
141 /* leafs must have a 0 instance identifier */
142 if (tp->type == SNMP_NODE_LEAF &&
143 (value->var.len != tp->oid.len + 1 ||
144 value->var.subs[tp->oid.len] != 0)) {
146 snmp_debug("find: bad leaf index");
147 *errp = SNMP_SYNTAX_NOSUCHINSTANCE;
151 snmp_debug("find: found %s",
152 asn_oid2str_r(&value->var, oidbuf));
156 static struct snmp_node *
157 find_subnode(const struct snmp_value *value)
159 struct snmp_node *tp;
161 for (tp = tree; tp < tree + tree_size; tp++) {
162 if (asn_is_suboid(&value->var, &tp->oid))
169 snmp_pdu_create_response(const struct snmp_pdu *pdu, struct snmp_pdu *resp)
171 memset(resp, 0, sizeof(*resp));
172 strcpy(resp->community, pdu->community);
173 resp->version = pdu->version;
174 resp->type = SNMP_PDU_RESPONSE;
175 resp->request_id = pdu->request_id;
176 resp->version = pdu->version;
178 if (resp->version != SNMP_V3)
181 memcpy(&resp->engine, &pdu->engine, sizeof(pdu->engine));
182 memcpy(&resp->user, &pdu->user, sizeof(pdu->user));
183 snmp_pdu_init_secparams(resp);
184 resp->identifier = pdu->identifier;
185 resp->security_model = pdu->security_model;
186 resp->context_engine_len = pdu->context_engine_len;
187 memcpy(resp->context_engine, pdu->context_engine,
188 resp->context_engine_len);
189 strlcpy(resp->context_name, pdu->context_name,
190 sizeof(resp->context_name));
194 * Execute a GET operation. The tree is rooted at the global 'root'.
195 * Build the response PDU on the fly. If the return code is SNMP_RET_ERR
196 * the pdu error status and index will be set.
199 snmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b,
200 struct snmp_pdu *resp, void *data)
204 struct snmp_node *tp;
205 enum snmp_syntax except;
206 struct context context;
209 memset(&context, 0, sizeof(context));
210 context.ctx.data = data;
212 snmp_pdu_create_response(pdu, resp);
214 if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
215 /* cannot even encode header - very bad */
216 return (SNMP_RET_IGN);
218 for (i = 0; i < pdu->nbindings; i++) {
219 resp->bindings[i].var = pdu->bindings[i].var;
220 if ((tp = find_node(&pdu->bindings[i], &except)) == NULL) {
221 if (pdu->version == SNMP_V1) {
223 snmp_debug("get: nosuchname");
224 pdu->error_status = SNMP_ERR_NOSUCHNAME;
225 pdu->error_index = i + 1;
227 return (SNMP_RET_ERR);
230 snmp_debug("get: exception %u", except);
231 resp->bindings[i].syntax = except;
234 /* call the action to fetch the value. */
235 resp->bindings[i].syntax = tp->syntax;
236 ret = (*tp->op)(&context.ctx, &resp->bindings[i],
237 tp->oid.len, tp->index, SNMP_OP_GET);
239 snmp_debug("get: action returns %d", ret);
241 if (ret == SNMP_ERR_NOSUCHNAME) {
242 if (pdu->version == SNMP_V1) {
243 pdu->error_status = SNMP_ERR_NOSUCHNAME;
244 pdu->error_index = i + 1;
246 return (SNMP_RET_ERR);
249 snmp_debug("get: exception noSuchInstance");
250 resp->bindings[i].syntax = SNMP_SYNTAX_NOSUCHINSTANCE;
252 } else if (ret != SNMP_ERR_NOERROR) {
253 pdu->error_status = SNMP_ERR_GENERR;
254 pdu->error_index = i + 1;
256 return (SNMP_RET_ERR);
261 err = snmp_binding_encode(resp_b, &resp->bindings[i]);
263 if (err == ASN_ERR_EOBUF) {
264 pdu->error_status = SNMP_ERR_TOOBIG;
265 pdu->error_index = 0;
267 return (SNMP_RET_ERR);
269 if (err != ASN_ERR_OK) {
271 snmp_debug("get: binding encoding: %u", err);
272 pdu->error_status = SNMP_ERR_GENERR;
273 pdu->error_index = i + 1;
275 return (SNMP_RET_ERR);
279 if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
280 snmp_debug("get: failed to encode PDU");
281 return (SNMP_RET_ERR);
284 return (SNMP_RET_OK);
287 static struct snmp_node *
288 next_node(const struct snmp_value *value, int *pnext)
290 struct snmp_node *tp;
293 snmp_debug("next: searching %s",
294 asn_oid2str_r(&value->var, oidbuf));
297 for (tp = tree; tp < tree + tree_size; tp++) {
298 if (asn_is_suboid(&tp->oid, &value->var)) {
299 /* the tree OID is a sub-oid of the requested OID. */
300 if (tp->type == SNMP_NODE_LEAF) {
301 if (tp->oid.len == value->var.len) {
302 /* request for scalar type */
304 snmp_debug("next: found scalar %s",
305 asn_oid2str_r(&tp->oid, oidbuf));
311 snmp_debug("next: found column %s",
312 asn_oid2str_r(&tp->oid, oidbuf));
315 } else if (asn_is_suboid(&value->var, &tp->oid) ||
316 asn_compare_oid(&tp->oid, &value->var) >= 0) {
318 snmp_debug("next: found %s",
319 asn_oid2str_r(&tp->oid, oidbuf));
326 snmp_debug("next: failed");
332 do_getnext(struct context *context, const struct snmp_value *inb,
333 struct snmp_value *outb, struct snmp_pdu *pdu)
335 const struct snmp_node *tp;
338 if ((tp = next_node(inb, &next)) == NULL)
341 /* retain old variable if we are doing a GETNEXT on an exact
342 * matched leaf only */
343 if (tp->type == SNMP_NODE_LEAF || next)
346 outb->var = inb->var;
349 outb->syntax = tp->syntax;
350 if (tp->type == SNMP_NODE_LEAF) {
351 /* make a GET operation */
352 outb->var.subs[outb->var.len++] = 0;
353 ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
354 tp->index, SNMP_OP_GET);
357 ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
358 tp->index, SNMP_OP_GETNEXT);
360 if (ret != SNMP_ERR_NOSUCHNAME) {
362 if (ret != SNMP_ERR_NOERROR && TR(GETNEXT))
363 snmp_debug("getnext: %s returns %u",
364 asn_oid2str(&outb->var), ret);
368 /* object has no data - try next */
369 if (++tp == tree + tree_size)
373 snmp_debug("getnext: no data - avancing to %s",
374 asn_oid2str(&tp->oid));
379 if (ret == SNMP_ERR_NOSUCHNAME) {
381 outb->var = inb->var;
382 if (pdu->version == SNMP_V1) {
383 pdu->error_status = SNMP_ERR_NOSUCHNAME;
384 return (SNMP_RET_ERR);
386 outb->syntax = SNMP_SYNTAX_ENDOFMIBVIEW;
388 } else if (ret != SNMP_ERR_NOERROR) {
389 pdu->error_status = SNMP_ERR_GENERR;
390 return (SNMP_RET_ERR);
392 return (SNMP_RET_OK);
397 * Execute a GETNEXT operation. The tree is rooted at the global 'root'.
398 * Build the response PDU on the fly. The return is:
401 snmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b,
402 struct snmp_pdu *resp, void *data)
404 struct context context;
407 enum snmp_ret result;
409 memset(&context, 0, sizeof(context));
410 context.ctx.data = data;
412 snmp_pdu_create_response(pdu, resp);
414 if (snmp_pdu_encode_header(resp_b, resp))
415 return (SNMP_RET_IGN);
417 for (i = 0; i < pdu->nbindings; i++) {
418 result = do_getnext(&context, &pdu->bindings[i],
419 &resp->bindings[i], pdu);
421 if (result != SNMP_RET_OK) {
422 pdu->error_index = i + 1;
429 err = snmp_binding_encode(resp_b, &resp->bindings[i]);
431 if (err == ASN_ERR_EOBUF) {
432 pdu->error_status = SNMP_ERR_TOOBIG;
433 pdu->error_index = 0;
435 return (SNMP_RET_ERR);
437 if (err != ASN_ERR_OK) {
439 snmp_debug("getnext: binding encoding: %u", err);
440 pdu->error_status = SNMP_ERR_GENERR;
441 pdu->error_index = i + 1;
443 return (SNMP_RET_ERR);
447 if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
448 snmp_debug("getnext: failed to encode PDU");
449 return (SNMP_RET_ERR);
452 return (SNMP_RET_OK);
456 snmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b,
457 struct snmp_pdu *resp, void *data)
459 struct context context;
464 enum snmp_ret result;
467 memset(&context, 0, sizeof(context));
468 context.ctx.data = data;
470 snmp_pdu_create_response(pdu, resp);
472 if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
473 /* cannot even encode header - very bad */
474 return (SNMP_RET_IGN);
476 if ((non_rep = pdu->error_status) > pdu->nbindings)
477 non_rep = pdu->nbindings;
480 for (i = 0; i < non_rep; i++) {
481 result = do_getnext(&context, &pdu->bindings[i],
482 &resp->bindings[resp->nbindings], pdu);
484 if (result != SNMP_RET_OK) {
485 pdu->error_index = i + 1;
490 err = snmp_binding_encode(resp_b,
491 &resp->bindings[resp->nbindings++]);
493 if (err == ASN_ERR_EOBUF)
496 if (err != ASN_ERR_OK) {
498 snmp_debug("getnext: binding encoding: %u", err);
499 pdu->error_status = SNMP_ERR_GENERR;
500 pdu->error_index = i + 1;
502 return (SNMP_RET_ERR);
506 if (non_rep == pdu->nbindings)
510 for (cnt = 0; cnt < pdu->error_index; cnt++) {
512 for (i = non_rep; i < pdu->nbindings; i++) {
514 if (resp->nbindings == SNMP_MAX_BINDINGS)
519 result = do_getnext(&context, &pdu->bindings[i],
520 &resp->bindings[resp->nbindings], pdu);
522 result = do_getnext(&context,
523 &resp->bindings[resp->nbindings -
524 (pdu->nbindings - non_rep)],
525 &resp->bindings[resp->nbindings], pdu);
527 if (result != SNMP_RET_OK) {
528 pdu->error_index = i + 1;
532 if (resp->bindings[resp->nbindings].syntax !=
533 SNMP_SYNTAX_ENDOFMIBVIEW)
536 err = snmp_binding_encode(resp_b,
537 &resp->bindings[resp->nbindings++]);
539 if (err == ASN_ERR_EOBUF)
542 if (err != ASN_ERR_OK) {
544 snmp_debug("getnext: binding encoding: %u", err);
545 pdu->error_status = SNMP_ERR_GENERR;
546 pdu->error_index = i + 1;
548 return (SNMP_RET_ERR);
556 if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
557 snmp_debug("getnext: failed to encode PDU");
558 return (SNMP_RET_ERR);
561 return (SNMP_RET_OK);
565 * Rollback a SET operation. Failed index is 'i'.
568 rollback(struct context *context, struct snmp_pdu *pdu, u_int i)
570 struct snmp_value *b;
571 const struct snmp_node *np;
575 b = &pdu->bindings[i];
576 np = context->node[i];
578 context->ctx.scratch = &context->scratch[i];
580 ret = (*np->op)(&context->ctx, b, np->oid.len, np->index,
583 if (ret != SNMP_ERR_NOERROR) {
584 snmp_error("set: rollback failed (%d) on variable %s "
585 "index %u", ret, asn_oid2str(&b->var), i);
586 if (pdu->version != SNMP_V1) {
587 pdu->error_status = SNMP_ERR_UNDO_FAILED;
588 pdu->error_index = 0;
595 * Commit dependencies.
598 snmp_dep_commit(struct snmp_context *ctx)
600 struct context *context = (struct context *)ctx;
603 TAILQ_FOREACH(context->depend, &context->dlist, link) {
604 ctx->dep = &context->depend->dep;
607 snmp_debug("set: dependency commit %s",
608 asn_oid2str(&ctx->dep->obj));
610 ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT);
612 if (ret != SNMP_ERR_NOERROR) {
614 snmp_debug("set: dependency failed %d", ret);
618 return (SNMP_ERR_NOERROR);
622 * Rollback dependencies
625 snmp_dep_rollback(struct snmp_context *ctx)
627 struct context *context = (struct context *)ctx;
629 char objbuf[ASN_OIDSTRLEN];
630 char idxbuf[ASN_OIDSTRLEN];
632 ret1 = SNMP_ERR_NOERROR;
633 while ((context->depend =
634 TAILQ_PREV(context->depend, depend_list, link)) != NULL) {
635 ctx->dep = &context->depend->dep;
638 snmp_debug("set: dependency rollback %s",
639 asn_oid2str(&ctx->dep->obj));
641 ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK);
643 if (ret != SNMP_ERR_NOERROR) {
644 snmp_debug("set: dep rollback returns %u: %s %s", ret,
645 asn_oid2str_r(&ctx->dep->obj, objbuf),
646 asn_oid2str_r(&ctx->dep->idx, idxbuf));
647 if (ret1 == SNMP_ERR_NOERROR)
655 snmp_dep_finish(struct snmp_context *ctx)
657 struct context *context = (struct context *)ctx;
660 while ((d = TAILQ_FIRST(&context->dlist)) != NULL) {
662 (void)d->func(ctx, ctx->dep, SNMP_DEPOP_FINISH);
663 TAILQ_REMOVE(&context->dlist, d, link);
669 * Do a SET operation.
672 snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b,
673 struct snmp_pdu *resp, void *data)
678 struct context context;
679 const struct snmp_node *np;
680 struct snmp_value *b;
681 enum snmp_syntax except;
683 memset(&context, 0, sizeof(context));
684 TAILQ_INIT(&context.dlist);
685 context.ctx.data = data;
687 snmp_pdu_create_response(pdu, resp);
689 if (snmp_pdu_encode_header(resp_b, resp))
690 return (SNMP_RET_IGN);
693 * 1. Find all nodes, check that they are writeable and
694 * that the syntax is ok, copy over the binding to the response.
696 for (i = 0; i < pdu->nbindings; i++) {
697 b = &pdu->bindings[i];
699 if ((np = context.node[i] = find_node(b, &except)) == NULL) {
700 /* not found altogether or LEAF with wrong index */
702 snmp_debug("set: node not found %s",
703 asn_oid2str_r(&b->var, oidbuf));
704 if (pdu->version == SNMP_V1) {
705 pdu->error_index = i + 1;
706 pdu->error_status = SNMP_ERR_NOSUCHNAME;
707 } else if ((np = find_subnode(b)) != NULL) {
708 /* 2. intermediate object */
709 pdu->error_index = i + 1;
710 pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
711 } else if (except == SNMP_SYNTAX_NOSUCHOBJECT) {
712 pdu->error_index = i + 1;
713 pdu->error_status = SNMP_ERR_NO_ACCESS;
715 pdu->error_index = i + 1;
716 pdu->error_status = SNMP_ERR_NO_CREATION;
719 return (SNMP_RET_ERR);
722 * 2. write/createable?
723 * Can check this for leafs only, because in v2 we have
724 * to differentiate between NOT_WRITEABLE and NO_CREATION
725 * and only the action routine for COLUMNS knows, whether
728 if (np->type == SNMP_NODE_LEAF &&
729 !(np->flags & SNMP_NODE_CANSET)) {
730 if (pdu->version == SNMP_V1) {
731 pdu->error_index = i + 1;
732 pdu->error_status = SNMP_ERR_NOSUCHNAME;
734 pdu->error_index = i + 1;
735 pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
738 return (SNMP_RET_ERR);
741 * 3. Ensure the right syntax
743 if (np->syntax != b->syntax) {
744 if (pdu->version == SNMP_V1) {
745 pdu->error_index = i + 1;
746 pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */
748 pdu->error_index = i + 1;
749 pdu->error_status = SNMP_ERR_WRONG_TYPE;
752 return (SNMP_RET_ERR);
757 if (snmp_value_copy(&resp->bindings[i], b)) {
758 pdu->error_index = i + 1;
759 pdu->error_status = SNMP_ERR_GENERR;
761 return (SNMP_RET_ERR);
763 asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]);
764 if (asnerr == ASN_ERR_EOBUF) {
765 pdu->error_index = i + 1;
766 pdu->error_status = SNMP_ERR_TOOBIG;
768 return (SNMP_RET_ERR);
769 } else if (asnerr != ASN_ERR_OK) {
770 pdu->error_index = i + 1;
771 pdu->error_status = SNMP_ERR_GENERR;
773 return (SNMP_RET_ERR);
778 context.ctx.code = SNMP_RET_OK;
781 * 2. Call the SET method for each node. If a SET fails, rollback
782 * everything. Map error codes depending on the version.
784 for (i = 0; i < pdu->nbindings; i++) {
785 b = &pdu->bindings[i];
786 np = context.node[i];
788 context.ctx.var_index = i + 1;
789 context.ctx.scratch = &context.scratch[i];
791 ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
795 snmp_debug("set: action %s returns %d", np->name, ret);
797 if (pdu->version == SNMP_V1) {
799 case SNMP_ERR_NO_ACCESS:
800 ret = SNMP_ERR_NOSUCHNAME;
802 case SNMP_ERR_WRONG_TYPE:
803 /* should no happen */
804 ret = SNMP_ERR_BADVALUE;
806 case SNMP_ERR_WRONG_LENGTH:
807 ret = SNMP_ERR_BADVALUE;
809 case SNMP_ERR_WRONG_ENCODING:
810 /* should not happen */
811 ret = SNMP_ERR_BADVALUE;
813 case SNMP_ERR_WRONG_VALUE:
814 ret = SNMP_ERR_BADVALUE;
816 case SNMP_ERR_NO_CREATION:
817 ret = SNMP_ERR_NOSUCHNAME;
819 case SNMP_ERR_INCONS_VALUE:
820 ret = SNMP_ERR_BADVALUE;
822 case SNMP_ERR_RES_UNAVAIL:
823 ret = SNMP_ERR_GENERR;
825 case SNMP_ERR_COMMIT_FAILED:
826 ret = SNMP_ERR_GENERR;
828 case SNMP_ERR_UNDO_FAILED:
829 ret = SNMP_ERR_GENERR;
831 case SNMP_ERR_AUTH_ERR:
832 /* should not happen */
833 ret = SNMP_ERR_GENERR;
835 case SNMP_ERR_NOT_WRITEABLE:
836 ret = SNMP_ERR_NOSUCHNAME;
838 case SNMP_ERR_INCONS_NAME:
839 ret = SNMP_ERR_BADVALUE;
843 if (ret != SNMP_ERR_NOERROR) {
844 pdu->error_index = i + 1;
845 pdu->error_status = ret;
847 rollback(&context, pdu, i);
850 context.ctx.code = SNMP_RET_ERR;
857 * 3. Call dependencies
860 snmp_debug("set: set operations ok");
862 if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) {
863 pdu->error_status = ret;
864 pdu->error_index = context.ctx.var_index;
866 if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) {
867 if (pdu->version != SNMP_V1) {
868 pdu->error_status = SNMP_ERR_UNDO_FAILED;
869 pdu->error_index = 0;
872 rollback(&context, pdu, i);
875 context.ctx.code = SNMP_RET_ERR;
881 * 4. Commit and copy values from the original packet to the response.
882 * This is not the commit operation from RFC 1905 but rather an
883 * 'FREE RESOURCES' operation. It shouldn't fail.
886 snmp_debug("set: commiting");
888 for (i = 0; i < pdu->nbindings; i++) {
889 b = &resp->bindings[i];
890 np = context.node[i];
892 context.ctx.var_index = i + 1;
893 context.ctx.scratch = &context.scratch[i];
895 ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
898 if (ret != SNMP_ERR_NOERROR)
899 snmp_error("set: commit failed (%d) on"
900 " variable %s index %u", ret,
901 asn_oid2str_r(&b->var, oidbuf), i);
904 if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
905 snmp_error("set: fix_encoding failed");
907 context.ctx.code = SNMP_RET_IGN;
914 snmp_dep_finish(&context.ctx);
917 snmp_debug("set: returning %d", context.ctx.code);
919 return (context.ctx.code);
922 * Lookup a dependency. If it doesn't exist, create one
924 struct snmp_dependency *
925 snmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj,
926 const struct asn_oid *idx, size_t len, snmp_depop_t func)
928 struct context *context;
931 context = (struct context *)(void *)
932 ((char *)ctx - offsetof(struct context, ctx));
934 snmp_debug("depend: looking for %s", asn_oid2str(obj));
936 snmp_debug("depend: index is %s", asn_oid2str(idx));
938 TAILQ_FOREACH(d, &context->dlist, link)
939 if (asn_compare_oid(obj, &d->dep.obj) == 0 &&
940 ((idx == NULL && d->dep.idx.len == 0) ||
941 (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) {
943 snmp_debug("depend: found");
948 snmp_debug("depend: creating");
950 if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL)
952 memset(&d->dep, 0, len);
962 TAILQ_INSERT_TAIL(&context->dlist, d, link);
968 * Make an error response from a PDU. We do this without decoding the
969 * variable bindings. This means we can sent the junk back to a caller
970 * that has sent us junk in the first place.
973 snmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b,
974 struct asn_buf *resp_b)
978 struct snmp_pdu resp;
982 snmp_pdu_create_response(pdu, &resp);
984 if ((code = snmp_pdu_decode_header(pdu_b, &resp)) != SNMP_CODE_OK)
985 return (SNMP_RET_IGN);
987 if (pdu->version == SNMP_V3) {
988 if (resp.user.priv_proto != SNMP_PRIV_NOPRIV &&
989 (asn_get_header(pdu_b, &type, &resp.scoped_len) != ASN_ERR_OK
990 || type != ASN_TYPE_OCTETSTRING)) {
991 snmp_error("cannot decode encrypted pdu");
992 return (SNMP_RET_IGN);
995 if (asn_get_sequence(pdu_b, &len) != ASN_ERR_OK) {
996 snmp_error("cannot decode scoped pdu header");
997 return (SNMP_RET_IGN);
1000 len = SNMP_ENGINE_ID_SIZ;
1001 if (asn_get_octetstring(pdu_b, (u_char *)resp.context_engine,
1002 &len) != ASN_ERR_OK) {
1003 snmp_error("cannot decode msg context engine");
1004 return (SNMP_RET_IGN);
1006 resp.context_engine_len = len;
1007 len = SNMP_CONTEXT_NAME_SIZ;
1008 if (asn_get_octetstring(pdu_b, (u_char *)resp.context_name,
1009 &len) != ASN_ERR_OK) {
1010 snmp_error("cannot decode msg context name");
1011 return (SNMP_RET_IGN);
1013 resp.context_name[len] = '\0';
1017 if (asn_get_header(pdu_b, &type, &len) != ASN_ERR_OK) {
1018 snmp_error("cannot get pdu header");
1019 return (SNMP_RET_IGN);
1022 if ((type & ~ASN_TYPE_MASK) !=
1023 (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT)) {
1024 snmp_error("bad pdu header tag");
1025 return (SNMP_RET_IGN);
1028 err = snmp_parse_pdus_hdr(pdu_b, &resp, &len);
1029 if (ASN_ERR_STOPPED(err))
1030 return (SNMP_RET_IGN);
1031 if (pdu_b->asn_len < len)
1032 return (SNMP_RET_IGN);
1033 pdu_b->asn_len = len;
1035 /* now we have the bindings left - construct new message */
1036 resp.error_status = pdu->error_status;
1037 resp.error_index = pdu->error_index;
1038 resp.type = SNMP_PDU_RESPONSE;
1040 code = snmp_pdu_encode_header(resp_b, &resp);
1041 if (code != SNMP_CODE_OK)
1042 return (SNMP_RET_IGN);
1044 if (pdu_b->asn_len > resp_b->asn_len)
1046 return (SNMP_RET_IGN);
1047 (void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len);
1048 resp_b->asn_len -= pdu_b->asn_len;
1049 resp_b->asn_ptr += pdu_b->asn_len;
1051 code = snmp_fix_encoding(resp_b, &resp);
1052 if (code != SNMP_CODE_OK)
1053 return (SNMP_RET_IGN);
1055 return (SNMP_RET_OK);
1059 snmp_debug_func(const char *fmt, ...)
1064 vfprintf(stderr, fmt, ap);
1066 fprintf(stderr, "\n");