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(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 return (snmp_fix_encoding(resp_b, resp));
282 static struct snmp_node *
283 next_node(const struct snmp_value *value, int *pnext)
285 struct snmp_node *tp;
288 snmp_debug("next: searching %s",
289 asn_oid2str_r(&value->var, oidbuf));
292 for (tp = tree; tp < tree + tree_size; tp++) {
293 if (asn_is_suboid(&tp->oid, &value->var)) {
294 /* the tree OID is a sub-oid of the requested OID. */
295 if (tp->type == SNMP_NODE_LEAF) {
296 if (tp->oid.len == value->var.len) {
297 /* request for scalar type */
299 snmp_debug("next: found scalar %s",
300 asn_oid2str_r(&tp->oid, oidbuf));
306 snmp_debug("next: found column %s",
307 asn_oid2str_r(&tp->oid, oidbuf));
310 } else if (asn_is_suboid(&value->var, &tp->oid) ||
311 asn_compare_oid(&tp->oid, &value->var) >= 0) {
313 snmp_debug("next: found %s",
314 asn_oid2str_r(&tp->oid, oidbuf));
321 snmp_debug("next: failed");
327 do_getnext(struct context *context, const struct snmp_value *inb,
328 struct snmp_value *outb, struct snmp_pdu *pdu)
330 const struct snmp_node *tp;
333 if ((tp = next_node(inb, &next)) == NULL)
336 /* retain old variable if we are doing a GETNEXT on an exact
337 * matched leaf only */
338 if (tp->type == SNMP_NODE_LEAF || next)
341 outb->var = inb->var;
344 outb->syntax = tp->syntax;
345 if (tp->type == SNMP_NODE_LEAF) {
346 /* make a GET operation */
347 outb->var.subs[outb->var.len++] = 0;
348 ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
349 tp->index, SNMP_OP_GET);
352 ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
353 tp->index, SNMP_OP_GETNEXT);
355 if (ret != SNMP_ERR_NOSUCHNAME) {
357 if (ret != SNMP_ERR_NOERROR && TR(GETNEXT))
358 snmp_debug("getnext: %s returns %u",
359 asn_oid2str(&outb->var), ret);
363 /* object has no data - try next */
364 if (++tp == tree + tree_size)
368 snmp_debug("getnext: no data - avancing to %s",
369 asn_oid2str(&tp->oid));
374 if (ret == SNMP_ERR_NOSUCHNAME) {
376 outb->var = inb->var;
377 if (pdu->version == SNMP_V1) {
378 pdu->error_status = SNMP_ERR_NOSUCHNAME;
379 return (SNMP_RET_ERR);
381 outb->syntax = SNMP_SYNTAX_ENDOFMIBVIEW;
383 } else if (ret != SNMP_ERR_NOERROR) {
384 pdu->error_status = SNMP_ERR_GENERR;
385 return (SNMP_RET_ERR);
387 return (SNMP_RET_OK);
392 * Execute a GETNEXT operation. The tree is rooted at the global 'root'.
393 * Build the response PDU on the fly. The return is:
396 snmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b,
397 struct snmp_pdu *resp, void *data)
399 struct context context;
402 enum snmp_ret result;
404 memset(&context, 0, sizeof(context));
405 context.ctx.data = data;
407 snmp_pdu_create_response(pdu, resp);
409 if (snmp_pdu_encode_header(resp_b, resp))
410 return (SNMP_RET_IGN);
412 for (i = 0; i < pdu->nbindings; i++) {
413 result = do_getnext(&context, &pdu->bindings[i],
414 &resp->bindings[i], pdu);
416 if (result != SNMP_RET_OK) {
417 pdu->error_index = i + 1;
424 err = snmp_binding_encode(resp_b, &resp->bindings[i]);
426 if (err == ASN_ERR_EOBUF) {
427 pdu->error_status = SNMP_ERR_TOOBIG;
428 pdu->error_index = 0;
430 return (SNMP_RET_ERR);
432 if (err != ASN_ERR_OK) {
434 snmp_debug("getnext: binding encoding: %u", err);
435 pdu->error_status = SNMP_ERR_GENERR;
436 pdu->error_index = i + 1;
438 return (SNMP_RET_ERR);
441 return (snmp_fix_encoding(resp_b, resp));
445 snmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b,
446 struct snmp_pdu *resp, void *data)
448 struct context context;
453 enum snmp_ret result;
456 memset(&context, 0, sizeof(context));
457 context.ctx.data = data;
459 snmp_pdu_create_response(pdu, resp);
461 if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
462 /* cannot even encode header - very bad */
463 return (SNMP_RET_IGN);
465 if ((non_rep = pdu->error_status) > pdu->nbindings)
466 non_rep = pdu->nbindings;
469 for (i = 0; i < non_rep; i++) {
470 result = do_getnext(&context, &pdu->bindings[i],
471 &resp->bindings[resp->nbindings], pdu);
473 if (result != SNMP_RET_OK) {
474 pdu->error_index = i + 1;
479 err = snmp_binding_encode(resp_b,
480 &resp->bindings[resp->nbindings++]);
482 if (err == ASN_ERR_EOBUF)
485 if (err != ASN_ERR_OK) {
487 snmp_debug("getnext: binding encoding: %u", err);
488 pdu->error_status = SNMP_ERR_GENERR;
489 pdu->error_index = i + 1;
491 return (SNMP_RET_ERR);
495 if (non_rep == pdu->nbindings)
499 for (cnt = 0; cnt < pdu->error_index; cnt++) {
501 for (i = non_rep; i < pdu->nbindings; i++) {
503 result = do_getnext(&context, &pdu->bindings[i],
504 &resp->bindings[resp->nbindings], pdu);
506 result = do_getnext(&context,
507 &resp->bindings[resp->nbindings -
508 (pdu->nbindings - non_rep)],
509 &resp->bindings[resp->nbindings], pdu);
511 if (result != SNMP_RET_OK) {
512 pdu->error_index = i + 1;
516 if (resp->bindings[resp->nbindings].syntax !=
517 SNMP_SYNTAX_ENDOFMIBVIEW)
520 err = snmp_binding_encode(resp_b,
521 &resp->bindings[resp->nbindings++]);
523 if (err == ASN_ERR_EOBUF)
526 if (err != ASN_ERR_OK) {
528 snmp_debug("getnext: binding encoding: %u", err);
529 pdu->error_status = SNMP_ERR_GENERR;
530 pdu->error_index = i + 1;
532 return (SNMP_RET_ERR);
540 return (snmp_fix_encoding(resp_b, resp));
544 * Rollback a SET operation. Failed index is 'i'.
547 rollback(struct context *context, struct snmp_pdu *pdu, u_int i)
549 struct snmp_value *b;
550 const struct snmp_node *np;
554 b = &pdu->bindings[i];
555 np = context->node[i];
557 context->ctx.scratch = &context->scratch[i];
559 ret = (*np->op)(&context->ctx, b, np->oid.len, np->index,
562 if (ret != SNMP_ERR_NOERROR) {
563 snmp_error("set: rollback failed (%d) on variable %s "
564 "index %u", ret, asn_oid2str(&b->var), i);
565 if (pdu->version != SNMP_V1) {
566 pdu->error_status = SNMP_ERR_UNDO_FAILED;
567 pdu->error_index = 0;
574 * Commit dependencies.
577 snmp_dep_commit(struct snmp_context *ctx)
579 struct context *context = (struct context *)ctx;
582 TAILQ_FOREACH(context->depend, &context->dlist, link) {
583 ctx->dep = &context->depend->dep;
586 snmp_debug("set: dependency commit %s",
587 asn_oid2str(&ctx->dep->obj));
589 ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT);
591 if (ret != SNMP_ERR_NOERROR) {
593 snmp_debug("set: dependency failed %d", ret);
597 return (SNMP_ERR_NOERROR);
601 * Rollback dependencies
604 snmp_dep_rollback(struct snmp_context *ctx)
606 struct context *context = (struct context *)ctx;
608 char objbuf[ASN_OIDSTRLEN];
609 char idxbuf[ASN_OIDSTRLEN];
611 ret1 = SNMP_ERR_NOERROR;
612 while ((context->depend =
613 TAILQ_PREV(context->depend, depend_list, link)) != NULL) {
614 ctx->dep = &context->depend->dep;
617 snmp_debug("set: dependency rollback %s",
618 asn_oid2str(&ctx->dep->obj));
620 ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK);
622 if (ret != SNMP_ERR_NOERROR) {
623 snmp_debug("set: dep rollback returns %u: %s %s", ret,
624 asn_oid2str_r(&ctx->dep->obj, objbuf),
625 asn_oid2str_r(&ctx->dep->idx, idxbuf));
626 if (ret1 == SNMP_ERR_NOERROR)
634 snmp_dep_finish(struct snmp_context *ctx)
636 struct context *context = (struct context *)ctx;
639 while ((d = TAILQ_FIRST(&context->dlist)) != NULL) {
641 (void)d->func(ctx, ctx->dep, SNMP_DEPOP_FINISH);
642 TAILQ_REMOVE(&context->dlist, d, link);
648 * Do a SET operation.
651 snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b,
652 struct snmp_pdu *resp, void *data)
657 struct context context;
658 const struct snmp_node *np;
659 struct snmp_value *b;
660 enum snmp_syntax except;
662 memset(&context, 0, sizeof(context));
663 TAILQ_INIT(&context.dlist);
664 context.ctx.data = data;
666 snmp_pdu_create_response(pdu, resp);
668 if (snmp_pdu_encode_header(resp_b, resp))
669 return (SNMP_RET_IGN);
672 * 1. Find all nodes, check that they are writeable and
673 * that the syntax is ok, copy over the binding to the response.
675 for (i = 0; i < pdu->nbindings; i++) {
676 b = &pdu->bindings[i];
678 if ((np = context.node[i] = find_node(b, &except)) == NULL) {
679 /* not found altogether or LEAF with wrong index */
681 snmp_debug("set: node not found %s",
682 asn_oid2str_r(&b->var, oidbuf));
683 if (pdu->version == SNMP_V1) {
684 pdu->error_index = i + 1;
685 pdu->error_status = SNMP_ERR_NOSUCHNAME;
686 } else if ((np = find_subnode(b)) != NULL) {
687 /* 2. intermediate object */
688 pdu->error_index = i + 1;
689 pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
690 } else if (except == SNMP_SYNTAX_NOSUCHOBJECT) {
691 pdu->error_index = i + 1;
692 pdu->error_status = SNMP_ERR_NO_ACCESS;
694 pdu->error_index = i + 1;
695 pdu->error_status = SNMP_ERR_NO_CREATION;
698 return (SNMP_RET_ERR);
701 * 2. write/createable?
702 * Can check this for leafs only, because in v2 we have
703 * to differentiate between NOT_WRITEABLE and NO_CREATION
704 * and only the action routine for COLUMNS knows, whether
707 if (np->type == SNMP_NODE_LEAF &&
708 !(np->flags & SNMP_NODE_CANSET)) {
709 if (pdu->version == SNMP_V1) {
710 pdu->error_index = i + 1;
711 pdu->error_status = SNMP_ERR_NOSUCHNAME;
713 pdu->error_index = i + 1;
714 pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
717 return (SNMP_RET_ERR);
720 * 3. Ensure the right syntax
722 if (np->syntax != b->syntax) {
723 if (pdu->version == SNMP_V1) {
724 pdu->error_index = i + 1;
725 pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */
727 pdu->error_index = i + 1;
728 pdu->error_status = SNMP_ERR_WRONG_TYPE;
731 return (SNMP_RET_ERR);
736 if (snmp_value_copy(&resp->bindings[i], b)) {
737 pdu->error_index = i + 1;
738 pdu->error_status = SNMP_ERR_GENERR;
740 return (SNMP_RET_ERR);
742 asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]);
743 if (asnerr == ASN_ERR_EOBUF) {
744 pdu->error_index = i + 1;
745 pdu->error_status = SNMP_ERR_TOOBIG;
747 return (SNMP_RET_ERR);
748 } else if (asnerr != ASN_ERR_OK) {
749 pdu->error_index = i + 1;
750 pdu->error_status = SNMP_ERR_GENERR;
752 return (SNMP_RET_ERR);
757 context.ctx.code = SNMP_RET_OK;
760 * 2. Call the SET method for each node. If a SET fails, rollback
761 * everything. Map error codes depending on the version.
763 for (i = 0; i < pdu->nbindings; i++) {
764 b = &pdu->bindings[i];
765 np = context.node[i];
767 context.ctx.var_index = i + 1;
768 context.ctx.scratch = &context.scratch[i];
770 ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
774 snmp_debug("set: action %s returns %d", np->name, ret);
776 if (pdu->version == SNMP_V1) {
778 case SNMP_ERR_NO_ACCESS:
779 ret = SNMP_ERR_NOSUCHNAME;
781 case SNMP_ERR_WRONG_TYPE:
782 /* should no happen */
783 ret = SNMP_ERR_BADVALUE;
785 case SNMP_ERR_WRONG_LENGTH:
786 ret = SNMP_ERR_BADVALUE;
788 case SNMP_ERR_WRONG_ENCODING:
789 /* should not happen */
790 ret = SNMP_ERR_BADVALUE;
792 case SNMP_ERR_WRONG_VALUE:
793 ret = SNMP_ERR_BADVALUE;
795 case SNMP_ERR_NO_CREATION:
796 ret = SNMP_ERR_NOSUCHNAME;
798 case SNMP_ERR_INCONS_VALUE:
799 ret = SNMP_ERR_BADVALUE;
801 case SNMP_ERR_RES_UNAVAIL:
802 ret = SNMP_ERR_GENERR;
804 case SNMP_ERR_COMMIT_FAILED:
805 ret = SNMP_ERR_GENERR;
807 case SNMP_ERR_UNDO_FAILED:
808 ret = SNMP_ERR_GENERR;
810 case SNMP_ERR_AUTH_ERR:
811 /* should not happen */
812 ret = SNMP_ERR_GENERR;
814 case SNMP_ERR_NOT_WRITEABLE:
815 ret = SNMP_ERR_NOSUCHNAME;
817 case SNMP_ERR_INCONS_NAME:
818 ret = SNMP_ERR_BADVALUE;
822 if (ret != SNMP_ERR_NOERROR) {
823 pdu->error_index = i + 1;
824 pdu->error_status = ret;
826 rollback(&context, pdu, i);
829 context.ctx.code = SNMP_RET_ERR;
836 * 3. Call dependencies
839 snmp_debug("set: set operations ok");
841 if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) {
842 pdu->error_status = ret;
843 pdu->error_index = context.ctx.var_index;
845 if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) {
846 if (pdu->version != SNMP_V1) {
847 pdu->error_status = SNMP_ERR_UNDO_FAILED;
848 pdu->error_index = 0;
851 rollback(&context, pdu, i);
854 context.ctx.code = SNMP_RET_ERR;
860 * 4. Commit and copy values from the original packet to the response.
861 * This is not the commit operation from RFC 1905 but rather an
862 * 'FREE RESOURCES' operation. It shouldn't fail.
865 snmp_debug("set: commiting");
867 for (i = 0; i < pdu->nbindings; i++) {
868 b = &resp->bindings[i];
869 np = context.node[i];
871 context.ctx.var_index = i + 1;
872 context.ctx.scratch = &context.scratch[i];
874 ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
877 if (ret != SNMP_ERR_NOERROR)
878 snmp_error("set: commit failed (%d) on"
879 " variable %s index %u", ret,
880 asn_oid2str_r(&b->var, oidbuf), i);
883 if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
884 snmp_error("set: fix_encoding failed");
886 context.ctx.code = SNMP_RET_IGN;
893 snmp_dep_finish(&context.ctx);
896 snmp_debug("set: returning %d", context.ctx.code);
898 return (context.ctx.code);
901 * Lookup a dependency. If it doesn't exist, create one
903 struct snmp_dependency *
904 snmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj,
905 const struct asn_oid *idx, size_t len, snmp_depop_t func)
907 struct context *context;
910 context = (struct context *)(void *)
911 ((char *)ctx - offsetof(struct context, ctx));
913 snmp_debug("depend: looking for %s", asn_oid2str(obj));
915 snmp_debug("depend: index is %s", asn_oid2str(idx));
917 TAILQ_FOREACH(d, &context->dlist, link)
918 if (asn_compare_oid(obj, &d->dep.obj) == 0 &&
919 ((idx == NULL && d->dep.idx.len == 0) ||
920 (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) {
922 snmp_debug("depend: found");
927 snmp_debug("depend: creating");
929 if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL)
931 memset(&d->dep, 0, len);
941 TAILQ_INSERT_TAIL(&context->dlist, d, link);
947 * Make an error response from a PDU. We do this without decoding the
948 * variable bindings. This means we can sent the junk back to a caller
949 * that has sent us junk in the first place.
952 snmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b,
953 struct asn_buf *resp_b)
956 struct snmp_pdu resp;
960 memset(&resp, 0, sizeof(resp));
961 if ((code = snmp_pdu_decode_header(pdu_b, &resp)) != SNMP_CODE_OK)
962 return (SNMP_RET_IGN);
964 if (pdu_b->asn_len < len)
965 return (SNMP_RET_IGN);
966 pdu_b->asn_len = len;
968 err = snmp_parse_pdus_hdr(pdu_b, &resp, &len);
969 if (ASN_ERR_STOPPED(err))
970 return (SNMP_RET_IGN);
971 if (pdu_b->asn_len < len)
972 return (SNMP_RET_IGN);
973 pdu_b->asn_len = len;
975 /* now we have the bindings left - construct new message */
976 resp.error_status = pdu->error_status;
977 resp.error_index = pdu->error_index;
978 resp.type = SNMP_PDU_RESPONSE;
980 code = snmp_pdu_encode_header(resp_b, &resp);
981 if (code != SNMP_CODE_OK)
982 return (SNMP_RET_IGN);
984 if (pdu_b->asn_len > resp_b->asn_len)
986 return (SNMP_RET_IGN);
987 (void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len);
988 resp_b->asn_len -= pdu_b->asn_len;
989 resp_b->asn_ptr += pdu_b->asn_len;
991 code = snmp_fix_encoding(resp_b, &resp);
992 if (code != SNMP_CODE_OK)
993 return (SNMP_RET_IGN);
995 return (SNMP_RET_OK);
999 snmp_debug_func(const char *fmt, ...)
1004 vfprintf(stderr, fmt, ap);
1006 fprintf(stderr, "\n");