snmpagent.c revision 122394
1179260Sjb/*
2179260Sjb * Copyright (c) 2001-2003
3179260Sjb *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4179260Sjb *	All rights reserved.
5179260Sjb *
6179260Sjb * Author: Harti Brandt <harti@freebsd.org>
7179260Sjb *
8179260Sjb * Redistribution of this software and documentation and use in source and
9179260Sjb * binary forms, with or without modification, are permitted provided that
10179260Sjb * the following conditions are met:
11179260Sjb *
12179260Sjb * 1. Redistributions of source code or documentation must retain the above
13179260Sjb *    copyright notice, this list of conditions and the following disclaimer.
14179260Sjb * 2. Redistributions in binary form must reproduce the above copyright
15179260Sjb *    notice, this list of conditions and the following disclaimer in the
16179260Sjb *    documentation and/or other materials provided with the distribution.
17179260Sjb * 3. Neither the name of the Institute nor the names of its contributors
18179260Sjb *    may be used to endorse or promote products derived from this software
19179260Sjb *    without specific prior written permission.
20179260Sjb *
21179260Sjb * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
22179260Sjb * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
23179260Sjb * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
24179260Sjb * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
25179260Sjb * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT,
26179260Sjb * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27179260Sjb * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
28179260Sjb * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29179260Sjb * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30179260Sjb * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
31179260Sjb * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32179260Sjb *
33179260Sjb * $Begemot: bsnmp/lib/snmpagent.c,v 1.14 2003/01/30 11:23:00 hbb Exp $
34179260Sjb *
35179260Sjb * SNMP Agent functions
36179260Sjb */
37179260Sjb#include <sys/types.h>
38179260Sjb#include <sys/queue.h>
39179260Sjb#include <stdio.h>
40179260Sjb#include <stdlib.h>
41179260Sjb#include <stddef.h>
42179260Sjb#include <stdarg.h>
43179260Sjb#include <string.h>
44179260Sjb
45179260Sjb#include "asn1.h"
46179260Sjb#include "snmp.h"
47179260Sjb#include "snmppriv.h"
48179260Sjb#include "snmpagent.h"
49179260Sjb
50179260Sjbstatic void snmp_debug_func(const char *fmt, ...);
51179260Sjb
52179260Sjbvoid (*snmp_debug)(const char *fmt, ...) = snmp_debug_func;
53179260Sjb
54179260Sjbstruct snmp_node *tree;
55179260Sjbu_int  tree_size;
56179260Sjb
57179260Sjb/*
58179260Sjb * Structure to hold dependencies during SET processing
59179260Sjb * The last two members of this structure must be the
60179260Sjb * dependency visible by the user and the user data.
61179260Sjb */
62179260Sjbstruct depend {
63179260Sjb	TAILQ_ENTRY(depend) link;
64179260Sjb	size_t	len;		/* size of data part */
65179260Sjb	snmp_depop_t	func;
66179260Sjb	struct snmp_dependency dep;
67179260Sjb	u_char	data[];
68179260Sjb};
69179260SjbTAILQ_HEAD(depend_list, depend);
70179260Sjb
71179260Sjb/*
72179260Sjb * Structure to hold atfinish functions during SET processing.
73179260Sjb */
74179260Sjbstruct finish {
75179260Sjb	STAILQ_ENTRY(finish) link;
76179260Sjb	snmp_set_finish_t func;
77179260Sjb	void	*arg;
78179260Sjb};
79179260SjbSTAILQ_HEAD(finish_list, finish);
80179260Sjb
81179260Sjb/*
82179260Sjb * Set context
83179260Sjb */
84179260Sjbstruct context {
85179260Sjb	struct snmp_context	ctx;
86179260Sjb	struct depend_list	dlist;
87179260Sjb	struct finish_list	flist;
88179260Sjb	const struct snmp_node	*node[SNMP_MAX_BINDINGS];
89179260Sjb	struct snmp_scratch	scratch[SNMP_MAX_BINDINGS];
90179260Sjb	struct depend		*depend;
91179260Sjb};
92179260Sjb
93179260Sjb#define	TR(W)	(snmp_trace & SNMP_TRACE_##W)
94179260Sjbu_int snmp_trace = 0;
95179260Sjb
96179260Sjbstatic char oidbuf[ASN_OIDSTRLEN];
97179260Sjb
98179260Sjb/*
99179260Sjb * Allocate a context
100179260Sjb */
101179260Sjbstruct snmp_context *
102179260Sjbsnmp_init_context(void)
103179260Sjb{
104179260Sjb	struct context *context;
105179260Sjb
106179260Sjb	if ((context = malloc(sizeof(*context))) == NULL)
107179260Sjb		return (NULL);
108179260Sjb
109179260Sjb	memset(context, 0, sizeof(*context));
110179260Sjb	TAILQ_INIT(&context->dlist);
111179260Sjb	STAILQ_INIT(&context->flist);
112179260Sjb
113179260Sjb	return (&context->ctx);
114179260Sjb}
115179260Sjb
116179260Sjb/*
117179260Sjb * Find a variable for SET/GET and the first GETBULK pass.
118179260Sjb * Return the node pointer. If the search fails, set the errp to
119179260Sjb * the correct SNMPv2 GET exception code.
120179260Sjb */
121179260Sjbstatic struct snmp_node *
122179260Sjbfind_node(const struct snmp_value *value, enum snmp_syntax *errp)
123179260Sjb{
124179260Sjb	struct snmp_node *tp;
125179260Sjb
126179260Sjb	if (TR(FIND))
127179260Sjb		snmp_debug("find: searching %s",
128179260Sjb		    asn_oid2str_r(&value->var, oidbuf));
129179260Sjb
130179260Sjb	/*
131179260Sjb	 * If we have an exact match (the entry in the table is a
132179260Sjb	 * sub-oid from the variable) we have found what we are for.
133179260Sjb	 * If the table oid is higher than the variable, there is no match.
134179260Sjb	 */
135179260Sjb	for (tp = tree; tp < tree + tree_size; tp++) {
136179260Sjb		if (asn_is_suboid(&tp->oid, &value->var))
137179260Sjb			goto found;
138179260Sjb		if (asn_compare_oid(&tp->oid, &value->var) >= 0)
139179260Sjb			break;
140179260Sjb	}
141179260Sjb
142179260Sjb	if (TR(FIND))
143179260Sjb		snmp_debug("find: no match");
144179260Sjb	*errp = SNMP_SYNTAX_NOSUCHOBJECT;
145179260Sjb	return (NULL);
146179260Sjb
147179260Sjb  found:
148179260Sjb	/* leafs must have a 0 instance identifier */
149179260Sjb	if (tp->type == SNMP_NODE_LEAF &&
150179260Sjb	    (value->var.len != tp->oid.len + 1 ||
151179260Sjb	     value->var.subs[tp->oid.len] != 0)) {
152179260Sjb		if (TR(FIND))
153179260Sjb			snmp_debug("find: bad leaf index");
154179260Sjb		*errp = SNMP_SYNTAX_NOSUCHINSTANCE;
155179260Sjb		return (NULL);
156179260Sjb	}
157179260Sjb	if (TR(FIND))
158179260Sjb		snmp_debug("find: found %s",
159179260Sjb		    asn_oid2str_r(&value->var, oidbuf));
160179260Sjb	return (tp);
161179260Sjb}
162179260Sjb
163179260Sjbstatic struct snmp_node *
164179260Sjbfind_subnode(const struct snmp_value *value)
165179260Sjb{
166179260Sjb	struct snmp_node *tp;
167179260Sjb
168179260Sjb	for (tp = tree; tp < tree + tree_size; tp++) {
169179260Sjb		if (asn_is_suboid(&value->var, &tp->oid))
170179260Sjb			return (tp);
171179260Sjb	}
172179260Sjb	return (NULL);
173179260Sjb}
174179260Sjb
175179260Sjb/*
176179260Sjb * Execute a GET operation. The tree is rooted at the global 'root'.
177179260Sjb * Build the response PDU on the fly. If the return code is SNMP_RET_ERR
178179260Sjb * the pdu error status and index will be set.
179179260Sjb */
180179260Sjbenum snmp_ret
181179260Sjbsnmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b,
182179260Sjb    struct snmp_pdu *resp, void *data)
183179260Sjb{
184179260Sjb	int ret;
185179260Sjb	u_int i;
186179260Sjb	struct snmp_node *tp;
187179260Sjb	enum snmp_syntax except;
188179260Sjb	struct context context;
189179260Sjb	enum asn_err err;
190179260Sjb
191179260Sjb	memset(&context, 0, sizeof(context));
192179260Sjb	context.ctx.data = data;
193179260Sjb
194179260Sjb	memset(resp, 0, sizeof(*resp));
195179260Sjb	strcpy(resp->community, pdu->community);
196179260Sjb	resp->version = pdu->version;
197179260Sjb	resp->type = SNMP_PDU_RESPONSE;
198179260Sjb	resp->request_id = pdu->request_id;
199179260Sjb	resp->version = pdu->version;
200179260Sjb
201179260Sjb	if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
202179260Sjb		/* cannot even encode header - very bad */
203179260Sjb		return (SNMP_RET_IGN);
204179260Sjb
205179260Sjb	for (i = 0; i < pdu->nbindings; i++) {
206179260Sjb		resp->bindings[i].var = pdu->bindings[i].var;
207179260Sjb		if ((tp = find_node(&pdu->bindings[i], &except)) == NULL) {
208179260Sjb			if (pdu->version == SNMP_V1) {
209179260Sjb				if (TR(GET))
210179260Sjb					snmp_debug("get: nosuchname");
211179260Sjb				pdu->error_status = SNMP_ERR_NOSUCHNAME;
212179260Sjb				pdu->error_index = i + 1;
213179260Sjb				snmp_pdu_free(resp);
214179260Sjb				return (SNMP_RET_ERR);
215179260Sjb			}
216179260Sjb			if (TR(GET))
217179260Sjb				snmp_debug("get: exception %u", except);
218179260Sjb			resp->bindings[i].syntax = except;
219179260Sjb
220179260Sjb		} else {
221179260Sjb			/* call the action to fetch the value. */
222179260Sjb			resp->bindings[i].syntax = tp->syntax;
223179260Sjb			ret = (*tp->op)(&context.ctx, &resp->bindings[i],
224179260Sjb			    tp->oid.len, tp->index, SNMP_OP_GET);
225179260Sjb			if (TR(GET))
226179260Sjb				snmp_debug("get: action returns %d", ret);
227179260Sjb
228179260Sjb			if (ret == SNMP_ERR_NOSUCHNAME) {
229179260Sjb				if (pdu->version == SNMP_V1) {
230179260Sjb					pdu->error_status = SNMP_ERR_NOSUCHNAME;
231179260Sjb					pdu->error_index = i + 1;
232179260Sjb					snmp_pdu_free(resp);
233179260Sjb					return (SNMP_RET_ERR);
234179260Sjb				}
235179260Sjb				if (TR(GET))
236179260Sjb					snmp_debug("get: exception noSuchInstance");
237179260Sjb				resp->bindings[i].syntax = SNMP_SYNTAX_NOSUCHINSTANCE;
238179260Sjb
239179260Sjb			} else if (ret != SNMP_ERR_NOERROR) {
240179260Sjb				pdu->error_status = SNMP_ERR_GENERR;
241179260Sjb				pdu->error_index = i + 1;
242179260Sjb				snmp_pdu_free(resp);
243179260Sjb				return (SNMP_RET_ERR);
244179260Sjb			}
245179260Sjb		}
246179260Sjb		resp->nbindings++;
247179260Sjb
248179260Sjb		err = snmp_binding_encode(resp_b, &resp->bindings[i]);
249179260Sjb
250179260Sjb		if (err == ASN_ERR_EOBUF) {
251179260Sjb			pdu->error_status = SNMP_ERR_TOOBIG;
252179260Sjb			pdu->error_index = 0;
253179260Sjb			snmp_pdu_free(resp);
254179260Sjb			return (SNMP_RET_ERR);
255179260Sjb		}
256179260Sjb		if (err != ASN_ERR_OK) {
257179260Sjb			if (TR(GET))
258179260Sjb				snmp_debug("get: binding encoding: %u", err);
259179260Sjb			pdu->error_status = SNMP_ERR_GENERR;
260179260Sjb			pdu->error_index = i + 1;
261179260Sjb			snmp_pdu_free(resp);
262179260Sjb			return (SNMP_RET_ERR);
263179260Sjb		}
264179260Sjb	}
265179260Sjb
266179260Sjb	return (snmp_fix_encoding(resp_b, resp));
267179260Sjb}
268179260Sjb
269179260Sjbstatic struct snmp_node *
270179260Sjbnext_node(const struct snmp_value *value, int *pnext)
271179260Sjb{
272179260Sjb	struct snmp_node *tp;
273179260Sjb
274179260Sjb	if (TR(FIND))
275179260Sjb		snmp_debug("next: searching %s",
276179260Sjb		    asn_oid2str_r(&value->var, oidbuf));
277179260Sjb
278179260Sjb	*pnext = 0;
279179260Sjb	for (tp = tree; tp < tree + tree_size; tp++) {
280179260Sjb		if (asn_is_suboid(&tp->oid, &value->var)) {
281179260Sjb			/* the tree OID is a sub-oid of the requested OID. */
282179260Sjb			if (tp->type == SNMP_NODE_LEAF) {
283179260Sjb				if (tp->oid.len == value->var.len) {
284179260Sjb					/* request for scalar type */
285179260Sjb					if (TR(FIND))
286179260Sjb						snmp_debug("next: found scalar %s",
287179260Sjb						    asn_oid2str_r(&tp->oid, oidbuf));
288179260Sjb					return (tp);
289179260Sjb				}
290179260Sjb				/* try next */
291179260Sjb			} else {
292179260Sjb				if (TR(FIND))
293179260Sjb					snmp_debug("next: found column %s",
294179260Sjb					    asn_oid2str_r(&tp->oid, oidbuf));
295179260Sjb				return (tp);
296179260Sjb			}
297179260Sjb		} else if (asn_is_suboid(&value->var, &tp->oid) ||
298179260Sjb		    asn_compare_oid(&tp->oid, &value->var) >= 0) {
299179260Sjb			if (TR(FIND))
300179260Sjb				snmp_debug("next: found %s",
301179260Sjb				    asn_oid2str_r(&tp->oid, oidbuf));
302179260Sjb			*pnext = 1;
303179260Sjb			return (tp);
304179260Sjb		}
305179260Sjb	}
306179260Sjb
307179260Sjb	if (TR(FIND))
308179260Sjb		snmp_debug("next: failed");
309179260Sjb
310179260Sjb	return (NULL);
311179260Sjb}
312179260Sjb
313179260Sjbstatic enum snmp_ret
314179260Sjbdo_getnext(struct context *context, const struct snmp_value *inb,
315179260Sjb    struct snmp_value *outb, struct snmp_pdu *pdu)
316179260Sjb{
317179260Sjb	const struct snmp_node *tp;
318179260Sjb	int ret, next;
319179260Sjb
320179260Sjb	if ((tp = next_node(inb, &next)) == NULL)
321179260Sjb		goto eofMib;
322179260Sjb
323179260Sjb	/* retain old variable if we are doing a GETNEXT on an exact
324179260Sjb	 * matched leaf only */
325179260Sjb	if (tp->type == SNMP_NODE_LEAF || next)
326179260Sjb		outb->var = tp->oid;
327179260Sjb	else
328179260Sjb		outb->var = inb->var;
329179260Sjb
330179260Sjb	for (;;) {
331179260Sjb		outb->syntax = tp->syntax;
332179260Sjb		if (tp->type == SNMP_NODE_LEAF) {
333179260Sjb			/* make a GET operation */
334179260Sjb			outb->var.subs[outb->var.len++] = 0;
335179260Sjb			ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
336179260Sjb			    tp->index, SNMP_OP_GET);
337179260Sjb		} else {
338179260Sjb			/* make a GETNEXT */
339179260Sjb			ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
340179260Sjb			     tp->index, SNMP_OP_GETNEXT);
341179260Sjb		}
342179260Sjb		if (ret != SNMP_ERR_NOSUCHNAME) {
343179260Sjb			/* got something */
344179260Sjb			if (ret != SNMP_ERR_NOERROR && TR(GETNEXT))
345179260Sjb				snmp_debug("getnext: %s returns %u",
346179260Sjb				    asn_oid2str(&outb->var), ret);
347179260Sjb			break;
348179260Sjb		}
349179260Sjb
350179260Sjb		/* object has no data - try next */
351179260Sjb		if (++tp == tree + tree_size)
352179260Sjb			break;
353179260Sjb		outb->var = tp->oid;
354179260Sjb	}
355179260Sjb
356179260Sjb	if (ret == SNMP_ERR_NOSUCHNAME) {
357179260Sjb  eofMib:
358179260Sjb		outb->var = inb->var;
359179260Sjb		if (pdu->version == SNMP_V1) {
360179260Sjb			pdu->error_status = SNMP_ERR_NOSUCHNAME;
361179260Sjb			return (SNMP_RET_ERR);
362179260Sjb		}
363179260Sjb		outb->syntax = SNMP_SYNTAX_ENDOFMIBVIEW;
364179260Sjb
365179260Sjb	} else if (ret != SNMP_ERR_NOERROR) {
366179260Sjb		pdu->error_status = SNMP_ERR_GENERR;
367179260Sjb		return (SNMP_RET_ERR);
368179260Sjb	}
369179260Sjb	return (SNMP_RET_OK);
370179260Sjb}
371179260Sjb
372179260Sjb
373179260Sjb/*
374179260Sjb * Execute a GETNEXT operation. The tree is rooted at the global 'root'.
375179260Sjb * Build the response PDU on the fly. The return is:
376179260Sjb */
377179260Sjbenum snmp_ret
378179260Sjbsnmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b,
379179260Sjb    struct snmp_pdu *resp, void *data)
380179260Sjb{
381179260Sjb	struct context context;
382179260Sjb	u_int i;
383179260Sjb	enum asn_err err;
384179260Sjb	enum snmp_ret result;
385179260Sjb
386179260Sjb	memset(&context, 0, sizeof(context));
387179260Sjb	context.ctx.data = data;
388179260Sjb
389179260Sjb	memset(resp, 0, sizeof(*resp));
390179260Sjb	strcpy(resp->community, pdu->community);
391179260Sjb	resp->type = SNMP_PDU_RESPONSE;
392179260Sjb	resp->request_id = pdu->request_id;
393179260Sjb	resp->version = pdu->version;
394179260Sjb
395179260Sjb	if (snmp_pdu_encode_header(resp_b, resp))
396179260Sjb		return (SNMP_RET_IGN);
397179260Sjb
398179260Sjb	for (i = 0; i < pdu->nbindings; i++) {
399179260Sjb		result = do_getnext(&context, &pdu->bindings[i],
400179260Sjb		    &resp->bindings[i], pdu);
401179260Sjb
402179260Sjb		if (result != SNMP_RET_OK) {
403179260Sjb			pdu->error_index = i + 1;
404179260Sjb			snmp_pdu_free(resp);
405179260Sjb			return (result);
406179260Sjb		}
407179260Sjb
408179260Sjb		resp->nbindings++;
409179260Sjb
410179260Sjb		err = snmp_binding_encode(resp_b, &resp->bindings[i]);
411179260Sjb
412179260Sjb		if (err == ASN_ERR_EOBUF) {
413179260Sjb			pdu->error_status = SNMP_ERR_TOOBIG;
414179260Sjb			pdu->error_index = 0;
415179260Sjb			snmp_pdu_free(resp);
416179260Sjb			return (SNMP_RET_ERR);
417179260Sjb		}
418179260Sjb		if (err != ASN_ERR_OK) {
419179260Sjb			if (TR(GET))
420179260Sjb				snmp_debug("getnext: binding encoding: %u", err);
421179260Sjb			pdu->error_status = SNMP_ERR_GENERR;
422179260Sjb			pdu->error_index = i + 1;
423179260Sjb			snmp_pdu_free(resp);
424179260Sjb			return (SNMP_RET_ERR);
425179260Sjb		}
426179260Sjb	}
427179260Sjb	return (snmp_fix_encoding(resp_b, resp));
428179260Sjb}
429179260Sjb
430179260Sjbenum snmp_ret
431179260Sjbsnmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b,
432179260Sjb    struct snmp_pdu *resp, void *data)
433179260Sjb{
434179260Sjb	struct context context;
435179260Sjb	u_int i;
436179260Sjb	int cnt;
437179260Sjb	u_int non_rep;
438179260Sjb	int eomib;
439179260Sjb	enum snmp_ret result;
440179260Sjb	enum asn_err err;
441179260Sjb
442179260Sjb	memset(&context, 0, sizeof(context));
443179260Sjb	context.ctx.data = data;
444179260Sjb
445179260Sjb	memset(resp, 0, sizeof(*resp));
446179260Sjb	strcpy(resp->community, pdu->community);
447179260Sjb	resp->version = pdu->version;
448179260Sjb	resp->type = SNMP_PDU_RESPONSE;
449179260Sjb	resp->request_id = pdu->request_id;
450179260Sjb	resp->version = pdu->version;
451179260Sjb
452179260Sjb	if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
453179260Sjb		/* cannot even encode header - very bad */
454179260Sjb		return (SNMP_RET_IGN);
455179260Sjb
456179260Sjb	if ((non_rep = pdu->error_status) > pdu->nbindings)
457179260Sjb		non_rep = pdu->nbindings;
458179260Sjb
459179260Sjb	/* non-repeaters */
460179260Sjb	for (i = 0; i < non_rep; i++) {
461179260Sjb		result = do_getnext(&context, &pdu->bindings[i],
462179260Sjb		    &resp->bindings[resp->nbindings], pdu);
463179260Sjb
464179260Sjb		if (result != SNMP_RET_OK) {
465179260Sjb			pdu->error_index = i + 1;
466179260Sjb			snmp_pdu_free(resp);
467179260Sjb			return (result);
468179260Sjb		}
469179260Sjb
470179260Sjb		err = snmp_binding_encode(resp_b,
471179260Sjb		    &resp->bindings[resp->nbindings++]);
472179260Sjb
473179260Sjb		if (err == ASN_ERR_EOBUF)
474179260Sjb			goto done;
475179260Sjb
476179260Sjb		if (err != ASN_ERR_OK) {
477179260Sjb			if (TR(GET))
478179260Sjb				snmp_debug("getnext: binding encoding: %u", err);
479179260Sjb			pdu->error_status = SNMP_ERR_GENERR;
480179260Sjb			pdu->error_index = i + 1;
481179260Sjb			snmp_pdu_free(resp);
482179260Sjb			return (SNMP_RET_ERR);
483179260Sjb		}
484179260Sjb	}
485179260Sjb
486179260Sjb	if (non_rep == pdu->nbindings)
487179260Sjb		goto done;
488179260Sjb
489179260Sjb	/* repeates */
490179260Sjb	for (cnt = 0; cnt < pdu->error_index; cnt++) {
491179260Sjb		eomib = 1;
492179260Sjb		for (i = non_rep; i < pdu->nbindings; i++) {
493179260Sjb			if (cnt == 0)
494179260Sjb				result = do_getnext(&context, &pdu->bindings[i],
495179260Sjb				    &resp->bindings[resp->nbindings], pdu);
496179260Sjb			else
497179260Sjb				result = do_getnext(&context,
498179260Sjb				    &resp->bindings[resp->nbindings -
499179260Sjb				    (pdu->nbindings - non_rep)],
500179260Sjb				    &resp->bindings[resp->nbindings], pdu);
501179260Sjb
502179260Sjb			if (result != SNMP_RET_OK) {
503179260Sjb				pdu->error_index = i + 1;
504179260Sjb				snmp_pdu_free(resp);
505179260Sjb				return (result);
506179260Sjb			}
507179260Sjb			if (resp->bindings[resp->nbindings].syntax !=
508179260Sjb			    SNMP_SYNTAX_ENDOFMIBVIEW)
509179260Sjb				eomib = 0;
510179260Sjb
511179260Sjb			err = snmp_binding_encode(resp_b,
512179260Sjb			    &resp->bindings[resp->nbindings++]);
513179260Sjb
514179260Sjb			if (err == ASN_ERR_EOBUF)
515179260Sjb				goto done;
516179260Sjb
517179260Sjb			if (err != ASN_ERR_OK) {
518179260Sjb				if (TR(GET))
519179260Sjb					snmp_debug("getnext: binding encoding: %u", err);
520179260Sjb				pdu->error_status = SNMP_ERR_GENERR;
521179260Sjb				pdu->error_index = i + 1;
522179260Sjb				snmp_pdu_free(resp);
523179260Sjb				return (SNMP_RET_ERR);
524179260Sjb			}
525179260Sjb		}
526179260Sjb		if (eomib)
527179260Sjb			break;
528179260Sjb	}
529179260Sjb
530179260Sjb  done:
531179260Sjb	return (snmp_fix_encoding(resp_b, resp));
532179260Sjb}
533179260Sjb
534179260Sjb/*
535179260Sjb * Rollback a SET operation. Failed index is 'i'.
536179260Sjb */
537179260Sjbstatic void
538179260Sjbrollback(struct context *context, struct snmp_pdu *pdu, u_int i)
539179260Sjb{
540179260Sjb	struct snmp_value *b;
541179260Sjb	const struct snmp_node *np;
542179260Sjb	int ret;
543179260Sjb
544179260Sjb	while (i-- > 0) {
545179260Sjb		b = &pdu->bindings[i];
546179260Sjb		np = context->node[i];
547179260Sjb
548179260Sjb		context->ctx.scratch = &context->scratch[i];
549179260Sjb
550179260Sjb		ret = (*np->op)(&context->ctx, b, np->oid.len, np->index,
551179260Sjb		    SNMP_OP_ROLLBACK);
552179260Sjb
553179260Sjb		if (ret != SNMP_ERR_NOERROR) {
554179260Sjb			snmp_error("set: rollback failed (%d) on variable %s "
555179260Sjb			    "index %u", ret, asn_oid2str(&b->var), i);
556179260Sjb			if (pdu->version != SNMP_V1) {
557179260Sjb				pdu->error_status = SNMP_ERR_UNDO_FAILED;
558179260Sjb				pdu->error_index = 0;
559179260Sjb			}
560179260Sjb		}
561179260Sjb	}
562179260Sjb}
563179260Sjb
564179260Sjb/*
565179260Sjb * Commit dependencies.
566179260Sjb */
567179260Sjbint
568179260Sjbsnmp_dep_commit(struct snmp_context *ctx)
569179260Sjb{
570179260Sjb	struct context *context = (struct context *)ctx;
571179260Sjb	int ret;
572179260Sjb
573179260Sjb	TAILQ_FOREACH(context->depend, &context->dlist, link) {
574179260Sjb		ctx->dep = &context->depend->dep;
575179260Sjb
576179260Sjb		if (TR(SET))
577179260Sjb			snmp_debug("set: dependency commit %s",
578179260Sjb			    asn_oid2str(&ctx->dep->obj));
579179260Sjb
580179260Sjb		ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT);
581179260Sjb
582179260Sjb		if (ret != SNMP_ERR_NOERROR) {
583179260Sjb			if (TR(SET))
584179260Sjb				snmp_debug("set: dependency failed %d", ret);
585179260Sjb			return (ret);
586179260Sjb		}
587179260Sjb	}
588179260Sjb	return (SNMP_ERR_NOERROR);
589179260Sjb}
590179260Sjb
591179260Sjb/*
592179260Sjb * Rollback dependencies
593179260Sjb */
594179260Sjbint
595179260Sjbsnmp_dep_rollback(struct snmp_context *ctx)
596179260Sjb{
597179260Sjb	struct context *context = (struct context *)ctx;
598179260Sjb	int ret, ret1;
599179260Sjb	char objbuf[ASN_OIDSTRLEN];
600179260Sjb	char idxbuf[ASN_OIDSTRLEN];
601179260Sjb
602179260Sjb	ret1 = SNMP_ERR_NOERROR;
603179260Sjb	while ((context->depend =
604179260Sjb	    TAILQ_PREV(context->depend, depend_list, link)) != NULL) {
605179260Sjb		ctx->dep = &context->depend->dep;
606179260Sjb
607179260Sjb		if (TR(SET))
608179260Sjb			snmp_debug("set: dependency rollback %s",
609179260Sjb			    asn_oid2str(&ctx->dep->obj));
610179260Sjb
611179260Sjb		ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK);
612179260Sjb
613179260Sjb		if (ret != SNMP_ERR_NOERROR) {
614179260Sjb			snmp_debug("set: dep rollback returns %u: %s %s", ret,
615179260Sjb			    asn_oid2str_r(&ctx->dep->obj, objbuf),
616179260Sjb			    asn_oid2str_r(&ctx->dep->idx, idxbuf));
617179260Sjb			if (ret1 == SNMP_ERR_NOERROR)
618179260Sjb				ret1 = ret;
619179260Sjb		}
620179260Sjb	}
621179260Sjb	return (ret1);
622179260Sjb}
623179260Sjb
624179260Sjb/*
625179260Sjb * Do a SET operation.
626179260Sjb */
627179260Sjbenum snmp_ret
628179260Sjbsnmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b,
629179260Sjb    struct snmp_pdu *resp, void *data)
630179260Sjb{
631179260Sjb	int ret;
632179260Sjb	u_int i;
633179260Sjb	enum snmp_ret code;
634179260Sjb	enum asn_err asnerr;
635179260Sjb	struct context context;
636179260Sjb	const struct snmp_node *np;
637179260Sjb	struct finish *f;
638179260Sjb	struct depend *d;
639179260Sjb	struct snmp_value *b;
640179260Sjb	enum snmp_syntax except;
641179260Sjb
642179260Sjb	memset(&context, 0, sizeof(context));
643179260Sjb	TAILQ_INIT(&context.dlist);
644179260Sjb	STAILQ_INIT(&context.flist);
645179260Sjb	context.ctx.data = data;
646179260Sjb
647179260Sjb	memset(resp, 0, sizeof(*resp));
648179260Sjb	strcpy(resp->community, pdu->community);
649179260Sjb	resp->type = SNMP_PDU_RESPONSE;
650179260Sjb	resp->request_id = pdu->request_id;
651179260Sjb	resp->version = pdu->version;
652179260Sjb
653179260Sjb	if (snmp_pdu_encode_header(resp_b, resp))
654179260Sjb		return (SNMP_RET_IGN);
655179260Sjb
656179260Sjb	/*
657179260Sjb	 * 1. Find all nodes, check that they are writeable and
658179260Sjb	 *    that the syntax is ok, copy over the binding to the response.
659179260Sjb	 */
660179260Sjb	for (i = 0; i < pdu->nbindings; i++) {
661179260Sjb		b = &pdu->bindings[i];
662179260Sjb
663179260Sjb		if ((np = context.node[i] = find_node(b, &except)) == NULL) {
664179260Sjb			/* not found altogether or LEAF with wrong index */
665179260Sjb			if (TR(SET))
666179260Sjb				snmp_debug("set: node not found %s",
667179260Sjb				    asn_oid2str_r(&b->var, oidbuf));
668179260Sjb			if (pdu->version == SNMP_V1) {
669179260Sjb				pdu->error_index = i + 1;
670179260Sjb				pdu->error_status = SNMP_ERR_NOSUCHNAME;
671179260Sjb			} else if ((np = find_subnode(b)) != NULL) {
672179260Sjb				/* 2. intermediate object */
673179260Sjb				pdu->error_index = i + 1;
674179260Sjb				pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
675179260Sjb			} else if (except == SNMP_SYNTAX_NOSUCHOBJECT) {
676179260Sjb				pdu->error_index = i + 1;
677179260Sjb				pdu->error_status = SNMP_ERR_NO_ACCESS;
678179260Sjb			} else {
679179260Sjb				pdu->error_index = i + 1;
680179260Sjb				pdu->error_status = SNMP_ERR_NO_CREATION;
681179260Sjb			}
682179260Sjb			snmp_pdu_free(resp);
683179260Sjb			return (SNMP_RET_ERR);
684179260Sjb		}
685179260Sjb		/*
686179260Sjb		 * 2. write/createable?
687179260Sjb		 * Can check this for leafs only, because in v2 we have
688179260Sjb		 * to differentiate between NOT_WRITEABLE and NO_CREATION
689179260Sjb		 * and only the action routine for COLUMNS knows, whether
690179260Sjb		 * a column exists.
691179260Sjb		 */
692179260Sjb		if (np->type == SNMP_NODE_LEAF &&
693179260Sjb		    !(np->flags & SNMP_NODE_CANSET)) {
694179260Sjb			if (pdu->version == SNMP_V1) {
695179260Sjb				pdu->error_index = i + 1;
696179260Sjb				pdu->error_status = SNMP_ERR_NOSUCHNAME;
697179260Sjb			} else {
698179260Sjb				pdu->error_index = i + 1;
699179260Sjb				pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
700179260Sjb			}
701179260Sjb			snmp_pdu_free(resp);
702179260Sjb			return (SNMP_RET_ERR);
703179260Sjb		}
704179260Sjb		/*
705179260Sjb		 * 3. Ensure the right syntax
706179260Sjb		 */
707179260Sjb		if (np->syntax != b->syntax) {
708179260Sjb			if (pdu->version == SNMP_V1) {
709179260Sjb				pdu->error_index = i + 1;
710179260Sjb				pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */
711179260Sjb			} else {
712179260Sjb				pdu->error_index = i + 1;
713179260Sjb				pdu->error_status = SNMP_ERR_WRONG_TYPE;
714179260Sjb			}
715179260Sjb			snmp_pdu_free(resp);
716179260Sjb			return (SNMP_RET_ERR);
717179260Sjb		}
718179260Sjb		/*
719179260Sjb		 * 4. Copy binding
720179260Sjb		 */
721179260Sjb		if (snmp_value_copy(&resp->bindings[i], b)) {
722179260Sjb			pdu->error_index = i + 1;
723179260Sjb			pdu->error_status = SNMP_ERR_GENERR;
724179260Sjb			snmp_pdu_free(resp);
725179260Sjb			return (SNMP_RET_ERR);
726179260Sjb		}
727179260Sjb		asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]);
728179260Sjb		if (asnerr == ASN_ERR_EOBUF) {
729179260Sjb			pdu->error_index = i + 1;
730179260Sjb			pdu->error_status = SNMP_ERR_TOOBIG;
731179260Sjb			snmp_pdu_free(resp);
732179260Sjb			return (SNMP_RET_ERR);
733179260Sjb		} else if (asnerr != ASN_ERR_OK) {
734179260Sjb			pdu->error_index = i + 1;
735179260Sjb			pdu->error_status = SNMP_ERR_GENERR;
736179260Sjb			snmp_pdu_free(resp);
737179260Sjb			return (SNMP_RET_ERR);
738179260Sjb		}
739179260Sjb		resp->nbindings++;
740179260Sjb	}
741179260Sjb
742179260Sjb	code = SNMP_RET_OK;
743179260Sjb
744179260Sjb	/*
745179260Sjb	 * 2. Call the SET method for each node. If a SET fails, rollback
746179260Sjb	 *    everything. Map error codes depending on the version.
747179260Sjb	 */
748179260Sjb	for (i = 0; i < pdu->nbindings; i++) {
749179260Sjb		b = &pdu->bindings[i];
750179260Sjb		np = context.node[i];
751179260Sjb
752179260Sjb		context.ctx.var_index = i + 1;
753179260Sjb		context.ctx.scratch = &context.scratch[i];
754179260Sjb
755179260Sjb		ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
756179260Sjb		    SNMP_OP_SET);
757179260Sjb
758179260Sjb		if (TR(SET))
759179260Sjb			snmp_debug("set: action %s returns %d", np->name, ret);
760179260Sjb
761179260Sjb		if (pdu->version == SNMP_V1) {
762179260Sjb			switch (ret) {
763179260Sjb			  case SNMP_ERR_NO_ACCESS:
764179260Sjb				ret = SNMP_ERR_NOSUCHNAME;
765179260Sjb				break;
766179260Sjb			  case SNMP_ERR_WRONG_TYPE:
767179260Sjb				/* should no happen */
768179260Sjb				ret = SNMP_ERR_BADVALUE;
769179260Sjb				break;
770179260Sjb			  case SNMP_ERR_WRONG_LENGTH:
771179260Sjb				ret = SNMP_ERR_BADVALUE;
772179260Sjb				break;
773179260Sjb			  case SNMP_ERR_WRONG_ENCODING:
774179260Sjb				/* should not happen */
775179260Sjb				ret = SNMP_ERR_BADVALUE;
776179260Sjb				break;
777179260Sjb			  case SNMP_ERR_WRONG_VALUE:
778179260Sjb				ret = SNMP_ERR_BADVALUE;
779179260Sjb				break;
780179260Sjb			  case SNMP_ERR_NO_CREATION:
781179260Sjb				ret = SNMP_ERR_NOSUCHNAME;
782179260Sjb				break;
783179260Sjb			  case SNMP_ERR_INCONS_VALUE:
784179260Sjb				ret = SNMP_ERR_BADVALUE;
785179260Sjb				break;
786179260Sjb			  case SNMP_ERR_RES_UNAVAIL:
787179260Sjb				ret = SNMP_ERR_GENERR;
788179260Sjb				break;
789179260Sjb			  case SNMP_ERR_COMMIT_FAILED:
790179260Sjb				ret = SNMP_ERR_GENERR;
791179260Sjb				break;
792179260Sjb			  case SNMP_ERR_UNDO_FAILED:
793179260Sjb				ret = SNMP_ERR_GENERR;
794179260Sjb				break;
795179260Sjb			  case SNMP_ERR_AUTH_ERR:
796179260Sjb				/* should not happen */
797179260Sjb				ret = SNMP_ERR_GENERR;
798179260Sjb				break;
799179260Sjb			  case SNMP_ERR_NOT_WRITEABLE:
800179260Sjb				ret = SNMP_ERR_NOSUCHNAME;
801179260Sjb				break;
802179260Sjb			  case SNMP_ERR_INCONS_NAME:
803179260Sjb				ret = SNMP_ERR_BADVALUE;
804179260Sjb				break;
805179260Sjb			}
806179260Sjb		}
807179260Sjb		if (ret != SNMP_ERR_NOERROR) {
808179260Sjb			pdu->error_index = i + 1;
809179260Sjb			pdu->error_status = ret;
810179260Sjb
811179260Sjb			rollback(&context, pdu, i);
812179260Sjb			snmp_pdu_free(resp);
813179260Sjb
814179260Sjb			code = SNMP_RET_ERR;
815179260Sjb
816179260Sjb			goto errout;
817179260Sjb		}
818179260Sjb	}
819179260Sjb
820179260Sjb	/*
821179260Sjb	 * 3. Call dependencies
822179260Sjb	 */
823179260Sjb	if (TR(SET))
824179260Sjb		snmp_debug("set: set operations ok");
825179260Sjb
826179260Sjb	if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) {
827179260Sjb		pdu->error_status = ret;
828179260Sjb		pdu->error_index = context.ctx.var_index;
829179260Sjb
830179260Sjb		if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) {
831179260Sjb			if (pdu->version != SNMP_V1) {
832179260Sjb				pdu->error_status = SNMP_ERR_UNDO_FAILED;
833179260Sjb				pdu->error_index = 0;
834179260Sjb			}
835179260Sjb		}
836179260Sjb		rollback(&context, pdu, i);
837179260Sjb		snmp_pdu_free(resp);
838179260Sjb
839179260Sjb		code = SNMP_RET_ERR;
840179260Sjb
841179260Sjb		goto errout;
842179260Sjb	}
843179260Sjb
844179260Sjb	/*
845179260Sjb	 * 4. Commit and copy values from the original packet to the response.
846179260Sjb	 *    This is not the commit operation from RFC 1905 but rather an
847179260Sjb	 *    'FREE RESOURCES' operation. It shouldn't fail.
848179260Sjb	 */
849179260Sjb	if (TR(SET))
850179260Sjb		snmp_debug("set: commiting");
851179260Sjb
852179260Sjb	for (i = 0; i < pdu->nbindings; i++) {
853179260Sjb		b = &resp->bindings[i];
854179260Sjb		np = context.node[i];
855179260Sjb
856179260Sjb		context.ctx.var_index = i + 1;
857179260Sjb		context.ctx.scratch = &context.scratch[i];
858179260Sjb
859179260Sjb		ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
860179260Sjb		    SNMP_OP_COMMIT);
861179260Sjb
862179260Sjb		if (ret != SNMP_ERR_NOERROR)
863179260Sjb			snmp_error("set: commit failed (%d) on"
864179260Sjb			    " variable %s index %u", ret,
865179260Sjb			    asn_oid2str_r(&b->var, oidbuf), i);
866179260Sjb	}
867179260Sjb
868179260Sjb	if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
869179260Sjb		snmp_error("set: fix_encoding failed");
870179260Sjb		snmp_pdu_free(resp);
871179260Sjb		code = SNMP_RET_IGN;
872179260Sjb	}
873179260Sjb
874179260Sjb	/*
875179260Sjb	 * Done
876179260Sjb	 */
877179260Sjb  errout:
878179260Sjb	while ((d = TAILQ_FIRST(&context.dlist)) != NULL) {
879179260Sjb		TAILQ_REMOVE(&context.dlist, d, link);
880179260Sjb		free(d);
881179260Sjb	}
882179260Sjb
883179260Sjb	/*
884179260Sjb	 * call finish function
885179260Sjb	 */
886179260Sjb	while ((f = STAILQ_FIRST(&context.flist)) != NULL) {
887179260Sjb		STAILQ_REMOVE_HEAD(&context.flist, link);
888179260Sjb		(*f->func)(&context.ctx, code != SNMP_RET_OK, f->arg);
889179260Sjb		free(f);
890179260Sjb	}
891179260Sjb
892179260Sjb	if (TR(SET))
893179260Sjb		snmp_debug("set: returning %d", code);
894179260Sjb
895179260Sjb	return (code);
896179260Sjb}
897179260Sjb/*
898179260Sjb * Lookup a dependency. If it doesn't exist, create one
899179260Sjb */
900179260Sjbstruct snmp_dependency *
901179260Sjbsnmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj,
902179260Sjb    const struct asn_oid *idx, size_t len, snmp_depop_t func)
903179260Sjb{
904179260Sjb	struct context *context;
905179260Sjb	struct depend *d;
906179260Sjb
907179260Sjb	context = (struct context *)(void *)
908179260Sjb	    ((char *)ctx - offsetof(struct context, ctx));
909179260Sjb	if (TR(DEPEND)) {
910179260Sjb		snmp_debug("depend: looking for %s", asn_oid2str(obj));
911179260Sjb		if (idx)
912179260Sjb			snmp_debug("depend: index is %s", asn_oid2str(idx));
913179260Sjb	}
914179260Sjb	TAILQ_FOREACH(d, &context->dlist, link)
915179260Sjb		if (asn_compare_oid(obj, &d->dep.obj) == 0 &&
916179260Sjb		    ((idx == NULL && d->dep.idx.len == 0) ||
917179260Sjb		     (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) {
918179260Sjb			if(TR(DEPEND))
919179260Sjb				snmp_debug("depend: found");
920179260Sjb			return (&d->dep);
921179260Sjb		}
922179260Sjb
923179260Sjb	if(TR(DEPEND))
924179260Sjb		snmp_debug("depend: creating");
925179260Sjb
926179260Sjb	if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL)
927179260Sjb		return (NULL);
928179260Sjb	memset(&d->dep, 0, len);
929179260Sjb
930179260Sjb	d->dep.obj = *obj;
931179260Sjb	if (idx == NULL)
932179260Sjb		d->dep.idx.len = 0;
933179260Sjb	else
934179260Sjb		d->dep.idx = *idx;
935179260Sjb	d->len = len;
936179260Sjb	d->func = func;
937179260Sjb
938179260Sjb	TAILQ_INSERT_TAIL(&context->dlist, d, link);
939179260Sjb
940179260Sjb	return (&d->dep);
941179260Sjb}
942179260Sjb
943179260Sjb/*
944179260Sjb * Register a finish function.
945179260Sjb */
946179260Sjbint
947179260Sjbsnmp_set_atfinish(struct snmp_context *ctx, snmp_set_finish_t func, void *arg)
948179260Sjb{
949179260Sjb	struct context *context;
950179260Sjb	struct finish *f;
951179260Sjb
952179260Sjb	context = (struct context *)(void *)
953179260Sjb	    ((char *)ctx - offsetof(struct context, ctx));
954179260Sjb	if ((f = malloc(sizeof(struct finish))) == NULL)
955179260Sjb		return (-1);
956179260Sjb	f->func = func;
957179260Sjb	f->arg = arg;
958179260Sjb	STAILQ_INSERT_TAIL(&context->flist, f, link);
959179260Sjb
960179260Sjb	return (0);
961179260Sjb}
962179260Sjb
963179260Sjb/*
964179260Sjb * Make an error response from a PDU. We do this without decoding the
965179260Sjb * variable bindings. This means we can sent the junk back to a caller
966179260Sjb * that has sent us junk in the first place.
967179260Sjb */
968179260Sjbenum snmp_ret
969179260Sjbsnmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b,
970179260Sjb    struct asn_buf *resp_b)
971179260Sjb{
972179260Sjb	asn_len_t len;
973179260Sjb	struct snmp_pdu resp;
974179260Sjb	enum asn_err err;
975179260Sjb	enum snmp_code code;
976179260Sjb
977179260Sjb	memset(&resp, 0, sizeof(resp));
978179260Sjb
979179260Sjb	/* Message sequence */
980179260Sjb	if (asn_get_sequence(pdu_b, &len) != ASN_ERR_OK)
981179260Sjb		return (SNMP_RET_IGN);
982179260Sjb	if (pdu_b->asn_len < len)
983179260Sjb		return (SNMP_RET_IGN);
984179260Sjb
985179260Sjb	err = snmp_parse_message_hdr(pdu_b, &resp, &len);
986179260Sjb	if (ASN_ERR_STOPPED(err))
987179260Sjb		return (SNMP_RET_IGN);
988179260Sjb	if (pdu_b->asn_len < len)
989179260Sjb		return (SNMP_RET_IGN);
990179260Sjb	pdu_b->asn_len = len;
991179260Sjb
992179260Sjb	err = snmp_parse_pdus_hdr(pdu_b, &resp, &len);
993179260Sjb	if (ASN_ERR_STOPPED(err))
994179260Sjb		return (SNMP_RET_IGN);
995179260Sjb	if (pdu_b->asn_len < len)
996179260Sjb		return (SNMP_RET_IGN);
997179260Sjb	pdu_b->asn_len = len;
998179260Sjb
999179260Sjb	/* now we have the bindings left - construct new message */
1000179260Sjb	resp.error_status = pdu->error_status;
1001179260Sjb	resp.error_index = pdu->error_index;
1002179260Sjb	resp.type = SNMP_PDU_RESPONSE;
1003179260Sjb
1004179260Sjb	code = snmp_pdu_encode_header(resp_b, &resp);
1005179260Sjb	if (code != SNMP_CODE_OK)
1006179260Sjb		return (SNMP_RET_IGN);
1007179260Sjb
1008179260Sjb	if (pdu_b->asn_len > resp_b->asn_len)
1009179260Sjb		/* too short */
1010179260Sjb		return (SNMP_RET_IGN);
1011179260Sjb	(void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len);
1012179260Sjb	resp_b->asn_len -= pdu_b->asn_len;
1013179260Sjb	resp_b->asn_ptr += pdu_b->asn_len;
1014179260Sjb
1015179260Sjb	code = snmp_fix_encoding(resp_b, &resp);
1016179260Sjb	if (code != SNMP_CODE_OK)
1017179260Sjb		return (SNMP_RET_IGN);
1018179260Sjb
1019179260Sjb	return (SNMP_RET_OK);
1020179260Sjb}
1021179260Sjb
1022179260Sjbstatic void
1023179260Sjbsnmp_debug_func(const char *fmt, ...)
1024179260Sjb{
1025179260Sjb	va_list ap;
1026179260Sjb
1027179260Sjb	va_start(ap, fmt);
1028179260Sjb	vfprintf(stderr, fmt, ap);
1029179260Sjb	va_end(ap);
1030179260Sjb	fprintf(stderr, "\n");
1031179260Sjb}
1032179260Sjb