snmpagent.c revision 260640
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	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 */
198enum snmp_ret
199snmp_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
282static struct snmp_node *
283next_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
326static enum snmp_ret
327do_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 */
395enum snmp_ret
396snmp_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
444enum snmp_ret
445snmp_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
503			if (resp->nbindings == SNMP_MAX_BINDINGS)
504				/* PDU is full */
505				goto done;
506
507			if (cnt == 0)
508				result = do_getnext(&context, &pdu->bindings[i],
509				    &resp->bindings[resp->nbindings], pdu);
510			else
511				result = do_getnext(&context,
512				    &resp->bindings[resp->nbindings -
513				    (pdu->nbindings - non_rep)],
514				    &resp->bindings[resp->nbindings], pdu);
515
516			if (result != SNMP_RET_OK) {
517				pdu->error_index = i + 1;
518				snmp_pdu_free(resp);
519				return (result);
520			}
521			if (resp->bindings[resp->nbindings].syntax !=
522			    SNMP_SYNTAX_ENDOFMIBVIEW)
523				eomib = 0;
524
525			err = snmp_binding_encode(resp_b,
526			    &resp->bindings[resp->nbindings++]);
527
528			if (err == ASN_ERR_EOBUF)
529				goto done;
530
531			if (err != ASN_ERR_OK) {
532				if (TR(GET))
533					snmp_debug("getnext: binding encoding: %u", err);
534				pdu->error_status = SNMP_ERR_GENERR;
535				pdu->error_index = i + 1;
536				snmp_pdu_free(resp);
537				return (SNMP_RET_ERR);
538			}
539		}
540		if (eomib)
541			break;
542	}
543
544  done:
545	return (snmp_fix_encoding(resp_b, resp));
546}
547
548/*
549 * Rollback a SET operation. Failed index is 'i'.
550 */
551static void
552rollback(struct context *context, struct snmp_pdu *pdu, u_int i)
553{
554	struct snmp_value *b;
555	const struct snmp_node *np;
556	int ret;
557
558	while (i-- > 0) {
559		b = &pdu->bindings[i];
560		np = context->node[i];
561
562		context->ctx.scratch = &context->scratch[i];
563
564		ret = (*np->op)(&context->ctx, b, np->oid.len, np->index,
565		    SNMP_OP_ROLLBACK);
566
567		if (ret != SNMP_ERR_NOERROR) {
568			snmp_error("set: rollback failed (%d) on variable %s "
569			    "index %u", ret, asn_oid2str(&b->var), i);
570			if (pdu->version != SNMP_V1) {
571				pdu->error_status = SNMP_ERR_UNDO_FAILED;
572				pdu->error_index = 0;
573			}
574		}
575	}
576}
577
578/*
579 * Commit dependencies.
580 */
581int
582snmp_dep_commit(struct snmp_context *ctx)
583{
584	struct context *context = (struct context *)ctx;
585	int ret;
586
587	TAILQ_FOREACH(context->depend, &context->dlist, link) {
588		ctx->dep = &context->depend->dep;
589
590		if (TR(SET))
591			snmp_debug("set: dependency commit %s",
592			    asn_oid2str(&ctx->dep->obj));
593
594		ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT);
595
596		if (ret != SNMP_ERR_NOERROR) {
597			if (TR(SET))
598				snmp_debug("set: dependency failed %d", ret);
599			return (ret);
600		}
601	}
602	return (SNMP_ERR_NOERROR);
603}
604
605/*
606 * Rollback dependencies
607 */
608int
609snmp_dep_rollback(struct snmp_context *ctx)
610{
611	struct context *context = (struct context *)ctx;
612	int ret, ret1;
613	char objbuf[ASN_OIDSTRLEN];
614	char idxbuf[ASN_OIDSTRLEN];
615
616	ret1 = SNMP_ERR_NOERROR;
617	while ((context->depend =
618	    TAILQ_PREV(context->depend, depend_list, link)) != NULL) {
619		ctx->dep = &context->depend->dep;
620
621		if (TR(SET))
622			snmp_debug("set: dependency rollback %s",
623			    asn_oid2str(&ctx->dep->obj));
624
625		ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK);
626
627		if (ret != SNMP_ERR_NOERROR) {
628			snmp_debug("set: dep rollback returns %u: %s %s", ret,
629			    asn_oid2str_r(&ctx->dep->obj, objbuf),
630			    asn_oid2str_r(&ctx->dep->idx, idxbuf));
631			if (ret1 == SNMP_ERR_NOERROR)
632				ret1 = ret;
633		}
634	}
635	return (ret1);
636}
637
638void
639snmp_dep_finish(struct snmp_context *ctx)
640{
641	struct context *context = (struct context *)ctx;
642	struct depend *d;
643
644	while ((d = TAILQ_FIRST(&context->dlist)) != NULL) {
645		ctx->dep = &d->dep;
646		(void)d->func(ctx, ctx->dep, SNMP_DEPOP_FINISH);
647		TAILQ_REMOVE(&context->dlist, d, link);
648		free(d);
649	}
650}
651
652/*
653 * Do a SET operation.
654 */
655enum snmp_ret
656snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b,
657    struct snmp_pdu *resp, void *data)
658{
659	int ret;
660	u_int i;
661	enum asn_err asnerr;
662	struct context context;
663	const struct snmp_node *np;
664	struct snmp_value *b;
665	enum snmp_syntax except;
666
667	memset(&context, 0, sizeof(context));
668	TAILQ_INIT(&context.dlist);
669	context.ctx.data = data;
670
671	snmp_pdu_create_response(pdu, resp);
672
673	if (snmp_pdu_encode_header(resp_b, resp))
674		return (SNMP_RET_IGN);
675
676	/*
677	 * 1. Find all nodes, check that they are writeable and
678	 *    that the syntax is ok, copy over the binding to the response.
679	 */
680	for (i = 0; i < pdu->nbindings; i++) {
681		b = &pdu->bindings[i];
682
683		if ((np = context.node[i] = find_node(b, &except)) == NULL) {
684			/* not found altogether or LEAF with wrong index */
685			if (TR(SET))
686				snmp_debug("set: node not found %s",
687				    asn_oid2str_r(&b->var, oidbuf));
688			if (pdu->version == SNMP_V1) {
689				pdu->error_index = i + 1;
690				pdu->error_status = SNMP_ERR_NOSUCHNAME;
691			} else if ((np = find_subnode(b)) != NULL) {
692				/* 2. intermediate object */
693				pdu->error_index = i + 1;
694				pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
695			} else if (except == SNMP_SYNTAX_NOSUCHOBJECT) {
696				pdu->error_index = i + 1;
697				pdu->error_status = SNMP_ERR_NO_ACCESS;
698			} else {
699				pdu->error_index = i + 1;
700				pdu->error_status = SNMP_ERR_NO_CREATION;
701			}
702			snmp_pdu_free(resp);
703			return (SNMP_RET_ERR);
704		}
705		/*
706		 * 2. write/createable?
707		 * Can check this for leafs only, because in v2 we have
708		 * to differentiate between NOT_WRITEABLE and NO_CREATION
709		 * and only the action routine for COLUMNS knows, whether
710		 * a column exists.
711		 */
712		if (np->type == SNMP_NODE_LEAF &&
713		    !(np->flags & SNMP_NODE_CANSET)) {
714			if (pdu->version == SNMP_V1) {
715				pdu->error_index = i + 1;
716				pdu->error_status = SNMP_ERR_NOSUCHNAME;
717			} else {
718				pdu->error_index = i + 1;
719				pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
720			}
721			snmp_pdu_free(resp);
722			return (SNMP_RET_ERR);
723		}
724		/*
725		 * 3. Ensure the right syntax
726		 */
727		if (np->syntax != b->syntax) {
728			if (pdu->version == SNMP_V1) {
729				pdu->error_index = i + 1;
730				pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */
731			} else {
732				pdu->error_index = i + 1;
733				pdu->error_status = SNMP_ERR_WRONG_TYPE;
734			}
735			snmp_pdu_free(resp);
736			return (SNMP_RET_ERR);
737		}
738		/*
739		 * 4. Copy binding
740		 */
741		if (snmp_value_copy(&resp->bindings[i], b)) {
742			pdu->error_index = i + 1;
743			pdu->error_status = SNMP_ERR_GENERR;
744			snmp_pdu_free(resp);
745			return (SNMP_RET_ERR);
746		}
747		asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]);
748		if (asnerr == ASN_ERR_EOBUF) {
749			pdu->error_index = i + 1;
750			pdu->error_status = SNMP_ERR_TOOBIG;
751			snmp_pdu_free(resp);
752			return (SNMP_RET_ERR);
753		} else if (asnerr != ASN_ERR_OK) {
754			pdu->error_index = i + 1;
755			pdu->error_status = SNMP_ERR_GENERR;
756			snmp_pdu_free(resp);
757			return (SNMP_RET_ERR);
758		}
759		resp->nbindings++;
760	}
761
762	context.ctx.code = SNMP_RET_OK;
763
764	/*
765	 * 2. Call the SET method for each node. If a SET fails, rollback
766	 *    everything. Map error codes depending on the version.
767	 */
768	for (i = 0; i < pdu->nbindings; i++) {
769		b = &pdu->bindings[i];
770		np = context.node[i];
771
772		context.ctx.var_index = i + 1;
773		context.ctx.scratch = &context.scratch[i];
774
775		ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
776		    SNMP_OP_SET);
777
778		if (TR(SET))
779			snmp_debug("set: action %s returns %d", np->name, ret);
780
781		if (pdu->version == SNMP_V1) {
782			switch (ret) {
783			  case SNMP_ERR_NO_ACCESS:
784				ret = SNMP_ERR_NOSUCHNAME;
785				break;
786			  case SNMP_ERR_WRONG_TYPE:
787				/* should no happen */
788				ret = SNMP_ERR_BADVALUE;
789				break;
790			  case SNMP_ERR_WRONG_LENGTH:
791				ret = SNMP_ERR_BADVALUE;
792				break;
793			  case SNMP_ERR_WRONG_ENCODING:
794				/* should not happen */
795				ret = SNMP_ERR_BADVALUE;
796				break;
797			  case SNMP_ERR_WRONG_VALUE:
798				ret = SNMP_ERR_BADVALUE;
799				break;
800			  case SNMP_ERR_NO_CREATION:
801				ret = SNMP_ERR_NOSUCHNAME;
802				break;
803			  case SNMP_ERR_INCONS_VALUE:
804				ret = SNMP_ERR_BADVALUE;
805				break;
806			  case SNMP_ERR_RES_UNAVAIL:
807				ret = SNMP_ERR_GENERR;
808				break;
809			  case SNMP_ERR_COMMIT_FAILED:
810				ret = SNMP_ERR_GENERR;
811				break;
812			  case SNMP_ERR_UNDO_FAILED:
813				ret = SNMP_ERR_GENERR;
814				break;
815			  case SNMP_ERR_AUTH_ERR:
816				/* should not happen */
817				ret = SNMP_ERR_GENERR;
818				break;
819			  case SNMP_ERR_NOT_WRITEABLE:
820				ret = SNMP_ERR_NOSUCHNAME;
821				break;
822			  case SNMP_ERR_INCONS_NAME:
823				ret = SNMP_ERR_BADVALUE;
824				break;
825			}
826		}
827		if (ret != SNMP_ERR_NOERROR) {
828			pdu->error_index = i + 1;
829			pdu->error_status = ret;
830
831			rollback(&context, pdu, i);
832			snmp_pdu_free(resp);
833
834			context.ctx.code = SNMP_RET_ERR;
835
836			goto errout;
837		}
838	}
839
840	/*
841	 * 3. Call dependencies
842	 */
843	if (TR(SET))
844		snmp_debug("set: set operations ok");
845
846	if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) {
847		pdu->error_status = ret;
848		pdu->error_index = context.ctx.var_index;
849
850		if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) {
851			if (pdu->version != SNMP_V1) {
852				pdu->error_status = SNMP_ERR_UNDO_FAILED;
853				pdu->error_index = 0;
854			}
855		}
856		rollback(&context, pdu, i);
857		snmp_pdu_free(resp);
858
859		context.ctx.code = SNMP_RET_ERR;
860
861		goto errout;
862	}
863
864	/*
865	 * 4. Commit and copy values from the original packet to the response.
866	 *    This is not the commit operation from RFC 1905 but rather an
867	 *    'FREE RESOURCES' operation. It shouldn't fail.
868	 */
869	if (TR(SET))
870		snmp_debug("set: commiting");
871
872	for (i = 0; i < pdu->nbindings; i++) {
873		b = &resp->bindings[i];
874		np = context.node[i];
875
876		context.ctx.var_index = i + 1;
877		context.ctx.scratch = &context.scratch[i];
878
879		ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
880		    SNMP_OP_COMMIT);
881
882		if (ret != SNMP_ERR_NOERROR)
883			snmp_error("set: commit failed (%d) on"
884			    " variable %s index %u", ret,
885			    asn_oid2str_r(&b->var, oidbuf), i);
886	}
887
888	if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
889		snmp_error("set: fix_encoding failed");
890		snmp_pdu_free(resp);
891		context.ctx.code = SNMP_RET_IGN;
892	}
893
894	/*
895	 * Done
896	 */
897  errout:
898	snmp_dep_finish(&context.ctx);
899
900	if (TR(SET))
901		snmp_debug("set: returning %d", context.ctx.code);
902
903	return (context.ctx.code);
904}
905/*
906 * Lookup a dependency. If it doesn't exist, create one
907 */
908struct snmp_dependency *
909snmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj,
910    const struct asn_oid *idx, size_t len, snmp_depop_t func)
911{
912	struct context *context;
913	struct depend *d;
914
915	context = (struct context *)(void *)
916	    ((char *)ctx - offsetof(struct context, ctx));
917	if (TR(DEPEND)) {
918		snmp_debug("depend: looking for %s", asn_oid2str(obj));
919		if (idx)
920			snmp_debug("depend: index is %s", asn_oid2str(idx));
921	}
922	TAILQ_FOREACH(d, &context->dlist, link)
923		if (asn_compare_oid(obj, &d->dep.obj) == 0 &&
924		    ((idx == NULL && d->dep.idx.len == 0) ||
925		     (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) {
926			if(TR(DEPEND))
927				snmp_debug("depend: found");
928			return (&d->dep);
929		}
930
931	if(TR(DEPEND))
932		snmp_debug("depend: creating");
933
934	if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL)
935		return (NULL);
936	memset(&d->dep, 0, len);
937
938	d->dep.obj = *obj;
939	if (idx == NULL)
940		d->dep.idx.len = 0;
941	else
942		d->dep.idx = *idx;
943	d->len = len;
944	d->func = func;
945
946	TAILQ_INSERT_TAIL(&context->dlist, d, link);
947
948	return (&d->dep);
949}
950
951/*
952 * Make an error response from a PDU. We do this without decoding the
953 * variable bindings. This means we can sent the junk back to a caller
954 * that has sent us junk in the first place.
955 */
956enum snmp_ret
957snmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b,
958    struct asn_buf *resp_b)
959{
960	asn_len_t len;
961	struct snmp_pdu resp;
962	enum asn_err err;
963	enum snmp_code code;
964
965	memset(&resp, 0, sizeof(resp));
966	if ((code = snmp_pdu_decode_header(pdu_b, &resp)) != SNMP_CODE_OK)
967		return (SNMP_RET_IGN);
968
969	if (pdu_b->asn_len < len)
970		return (SNMP_RET_IGN);
971	pdu_b->asn_len = len;
972
973	err = snmp_parse_pdus_hdr(pdu_b, &resp, &len);
974	if (ASN_ERR_STOPPED(err))
975		return (SNMP_RET_IGN);
976	if (pdu_b->asn_len < len)
977		return (SNMP_RET_IGN);
978	pdu_b->asn_len = len;
979
980	/* now we have the bindings left - construct new message */
981	resp.error_status = pdu->error_status;
982	resp.error_index = pdu->error_index;
983	resp.type = SNMP_PDU_RESPONSE;
984
985	code = snmp_pdu_encode_header(resp_b, &resp);
986	if (code != SNMP_CODE_OK)
987		return (SNMP_RET_IGN);
988
989	if (pdu_b->asn_len > resp_b->asn_len)
990		/* too short */
991		return (SNMP_RET_IGN);
992	(void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len);
993	resp_b->asn_len -= pdu_b->asn_len;
994	resp_b->asn_ptr += pdu_b->asn_len;
995
996	code = snmp_fix_encoding(resp_b, &resp);
997	if (code != SNMP_CODE_OK)
998		return (SNMP_RET_IGN);
999
1000	return (SNMP_RET_OK);
1001}
1002
1003static void
1004snmp_debug_func(const char *fmt, ...)
1005{
1006	va_list ap;
1007
1008	va_start(ap, fmt);
1009	vfprintf(stderr, fmt, ap);
1010	va_end(ap);
1011	fprintf(stderr, "\n");
1012}
1013