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