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