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