Home | History | Annotate | Download | only in snmp
      1 /**
      2  * @file
      3  * SNMP input message processing (RFC1157).
      4  */
      5 
      6 /*
      7  * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
      8  * All rights reserved.
      9  *
     10  * Redistribution and use in source and binary forms, with or without modification,
     11  * are permitted provided that the following conditions are met:
     12  *
     13  * 1. Redistributions of source code must retain the above copyright notice,
     14  *    this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright notice,
     16  *    this list of conditions and the following disclaimer in the documentation
     17  *    and/or other materials provided with the distribution.
     18  * 3. The name of the author may not be used to endorse or promote products
     19  *    derived from this software without specific prior written permission.
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     22  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     23  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
     24  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
     26  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
     29  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
     30  * OF SUCH DAMAGE.
     31  *
     32  * Author: Christiaan Simons <christiaan.simons (at) axon.tv>
     33  */
     34 
     35 #include "lwip/opt.h"
     36 
     37 #if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
     38 
     39 #include "lwip/snmp.h"
     40 #include "lwip/snmp_asn1.h"
     41 #include "lwip/snmp_msg.h"
     42 #include "lwip/snmp_structs.h"
     43 #include "lwip/ip_addr.h"
     44 #include "lwip/memp.h"
     45 #include "lwip/udp.h"
     46 #include "lwip/stats.h"
     47 
     48 #include <string.h>
     49 
     50 /* public (non-static) constants */
     51 /** SNMP v1 == 0 */
     52 const s32_t snmp_version = 0;
     53 /** default SNMP community string */
     54 const char snmp_publiccommunity[7] = "public";
     55 
     56 /* statically allocated buffers for SNMP_CONCURRENT_REQUESTS */
     57 struct snmp_msg_pstat msg_input_list[SNMP_CONCURRENT_REQUESTS];
     58 /* UDP Protocol Control Block */
     59 struct udp_pcb *snmp1_pcb;
     60 
     61 static void snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
     62 static err_t snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat);
     63 static err_t snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat);
     64 
     65 
     66 /**
     67  * Starts SNMP Agent.
     68  * Allocates UDP pcb and binds it to IP_ADDR_ANY port 161.
     69  */
     70 void
     71 snmp_init(void)
     72 {
     73   struct snmp_msg_pstat *msg_ps;
     74   u8_t i;
     75 
     76   snmp1_pcb = udp_new();
     77   if (snmp1_pcb != NULL)
     78   {
     79     udp_recv(snmp1_pcb, snmp_recv, (void *)SNMP_IN_PORT);
     80     udp_bind(snmp1_pcb, IP_ADDR_ANY, SNMP_IN_PORT);
     81   }
     82   msg_ps = &msg_input_list[0];
     83   for (i=0; i<SNMP_CONCURRENT_REQUESTS; i++)
     84   {
     85     msg_ps->state = SNMP_MSG_EMPTY;
     86     msg_ps->error_index = 0;
     87     msg_ps->error_status = SNMP_ES_NOERROR;
     88     msg_ps++;
     89   }
     90   trap_msg.pcb = snmp1_pcb;
     91 
     92 #ifdef SNMP_PRIVATE_MIB_INIT
     93   /* If defined, rhis must be a function-like define to initialize the
     94    * private MIB after the stack has been initialized.
     95    * The private MIB can also be initialized in tcpip_callback (or after
     96    * the stack is initialized), this define is only for convenience. */
     97   SNMP_PRIVATE_MIB_INIT();
     98 #endif /* SNMP_PRIVATE_MIB_INIT */
     99 
    100   /* The coldstart trap will only be output
    101      if our outgoing interface is up & configured  */
    102   snmp_coldstart_trap();
    103 }
    104 
    105 static void
    106 snmp_error_response(struct snmp_msg_pstat *msg_ps, u8_t error)
    107 {
    108   snmp_varbind_list_free(&msg_ps->outvb);
    109   msg_ps->outvb = msg_ps->invb;
    110   msg_ps->invb.head = NULL;
    111   msg_ps->invb.tail = NULL;
    112   msg_ps->invb.count = 0;
    113   msg_ps->error_status = error;
    114   msg_ps->error_index = 1 + msg_ps->vb_idx;
    115   snmp_send_response(msg_ps);
    116   snmp_varbind_list_free(&msg_ps->outvb);
    117   msg_ps->state = SNMP_MSG_EMPTY;
    118 }
    119 
    120 static void
    121 snmp_ok_response(struct snmp_msg_pstat *msg_ps)
    122 {
    123   err_t err_ret;
    124 
    125   err_ret = snmp_send_response(msg_ps);
    126   if (err_ret == ERR_MEM)
    127   {
    128     /* serious memory problem, can't return tooBig */
    129   }
    130   else
    131   {
    132     LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event = %"S32_F"\n",msg_ps->error_status));
    133   }
    134   /* free varbinds (if available) */
    135   snmp_varbind_list_free(&msg_ps->invb);
    136   snmp_varbind_list_free(&msg_ps->outvb);
    137   msg_ps->state = SNMP_MSG_EMPTY;
    138 }
    139 
    140 /**
    141  * Service an internal or external event for SNMP GET.
    142  *
    143  * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
    144  * @param msg_ps points to the assosicated message process state
    145  */
    146 static void
    147 snmp_msg_get_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
    148 {
    149   LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_get_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
    150 
    151   if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
    152   {
    153     struct mib_external_node *en;
    154     struct snmp_name_ptr np;
    155 
    156     /* get_object_def() answer*/
    157     en = msg_ps->ext_mib_node;
    158     np = msg_ps->ext_name_ptr;
    159 
    160     /* translate answer into a known lifeform */
    161     en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
    162     if ((msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) &&
    163         (msg_ps->ext_object_def.access & MIB_ACCESS_READ))
    164     {
    165       msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE;
    166       en->get_value_q(request_id, &msg_ps->ext_object_def);
    167     }
    168     else
    169     {
    170       en->get_object_def_pc(request_id, np.ident_len, np.ident);
    171       /* search failed, object id points to unknown object (nosuchname) */
    172       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
    173     }
    174   }
    175   else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE)
    176   {
    177     struct mib_external_node *en;
    178     struct snmp_varbind *vb;
    179 
    180     /* get_value() answer */
    181     en = msg_ps->ext_mib_node;
    182 
    183     /* allocate output varbind */
    184     vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
    185     LWIP_ASSERT("vb != NULL",vb != NULL);
    186     if (vb != NULL)
    187     {
    188       vb->next = NULL;
    189       vb->prev = NULL;
    190 
    191       /* move name from invb to outvb */
    192       vb->ident = msg_ps->vb_ptr->ident;
    193       vb->ident_len = msg_ps->vb_ptr->ident_len;
    194       /* ensure this memory is refereced once only */
    195       msg_ps->vb_ptr->ident = NULL;
    196       msg_ps->vb_ptr->ident_len = 0;
    197 
    198       vb->value_type = msg_ps->ext_object_def.asn_type;
    199       LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff);
    200       vb->value_len = (u8_t)msg_ps->ext_object_def.v_len;
    201       if (vb->value_len > 0)
    202       {
    203         LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE);
    204         vb->value = memp_malloc(MEMP_SNMP_VALUE);
    205         LWIP_ASSERT("vb->value != NULL",vb->value != NULL);
    206         if (vb->value != NULL)
    207         {
    208           en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value);
    209           snmp_varbind_tail_add(&msg_ps->outvb, vb);
    210           /* search again (if vb_idx < msg_ps->invb.count) */
    211           msg_ps->state = SNMP_MSG_SEARCH_OBJ;
    212           msg_ps->vb_idx += 1;
    213         }
    214         else
    215         {
    216           en->get_value_pc(request_id, &msg_ps->ext_object_def);
    217           LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no variable space\n"));
    218           msg_ps->vb_ptr->ident = vb->ident;
    219           msg_ps->vb_ptr->ident_len = vb->ident_len;
    220           memp_free(MEMP_SNMP_VARBIND, vb);
    221           snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
    222         }
    223       }
    224       else
    225       {
    226         /* vb->value_len == 0, empty value (e.g. empty string) */
    227         en->get_value_a(request_id, &msg_ps->ext_object_def, 0, NULL);
    228         vb->value = NULL;
    229         snmp_varbind_tail_add(&msg_ps->outvb, vb);
    230         /* search again (if vb_idx < msg_ps->invb.count) */
    231         msg_ps->state = SNMP_MSG_SEARCH_OBJ;
    232         msg_ps->vb_idx += 1;
    233       }
    234     }
    235     else
    236     {
    237       en->get_value_pc(request_id, &msg_ps->ext_object_def);
    238       LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no outvb space\n"));
    239       snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
    240     }
    241   }
    242 
    243   while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
    244          (msg_ps->vb_idx < msg_ps->invb.count))
    245   {
    246     struct mib_node *mn;
    247     struct snmp_name_ptr np;
    248 
    249     if (msg_ps->vb_idx == 0)
    250     {
    251       msg_ps->vb_ptr = msg_ps->invb.head;
    252     }
    253     else
    254     {
    255       msg_ps->vb_ptr = msg_ps->vb_ptr->next;
    256     }
    257     /** test object identifier for .iso.org.dod.internet prefix */
    258     if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len,  msg_ps->vb_ptr->ident))
    259     {
    260       mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
    261                              msg_ps->vb_ptr->ident + 4, &np);
    262       if (mn != NULL)
    263       {
    264         if (mn->node_type == MIB_NODE_EX)
    265         {
    266           /* external object */
    267           struct mib_external_node *en = (struct mib_external_node*)mn;
    268 
    269           msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
    270           /* save en && args in msg_ps!! */
    271           msg_ps->ext_mib_node = en;
    272           msg_ps->ext_name_ptr = np;
    273 
    274           en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
    275         }
    276         else
    277         {
    278           /* internal object */
    279           struct obj_def object_def;
    280 
    281           msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
    282           mn->get_object_def(np.ident_len, np.ident, &object_def);
    283           if ((object_def.instance != MIB_OBJECT_NONE) &&
    284             (object_def.access & MIB_ACCESS_READ))
    285           {
    286             mn = mn;
    287           }
    288           else
    289           {
    290             /* search failed, object id points to unknown object (nosuchname) */
    291             mn =  NULL;
    292           }
    293           if (mn != NULL)
    294           {
    295             struct snmp_varbind *vb;
    296 
    297             msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE;
    298             /* allocate output varbind */
    299             vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
    300             LWIP_ASSERT("vb != NULL",vb != NULL);
    301             if (vb != NULL)
    302             {
    303               vb->next = NULL;
    304               vb->prev = NULL;
    305 
    306               /* move name from invb to outvb */
    307               vb->ident = msg_ps->vb_ptr->ident;
    308               vb->ident_len = msg_ps->vb_ptr->ident_len;
    309               /* ensure this memory is refereced once only */
    310               msg_ps->vb_ptr->ident = NULL;
    311               msg_ps->vb_ptr->ident_len = 0;
    312 
    313               vb->value_type = object_def.asn_type;
    314               LWIP_ASSERT("invalid length", object_def.v_len <= 0xff);
    315               vb->value_len = (u8_t)object_def.v_len;
    316               if (vb->value_len > 0)
    317               {
    318                 LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low",
    319                   vb->value_len <= SNMP_MAX_VALUE_SIZE);
    320                 vb->value = memp_malloc(MEMP_SNMP_VALUE);
    321                 LWIP_ASSERT("vb->value != NULL",vb->value != NULL);
    322                 if (vb->value != NULL)
    323                 {
    324                   mn->get_value(&object_def, vb->value_len, vb->value);
    325                   snmp_varbind_tail_add(&msg_ps->outvb, vb);
    326                   msg_ps->state = SNMP_MSG_SEARCH_OBJ;
    327                   msg_ps->vb_idx += 1;
    328                 }
    329                 else
    330                 {
    331                   LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate variable space\n"));
    332                   msg_ps->vb_ptr->ident = vb->ident;
    333                   msg_ps->vb_ptr->ident_len = vb->ident_len;
    334                   memp_free(MEMP_SNMP_VARBIND, vb);
    335                   snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
    336                 }
    337               }
    338               else
    339               {
    340                 /* vb->value_len == 0, empty value (e.g. empty string) */
    341                 vb->value = NULL;
    342                 snmp_varbind_tail_add(&msg_ps->outvb, vb);
    343                 msg_ps->state = SNMP_MSG_SEARCH_OBJ;
    344                 msg_ps->vb_idx += 1;
    345               }
    346             }
    347             else
    348             {
    349               LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate outvb space\n"));
    350               snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
    351             }
    352           }
    353         }
    354       }
    355     }
    356     else
    357     {
    358       mn = NULL;
    359     }
    360     if (mn == NULL)
    361     {
    362       /* mn == NULL, noSuchName */
    363       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
    364     }
    365   }
    366   if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
    367       (msg_ps->vb_idx == msg_ps->invb.count))
    368   {
    369     snmp_ok_response(msg_ps);
    370   }
    371 }
    372 
    373 /**
    374  * Service an internal or external event for SNMP GETNEXT.
    375  *
    376  * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
    377  * @param msg_ps points to the assosicated message process state
    378  */
    379 static void
    380 snmp_msg_getnext_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
    381 {
    382   LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
    383 
    384   if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
    385   {
    386     struct mib_external_node *en;
    387 
    388     /* get_object_def() answer*/
    389     en = msg_ps->ext_mib_node;
    390 
    391     /* translate answer into a known lifeform */
    392     en->get_object_def_a(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1], &msg_ps->ext_object_def);
    393     if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
    394     {
    395       msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE;
    396       en->get_value_q(request_id, &msg_ps->ext_object_def);
    397     }
    398     else
    399     {
    400       en->get_object_def_pc(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1]);
    401       /* search failed, object id points to unknown object (nosuchname) */
    402       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
    403     }
    404   }
    405   else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE)
    406   {
    407     struct mib_external_node *en;
    408     struct snmp_varbind *vb;
    409 
    410     /* get_value() answer */
    411     en = msg_ps->ext_mib_node;
    412 
    413     LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff);
    414     vb = snmp_varbind_alloc(&msg_ps->ext_oid,
    415                             msg_ps->ext_object_def.asn_type,
    416                             (u8_t)msg_ps->ext_object_def.v_len);
    417     if (vb != NULL)
    418     {
    419       en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value);
    420       snmp_varbind_tail_add(&msg_ps->outvb, vb);
    421       msg_ps->state = SNMP_MSG_SEARCH_OBJ;
    422       msg_ps->vb_idx += 1;
    423     }
    424     else
    425     {
    426       en->get_value_pc(request_id, &msg_ps->ext_object_def);
    427       LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: couldn't allocate outvb space\n"));
    428       snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
    429     }
    430   }
    431 
    432   while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
    433          (msg_ps->vb_idx < msg_ps->invb.count))
    434   {
    435     struct mib_node *mn;
    436     struct snmp_obj_id oid;
    437 
    438     if (msg_ps->vb_idx == 0)
    439     {
    440       msg_ps->vb_ptr = msg_ps->invb.head;
    441     }
    442     else
    443     {
    444       msg_ps->vb_ptr = msg_ps->vb_ptr->next;
    445     }
    446     if (snmp_iso_prefix_expand(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident, &oid))
    447     {
    448       if (msg_ps->vb_ptr->ident_len > 3)
    449       {
    450         /* can offset ident_len and ident */
    451         mn = snmp_expand_tree((struct mib_node*)&internet,
    452                               msg_ps->vb_ptr->ident_len - 4,
    453                               msg_ps->vb_ptr->ident + 4, &oid);
    454       }
    455       else
    456       {
    457         /* can't offset ident_len -4, ident + 4 */
    458         mn = snmp_expand_tree((struct mib_node*)&internet, 0, NULL, &oid);
    459       }
    460     }
    461     else
    462     {
    463       mn = NULL;
    464     }
    465     if (mn != NULL)
    466     {
    467       if (mn->node_type == MIB_NODE_EX)
    468       {
    469         /* external object */
    470         struct mib_external_node *en = (struct mib_external_node*)mn;
    471 
    472         msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
    473         /* save en && args in msg_ps!! */
    474         msg_ps->ext_mib_node = en;
    475         msg_ps->ext_oid = oid;
    476 
    477         en->get_object_def_q(en->addr_inf, request_id, 1, &oid.id[oid.len - 1]);
    478       }
    479       else
    480       {
    481         /* internal object */
    482         struct obj_def object_def;
    483         struct snmp_varbind *vb;
    484 
    485         msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
    486         mn->get_object_def(1, &oid.id[oid.len - 1], &object_def);
    487 
    488         LWIP_ASSERT("invalid length", object_def.v_len <= 0xff);
    489         vb = snmp_varbind_alloc(&oid, object_def.asn_type, (u8_t)object_def.v_len);
    490         if (vb != NULL)
    491         {
    492           msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE;
    493           mn->get_value(&object_def, object_def.v_len, vb->value);
    494           snmp_varbind_tail_add(&msg_ps->outvb, vb);
    495           msg_ps->state = SNMP_MSG_SEARCH_OBJ;
    496           msg_ps->vb_idx += 1;
    497         }
    498         else
    499         {
    500           LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv couldn't allocate outvb space\n"));
    501           snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
    502         }
    503       }
    504     }
    505     if (mn == NULL)
    506     {
    507       /* mn == NULL, noSuchName */
    508       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
    509     }
    510   }
    511   if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
    512       (msg_ps->vb_idx == msg_ps->invb.count))
    513   {
    514     snmp_ok_response(msg_ps);
    515   }
    516 }
    517 
    518 /**
    519  * Service an internal or external event for SNMP SET.
    520  *
    521  * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
    522  * @param msg_ps points to the assosicated message process state
    523  */
    524 static void
    525 snmp_msg_set_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
    526 {
    527   LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_set_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
    528 
    529   if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
    530   {
    531     struct mib_external_node *en;
    532     struct snmp_name_ptr np;
    533 
    534     /* get_object_def() answer*/
    535     en = msg_ps->ext_mib_node;
    536     np = msg_ps->ext_name_ptr;
    537 
    538     /* translate answer into a known lifeform */
    539     en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
    540     if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
    541     {
    542       msg_ps->state = SNMP_MSG_EXTERNAL_SET_TEST;
    543       en->set_test_q(request_id, &msg_ps->ext_object_def);
    544     }
    545     else
    546     {
    547       en->get_object_def_pc(request_id, np.ident_len, np.ident);
    548       /* search failed, object id points to unknown object (nosuchname) */
    549       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
    550     }
    551   }
    552   else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_TEST)
    553   {
    554     struct mib_external_node *en;
    555 
    556     /* set_test() answer*/
    557     en = msg_ps->ext_mib_node;
    558 
    559     if (msg_ps->ext_object_def.access & MIB_ACCESS_WRITE)
    560     {
    561        if ((msg_ps->ext_object_def.asn_type == msg_ps->vb_ptr->value_type) &&
    562            (en->set_test_a(request_id,&msg_ps->ext_object_def,
    563                            msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0))
    564       {
    565         msg_ps->state = SNMP_MSG_SEARCH_OBJ;
    566         msg_ps->vb_idx += 1;
    567       }
    568       else
    569       {
    570         en->set_test_pc(request_id,&msg_ps->ext_object_def);
    571         /* bad value */
    572         snmp_error_response(msg_ps,SNMP_ES_BADVALUE);
    573       }
    574     }
    575     else
    576     {
    577       en->set_test_pc(request_id,&msg_ps->ext_object_def);
    578       /* object not available for set */
    579       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
    580     }
    581   }
    582   else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF_S)
    583   {
    584     struct mib_external_node *en;
    585     struct snmp_name_ptr np;
    586 
    587     /* get_object_def() answer*/
    588     en = msg_ps->ext_mib_node;
    589     np = msg_ps->ext_name_ptr;
    590 
    591     /* translate answer into a known lifeform */
    592     en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
    593     if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
    594     {
    595       msg_ps->state = SNMP_MSG_EXTERNAL_SET_VALUE;
    596       en->set_value_q(request_id, &msg_ps->ext_object_def,
    597                       msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value);
    598     }
    599     else
    600     {
    601       en->get_object_def_pc(request_id, np.ident_len, np.ident);
    602       /* set_value failed, object has disappeared for some odd reason?? */
    603       snmp_error_response(msg_ps,SNMP_ES_GENERROR);
    604     }
    605   }
    606   else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_VALUE)
    607   {
    608     struct mib_external_node *en;
    609 
    610     /** set_value_a() */
    611     en = msg_ps->ext_mib_node;
    612     en->set_value_a(request_id, &msg_ps->ext_object_def,
    613       msg_ps->vb_ptr->value_len, msg_ps->vb_ptr->value);
    614 
    615     /** @todo use set_value_pc() if toobig */
    616     msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
    617     msg_ps->vb_idx += 1;
    618   }
    619 
    620   /* test all values before setting */
    621   while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
    622          (msg_ps->vb_idx < msg_ps->invb.count))
    623   {
    624     struct mib_node *mn;
    625     struct snmp_name_ptr np;
    626 
    627     if (msg_ps->vb_idx == 0)
    628     {
    629       msg_ps->vb_ptr = msg_ps->invb.head;
    630     }
    631     else
    632     {
    633       msg_ps->vb_ptr = msg_ps->vb_ptr->next;
    634     }
    635     /** test object identifier for .iso.org.dod.internet prefix */
    636     if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len,  msg_ps->vb_ptr->ident))
    637     {
    638       mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
    639                              msg_ps->vb_ptr->ident + 4, &np);
    640       if (mn != NULL)
    641       {
    642         if (mn->node_type == MIB_NODE_EX)
    643         {
    644           /* external object */
    645           struct mib_external_node *en = (struct mib_external_node*)mn;
    646 
    647           msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
    648           /* save en && args in msg_ps!! */
    649           msg_ps->ext_mib_node = en;
    650           msg_ps->ext_name_ptr = np;
    651 
    652           en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
    653         }
    654         else
    655         {
    656           /* internal object */
    657           struct obj_def object_def;
    658 
    659           msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
    660           mn->get_object_def(np.ident_len, np.ident, &object_def);
    661           if (object_def.instance != MIB_OBJECT_NONE)
    662           {
    663             mn = mn;
    664           }
    665           else
    666           {
    667             /* search failed, object id points to unknown object (nosuchname) */
    668             mn = NULL;
    669           }
    670           if (mn != NULL)
    671           {
    672             msg_ps->state = SNMP_MSG_INTERNAL_SET_TEST;
    673 
    674             if (object_def.access & MIB_ACCESS_WRITE)
    675             {
    676               if ((object_def.asn_type == msg_ps->vb_ptr->value_type) &&
    677                   (mn->set_test(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0))
    678               {
    679                 msg_ps->state = SNMP_MSG_SEARCH_OBJ;
    680                 msg_ps->vb_idx += 1;
    681               }
    682               else
    683               {
    684                 /* bad value */
    685                 snmp_error_response(msg_ps,SNMP_ES_BADVALUE);
    686               }
    687             }
    688             else
    689             {
    690               /* object not available for set */
    691               snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
    692             }
    693           }
    694         }
    695       }
    696     }
    697     else
    698     {
    699       mn = NULL;
    700     }
    701     if (mn == NULL)
    702     {
    703       /* mn == NULL, noSuchName */
    704       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
    705     }
    706   }
    707 
    708   if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
    709       (msg_ps->vb_idx == msg_ps->invb.count))
    710   {
    711     msg_ps->vb_idx = 0;
    712     msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
    713   }
    714 
    715   /* set all values "atomically" (be as "atomic" as possible) */
    716   while ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) &&
    717          (msg_ps->vb_idx < msg_ps->invb.count))
    718   {
    719     struct mib_node *mn;
    720     struct snmp_name_ptr np;
    721 
    722     if (msg_ps->vb_idx == 0)
    723     {
    724       msg_ps->vb_ptr = msg_ps->invb.head;
    725     }
    726     else
    727     {
    728       msg_ps->vb_ptr = msg_ps->vb_ptr->next;
    729     }
    730     /* skip iso prefix test, was done previously while settesting() */
    731     mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
    732                            msg_ps->vb_ptr->ident + 4, &np);
    733     /* check if object is still available
    734        (e.g. external hot-plug thingy present?) */
    735     if (mn != NULL)
    736     {
    737       if (mn->node_type == MIB_NODE_EX)
    738       {
    739         /* external object */
    740         struct mib_external_node *en = (struct mib_external_node*)mn;
    741 
    742         msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF_S;
    743         /* save en && args in msg_ps!! */
    744         msg_ps->ext_mib_node = en;
    745         msg_ps->ext_name_ptr = np;
    746 
    747         en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
    748       }
    749       else
    750       {
    751         /* internal object */
    752         struct obj_def object_def;
    753 
    754         msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF_S;
    755         mn->get_object_def(np.ident_len, np.ident, &object_def);
    756         msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
    757         mn->set_value(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value);
    758         msg_ps->vb_idx += 1;
    759       }
    760     }
    761   }
    762   if ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) &&
    763       (msg_ps->vb_idx == msg_ps->invb.count))
    764   {
    765     /* simply echo the input if we can set it
    766        @todo do we need to return the actual value?
    767        e.g. if value is silently modified or behaves sticky? */
    768     msg_ps->outvb = msg_ps->invb;
    769     msg_ps->invb.head = NULL;
    770     msg_ps->invb.tail = NULL;
    771     msg_ps->invb.count = 0;
    772     snmp_ok_response(msg_ps);
    773   }
    774 }
    775 
    776 
    777 /**
    778  * Handle one internal or external event.
    779  * Called for one async event. (recv external/private answer)
    780  *
    781  * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
    782  */
    783 void
    784 snmp_msg_event(u8_t request_id)
    785 {
    786   struct snmp_msg_pstat *msg_ps;
    787 
    788   if (request_id < SNMP_CONCURRENT_REQUESTS)
    789   {
    790     msg_ps = &msg_input_list[request_id];
    791     if (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ)
    792     {
    793       snmp_msg_getnext_event(request_id, msg_ps);
    794     }
    795     else if (msg_ps->rt == SNMP_ASN1_PDU_GET_REQ)
    796     {
    797       snmp_msg_get_event(request_id, msg_ps);
    798     }
    799     else if(msg_ps->rt == SNMP_ASN1_PDU_SET_REQ)
    800     {
    801       snmp_msg_set_event(request_id, msg_ps);
    802     }
    803   }
    804 }
    805 
    806 
    807 /* lwIP UDP receive callback function */
    808 static void
    809 snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
    810 {
    811   struct snmp_msg_pstat *msg_ps;
    812   u8_t req_idx;
    813   err_t err_ret;
    814   u16_t payload_len = p->tot_len;
    815   u16_t payload_ofs = 0;
    816   u16_t varbind_ofs = 0;
    817 
    818   /* suppress unused argument warning */
    819   LWIP_UNUSED_ARG(arg);
    820 
    821   /* traverse input message process list, look for SNMP_MSG_EMPTY */
    822   msg_ps = &msg_input_list[0];
    823   req_idx = 0;
    824   while ((req_idx < SNMP_CONCURRENT_REQUESTS) && (msg_ps->state != SNMP_MSG_EMPTY))
    825   {
    826     req_idx++;
    827     msg_ps++;
    828   }
    829   if (req_idx == SNMP_CONCURRENT_REQUESTS)
    830   {
    831     /* exceeding number of concurrent requests */
    832     pbuf_free(p);
    833     return;
    834   }
    835 
    836   /* accepting request */
    837   snmp_inc_snmpinpkts();
    838   /* record used 'protocol control block' */
    839   msg_ps->pcb = pcb;
    840   /* source address (network order) */
    841   msg_ps->sip = *addr;
    842   /* source port (host order (lwIP oddity)) */
    843   msg_ps->sp = port;
    844 
    845   /* check total length, version, community, pdu type */
    846   err_ret = snmp_pdu_header_check(p, payload_ofs, payload_len, &varbind_ofs, msg_ps);
    847   /* Only accept requests and requests without error (be robust) */
    848   /* Reject response and trap headers or error requests as input! */
    849   if ((err_ret != ERR_OK) ||
    850       ((msg_ps->rt != SNMP_ASN1_PDU_GET_REQ) &&
    851        (msg_ps->rt != SNMP_ASN1_PDU_GET_NEXT_REQ) &&
    852        (msg_ps->rt != SNMP_ASN1_PDU_SET_REQ)) ||
    853       ((msg_ps->error_status != SNMP_ES_NOERROR) ||
    854        (msg_ps->error_index != 0)) )
    855   {
    856     /* header check failed drop request silently, do not return error! */
    857     pbuf_free(p);
    858     LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check() failed\n"));
    859     return;
    860   }
    861   LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv ok, community %s\n", msg_ps->community));
    862 
    863   /* Builds a list of variable bindings. Copy the varbinds from the pbuf
    864     chain to glue them when these are divided over two or more pbuf's. */
    865   err_ret = snmp_pdu_dec_varbindlist(p, varbind_ofs, &varbind_ofs, msg_ps);
    866   /* we've decoded the incoming message, release input msg now */
    867   pbuf_free(p);
    868   if ((err_ret != ERR_OK) || (msg_ps->invb.count == 0))
    869   {
    870     /* varbind-list decode failed, or varbind list empty.
    871        drop request silently, do not return error!
    872        (errors are only returned for a specific varbind failure) */
    873     LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_dec_varbindlist() failed\n"));
    874     return;
    875   }
    876 
    877   msg_ps->error_status = SNMP_ES_NOERROR;
    878   msg_ps->error_index = 0;
    879   /* find object for each variable binding */
    880   msg_ps->state = SNMP_MSG_SEARCH_OBJ;
    881   /* first variable binding from list to inspect */
    882   msg_ps->vb_idx = 0;
    883 
    884   LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv varbind cnt=%"U16_F"\n",(u16_t)msg_ps->invb.count));
    885 
    886   /* handle input event and as much objects as possible in one go */
    887   snmp_msg_event(req_idx);
    888 }
    889 
    890 /**
    891  * Checks and decodes incoming SNMP message header, logs header errors.
    892  *
    893  * @param p points to pbuf chain of SNMP message (UDP payload)
    894  * @param ofs points to first octet of SNMP message
    895  * @param pdu_len the length of the UDP payload
    896  * @param ofs_ret returns the ofset of the variable bindings
    897  * @param m_stat points to the current message request state return
    898  * @return
    899  * - ERR_OK SNMP header is sane and accepted
    900  * - ERR_ARG SNMP header is either malformed or rejected
    901  */
    902 static err_t
    903 snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
    904 {
    905   err_t derr;
    906   u16_t len, ofs_base;
    907   u8_t  len_octets;
    908   u8_t  type;
    909   s32_t version;
    910 
    911   ofs_base = ofs;
    912   snmp_asn1_dec_type(p, ofs, &type);
    913   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    914   if ((derr != ERR_OK) ||
    915       (pdu_len != (1 + len_octets + len)) ||
    916       (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
    917   {
    918     snmp_inc_snmpinasnparseerrs();
    919     return ERR_ARG;
    920   }
    921   ofs += (1 + len_octets);
    922   snmp_asn1_dec_type(p, ofs, &type);
    923   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    924   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
    925   {
    926     /* can't decode or no integer (version) */
    927     snmp_inc_snmpinasnparseerrs();
    928     return ERR_ARG;
    929   }
    930   derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &version);
    931   if (derr != ERR_OK)
    932   {
    933     /* can't decode */
    934     snmp_inc_snmpinasnparseerrs();
    935     return ERR_ARG;
    936   }
    937   if (version != 0)
    938   {
    939     /* not version 1 */
    940     snmp_inc_snmpinbadversions();
    941     return ERR_ARG;
    942   }
    943   ofs += (1 + len_octets + len);
    944   snmp_asn1_dec_type(p, ofs, &type);
    945   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    946   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)))
    947   {
    948     /* can't decode or no octet string (community) */
    949     snmp_inc_snmpinasnparseerrs();
    950     return ERR_ARG;
    951   }
    952   derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, SNMP_COMMUNITY_STR_LEN, m_stat->community);
    953   if (derr != ERR_OK)
    954   {
    955     snmp_inc_snmpinasnparseerrs();
    956     return ERR_ARG;
    957   }
    958   /* add zero terminator */
    959   len = ((len < (SNMP_COMMUNITY_STR_LEN))?(len):(SNMP_COMMUNITY_STR_LEN));
    960   m_stat->community[len] = 0;
    961   m_stat->com_strlen = (u8_t)len;
    962   if (strncmp(snmp_publiccommunity, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0)
    963   {
    964     /** @todo: move this if we need to check more names */
    965     snmp_inc_snmpinbadcommunitynames();
    966     snmp_authfail_trap();
    967     return ERR_ARG;
    968   }
    969   ofs += (1 + len_octets + len);
    970   snmp_asn1_dec_type(p, ofs, &type);
    971   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
    972   if (derr != ERR_OK)
    973   {
    974     snmp_inc_snmpinasnparseerrs();
    975     return ERR_ARG;
    976   }
    977   switch(type)
    978   {
    979     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_REQ):
    980       /* GetRequest PDU */
    981       snmp_inc_snmpingetrequests();
    982       derr = ERR_OK;
    983       break;
    984     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_NEXT_REQ):
    985       /* GetNextRequest PDU */
    986       snmp_inc_snmpingetnexts();
    987       derr = ERR_OK;
    988       break;
    989     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP):
    990       /* GetResponse PDU */
    991       snmp_inc_snmpingetresponses();
    992       derr = ERR_ARG;
    993       break;
    994     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ):
    995       /* SetRequest PDU */
    996       snmp_inc_snmpinsetrequests();
    997       derr = ERR_OK;
    998       break;
    999     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP):
   1000       /* Trap PDU */
   1001       snmp_inc_snmpintraps();
   1002       derr = ERR_ARG;
   1003       break;
   1004     default:
   1005       snmp_inc_snmpinasnparseerrs();
   1006       derr = ERR_ARG;
   1007       break;
   1008   }
   1009   if (derr != ERR_OK)
   1010   {
   1011     /* unsupported input PDU for this agent (no parse error) */
   1012     return ERR_ARG;
   1013   }
   1014   m_stat->rt = type & 0x1F;
   1015   ofs += (1 + len_octets);
   1016   if (len != (pdu_len - (ofs - ofs_base)))
   1017   {
   1018     /* decoded PDU length does not equal actual payload length */
   1019     snmp_inc_snmpinasnparseerrs();
   1020     return ERR_ARG;
   1021   }
   1022   snmp_asn1_dec_type(p, ofs, &type);
   1023   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
   1024   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
   1025   {
   1026     /* can't decode or no integer (request ID) */
   1027     snmp_inc_snmpinasnparseerrs();
   1028     return ERR_ARG;
   1029   }
   1030   derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->rid);
   1031   if (derr != ERR_OK)
   1032   {
   1033     /* can't decode */
   1034     snmp_inc_snmpinasnparseerrs();
   1035     return ERR_ARG;
   1036   }
   1037   ofs += (1 + len_octets + len);
   1038   snmp_asn1_dec_type(p, ofs, &type);
   1039   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
   1040   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
   1041   {
   1042     /* can't decode or no integer (error-status) */
   1043     snmp_inc_snmpinasnparseerrs();
   1044     return ERR_ARG;
   1045   }
   1046   /* must be noError (0) for incoming requests.
   1047      log errors for mib-2 completeness and for debug purposes */
   1048   derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_status);
   1049   if (derr != ERR_OK)
   1050   {
   1051     /* can't decode */
   1052     snmp_inc_snmpinasnparseerrs();
   1053     return ERR_ARG;
   1054   }
   1055   switch (m_stat->error_status)
   1056   {
   1057     case SNMP_ES_TOOBIG:
   1058       snmp_inc_snmpintoobigs();
   1059       break;
   1060     case SNMP_ES_NOSUCHNAME:
   1061       snmp_inc_snmpinnosuchnames();
   1062       break;
   1063     case SNMP_ES_BADVALUE:
   1064       snmp_inc_snmpinbadvalues();
   1065       break;
   1066     case SNMP_ES_READONLY:
   1067       snmp_inc_snmpinreadonlys();
   1068       break;
   1069     case SNMP_ES_GENERROR:
   1070       snmp_inc_snmpingenerrs();
   1071       break;
   1072   }
   1073   ofs += (1 + len_octets + len);
   1074   snmp_asn1_dec_type(p, ofs, &type);
   1075   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
   1076   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
   1077   {
   1078     /* can't decode or no integer (error-index) */
   1079     snmp_inc_snmpinasnparseerrs();
   1080     return ERR_ARG;
   1081   }
   1082   /* must be 0 for incoming requests.
   1083      decode anyway to catch bad integers (and dirty tricks) */
   1084   derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_index);
   1085   if (derr != ERR_OK)
   1086   {
   1087     /* can't decode */
   1088     snmp_inc_snmpinasnparseerrs();
   1089     return ERR_ARG;
   1090   }
   1091   ofs += (1 + len_octets + len);
   1092   *ofs_ret = ofs;
   1093   return ERR_OK;
   1094 }
   1095 
   1096 static err_t
   1097 snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
   1098 {
   1099   err_t derr;
   1100   u16_t len, vb_len;
   1101   u8_t  len_octets;
   1102   u8_t type;
   1103 
   1104   /* variable binding list */
   1105   snmp_asn1_dec_type(p, ofs, &type);
   1106   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &vb_len);
   1107   if ((derr != ERR_OK) ||
   1108       (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
   1109   {
   1110     snmp_inc_snmpinasnparseerrs();
   1111     return ERR_ARG;
   1112   }
   1113   ofs += (1 + len_octets);
   1114 
   1115   /* start with empty list */
   1116   m_stat->invb.count = 0;
   1117   m_stat->invb.head = NULL;
   1118   m_stat->invb.tail = NULL;
   1119 
   1120   while (vb_len > 0)
   1121   {
   1122     struct snmp_obj_id oid, oid_value;
   1123     struct snmp_varbind *vb;
   1124 
   1125     snmp_asn1_dec_type(p, ofs, &type);
   1126     derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
   1127     if ((derr != ERR_OK) ||
   1128         (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)) ||
   1129         (len == 0) || (len > vb_len))
   1130     {
   1131       snmp_inc_snmpinasnparseerrs();
   1132       /* free varbinds (if available) */
   1133       snmp_varbind_list_free(&m_stat->invb);
   1134       return ERR_ARG;
   1135     }
   1136     ofs += (1 + len_octets);
   1137     vb_len -= (1 + len_octets);
   1138 
   1139     snmp_asn1_dec_type(p, ofs, &type);
   1140     derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
   1141     if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)))
   1142     {
   1143       /* can't decode object name length */
   1144       snmp_inc_snmpinasnparseerrs();
   1145       /* free varbinds (if available) */
   1146       snmp_varbind_list_free(&m_stat->invb);
   1147       return ERR_ARG;
   1148     }
   1149     derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid);
   1150     if (derr != ERR_OK)
   1151     {
   1152       /* can't decode object name */
   1153       snmp_inc_snmpinasnparseerrs();
   1154       /* free varbinds (if available) */
   1155       snmp_varbind_list_free(&m_stat->invb);
   1156       return ERR_ARG;
   1157     }
   1158     ofs += (1 + len_octets + len);
   1159     vb_len -= (1 + len_octets + len);
   1160 
   1161     snmp_asn1_dec_type(p, ofs, &type);
   1162     derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
   1163     if (derr != ERR_OK)
   1164     {
   1165       /* can't decode object value length */
   1166       snmp_inc_snmpinasnparseerrs();
   1167       /* free varbinds (if available) */
   1168       snmp_varbind_list_free(&m_stat->invb);
   1169       return ERR_ARG;
   1170     }
   1171 
   1172     switch (type)
   1173     {
   1174       case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
   1175         vb = snmp_varbind_alloc(&oid, type, sizeof(s32_t));
   1176         if (vb != NULL)
   1177         {
   1178           s32_t *vptr = (s32_t*)vb->value;
   1179 
   1180           derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, vptr);
   1181           snmp_varbind_tail_add(&m_stat->invb, vb);
   1182         }
   1183         else
   1184         {
   1185           derr = ERR_ARG;
   1186         }
   1187         break;
   1188       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
   1189       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
   1190       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
   1191         vb = snmp_varbind_alloc(&oid, type, sizeof(u32_t));
   1192         if (vb != NULL)
   1193         {
   1194           u32_t *vptr = (u32_t*)vb->value;
   1195 
   1196           derr = snmp_asn1_dec_u32t(p, ofs + 1 + len_octets, len, vptr);
   1197           snmp_varbind_tail_add(&m_stat->invb, vb);
   1198         }
   1199         else
   1200         {
   1201           derr = ERR_ARG;
   1202         }
   1203         break;
   1204       case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
   1205       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
   1206         LWIP_ASSERT("invalid length", len <= 0xff);
   1207         vb = snmp_varbind_alloc(&oid, type, (u8_t)len);
   1208         if (vb != NULL)
   1209         {
   1210           derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value);
   1211           snmp_varbind_tail_add(&m_stat->invb, vb);
   1212         }
   1213         else
   1214         {
   1215           derr = ERR_ARG;
   1216         }
   1217         break;
   1218       case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
   1219         vb = snmp_varbind_alloc(&oid, type, 0);
   1220         if (vb != NULL)
   1221         {
   1222           snmp_varbind_tail_add(&m_stat->invb, vb);
   1223           derr = ERR_OK;
   1224         }
   1225         else
   1226         {
   1227           derr = ERR_ARG;
   1228         }
   1229         break;
   1230       case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
   1231         derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid_value);
   1232         if (derr == ERR_OK)
   1233         {
   1234           vb = snmp_varbind_alloc(&oid, type, oid_value.len * sizeof(s32_t));
   1235           if (vb != NULL)
   1236           {
   1237             u8_t i = oid_value.len;
   1238             s32_t *vptr = (s32_t*)vb->value;
   1239 
   1240             while(i > 0)
   1241             {
   1242               i--;
   1243               vptr[i] = oid_value.id[i];
   1244             }
   1245             snmp_varbind_tail_add(&m_stat->invb, vb);
   1246             derr = ERR_OK;
   1247           }
   1248           else
   1249           {
   1250             derr = ERR_ARG;
   1251           }
   1252         }
   1253         break;
   1254       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
   1255         if (len == 4)
   1256         {
   1257           /* must be exactly 4 octets! */
   1258           vb = snmp_varbind_alloc(&oid, type, 4);
   1259           if (vb != NULL)
   1260           {
   1261             derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value);
   1262             snmp_varbind_tail_add(&m_stat->invb, vb);
   1263           }
   1264           else
   1265           {
   1266             derr = ERR_ARG;
   1267           }
   1268         }
   1269         else
   1270         {
   1271           derr = ERR_ARG;
   1272         }
   1273         break;
   1274       default:
   1275         derr = ERR_ARG;
   1276         break;
   1277     }
   1278     if (derr != ERR_OK)
   1279     {
   1280       snmp_inc_snmpinasnparseerrs();
   1281       /* free varbinds (if available) */
   1282       snmp_varbind_list_free(&m_stat->invb);
   1283       return ERR_ARG;
   1284     }
   1285     ofs += (1 + len_octets + len);
   1286     vb_len -= (1 + len_octets + len);
   1287   }
   1288 
   1289   if (m_stat->rt == SNMP_ASN1_PDU_SET_REQ)
   1290   {
   1291     snmp_add_snmpintotalsetvars(m_stat->invb.count);
   1292   }
   1293   else
   1294   {
   1295     snmp_add_snmpintotalreqvars(m_stat->invb.count);
   1296   }
   1297 
   1298   *ofs_ret = ofs;
   1299   return ERR_OK;
   1300 }
   1301 
   1302 struct snmp_varbind*
   1303 snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len)
   1304 {
   1305   struct snmp_varbind *vb;
   1306 
   1307   vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
   1308   LWIP_ASSERT("vb != NULL",vb != NULL);
   1309   if (vb != NULL)
   1310   {
   1311     u8_t i;
   1312 
   1313     vb->next = NULL;
   1314     vb->prev = NULL;
   1315     i = oid->len;
   1316     vb->ident_len = i;
   1317     if (i > 0)
   1318     {
   1319       LWIP_ASSERT("SNMP_MAX_TREE_DEPTH is configured too low", i <= SNMP_MAX_TREE_DEPTH);
   1320       /* allocate array of s32_t for our object identifier */
   1321       vb->ident = (s32_t*)memp_malloc(MEMP_SNMP_VALUE);
   1322       LWIP_ASSERT("vb->ident != NULL",vb->ident != NULL);
   1323       if (vb->ident == NULL)
   1324       {
   1325         memp_free(MEMP_SNMP_VARBIND, vb);
   1326         return NULL;
   1327       }
   1328       while(i > 0)
   1329       {
   1330         i--;
   1331         vb->ident[i] = oid->id[i];
   1332       }
   1333     }
   1334     else
   1335     {
   1336       /* i == 0, pass zero length object identifier */
   1337       vb->ident = NULL;
   1338     }
   1339     vb->value_type = type;
   1340     vb->value_len = len;
   1341     if (len > 0)
   1342     {
   1343       LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE);
   1344       /* allocate raw bytes for our object value */
   1345       vb->value = memp_malloc(MEMP_SNMP_VALUE);
   1346       LWIP_ASSERT("vb->value != NULL",vb->value != NULL);
   1347       if (vb->value == NULL)
   1348       {
   1349         if (vb->ident != NULL)
   1350         {
   1351           memp_free(MEMP_SNMP_VALUE, vb->ident);
   1352         }
   1353         memp_free(MEMP_SNMP_VARBIND, vb);
   1354         return NULL;
   1355       }
   1356     }
   1357     else
   1358     {
   1359       /* ASN1_NUL type, or zero length ASN1_OC_STR */
   1360       vb->value = NULL;
   1361     }
   1362   }
   1363   return vb;
   1364 }
   1365 
   1366 void
   1367 snmp_varbind_free(struct snmp_varbind *vb)
   1368 {
   1369   if (vb->value != NULL )
   1370   {
   1371     memp_free(MEMP_SNMP_VALUE, vb->value);
   1372   }
   1373   if (vb->ident != NULL )
   1374   {
   1375     memp_free(MEMP_SNMP_VALUE, vb->ident);
   1376   }
   1377   memp_free(MEMP_SNMP_VARBIND, vb);
   1378 }
   1379 
   1380 void
   1381 snmp_varbind_list_free(struct snmp_varbind_root *root)
   1382 {
   1383   struct snmp_varbind *vb, *prev;
   1384 
   1385   vb = root->tail;
   1386   while ( vb != NULL )
   1387   {
   1388     prev = vb->prev;
   1389     snmp_varbind_free(vb);
   1390     vb = prev;
   1391   }
   1392   root->count = 0;
   1393   root->head = NULL;
   1394   root->tail = NULL;
   1395 }
   1396 
   1397 void
   1398 snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb)
   1399 {
   1400   if (root->count == 0)
   1401   {
   1402     /* add first varbind to list */
   1403     root->head = vb;
   1404     root->tail = vb;
   1405   }
   1406   else
   1407   {
   1408     /* add nth varbind to list tail */
   1409     root->tail->next = vb;
   1410     vb->prev = root->tail;
   1411     root->tail = vb;
   1412   }
   1413   root->count += 1;
   1414 }
   1415 
   1416 struct snmp_varbind*
   1417 snmp_varbind_tail_remove(struct snmp_varbind_root *root)
   1418 {
   1419   struct snmp_varbind* vb;
   1420 
   1421   if (root->count > 0)
   1422   {
   1423     /* remove tail varbind */
   1424     vb = root->tail;
   1425     root->tail = vb->prev;
   1426     vb->prev->next = NULL;
   1427     root->count -= 1;
   1428   }
   1429   else
   1430   {
   1431     /* nothing to remove */
   1432     vb = NULL;
   1433   }
   1434   return vb;
   1435 }
   1436 
   1437 #endif /* LWIP_SNMP */
   1438