Deleted Added
full compact
snmpagent.c (133211) snmpagent.c (142810)
1/*
2 * Copyright (c) 2001-2003
3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 * All rights reserved.
5 *
6 * Author: Harti Brandt <harti@freebsd.org>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
1/*
2 * Copyright (c) 2001-2003
3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 * All rights reserved.
5 *
6 * Author: Harti Brandt <harti@freebsd.org>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $Begemot: bsnmp/lib/snmpagent.c,v 1.18 2004/08/06 08:46:55 brandt Exp $
29 * $Begemot: bsnmp/lib/snmpagent.c,v 1.19 2004/08/19 17:09:09 brandt Exp $
30 *
31 * SNMP Agent functions
32 */
33#include <sys/types.h>
34#include <sys/queue.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <stddef.h>
38#include <stdarg.h>
39#include <stdint.h>
40#include <string.h>
41
42#include "asn1.h"
43#include "snmp.h"
44#include "snmppriv.h"
45#include "snmpagent.h"
46
47static void snmp_debug_func(const char *fmt, ...);
48
49void (*snmp_debug)(const char *fmt, ...) = snmp_debug_func;
50
51struct snmp_node *tree;
52u_int tree_size;
53
54/*
55 * Structure to hold dependencies during SET processing
56 * The last two members of this structure must be the
57 * dependency visible by the user and the user data.
58 */
59struct depend {
60 TAILQ_ENTRY(depend) link;
61 size_t len; /* size of data part */
62 snmp_depop_t func;
63 struct snmp_dependency dep;
64#if defined(__GNUC__) && __GNUC__ < 3
65 u_char data[0];
66#else
67 u_char data[];
68#endif
69};
70TAILQ_HEAD(depend_list, depend);
71
72/*
73 * Set context
74 */
75struct context {
76 struct snmp_context ctx;
77 struct depend_list dlist;
78 const struct snmp_node *node[SNMP_MAX_BINDINGS];
79 struct snmp_scratch scratch[SNMP_MAX_BINDINGS];
80 struct depend *depend;
81};
82
83#define TR(W) (snmp_trace & SNMP_TRACE_##W)
84u_int snmp_trace = 0;
85
86static char oidbuf[ASN_OIDSTRLEN];
87
88/*
89 * Allocate a context
90 */
91struct snmp_context *
92snmp_init_context(void)
93{
94 struct context *context;
95
96 if ((context = malloc(sizeof(*context))) == NULL)
97 return (NULL);
98
99 memset(context, 0, sizeof(*context));
100 TAILQ_INIT(&context->dlist);
101
102 return (&context->ctx);
103}
104
105/*
106 * Find a variable for SET/GET and the first GETBULK pass.
107 * Return the node pointer. If the search fails, set the errp to
108 * the correct SNMPv2 GET exception code.
109 */
110static struct snmp_node *
111find_node(const struct snmp_value *value, enum snmp_syntax *errp)
112{
113 struct snmp_node *tp;
114
115 if (TR(FIND))
116 snmp_debug("find: searching %s",
117 asn_oid2str_r(&value->var, oidbuf));
118
119 /*
120 * If we have an exact match (the entry in the table is a
121 * sub-oid from the variable) we have found what we are for.
122 * If the table oid is higher than the variable, there is no match.
123 */
124 for (tp = tree; tp < tree + tree_size; tp++) {
125 if (asn_is_suboid(&tp->oid, &value->var))
126 goto found;
127 if (asn_compare_oid(&tp->oid, &value->var) >= 0)
128 break;
129 }
130
131 if (TR(FIND))
132 snmp_debug("find: no match");
133 *errp = SNMP_SYNTAX_NOSUCHOBJECT;
134 return (NULL);
135
136 found:
137 /* leafs must have a 0 instance identifier */
138 if (tp->type == SNMP_NODE_LEAF &&
139 (value->var.len != tp->oid.len + 1 ||
140 value->var.subs[tp->oid.len] != 0)) {
141 if (TR(FIND))
142 snmp_debug("find: bad leaf index");
143 *errp = SNMP_SYNTAX_NOSUCHINSTANCE;
144 return (NULL);
145 }
146 if (TR(FIND))
147 snmp_debug("find: found %s",
148 asn_oid2str_r(&value->var, oidbuf));
149 return (tp);
150}
151
152static struct snmp_node *
153find_subnode(const struct snmp_value *value)
154{
155 struct snmp_node *tp;
156
157 for (tp = tree; tp < tree + tree_size; tp++) {
158 if (asn_is_suboid(&value->var, &tp->oid))
159 return (tp);
160 }
161 return (NULL);
162}
163
164/*
165 * Execute a GET operation. The tree is rooted at the global 'root'.
166 * Build the response PDU on the fly. If the return code is SNMP_RET_ERR
167 * the pdu error status and index will be set.
168 */
169enum snmp_ret
170snmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b,
171 struct snmp_pdu *resp, void *data)
172{
173 int ret;
174 u_int i;
175 struct snmp_node *tp;
176 enum snmp_syntax except;
177 struct context context;
178 enum asn_err err;
179
180 memset(&context, 0, sizeof(context));
181 context.ctx.data = data;
182
183 memset(resp, 0, sizeof(*resp));
184 strcpy(resp->community, pdu->community);
185 resp->version = pdu->version;
186 resp->type = SNMP_PDU_RESPONSE;
187 resp->request_id = pdu->request_id;
188 resp->version = pdu->version;
189
190 if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
191 /* cannot even encode header - very bad */
192 return (SNMP_RET_IGN);
193
194 for (i = 0; i < pdu->nbindings; i++) {
195 resp->bindings[i].var = pdu->bindings[i].var;
196 if ((tp = find_node(&pdu->bindings[i], &except)) == NULL) {
197 if (pdu->version == SNMP_V1) {
198 if (TR(GET))
199 snmp_debug("get: nosuchname");
200 pdu->error_status = SNMP_ERR_NOSUCHNAME;
201 pdu->error_index = i + 1;
202 snmp_pdu_free(resp);
203 return (SNMP_RET_ERR);
204 }
205 if (TR(GET))
206 snmp_debug("get: exception %u", except);
207 resp->bindings[i].syntax = except;
208
209 } else {
210 /* call the action to fetch the value. */
211 resp->bindings[i].syntax = tp->syntax;
212 ret = (*tp->op)(&context.ctx, &resp->bindings[i],
213 tp->oid.len, tp->index, SNMP_OP_GET);
214 if (TR(GET))
215 snmp_debug("get: action returns %d", ret);
216
217 if (ret == SNMP_ERR_NOSUCHNAME) {
218 if (pdu->version == SNMP_V1) {
219 pdu->error_status = SNMP_ERR_NOSUCHNAME;
220 pdu->error_index = i + 1;
221 snmp_pdu_free(resp);
222 return (SNMP_RET_ERR);
223 }
224 if (TR(GET))
225 snmp_debug("get: exception noSuchInstance");
226 resp->bindings[i].syntax = SNMP_SYNTAX_NOSUCHINSTANCE;
227
228 } else if (ret != SNMP_ERR_NOERROR) {
229 pdu->error_status = SNMP_ERR_GENERR;
230 pdu->error_index = i + 1;
231 snmp_pdu_free(resp);
232 return (SNMP_RET_ERR);
233 }
234 }
235 resp->nbindings++;
236
237 err = snmp_binding_encode(resp_b, &resp->bindings[i]);
238
239 if (err == ASN_ERR_EOBUF) {
240 pdu->error_status = SNMP_ERR_TOOBIG;
241 pdu->error_index = 0;
242 snmp_pdu_free(resp);
243 return (SNMP_RET_ERR);
244 }
245 if (err != ASN_ERR_OK) {
246 if (TR(GET))
247 snmp_debug("get: binding encoding: %u", err);
248 pdu->error_status = SNMP_ERR_GENERR;
249 pdu->error_index = i + 1;
250 snmp_pdu_free(resp);
251 return (SNMP_RET_ERR);
252 }
253 }
254
255 return (snmp_fix_encoding(resp_b, resp));
256}
257
258static struct snmp_node *
259next_node(const struct snmp_value *value, int *pnext)
260{
261 struct snmp_node *tp;
262
263 if (TR(FIND))
264 snmp_debug("next: searching %s",
265 asn_oid2str_r(&value->var, oidbuf));
266
267 *pnext = 0;
268 for (tp = tree; tp < tree + tree_size; tp++) {
269 if (asn_is_suboid(&tp->oid, &value->var)) {
270 /* the tree OID is a sub-oid of the requested OID. */
271 if (tp->type == SNMP_NODE_LEAF) {
272 if (tp->oid.len == value->var.len) {
273 /* request for scalar type */
274 if (TR(FIND))
275 snmp_debug("next: found scalar %s",
276 asn_oid2str_r(&tp->oid, oidbuf));
277 return (tp);
278 }
279 /* try next */
280 } else {
281 if (TR(FIND))
282 snmp_debug("next: found column %s",
283 asn_oid2str_r(&tp->oid, oidbuf));
284 return (tp);
285 }
286 } else if (asn_is_suboid(&value->var, &tp->oid) ||
287 asn_compare_oid(&tp->oid, &value->var) >= 0) {
288 if (TR(FIND))
289 snmp_debug("next: found %s",
290 asn_oid2str_r(&tp->oid, oidbuf));
291 *pnext = 1;
292 return (tp);
293 }
294 }
295
296 if (TR(FIND))
297 snmp_debug("next: failed");
298
299 return (NULL);
300}
301
302static enum snmp_ret
303do_getnext(struct context *context, const struct snmp_value *inb,
304 struct snmp_value *outb, struct snmp_pdu *pdu)
305{
306 const struct snmp_node *tp;
307 int ret, next;
308
309 if ((tp = next_node(inb, &next)) == NULL)
310 goto eofMib;
311
312 /* retain old variable if we are doing a GETNEXT on an exact
313 * matched leaf only */
314 if (tp->type == SNMP_NODE_LEAF || next)
315 outb->var = tp->oid;
316 else
317 outb->var = inb->var;
318
319 for (;;) {
320 outb->syntax = tp->syntax;
321 if (tp->type == SNMP_NODE_LEAF) {
322 /* make a GET operation */
323 outb->var.subs[outb->var.len++] = 0;
324 ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
325 tp->index, SNMP_OP_GET);
326 } else {
327 /* make a GETNEXT */
328 ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
329 tp->index, SNMP_OP_GETNEXT);
330 }
331 if (ret != SNMP_ERR_NOSUCHNAME) {
332 /* got something */
333 if (ret != SNMP_ERR_NOERROR && TR(GETNEXT))
334 snmp_debug("getnext: %s returns %u",
335 asn_oid2str(&outb->var), ret);
336 break;
337 }
338
339 /* object has no data - try next */
340 if (++tp == tree + tree_size)
341 break;
30 *
31 * SNMP Agent functions
32 */
33#include <sys/types.h>
34#include <sys/queue.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <stddef.h>
38#include <stdarg.h>
39#include <stdint.h>
40#include <string.h>
41
42#include "asn1.h"
43#include "snmp.h"
44#include "snmppriv.h"
45#include "snmpagent.h"
46
47static void snmp_debug_func(const char *fmt, ...);
48
49void (*snmp_debug)(const char *fmt, ...) = snmp_debug_func;
50
51struct snmp_node *tree;
52u_int tree_size;
53
54/*
55 * Structure to hold dependencies during SET processing
56 * The last two members of this structure must be the
57 * dependency visible by the user and the user data.
58 */
59struct depend {
60 TAILQ_ENTRY(depend) link;
61 size_t len; /* size of data part */
62 snmp_depop_t func;
63 struct snmp_dependency dep;
64#if defined(__GNUC__) && __GNUC__ < 3
65 u_char data[0];
66#else
67 u_char data[];
68#endif
69};
70TAILQ_HEAD(depend_list, depend);
71
72/*
73 * Set context
74 */
75struct context {
76 struct snmp_context ctx;
77 struct depend_list dlist;
78 const struct snmp_node *node[SNMP_MAX_BINDINGS];
79 struct snmp_scratch scratch[SNMP_MAX_BINDINGS];
80 struct depend *depend;
81};
82
83#define TR(W) (snmp_trace & SNMP_TRACE_##W)
84u_int snmp_trace = 0;
85
86static char oidbuf[ASN_OIDSTRLEN];
87
88/*
89 * Allocate a context
90 */
91struct snmp_context *
92snmp_init_context(void)
93{
94 struct context *context;
95
96 if ((context = malloc(sizeof(*context))) == NULL)
97 return (NULL);
98
99 memset(context, 0, sizeof(*context));
100 TAILQ_INIT(&context->dlist);
101
102 return (&context->ctx);
103}
104
105/*
106 * Find a variable for SET/GET and the first GETBULK pass.
107 * Return the node pointer. If the search fails, set the errp to
108 * the correct SNMPv2 GET exception code.
109 */
110static struct snmp_node *
111find_node(const struct snmp_value *value, enum snmp_syntax *errp)
112{
113 struct snmp_node *tp;
114
115 if (TR(FIND))
116 snmp_debug("find: searching %s",
117 asn_oid2str_r(&value->var, oidbuf));
118
119 /*
120 * If we have an exact match (the entry in the table is a
121 * sub-oid from the variable) we have found what we are for.
122 * If the table oid is higher than the variable, there is no match.
123 */
124 for (tp = tree; tp < tree + tree_size; tp++) {
125 if (asn_is_suboid(&tp->oid, &value->var))
126 goto found;
127 if (asn_compare_oid(&tp->oid, &value->var) >= 0)
128 break;
129 }
130
131 if (TR(FIND))
132 snmp_debug("find: no match");
133 *errp = SNMP_SYNTAX_NOSUCHOBJECT;
134 return (NULL);
135
136 found:
137 /* leafs must have a 0 instance identifier */
138 if (tp->type == SNMP_NODE_LEAF &&
139 (value->var.len != tp->oid.len + 1 ||
140 value->var.subs[tp->oid.len] != 0)) {
141 if (TR(FIND))
142 snmp_debug("find: bad leaf index");
143 *errp = SNMP_SYNTAX_NOSUCHINSTANCE;
144 return (NULL);
145 }
146 if (TR(FIND))
147 snmp_debug("find: found %s",
148 asn_oid2str_r(&value->var, oidbuf));
149 return (tp);
150}
151
152static struct snmp_node *
153find_subnode(const struct snmp_value *value)
154{
155 struct snmp_node *tp;
156
157 for (tp = tree; tp < tree + tree_size; tp++) {
158 if (asn_is_suboid(&value->var, &tp->oid))
159 return (tp);
160 }
161 return (NULL);
162}
163
164/*
165 * Execute a GET operation. The tree is rooted at the global 'root'.
166 * Build the response PDU on the fly. If the return code is SNMP_RET_ERR
167 * the pdu error status and index will be set.
168 */
169enum snmp_ret
170snmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b,
171 struct snmp_pdu *resp, void *data)
172{
173 int ret;
174 u_int i;
175 struct snmp_node *tp;
176 enum snmp_syntax except;
177 struct context context;
178 enum asn_err err;
179
180 memset(&context, 0, sizeof(context));
181 context.ctx.data = data;
182
183 memset(resp, 0, sizeof(*resp));
184 strcpy(resp->community, pdu->community);
185 resp->version = pdu->version;
186 resp->type = SNMP_PDU_RESPONSE;
187 resp->request_id = pdu->request_id;
188 resp->version = pdu->version;
189
190 if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
191 /* cannot even encode header - very bad */
192 return (SNMP_RET_IGN);
193
194 for (i = 0; i < pdu->nbindings; i++) {
195 resp->bindings[i].var = pdu->bindings[i].var;
196 if ((tp = find_node(&pdu->bindings[i], &except)) == NULL) {
197 if (pdu->version == SNMP_V1) {
198 if (TR(GET))
199 snmp_debug("get: nosuchname");
200 pdu->error_status = SNMP_ERR_NOSUCHNAME;
201 pdu->error_index = i + 1;
202 snmp_pdu_free(resp);
203 return (SNMP_RET_ERR);
204 }
205 if (TR(GET))
206 snmp_debug("get: exception %u", except);
207 resp->bindings[i].syntax = except;
208
209 } else {
210 /* call the action to fetch the value. */
211 resp->bindings[i].syntax = tp->syntax;
212 ret = (*tp->op)(&context.ctx, &resp->bindings[i],
213 tp->oid.len, tp->index, SNMP_OP_GET);
214 if (TR(GET))
215 snmp_debug("get: action returns %d", ret);
216
217 if (ret == SNMP_ERR_NOSUCHNAME) {
218 if (pdu->version == SNMP_V1) {
219 pdu->error_status = SNMP_ERR_NOSUCHNAME;
220 pdu->error_index = i + 1;
221 snmp_pdu_free(resp);
222 return (SNMP_RET_ERR);
223 }
224 if (TR(GET))
225 snmp_debug("get: exception noSuchInstance");
226 resp->bindings[i].syntax = SNMP_SYNTAX_NOSUCHINSTANCE;
227
228 } else if (ret != SNMP_ERR_NOERROR) {
229 pdu->error_status = SNMP_ERR_GENERR;
230 pdu->error_index = i + 1;
231 snmp_pdu_free(resp);
232 return (SNMP_RET_ERR);
233 }
234 }
235 resp->nbindings++;
236
237 err = snmp_binding_encode(resp_b, &resp->bindings[i]);
238
239 if (err == ASN_ERR_EOBUF) {
240 pdu->error_status = SNMP_ERR_TOOBIG;
241 pdu->error_index = 0;
242 snmp_pdu_free(resp);
243 return (SNMP_RET_ERR);
244 }
245 if (err != ASN_ERR_OK) {
246 if (TR(GET))
247 snmp_debug("get: binding encoding: %u", err);
248 pdu->error_status = SNMP_ERR_GENERR;
249 pdu->error_index = i + 1;
250 snmp_pdu_free(resp);
251 return (SNMP_RET_ERR);
252 }
253 }
254
255 return (snmp_fix_encoding(resp_b, resp));
256}
257
258static struct snmp_node *
259next_node(const struct snmp_value *value, int *pnext)
260{
261 struct snmp_node *tp;
262
263 if (TR(FIND))
264 snmp_debug("next: searching %s",
265 asn_oid2str_r(&value->var, oidbuf));
266
267 *pnext = 0;
268 for (tp = tree; tp < tree + tree_size; tp++) {
269 if (asn_is_suboid(&tp->oid, &value->var)) {
270 /* the tree OID is a sub-oid of the requested OID. */
271 if (tp->type == SNMP_NODE_LEAF) {
272 if (tp->oid.len == value->var.len) {
273 /* request for scalar type */
274 if (TR(FIND))
275 snmp_debug("next: found scalar %s",
276 asn_oid2str_r(&tp->oid, oidbuf));
277 return (tp);
278 }
279 /* try next */
280 } else {
281 if (TR(FIND))
282 snmp_debug("next: found column %s",
283 asn_oid2str_r(&tp->oid, oidbuf));
284 return (tp);
285 }
286 } else if (asn_is_suboid(&value->var, &tp->oid) ||
287 asn_compare_oid(&tp->oid, &value->var) >= 0) {
288 if (TR(FIND))
289 snmp_debug("next: found %s",
290 asn_oid2str_r(&tp->oid, oidbuf));
291 *pnext = 1;
292 return (tp);
293 }
294 }
295
296 if (TR(FIND))
297 snmp_debug("next: failed");
298
299 return (NULL);
300}
301
302static enum snmp_ret
303do_getnext(struct context *context, const struct snmp_value *inb,
304 struct snmp_value *outb, struct snmp_pdu *pdu)
305{
306 const struct snmp_node *tp;
307 int ret, next;
308
309 if ((tp = next_node(inb, &next)) == NULL)
310 goto eofMib;
311
312 /* retain old variable if we are doing a GETNEXT on an exact
313 * matched leaf only */
314 if (tp->type == SNMP_NODE_LEAF || next)
315 outb->var = tp->oid;
316 else
317 outb->var = inb->var;
318
319 for (;;) {
320 outb->syntax = tp->syntax;
321 if (tp->type == SNMP_NODE_LEAF) {
322 /* make a GET operation */
323 outb->var.subs[outb->var.len++] = 0;
324 ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
325 tp->index, SNMP_OP_GET);
326 } else {
327 /* make a GETNEXT */
328 ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
329 tp->index, SNMP_OP_GETNEXT);
330 }
331 if (ret != SNMP_ERR_NOSUCHNAME) {
332 /* got something */
333 if (ret != SNMP_ERR_NOERROR && TR(GETNEXT))
334 snmp_debug("getnext: %s returns %u",
335 asn_oid2str(&outb->var), ret);
336 break;
337 }
338
339 /* object has no data - try next */
340 if (++tp == tree + tree_size)
341 break;
342
343 if (TR(GETNEXT))
344 snmp_debug("getnext: no data - avancing to %s",
345 asn_oid2str(&tp->oid));
346
342 outb->var = tp->oid;
343 }
344
345 if (ret == SNMP_ERR_NOSUCHNAME) {
346 eofMib:
347 outb->var = inb->var;
348 if (pdu->version == SNMP_V1) {
349 pdu->error_status = SNMP_ERR_NOSUCHNAME;
350 return (SNMP_RET_ERR);
351 }
352 outb->syntax = SNMP_SYNTAX_ENDOFMIBVIEW;
353
354 } else if (ret != SNMP_ERR_NOERROR) {
355 pdu->error_status = SNMP_ERR_GENERR;
356 return (SNMP_RET_ERR);
357 }
358 return (SNMP_RET_OK);
359}
360
361
362/*
363 * Execute a GETNEXT operation. The tree is rooted at the global 'root'.
364 * Build the response PDU on the fly. The return is:
365 */
366enum snmp_ret
367snmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b,
368 struct snmp_pdu *resp, void *data)
369{
370 struct context context;
371 u_int i;
372 enum asn_err err;
373 enum snmp_ret result;
374
375 memset(&context, 0, sizeof(context));
376 context.ctx.data = data;
377
378 memset(resp, 0, sizeof(*resp));
379 strcpy(resp->community, pdu->community);
380 resp->type = SNMP_PDU_RESPONSE;
381 resp->request_id = pdu->request_id;
382 resp->version = pdu->version;
383
384 if (snmp_pdu_encode_header(resp_b, resp))
385 return (SNMP_RET_IGN);
386
387 for (i = 0; i < pdu->nbindings; i++) {
388 result = do_getnext(&context, &pdu->bindings[i],
389 &resp->bindings[i], pdu);
390
391 if (result != SNMP_RET_OK) {
392 pdu->error_index = i + 1;
393 snmp_pdu_free(resp);
394 return (result);
395 }
396
397 resp->nbindings++;
398
399 err = snmp_binding_encode(resp_b, &resp->bindings[i]);
400
401 if (err == ASN_ERR_EOBUF) {
402 pdu->error_status = SNMP_ERR_TOOBIG;
403 pdu->error_index = 0;
404 snmp_pdu_free(resp);
405 return (SNMP_RET_ERR);
406 }
407 if (err != ASN_ERR_OK) {
408 if (TR(GET))
409 snmp_debug("getnext: binding encoding: %u", err);
410 pdu->error_status = SNMP_ERR_GENERR;
411 pdu->error_index = i + 1;
412 snmp_pdu_free(resp);
413 return (SNMP_RET_ERR);
414 }
415 }
416 return (snmp_fix_encoding(resp_b, resp));
417}
418
419enum snmp_ret
420snmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b,
421 struct snmp_pdu *resp, void *data)
422{
423 struct context context;
424 u_int i;
425 int cnt;
426 u_int non_rep;
427 int eomib;
428 enum snmp_ret result;
429 enum asn_err err;
430
431 memset(&context, 0, sizeof(context));
432 context.ctx.data = data;
433
434 memset(resp, 0, sizeof(*resp));
435 strcpy(resp->community, pdu->community);
436 resp->version = pdu->version;
437 resp->type = SNMP_PDU_RESPONSE;
438 resp->request_id = pdu->request_id;
439 resp->version = pdu->version;
440
441 if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
442 /* cannot even encode header - very bad */
443 return (SNMP_RET_IGN);
444
445 if ((non_rep = pdu->error_status) > pdu->nbindings)
446 non_rep = pdu->nbindings;
447
448 /* non-repeaters */
449 for (i = 0; i < non_rep; i++) {
450 result = do_getnext(&context, &pdu->bindings[i],
451 &resp->bindings[resp->nbindings], pdu);
452
453 if (result != SNMP_RET_OK) {
454 pdu->error_index = i + 1;
455 snmp_pdu_free(resp);
456 return (result);
457 }
458
459 err = snmp_binding_encode(resp_b,
460 &resp->bindings[resp->nbindings++]);
461
462 if (err == ASN_ERR_EOBUF)
463 goto done;
464
465 if (err != ASN_ERR_OK) {
466 if (TR(GET))
467 snmp_debug("getnext: binding encoding: %u", err);
468 pdu->error_status = SNMP_ERR_GENERR;
469 pdu->error_index = i + 1;
470 snmp_pdu_free(resp);
471 return (SNMP_RET_ERR);
472 }
473 }
474
475 if (non_rep == pdu->nbindings)
476 goto done;
477
478 /* repeates */
479 for (cnt = 0; cnt < pdu->error_index; cnt++) {
480 eomib = 1;
481 for (i = non_rep; i < pdu->nbindings; i++) {
482 if (cnt == 0)
483 result = do_getnext(&context, &pdu->bindings[i],
484 &resp->bindings[resp->nbindings], pdu);
485 else
486 result = do_getnext(&context,
487 &resp->bindings[resp->nbindings -
488 (pdu->nbindings - non_rep)],
489 &resp->bindings[resp->nbindings], pdu);
490
491 if (result != SNMP_RET_OK) {
492 pdu->error_index = i + 1;
493 snmp_pdu_free(resp);
494 return (result);
495 }
496 if (resp->bindings[resp->nbindings].syntax !=
497 SNMP_SYNTAX_ENDOFMIBVIEW)
498 eomib = 0;
499
500 err = snmp_binding_encode(resp_b,
501 &resp->bindings[resp->nbindings++]);
502
503 if (err == ASN_ERR_EOBUF)
504 goto done;
505
506 if (err != ASN_ERR_OK) {
507 if (TR(GET))
508 snmp_debug("getnext: binding encoding: %u", err);
509 pdu->error_status = SNMP_ERR_GENERR;
510 pdu->error_index = i + 1;
511 snmp_pdu_free(resp);
512 return (SNMP_RET_ERR);
513 }
514 }
515 if (eomib)
516 break;
517 }
518
519 done:
520 return (snmp_fix_encoding(resp_b, resp));
521}
522
523/*
524 * Rollback a SET operation. Failed index is 'i'.
525 */
526static void
527rollback(struct context *context, struct snmp_pdu *pdu, u_int i)
528{
529 struct snmp_value *b;
530 const struct snmp_node *np;
531 int ret;
532
533 while (i-- > 0) {
534 b = &pdu->bindings[i];
535 np = context->node[i];
536
537 context->ctx.scratch = &context->scratch[i];
538
539 ret = (*np->op)(&context->ctx, b, np->oid.len, np->index,
540 SNMP_OP_ROLLBACK);
541
542 if (ret != SNMP_ERR_NOERROR) {
543 snmp_error("set: rollback failed (%d) on variable %s "
544 "index %u", ret, asn_oid2str(&b->var), i);
545 if (pdu->version != SNMP_V1) {
546 pdu->error_status = SNMP_ERR_UNDO_FAILED;
547 pdu->error_index = 0;
548 }
549 }
550 }
551}
552
553/*
554 * Commit dependencies.
555 */
556int
557snmp_dep_commit(struct snmp_context *ctx)
558{
559 struct context *context = (struct context *)ctx;
560 int ret;
561
562 TAILQ_FOREACH(context->depend, &context->dlist, link) {
563 ctx->dep = &context->depend->dep;
564
565 if (TR(SET))
566 snmp_debug("set: dependency commit %s",
567 asn_oid2str(&ctx->dep->obj));
568
569 ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT);
570
571 if (ret != SNMP_ERR_NOERROR) {
572 if (TR(SET))
573 snmp_debug("set: dependency failed %d", ret);
574 return (ret);
575 }
576 }
577 return (SNMP_ERR_NOERROR);
578}
579
580/*
581 * Rollback dependencies
582 */
583int
584snmp_dep_rollback(struct snmp_context *ctx)
585{
586 struct context *context = (struct context *)ctx;
587 int ret, ret1;
588 char objbuf[ASN_OIDSTRLEN];
589 char idxbuf[ASN_OIDSTRLEN];
590
591 ret1 = SNMP_ERR_NOERROR;
592 while ((context->depend =
593 TAILQ_PREV(context->depend, depend_list, link)) != NULL) {
594 ctx->dep = &context->depend->dep;
595
596 if (TR(SET))
597 snmp_debug("set: dependency rollback %s",
598 asn_oid2str(&ctx->dep->obj));
599
600 ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK);
601
602 if (ret != SNMP_ERR_NOERROR) {
603 snmp_debug("set: dep rollback returns %u: %s %s", ret,
604 asn_oid2str_r(&ctx->dep->obj, objbuf),
605 asn_oid2str_r(&ctx->dep->idx, idxbuf));
606 if (ret1 == SNMP_ERR_NOERROR)
607 ret1 = ret;
608 }
609 }
610 return (ret1);
611}
612
613void
614snmp_dep_finish(struct snmp_context *ctx)
615{
616 struct context *context = (struct context *)ctx;
617 struct depend *d;
618
619 while ((d = TAILQ_FIRST(&context->dlist)) != NULL) {
620 ctx->dep = &d->dep;
621 (void)d->func(ctx, ctx->dep, SNMP_DEPOP_FINISH);
622 TAILQ_REMOVE(&context->dlist, d, link);
623 free(d);
624 }
625}
626
627/*
628 * Do a SET operation.
629 */
630enum snmp_ret
631snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b,
632 struct snmp_pdu *resp, void *data)
633{
634 int ret;
635 u_int i;
636 enum asn_err asnerr;
637 struct context context;
638 const struct snmp_node *np;
639 struct snmp_value *b;
640 enum snmp_syntax except;
641
642 memset(&context, 0, sizeof(context));
643 TAILQ_INIT(&context.dlist);
644 context.ctx.data = data;
645
646 memset(resp, 0, sizeof(*resp));
647 strcpy(resp->community, pdu->community);
648 resp->type = SNMP_PDU_RESPONSE;
649 resp->request_id = pdu->request_id;
650 resp->version = pdu->version;
651
652 if (snmp_pdu_encode_header(resp_b, resp))
653 return (SNMP_RET_IGN);
654
655 /*
656 * 1. Find all nodes, check that they are writeable and
657 * that the syntax is ok, copy over the binding to the response.
658 */
659 for (i = 0; i < pdu->nbindings; i++) {
660 b = &pdu->bindings[i];
661
662 if ((np = context.node[i] = find_node(b, &except)) == NULL) {
663 /* not found altogether or LEAF with wrong index */
664 if (TR(SET))
665 snmp_debug("set: node not found %s",
666 asn_oid2str_r(&b->var, oidbuf));
667 if (pdu->version == SNMP_V1) {
668 pdu->error_index = i + 1;
669 pdu->error_status = SNMP_ERR_NOSUCHNAME;
670 } else if ((np = find_subnode(b)) != NULL) {
671 /* 2. intermediate object */
672 pdu->error_index = i + 1;
673 pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
674 } else if (except == SNMP_SYNTAX_NOSUCHOBJECT) {
675 pdu->error_index = i + 1;
676 pdu->error_status = SNMP_ERR_NO_ACCESS;
677 } else {
678 pdu->error_index = i + 1;
679 pdu->error_status = SNMP_ERR_NO_CREATION;
680 }
681 snmp_pdu_free(resp);
682 return (SNMP_RET_ERR);
683 }
684 /*
685 * 2. write/createable?
686 * Can check this for leafs only, because in v2 we have
687 * to differentiate between NOT_WRITEABLE and NO_CREATION
688 * and only the action routine for COLUMNS knows, whether
689 * a column exists.
690 */
691 if (np->type == SNMP_NODE_LEAF &&
692 !(np->flags & SNMP_NODE_CANSET)) {
693 if (pdu->version == SNMP_V1) {
694 pdu->error_index = i + 1;
695 pdu->error_status = SNMP_ERR_NOSUCHNAME;
696 } else {
697 pdu->error_index = i + 1;
698 pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
699 }
700 snmp_pdu_free(resp);
701 return (SNMP_RET_ERR);
702 }
703 /*
704 * 3. Ensure the right syntax
705 */
706 if (np->syntax != b->syntax) {
707 if (pdu->version == SNMP_V1) {
708 pdu->error_index = i + 1;
709 pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */
710 } else {
711 pdu->error_index = i + 1;
712 pdu->error_status = SNMP_ERR_WRONG_TYPE;
713 }
714 snmp_pdu_free(resp);
715 return (SNMP_RET_ERR);
716 }
717 /*
718 * 4. Copy binding
719 */
720 if (snmp_value_copy(&resp->bindings[i], b)) {
721 pdu->error_index = i + 1;
722 pdu->error_status = SNMP_ERR_GENERR;
723 snmp_pdu_free(resp);
724 return (SNMP_RET_ERR);
725 }
726 asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]);
727 if (asnerr == ASN_ERR_EOBUF) {
728 pdu->error_index = i + 1;
729 pdu->error_status = SNMP_ERR_TOOBIG;
730 snmp_pdu_free(resp);
731 return (SNMP_RET_ERR);
732 } else if (asnerr != ASN_ERR_OK) {
733 pdu->error_index = i + 1;
734 pdu->error_status = SNMP_ERR_GENERR;
735 snmp_pdu_free(resp);
736 return (SNMP_RET_ERR);
737 }
738 resp->nbindings++;
739 }
740
741 context.ctx.code = SNMP_RET_OK;
742
743 /*
744 * 2. Call the SET method for each node. If a SET fails, rollback
745 * everything. Map error codes depending on the version.
746 */
747 for (i = 0; i < pdu->nbindings; i++) {
748 b = &pdu->bindings[i];
749 np = context.node[i];
750
751 context.ctx.var_index = i + 1;
752 context.ctx.scratch = &context.scratch[i];
753
754 ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
755 SNMP_OP_SET);
756
757 if (TR(SET))
758 snmp_debug("set: action %s returns %d", np->name, ret);
759
760 if (pdu->version == SNMP_V1) {
761 switch (ret) {
762 case SNMP_ERR_NO_ACCESS:
763 ret = SNMP_ERR_NOSUCHNAME;
764 break;
765 case SNMP_ERR_WRONG_TYPE:
766 /* should no happen */
767 ret = SNMP_ERR_BADVALUE;
768 break;
769 case SNMP_ERR_WRONG_LENGTH:
770 ret = SNMP_ERR_BADVALUE;
771 break;
772 case SNMP_ERR_WRONG_ENCODING:
773 /* should not happen */
774 ret = SNMP_ERR_BADVALUE;
775 break;
776 case SNMP_ERR_WRONG_VALUE:
777 ret = SNMP_ERR_BADVALUE;
778 break;
779 case SNMP_ERR_NO_CREATION:
780 ret = SNMP_ERR_NOSUCHNAME;
781 break;
782 case SNMP_ERR_INCONS_VALUE:
783 ret = SNMP_ERR_BADVALUE;
784 break;
785 case SNMP_ERR_RES_UNAVAIL:
786 ret = SNMP_ERR_GENERR;
787 break;
788 case SNMP_ERR_COMMIT_FAILED:
789 ret = SNMP_ERR_GENERR;
790 break;
791 case SNMP_ERR_UNDO_FAILED:
792 ret = SNMP_ERR_GENERR;
793 break;
794 case SNMP_ERR_AUTH_ERR:
795 /* should not happen */
796 ret = SNMP_ERR_GENERR;
797 break;
798 case SNMP_ERR_NOT_WRITEABLE:
799 ret = SNMP_ERR_NOSUCHNAME;
800 break;
801 case SNMP_ERR_INCONS_NAME:
802 ret = SNMP_ERR_BADVALUE;
803 break;
804 }
805 }
806 if (ret != SNMP_ERR_NOERROR) {
807 pdu->error_index = i + 1;
808 pdu->error_status = ret;
809
810 rollback(&context, pdu, i);
811 snmp_pdu_free(resp);
812
813 context.ctx.code = SNMP_RET_ERR;
814
815 goto errout;
816 }
817 }
818
819 /*
820 * 3. Call dependencies
821 */
822 if (TR(SET))
823 snmp_debug("set: set operations ok");
824
825 if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) {
826 pdu->error_status = ret;
827 pdu->error_index = context.ctx.var_index;
828
829 if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) {
830 if (pdu->version != SNMP_V1) {
831 pdu->error_status = SNMP_ERR_UNDO_FAILED;
832 pdu->error_index = 0;
833 }
834 }
835 rollback(&context, pdu, i);
836 snmp_pdu_free(resp);
837
838 context.ctx.code = SNMP_RET_ERR;
839
840 goto errout;
841 }
842
843 /*
844 * 4. Commit and copy values from the original packet to the response.
845 * This is not the commit operation from RFC 1905 but rather an
846 * 'FREE RESOURCES' operation. It shouldn't fail.
847 */
848 if (TR(SET))
849 snmp_debug("set: commiting");
850
851 for (i = 0; i < pdu->nbindings; i++) {
852 b = &resp->bindings[i];
853 np = context.node[i];
854
855 context.ctx.var_index = i + 1;
856 context.ctx.scratch = &context.scratch[i];
857
858 ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
859 SNMP_OP_COMMIT);
860
861 if (ret != SNMP_ERR_NOERROR)
862 snmp_error("set: commit failed (%d) on"
863 " variable %s index %u", ret,
864 asn_oid2str_r(&b->var, oidbuf), i);
865 }
866
867 if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
868 snmp_error("set: fix_encoding failed");
869 snmp_pdu_free(resp);
870 context.ctx.code = SNMP_RET_IGN;
871 }
872
873 /*
874 * Done
875 */
876 errout:
877 snmp_dep_finish(&context.ctx);
878
879 if (TR(SET))
880 snmp_debug("set: returning %d", context.ctx.code);
881
882 return (context.ctx.code);
883}
884/*
885 * Lookup a dependency. If it doesn't exist, create one
886 */
887struct snmp_dependency *
888snmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj,
889 const struct asn_oid *idx, size_t len, snmp_depop_t func)
890{
891 struct context *context;
892 struct depend *d;
893
894 context = (struct context *)(void *)
895 ((char *)ctx - offsetof(struct context, ctx));
896 if (TR(DEPEND)) {
897 snmp_debug("depend: looking for %s", asn_oid2str(obj));
898 if (idx)
899 snmp_debug("depend: index is %s", asn_oid2str(idx));
900 }
901 TAILQ_FOREACH(d, &context->dlist, link)
902 if (asn_compare_oid(obj, &d->dep.obj) == 0 &&
903 ((idx == NULL && d->dep.idx.len == 0) ||
904 (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) {
905 if(TR(DEPEND))
906 snmp_debug("depend: found");
907 return (&d->dep);
908 }
909
910 if(TR(DEPEND))
911 snmp_debug("depend: creating");
912
913 if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL)
914 return (NULL);
915 memset(&d->dep, 0, len);
916
917 d->dep.obj = *obj;
918 if (idx == NULL)
919 d->dep.idx.len = 0;
920 else
921 d->dep.idx = *idx;
922 d->len = len;
923 d->func = func;
924
925 TAILQ_INSERT_TAIL(&context->dlist, d, link);
926
927 return (&d->dep);
928}
929
930/*
931 * Make an error response from a PDU. We do this without decoding the
932 * variable bindings. This means we can sent the junk back to a caller
933 * that has sent us junk in the first place.
934 */
935enum snmp_ret
936snmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b,
937 struct asn_buf *resp_b)
938{
939 asn_len_t len;
940 struct snmp_pdu resp;
941 enum asn_err err;
942 enum snmp_code code;
943
944 memset(&resp, 0, sizeof(resp));
945
946 /* Message sequence */
947 if (asn_get_sequence(pdu_b, &len) != ASN_ERR_OK)
948 return (SNMP_RET_IGN);
949 if (pdu_b->asn_len < len)
950 return (SNMP_RET_IGN);
951
952 err = snmp_parse_message_hdr(pdu_b, &resp, &len);
953 if (ASN_ERR_STOPPED(err))
954 return (SNMP_RET_IGN);
955 if (pdu_b->asn_len < len)
956 return (SNMP_RET_IGN);
957 pdu_b->asn_len = len;
958
959 err = snmp_parse_pdus_hdr(pdu_b, &resp, &len);
960 if (ASN_ERR_STOPPED(err))
961 return (SNMP_RET_IGN);
962 if (pdu_b->asn_len < len)
963 return (SNMP_RET_IGN);
964 pdu_b->asn_len = len;
965
966 /* now we have the bindings left - construct new message */
967 resp.error_status = pdu->error_status;
968 resp.error_index = pdu->error_index;
969 resp.type = SNMP_PDU_RESPONSE;
970
971 code = snmp_pdu_encode_header(resp_b, &resp);
972 if (code != SNMP_CODE_OK)
973 return (SNMP_RET_IGN);
974
975 if (pdu_b->asn_len > resp_b->asn_len)
976 /* too short */
977 return (SNMP_RET_IGN);
978 (void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len);
979 resp_b->asn_len -= pdu_b->asn_len;
980 resp_b->asn_ptr += pdu_b->asn_len;
981
982 code = snmp_fix_encoding(resp_b, &resp);
983 if (code != SNMP_CODE_OK)
984 return (SNMP_RET_IGN);
985
986 return (SNMP_RET_OK);
987}
988
989static void
990snmp_debug_func(const char *fmt, ...)
991{
992 va_list ap;
993
994 va_start(ap, fmt);
995 vfprintf(stderr, fmt, ap);
996 va_end(ap);
997 fprintf(stderr, "\n");
998}
347 outb->var = tp->oid;
348 }
349
350 if (ret == SNMP_ERR_NOSUCHNAME) {
351 eofMib:
352 outb->var = inb->var;
353 if (pdu->version == SNMP_V1) {
354 pdu->error_status = SNMP_ERR_NOSUCHNAME;
355 return (SNMP_RET_ERR);
356 }
357 outb->syntax = SNMP_SYNTAX_ENDOFMIBVIEW;
358
359 } else if (ret != SNMP_ERR_NOERROR) {
360 pdu->error_status = SNMP_ERR_GENERR;
361 return (SNMP_RET_ERR);
362 }
363 return (SNMP_RET_OK);
364}
365
366
367/*
368 * Execute a GETNEXT operation. The tree is rooted at the global 'root'.
369 * Build the response PDU on the fly. The return is:
370 */
371enum snmp_ret
372snmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b,
373 struct snmp_pdu *resp, void *data)
374{
375 struct context context;
376 u_int i;
377 enum asn_err err;
378 enum snmp_ret result;
379
380 memset(&context, 0, sizeof(context));
381 context.ctx.data = data;
382
383 memset(resp, 0, sizeof(*resp));
384 strcpy(resp->community, pdu->community);
385 resp->type = SNMP_PDU_RESPONSE;
386 resp->request_id = pdu->request_id;
387 resp->version = pdu->version;
388
389 if (snmp_pdu_encode_header(resp_b, resp))
390 return (SNMP_RET_IGN);
391
392 for (i = 0; i < pdu->nbindings; i++) {
393 result = do_getnext(&context, &pdu->bindings[i],
394 &resp->bindings[i], pdu);
395
396 if (result != SNMP_RET_OK) {
397 pdu->error_index = i + 1;
398 snmp_pdu_free(resp);
399 return (result);
400 }
401
402 resp->nbindings++;
403
404 err = snmp_binding_encode(resp_b, &resp->bindings[i]);
405
406 if (err == ASN_ERR_EOBUF) {
407 pdu->error_status = SNMP_ERR_TOOBIG;
408 pdu->error_index = 0;
409 snmp_pdu_free(resp);
410 return (SNMP_RET_ERR);
411 }
412 if (err != ASN_ERR_OK) {
413 if (TR(GET))
414 snmp_debug("getnext: binding encoding: %u", err);
415 pdu->error_status = SNMP_ERR_GENERR;
416 pdu->error_index = i + 1;
417 snmp_pdu_free(resp);
418 return (SNMP_RET_ERR);
419 }
420 }
421 return (snmp_fix_encoding(resp_b, resp));
422}
423
424enum snmp_ret
425snmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b,
426 struct snmp_pdu *resp, void *data)
427{
428 struct context context;
429 u_int i;
430 int cnt;
431 u_int non_rep;
432 int eomib;
433 enum snmp_ret result;
434 enum asn_err err;
435
436 memset(&context, 0, sizeof(context));
437 context.ctx.data = data;
438
439 memset(resp, 0, sizeof(*resp));
440 strcpy(resp->community, pdu->community);
441 resp->version = pdu->version;
442 resp->type = SNMP_PDU_RESPONSE;
443 resp->request_id = pdu->request_id;
444 resp->version = pdu->version;
445
446 if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
447 /* cannot even encode header - very bad */
448 return (SNMP_RET_IGN);
449
450 if ((non_rep = pdu->error_status) > pdu->nbindings)
451 non_rep = pdu->nbindings;
452
453 /* non-repeaters */
454 for (i = 0; i < non_rep; i++) {
455 result = do_getnext(&context, &pdu->bindings[i],
456 &resp->bindings[resp->nbindings], pdu);
457
458 if (result != SNMP_RET_OK) {
459 pdu->error_index = i + 1;
460 snmp_pdu_free(resp);
461 return (result);
462 }
463
464 err = snmp_binding_encode(resp_b,
465 &resp->bindings[resp->nbindings++]);
466
467 if (err == ASN_ERR_EOBUF)
468 goto done;
469
470 if (err != ASN_ERR_OK) {
471 if (TR(GET))
472 snmp_debug("getnext: binding encoding: %u", err);
473 pdu->error_status = SNMP_ERR_GENERR;
474 pdu->error_index = i + 1;
475 snmp_pdu_free(resp);
476 return (SNMP_RET_ERR);
477 }
478 }
479
480 if (non_rep == pdu->nbindings)
481 goto done;
482
483 /* repeates */
484 for (cnt = 0; cnt < pdu->error_index; cnt++) {
485 eomib = 1;
486 for (i = non_rep; i < pdu->nbindings; i++) {
487 if (cnt == 0)
488 result = do_getnext(&context, &pdu->bindings[i],
489 &resp->bindings[resp->nbindings], pdu);
490 else
491 result = do_getnext(&context,
492 &resp->bindings[resp->nbindings -
493 (pdu->nbindings - non_rep)],
494 &resp->bindings[resp->nbindings], pdu);
495
496 if (result != SNMP_RET_OK) {
497 pdu->error_index = i + 1;
498 snmp_pdu_free(resp);
499 return (result);
500 }
501 if (resp->bindings[resp->nbindings].syntax !=
502 SNMP_SYNTAX_ENDOFMIBVIEW)
503 eomib = 0;
504
505 err = snmp_binding_encode(resp_b,
506 &resp->bindings[resp->nbindings++]);
507
508 if (err == ASN_ERR_EOBUF)
509 goto done;
510
511 if (err != ASN_ERR_OK) {
512 if (TR(GET))
513 snmp_debug("getnext: binding encoding: %u", err);
514 pdu->error_status = SNMP_ERR_GENERR;
515 pdu->error_index = i + 1;
516 snmp_pdu_free(resp);
517 return (SNMP_RET_ERR);
518 }
519 }
520 if (eomib)
521 break;
522 }
523
524 done:
525 return (snmp_fix_encoding(resp_b, resp));
526}
527
528/*
529 * Rollback a SET operation. Failed index is 'i'.
530 */
531static void
532rollback(struct context *context, struct snmp_pdu *pdu, u_int i)
533{
534 struct snmp_value *b;
535 const struct snmp_node *np;
536 int ret;
537
538 while (i-- > 0) {
539 b = &pdu->bindings[i];
540 np = context->node[i];
541
542 context->ctx.scratch = &context->scratch[i];
543
544 ret = (*np->op)(&context->ctx, b, np->oid.len, np->index,
545 SNMP_OP_ROLLBACK);
546
547 if (ret != SNMP_ERR_NOERROR) {
548 snmp_error("set: rollback failed (%d) on variable %s "
549 "index %u", ret, asn_oid2str(&b->var), i);
550 if (pdu->version != SNMP_V1) {
551 pdu->error_status = SNMP_ERR_UNDO_FAILED;
552 pdu->error_index = 0;
553 }
554 }
555 }
556}
557
558/*
559 * Commit dependencies.
560 */
561int
562snmp_dep_commit(struct snmp_context *ctx)
563{
564 struct context *context = (struct context *)ctx;
565 int ret;
566
567 TAILQ_FOREACH(context->depend, &context->dlist, link) {
568 ctx->dep = &context->depend->dep;
569
570 if (TR(SET))
571 snmp_debug("set: dependency commit %s",
572 asn_oid2str(&ctx->dep->obj));
573
574 ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT);
575
576 if (ret != SNMP_ERR_NOERROR) {
577 if (TR(SET))
578 snmp_debug("set: dependency failed %d", ret);
579 return (ret);
580 }
581 }
582 return (SNMP_ERR_NOERROR);
583}
584
585/*
586 * Rollback dependencies
587 */
588int
589snmp_dep_rollback(struct snmp_context *ctx)
590{
591 struct context *context = (struct context *)ctx;
592 int ret, ret1;
593 char objbuf[ASN_OIDSTRLEN];
594 char idxbuf[ASN_OIDSTRLEN];
595
596 ret1 = SNMP_ERR_NOERROR;
597 while ((context->depend =
598 TAILQ_PREV(context->depend, depend_list, link)) != NULL) {
599 ctx->dep = &context->depend->dep;
600
601 if (TR(SET))
602 snmp_debug("set: dependency rollback %s",
603 asn_oid2str(&ctx->dep->obj));
604
605 ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK);
606
607 if (ret != SNMP_ERR_NOERROR) {
608 snmp_debug("set: dep rollback returns %u: %s %s", ret,
609 asn_oid2str_r(&ctx->dep->obj, objbuf),
610 asn_oid2str_r(&ctx->dep->idx, idxbuf));
611 if (ret1 == SNMP_ERR_NOERROR)
612 ret1 = ret;
613 }
614 }
615 return (ret1);
616}
617
618void
619snmp_dep_finish(struct snmp_context *ctx)
620{
621 struct context *context = (struct context *)ctx;
622 struct depend *d;
623
624 while ((d = TAILQ_FIRST(&context->dlist)) != NULL) {
625 ctx->dep = &d->dep;
626 (void)d->func(ctx, ctx->dep, SNMP_DEPOP_FINISH);
627 TAILQ_REMOVE(&context->dlist, d, link);
628 free(d);
629 }
630}
631
632/*
633 * Do a SET operation.
634 */
635enum snmp_ret
636snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b,
637 struct snmp_pdu *resp, void *data)
638{
639 int ret;
640 u_int i;
641 enum asn_err asnerr;
642 struct context context;
643 const struct snmp_node *np;
644 struct snmp_value *b;
645 enum snmp_syntax except;
646
647 memset(&context, 0, sizeof(context));
648 TAILQ_INIT(&context.dlist);
649 context.ctx.data = data;
650
651 memset(resp, 0, sizeof(*resp));
652 strcpy(resp->community, pdu->community);
653 resp->type = SNMP_PDU_RESPONSE;
654 resp->request_id = pdu->request_id;
655 resp->version = pdu->version;
656
657 if (snmp_pdu_encode_header(resp_b, resp))
658 return (SNMP_RET_IGN);
659
660 /*
661 * 1. Find all nodes, check that they are writeable and
662 * that the syntax is ok, copy over the binding to the response.
663 */
664 for (i = 0; i < pdu->nbindings; i++) {
665 b = &pdu->bindings[i];
666
667 if ((np = context.node[i] = find_node(b, &except)) == NULL) {
668 /* not found altogether or LEAF with wrong index */
669 if (TR(SET))
670 snmp_debug("set: node not found %s",
671 asn_oid2str_r(&b->var, oidbuf));
672 if (pdu->version == SNMP_V1) {
673 pdu->error_index = i + 1;
674 pdu->error_status = SNMP_ERR_NOSUCHNAME;
675 } else if ((np = find_subnode(b)) != NULL) {
676 /* 2. intermediate object */
677 pdu->error_index = i + 1;
678 pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
679 } else if (except == SNMP_SYNTAX_NOSUCHOBJECT) {
680 pdu->error_index = i + 1;
681 pdu->error_status = SNMP_ERR_NO_ACCESS;
682 } else {
683 pdu->error_index = i + 1;
684 pdu->error_status = SNMP_ERR_NO_CREATION;
685 }
686 snmp_pdu_free(resp);
687 return (SNMP_RET_ERR);
688 }
689 /*
690 * 2. write/createable?
691 * Can check this for leafs only, because in v2 we have
692 * to differentiate between NOT_WRITEABLE and NO_CREATION
693 * and only the action routine for COLUMNS knows, whether
694 * a column exists.
695 */
696 if (np->type == SNMP_NODE_LEAF &&
697 !(np->flags & SNMP_NODE_CANSET)) {
698 if (pdu->version == SNMP_V1) {
699 pdu->error_index = i + 1;
700 pdu->error_status = SNMP_ERR_NOSUCHNAME;
701 } else {
702 pdu->error_index = i + 1;
703 pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
704 }
705 snmp_pdu_free(resp);
706 return (SNMP_RET_ERR);
707 }
708 /*
709 * 3. Ensure the right syntax
710 */
711 if (np->syntax != b->syntax) {
712 if (pdu->version == SNMP_V1) {
713 pdu->error_index = i + 1;
714 pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */
715 } else {
716 pdu->error_index = i + 1;
717 pdu->error_status = SNMP_ERR_WRONG_TYPE;
718 }
719 snmp_pdu_free(resp);
720 return (SNMP_RET_ERR);
721 }
722 /*
723 * 4. Copy binding
724 */
725 if (snmp_value_copy(&resp->bindings[i], b)) {
726 pdu->error_index = i + 1;
727 pdu->error_status = SNMP_ERR_GENERR;
728 snmp_pdu_free(resp);
729 return (SNMP_RET_ERR);
730 }
731 asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]);
732 if (asnerr == ASN_ERR_EOBUF) {
733 pdu->error_index = i + 1;
734 pdu->error_status = SNMP_ERR_TOOBIG;
735 snmp_pdu_free(resp);
736 return (SNMP_RET_ERR);
737 } else if (asnerr != ASN_ERR_OK) {
738 pdu->error_index = i + 1;
739 pdu->error_status = SNMP_ERR_GENERR;
740 snmp_pdu_free(resp);
741 return (SNMP_RET_ERR);
742 }
743 resp->nbindings++;
744 }
745
746 context.ctx.code = SNMP_RET_OK;
747
748 /*
749 * 2. Call the SET method for each node. If a SET fails, rollback
750 * everything. Map error codes depending on the version.
751 */
752 for (i = 0; i < pdu->nbindings; i++) {
753 b = &pdu->bindings[i];
754 np = context.node[i];
755
756 context.ctx.var_index = i + 1;
757 context.ctx.scratch = &context.scratch[i];
758
759 ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
760 SNMP_OP_SET);
761
762 if (TR(SET))
763 snmp_debug("set: action %s returns %d", np->name, ret);
764
765 if (pdu->version == SNMP_V1) {
766 switch (ret) {
767 case SNMP_ERR_NO_ACCESS:
768 ret = SNMP_ERR_NOSUCHNAME;
769 break;
770 case SNMP_ERR_WRONG_TYPE:
771 /* should no happen */
772 ret = SNMP_ERR_BADVALUE;
773 break;
774 case SNMP_ERR_WRONG_LENGTH:
775 ret = SNMP_ERR_BADVALUE;
776 break;
777 case SNMP_ERR_WRONG_ENCODING:
778 /* should not happen */
779 ret = SNMP_ERR_BADVALUE;
780 break;
781 case SNMP_ERR_WRONG_VALUE:
782 ret = SNMP_ERR_BADVALUE;
783 break;
784 case SNMP_ERR_NO_CREATION:
785 ret = SNMP_ERR_NOSUCHNAME;
786 break;
787 case SNMP_ERR_INCONS_VALUE:
788 ret = SNMP_ERR_BADVALUE;
789 break;
790 case SNMP_ERR_RES_UNAVAIL:
791 ret = SNMP_ERR_GENERR;
792 break;
793 case SNMP_ERR_COMMIT_FAILED:
794 ret = SNMP_ERR_GENERR;
795 break;
796 case SNMP_ERR_UNDO_FAILED:
797 ret = SNMP_ERR_GENERR;
798 break;
799 case SNMP_ERR_AUTH_ERR:
800 /* should not happen */
801 ret = SNMP_ERR_GENERR;
802 break;
803 case SNMP_ERR_NOT_WRITEABLE:
804 ret = SNMP_ERR_NOSUCHNAME;
805 break;
806 case SNMP_ERR_INCONS_NAME:
807 ret = SNMP_ERR_BADVALUE;
808 break;
809 }
810 }
811 if (ret != SNMP_ERR_NOERROR) {
812 pdu->error_index = i + 1;
813 pdu->error_status = ret;
814
815 rollback(&context, pdu, i);
816 snmp_pdu_free(resp);
817
818 context.ctx.code = SNMP_RET_ERR;
819
820 goto errout;
821 }
822 }
823
824 /*
825 * 3. Call dependencies
826 */
827 if (TR(SET))
828 snmp_debug("set: set operations ok");
829
830 if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) {
831 pdu->error_status = ret;
832 pdu->error_index = context.ctx.var_index;
833
834 if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) {
835 if (pdu->version != SNMP_V1) {
836 pdu->error_status = SNMP_ERR_UNDO_FAILED;
837 pdu->error_index = 0;
838 }
839 }
840 rollback(&context, pdu, i);
841 snmp_pdu_free(resp);
842
843 context.ctx.code = SNMP_RET_ERR;
844
845 goto errout;
846 }
847
848 /*
849 * 4. Commit and copy values from the original packet to the response.
850 * This is not the commit operation from RFC 1905 but rather an
851 * 'FREE RESOURCES' operation. It shouldn't fail.
852 */
853 if (TR(SET))
854 snmp_debug("set: commiting");
855
856 for (i = 0; i < pdu->nbindings; i++) {
857 b = &resp->bindings[i];
858 np = context.node[i];
859
860 context.ctx.var_index = i + 1;
861 context.ctx.scratch = &context.scratch[i];
862
863 ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
864 SNMP_OP_COMMIT);
865
866 if (ret != SNMP_ERR_NOERROR)
867 snmp_error("set: commit failed (%d) on"
868 " variable %s index %u", ret,
869 asn_oid2str_r(&b->var, oidbuf), i);
870 }
871
872 if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
873 snmp_error("set: fix_encoding failed");
874 snmp_pdu_free(resp);
875 context.ctx.code = SNMP_RET_IGN;
876 }
877
878 /*
879 * Done
880 */
881 errout:
882 snmp_dep_finish(&context.ctx);
883
884 if (TR(SET))
885 snmp_debug("set: returning %d", context.ctx.code);
886
887 return (context.ctx.code);
888}
889/*
890 * Lookup a dependency. If it doesn't exist, create one
891 */
892struct snmp_dependency *
893snmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj,
894 const struct asn_oid *idx, size_t len, snmp_depop_t func)
895{
896 struct context *context;
897 struct depend *d;
898
899 context = (struct context *)(void *)
900 ((char *)ctx - offsetof(struct context, ctx));
901 if (TR(DEPEND)) {
902 snmp_debug("depend: looking for %s", asn_oid2str(obj));
903 if (idx)
904 snmp_debug("depend: index is %s", asn_oid2str(idx));
905 }
906 TAILQ_FOREACH(d, &context->dlist, link)
907 if (asn_compare_oid(obj, &d->dep.obj) == 0 &&
908 ((idx == NULL && d->dep.idx.len == 0) ||
909 (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) {
910 if(TR(DEPEND))
911 snmp_debug("depend: found");
912 return (&d->dep);
913 }
914
915 if(TR(DEPEND))
916 snmp_debug("depend: creating");
917
918 if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL)
919 return (NULL);
920 memset(&d->dep, 0, len);
921
922 d->dep.obj = *obj;
923 if (idx == NULL)
924 d->dep.idx.len = 0;
925 else
926 d->dep.idx = *idx;
927 d->len = len;
928 d->func = func;
929
930 TAILQ_INSERT_TAIL(&context->dlist, d, link);
931
932 return (&d->dep);
933}
934
935/*
936 * Make an error response from a PDU. We do this without decoding the
937 * variable bindings. This means we can sent the junk back to a caller
938 * that has sent us junk in the first place.
939 */
940enum snmp_ret
941snmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b,
942 struct asn_buf *resp_b)
943{
944 asn_len_t len;
945 struct snmp_pdu resp;
946 enum asn_err err;
947 enum snmp_code code;
948
949 memset(&resp, 0, sizeof(resp));
950
951 /* Message sequence */
952 if (asn_get_sequence(pdu_b, &len) != ASN_ERR_OK)
953 return (SNMP_RET_IGN);
954 if (pdu_b->asn_len < len)
955 return (SNMP_RET_IGN);
956
957 err = snmp_parse_message_hdr(pdu_b, &resp, &len);
958 if (ASN_ERR_STOPPED(err))
959 return (SNMP_RET_IGN);
960 if (pdu_b->asn_len < len)
961 return (SNMP_RET_IGN);
962 pdu_b->asn_len = len;
963
964 err = snmp_parse_pdus_hdr(pdu_b, &resp, &len);
965 if (ASN_ERR_STOPPED(err))
966 return (SNMP_RET_IGN);
967 if (pdu_b->asn_len < len)
968 return (SNMP_RET_IGN);
969 pdu_b->asn_len = len;
970
971 /* now we have the bindings left - construct new message */
972 resp.error_status = pdu->error_status;
973 resp.error_index = pdu->error_index;
974 resp.type = SNMP_PDU_RESPONSE;
975
976 code = snmp_pdu_encode_header(resp_b, &resp);
977 if (code != SNMP_CODE_OK)
978 return (SNMP_RET_IGN);
979
980 if (pdu_b->asn_len > resp_b->asn_len)
981 /* too short */
982 return (SNMP_RET_IGN);
983 (void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len);
984 resp_b->asn_len -= pdu_b->asn_len;
985 resp_b->asn_ptr += pdu_b->asn_len;
986
987 code = snmp_fix_encoding(resp_b, &resp);
988 if (code != SNMP_CODE_OK)
989 return (SNMP_RET_IGN);
990
991 return (SNMP_RET_OK);
992}
993
994static void
995snmp_debug_func(const char *fmt, ...)
996{
997 va_list ap;
998
999 va_start(ap, fmt);
1000 vfprintf(stderr, fmt, ap);
1001 va_end(ap);
1002 fprintf(stderr, "\n");
1003}