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