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