]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - contrib/bsnmp/lib/snmpagent.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / contrib / bsnmp / lib / snmpagent.c
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
51 static void snmp_debug_func(const char *fmt, ...);
52
53 void (*snmp_debug)(const char *fmt, ...) = snmp_debug_func;
54
55 struct snmp_node *tree;
56 u_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  */
63 struct 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 };
74 TAILQ_HEAD(depend_list, depend);
75
76 /*
77  * Set context
78  */
79 struct 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)
88 u_int snmp_trace = 0;
89
90 static char oidbuf[ASN_OIDSTRLEN];
91
92 /*
93  * Allocate a context
94  */
95 struct snmp_context *
96 snmp_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  */
114 static struct snmp_node *
115 find_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
156 static struct snmp_node *
157 find_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
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  */
173 enum snmp_ret
174 snmp_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
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;
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
262 static struct snmp_node *
263 next_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
306 static enum snmp_ret
307 do_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  */
375 enum snmp_ret
376 snmp_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
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;
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
428 enum snmp_ret
429 snmp_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
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;
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  */
535 static void
536 rollback(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  */
565 int
566 snmp_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  */
592 int
593 snmp_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
622 void
623 snmp_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  */
639 enum snmp_ret
640 snmp_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
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;
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  */
896 struct snmp_dependency *
897 snmp_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  */
944 enum snmp_ret
945 snmp_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));
954
955         /* Message sequence */
956         if (asn_get_sequence(pdu_b, &len) != ASN_ERR_OK)
957                 return (SNMP_RET_IGN);
958         if (pdu_b->asn_len < len)
959                 return (SNMP_RET_IGN);
960
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
998 static void
999 snmp_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 }