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