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