]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/bsnmp/lib/snmpagent.c
MFstable/11 r310901:
[FreeBSD/stable/10.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(const 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         if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
280                 snmp_debug("get: failed to encode PDU");
281                 return (SNMP_RET_ERR);
282         }
283
284         return (SNMP_RET_OK);
285 }
286
287 static struct snmp_node *
288 next_node(const struct snmp_value *value, int *pnext)
289 {
290         struct snmp_node *tp;
291
292         if (TR(FIND))
293                 snmp_debug("next: searching %s",
294                     asn_oid2str_r(&value->var, oidbuf));
295
296         *pnext = 0;
297         for (tp = tree; tp < tree + tree_size; tp++) {
298                 if (asn_is_suboid(&tp->oid, &value->var)) {
299                         /* the tree OID is a sub-oid of the requested OID. */
300                         if (tp->type == SNMP_NODE_LEAF) {
301                                 if (tp->oid.len == value->var.len) {
302                                         /* request for scalar type */
303                                         if (TR(FIND))
304                                                 snmp_debug("next: found scalar %s",
305                                                     asn_oid2str_r(&tp->oid, oidbuf));
306                                         return (tp);
307                                 }
308                                 /* try next */
309                         } else {
310                                 if (TR(FIND))
311                                         snmp_debug("next: found column %s",
312                                             asn_oid2str_r(&tp->oid, oidbuf));
313                                 return (tp);
314                         }
315                 } else if (asn_is_suboid(&value->var, &tp->oid) ||
316                     asn_compare_oid(&tp->oid, &value->var) >= 0) {
317                         if (TR(FIND))
318                                 snmp_debug("next: found %s",
319                                     asn_oid2str_r(&tp->oid, oidbuf));
320                         *pnext = 1;
321                         return (tp);
322                 }
323         }
324
325         if (TR(FIND))
326                 snmp_debug("next: failed");
327
328         return (NULL);
329 }
330
331 static enum snmp_ret
332 do_getnext(struct context *context, const struct snmp_value *inb,
333     struct snmp_value *outb, struct snmp_pdu *pdu)
334 {
335         const struct snmp_node *tp;
336         int ret, next;
337
338         if ((tp = next_node(inb, &next)) == NULL)
339                 goto eofMib;
340
341         /* retain old variable if we are doing a GETNEXT on an exact
342          * matched leaf only */
343         if (tp->type == SNMP_NODE_LEAF || next)
344                 outb->var = tp->oid;
345         else
346                 outb->var = inb->var;
347
348         for (;;) {
349                 outb->syntax = tp->syntax;
350                 if (tp->type == SNMP_NODE_LEAF) {
351                         /* make a GET operation */
352                         outb->var.subs[outb->var.len++] = 0;
353                         ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
354                             tp->index, SNMP_OP_GET);
355                 } else {
356                         /* make a GETNEXT */
357                         ret = (*tp->op)(&context->ctx, outb, tp->oid.len,
358                              tp->index, SNMP_OP_GETNEXT);
359                 }
360                 if (ret != SNMP_ERR_NOSUCHNAME) {
361                         /* got something */
362                         if (ret != SNMP_ERR_NOERROR && TR(GETNEXT))
363                                 snmp_debug("getnext: %s returns %u",
364                                     asn_oid2str(&outb->var), ret);
365                         break;
366                 }
367
368                 /* object has no data - try next */
369                 if (++tp == tree + tree_size)
370                         break;
371
372                 if (TR(GETNEXT))
373                         snmp_debug("getnext: no data - avancing to %s",
374                             asn_oid2str(&tp->oid));
375
376                 outb->var = tp->oid;
377         }
378
379         if (ret == SNMP_ERR_NOSUCHNAME) {
380   eofMib:
381                 outb->var = inb->var;
382                 if (pdu->version == SNMP_V1) {
383                         pdu->error_status = SNMP_ERR_NOSUCHNAME;
384                         return (SNMP_RET_ERR);
385                 }
386                 outb->syntax = SNMP_SYNTAX_ENDOFMIBVIEW;
387
388         } else if (ret != SNMP_ERR_NOERROR) {
389                 pdu->error_status = SNMP_ERR_GENERR;
390                 return (SNMP_RET_ERR);
391         }
392         return (SNMP_RET_OK);
393 }
394
395
396 /*
397  * Execute a GETNEXT operation. The tree is rooted at the global 'root'.
398  * Build the response PDU on the fly. The return is:
399  */
400 enum snmp_ret
401 snmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b,
402     struct snmp_pdu *resp, void *data)
403 {
404         struct context context;
405         u_int i;
406         enum asn_err err;
407         enum snmp_ret result;
408
409         memset(&context, 0, sizeof(context));
410         context.ctx.data = data;
411
412         snmp_pdu_create_response(pdu, resp);
413
414         if (snmp_pdu_encode_header(resp_b, resp))
415                 return (SNMP_RET_IGN);
416
417         for (i = 0; i < pdu->nbindings; i++) {
418                 result = do_getnext(&context, &pdu->bindings[i],
419                     &resp->bindings[i], pdu);
420
421                 if (result != SNMP_RET_OK) {
422                         pdu->error_index = i + 1;
423                         snmp_pdu_free(resp);
424                         return (result);
425                 }
426
427                 resp->nbindings++;
428
429                 err = snmp_binding_encode(resp_b, &resp->bindings[i]);
430
431                 if (err == ASN_ERR_EOBUF) {
432                         pdu->error_status = SNMP_ERR_TOOBIG;
433                         pdu->error_index = 0;
434                         snmp_pdu_free(resp);
435                         return (SNMP_RET_ERR);
436                 }
437                 if (err != ASN_ERR_OK) {
438                         if (TR(GET))
439                                 snmp_debug("getnext: binding encoding: %u", err);
440                         pdu->error_status = SNMP_ERR_GENERR;
441                         pdu->error_index = i + 1;
442                         snmp_pdu_free(resp);
443                         return (SNMP_RET_ERR);
444                 }
445         }
446
447         if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
448                 snmp_debug("getnext: failed to encode PDU");
449                 return (SNMP_RET_ERR);
450         }
451
452         return (SNMP_RET_OK);
453 }
454
455 enum snmp_ret
456 snmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b,
457     struct snmp_pdu *resp, void *data)
458 {
459         struct context context;
460         u_int i;
461         int cnt;
462         u_int non_rep;
463         int eomib;
464         enum snmp_ret result;
465         enum asn_err err;
466
467         memset(&context, 0, sizeof(context));
468         context.ctx.data = data;
469
470         snmp_pdu_create_response(pdu, resp);
471
472         if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
473                 /* cannot even encode header - very bad */
474                 return (SNMP_RET_IGN);
475
476         if ((non_rep = pdu->error_status) > pdu->nbindings)
477                 non_rep = pdu->nbindings;
478
479         /* non-repeaters */
480         for (i = 0; i < non_rep; i++) {
481                 result = do_getnext(&context, &pdu->bindings[i],
482                     &resp->bindings[resp->nbindings], pdu);
483
484                 if (result != SNMP_RET_OK) {
485                         pdu->error_index = i + 1;
486                         snmp_pdu_free(resp);
487                         return (result);
488                 }
489
490                 err = snmp_binding_encode(resp_b,
491                     &resp->bindings[resp->nbindings++]);
492
493                 if (err == ASN_ERR_EOBUF)
494                         goto done;
495
496                 if (err != ASN_ERR_OK) {
497                         if (TR(GET))
498                                 snmp_debug("getnext: binding encoding: %u", err);
499                         pdu->error_status = SNMP_ERR_GENERR;
500                         pdu->error_index = i + 1;
501                         snmp_pdu_free(resp);
502                         return (SNMP_RET_ERR);
503                 }
504         }
505
506         if (non_rep == pdu->nbindings)
507                 goto done;
508
509         /* repeates */
510         for (cnt = 0; cnt < pdu->error_index; cnt++) {
511                 eomib = 1;
512                 for (i = non_rep; i < pdu->nbindings; i++) {
513
514                         if (resp->nbindings == SNMP_MAX_BINDINGS)
515                                 /* PDU is full */
516                                 goto done;
517
518                         if (cnt == 0)
519                                 result = do_getnext(&context, &pdu->bindings[i],
520                                     &resp->bindings[resp->nbindings], pdu);
521                         else
522                                 result = do_getnext(&context,
523                                     &resp->bindings[resp->nbindings -
524                                     (pdu->nbindings - non_rep)],
525                                     &resp->bindings[resp->nbindings], pdu);
526
527                         if (result != SNMP_RET_OK) {
528                                 pdu->error_index = i + 1;
529                                 snmp_pdu_free(resp);
530                                 return (result);
531                         }
532                         if (resp->bindings[resp->nbindings].syntax !=
533                             SNMP_SYNTAX_ENDOFMIBVIEW)
534                                 eomib = 0;
535
536                         err = snmp_binding_encode(resp_b,
537                             &resp->bindings[resp->nbindings++]);
538
539                         if (err == ASN_ERR_EOBUF)
540                                 goto done;
541
542                         if (err != ASN_ERR_OK) {
543                                 if (TR(GET))
544                                         snmp_debug("getnext: binding encoding: %u", err);
545                                 pdu->error_status = SNMP_ERR_GENERR;
546                                 pdu->error_index = i + 1;
547                                 snmp_pdu_free(resp);
548                                 return (SNMP_RET_ERR);
549                         }
550                 }
551                 if (eomib)
552                         break;
553         }
554
555   done:
556         if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
557                 snmp_debug("getnext: failed to encode PDU");
558                 return (SNMP_RET_ERR);
559         }
560
561         return (SNMP_RET_OK);
562 }
563
564 /*
565  * Rollback a SET operation. Failed index is 'i'.
566  */
567 static void
568 rollback(struct context *context, struct snmp_pdu *pdu, u_int i)
569 {
570         struct snmp_value *b;
571         const struct snmp_node *np;
572         int ret;
573
574         while (i-- > 0) {
575                 b = &pdu->bindings[i];
576                 np = context->node[i];
577
578                 context->ctx.scratch = &context->scratch[i];
579
580                 ret = (*np->op)(&context->ctx, b, np->oid.len, np->index,
581                     SNMP_OP_ROLLBACK);
582
583                 if (ret != SNMP_ERR_NOERROR) {
584                         snmp_error("set: rollback failed (%d) on variable %s "
585                             "index %u", ret, asn_oid2str(&b->var), i);
586                         if (pdu->version != SNMP_V1) {
587                                 pdu->error_status = SNMP_ERR_UNDO_FAILED;
588                                 pdu->error_index = 0;
589                         }
590                 }
591         }
592 }
593
594 /*
595  * Commit dependencies.
596  */
597 int
598 snmp_dep_commit(struct snmp_context *ctx)
599 {
600         struct context *context = (struct context *)ctx;
601         int ret;
602
603         TAILQ_FOREACH(context->depend, &context->dlist, link) {
604                 ctx->dep = &context->depend->dep;
605
606                 if (TR(SET))
607                         snmp_debug("set: dependency commit %s",
608                             asn_oid2str(&ctx->dep->obj));
609
610                 ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT);
611
612                 if (ret != SNMP_ERR_NOERROR) {
613                         if (TR(SET))
614                                 snmp_debug("set: dependency failed %d", ret);
615                         return (ret);
616                 }
617         }
618         return (SNMP_ERR_NOERROR);
619 }
620
621 /*
622  * Rollback dependencies
623  */
624 int
625 snmp_dep_rollback(struct snmp_context *ctx)
626 {
627         struct context *context = (struct context *)ctx;
628         int ret, ret1;
629         char objbuf[ASN_OIDSTRLEN];
630         char idxbuf[ASN_OIDSTRLEN];
631
632         ret1 = SNMP_ERR_NOERROR;
633         while ((context->depend =
634             TAILQ_PREV(context->depend, depend_list, link)) != NULL) {
635                 ctx->dep = &context->depend->dep;
636
637                 if (TR(SET))
638                         snmp_debug("set: dependency rollback %s",
639                             asn_oid2str(&ctx->dep->obj));
640
641                 ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK);
642
643                 if (ret != SNMP_ERR_NOERROR) {
644                         snmp_debug("set: dep rollback returns %u: %s %s", ret,
645                             asn_oid2str_r(&ctx->dep->obj, objbuf),
646                             asn_oid2str_r(&ctx->dep->idx, idxbuf));
647                         if (ret1 == SNMP_ERR_NOERROR)
648                                 ret1 = ret;
649                 }
650         }
651         return (ret1);
652 }
653
654 void
655 snmp_dep_finish(struct snmp_context *ctx)
656 {
657         struct context *context = (struct context *)ctx;
658         struct depend *d;
659
660         while ((d = TAILQ_FIRST(&context->dlist)) != NULL) {
661                 ctx->dep = &d->dep;
662                 (void)d->func(ctx, ctx->dep, SNMP_DEPOP_FINISH);
663                 TAILQ_REMOVE(&context->dlist, d, link);
664                 free(d);
665         }
666 }
667
668 /*
669  * Do a SET operation.
670  */
671 enum snmp_ret
672 snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b,
673     struct snmp_pdu *resp, void *data)
674 {
675         int ret;
676         u_int i;
677         enum asn_err asnerr;
678         struct context context;
679         const struct snmp_node *np;
680         struct snmp_value *b;
681         enum snmp_syntax except;
682
683         memset(&context, 0, sizeof(context));
684         TAILQ_INIT(&context.dlist);
685         context.ctx.data = data;
686
687         snmp_pdu_create_response(pdu, resp);
688
689         if (snmp_pdu_encode_header(resp_b, resp))
690                 return (SNMP_RET_IGN);
691
692         /*
693          * 1. Find all nodes, check that they are writeable and
694          *    that the syntax is ok, copy over the binding to the response.
695          */
696         for (i = 0; i < pdu->nbindings; i++) {
697                 b = &pdu->bindings[i];
698
699                 if ((np = context.node[i] = find_node(b, &except)) == NULL) {
700                         /* not found altogether or LEAF with wrong index */
701                         if (TR(SET))
702                                 snmp_debug("set: node not found %s",
703                                     asn_oid2str_r(&b->var, oidbuf));
704                         if (pdu->version == SNMP_V1) {
705                                 pdu->error_index = i + 1;
706                                 pdu->error_status = SNMP_ERR_NOSUCHNAME;
707                         } else if ((np = find_subnode(b)) != NULL) {
708                                 /* 2. intermediate object */
709                                 pdu->error_index = i + 1;
710                                 pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
711                         } else if (except == SNMP_SYNTAX_NOSUCHOBJECT) {
712                                 pdu->error_index = i + 1;
713                                 pdu->error_status = SNMP_ERR_NO_ACCESS;
714                         } else {
715                                 pdu->error_index = i + 1;
716                                 pdu->error_status = SNMP_ERR_NO_CREATION;
717                         }
718                         snmp_pdu_free(resp);
719                         return (SNMP_RET_ERR);
720                 }
721                 /*
722                  * 2. write/createable?
723                  * Can check this for leafs only, because in v2 we have
724                  * to differentiate between NOT_WRITEABLE and NO_CREATION
725                  * and only the action routine for COLUMNS knows, whether
726                  * a column exists.
727                  */
728                 if (np->type == SNMP_NODE_LEAF &&
729                     !(np->flags & SNMP_NODE_CANSET)) {
730                         if (pdu->version == SNMP_V1) {
731                                 pdu->error_index = i + 1;
732                                 pdu->error_status = SNMP_ERR_NOSUCHNAME;
733                         } else {
734                                 pdu->error_index = i + 1;
735                                 pdu->error_status = SNMP_ERR_NOT_WRITEABLE;
736                         }
737                         snmp_pdu_free(resp);
738                         return (SNMP_RET_ERR);
739                 }
740                 /*
741                  * 3. Ensure the right syntax
742                  */
743                 if (np->syntax != b->syntax) {
744                         if (pdu->version == SNMP_V1) {
745                                 pdu->error_index = i + 1;
746                                 pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */
747                         } else {
748                                 pdu->error_index = i + 1;
749                                 pdu->error_status = SNMP_ERR_WRONG_TYPE;
750                         }
751                         snmp_pdu_free(resp);
752                         return (SNMP_RET_ERR);
753                 }
754                 /*
755                  * 4. Copy binding
756                  */
757                 if (snmp_value_copy(&resp->bindings[i], b)) {
758                         pdu->error_index = i + 1;
759                         pdu->error_status = SNMP_ERR_GENERR;
760                         snmp_pdu_free(resp);
761                         return (SNMP_RET_ERR);
762                 }
763                 asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]);
764                 if (asnerr == ASN_ERR_EOBUF) {
765                         pdu->error_index = i + 1;
766                         pdu->error_status = SNMP_ERR_TOOBIG;
767                         snmp_pdu_free(resp);
768                         return (SNMP_RET_ERR);
769                 } else if (asnerr != ASN_ERR_OK) {
770                         pdu->error_index = i + 1;
771                         pdu->error_status = SNMP_ERR_GENERR;
772                         snmp_pdu_free(resp);
773                         return (SNMP_RET_ERR);
774                 }
775                 resp->nbindings++;
776         }
777
778         context.ctx.code = SNMP_RET_OK;
779
780         /*
781          * 2. Call the SET method for each node. If a SET fails, rollback
782          *    everything. Map error codes depending on the version.
783          */
784         for (i = 0; i < pdu->nbindings; i++) {
785                 b = &pdu->bindings[i];
786                 np = context.node[i];
787
788                 context.ctx.var_index = i + 1;
789                 context.ctx.scratch = &context.scratch[i];
790
791                 ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
792                     SNMP_OP_SET);
793
794                 if (TR(SET))
795                         snmp_debug("set: action %s returns %d", np->name, ret);
796
797                 if (pdu->version == SNMP_V1) {
798                         switch (ret) {
799                           case SNMP_ERR_NO_ACCESS:
800                                 ret = SNMP_ERR_NOSUCHNAME;
801                                 break;
802                           case SNMP_ERR_WRONG_TYPE:
803                                 /* should no happen */
804                                 ret = SNMP_ERR_BADVALUE;
805                                 break;
806                           case SNMP_ERR_WRONG_LENGTH:
807                                 ret = SNMP_ERR_BADVALUE;
808                                 break;
809                           case SNMP_ERR_WRONG_ENCODING:
810                                 /* should not happen */
811                                 ret = SNMP_ERR_BADVALUE;
812                                 break;
813                           case SNMP_ERR_WRONG_VALUE:
814                                 ret = SNMP_ERR_BADVALUE;
815                                 break;
816                           case SNMP_ERR_NO_CREATION:
817                                 ret = SNMP_ERR_NOSUCHNAME;
818                                 break;
819                           case SNMP_ERR_INCONS_VALUE:
820                                 ret = SNMP_ERR_BADVALUE;
821                                 break;
822                           case SNMP_ERR_RES_UNAVAIL:
823                                 ret = SNMP_ERR_GENERR;
824                                 break;
825                           case SNMP_ERR_COMMIT_FAILED:
826                                 ret = SNMP_ERR_GENERR;
827                                 break;
828                           case SNMP_ERR_UNDO_FAILED:
829                                 ret = SNMP_ERR_GENERR;
830                                 break;
831                           case SNMP_ERR_AUTH_ERR:
832                                 /* should not happen */
833                                 ret = SNMP_ERR_GENERR;
834                                 break;
835                           case SNMP_ERR_NOT_WRITEABLE:
836                                 ret = SNMP_ERR_NOSUCHNAME;
837                                 break;
838                           case SNMP_ERR_INCONS_NAME:
839                                 ret = SNMP_ERR_BADVALUE;
840                                 break;
841                         }
842                 }
843                 if (ret != SNMP_ERR_NOERROR) {
844                         pdu->error_index = i + 1;
845                         pdu->error_status = ret;
846
847                         rollback(&context, pdu, i);
848                         snmp_pdu_free(resp);
849
850                         context.ctx.code = SNMP_RET_ERR;
851
852                         goto errout;
853                 }
854         }
855
856         /*
857          * 3. Call dependencies
858          */
859         if (TR(SET))
860                 snmp_debug("set: set operations ok");
861
862         if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) {
863                 pdu->error_status = ret;
864                 pdu->error_index = context.ctx.var_index;
865
866                 if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) {
867                         if (pdu->version != SNMP_V1) {
868                                 pdu->error_status = SNMP_ERR_UNDO_FAILED;
869                                 pdu->error_index = 0;
870                         }
871                 }
872                 rollback(&context, pdu, i);
873                 snmp_pdu_free(resp);
874
875                 context.ctx.code = SNMP_RET_ERR;
876
877                 goto errout;
878         }
879
880         /*
881          * 4. Commit and copy values from the original packet to the response.
882          *    This is not the commit operation from RFC 1905 but rather an
883          *    'FREE RESOURCES' operation. It shouldn't fail.
884          */
885         if (TR(SET))
886                 snmp_debug("set: commiting");
887
888         for (i = 0; i < pdu->nbindings; i++) {
889                 b = &resp->bindings[i];
890                 np = context.node[i];
891
892                 context.ctx.var_index = i + 1;
893                 context.ctx.scratch = &context.scratch[i];
894
895                 ret = (*np->op)(&context.ctx, b, np->oid.len, np->index,
896                     SNMP_OP_COMMIT);
897
898                 if (ret != SNMP_ERR_NOERROR)
899                         snmp_error("set: commit failed (%d) on"
900                             " variable %s index %u", ret,
901                             asn_oid2str_r(&b->var, oidbuf), i);
902         }
903
904         if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) {
905                 snmp_error("set: fix_encoding failed");
906                 snmp_pdu_free(resp);
907                 context.ctx.code = SNMP_RET_IGN;
908         }
909
910         /*
911          * Done
912          */
913   errout:
914         snmp_dep_finish(&context.ctx);
915
916         if (TR(SET))
917                 snmp_debug("set: returning %d", context.ctx.code);
918
919         return (context.ctx.code);
920 }
921 /*
922  * Lookup a dependency. If it doesn't exist, create one
923  */
924 struct snmp_dependency *
925 snmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj,
926     const struct asn_oid *idx, size_t len, snmp_depop_t func)
927 {
928         struct context *context;
929         struct depend *d;
930
931         context = (struct context *)(void *)
932             ((char *)ctx - offsetof(struct context, ctx));
933         if (TR(DEPEND)) {
934                 snmp_debug("depend: looking for %s", asn_oid2str(obj));
935                 if (idx)
936                         snmp_debug("depend: index is %s", asn_oid2str(idx));
937         }
938         TAILQ_FOREACH(d, &context->dlist, link)
939                 if (asn_compare_oid(obj, &d->dep.obj) == 0 &&
940                     ((idx == NULL && d->dep.idx.len == 0) ||
941                      (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) {
942                         if(TR(DEPEND))
943                                 snmp_debug("depend: found");
944                         return (&d->dep);
945                 }
946
947         if(TR(DEPEND))
948                 snmp_debug("depend: creating");
949
950         if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL)
951                 return (NULL);
952         memset(&d->dep, 0, len);
953
954         d->dep.obj = *obj;
955         if (idx == NULL)
956                 d->dep.idx.len = 0;
957         else
958                 d->dep.idx = *idx;
959         d->len = len;
960         d->func = func;
961
962         TAILQ_INSERT_TAIL(&context->dlist, d, link);
963
964         return (&d->dep);
965 }
966
967 /*
968  * Make an error response from a PDU. We do this without decoding the
969  * variable bindings. This means we can sent the junk back to a caller
970  * that has sent us junk in the first place.
971  */
972 enum snmp_ret
973 snmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b,
974     struct asn_buf *resp_b)
975 {
976         u_char type;
977         asn_len_t len;
978         struct snmp_pdu resp;
979         enum asn_err err;
980         enum snmp_code code;
981
982         snmp_pdu_create_response(pdu, &resp);
983
984         if ((code = snmp_pdu_decode_header(pdu_b, &resp)) != SNMP_CODE_OK)
985                 return (SNMP_RET_IGN);
986
987         if (pdu->version == SNMP_V3) {
988                 if (resp.user.priv_proto != SNMP_PRIV_NOPRIV &&
989                    (asn_get_header(pdu_b, &type, &resp.scoped_len) != ASN_ERR_OK
990                    || type != ASN_TYPE_OCTETSTRING)) {
991                         snmp_error("cannot decode encrypted pdu");
992                         return (SNMP_RET_IGN);
993                 }
994
995                 if (asn_get_sequence(pdu_b, &len) != ASN_ERR_OK) {
996                         snmp_error("cannot decode scoped pdu header");
997                         return (SNMP_RET_IGN);
998                 }
999
1000                 len = SNMP_ENGINE_ID_SIZ;
1001                 if (asn_get_octetstring(pdu_b, (u_char *)resp.context_engine,
1002                     &len) != ASN_ERR_OK) {
1003                         snmp_error("cannot decode msg context engine");
1004                         return (SNMP_RET_IGN);
1005                 }
1006                 resp.context_engine_len = len;
1007                 len = SNMP_CONTEXT_NAME_SIZ;
1008                 if (asn_get_octetstring(pdu_b, (u_char *)resp.context_name,
1009                     &len) != ASN_ERR_OK) {
1010                         snmp_error("cannot decode msg context name");
1011                         return (SNMP_RET_IGN);
1012                 }
1013                 resp.context_name[len] = '\0';
1014         }
1015
1016
1017         if (asn_get_header(pdu_b, &type, &len) != ASN_ERR_OK) {
1018                 snmp_error("cannot get pdu header");
1019                 return (SNMP_RET_IGN);
1020         }
1021
1022         if ((type & ~ASN_TYPE_MASK) !=
1023             (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT)) {
1024                 snmp_error("bad pdu header tag");
1025                 return (SNMP_RET_IGN);
1026         }
1027
1028         err = snmp_parse_pdus_hdr(pdu_b, &resp, &len);
1029         if (ASN_ERR_STOPPED(err))
1030                 return (SNMP_RET_IGN);
1031         if (pdu_b->asn_len < len)
1032                 return (SNMP_RET_IGN);
1033         pdu_b->asn_len = len;
1034
1035         /* now we have the bindings left - construct new message */
1036         resp.error_status = pdu->error_status;
1037         resp.error_index = pdu->error_index;
1038         resp.type = SNMP_PDU_RESPONSE;
1039
1040         code = snmp_pdu_encode_header(resp_b, &resp);
1041         if (code != SNMP_CODE_OK)
1042                 return (SNMP_RET_IGN);
1043
1044         if (pdu_b->asn_len > resp_b->asn_len)
1045                 /* too short */
1046                 return (SNMP_RET_IGN);
1047         (void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len);
1048         resp_b->asn_len -= pdu_b->asn_len;
1049         resp_b->asn_ptr += pdu_b->asn_len;
1050
1051         code = snmp_fix_encoding(resp_b, &resp);
1052         if (code != SNMP_CODE_OK)
1053                 return (SNMP_RET_IGN);
1054
1055         return (SNMP_RET_OK);
1056 }
1057
1058 static void
1059 snmp_debug_func(const char *fmt, ...)
1060 {
1061         va_list ap;
1062
1063         va_start(ap, fmt);
1064         vfprintf(stderr, fmt, ap);
1065         va_end(ap);
1066         fprintf(stderr, "\n");
1067 }