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