]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/bsnmp/lib/snmpagent.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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 static void
169 snmp_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         memcpy(&resp->engine, &pdu->engine, sizeof(pdu->engine));
182         memcpy(&resp->user, &pdu->user, sizeof(pdu->user));
183         snmp_pdu_init_secparams(resp);
184         resp->identifier = pdu->identifier;
185         resp->security_model = pdu->security_model;
186         resp->context_engine_len = pdu->context_engine_len;
187         memcpy(resp->context_engine, pdu->context_engine,
188             resp->context_engine_len);
189         strlcpy(resp->context_name, pdu->context_name,
190             sizeof(resp->context_name));
191 }
192
193 /*
194  * Execute a GET operation. The tree is rooted at the global 'root'.
195  * Build the response PDU on the fly. If the return code is SNMP_RET_ERR
196  * the pdu error status and index will be set.
197  */
198 enum snmp_ret
199 snmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b,
200     struct snmp_pdu *resp, void *data)
201 {
202         int ret;
203         u_int i;
204         struct snmp_node *tp;
205         enum snmp_syntax except;
206         struct context context;
207         enum asn_err err;
208
209         memset(&context, 0, sizeof(context));
210         context.ctx.data = data;
211
212         snmp_pdu_create_response(pdu, resp);
213
214         if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
215                 /* cannot even encode header - very bad */
216                 return (SNMP_RET_IGN);
217
218         for (i = 0; i < pdu->nbindings; i++) {
219                 resp->bindings[i].var = pdu->bindings[i].var;
220                 if ((tp = find_node(&pdu->bindings[i], &except)) == NULL) {
221                         if (pdu->version == SNMP_V1) {
222                                 if (TR(GET))
223                                         snmp_debug("get: nosuchname");
224                                 pdu->error_status = SNMP_ERR_NOSUCHNAME;
225                                 pdu->error_index = i + 1;
226                                 snmp_pdu_free(resp);
227                                 return (SNMP_RET_ERR);
228                         }
229                         if (TR(GET))
230                                 snmp_debug("get: exception %u", except);
231                         resp->bindings[i].syntax = except;
232
233                 } else {
234                         /* call the action to fetch the value. */
235                         resp->bindings[i].syntax = tp->syntax;
236                         ret = (*tp->op)(&context.ctx, &resp->bindings[i],
237                             tp->oid.len, tp->index, SNMP_OP_GET);
238                         if (TR(GET))
239                                 snmp_debug("get: action returns %d", ret);
240
241                         if (ret == SNMP_ERR_NOSUCHNAME) {
242                                 if (pdu->version == SNMP_V1) {
243                                         pdu->error_status = SNMP_ERR_NOSUCHNAME;
244                                         pdu->error_index = i + 1;
245                                         snmp_pdu_free(resp);
246                                         return (SNMP_RET_ERR);
247                                 }
248                                 if (TR(GET))
249                                         snmp_debug("get: exception noSuchInstance");
250                                 resp->bindings[i].syntax = SNMP_SYNTAX_NOSUCHINSTANCE;
251
252                         } else if (ret != SNMP_ERR_NOERROR) {
253                                 pdu->error_status = SNMP_ERR_GENERR;
254                                 pdu->error_index = i + 1;
255                                 snmp_pdu_free(resp);
256                                 return (SNMP_RET_ERR);
257                         }
258                 }
259                 resp->nbindings++;
260
261                 err = snmp_binding_encode(resp_b, &resp->bindings[i]);
262
263                 if (err == ASN_ERR_EOBUF) {
264                         pdu->error_status = SNMP_ERR_TOOBIG;
265                         pdu->error_index = 0;
266                         snmp_pdu_free(resp);
267                         return (SNMP_RET_ERR);
268                 }
269                 if (err != ASN_ERR_OK) {
270                         if (TR(GET))
271                                 snmp_debug("get: binding encoding: %u", err);
272                         pdu->error_status = SNMP_ERR_GENERR;
273                         pdu->error_index = i + 1;
274                         snmp_pdu_free(resp);
275                         return (SNMP_RET_ERR);
276                 }
277         }
278
279         return (snmp_fix_encoding(resp_b, resp));
280 }
281
282 static struct snmp_node *
283 next_node(const struct snmp_value *value, int *pnext)
284 {
285         struct snmp_node *tp;
286
287         if (TR(FIND))
288                 snmp_debug("next: searching %s",
289                     asn_oid2str_r(&value->var, oidbuf));
290
291         *pnext = 0;
292         for (tp = tree; tp < tree + tree_size; tp++) {
293                 if (asn_is_suboid(&tp->oid, &value->var)) {
294                         /* the tree OID is a sub-oid of the requested OID. */
295                         if (tp->type == SNMP_NODE_LEAF) {
296                                 if (tp->oid.len == value->var.len) {
297                                         /* request for scalar type */
298                                         if (TR(FIND))
299                                                 snmp_debug("next: found scalar %s",
300                                                     asn_oid2str_r(&tp->oid, oidbuf));
301                                         return (tp);
302                                 }
303                                 /* try next */
304                         } else {
305                                 if (TR(FIND))
306                                         snmp_debug("next: found column %s",
307                                             asn_oid2str_r(&tp->oid, oidbuf));
308                                 return (tp);
309                         }
310                 } else if (asn_is_suboid(&value->var, &tp->oid) ||
311                     asn_compare_oid(&tp->oid, &value->var) >= 0) {
312                         if (TR(FIND))
313                                 snmp_debug("next: found %s",
314                                     asn_oid2str_r(&tp->oid, oidbuf));
315                         *pnext = 1;
316                         return (tp);
317                 }
318         }
319
320         if (TR(FIND))
321                 snmp_debug("next: failed");
322
323         return (NULL);
324 }
325
326 static enum snmp_ret
327 do_getnext(struct context *context, const struct snmp_value *inb,
328     struct snmp_value *outb, struct snmp_pdu *pdu)
329 {
330         const struct snmp_node *tp;
331         int ret, next;
332
333         if ((tp = next_node(inb, &next)) == NULL)
334                 goto eofMib;
335
336         /* retain old variable if we are doing a GETNEXT on an exact
337          * matched leaf only */
338         if (tp->type == SNMP_NODE_LEAF || next)
339                 outb->var = tp->oid;
340         else
341                 outb->var = inb->var;
342
343         for (;;) {
344                 outb->syntax = tp->syntax;
345                 if (tp->type == SNMP_NODE_LEAF) {
346                         /* make a GET operation */
347                         outb->var.subs[outb->var.len++] = 0;
348                         ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
349                             tp->index, SNMP_OP_GET);
350                 } else {
351                         /* make a GETNEXT */
352                         ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
353                              tp->index, SNMP_OP_GETNEXT);
354                 }
355                 if (ret != SNMP_ERR_NOSUCHNAME) {
356                         /* got something */
357                         if (ret != SNMP_ERR_NOERROR && TR(GETNEXT))
358                                 snmp_debug("getnext: %s returns %u",
359                                     asn_oid2str(&outb->var), ret);
360                         break;
361                 }
362
363                 /* object has no data - try next */
364                 if (++tp == tree + tree_size)
365                         break;
366
367                 if (TR(GETNEXT))
368                         snmp_debug("getnext: no data - avancing to %s",
369                             asn_oid2str(&tp->oid));
370
371                 outb->var = tp->oid;
372         }
373
374         if (ret == SNMP_ERR_NOSUCHNAME) {
375   eofMib:
376                 outb->var = inb->var;
377                 if (pdu->version == SNMP_V1) {
378                         pdu->error_status = SNMP_ERR_NOSUCHNAME;
379                         return (SNMP_RET_ERR);
380                 }
381                 outb->syntax = SNMP_SYNTAX_ENDOFMIBVIEW;
382
383         } else if (ret != SNMP_ERR_NOERROR) {
384                 pdu->error_status = SNMP_ERR_GENERR;
385                 return (SNMP_RET_ERR);
386         }
387         return (SNMP_RET_OK);
388 }
389
390
391 /*
392  * Execute a GETNEXT operation. The tree is rooted at the global 'root'.
393  * Build the response PDU on the fly. The return is:
394  */
395 enum snmp_ret
396 snmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b,
397     struct snmp_pdu *resp, void *data)
398 {
399         struct context context;
400         u_int i;
401         enum asn_err err;
402         enum snmp_ret result;
403
404         memset(&context, 0, sizeof(context));
405         context.ctx.data = data;
406
407         snmp_pdu_create_response(pdu, resp);
408
409         if (snmp_pdu_encode_header(resp_b, resp))
410                 return (SNMP_RET_IGN);
411
412         for (i = 0; i < pdu->nbindings; i++) {
413                 result = do_getnext(&context, &pdu->bindings[i],
414                     &resp->bindings[i], pdu);
415
416                 if (result != SNMP_RET_OK) {
417                         pdu->error_index = i + 1;
418                         snmp_pdu_free(resp);
419                         return (result);
420                 }
421
422                 resp->nbindings++;
423
424                 err = snmp_binding_encode(resp_b, &resp->bindings[i]);
425
426                 if (err == ASN_ERR_EOBUF) {
427                         pdu->error_status = SNMP_ERR_TOOBIG;
428                         pdu->error_index = 0;
429                         snmp_pdu_free(resp);
430                         return (SNMP_RET_ERR);
431                 }
432                 if (err != ASN_ERR_OK) {
433                         if (TR(GET))
434                                 snmp_debug("getnext: binding encoding: %u", err);
435                         pdu->error_status = SNMP_ERR_GENERR;
436                         pdu->error_index = i + 1;
437                         snmp_pdu_free(resp);
438                         return (SNMP_RET_ERR);
439                 }
440         }
441         return (snmp_fix_encoding(resp_b, resp));
442 }
443
444 enum snmp_ret
445 snmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b,
446     struct snmp_pdu *resp, void *data)
447 {
448         struct context context;
449         u_int i;
450         int cnt;
451         u_int non_rep;
452         int eomib;
453         enum snmp_ret result;
454         enum asn_err err;
455
456         memset(&context, 0, sizeof(context));
457         context.ctx.data = data;
458
459         snmp_pdu_create_response(pdu, resp);
460
461         if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
462                 /* cannot even encode header - very bad */
463                 return (SNMP_RET_IGN);
464
465         if ((non_rep = pdu->error_status) > pdu->nbindings)
466                 non_rep = pdu->nbindings;
467
468         /* non-repeaters */
469         for (i = 0; i < non_rep; i++) {
470                 result = do_getnext(&context, &pdu->bindings[i],
471                     &resp->bindings[resp->nbindings], pdu);
472
473                 if (result != SNMP_RET_OK) {
474                         pdu->error_index = i + 1;
475                         snmp_pdu_free(resp);
476                         return (result);
477                 }
478
479                 err = snmp_binding_encode(resp_b,
480                     &resp->bindings[resp->nbindings++]);
481
482                 if (err == ASN_ERR_EOBUF)
483                         goto done;
484
485                 if (err != ASN_ERR_OK) {
486                         if (TR(GET))
487                                 snmp_debug("getnext: binding encoding: %u", err);
488                         pdu->error_status = SNMP_ERR_GENERR;
489                         pdu->error_index = i + 1;
490                         snmp_pdu_free(resp);
491                         return (SNMP_RET_ERR);
492                 }
493         }
494
495         if (non_rep == pdu->nbindings)
496                 goto done;
497
498         /* repeates */
499         for (cnt = 0; cnt < pdu->error_index; cnt++) {
500                 eomib = 1;
501                 for (i = non_rep; i < pdu->nbindings; i++) {
502                         if (cnt == 0) 
503                                 result = do_getnext(&context, &pdu->bindings[i],
504                                     &resp->bindings[resp->nbindings], pdu);
505                         else
506                                 result = do_getnext(&context,
507                                     &resp->bindings[resp->nbindings -
508                                     (pdu->nbindings - non_rep)],
509                                     &resp->bindings[resp->nbindings], pdu);
510
511                         if (result != SNMP_RET_OK) {
512                                 pdu->error_index = i + 1;
513                                 snmp_pdu_free(resp);
514                                 return (result);
515                         }
516                         if (resp->bindings[resp->nbindings].syntax !=
517                             SNMP_SYNTAX_ENDOFMIBVIEW)
518                                 eomib = 0;
519
520                         err = snmp_binding_encode(resp_b,
521                             &resp->bindings[resp->nbindings++]);
522
523                         if (err == ASN_ERR_EOBUF)
524                                 goto done;
525
526                         if (err != ASN_ERR_OK) {
527                                 if (TR(GET))
528                                         snmp_debug("getnext: binding encoding: %u", err);
529                                 pdu->error_status = SNMP_ERR_GENERR;
530                                 pdu->error_index = i + 1;
531                                 snmp_pdu_free(resp);
532                                 return (SNMP_RET_ERR);
533                         }
534                 }
535                 if (eomib)
536                         break;
537         }
538
539   done:
540         return (snmp_fix_encoding(resp_b, resp));
541 }
542
543 /*
544  * Rollback a SET operation. Failed index is 'i'.
545  */
546 static void
547 rollback(struct context *context, struct snmp_pdu *pdu, u_int i)
548 {
549         struct snmp_value *b;
550         const struct snmp_node *np;
551         int ret;
552
553         while (i-- > 0) {
554                 b = &pdu->bindings[i];
555                 np = context->node[i];
556
557                 context->ctx.scratch = &context->scratch[i];
558
559                 ret = (*np->op)(&context->ctx, b, np->oid.len, np->index,
560                     SNMP_OP_ROLLBACK);
561
562                 if (ret != SNMP_ERR_NOERROR) {
563                         snmp_error("set: rollback failed (%d) on variable %s "
564                             "index %u", ret, asn_oid2str(&b->var), i);
565                         if (pdu->version != SNMP_V1) {
566                                 pdu->error_status = SNMP_ERR_UNDO_FAILED;
567                                 pdu->error_index = 0;
568                         }
569                 }
570         }
571 }
572
573 /*
574  * Commit dependencies.
575  */
576 int
577 snmp_dep_commit(struct snmp_context *ctx)
578 {
579         struct context *context = (struct context *)ctx;
580         int ret;
581
582         TAILQ_FOREACH(context->depend, &context->dlist, link) {
583                 ctx->dep = &context->depend->dep;
584
585                 if (TR(SET))
586                         snmp_debug("set: dependency commit %s",
587                             asn_oid2str(&ctx->dep->obj));
588
589                 ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT);
590
591                 if (ret != SNMP_ERR_NOERROR) {
592                         if (TR(SET))
593                                 snmp_debug("set: dependency failed %d", ret);
594                         return (ret);
595                 }
596         }
597         return (SNMP_ERR_NOERROR);
598 }
599
600 /*
601  * Rollback dependencies
602  */
603 int
604 snmp_dep_rollback(struct snmp_context *ctx)
605 {
606         struct context *context = (struct context *)ctx;
607         int ret, ret1;
608         char objbuf[ASN_OIDSTRLEN];
609         char idxbuf[ASN_OIDSTRLEN];
610
611         ret1 = SNMP_ERR_NOERROR;
612         while ((context->depend =
613             TAILQ_PREV(context->depend, depend_list, link)) != NULL) {
614                 ctx->dep = &context->depend->dep;
615
616                 if (TR(SET))
617                         snmp_debug("set: dependency rollback %s",
618                             asn_oid2str(&ctx->dep->obj));
619
620                 ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK);
621
622                 if (ret != SNMP_ERR_NOERROR) {
623                         snmp_debug("set: dep rollback returns %u: %s %s", ret,
624                             asn_oid2str_r(&ctx->dep->obj, objbuf),
625                             asn_oid2str_r(&ctx->dep->idx, idxbuf));
626                         if (ret1 == SNMP_ERR_NOERROR)
627                                 ret1 = ret;
628                 }
629         }
630         return (ret1);
631 }
632
633 void
634 snmp_dep_finish(struct snmp_context *ctx)
635 {
636         struct context *context = (struct context *)ctx;
637         struct depend *d;
638
639         while ((d = TAILQ_FIRST(&context->dlist)) != NULL) {
640                 ctx->dep = &d->dep;
641                 (void)d->func(ctx, ctx->dep, SNMP_DEPOP_FINISH);
642                 TAILQ_REMOVE(&context->dlist, d, link);
643                 free(d);
644         }
645 }
646
647 /*
648  * Do a SET operation.
649  */
650 enum snmp_ret
651 snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b,
652     struct snmp_pdu *resp, void *data)
653 {
654         int ret;
655         u_int i;
656         enum asn_err asnerr;
657         struct context context;
658         const struct snmp_node *np;
659         struct snmp_value *b;
660         enum snmp_syntax except;
661
662         memset(&context, 0, sizeof(context));
663         TAILQ_INIT(&context.dlist);
664         context.ctx.data = data;
665
666         snmp_pdu_create_response(pdu, resp);
667
668         if (snmp_pdu_encode_header(resp_b, resp))
669                 return (SNMP_RET_IGN);
670
671         /* 
672          * 1. Find all nodes, check that they are writeable and
673          *    that the syntax is ok, copy over the binding to the response.
674          */
675         for (i = 0; i < pdu->nbindings; i++) {
676                 b = &pdu->bindings[i];
677
678                 if ((np = context.node[i] = find_node(b, &except)) == NULL) {
679                         /* not found altogether or LEAF with wrong index */
680                         if (TR(SET))
681                                 snmp_debug("set: node not found %s",
682                                     asn_oid2str_r(&b->var, oidbuf));
683                         if (pdu->version == SNMP_V1) {
684                                 pdu->error_index = i + 1;
685                                 pdu->error_status = SNMP_ERR_NOSUCHNAME;
686                         } else if ((np = find_subnode(b)) != NULL) {
687                                 /* 2. intermediate object */
688                                 pdu->error_index = i + 1;
689                                 pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
690                         } else if (except == SNMP_SYNTAX_NOSUCHOBJECT) {
691                                 pdu->error_index = i + 1;
692                                 pdu->error_status = SNMP_ERR_NO_ACCESS;
693                         } else {
694                                 pdu->error_index = i + 1;
695                                 pdu->error_status = SNMP_ERR_NO_CREATION;
696                         }
697                         snmp_pdu_free(resp);
698                         return (SNMP_RET_ERR);
699                 }
700                 /*
701                  * 2. write/createable?
702                  * Can check this for leafs only, because in v2 we have
703                  * to differentiate between NOT_WRITEABLE and NO_CREATION
704                  * and only the action routine for COLUMNS knows, whether
705                  * a column exists.
706                  */
707                 if (np->type == SNMP_NODE_LEAF &&
708                     !(np->flags & SNMP_NODE_CANSET)) {
709                         if (pdu->version == SNMP_V1) {
710                                 pdu->error_index = i + 1;
711                                 pdu->error_status = SNMP_ERR_NOSUCHNAME;
712                         } else {
713                                 pdu->error_index = i + 1;
714                                 pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
715                         }
716                         snmp_pdu_free(resp);
717                         return (SNMP_RET_ERR);
718                 }
719                 /*
720                  * 3. Ensure the right syntax
721                  */
722                 if (np->syntax != b->syntax) {
723                         if (pdu->version == SNMP_V1) {
724                                 pdu->error_index = i + 1;
725                                 pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */
726                         } else {
727                                 pdu->error_index = i + 1;
728                                 pdu->error_status = SNMP_ERR_WRONG_TYPE;
729                         }
730                         snmp_pdu_free(resp);
731                         return (SNMP_RET_ERR);
732                 }
733                 /*
734                  * 4. Copy binding
735                  */
736                 if (snmp_value_copy(&resp->bindings[i], b)) {
737                         pdu->error_index = i + 1;
738                         pdu->error_status = SNMP_ERR_GENERR;
739                         snmp_pdu_free(resp);
740                         return (SNMP_RET_ERR);
741                 }
742                 asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]);
743                 if (asnerr == ASN_ERR_EOBUF) {
744                         pdu->error_index = i + 1;
745                         pdu->error_status = SNMP_ERR_TOOBIG;
746                         snmp_pdu_free(resp);
747                         return (SNMP_RET_ERR);
748                 } else if (asnerr != ASN_ERR_OK) {
749                         pdu->error_index = i + 1;
750                         pdu->error_status = SNMP_ERR_GENERR;
751                         snmp_pdu_free(resp);
752                         return (SNMP_RET_ERR);
753                 }
754                 resp->nbindings++;
755         }
756
757         context.ctx.code = SNMP_RET_OK;
758
759         /*
760          * 2. Call the SET method for each node. If a SET fails, rollback
761          *    everything. Map error codes depending on the version.
762          */
763         for (i = 0; i < pdu->nbindings; i++) {
764                 b = &pdu->bindings[i];
765                 np = context.node[i];
766
767                 context.ctx.var_index = i + 1;
768                 context.ctx.scratch = &context.scratch[i];
769
770                 ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
771                     SNMP_OP_SET);
772
773                 if (TR(SET))
774                         snmp_debug("set: action %s returns %d", np->name, ret);
775
776                 if (pdu->version == SNMP_V1) {
777                         switch (ret) {
778                           case SNMP_ERR_NO_ACCESS:
779                                 ret = SNMP_ERR_NOSUCHNAME;
780                                 break;
781                           case SNMP_ERR_WRONG_TYPE:
782                                 /* should no happen */
783                                 ret = SNMP_ERR_BADVALUE;
784                                 break;
785                           case SNMP_ERR_WRONG_LENGTH:
786                                 ret = SNMP_ERR_BADVALUE;
787                                 break;
788                           case SNMP_ERR_WRONG_ENCODING:
789                                 /* should not happen */
790                                 ret = SNMP_ERR_BADVALUE;
791                                 break;
792                           case SNMP_ERR_WRONG_VALUE:
793                                 ret = SNMP_ERR_BADVALUE;
794                                 break;
795                           case SNMP_ERR_NO_CREATION:
796                                 ret = SNMP_ERR_NOSUCHNAME;
797                                 break;
798                           case SNMP_ERR_INCONS_VALUE:
799                                 ret = SNMP_ERR_BADVALUE;
800                                 break;
801                           case SNMP_ERR_RES_UNAVAIL:
802                                 ret = SNMP_ERR_GENERR;
803                                 break;
804                           case SNMP_ERR_COMMIT_FAILED:
805                                 ret = SNMP_ERR_GENERR;
806                                 break;
807                           case SNMP_ERR_UNDO_FAILED:
808                                 ret = SNMP_ERR_GENERR;
809                                 break;
810                           case SNMP_ERR_AUTH_ERR:
811                                 /* should not happen */
812                                 ret = SNMP_ERR_GENERR;
813                                 break;
814                           case SNMP_ERR_NOT_WRITEABLE:
815                                 ret = SNMP_ERR_NOSUCHNAME;
816                                 break;
817                           case SNMP_ERR_INCONS_NAME:
818                                 ret = SNMP_ERR_BADVALUE;
819                                 break;
820                         }
821                 }
822                 if (ret != SNMP_ERR_NOERROR) {
823                         pdu->error_index = i + 1;
824                         pdu->error_status = ret;
825
826                         rollback(&context, pdu, i);
827                         snmp_pdu_free(resp);
828
829                         context.ctx.code = SNMP_RET_ERR;
830
831                         goto errout;
832                 }
833         }
834
835         /*
836          * 3. Call dependencies
837          */
838         if (TR(SET))
839                 snmp_debug("set: set operations ok");
840
841         if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) {
842                 pdu->error_status = ret;
843                 pdu->error_index = context.ctx.var_index;
844
845                 if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) {
846                         if (pdu->version != SNMP_V1) {
847                                 pdu->error_status = SNMP_ERR_UNDO_FAILED;
848                                 pdu->error_index = 0;
849                         }
850                 }
851                 rollback(&context, pdu, i);
852                 snmp_pdu_free(resp);
853
854                 context.ctx.code = SNMP_RET_ERR;
855
856                 goto errout;
857         }
858
859         /*
860          * 4. Commit and copy values from the original packet to the response.
861          *    This is not the commit operation from RFC 1905 but rather an
862          *    'FREE RESOURCES' operation. It shouldn't fail.
863          */
864         if (TR(SET))
865                 snmp_debug("set: commiting");
866
867         for (i = 0; i < pdu->nbindings; i++) {
868                 b = &resp->bindings[i];
869                 np = context.node[i];
870
871                 context.ctx.var_index = i + 1;
872                 context.ctx.scratch = &context.scratch[i];
873
874                 ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
875                     SNMP_OP_COMMIT);
876
877                 if (ret != SNMP_ERR_NOERROR)
878                         snmp_error("set: commit failed (%d) on"
879                             " variable %s index %u", ret,
880                             asn_oid2str_r(&b->var, oidbuf), i);
881         }
882
883         if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
884                 snmp_error("set: fix_encoding failed");
885                 snmp_pdu_free(resp);
886                 context.ctx.code = SNMP_RET_IGN;
887         }
888
889         /*
890          * Done
891          */
892   errout:
893         snmp_dep_finish(&context.ctx);
894
895         if (TR(SET))
896                 snmp_debug("set: returning %d", context.ctx.code);
897
898         return (context.ctx.code);
899 }
900 /*
901  * Lookup a dependency. If it doesn't exist, create one
902  */
903 struct snmp_dependency *
904 snmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj,
905     const struct asn_oid *idx, size_t len, snmp_depop_t func)
906 {
907         struct context *context;
908         struct depend *d;
909
910         context = (struct context *)(void *)
911             ((char *)ctx - offsetof(struct context, ctx));
912         if (TR(DEPEND)) {
913                 snmp_debug("depend: looking for %s", asn_oid2str(obj));
914                 if (idx)
915                         snmp_debug("depend: index is %s", asn_oid2str(idx));
916         }
917         TAILQ_FOREACH(d, &context->dlist, link)
918                 if (asn_compare_oid(obj, &d->dep.obj) == 0 &&
919                     ((idx == NULL && d->dep.idx.len == 0) ||
920                      (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) {
921                         if(TR(DEPEND))
922                                 snmp_debug("depend: found");
923                         return (&d->dep);
924                 }
925
926         if(TR(DEPEND))
927                 snmp_debug("depend: creating");
928
929         if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL)
930                 return (NULL);
931         memset(&d->dep, 0, len);
932
933         d->dep.obj = *obj;
934         if (idx == NULL)
935                 d->dep.idx.len = 0;
936         else
937                 d->dep.idx = *idx;
938         d->len = len;
939         d->func = func;
940
941         TAILQ_INSERT_TAIL(&context->dlist, d, link);
942
943         return (&d->dep);
944 }
945
946 /*
947  * Make an error response from a PDU. We do this without decoding the
948  * variable bindings. This means we can sent the junk back to a caller
949  * that has sent us junk in the first place. 
950  */
951 enum snmp_ret
952 snmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b,
953     struct asn_buf *resp_b)
954 {
955         asn_len_t len;
956         struct snmp_pdu resp;
957         enum asn_err err;
958         enum snmp_code code;
959
960         memset(&resp, 0, sizeof(resp));
961         if ((code = snmp_pdu_decode_header(pdu_b, &resp)) != SNMP_CODE_OK)
962                 return (SNMP_RET_IGN);
963
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 }