1122394Sharti/*
2122394Sharti * Copyright (c) 2001-2003
3122394Sharti *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4122394Sharti *	All rights reserved.
5122394Sharti *
6122394Sharti * Author: Harti Brandt <harti@freebsd.org>
7310903Sngie *
8133211Sharti * Redistribution and use in source and binary forms, with or without
9133211Sharti * modification, are permitted provided that the following conditions
10133211Sharti * are met:
11133211Sharti * 1. Redistributions of source code must retain the above copyright
12133211Sharti *    notice, this list of conditions and the following disclaimer.
13122394Sharti * 2. Redistributions in binary form must reproduce the above copyright
14122394Sharti *    notice, this list of conditions and the following disclaimer in the
15122394Sharti *    documentation and/or other materials provided with the distribution.
16310903Sngie *
17133211Sharti * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18133211Sharti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19133211Sharti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20133211Sharti * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21133211Sharti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22133211Sharti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23133211Sharti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24133211Sharti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25133211Sharti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26133211Sharti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27133211Sharti * SUCH DAMAGE.
28122394Sharti *
29150920Sharti * $Begemot: bsnmp/lib/snmpagent.c,v 1.20 2005/10/04 11:21:33 brandt_h Exp $
30122394Sharti *
31122394Sharti * SNMP Agent functions
32122394Sharti */
33122394Sharti#include <sys/types.h>
34122394Sharti#include <sys/queue.h>
35122394Sharti#include <stdio.h>
36122394Sharti#include <stdlib.h>
37122394Sharti#include <stddef.h>
38122394Sharti#include <stdarg.h>
39150920Sharti#ifdef HAVE_STDINT_H
40133211Sharti#include <stdint.h>
41150920Sharti#elif defined(HAVE_INTTYPES_H)
42150920Sharti#include <inttypes.h>
43150920Sharti#endif
44122394Sharti#include <string.h>
45122394Sharti
46122394Sharti#include "asn1.h"
47122394Sharti#include "snmp.h"
48122394Sharti#include "snmppriv.h"
49122394Sharti#include "snmpagent.h"
50122394Sharti
51122394Shartistatic void snmp_debug_func(const char *fmt, ...);
52122394Sharti
53122394Shartivoid (*snmp_debug)(const char *fmt, ...) = snmp_debug_func;
54122394Sharti
55122394Shartistruct snmp_node *tree;
56122394Shartiu_int  tree_size;
57122394Sharti
58122394Sharti/*
59122394Sharti * Structure to hold dependencies during SET processing
60122394Sharti * The last two members of this structure must be the
61122394Sharti * dependency visible by the user and the user data.
62122394Sharti */
63122394Shartistruct depend {
64122394Sharti	TAILQ_ENTRY(depend) link;
65122394Sharti	size_t	len;		/* size of data part */
66122394Sharti	snmp_depop_t	func;
67122394Sharti	struct snmp_dependency dep;
68133211Sharti#if defined(__GNUC__) && __GNUC__ < 3
69133211Sharti	u_char	data[0];
70133211Sharti#else
71122394Sharti	u_char	data[];
72133211Sharti#endif
73122394Sharti};
74122394ShartiTAILQ_HEAD(depend_list, depend);
75122394Sharti
76122394Sharti/*
77122394Sharti * Set context
78122394Sharti */
79122394Shartistruct context {
80122394Sharti	struct snmp_context	ctx;
81122394Sharti	struct depend_list	dlist;
82122394Sharti	const struct snmp_node	*node[SNMP_MAX_BINDINGS];
83122394Sharti	struct snmp_scratch	scratch[SNMP_MAX_BINDINGS];
84122394Sharti	struct depend		*depend;
85122394Sharti};
86122394Sharti
87122394Sharti#define	TR(W)	(snmp_trace & SNMP_TRACE_##W)
88122394Shartiu_int snmp_trace = 0;
89122394Sharti
90122394Shartistatic char oidbuf[ASN_OIDSTRLEN];
91122394Sharti
92122394Sharti/*
93122394Sharti * Allocate a context
94122394Sharti */
95122394Shartistruct snmp_context *
96122394Shartisnmp_init_context(void)
97122394Sharti{
98122394Sharti	struct context *context;
99122394Sharti
100122394Sharti	if ((context = malloc(sizeof(*context))) == NULL)
101122394Sharti		return (NULL);
102122394Sharti
103122394Sharti	memset(context, 0, sizeof(*context));
104122394Sharti	TAILQ_INIT(&context->dlist);
105122394Sharti
106122394Sharti	return (&context->ctx);
107122394Sharti}
108122394Sharti
109122394Sharti/*
110122394Sharti * Find a variable for SET/GET and the first GETBULK pass.
111122394Sharti * Return the node pointer. If the search fails, set the errp to
112122394Sharti * the correct SNMPv2 GET exception code.
113122394Sharti */
114122394Shartistatic struct snmp_node *
115122394Shartifind_node(const struct snmp_value *value, enum snmp_syntax *errp)
116122394Sharti{
117122394Sharti	struct snmp_node *tp;
118122394Sharti
119122394Sharti	if (TR(FIND))
120122394Sharti		snmp_debug("find: searching %s",
121122394Sharti		    asn_oid2str_r(&value->var, oidbuf));
122122394Sharti
123122394Sharti	/*
124122394Sharti	 * If we have an exact match (the entry in the table is a
125122394Sharti	 * sub-oid from the variable) we have found what we are for.
126122394Sharti	 * If the table oid is higher than the variable, there is no match.
127122394Sharti	 */
128122394Sharti	for (tp = tree; tp < tree + tree_size; tp++) {
129122394Sharti		if (asn_is_suboid(&tp->oid, &value->var))
130122394Sharti			goto found;
131122394Sharti		if (asn_compare_oid(&tp->oid, &value->var) >= 0)
132122394Sharti			break;
133122394Sharti	}
134122394Sharti
135122394Sharti	if (TR(FIND))
136122394Sharti		snmp_debug("find: no match");
137122394Sharti	*errp = SNMP_SYNTAX_NOSUCHOBJECT;
138122394Sharti	return (NULL);
139122394Sharti
140122394Sharti  found:
141122394Sharti	/* leafs must have a 0 instance identifier */
142122394Sharti	if (tp->type == SNMP_NODE_LEAF &&
143122394Sharti	    (value->var.len != tp->oid.len + 1 ||
144122394Sharti	     value->var.subs[tp->oid.len] != 0)) {
145122394Sharti		if (TR(FIND))
146122394Sharti			snmp_debug("find: bad leaf index");
147122394Sharti		*errp = SNMP_SYNTAX_NOSUCHINSTANCE;
148122394Sharti		return (NULL);
149122394Sharti	}
150122394Sharti	if (TR(FIND))
151122394Sharti		snmp_debug("find: found %s",
152122394Sharti		    asn_oid2str_r(&value->var, oidbuf));
153122394Sharti	return (tp);
154122394Sharti}
155122394Sharti
156122394Shartistatic struct snmp_node *
157122394Shartifind_subnode(const struct snmp_value *value)
158122394Sharti{
159122394Sharti	struct snmp_node *tp;
160122394Sharti
161122394Sharti	for (tp = tree; tp < tree + tree_size; tp++) {
162122394Sharti		if (asn_is_suboid(&value->var, &tp->oid))
163122394Sharti			return (tp);
164122394Sharti	}
165122394Sharti	return (NULL);
166122394Sharti}
167122394Sharti
168216294Ssyrinxstatic void
169301661Sngiesnmp_pdu_create_response(const struct snmp_pdu *pdu, struct snmp_pdu *resp)
170216294Ssyrinx{
171216294Ssyrinx	memset(resp, 0, sizeof(*resp));
172216294Ssyrinx	strcpy(resp->community, pdu->community);
173216294Ssyrinx	resp->version = pdu->version;
174216294Ssyrinx	resp->type = SNMP_PDU_RESPONSE;
175216294Ssyrinx	resp->request_id = pdu->request_id;
176216294Ssyrinx	resp->version = pdu->version;
177216294Ssyrinx
178216294Ssyrinx	if (resp->version != SNMP_V3)
179216294Ssyrinx		return;
180216294Ssyrinx
181216594Ssyrinx	memcpy(&resp->engine, &pdu->engine, sizeof(pdu->engine));
182216594Ssyrinx	memcpy(&resp->user, &pdu->user, sizeof(pdu->user));
183216594Ssyrinx	snmp_pdu_init_secparams(resp);
184216294Ssyrinx	resp->identifier = pdu->identifier;
185216294Ssyrinx	resp->security_model = pdu->security_model;
186216294Ssyrinx	resp->context_engine_len = pdu->context_engine_len;
187216294Ssyrinx	memcpy(resp->context_engine, pdu->context_engine,
188216294Ssyrinx	    resp->context_engine_len);
189216294Ssyrinx	strlcpy(resp->context_name, pdu->context_name,
190216294Ssyrinx	    sizeof(resp->context_name));
191216294Ssyrinx}
192216294Ssyrinx
193122394Sharti/*
194122394Sharti * Execute a GET operation. The tree is rooted at the global 'root'.
195122394Sharti * Build the response PDU on the fly. If the return code is SNMP_RET_ERR
196122394Sharti * the pdu error status and index will be set.
197122394Sharti */
198122394Shartienum snmp_ret
199122394Shartisnmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b,
200122394Sharti    struct snmp_pdu *resp, void *data)
201122394Sharti{
202122394Sharti	int ret;
203122394Sharti	u_int i;
204122394Sharti	struct snmp_node *tp;
205122394Sharti	enum snmp_syntax except;
206122394Sharti	struct context context;
207122394Sharti	enum asn_err err;
208122394Sharti
209122394Sharti	memset(&context, 0, sizeof(context));
210122394Sharti	context.ctx.data = data;
211122394Sharti
212216294Ssyrinx	snmp_pdu_create_response(pdu, resp);
213122394Sharti
214122394Sharti	if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
215122394Sharti		/* cannot even encode header - very bad */
216122394Sharti		return (SNMP_RET_IGN);
217122394Sharti
218122394Sharti	for (i = 0; i < pdu->nbindings; i++) {
219122394Sharti		resp->bindings[i].var = pdu->bindings[i].var;
220122394Sharti		if ((tp = find_node(&pdu->bindings[i], &except)) == NULL) {
221122394Sharti			if (pdu->version == SNMP_V1) {
222122394Sharti				if (TR(GET))
223122394Sharti					snmp_debug("get: nosuchname");
224122394Sharti				pdu->error_status = SNMP_ERR_NOSUCHNAME;
225122394Sharti				pdu->error_index = i + 1;
226122394Sharti				snmp_pdu_free(resp);
227122394Sharti				return (SNMP_RET_ERR);
228122394Sharti			}
229122394Sharti			if (TR(GET))
230122394Sharti				snmp_debug("get: exception %u", except);
231122394Sharti			resp->bindings[i].syntax = except;
232122394Sharti
233122394Sharti		} else {
234122394Sharti			/* call the action to fetch the value. */
235122394Sharti			resp->bindings[i].syntax = tp->syntax;
236122394Sharti			ret = (*tp->op)(&context.ctx, &resp->bindings[i],
237122394Sharti			    tp->oid.len, tp->index, SNMP_OP_GET);
238122394Sharti			if (TR(GET))
239122394Sharti				snmp_debug("get: action returns %d", ret);
240122394Sharti
241122394Sharti			if (ret == SNMP_ERR_NOSUCHNAME) {
242122394Sharti				if (pdu->version == SNMP_V1) {
243122394Sharti					pdu->error_status = SNMP_ERR_NOSUCHNAME;
244122394Sharti					pdu->error_index = i + 1;
245122394Sharti					snmp_pdu_free(resp);
246122394Sharti					return (SNMP_RET_ERR);
247122394Sharti				}
248122394Sharti				if (TR(GET))
249122394Sharti					snmp_debug("get: exception noSuchInstance");
250122394Sharti				resp->bindings[i].syntax = SNMP_SYNTAX_NOSUCHINSTANCE;
251122394Sharti
252122394Sharti			} else if (ret != SNMP_ERR_NOERROR) {
253122394Sharti				pdu->error_status = SNMP_ERR_GENERR;
254122394Sharti				pdu->error_index = i + 1;
255122394Sharti				snmp_pdu_free(resp);
256122394Sharti				return (SNMP_RET_ERR);
257122394Sharti			}
258122394Sharti		}
259122394Sharti		resp->nbindings++;
260122394Sharti
261122394Sharti		err = snmp_binding_encode(resp_b, &resp->bindings[i]);
262122394Sharti
263122394Sharti		if (err == ASN_ERR_EOBUF) {
264122394Sharti			pdu->error_status = SNMP_ERR_TOOBIG;
265122394Sharti			pdu->error_index = 0;
266122394Sharti			snmp_pdu_free(resp);
267122394Sharti			return (SNMP_RET_ERR);
268122394Sharti		}
269122394Sharti		if (err != ASN_ERR_OK) {
270122394Sharti			if (TR(GET))
271122394Sharti				snmp_debug("get: binding encoding: %u", err);
272122394Sharti			pdu->error_status = SNMP_ERR_GENERR;
273122394Sharti			pdu->error_index = i + 1;
274122394Sharti			snmp_pdu_free(resp);
275122394Sharti			return (SNMP_RET_ERR);
276122394Sharti		}
277122394Sharti	}
278122394Sharti
279301661Sngie	if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
280301661Sngie		snmp_debug("get: failed to encode PDU");
281301661Sngie		return (SNMP_RET_ERR);
282301661Sngie	}
283301661Sngie
284301661Sngie	return (SNMP_RET_OK);
285122394Sharti}
286122394Sharti
287122394Shartistatic struct snmp_node *
288122394Shartinext_node(const struct snmp_value *value, int *pnext)
289122394Sharti{
290122394Sharti	struct snmp_node *tp;
291122394Sharti
292122394Sharti	if (TR(FIND))
293122394Sharti		snmp_debug("next: searching %s",
294122394Sharti		    asn_oid2str_r(&value->var, oidbuf));
295122394Sharti
296122394Sharti	*pnext = 0;
297122394Sharti	for (tp = tree; tp < tree + tree_size; tp++) {
298122394Sharti		if (asn_is_suboid(&tp->oid, &value->var)) {
299122394Sharti			/* the tree OID is a sub-oid of the requested OID. */
300122394Sharti			if (tp->type == SNMP_NODE_LEAF) {
301122394Sharti				if (tp->oid.len == value->var.len) {
302122394Sharti					/* request for scalar type */
303122394Sharti					if (TR(FIND))
304122394Sharti						snmp_debug("next: found scalar %s",
305122394Sharti						    asn_oid2str_r(&tp->oid, oidbuf));
306122394Sharti					return (tp);
307122394Sharti				}
308122394Sharti				/* try next */
309122394Sharti			} else {
310122394Sharti				if (TR(FIND))
311122394Sharti					snmp_debug("next: found column %s",
312122394Sharti					    asn_oid2str_r(&tp->oid, oidbuf));
313122394Sharti				return (tp);
314122394Sharti			}
315122394Sharti		} else if (asn_is_suboid(&value->var, &tp->oid) ||
316122394Sharti		    asn_compare_oid(&tp->oid, &value->var) >= 0) {
317122394Sharti			if (TR(FIND))
318122394Sharti				snmp_debug("next: found %s",
319122394Sharti				    asn_oid2str_r(&tp->oid, oidbuf));
320122394Sharti			*pnext = 1;
321122394Sharti			return (tp);
322122394Sharti		}
323122394Sharti	}
324122394Sharti
325122394Sharti	if (TR(FIND))
326122394Sharti		snmp_debug("next: failed");
327122394Sharti
328122394Sharti	return (NULL);
329122394Sharti}
330122394Sharti
331122394Shartistatic enum snmp_ret
332122394Shartido_getnext(struct context *context, const struct snmp_value *inb,
333122394Sharti    struct snmp_value *outb, struct snmp_pdu *pdu)
334122394Sharti{
335122394Sharti	const struct snmp_node *tp;
336122394Sharti	int ret, next;
337122394Sharti
338122394Sharti	if ((tp = next_node(inb, &next)) == NULL)
339122394Sharti		goto eofMib;
340122394Sharti
341122394Sharti	/* retain old variable if we are doing a GETNEXT on an exact
342122394Sharti	 * matched leaf only */
343122394Sharti	if (tp->type == SNMP_NODE_LEAF || next)
344122394Sharti		outb->var = tp->oid;
345122394Sharti	else
346122394Sharti		outb->var = inb->var;
347122394Sharti
348122394Sharti	for (;;) {
349122394Sharti		outb->syntax = tp->syntax;
350122394Sharti		if (tp->type == SNMP_NODE_LEAF) {
351122394Sharti			/* make a GET operation */
352122394Sharti			outb->var.subs[outb->var.len++] = 0;
353122394Sharti			ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
354122394Sharti			    tp->index, SNMP_OP_GET);
355122394Sharti		} else {
356122394Sharti			/* make a GETNEXT */
357122394Sharti			ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
358122394Sharti			     tp->index, SNMP_OP_GETNEXT);
359122394Sharti		}
360122394Sharti		if (ret != SNMP_ERR_NOSUCHNAME) {
361122394Sharti			/* got something */
362122394Sharti			if (ret != SNMP_ERR_NOERROR && TR(GETNEXT))
363122394Sharti				snmp_debug("getnext: %s returns %u",
364122394Sharti				    asn_oid2str(&outb->var), ret);
365122394Sharti			break;
366122394Sharti		}
367122394Sharti
368122394Sharti		/* object has no data - try next */
369122394Sharti		if (++tp == tree + tree_size)
370122394Sharti			break;
371142810Sharti
372142810Sharti		if (TR(GETNEXT))
373142810Sharti			snmp_debug("getnext: no data - avancing to %s",
374142810Sharti			    asn_oid2str(&tp->oid));
375142810Sharti
376122394Sharti		outb->var = tp->oid;
377122394Sharti	}
378122394Sharti
379122394Sharti	if (ret == SNMP_ERR_NOSUCHNAME) {
380122394Sharti  eofMib:
381122394Sharti		outb->var = inb->var;
382122394Sharti		if (pdu->version == SNMP_V1) {
383122394Sharti			pdu->error_status = SNMP_ERR_NOSUCHNAME;
384122394Sharti			return (SNMP_RET_ERR);
385122394Sharti		}
386122394Sharti		outb->syntax = SNMP_SYNTAX_ENDOFMIBVIEW;
387122394Sharti
388122394Sharti	} else if (ret != SNMP_ERR_NOERROR) {
389122394Sharti		pdu->error_status = SNMP_ERR_GENERR;
390122394Sharti		return (SNMP_RET_ERR);
391122394Sharti	}
392122394Sharti	return (SNMP_RET_OK);
393122394Sharti}
394122394Sharti
395122394Sharti
396122394Sharti/*
397122394Sharti * Execute a GETNEXT operation. The tree is rooted at the global 'root'.
398122394Sharti * Build the response PDU on the fly. The return is:
399122394Sharti */
400122394Shartienum snmp_ret
401122394Shartisnmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b,
402122394Sharti    struct snmp_pdu *resp, void *data)
403122394Sharti{
404122394Sharti	struct context context;
405122394Sharti	u_int i;
406122394Sharti	enum asn_err err;
407122394Sharti	enum snmp_ret result;
408122394Sharti
409122394Sharti	memset(&context, 0, sizeof(context));
410122394Sharti	context.ctx.data = data;
411122394Sharti
412216294Ssyrinx	snmp_pdu_create_response(pdu, resp);
413122394Sharti
414122394Sharti	if (snmp_pdu_encode_header(resp_b, resp))
415122394Sharti		return (SNMP_RET_IGN);
416122394Sharti
417122394Sharti	for (i = 0; i < pdu->nbindings; i++) {
418122394Sharti		result = do_getnext(&context, &pdu->bindings[i],
419122394Sharti		    &resp->bindings[i], pdu);
420122394Sharti
421122394Sharti		if (result != SNMP_RET_OK) {
422122394Sharti			pdu->error_index = i + 1;
423122394Sharti			snmp_pdu_free(resp);
424122394Sharti			return (result);
425122394Sharti		}
426122394Sharti
427122394Sharti		resp->nbindings++;
428122394Sharti
429122394Sharti		err = snmp_binding_encode(resp_b, &resp->bindings[i]);
430122394Sharti
431122394Sharti		if (err == ASN_ERR_EOBUF) {
432122394Sharti			pdu->error_status = SNMP_ERR_TOOBIG;
433122394Sharti			pdu->error_index = 0;
434122394Sharti			snmp_pdu_free(resp);
435122394Sharti			return (SNMP_RET_ERR);
436122394Sharti		}
437122394Sharti		if (err != ASN_ERR_OK) {
438122394Sharti			if (TR(GET))
439122394Sharti				snmp_debug("getnext: binding encoding: %u", err);
440122394Sharti			pdu->error_status = SNMP_ERR_GENERR;
441122394Sharti			pdu->error_index = i + 1;
442122394Sharti			snmp_pdu_free(resp);
443122394Sharti			return (SNMP_RET_ERR);
444122394Sharti		}
445122394Sharti	}
446301661Sngie
447301661Sngie	if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
448301661Sngie		snmp_debug("getnext: failed to encode PDU");
449301661Sngie		return (SNMP_RET_ERR);
450301661Sngie	}
451301661Sngie
452301661Sngie	return (SNMP_RET_OK);
453122394Sharti}
454122394Sharti
455122394Shartienum snmp_ret
456122394Shartisnmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b,
457122394Sharti    struct snmp_pdu *resp, void *data)
458122394Sharti{
459122394Sharti	struct context context;
460122394Sharti	u_int i;
461122394Sharti	int cnt;
462122394Sharti	u_int non_rep;
463122394Sharti	int eomib;
464122394Sharti	enum snmp_ret result;
465122394Sharti	enum asn_err err;
466122394Sharti
467122394Sharti	memset(&context, 0, sizeof(context));
468122394Sharti	context.ctx.data = data;
469122394Sharti
470216294Ssyrinx	snmp_pdu_create_response(pdu, resp);
471122394Sharti
472122394Sharti	if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
473122394Sharti		/* cannot even encode header - very bad */
474122394Sharti		return (SNMP_RET_IGN);
475122394Sharti
476122394Sharti	if ((non_rep = pdu->error_status) > pdu->nbindings)
477122394Sharti		non_rep = pdu->nbindings;
478122394Sharti
479122394Sharti	/* non-repeaters */
480122394Sharti	for (i = 0; i < non_rep; i++) {
481122394Sharti		result = do_getnext(&context, &pdu->bindings[i],
482122394Sharti		    &resp->bindings[resp->nbindings], pdu);
483122394Sharti
484122394Sharti		if (result != SNMP_RET_OK) {
485122394Sharti			pdu->error_index = i + 1;
486122394Sharti			snmp_pdu_free(resp);
487122394Sharti			return (result);
488122394Sharti		}
489122394Sharti
490122394Sharti		err = snmp_binding_encode(resp_b,
491122394Sharti		    &resp->bindings[resp->nbindings++]);
492122394Sharti
493122394Sharti		if (err == ASN_ERR_EOBUF)
494122394Sharti			goto done;
495122394Sharti
496122394Sharti		if (err != ASN_ERR_OK) {
497122394Sharti			if (TR(GET))
498122394Sharti				snmp_debug("getnext: binding encoding: %u", err);
499122394Sharti			pdu->error_status = SNMP_ERR_GENERR;
500122394Sharti			pdu->error_index = i + 1;
501122394Sharti			snmp_pdu_free(resp);
502122394Sharti			return (SNMP_RET_ERR);
503122394Sharti		}
504122394Sharti	}
505122394Sharti
506122394Sharti	if (non_rep == pdu->nbindings)
507122394Sharti		goto done;
508122394Sharti
509122394Sharti	/* repeates */
510122394Sharti	for (cnt = 0; cnt < pdu->error_index; cnt++) {
511122394Sharti		eomib = 1;
512122394Sharti		for (i = non_rep; i < pdu->nbindings; i++) {
513260638Sdelphij
514260638Sdelphij			if (resp->nbindings == SNMP_MAX_BINDINGS)
515260638Sdelphij				/* PDU is full */
516260638Sdelphij				goto done;
517260638Sdelphij
518310903Sngie			if (cnt == 0)
519122394Sharti				result = do_getnext(&context, &pdu->bindings[i],
520122394Sharti				    &resp->bindings[resp->nbindings], pdu);
521122394Sharti			else
522122394Sharti				result = do_getnext(&context,
523122394Sharti				    &resp->bindings[resp->nbindings -
524122394Sharti				    (pdu->nbindings - non_rep)],
525122394Sharti				    &resp->bindings[resp->nbindings], pdu);
526122394Sharti
527122394Sharti			if (result != SNMP_RET_OK) {
528122394Sharti				pdu->error_index = i + 1;
529122394Sharti				snmp_pdu_free(resp);
530122394Sharti				return (result);
531122394Sharti			}
532122394Sharti			if (resp->bindings[resp->nbindings].syntax !=
533122394Sharti			    SNMP_SYNTAX_ENDOFMIBVIEW)
534122394Sharti				eomib = 0;
535122394Sharti
536122394Sharti			err = snmp_binding_encode(resp_b,
537122394Sharti			    &resp->bindings[resp->nbindings++]);
538122394Sharti
539122394Sharti			if (err == ASN_ERR_EOBUF)
540122394Sharti				goto done;
541122394Sharti
542122394Sharti			if (err != ASN_ERR_OK) {
543122394Sharti				if (TR(GET))
544122394Sharti					snmp_debug("getnext: binding encoding: %u", err);
545122394Sharti				pdu->error_status = SNMP_ERR_GENERR;
546122394Sharti				pdu->error_index = i + 1;
547122394Sharti				snmp_pdu_free(resp);
548122394Sharti				return (SNMP_RET_ERR);
549122394Sharti			}
550122394Sharti		}
551122394Sharti		if (eomib)
552122394Sharti			break;
553122394Sharti	}
554122394Sharti
555122394Sharti  done:
556301661Sngie	if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
557301661Sngie		snmp_debug("getnext: failed to encode PDU");
558301661Sngie		return (SNMP_RET_ERR);
559301661Sngie	}
560301661Sngie
561301661Sngie	return (SNMP_RET_OK);
562122394Sharti}
563122394Sharti
564122394Sharti/*
565122394Sharti * Rollback a SET operation. Failed index is 'i'.
566122394Sharti */
567122394Shartistatic void
568122394Shartirollback(struct context *context, struct snmp_pdu *pdu, u_int i)
569122394Sharti{
570122394Sharti	struct snmp_value *b;
571122394Sharti	const struct snmp_node *np;
572122394Sharti	int ret;
573122394Sharti
574122394Sharti	while (i-- > 0) {
575122394Sharti		b = &pdu->bindings[i];
576122394Sharti		np = context->node[i];
577122394Sharti
578122394Sharti		context->ctx.scratch = &context->scratch[i];
579122394Sharti
580122394Sharti		ret = (*np->op)(&context->ctx, b, np->oid.len, np->index,
581122394Sharti		    SNMP_OP_ROLLBACK);
582122394Sharti
583122394Sharti		if (ret != SNMP_ERR_NOERROR) {
584122394Sharti			snmp_error("set: rollback failed (%d) on variable %s "
585122394Sharti			    "index %u", ret, asn_oid2str(&b->var), i);
586122394Sharti			if (pdu->version != SNMP_V1) {
587122394Sharti				pdu->error_status = SNMP_ERR_UNDO_FAILED;
588122394Sharti				pdu->error_index = 0;
589122394Sharti			}
590122394Sharti		}
591122394Sharti	}
592122394Sharti}
593122394Sharti
594122394Sharti/*
595122394Sharti * Commit dependencies.
596122394Sharti */
597122394Shartiint
598122394Shartisnmp_dep_commit(struct snmp_context *ctx)
599122394Sharti{
600122394Sharti	struct context *context = (struct context *)ctx;
601122394Sharti	int ret;
602122394Sharti
603122394Sharti	TAILQ_FOREACH(context->depend, &context->dlist, link) {
604122394Sharti		ctx->dep = &context->depend->dep;
605122394Sharti
606122394Sharti		if (TR(SET))
607122394Sharti			snmp_debug("set: dependency commit %s",
608122394Sharti			    asn_oid2str(&ctx->dep->obj));
609122394Sharti
610122394Sharti		ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT);
611122394Sharti
612122394Sharti		if (ret != SNMP_ERR_NOERROR) {
613122394Sharti			if (TR(SET))
614122394Sharti				snmp_debug("set: dependency failed %d", ret);
615122394Sharti			return (ret);
616122394Sharti		}
617122394Sharti	}
618122394Sharti	return (SNMP_ERR_NOERROR);
619122394Sharti}
620122394Sharti
621122394Sharti/*
622122394Sharti * Rollback dependencies
623122394Sharti */
624122394Shartiint
625122394Shartisnmp_dep_rollback(struct snmp_context *ctx)
626122394Sharti{
627122394Sharti	struct context *context = (struct context *)ctx;
628122394Sharti	int ret, ret1;
629122394Sharti	char objbuf[ASN_OIDSTRLEN];
630122394Sharti	char idxbuf[ASN_OIDSTRLEN];
631122394Sharti
632122394Sharti	ret1 = SNMP_ERR_NOERROR;
633122394Sharti	while ((context->depend =
634122394Sharti	    TAILQ_PREV(context->depend, depend_list, link)) != NULL) {
635122394Sharti		ctx->dep = &context->depend->dep;
636122394Sharti
637122394Sharti		if (TR(SET))
638122394Sharti			snmp_debug("set: dependency rollback %s",
639122394Sharti			    asn_oid2str(&ctx->dep->obj));
640122394Sharti
641122394Sharti		ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK);
642122394Sharti
643122394Sharti		if (ret != SNMP_ERR_NOERROR) {
644122394Sharti			snmp_debug("set: dep rollback returns %u: %s %s", ret,
645122394Sharti			    asn_oid2str_r(&ctx->dep->obj, objbuf),
646122394Sharti			    asn_oid2str_r(&ctx->dep->idx, idxbuf));
647122394Sharti			if (ret1 == SNMP_ERR_NOERROR)
648122394Sharti				ret1 = ret;
649122394Sharti		}
650122394Sharti	}
651122394Sharti	return (ret1);
652122394Sharti}
653122394Sharti
654128237Shartivoid
655128237Shartisnmp_dep_finish(struct snmp_context *ctx)
656128237Sharti{
657128237Sharti	struct context *context = (struct context *)ctx;
658128237Sharti	struct depend *d;
659128237Sharti
660128237Sharti	while ((d = TAILQ_FIRST(&context->dlist)) != NULL) {
661128237Sharti		ctx->dep = &d->dep;
662128237Sharti		(void)d->func(ctx, ctx->dep, SNMP_DEPOP_FINISH);
663128237Sharti		TAILQ_REMOVE(&context->dlist, d, link);
664128237Sharti		free(d);
665128237Sharti	}
666128237Sharti}
667128237Sharti
668122394Sharti/*
669122394Sharti * Do a SET operation.
670122394Sharti */
671122394Shartienum snmp_ret
672122394Shartisnmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b,
673122394Sharti    struct snmp_pdu *resp, void *data)
674122394Sharti{
675122394Sharti	int ret;
676122394Sharti	u_int i;
677122394Sharti	enum asn_err asnerr;
678122394Sharti	struct context context;
679122394Sharti	const struct snmp_node *np;
680122394Sharti	struct snmp_value *b;
681122394Sharti	enum snmp_syntax except;
682122394Sharti
683122394Sharti	memset(&context, 0, sizeof(context));
684122394Sharti	TAILQ_INIT(&context.dlist);
685122394Sharti	context.ctx.data = data;
686122394Sharti
687216294Ssyrinx	snmp_pdu_create_response(pdu, resp);
688122394Sharti
689122394Sharti	if (snmp_pdu_encode_header(resp_b, resp))
690122394Sharti		return (SNMP_RET_IGN);
691122394Sharti
692310903Sngie	/*
693122394Sharti	 * 1. Find all nodes, check that they are writeable and
694122394Sharti	 *    that the syntax is ok, copy over the binding to the response.
695122394Sharti	 */
696122394Sharti	for (i = 0; i < pdu->nbindings; i++) {
697122394Sharti		b = &pdu->bindings[i];
698122394Sharti
699122394Sharti		if ((np = context.node[i] = find_node(b, &except)) == NULL) {
700122394Sharti			/* not found altogether or LEAF with wrong index */
701122394Sharti			if (TR(SET))
702122394Sharti				snmp_debug("set: node not found %s",
703122394Sharti				    asn_oid2str_r(&b->var, oidbuf));
704122394Sharti			if (pdu->version == SNMP_V1) {
705122394Sharti				pdu->error_index = i + 1;
706122394Sharti				pdu->error_status = SNMP_ERR_NOSUCHNAME;
707122394Sharti			} else if ((np = find_subnode(b)) != NULL) {
708122394Sharti				/* 2. intermediate object */
709122394Sharti				pdu->error_index = i + 1;
710122394Sharti				pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
711122394Sharti			} else if (except == SNMP_SYNTAX_NOSUCHOBJECT) {
712122394Sharti				pdu->error_index = i + 1;
713122394Sharti				pdu->error_status = SNMP_ERR_NO_ACCESS;
714122394Sharti			} else {
715122394Sharti				pdu->error_index = i + 1;
716122394Sharti				pdu->error_status = SNMP_ERR_NO_CREATION;
717122394Sharti			}
718122394Sharti			snmp_pdu_free(resp);
719122394Sharti			return (SNMP_RET_ERR);
720122394Sharti		}
721122394Sharti		/*
722122394Sharti		 * 2. write/createable?
723122394Sharti		 * Can check this for leafs only, because in v2 we have
724122394Sharti		 * to differentiate between NOT_WRITEABLE and NO_CREATION
725122394Sharti		 * and only the action routine for COLUMNS knows, whether
726122394Sharti		 * a column exists.
727122394Sharti		 */
728122394Sharti		if (np->type == SNMP_NODE_LEAF &&
729122394Sharti		    !(np->flags & SNMP_NODE_CANSET)) {
730122394Sharti			if (pdu->version == SNMP_V1) {
731122394Sharti				pdu->error_index = i + 1;
732122394Sharti				pdu->error_status = SNMP_ERR_NOSUCHNAME;
733122394Sharti			} else {
734122394Sharti				pdu->error_index = i + 1;
735122394Sharti				pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
736122394Sharti			}
737122394Sharti			snmp_pdu_free(resp);
738122394Sharti			return (SNMP_RET_ERR);
739122394Sharti		}
740122394Sharti		/*
741122394Sharti		 * 3. Ensure the right syntax
742122394Sharti		 */
743122394Sharti		if (np->syntax != b->syntax) {
744122394Sharti			if (pdu->version == SNMP_V1) {
745122394Sharti				pdu->error_index = i + 1;
746122394Sharti				pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */
747122394Sharti			} else {
748122394Sharti				pdu->error_index = i + 1;
749122394Sharti				pdu->error_status = SNMP_ERR_WRONG_TYPE;
750122394Sharti			}
751122394Sharti			snmp_pdu_free(resp);
752122394Sharti			return (SNMP_RET_ERR);
753122394Sharti		}
754122394Sharti		/*
755122394Sharti		 * 4. Copy binding
756122394Sharti		 */
757122394Sharti		if (snmp_value_copy(&resp->bindings[i], b)) {
758122394Sharti			pdu->error_index = i + 1;
759122394Sharti			pdu->error_status = SNMP_ERR_GENERR;
760122394Sharti			snmp_pdu_free(resp);
761122394Sharti			return (SNMP_RET_ERR);
762122394Sharti		}
763122394Sharti		asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]);
764122394Sharti		if (asnerr == ASN_ERR_EOBUF) {
765122394Sharti			pdu->error_index = i + 1;
766122394Sharti			pdu->error_status = SNMP_ERR_TOOBIG;
767122394Sharti			snmp_pdu_free(resp);
768122394Sharti			return (SNMP_RET_ERR);
769122394Sharti		} else if (asnerr != ASN_ERR_OK) {
770122394Sharti			pdu->error_index = i + 1;
771122394Sharti			pdu->error_status = SNMP_ERR_GENERR;
772122394Sharti			snmp_pdu_free(resp);
773122394Sharti			return (SNMP_RET_ERR);
774122394Sharti		}
775122394Sharti		resp->nbindings++;
776122394Sharti	}
777122394Sharti
778128237Sharti	context.ctx.code = SNMP_RET_OK;
779122394Sharti
780122394Sharti	/*
781122394Sharti	 * 2. Call the SET method for each node. If a SET fails, rollback
782122394Sharti	 *    everything. Map error codes depending on the version.
783122394Sharti	 */
784122394Sharti	for (i = 0; i < pdu->nbindings; i++) {
785122394Sharti		b = &pdu->bindings[i];
786122394Sharti		np = context.node[i];
787122394Sharti
788122394Sharti		context.ctx.var_index = i + 1;
789122394Sharti		context.ctx.scratch = &context.scratch[i];
790122394Sharti
791122394Sharti		ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
792122394Sharti		    SNMP_OP_SET);
793122394Sharti
794122394Sharti		if (TR(SET))
795122394Sharti			snmp_debug("set: action %s returns %d", np->name, ret);
796122394Sharti
797122394Sharti		if (pdu->version == SNMP_V1) {
798122394Sharti			switch (ret) {
799122394Sharti			  case SNMP_ERR_NO_ACCESS:
800122394Sharti				ret = SNMP_ERR_NOSUCHNAME;
801122394Sharti				break;
802122394Sharti			  case SNMP_ERR_WRONG_TYPE:
803122394Sharti				/* should no happen */
804122394Sharti				ret = SNMP_ERR_BADVALUE;
805122394Sharti				break;
806122394Sharti			  case SNMP_ERR_WRONG_LENGTH:
807122394Sharti				ret = SNMP_ERR_BADVALUE;
808122394Sharti				break;
809122394Sharti			  case SNMP_ERR_WRONG_ENCODING:
810122394Sharti				/* should not happen */
811122394Sharti				ret = SNMP_ERR_BADVALUE;
812122394Sharti				break;
813122394Sharti			  case SNMP_ERR_WRONG_VALUE:
814122394Sharti				ret = SNMP_ERR_BADVALUE;
815122394Sharti				break;
816122394Sharti			  case SNMP_ERR_NO_CREATION:
817122394Sharti				ret = SNMP_ERR_NOSUCHNAME;
818122394Sharti				break;
819122394Sharti			  case SNMP_ERR_INCONS_VALUE:
820122394Sharti				ret = SNMP_ERR_BADVALUE;
821122394Sharti				break;
822122394Sharti			  case SNMP_ERR_RES_UNAVAIL:
823122394Sharti				ret = SNMP_ERR_GENERR;
824122394Sharti				break;
825122394Sharti			  case SNMP_ERR_COMMIT_FAILED:
826122394Sharti				ret = SNMP_ERR_GENERR;
827122394Sharti				break;
828122394Sharti			  case SNMP_ERR_UNDO_FAILED:
829122394Sharti				ret = SNMP_ERR_GENERR;
830122394Sharti				break;
831122394Sharti			  case SNMP_ERR_AUTH_ERR:
832122394Sharti				/* should not happen */
833122394Sharti				ret = SNMP_ERR_GENERR;
834122394Sharti				break;
835122394Sharti			  case SNMP_ERR_NOT_WRITEABLE:
836122394Sharti				ret = SNMP_ERR_NOSUCHNAME;
837122394Sharti				break;
838122394Sharti			  case SNMP_ERR_INCONS_NAME:
839122394Sharti				ret = SNMP_ERR_BADVALUE;
840122394Sharti				break;
841122394Sharti			}
842122394Sharti		}
843122394Sharti		if (ret != SNMP_ERR_NOERROR) {
844122394Sharti			pdu->error_index = i + 1;
845122394Sharti			pdu->error_status = ret;
846122394Sharti
847122394Sharti			rollback(&context, pdu, i);
848122394Sharti			snmp_pdu_free(resp);
849122394Sharti
850128237Sharti			context.ctx.code = SNMP_RET_ERR;
851122394Sharti
852122394Sharti			goto errout;
853122394Sharti		}
854122394Sharti	}
855122394Sharti
856122394Sharti	/*
857122394Sharti	 * 3. Call dependencies
858122394Sharti	 */
859122394Sharti	if (TR(SET))
860122394Sharti		snmp_debug("set: set operations ok");
861122394Sharti
862122394Sharti	if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) {
863122394Sharti		pdu->error_status = ret;
864122394Sharti		pdu->error_index = context.ctx.var_index;
865122394Sharti
866122394Sharti		if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) {
867122394Sharti			if (pdu->version != SNMP_V1) {
868122394Sharti				pdu->error_status = SNMP_ERR_UNDO_FAILED;
869122394Sharti				pdu->error_index = 0;
870122394Sharti			}
871122394Sharti		}
872122394Sharti		rollback(&context, pdu, i);
873122394Sharti		snmp_pdu_free(resp);
874122394Sharti
875128237Sharti		context.ctx.code = SNMP_RET_ERR;
876122394Sharti
877122394Sharti		goto errout;
878122394Sharti	}
879122394Sharti
880122394Sharti	/*
881122394Sharti	 * 4. Commit and copy values from the original packet to the response.
882122394Sharti	 *    This is not the commit operation from RFC 1905 but rather an
883122394Sharti	 *    'FREE RESOURCES' operation. It shouldn't fail.
884122394Sharti	 */
885122394Sharti	if (TR(SET))
886122394Sharti		snmp_debug("set: commiting");
887122394Sharti
888122394Sharti	for (i = 0; i < pdu->nbindings; i++) {
889122394Sharti		b = &resp->bindings[i];
890122394Sharti		np = context.node[i];
891122394Sharti
892122394Sharti		context.ctx.var_index = i + 1;
893122394Sharti		context.ctx.scratch = &context.scratch[i];
894122394Sharti
895122394Sharti		ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
896122394Sharti		    SNMP_OP_COMMIT);
897122394Sharti
898122394Sharti		if (ret != SNMP_ERR_NOERROR)
899122394Sharti			snmp_error("set: commit failed (%d) on"
900122394Sharti			    " variable %s index %u", ret,
901122394Sharti			    asn_oid2str_r(&b->var, oidbuf), i);
902122394Sharti	}
903122394Sharti
904122394Sharti	if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
905122394Sharti		snmp_error("set: fix_encoding failed");
906122394Sharti		snmp_pdu_free(resp);
907128237Sharti		context.ctx.code = SNMP_RET_IGN;
908122394Sharti	}
909122394Sharti
910122394Sharti	/*
911122394Sharti	 * Done
912122394Sharti	 */
913122394Sharti  errout:
914128237Sharti	snmp_dep_finish(&context.ctx);
915122394Sharti
916122394Sharti	if (TR(SET))
917128237Sharti		snmp_debug("set: returning %d", context.ctx.code);
918122394Sharti
919128237Sharti	return (context.ctx.code);
920122394Sharti}
921122394Sharti/*
922122394Sharti * Lookup a dependency. If it doesn't exist, create one
923122394Sharti */
924122394Shartistruct snmp_dependency *
925122394Shartisnmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj,
926122394Sharti    const struct asn_oid *idx, size_t len, snmp_depop_t func)
927122394Sharti{
928122394Sharti	struct context *context;
929122394Sharti	struct depend *d;
930122394Sharti
931122394Sharti	context = (struct context *)(void *)
932122394Sharti	    ((char *)ctx - offsetof(struct context, ctx));
933122394Sharti	if (TR(DEPEND)) {
934122394Sharti		snmp_debug("depend: looking for %s", asn_oid2str(obj));
935122394Sharti		if (idx)
936122394Sharti			snmp_debug("depend: index is %s", asn_oid2str(idx));
937122394Sharti	}
938122394Sharti	TAILQ_FOREACH(d, &context->dlist, link)
939122394Sharti		if (asn_compare_oid(obj, &d->dep.obj) == 0 &&
940122394Sharti		    ((idx == NULL && d->dep.idx.len == 0) ||
941122394Sharti		     (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) {
942122394Sharti			if(TR(DEPEND))
943122394Sharti				snmp_debug("depend: found");
944122394Sharti			return (&d->dep);
945122394Sharti		}
946122394Sharti
947122394Sharti	if(TR(DEPEND))
948122394Sharti		snmp_debug("depend: creating");
949122394Sharti
950122394Sharti	if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL)
951122394Sharti		return (NULL);
952122394Sharti	memset(&d->dep, 0, len);
953122394Sharti
954122394Sharti	d->dep.obj = *obj;
955122394Sharti	if (idx == NULL)
956122394Sharti		d->dep.idx.len = 0;
957122394Sharti	else
958122394Sharti		d->dep.idx = *idx;
959122394Sharti	d->len = len;
960122394Sharti	d->func = func;
961122394Sharti
962122394Sharti	TAILQ_INSERT_TAIL(&context->dlist, d, link);
963122394Sharti
964122394Sharti	return (&d->dep);
965122394Sharti}
966122394Sharti
967122394Sharti/*
968122394Sharti * Make an error response from a PDU. We do this without decoding the
969122394Sharti * variable bindings. This means we can sent the junk back to a caller
970310903Sngie * that has sent us junk in the first place.
971122394Sharti */
972122394Shartienum snmp_ret
973122394Shartisnmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b,
974122394Sharti    struct asn_buf *resp_b)
975122394Sharti{
976301661Sngie	u_char type;
977122394Sharti	asn_len_t len;
978122394Sharti	struct snmp_pdu resp;
979122394Sharti	enum asn_err err;
980122394Sharti	enum snmp_code code;
981122394Sharti
982301661Sngie	snmp_pdu_create_response(pdu, &resp);
983301661Sngie
984216294Ssyrinx	if ((code = snmp_pdu_decode_header(pdu_b, &resp)) != SNMP_CODE_OK)
985122394Sharti		return (SNMP_RET_IGN);
986122394Sharti
987301661Sngie	if (pdu->version == SNMP_V3) {
988301661Sngie		if (resp.user.priv_proto != SNMP_PRIV_NOPRIV &&
989301661Sngie		   (asn_get_header(pdu_b, &type, &resp.scoped_len) != ASN_ERR_OK
990301661Sngie		   || type != ASN_TYPE_OCTETSTRING)) {
991301661Sngie			snmp_error("cannot decode encrypted pdu");
992301661Sngie			return (SNMP_RET_IGN);
993301661Sngie		}
994301661Sngie
995301661Sngie		if (asn_get_sequence(pdu_b, &len) != ASN_ERR_OK) {
996301661Sngie			snmp_error("cannot decode scoped pdu header");
997301661Sngie			return (SNMP_RET_IGN);
998301661Sngie		}
999301661Sngie
1000301661Sngie		len = SNMP_ENGINE_ID_SIZ;
1001301661Sngie		if (asn_get_octetstring(pdu_b, (u_char *)resp.context_engine,
1002301661Sngie		    &len) != ASN_ERR_OK) {
1003301661Sngie			snmp_error("cannot decode msg context engine");
1004301661Sngie			return (SNMP_RET_IGN);
1005301661Sngie		}
1006301661Sngie		resp.context_engine_len = len;
1007301661Sngie		len = SNMP_CONTEXT_NAME_SIZ;
1008301661Sngie		if (asn_get_octetstring(pdu_b, (u_char *)resp.context_name,
1009301661Sngie		    &len) != ASN_ERR_OK) {
1010301661Sngie			snmp_error("cannot decode msg context name");
1011301661Sngie			return (SNMP_RET_IGN);
1012301661Sngie		}
1013301661Sngie		resp.context_name[len] = '\0';
1014301661Sngie	}
1015301661Sngie
1016301661Sngie
1017301661Sngie	if (asn_get_header(pdu_b, &type, &len) != ASN_ERR_OK) {
1018301661Sngie		snmp_error("cannot get pdu header");
1019122394Sharti		return (SNMP_RET_IGN);
1020301661Sngie	}
1021122394Sharti
1022301661Sngie	if ((type & ~ASN_TYPE_MASK) !=
1023301661Sngie	    (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT)) {
1024301661Sngie		snmp_error("bad pdu header tag");
1025301661Sngie		return (SNMP_RET_IGN);
1026301661Sngie	}
1027301661Sngie
1028122394Sharti	err = snmp_parse_pdus_hdr(pdu_b, &resp, &len);
1029122394Sharti	if (ASN_ERR_STOPPED(err))
1030122394Sharti		return (SNMP_RET_IGN);
1031122394Sharti	if (pdu_b->asn_len < len)
1032122394Sharti		return (SNMP_RET_IGN);
1033122394Sharti	pdu_b->asn_len = len;
1034122394Sharti
1035122394Sharti	/* now we have the bindings left - construct new message */
1036122394Sharti	resp.error_status = pdu->error_status;
1037122394Sharti	resp.error_index = pdu->error_index;
1038122394Sharti	resp.type = SNMP_PDU_RESPONSE;
1039122394Sharti
1040122394Sharti	code = snmp_pdu_encode_header(resp_b, &resp);
1041122394Sharti	if (code != SNMP_CODE_OK)
1042122394Sharti		return (SNMP_RET_IGN);
1043122394Sharti
1044122394Sharti	if (pdu_b->asn_len > resp_b->asn_len)
1045122394Sharti		/* too short */
1046122394Sharti		return (SNMP_RET_IGN);
1047122394Sharti	(void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len);
1048122394Sharti	resp_b->asn_len -= pdu_b->asn_len;
1049122394Sharti	resp_b->asn_ptr += pdu_b->asn_len;
1050122394Sharti
1051122394Sharti	code = snmp_fix_encoding(resp_b, &resp);
1052122394Sharti	if (code != SNMP_CODE_OK)
1053122394Sharti		return (SNMP_RET_IGN);
1054122394Sharti
1055122394Sharti	return (SNMP_RET_OK);
1056122394Sharti}
1057122394Sharti
1058122394Shartistatic void
1059122394Shartisnmp_debug_func(const char *fmt, ...)
1060122394Sharti{
1061122394Sharti	va_list ap;
1062122394Sharti
1063122394Sharti	va_start(ap, fmt);
1064122394Sharti	vfprintf(stderr, fmt, ap);
1065122394Sharti	va_end(ap);
1066122394Sharti	fprintf(stderr, "\n");
1067122394Sharti}
1068