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