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