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