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