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 if (resp->nbindings == SNMP_MAX_BINDINGS)
508 result = do_getnext(&context, &pdu->bindings[i],
509 &resp->bindings[resp->nbindings], pdu);
511 result = do_getnext(&context,
512 &resp->bindings[resp->nbindings -
513 (pdu->nbindings - non_rep)],
514 &resp->bindings[resp->nbindings], pdu);
516 if (result != SNMP_RET_OK) {
517 pdu->error_index = i + 1;
521 if (resp->bindings[resp->nbindings].syntax !=
522 SNMP_SYNTAX_ENDOFMIBVIEW)
525 err = snmp_binding_encode(resp_b,
526 &resp->bindings[resp->nbindings++]);
528 if (err == ASN_ERR_EOBUF)
531 if (err != ASN_ERR_OK) {
533 snmp_debug("getnext: binding encoding: %u", err);
534 pdu->error_status = SNMP_ERR_GENERR;
535 pdu->error_index = i + 1;
537 return (SNMP_RET_ERR);
545 return (snmp_fix_encoding(resp_b, resp));
549 * Rollback a SET operation. Failed index is 'i'.
552 rollback(struct context *context, struct snmp_pdu *pdu, u_int i)
554 struct snmp_value *b;
555 const struct snmp_node *np;
559 b = &pdu->bindings[i];
560 np = context->node[i];
562 context->ctx.scratch = &context->scratch[i];
564 ret = (*np->op)(&context->ctx, b, np->oid.len, np->index,
567 if (ret != SNMP_ERR_NOERROR) {
568 snmp_error("set: rollback failed (%d) on variable %s "
569 "index %u", ret, asn_oid2str(&b->var), i);
570 if (pdu->version != SNMP_V1) {
571 pdu->error_status = SNMP_ERR_UNDO_FAILED;
572 pdu->error_index = 0;
579 * Commit dependencies.
582 snmp_dep_commit(struct snmp_context *ctx)
584 struct context *context = (struct context *)ctx;
587 TAILQ_FOREACH(context->depend, &context->dlist, link) {
588 ctx->dep = &context->depend->dep;
591 snmp_debug("set: dependency commit %s",
592 asn_oid2str(&ctx->dep->obj));
594 ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT);
596 if (ret != SNMP_ERR_NOERROR) {
598 snmp_debug("set: dependency failed %d", ret);
602 return (SNMP_ERR_NOERROR);
606 * Rollback dependencies
609 snmp_dep_rollback(struct snmp_context *ctx)
611 struct context *context = (struct context *)ctx;
613 char objbuf[ASN_OIDSTRLEN];
614 char idxbuf[ASN_OIDSTRLEN];
616 ret1 = SNMP_ERR_NOERROR;
617 while ((context->depend =
618 TAILQ_PREV(context->depend, depend_list, link)) != NULL) {
619 ctx->dep = &context->depend->dep;
622 snmp_debug("set: dependency rollback %s",
623 asn_oid2str(&ctx->dep->obj));
625 ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK);
627 if (ret != SNMP_ERR_NOERROR) {
628 snmp_debug("set: dep rollback returns %u: %s %s", ret,
629 asn_oid2str_r(&ctx->dep->obj, objbuf),
630 asn_oid2str_r(&ctx->dep->idx, idxbuf));
631 if (ret1 == SNMP_ERR_NOERROR)
639 snmp_dep_finish(struct snmp_context *ctx)
641 struct context *context = (struct context *)ctx;
644 while ((d = TAILQ_FIRST(&context->dlist)) != NULL) {
646 (void)d->func(ctx, ctx->dep, SNMP_DEPOP_FINISH);
647 TAILQ_REMOVE(&context->dlist, d, link);
653 * Do a SET operation.
656 snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b,
657 struct snmp_pdu *resp, void *data)
662 struct context context;
663 const struct snmp_node *np;
664 struct snmp_value *b;
665 enum snmp_syntax except;
667 memset(&context, 0, sizeof(context));
668 TAILQ_INIT(&context.dlist);
669 context.ctx.data = data;
671 snmp_pdu_create_response(pdu, resp);
673 if (snmp_pdu_encode_header(resp_b, resp))
674 return (SNMP_RET_IGN);
677 * 1. Find all nodes, check that they are writeable and
678 * that the syntax is ok, copy over the binding to the response.
680 for (i = 0; i < pdu->nbindings; i++) {
681 b = &pdu->bindings[i];
683 if ((np = context.node[i] = find_node(b, &except)) == NULL) {
684 /* not found altogether or LEAF with wrong index */
686 snmp_debug("set: node not found %s",
687 asn_oid2str_r(&b->var, oidbuf));
688 if (pdu->version == SNMP_V1) {
689 pdu->error_index = i + 1;
690 pdu->error_status = SNMP_ERR_NOSUCHNAME;
691 } else if ((np = find_subnode(b)) != NULL) {
692 /* 2. intermediate object */
693 pdu->error_index = i + 1;
694 pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
695 } else if (except == SNMP_SYNTAX_NOSUCHOBJECT) {
696 pdu->error_index = i + 1;
697 pdu->error_status = SNMP_ERR_NO_ACCESS;
699 pdu->error_index = i + 1;
700 pdu->error_status = SNMP_ERR_NO_CREATION;
703 return (SNMP_RET_ERR);
706 * 2. write/createable?
707 * Can check this for leafs only, because in v2 we have
708 * to differentiate between NOT_WRITEABLE and NO_CREATION
709 * and only the action routine for COLUMNS knows, whether
712 if (np->type == SNMP_NODE_LEAF &&
713 !(np->flags & SNMP_NODE_CANSET)) {
714 if (pdu->version == SNMP_V1) {
715 pdu->error_index = i + 1;
716 pdu->error_status = SNMP_ERR_NOSUCHNAME;
718 pdu->error_index = i + 1;
719 pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
722 return (SNMP_RET_ERR);
725 * 3. Ensure the right syntax
727 if (np->syntax != b->syntax) {
728 if (pdu->version == SNMP_V1) {
729 pdu->error_index = i + 1;
730 pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */
732 pdu->error_index = i + 1;
733 pdu->error_status = SNMP_ERR_WRONG_TYPE;
736 return (SNMP_RET_ERR);
741 if (snmp_value_copy(&resp->bindings[i], b)) {
742 pdu->error_index = i + 1;
743 pdu->error_status = SNMP_ERR_GENERR;
745 return (SNMP_RET_ERR);
747 asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]);
748 if (asnerr == ASN_ERR_EOBUF) {
749 pdu->error_index = i + 1;
750 pdu->error_status = SNMP_ERR_TOOBIG;
752 return (SNMP_RET_ERR);
753 } else if (asnerr != ASN_ERR_OK) {
754 pdu->error_index = i + 1;
755 pdu->error_status = SNMP_ERR_GENERR;
757 return (SNMP_RET_ERR);
762 context.ctx.code = SNMP_RET_OK;
765 * 2. Call the SET method for each node. If a SET fails, rollback
766 * everything. Map error codes depending on the version.
768 for (i = 0; i < pdu->nbindings; i++) {
769 b = &pdu->bindings[i];
770 np = context.node[i];
772 context.ctx.var_index = i + 1;
773 context.ctx.scratch = &context.scratch[i];
775 ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
779 snmp_debug("set: action %s returns %d", np->name, ret);
781 if (pdu->version == SNMP_V1) {
783 case SNMP_ERR_NO_ACCESS:
784 ret = SNMP_ERR_NOSUCHNAME;
786 case SNMP_ERR_WRONG_TYPE:
787 /* should no happen */
788 ret = SNMP_ERR_BADVALUE;
790 case SNMP_ERR_WRONG_LENGTH:
791 ret = SNMP_ERR_BADVALUE;
793 case SNMP_ERR_WRONG_ENCODING:
794 /* should not happen */
795 ret = SNMP_ERR_BADVALUE;
797 case SNMP_ERR_WRONG_VALUE:
798 ret = SNMP_ERR_BADVALUE;
800 case SNMP_ERR_NO_CREATION:
801 ret = SNMP_ERR_NOSUCHNAME;
803 case SNMP_ERR_INCONS_VALUE:
804 ret = SNMP_ERR_BADVALUE;
806 case SNMP_ERR_RES_UNAVAIL:
807 ret = SNMP_ERR_GENERR;
809 case SNMP_ERR_COMMIT_FAILED:
810 ret = SNMP_ERR_GENERR;
812 case SNMP_ERR_UNDO_FAILED:
813 ret = SNMP_ERR_GENERR;
815 case SNMP_ERR_AUTH_ERR:
816 /* should not happen */
817 ret = SNMP_ERR_GENERR;
819 case SNMP_ERR_NOT_WRITEABLE:
820 ret = SNMP_ERR_NOSUCHNAME;
822 case SNMP_ERR_INCONS_NAME:
823 ret = SNMP_ERR_BADVALUE;
827 if (ret != SNMP_ERR_NOERROR) {
828 pdu->error_index = i + 1;
829 pdu->error_status = ret;
831 rollback(&context, pdu, i);
834 context.ctx.code = SNMP_RET_ERR;
841 * 3. Call dependencies
844 snmp_debug("set: set operations ok");
846 if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) {
847 pdu->error_status = ret;
848 pdu->error_index = context.ctx.var_index;
850 if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) {
851 if (pdu->version != SNMP_V1) {
852 pdu->error_status = SNMP_ERR_UNDO_FAILED;
853 pdu->error_index = 0;
856 rollback(&context, pdu, i);
859 context.ctx.code = SNMP_RET_ERR;
865 * 4. Commit and copy values from the original packet to the response.
866 * This is not the commit operation from RFC 1905 but rather an
867 * 'FREE RESOURCES' operation. It shouldn't fail.
870 snmp_debug("set: commiting");
872 for (i = 0; i < pdu->nbindings; i++) {
873 b = &resp->bindings[i];
874 np = context.node[i];
876 context.ctx.var_index = i + 1;
877 context.ctx.scratch = &context.scratch[i];
879 ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
882 if (ret != SNMP_ERR_NOERROR)
883 snmp_error("set: commit failed (%d) on"
884 " variable %s index %u", ret,
885 asn_oid2str_r(&b->var, oidbuf), i);
888 if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
889 snmp_error("set: fix_encoding failed");
891 context.ctx.code = SNMP_RET_IGN;
898 snmp_dep_finish(&context.ctx);
901 snmp_debug("set: returning %d", context.ctx.code);
903 return (context.ctx.code);
906 * Lookup a dependency. If it doesn't exist, create one
908 struct snmp_dependency *
909 snmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj,
910 const struct asn_oid *idx, size_t len, snmp_depop_t func)
912 struct context *context;
915 context = (struct context *)(void *)
916 ((char *)ctx - offsetof(struct context, ctx));
918 snmp_debug("depend: looking for %s", asn_oid2str(obj));
920 snmp_debug("depend: index is %s", asn_oid2str(idx));
922 TAILQ_FOREACH(d, &context->dlist, link)
923 if (asn_compare_oid(obj, &d->dep.obj) == 0 &&
924 ((idx == NULL && d->dep.idx.len == 0) ||
925 (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) {
927 snmp_debug("depend: found");
932 snmp_debug("depend: creating");
934 if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL)
936 memset(&d->dep, 0, len);
946 TAILQ_INSERT_TAIL(&context->dlist, d, link);
952 * Make an error response from a PDU. We do this without decoding the
953 * variable bindings. This means we can sent the junk back to a caller
954 * that has sent us junk in the first place.
957 snmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b,
958 struct asn_buf *resp_b)
961 struct snmp_pdu resp;
965 memset(&resp, 0, sizeof(resp));
966 if ((code = snmp_pdu_decode_header(pdu_b, &resp)) != SNMP_CODE_OK)
967 return (SNMP_RET_IGN);
969 if (pdu_b->asn_len < len)
970 return (SNMP_RET_IGN);
971 pdu_b->asn_len = len;
973 err = snmp_parse_pdus_hdr(pdu_b, &resp, &len);
974 if (ASN_ERR_STOPPED(err))
975 return (SNMP_RET_IGN);
976 if (pdu_b->asn_len < len)
977 return (SNMP_RET_IGN);
978 pdu_b->asn_len = len;
980 /* now we have the bindings left - construct new message */
981 resp.error_status = pdu->error_status;
982 resp.error_index = pdu->error_index;
983 resp.type = SNMP_PDU_RESPONSE;
985 code = snmp_pdu_encode_header(resp_b, &resp);
986 if (code != SNMP_CODE_OK)
987 return (SNMP_RET_IGN);
989 if (pdu_b->asn_len > resp_b->asn_len)
991 return (SNMP_RET_IGN);
992 (void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len);
993 resp_b->asn_len -= pdu_b->asn_len;
994 resp_b->asn_ptr += pdu_b->asn_len;
996 code = snmp_fix_encoding(resp_b, &resp);
997 if (code != SNMP_CODE_OK)
998 return (SNMP_RET_IGN);
1000 return (SNMP_RET_OK);
1004 snmp_debug_func(const char *fmt, ...)
1009 vfprintf(stderr, fmt, ap);
1011 fprintf(stderr, "\n");