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