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