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