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