snmpagent.c revision 150920
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>
7133211Sharti *
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.
16133211Sharti *
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
168122394Sharti/*
169122394Sharti * Execute a GET operation. The tree is rooted at the global 'root'.
170122394Sharti * Build the response PDU on the fly. If the return code is SNMP_RET_ERR
171122394Sharti * the pdu error status and index will be set.
172122394Sharti */
173122394Shartienum snmp_ret
174122394Shartisnmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b,
175122394Sharti    struct snmp_pdu *resp, void *data)
176122394Sharti{
177122394Sharti	int ret;
178122394Sharti	u_int i;
179122394Sharti	struct snmp_node *tp;
180122394Sharti	enum snmp_syntax except;
181122394Sharti	struct context context;
182122394Sharti	enum asn_err err;
183122394Sharti
184122394Sharti	memset(&context, 0, sizeof(context));
185122394Sharti	context.ctx.data = data;
186122394Sharti
187122394Sharti	memset(resp, 0, sizeof(*resp));
188122394Sharti	strcpy(resp->community, pdu->community);
189122394Sharti	resp->version = pdu->version;
190122394Sharti	resp->type = SNMP_PDU_RESPONSE;
191122394Sharti	resp->request_id = pdu->request_id;
192122394Sharti	resp->version = pdu->version;
193122394Sharti
194122394Sharti	if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
195122394Sharti		/* cannot even encode header - very bad */
196122394Sharti		return (SNMP_RET_IGN);
197122394Sharti
198122394Sharti	for (i = 0; i < pdu->nbindings; i++) {
199122394Sharti		resp->bindings[i].var = pdu->bindings[i].var;
200122394Sharti		if ((tp = find_node(&pdu->bindings[i], &except)) == NULL) {
201122394Sharti			if (pdu->version == SNMP_V1) {
202122394Sharti				if (TR(GET))
203122394Sharti					snmp_debug("get: nosuchname");
204122394Sharti				pdu->error_status = SNMP_ERR_NOSUCHNAME;
205122394Sharti				pdu->error_index = i + 1;
206122394Sharti				snmp_pdu_free(resp);
207122394Sharti				return (SNMP_RET_ERR);
208122394Sharti			}
209122394Sharti			if (TR(GET))
210122394Sharti				snmp_debug("get: exception %u", except);
211122394Sharti			resp->bindings[i].syntax = except;
212122394Sharti
213122394Sharti		} else {
214122394Sharti			/* call the action to fetch the value. */
215122394Sharti			resp->bindings[i].syntax = tp->syntax;
216122394Sharti			ret = (*tp->op)(&context.ctx, &resp->bindings[i],
217122394Sharti			    tp->oid.len, tp->index, SNMP_OP_GET);
218122394Sharti			if (TR(GET))
219122394Sharti				snmp_debug("get: action returns %d", ret);
220122394Sharti
221122394Sharti			if (ret == SNMP_ERR_NOSUCHNAME) {
222122394Sharti				if (pdu->version == SNMP_V1) {
223122394Sharti					pdu->error_status = SNMP_ERR_NOSUCHNAME;
224122394Sharti					pdu->error_index = i + 1;
225122394Sharti					snmp_pdu_free(resp);
226122394Sharti					return (SNMP_RET_ERR);
227122394Sharti				}
228122394Sharti				if (TR(GET))
229122394Sharti					snmp_debug("get: exception noSuchInstance");
230122394Sharti				resp->bindings[i].syntax = SNMP_SYNTAX_NOSUCHINSTANCE;
231122394Sharti
232122394Sharti			} else if (ret != SNMP_ERR_NOERROR) {
233122394Sharti				pdu->error_status = SNMP_ERR_GENERR;
234122394Sharti				pdu->error_index = i + 1;
235122394Sharti				snmp_pdu_free(resp);
236122394Sharti				return (SNMP_RET_ERR);
237122394Sharti			}
238122394Sharti		}
239122394Sharti		resp->nbindings++;
240122394Sharti
241122394Sharti		err = snmp_binding_encode(resp_b, &resp->bindings[i]);
242122394Sharti
243122394Sharti		if (err == ASN_ERR_EOBUF) {
244122394Sharti			pdu->error_status = SNMP_ERR_TOOBIG;
245122394Sharti			pdu->error_index = 0;
246122394Sharti			snmp_pdu_free(resp);
247122394Sharti			return (SNMP_RET_ERR);
248122394Sharti		}
249122394Sharti		if (err != ASN_ERR_OK) {
250122394Sharti			if (TR(GET))
251122394Sharti				snmp_debug("get: binding encoding: %u", err);
252122394Sharti			pdu->error_status = SNMP_ERR_GENERR;
253122394Sharti			pdu->error_index = i + 1;
254122394Sharti			snmp_pdu_free(resp);
255122394Sharti			return (SNMP_RET_ERR);
256122394Sharti		}
257122394Sharti	}
258122394Sharti
259122394Sharti	return (snmp_fix_encoding(resp_b, resp));
260122394Sharti}
261122394Sharti
262122394Shartistatic struct snmp_node *
263122394Shartinext_node(const struct snmp_value *value, int *pnext)
264122394Sharti{
265122394Sharti	struct snmp_node *tp;
266122394Sharti
267122394Sharti	if (TR(FIND))
268122394Sharti		snmp_debug("next: searching %s",
269122394Sharti		    asn_oid2str_r(&value->var, oidbuf));
270122394Sharti
271122394Sharti	*pnext = 0;
272122394Sharti	for (tp = tree; tp < tree + tree_size; tp++) {
273122394Sharti		if (asn_is_suboid(&tp->oid, &value->var)) {
274122394Sharti			/* the tree OID is a sub-oid of the requested OID. */
275122394Sharti			if (tp->type == SNMP_NODE_LEAF) {
276122394Sharti				if (tp->oid.len == value->var.len) {
277122394Sharti					/* request for scalar type */
278122394Sharti					if (TR(FIND))
279122394Sharti						snmp_debug("next: found scalar %s",
280122394Sharti						    asn_oid2str_r(&tp->oid, oidbuf));
281122394Sharti					return (tp);
282122394Sharti				}
283122394Sharti				/* try next */
284122394Sharti			} else {
285122394Sharti				if (TR(FIND))
286122394Sharti					snmp_debug("next: found column %s",
287122394Sharti					    asn_oid2str_r(&tp->oid, oidbuf));
288122394Sharti				return (tp);
289122394Sharti			}
290122394Sharti		} else if (asn_is_suboid(&value->var, &tp->oid) ||
291122394Sharti		    asn_compare_oid(&tp->oid, &value->var) >= 0) {
292122394Sharti			if (TR(FIND))
293122394Sharti				snmp_debug("next: found %s",
294122394Sharti				    asn_oid2str_r(&tp->oid, oidbuf));
295122394Sharti			*pnext = 1;
296122394Sharti			return (tp);
297122394Sharti		}
298122394Sharti	}
299122394Sharti
300122394Sharti	if (TR(FIND))
301122394Sharti		snmp_debug("next: failed");
302122394Sharti
303122394Sharti	return (NULL);
304122394Sharti}
305122394Sharti
306122394Shartistatic enum snmp_ret
307122394Shartido_getnext(struct context *context, const struct snmp_value *inb,
308122394Sharti    struct snmp_value *outb, struct snmp_pdu *pdu)
309122394Sharti{
310122394Sharti	const struct snmp_node *tp;
311122394Sharti	int ret, next;
312122394Sharti
313122394Sharti	if ((tp = next_node(inb, &next)) == NULL)
314122394Sharti		goto eofMib;
315122394Sharti
316122394Sharti	/* retain old variable if we are doing a GETNEXT on an exact
317122394Sharti	 * matched leaf only */
318122394Sharti	if (tp->type == SNMP_NODE_LEAF || next)
319122394Sharti		outb->var = tp->oid;
320122394Sharti	else
321122394Sharti		outb->var = inb->var;
322122394Sharti
323122394Sharti	for (;;) {
324122394Sharti		outb->syntax = tp->syntax;
325122394Sharti		if (tp->type == SNMP_NODE_LEAF) {
326122394Sharti			/* make a GET operation */
327122394Sharti			outb->var.subs[outb->var.len++] = 0;
328122394Sharti			ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
329122394Sharti			    tp->index, SNMP_OP_GET);
330122394Sharti		} else {
331122394Sharti			/* make a GETNEXT */
332122394Sharti			ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
333122394Sharti			     tp->index, SNMP_OP_GETNEXT);
334122394Sharti		}
335122394Sharti		if (ret != SNMP_ERR_NOSUCHNAME) {
336122394Sharti			/* got something */
337122394Sharti			if (ret != SNMP_ERR_NOERROR && TR(GETNEXT))
338122394Sharti				snmp_debug("getnext: %s returns %u",
339122394Sharti				    asn_oid2str(&outb->var), ret);
340122394Sharti			break;
341122394Sharti		}
342122394Sharti
343122394Sharti		/* object has no data - try next */
344122394Sharti		if (++tp == tree + tree_size)
345122394Sharti			break;
346142810Sharti
347142810Sharti		if (TR(GETNEXT))
348142810Sharti			snmp_debug("getnext: no data - avancing to %s",
349142810Sharti			    asn_oid2str(&tp->oid));
350142810Sharti
351122394Sharti		outb->var = tp->oid;
352122394Sharti	}
353122394Sharti
354122394Sharti	if (ret == SNMP_ERR_NOSUCHNAME) {
355122394Sharti  eofMib:
356122394Sharti		outb->var = inb->var;
357122394Sharti		if (pdu->version == SNMP_V1) {
358122394Sharti			pdu->error_status = SNMP_ERR_NOSUCHNAME;
359122394Sharti			return (SNMP_RET_ERR);
360122394Sharti		}
361122394Sharti		outb->syntax = SNMP_SYNTAX_ENDOFMIBVIEW;
362122394Sharti
363122394Sharti	} else if (ret != SNMP_ERR_NOERROR) {
364122394Sharti		pdu->error_status = SNMP_ERR_GENERR;
365122394Sharti		return (SNMP_RET_ERR);
366122394Sharti	}
367122394Sharti	return (SNMP_RET_OK);
368122394Sharti}
369122394Sharti
370122394Sharti
371122394Sharti/*
372122394Sharti * Execute a GETNEXT operation. The tree is rooted at the global 'root'.
373122394Sharti * Build the response PDU on the fly. The return is:
374122394Sharti */
375122394Shartienum snmp_ret
376122394Shartisnmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b,
377122394Sharti    struct snmp_pdu *resp, void *data)
378122394Sharti{
379122394Sharti	struct context context;
380122394Sharti	u_int i;
381122394Sharti	enum asn_err err;
382122394Sharti	enum snmp_ret result;
383122394Sharti
384122394Sharti	memset(&context, 0, sizeof(context));
385122394Sharti	context.ctx.data = data;
386122394Sharti
387122394Sharti	memset(resp, 0, sizeof(*resp));
388122394Sharti	strcpy(resp->community, pdu->community);
389122394Sharti	resp->type = SNMP_PDU_RESPONSE;
390122394Sharti	resp->request_id = pdu->request_id;
391122394Sharti	resp->version = pdu->version;
392122394Sharti
393122394Sharti	if (snmp_pdu_encode_header(resp_b, resp))
394122394Sharti		return (SNMP_RET_IGN);
395122394Sharti
396122394Sharti	for (i = 0; i < pdu->nbindings; i++) {
397122394Sharti		result = do_getnext(&context, &pdu->bindings[i],
398122394Sharti		    &resp->bindings[i], pdu);
399122394Sharti
400122394Sharti		if (result != SNMP_RET_OK) {
401122394Sharti			pdu->error_index = i + 1;
402122394Sharti			snmp_pdu_free(resp);
403122394Sharti			return (result);
404122394Sharti		}
405122394Sharti
406122394Sharti		resp->nbindings++;
407122394Sharti
408122394Sharti		err = snmp_binding_encode(resp_b, &resp->bindings[i]);
409122394Sharti
410122394Sharti		if (err == ASN_ERR_EOBUF) {
411122394Sharti			pdu->error_status = SNMP_ERR_TOOBIG;
412122394Sharti			pdu->error_index = 0;
413122394Sharti			snmp_pdu_free(resp);
414122394Sharti			return (SNMP_RET_ERR);
415122394Sharti		}
416122394Sharti		if (err != ASN_ERR_OK) {
417122394Sharti			if (TR(GET))
418122394Sharti				snmp_debug("getnext: binding encoding: %u", err);
419122394Sharti			pdu->error_status = SNMP_ERR_GENERR;
420122394Sharti			pdu->error_index = i + 1;
421122394Sharti			snmp_pdu_free(resp);
422122394Sharti			return (SNMP_RET_ERR);
423122394Sharti		}
424122394Sharti	}
425122394Sharti	return (snmp_fix_encoding(resp_b, resp));
426122394Sharti}
427122394Sharti
428122394Shartienum snmp_ret
429122394Shartisnmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b,
430122394Sharti    struct snmp_pdu *resp, void *data)
431122394Sharti{
432122394Sharti	struct context context;
433122394Sharti	u_int i;
434122394Sharti	int cnt;
435122394Sharti	u_int non_rep;
436122394Sharti	int eomib;
437122394Sharti	enum snmp_ret result;
438122394Sharti	enum asn_err err;
439122394Sharti
440122394Sharti	memset(&context, 0, sizeof(context));
441122394Sharti	context.ctx.data = data;
442122394Sharti
443122394Sharti	memset(resp, 0, sizeof(*resp));
444122394Sharti	strcpy(resp->community, pdu->community);
445122394Sharti	resp->version = pdu->version;
446122394Sharti	resp->type = SNMP_PDU_RESPONSE;
447122394Sharti	resp->request_id = pdu->request_id;
448122394Sharti	resp->version = pdu->version;
449122394Sharti
450122394Sharti	if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
451122394Sharti		/* cannot even encode header - very bad */
452122394Sharti		return (SNMP_RET_IGN);
453122394Sharti
454122394Sharti	if ((non_rep = pdu->error_status) > pdu->nbindings)
455122394Sharti		non_rep = pdu->nbindings;
456122394Sharti
457122394Sharti	/* non-repeaters */
458122394Sharti	for (i = 0; i < non_rep; i++) {
459122394Sharti		result = do_getnext(&context, &pdu->bindings[i],
460122394Sharti		    &resp->bindings[resp->nbindings], pdu);
461122394Sharti
462122394Sharti		if (result != SNMP_RET_OK) {
463122394Sharti			pdu->error_index = i + 1;
464122394Sharti			snmp_pdu_free(resp);
465122394Sharti			return (result);
466122394Sharti		}
467122394Sharti
468122394Sharti		err = snmp_binding_encode(resp_b,
469122394Sharti		    &resp->bindings[resp->nbindings++]);
470122394Sharti
471122394Sharti		if (err == ASN_ERR_EOBUF)
472122394Sharti			goto done;
473122394Sharti
474122394Sharti		if (err != ASN_ERR_OK) {
475122394Sharti			if (TR(GET))
476122394Sharti				snmp_debug("getnext: binding encoding: %u", err);
477122394Sharti			pdu->error_status = SNMP_ERR_GENERR;
478122394Sharti			pdu->error_index = i + 1;
479122394Sharti			snmp_pdu_free(resp);
480122394Sharti			return (SNMP_RET_ERR);
481122394Sharti		}
482122394Sharti	}
483122394Sharti
484122394Sharti	if (non_rep == pdu->nbindings)
485122394Sharti		goto done;
486122394Sharti
487122394Sharti	/* repeates */
488122394Sharti	for (cnt = 0; cnt < pdu->error_index; cnt++) {
489122394Sharti		eomib = 1;
490122394Sharti		for (i = non_rep; i < pdu->nbindings; i++) {
491122394Sharti			if (cnt == 0)
492122394Sharti				result = do_getnext(&context, &pdu->bindings[i],
493122394Sharti				    &resp->bindings[resp->nbindings], pdu);
494122394Sharti			else
495122394Sharti				result = do_getnext(&context,
496122394Sharti				    &resp->bindings[resp->nbindings -
497122394Sharti				    (pdu->nbindings - non_rep)],
498122394Sharti				    &resp->bindings[resp->nbindings], pdu);
499122394Sharti
500122394Sharti			if (result != SNMP_RET_OK) {
501122394Sharti				pdu->error_index = i + 1;
502122394Sharti				snmp_pdu_free(resp);
503122394Sharti				return (result);
504122394Sharti			}
505122394Sharti			if (resp->bindings[resp->nbindings].syntax !=
506122394Sharti			    SNMP_SYNTAX_ENDOFMIBVIEW)
507122394Sharti				eomib = 0;
508122394Sharti
509122394Sharti			err = snmp_binding_encode(resp_b,
510122394Sharti			    &resp->bindings[resp->nbindings++]);
511122394Sharti
512122394Sharti			if (err == ASN_ERR_EOBUF)
513122394Sharti				goto done;
514122394Sharti
515122394Sharti			if (err != ASN_ERR_OK) {
516122394Sharti				if (TR(GET))
517122394Sharti					snmp_debug("getnext: binding encoding: %u", err);
518122394Sharti				pdu->error_status = SNMP_ERR_GENERR;
519122394Sharti				pdu->error_index = i + 1;
520122394Sharti				snmp_pdu_free(resp);
521122394Sharti				return (SNMP_RET_ERR);
522122394Sharti			}
523122394Sharti		}
524122394Sharti		if (eomib)
525122394Sharti			break;
526122394Sharti	}
527122394Sharti
528122394Sharti  done:
529122394Sharti	return (snmp_fix_encoding(resp_b, resp));
530122394Sharti}
531122394Sharti
532122394Sharti/*
533122394Sharti * Rollback a SET operation. Failed index is 'i'.
534122394Sharti */
535122394Shartistatic void
536122394Shartirollback(struct context *context, struct snmp_pdu *pdu, u_int i)
537122394Sharti{
538122394Sharti	struct snmp_value *b;
539122394Sharti	const struct snmp_node *np;
540122394Sharti	int ret;
541122394Sharti
542122394Sharti	while (i-- > 0) {
543122394Sharti		b = &pdu->bindings[i];
544122394Sharti		np = context->node[i];
545122394Sharti
546122394Sharti		context->ctx.scratch = &context->scratch[i];
547122394Sharti
548122394Sharti		ret = (*np->op)(&context->ctx, b, np->oid.len, np->index,
549122394Sharti		    SNMP_OP_ROLLBACK);
550122394Sharti
551122394Sharti		if (ret != SNMP_ERR_NOERROR) {
552122394Sharti			snmp_error("set: rollback failed (%d) on variable %s "
553122394Sharti			    "index %u", ret, asn_oid2str(&b->var), i);
554122394Sharti			if (pdu->version != SNMP_V1) {
555122394Sharti				pdu->error_status = SNMP_ERR_UNDO_FAILED;
556122394Sharti				pdu->error_index = 0;
557122394Sharti			}
558122394Sharti		}
559122394Sharti	}
560122394Sharti}
561122394Sharti
562122394Sharti/*
563122394Sharti * Commit dependencies.
564122394Sharti */
565122394Shartiint
566122394Shartisnmp_dep_commit(struct snmp_context *ctx)
567122394Sharti{
568122394Sharti	struct context *context = (struct context *)ctx;
569122394Sharti	int ret;
570122394Sharti
571122394Sharti	TAILQ_FOREACH(context->depend, &context->dlist, link) {
572122394Sharti		ctx->dep = &context->depend->dep;
573122394Sharti
574122394Sharti		if (TR(SET))
575122394Sharti			snmp_debug("set: dependency commit %s",
576122394Sharti			    asn_oid2str(&ctx->dep->obj));
577122394Sharti
578122394Sharti		ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT);
579122394Sharti
580122394Sharti		if (ret != SNMP_ERR_NOERROR) {
581122394Sharti			if (TR(SET))
582122394Sharti				snmp_debug("set: dependency failed %d", ret);
583122394Sharti			return (ret);
584122394Sharti		}
585122394Sharti	}
586122394Sharti	return (SNMP_ERR_NOERROR);
587122394Sharti}
588122394Sharti
589122394Sharti/*
590122394Sharti * Rollback dependencies
591122394Sharti */
592122394Shartiint
593122394Shartisnmp_dep_rollback(struct snmp_context *ctx)
594122394Sharti{
595122394Sharti	struct context *context = (struct context *)ctx;
596122394Sharti	int ret, ret1;
597122394Sharti	char objbuf[ASN_OIDSTRLEN];
598122394Sharti	char idxbuf[ASN_OIDSTRLEN];
599122394Sharti
600122394Sharti	ret1 = SNMP_ERR_NOERROR;
601122394Sharti	while ((context->depend =
602122394Sharti	    TAILQ_PREV(context->depend, depend_list, link)) != NULL) {
603122394Sharti		ctx->dep = &context->depend->dep;
604122394Sharti
605122394Sharti		if (TR(SET))
606122394Sharti			snmp_debug("set: dependency rollback %s",
607122394Sharti			    asn_oid2str(&ctx->dep->obj));
608122394Sharti
609122394Sharti		ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK);
610122394Sharti
611122394Sharti		if (ret != SNMP_ERR_NOERROR) {
612122394Sharti			snmp_debug("set: dep rollback returns %u: %s %s", ret,
613122394Sharti			    asn_oid2str_r(&ctx->dep->obj, objbuf),
614122394Sharti			    asn_oid2str_r(&ctx->dep->idx, idxbuf));
615122394Sharti			if (ret1 == SNMP_ERR_NOERROR)
616122394Sharti				ret1 = ret;
617122394Sharti		}
618122394Sharti	}
619122394Sharti	return (ret1);
620122394Sharti}
621122394Sharti
622128237Shartivoid
623128237Shartisnmp_dep_finish(struct snmp_context *ctx)
624128237Sharti{
625128237Sharti	struct context *context = (struct context *)ctx;
626128237Sharti	struct depend *d;
627128237Sharti
628128237Sharti	while ((d = TAILQ_FIRST(&context->dlist)) != NULL) {
629128237Sharti		ctx->dep = &d->dep;
630128237Sharti		(void)d->func(ctx, ctx->dep, SNMP_DEPOP_FINISH);
631128237Sharti		TAILQ_REMOVE(&context->dlist, d, link);
632128237Sharti		free(d);
633128237Sharti	}
634128237Sharti}
635128237Sharti
636122394Sharti/*
637122394Sharti * Do a SET operation.
638122394Sharti */
639122394Shartienum snmp_ret
640122394Shartisnmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b,
641122394Sharti    struct snmp_pdu *resp, void *data)
642122394Sharti{
643122394Sharti	int ret;
644122394Sharti	u_int i;
645122394Sharti	enum asn_err asnerr;
646122394Sharti	struct context context;
647122394Sharti	const struct snmp_node *np;
648122394Sharti	struct snmp_value *b;
649122394Sharti	enum snmp_syntax except;
650122394Sharti
651122394Sharti	memset(&context, 0, sizeof(context));
652122394Sharti	TAILQ_INIT(&context.dlist);
653122394Sharti	context.ctx.data = data;
654122394Sharti
655122394Sharti	memset(resp, 0, sizeof(*resp));
656122394Sharti	strcpy(resp->community, pdu->community);
657122394Sharti	resp->type = SNMP_PDU_RESPONSE;
658122394Sharti	resp->request_id = pdu->request_id;
659122394Sharti	resp->version = pdu->version;
660122394Sharti
661122394Sharti	if (snmp_pdu_encode_header(resp_b, resp))
662122394Sharti		return (SNMP_RET_IGN);
663122394Sharti
664122394Sharti	/*
665122394Sharti	 * 1. Find all nodes, check that they are writeable and
666122394Sharti	 *    that the syntax is ok, copy over the binding to the response.
667122394Sharti	 */
668122394Sharti	for (i = 0; i < pdu->nbindings; i++) {
669122394Sharti		b = &pdu->bindings[i];
670122394Sharti
671122394Sharti		if ((np = context.node[i] = find_node(b, &except)) == NULL) {
672122394Sharti			/* not found altogether or LEAF with wrong index */
673122394Sharti			if (TR(SET))
674122394Sharti				snmp_debug("set: node not found %s",
675122394Sharti				    asn_oid2str_r(&b->var, oidbuf));
676122394Sharti			if (pdu->version == SNMP_V1) {
677122394Sharti				pdu->error_index = i + 1;
678122394Sharti				pdu->error_status = SNMP_ERR_NOSUCHNAME;
679122394Sharti			} else if ((np = find_subnode(b)) != NULL) {
680122394Sharti				/* 2. intermediate object */
681122394Sharti				pdu->error_index = i + 1;
682122394Sharti				pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
683122394Sharti			} else if (except == SNMP_SYNTAX_NOSUCHOBJECT) {
684122394Sharti				pdu->error_index = i + 1;
685122394Sharti				pdu->error_status = SNMP_ERR_NO_ACCESS;
686122394Sharti			} else {
687122394Sharti				pdu->error_index = i + 1;
688122394Sharti				pdu->error_status = SNMP_ERR_NO_CREATION;
689122394Sharti			}
690122394Sharti			snmp_pdu_free(resp);
691122394Sharti			return (SNMP_RET_ERR);
692122394Sharti		}
693122394Sharti		/*
694122394Sharti		 * 2. write/createable?
695122394Sharti		 * Can check this for leafs only, because in v2 we have
696122394Sharti		 * to differentiate between NOT_WRITEABLE and NO_CREATION
697122394Sharti		 * and only the action routine for COLUMNS knows, whether
698122394Sharti		 * a column exists.
699122394Sharti		 */
700122394Sharti		if (np->type == SNMP_NODE_LEAF &&
701122394Sharti		    !(np->flags & SNMP_NODE_CANSET)) {
702122394Sharti			if (pdu->version == SNMP_V1) {
703122394Sharti				pdu->error_index = i + 1;
704122394Sharti				pdu->error_status = SNMP_ERR_NOSUCHNAME;
705122394Sharti			} else {
706122394Sharti				pdu->error_index = i + 1;
707122394Sharti				pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
708122394Sharti			}
709122394Sharti			snmp_pdu_free(resp);
710122394Sharti			return (SNMP_RET_ERR);
711122394Sharti		}
712122394Sharti		/*
713122394Sharti		 * 3. Ensure the right syntax
714122394Sharti		 */
715122394Sharti		if (np->syntax != b->syntax) {
716122394Sharti			if (pdu->version == SNMP_V1) {
717122394Sharti				pdu->error_index = i + 1;
718122394Sharti				pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */
719122394Sharti			} else {
720122394Sharti				pdu->error_index = i + 1;
721122394Sharti				pdu->error_status = SNMP_ERR_WRONG_TYPE;
722122394Sharti			}
723122394Sharti			snmp_pdu_free(resp);
724122394Sharti			return (SNMP_RET_ERR);
725122394Sharti		}
726122394Sharti		/*
727122394Sharti		 * 4. Copy binding
728122394Sharti		 */
729122394Sharti		if (snmp_value_copy(&resp->bindings[i], b)) {
730122394Sharti			pdu->error_index = i + 1;
731122394Sharti			pdu->error_status = SNMP_ERR_GENERR;
732122394Sharti			snmp_pdu_free(resp);
733122394Sharti			return (SNMP_RET_ERR);
734122394Sharti		}
735122394Sharti		asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]);
736122394Sharti		if (asnerr == ASN_ERR_EOBUF) {
737122394Sharti			pdu->error_index = i + 1;
738122394Sharti			pdu->error_status = SNMP_ERR_TOOBIG;
739122394Sharti			snmp_pdu_free(resp);
740122394Sharti			return (SNMP_RET_ERR);
741122394Sharti		} else if (asnerr != ASN_ERR_OK) {
742122394Sharti			pdu->error_index = i + 1;
743122394Sharti			pdu->error_status = SNMP_ERR_GENERR;
744122394Sharti			snmp_pdu_free(resp);
745122394Sharti			return (SNMP_RET_ERR);
746122394Sharti		}
747122394Sharti		resp->nbindings++;
748122394Sharti	}
749122394Sharti
750128237Sharti	context.ctx.code = SNMP_RET_OK;
751122394Sharti
752122394Sharti	/*
753122394Sharti	 * 2. Call the SET method for each node. If a SET fails, rollback
754122394Sharti	 *    everything. Map error codes depending on the version.
755122394Sharti	 */
756122394Sharti	for (i = 0; i < pdu->nbindings; i++) {
757122394Sharti		b = &pdu->bindings[i];
758122394Sharti		np = context.node[i];
759122394Sharti
760122394Sharti		context.ctx.var_index = i + 1;
761122394Sharti		context.ctx.scratch = &context.scratch[i];
762122394Sharti
763122394Sharti		ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
764122394Sharti		    SNMP_OP_SET);
765122394Sharti
766122394Sharti		if (TR(SET))
767122394Sharti			snmp_debug("set: action %s returns %d", np->name, ret);
768122394Sharti
769122394Sharti		if (pdu->version == SNMP_V1) {
770122394Sharti			switch (ret) {
771122394Sharti			  case SNMP_ERR_NO_ACCESS:
772122394Sharti				ret = SNMP_ERR_NOSUCHNAME;
773122394Sharti				break;
774122394Sharti			  case SNMP_ERR_WRONG_TYPE:
775122394Sharti				/* should no happen */
776122394Sharti				ret = SNMP_ERR_BADVALUE;
777122394Sharti				break;
778122394Sharti			  case SNMP_ERR_WRONG_LENGTH:
779122394Sharti				ret = SNMP_ERR_BADVALUE;
780122394Sharti				break;
781122394Sharti			  case SNMP_ERR_WRONG_ENCODING:
782122394Sharti				/* should not happen */
783122394Sharti				ret = SNMP_ERR_BADVALUE;
784122394Sharti				break;
785122394Sharti			  case SNMP_ERR_WRONG_VALUE:
786122394Sharti				ret = SNMP_ERR_BADVALUE;
787122394Sharti				break;
788122394Sharti			  case SNMP_ERR_NO_CREATION:
789122394Sharti				ret = SNMP_ERR_NOSUCHNAME;
790122394Sharti				break;
791122394Sharti			  case SNMP_ERR_INCONS_VALUE:
792122394Sharti				ret = SNMP_ERR_BADVALUE;
793122394Sharti				break;
794122394Sharti			  case SNMP_ERR_RES_UNAVAIL:
795122394Sharti				ret = SNMP_ERR_GENERR;
796122394Sharti				break;
797122394Sharti			  case SNMP_ERR_COMMIT_FAILED:
798122394Sharti				ret = SNMP_ERR_GENERR;
799122394Sharti				break;
800122394Sharti			  case SNMP_ERR_UNDO_FAILED:
801122394Sharti				ret = SNMP_ERR_GENERR;
802122394Sharti				break;
803122394Sharti			  case SNMP_ERR_AUTH_ERR:
804122394Sharti				/* should not happen */
805122394Sharti				ret = SNMP_ERR_GENERR;
806122394Sharti				break;
807122394Sharti			  case SNMP_ERR_NOT_WRITEABLE:
808122394Sharti				ret = SNMP_ERR_NOSUCHNAME;
809122394Sharti				break;
810122394Sharti			  case SNMP_ERR_INCONS_NAME:
811122394Sharti				ret = SNMP_ERR_BADVALUE;
812122394Sharti				break;
813122394Sharti			}
814122394Sharti		}
815122394Sharti		if (ret != SNMP_ERR_NOERROR) {
816122394Sharti			pdu->error_index = i + 1;
817122394Sharti			pdu->error_status = ret;
818122394Sharti
819122394Sharti			rollback(&context, pdu, i);
820122394Sharti			snmp_pdu_free(resp);
821122394Sharti
822128237Sharti			context.ctx.code = SNMP_RET_ERR;
823122394Sharti
824122394Sharti			goto errout;
825122394Sharti		}
826122394Sharti	}
827122394Sharti
828122394Sharti	/*
829122394Sharti	 * 3. Call dependencies
830122394Sharti	 */
831122394Sharti	if (TR(SET))
832122394Sharti		snmp_debug("set: set operations ok");
833122394Sharti
834122394Sharti	if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) {
835122394Sharti		pdu->error_status = ret;
836122394Sharti		pdu->error_index = context.ctx.var_index;
837122394Sharti
838122394Sharti		if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) {
839122394Sharti			if (pdu->version != SNMP_V1) {
840122394Sharti				pdu->error_status = SNMP_ERR_UNDO_FAILED;
841122394Sharti				pdu->error_index = 0;
842122394Sharti			}
843122394Sharti		}
844122394Sharti		rollback(&context, pdu, i);
845122394Sharti		snmp_pdu_free(resp);
846122394Sharti
847128237Sharti		context.ctx.code = SNMP_RET_ERR;
848122394Sharti
849122394Sharti		goto errout;
850122394Sharti	}
851122394Sharti
852122394Sharti	/*
853122394Sharti	 * 4. Commit and copy values from the original packet to the response.
854122394Sharti	 *    This is not the commit operation from RFC 1905 but rather an
855122394Sharti	 *    'FREE RESOURCES' operation. It shouldn't fail.
856122394Sharti	 */
857122394Sharti	if (TR(SET))
858122394Sharti		snmp_debug("set: commiting");
859122394Sharti
860122394Sharti	for (i = 0; i < pdu->nbindings; i++) {
861122394Sharti		b = &resp->bindings[i];
862122394Sharti		np = context.node[i];
863122394Sharti
864122394Sharti		context.ctx.var_index = i + 1;
865122394Sharti		context.ctx.scratch = &context.scratch[i];
866122394Sharti
867122394Sharti		ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
868122394Sharti		    SNMP_OP_COMMIT);
869122394Sharti
870122394Sharti		if (ret != SNMP_ERR_NOERROR)
871122394Sharti			snmp_error("set: commit failed (%d) on"
872122394Sharti			    " variable %s index %u", ret,
873122394Sharti			    asn_oid2str_r(&b->var, oidbuf), i);
874122394Sharti	}
875122394Sharti
876122394Sharti	if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
877122394Sharti		snmp_error("set: fix_encoding failed");
878122394Sharti		snmp_pdu_free(resp);
879128237Sharti		context.ctx.code = SNMP_RET_IGN;
880122394Sharti	}
881122394Sharti
882122394Sharti	/*
883122394Sharti	 * Done
884122394Sharti	 */
885122394Sharti  errout:
886128237Sharti	snmp_dep_finish(&context.ctx);
887122394Sharti
888122394Sharti	if (TR(SET))
889128237Sharti		snmp_debug("set: returning %d", context.ctx.code);
890122394Sharti
891128237Sharti	return (context.ctx.code);
892122394Sharti}
893122394Sharti/*
894122394Sharti * Lookup a dependency. If it doesn't exist, create one
895122394Sharti */
896122394Shartistruct snmp_dependency *
897122394Shartisnmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj,
898122394Sharti    const struct asn_oid *idx, size_t len, snmp_depop_t func)
899122394Sharti{
900122394Sharti	struct context *context;
901122394Sharti	struct depend *d;
902122394Sharti
903122394Sharti	context = (struct context *)(void *)
904122394Sharti	    ((char *)ctx - offsetof(struct context, ctx));
905122394Sharti	if (TR(DEPEND)) {
906122394Sharti		snmp_debug("depend: looking for %s", asn_oid2str(obj));
907122394Sharti		if (idx)
908122394Sharti			snmp_debug("depend: index is %s", asn_oid2str(idx));
909122394Sharti	}
910122394Sharti	TAILQ_FOREACH(d, &context->dlist, link)
911122394Sharti		if (asn_compare_oid(obj, &d->dep.obj) == 0 &&
912122394Sharti		    ((idx == NULL && d->dep.idx.len == 0) ||
913122394Sharti		     (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) {
914122394Sharti			if(TR(DEPEND))
915122394Sharti				snmp_debug("depend: found");
916122394Sharti			return (&d->dep);
917122394Sharti		}
918122394Sharti
919122394Sharti	if(TR(DEPEND))
920122394Sharti		snmp_debug("depend: creating");
921122394Sharti
922122394Sharti	if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL)
923122394Sharti		return (NULL);
924122394Sharti	memset(&d->dep, 0, len);
925122394Sharti
926122394Sharti	d->dep.obj = *obj;
927122394Sharti	if (idx == NULL)
928122394Sharti		d->dep.idx.len = 0;
929122394Sharti	else
930122394Sharti		d->dep.idx = *idx;
931122394Sharti	d->len = len;
932122394Sharti	d->func = func;
933122394Sharti
934122394Sharti	TAILQ_INSERT_TAIL(&context->dlist, d, link);
935122394Sharti
936122394Sharti	return (&d->dep);
937122394Sharti}
938122394Sharti
939122394Sharti/*
940122394Sharti * Make an error response from a PDU. We do this without decoding the
941122394Sharti * variable bindings. This means we can sent the junk back to a caller
942122394Sharti * that has sent us junk in the first place.
943122394Sharti */
944122394Shartienum snmp_ret
945122394Shartisnmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b,
946122394Sharti    struct asn_buf *resp_b)
947122394Sharti{
948122394Sharti	asn_len_t len;
949122394Sharti	struct snmp_pdu resp;
950122394Sharti	enum asn_err err;
951122394Sharti	enum snmp_code code;
952122394Sharti
953122394Sharti	memset(&resp, 0, sizeof(resp));
954122394Sharti
955122394Sharti	/* Message sequence */
956122394Sharti	if (asn_get_sequence(pdu_b, &len) != ASN_ERR_OK)
957122394Sharti		return (SNMP_RET_IGN);
958122394Sharti	if (pdu_b->asn_len < len)
959122394Sharti		return (SNMP_RET_IGN);
960122394Sharti
961122394Sharti	err = snmp_parse_message_hdr(pdu_b, &resp, &len);
962122394Sharti	if (ASN_ERR_STOPPED(err))
963122394Sharti		return (SNMP_RET_IGN);
964122394Sharti	if (pdu_b->asn_len < len)
965122394Sharti		return (SNMP_RET_IGN);
966122394Sharti	pdu_b->asn_len = len;
967122394Sharti
968122394Sharti	err = snmp_parse_pdus_hdr(pdu_b, &resp, &len);
969122394Sharti	if (ASN_ERR_STOPPED(err))
970122394Sharti		return (SNMP_RET_IGN);
971122394Sharti	if (pdu_b->asn_len < len)
972122394Sharti		return (SNMP_RET_IGN);
973122394Sharti	pdu_b->asn_len = len;
974122394Sharti
975122394Sharti	/* now we have the bindings left - construct new message */
976122394Sharti	resp.error_status = pdu->error_status;
977122394Sharti	resp.error_index = pdu->error_index;
978122394Sharti	resp.type = SNMP_PDU_RESPONSE;
979122394Sharti
980122394Sharti	code = snmp_pdu_encode_header(resp_b, &resp);
981122394Sharti	if (code != SNMP_CODE_OK)
982122394Sharti		return (SNMP_RET_IGN);
983122394Sharti
984122394Sharti	if (pdu_b->asn_len > resp_b->asn_len)
985122394Sharti		/* too short */
986122394Sharti		return (SNMP_RET_IGN);
987122394Sharti	(void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len);
988122394Sharti	resp_b->asn_len -= pdu_b->asn_len;
989122394Sharti	resp_b->asn_ptr += pdu_b->asn_len;
990122394Sharti
991122394Sharti	code = snmp_fix_encoding(resp_b, &resp);
992122394Sharti	if (code != SNMP_CODE_OK)
993122394Sharti		return (SNMP_RET_IGN);
994122394Sharti
995122394Sharti	return (SNMP_RET_OK);
996122394Sharti}
997122394Sharti
998122394Shartistatic void
999122394Shartisnmp_debug_func(const char *fmt, ...)
1000122394Sharti{
1001122394Sharti	va_list ap;
1002122394Sharti
1003122394Sharti	va_start(ap, fmt);
1004122394Sharti	vfprintf(stderr, fmt, ap);
1005122394Sharti	va_end(ap);
1006122394Sharti	fprintf(stderr, "\n");
1007122394Sharti}
1008