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