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