My fork of the HTTPServer (working)

Dependents:   DGWWebServer LAN2

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers msg_in.c Source File

msg_in.c

Go to the documentation of this file.
00001 /**
00002  * @file
00003  * SNMP input message processing (RFC1157).
00004  */
00005 
00006 /*
00007  * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
00008  * All rights reserved.
00009  *
00010  * Redistribution and use in source and binary forms, with or without modification,
00011  * are permitted provided that the following conditions are met:
00012  *
00013  * 1. Redistributions of source code must retain the above copyright notice,
00014  *    this list of conditions and the following disclaimer.
00015  * 2. Redistributions in binary form must reproduce the above copyright notice,
00016  *    this list of conditions and the following disclaimer in the documentation
00017  *    and/or other materials provided with the distribution.
00018  * 3. The name of the author may not be used to endorse or promote products
00019  *    derived from this software without specific prior written permission.
00020  *
00021  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
00022  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00023  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
00024  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00025  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
00026  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00027  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00028  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
00029  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
00030  * OF SUCH DAMAGE.
00031  *
00032  * Author: Christiaan Simons <christiaan.simons@axon.tv>
00033  */
00034 
00035 #include "lwip/opt.h"
00036 
00037 #if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
00038 
00039 #include "lwip/ip_addr.h"
00040 #include "lwip/mem.h"
00041 #include "lwip/udp.h"
00042 #include "lwip/stats.h"
00043 #include "lwip/snmp.h"
00044 #include "lwip/snmp_asn1.h"
00045 #include "lwip/snmp_msg.h"
00046 #include "lwip/snmp_structs.h"
00047 
00048 #include <string.h>
00049 
00050 /* public (non-static) constants */
00051 /** SNMP v1 == 0 */
00052 const s32_t snmp_version = 0;
00053 /** default SNMP community string */
00054 const char snmp_publiccommunity[7] = "public";
00055 
00056 /* statically allocated buffers for SNMP_CONCURRENT_REQUESTS */
00057 struct snmp_msg_pstat msg_input_list[SNMP_CONCURRENT_REQUESTS];
00058 /* UDP Protocol Control Block */
00059 struct udp_pcb *snmp1_pcb;
00060 
00061 static void snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port);
00062 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);
00063 static err_t snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat);
00064 
00065 
00066 /**
00067  * Starts SNMP Agent.
00068  * Allocates UDP pcb and binds it to IP_ADDR_ANY port 161.
00069  */
00070 void
00071 snmp_init(void)
00072 {
00073   struct snmp_msg_pstat *msg_ps;
00074   u8_t i;
00075 
00076   snmp1_pcb = udp_new();
00077   if (snmp1_pcb != NULL)
00078   {
00079     udp_recv(snmp1_pcb, snmp_recv, (void *)SNMP_IN_PORT);
00080     udp_bind(snmp1_pcb, IP_ADDR_ANY, SNMP_IN_PORT);
00081   }
00082   msg_ps = &msg_input_list[0];
00083   for (i=0; i<SNMP_CONCURRENT_REQUESTS; i++)
00084   {
00085     msg_ps->state = SNMP_MSG_EMPTY;
00086     msg_ps->error_index = 0;
00087     msg_ps->error_status = SNMP_ES_NOERROR;
00088     msg_ps++;
00089   }
00090   trap_msg.pcb = snmp1_pcb;
00091   /* The coldstart trap will only be output
00092      if our outgoing interface is up & configured  */
00093   snmp_coldstart_trap();
00094 }
00095 
00096 static void
00097 snmp_error_response(struct snmp_msg_pstat *msg_ps, u8_t error)
00098 {
00099   snmp_varbind_list_free(&msg_ps->outvb);
00100   msg_ps->outvb = msg_ps->invb;
00101   msg_ps->invb.head = NULL;
00102   msg_ps->invb.tail = NULL;
00103   msg_ps->invb.count = 0;
00104   msg_ps->error_status = error;
00105   msg_ps->error_index = 1 + msg_ps->vb_idx;
00106   snmp_send_response(msg_ps);
00107   snmp_varbind_list_free(&msg_ps->outvb);
00108   msg_ps->state = SNMP_MSG_EMPTY;
00109 }
00110 
00111 static void
00112 snmp_ok_response(struct snmp_msg_pstat *msg_ps)
00113 {
00114   err_t err_ret;
00115 
00116   err_ret = snmp_send_response(msg_ps);
00117   if (err_ret == ERR_MEM)
00118   {
00119     /* serious memory problem, can't return tooBig */
00120   }
00121   else
00122   {
00123     LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event = %"S32_F"\n",msg_ps->error_status));
00124   }
00125   /* free varbinds (if available) */
00126   snmp_varbind_list_free(&msg_ps->invb);
00127   snmp_varbind_list_free(&msg_ps->outvb);
00128   msg_ps->state = SNMP_MSG_EMPTY;
00129 }
00130 
00131 /**
00132  * Service an internal or external event for SNMP GET.
00133  *
00134  * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
00135  * @param msg_ps points to the assosicated message process state
00136  */
00137 static void
00138 snmp_msg_get_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
00139 {
00140   LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_get_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
00141 
00142   if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
00143   {
00144     struct mib_external_node *en;
00145     struct snmp_name_ptr np;
00146 
00147     /* get_object_def() answer*/
00148     en = msg_ps->ext_mib_node;
00149     np = msg_ps->ext_name_ptr;
00150 
00151     /* translate answer into a known lifeform */
00152     en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
00153     if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
00154     {
00155       msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE;
00156       en->get_value_q(request_id, &msg_ps->ext_object_def);
00157     }
00158     else
00159     {
00160       en->get_object_def_pc(request_id, np.ident_len, np.ident);
00161       /* search failed, object id points to unknown object (nosuchname) */
00162       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
00163     }
00164   }
00165   else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE)
00166   {
00167     struct mib_external_node *en;
00168     struct snmp_varbind *vb;
00169 
00170     /* get_value() answer */
00171     en = msg_ps->ext_mib_node;
00172 
00173     /* allocate output varbind */
00174     vb = (struct snmp_varbind *)mem_malloc(sizeof(struct snmp_varbind));
00175     LWIP_ASSERT("vb != NULL",vb != NULL);
00176     if (vb != NULL)
00177     {
00178       vb->next = NULL;
00179       vb->prev = NULL;
00180 
00181       /* move name from invb to outvb */
00182       vb->ident = msg_ps->vb_ptr->ident;
00183       vb->ident_len = msg_ps->vb_ptr->ident_len;
00184       /* ensure this memory is refereced once only */
00185       msg_ps->vb_ptr->ident = NULL;
00186       msg_ps->vb_ptr->ident_len = 0;
00187 
00188       vb->value_type = msg_ps->ext_object_def.asn_type;
00189       vb->value_len =  msg_ps->ext_object_def.v_len;
00190       if (vb->value_len > 0)
00191       {
00192         vb->value = mem_malloc(vb->value_len);
00193         LWIP_ASSERT("vb->value != NULL",vb->value != NULL);
00194         if (vb->value != NULL)
00195         {
00196           en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value);
00197           snmp_varbind_tail_add(&msg_ps->outvb, vb);
00198           /* search again (if vb_idx < msg_ps->invb.count) */
00199           msg_ps->state = SNMP_MSG_SEARCH_OBJ;
00200           msg_ps->vb_idx += 1;
00201         }
00202         else
00203         {
00204           en->get_value_pc(request_id, &msg_ps->ext_object_def);
00205           LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no variable space\n"));
00206           msg_ps->vb_ptr->ident = vb->ident;
00207           msg_ps->vb_ptr->ident_len = vb->ident_len;
00208           mem_free(vb);
00209           snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
00210         }
00211       }
00212       else
00213       {
00214         /* vb->value_len == 0, empty value (e.g. empty string) */
00215         en->get_value_a(request_id, &msg_ps->ext_object_def, 0, NULL);
00216         vb->value = NULL;
00217         snmp_varbind_tail_add(&msg_ps->outvb, vb);
00218         /* search again (if vb_idx < msg_ps->invb.count) */
00219         msg_ps->state = SNMP_MSG_SEARCH_OBJ;
00220         msg_ps->vb_idx += 1;
00221       }
00222     }
00223     else
00224     {
00225       en->get_value_pc(request_id, &msg_ps->ext_object_def);
00226       LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no outvb space\n"));
00227       snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
00228     }
00229   }
00230 
00231   while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
00232          (msg_ps->vb_idx < msg_ps->invb.count))
00233   {
00234     struct mib_node *mn;
00235     struct snmp_name_ptr np;
00236 
00237     if (msg_ps->vb_idx == 0)
00238     {
00239       msg_ps->vb_ptr = msg_ps->invb.head;
00240     }
00241     else
00242     {
00243       msg_ps->vb_ptr = msg_ps->vb_ptr->next;
00244     }
00245     /** test object identifier for .iso.org.dod.internet prefix */
00246     if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len,  msg_ps->vb_ptr->ident))
00247     {
00248       mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
00249                              msg_ps->vb_ptr->ident + 4, &np);
00250       if (mn != NULL)
00251       {
00252         if (mn->node_type == MIB_NODE_EX)
00253         {
00254           /* external object */
00255           struct mib_external_node *en = (struct mib_external_node*)mn;
00256 
00257           msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
00258           /* save en && args in msg_ps!! */
00259           msg_ps->ext_mib_node = en;
00260           msg_ps->ext_name_ptr = np;
00261 
00262           en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
00263         }
00264         else
00265         {
00266           /* internal object */
00267           struct obj_def object_def;
00268 
00269           msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
00270           mn->get_object_def(np.ident_len, np.ident, &object_def);
00271           if (object_def.instance != MIB_OBJECT_NONE)
00272           {
00273             mn = mn;
00274           }
00275           else
00276           {
00277             /* search failed, object id points to unknown object (nosuchname) */
00278             mn =  NULL;
00279           }
00280           if (mn != NULL)
00281           {
00282             struct snmp_varbind *vb;
00283 
00284             msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE;
00285             /* allocate output varbind */
00286             vb = (struct snmp_varbind *)mem_malloc(sizeof(struct snmp_varbind));
00287             LWIP_ASSERT("vb != NULL",vb != NULL);
00288             if (vb != NULL)
00289             {
00290               vb->next = NULL;
00291               vb->prev = NULL;
00292 
00293               /* move name from invb to outvb */
00294               vb->ident = msg_ps->vb_ptr->ident;
00295               vb->ident_len = msg_ps->vb_ptr->ident_len;
00296               /* ensure this memory is refereced once only */
00297               msg_ps->vb_ptr->ident = NULL;
00298               msg_ps->vb_ptr->ident_len = 0;
00299 
00300               vb->value_type = object_def.asn_type;
00301               vb->value_len = object_def.v_len;
00302               if (vb->value_len > 0)
00303               {
00304                 vb->value = mem_malloc(vb->value_len);
00305                 LWIP_ASSERT("vb->value != NULL",vb->value != NULL);
00306                 if (vb->value != NULL)
00307                 {
00308                   mn->get_value(&object_def, vb->value_len, vb->value);
00309                   snmp_varbind_tail_add(&msg_ps->outvb, vb);
00310                   msg_ps->state = SNMP_MSG_SEARCH_OBJ;
00311                   msg_ps->vb_idx += 1;
00312                 }
00313                 else
00314                 {
00315                   LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate variable space\n"));
00316                   msg_ps->vb_ptr->ident = vb->ident;
00317                   msg_ps->vb_ptr->ident_len = vb->ident_len;
00318                   mem_free(vb);
00319                   snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
00320                 }
00321               }
00322               else
00323               {
00324                 /* vb->value_len == 0, empty value (e.g. empty string) */
00325                 vb->value = NULL;
00326                 snmp_varbind_tail_add(&msg_ps->outvb, vb);
00327                 msg_ps->state = SNMP_MSG_SEARCH_OBJ;
00328                 msg_ps->vb_idx += 1;
00329               }
00330             }
00331             else
00332             {
00333               LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate outvb space\n"));
00334               snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
00335             }
00336           }
00337         }
00338       }
00339     }
00340     else
00341     {
00342       mn = NULL;
00343     }
00344     if (mn == NULL)
00345     {
00346       /* mn == NULL, noSuchName */
00347       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
00348     }
00349   }
00350   if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
00351       (msg_ps->vb_idx == msg_ps->invb.count))
00352   {
00353     snmp_ok_response(msg_ps);
00354   }
00355 }
00356 
00357 /**
00358  * Service an internal or external event for SNMP GETNEXT.
00359  *
00360  * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
00361  * @param msg_ps points to the assosicated message process state
00362  */
00363 static void
00364 snmp_msg_getnext_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
00365 {
00366   LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
00367 
00368   if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
00369   {
00370     struct mib_external_node *en;
00371 
00372     /* get_object_def() answer*/
00373     en = msg_ps->ext_mib_node;
00374 
00375     /* translate answer into a known lifeform */
00376     en->get_object_def_a(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1], &msg_ps->ext_object_def);
00377     if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
00378     {
00379       msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE;
00380       en->get_value_q(request_id, &msg_ps->ext_object_def);
00381     }
00382     else
00383     {
00384       en->get_object_def_pc(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1]);
00385       /* search failed, object id points to unknown object (nosuchname) */
00386       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
00387     }
00388   }
00389   else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE)
00390   {
00391     struct mib_external_node *en;
00392     struct snmp_varbind *vb;
00393 
00394     /* get_value() answer */
00395     en = msg_ps->ext_mib_node;
00396 
00397     vb = snmp_varbind_alloc(&msg_ps->ext_oid,
00398                             msg_ps->ext_object_def.asn_type,
00399                             msg_ps->ext_object_def.v_len);
00400     if (vb != NULL)
00401     {
00402       en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value);
00403       snmp_varbind_tail_add(&msg_ps->outvb, vb);
00404       msg_ps->state = SNMP_MSG_SEARCH_OBJ;
00405       msg_ps->vb_idx += 1;
00406     }
00407     else
00408     {
00409       en->get_value_pc(request_id, &msg_ps->ext_object_def);
00410       LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: couldn't allocate outvb space\n"));
00411       snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
00412     }
00413   }
00414 
00415   while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
00416          (msg_ps->vb_idx < msg_ps->invb.count))
00417   {
00418     struct mib_node *mn;
00419     struct snmp_obj_id oid;
00420 
00421     if (msg_ps->vb_idx == 0)
00422     {
00423       msg_ps->vb_ptr = msg_ps->invb.head;
00424     }
00425     else
00426     {
00427       msg_ps->vb_ptr = msg_ps->vb_ptr->next;
00428     }
00429     if (snmp_iso_prefix_expand(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident, &oid))
00430     {
00431       if (msg_ps->vb_ptr->ident_len > 3)
00432       {
00433         /* can offset ident_len and ident */
00434         mn = snmp_expand_tree((struct mib_node*)&internet,
00435                               msg_ps->vb_ptr->ident_len - 4,
00436                               msg_ps->vb_ptr->ident + 4, &oid);
00437       }
00438       else
00439       {
00440         /* can't offset ident_len -4, ident + 4 */
00441         mn = snmp_expand_tree((struct mib_node*)&internet, 0, NULL, &oid);
00442       }
00443     }
00444     else
00445     {
00446       mn = NULL;
00447     }
00448     if (mn != NULL)
00449     {
00450       if (mn->node_type == MIB_NODE_EX)
00451       {
00452         /* external object */
00453         struct mib_external_node *en = (struct mib_external_node*)mn;
00454 
00455         msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
00456         /* save en && args in msg_ps!! */
00457         msg_ps->ext_mib_node = en;
00458         msg_ps->ext_oid = oid;
00459 
00460         en->get_object_def_q(en->addr_inf, request_id, 1, &oid.id[oid.len - 1]);
00461       }
00462       else
00463       {
00464         /* internal object */
00465         struct obj_def object_def;
00466         struct snmp_varbind *vb;
00467 
00468         msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
00469         mn->get_object_def(1, &oid.id[oid.len - 1], &object_def);
00470 
00471         vb = snmp_varbind_alloc(&oid, object_def.asn_type, object_def.v_len);
00472         if (vb != NULL)
00473         {
00474           msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE;
00475           mn->get_value(&object_def, object_def.v_len, vb->value);
00476           snmp_varbind_tail_add(&msg_ps->outvb, vb);
00477           msg_ps->state = SNMP_MSG_SEARCH_OBJ;
00478           msg_ps->vb_idx += 1;
00479         }
00480         else
00481         {
00482           LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv couldn't allocate outvb space\n"));
00483           snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
00484         }
00485       }
00486     }
00487     if (mn == NULL)
00488     {
00489       /* mn == NULL, noSuchName */
00490       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
00491     }
00492   }
00493   if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
00494       (msg_ps->vb_idx == msg_ps->invb.count))
00495   {
00496     snmp_ok_response(msg_ps);
00497   }
00498 }
00499 
00500 /**
00501  * Service an internal or external event for SNMP SET.
00502  *
00503  * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
00504  * @param msg_ps points to the assosicated message process state
00505  */
00506 static void
00507 snmp_msg_set_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
00508 {
00509   LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_set_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
00510 
00511   if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
00512   {
00513     struct mib_external_node *en;
00514     struct snmp_name_ptr np;
00515 
00516     /* get_object_def() answer*/
00517     en = msg_ps->ext_mib_node;
00518     np = msg_ps->ext_name_ptr;
00519 
00520     /* translate answer into a known lifeform */
00521     en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
00522     if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
00523     {
00524       msg_ps->state = SNMP_MSG_EXTERNAL_SET_TEST;
00525       en->set_test_q(request_id, &msg_ps->ext_object_def);
00526     }
00527     else
00528     {
00529       en->get_object_def_pc(request_id, np.ident_len, np.ident);
00530       /* search failed, object id points to unknown object (nosuchname) */
00531       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
00532     }
00533   }
00534   else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_TEST)
00535   {
00536     struct mib_external_node *en;
00537 
00538     /* set_test() answer*/
00539     en = msg_ps->ext_mib_node;
00540 
00541     if (msg_ps->ext_object_def.access == MIB_OBJECT_READ_WRITE)
00542     {
00543        if ((msg_ps->ext_object_def.asn_type == msg_ps->vb_ptr->value_type) &&
00544            (en->set_test_a(request_id,&msg_ps->ext_object_def,
00545                            msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0))
00546       {
00547         msg_ps->state = SNMP_MSG_SEARCH_OBJ;
00548         msg_ps->vb_idx += 1;
00549       }
00550       else
00551       {
00552         en->set_test_pc(request_id,&msg_ps->ext_object_def);
00553         /* bad value */
00554         snmp_error_response(msg_ps,SNMP_ES_BADVALUE);
00555       }
00556     }
00557     else
00558     {
00559       en->set_test_pc(request_id,&msg_ps->ext_object_def);
00560       /* object not available for set */
00561       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
00562     }
00563   }
00564   else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF_S)
00565   {
00566     struct mib_external_node *en;
00567     struct snmp_name_ptr np;
00568 
00569     /* get_object_def() answer*/
00570     en = msg_ps->ext_mib_node;
00571     np = msg_ps->ext_name_ptr;
00572 
00573     /* translate answer into a known lifeform */
00574     en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
00575     if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
00576     {
00577       msg_ps->state = SNMP_MSG_EXTERNAL_SET_VALUE;
00578       en->set_value_q(request_id, &msg_ps->ext_object_def,
00579                       msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value);
00580     }
00581     else
00582     {
00583       en->get_object_def_pc(request_id, np.ident_len, np.ident);
00584       /* set_value failed, object has disappeared for some odd reason?? */
00585       snmp_error_response(msg_ps,SNMP_ES_GENERROR);
00586     }
00587   }
00588   else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_VALUE)
00589   {
00590     struct mib_external_node *en;
00591 
00592     /** set_value_a() @todo: use reply value?? */
00593     en = msg_ps->ext_mib_node;
00594     en->set_value_a(request_id, &msg_ps->ext_object_def, 0, NULL);
00595 
00596     /** @todo use set_value_pc() if toobig */
00597     msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
00598     msg_ps->vb_idx += 1;
00599   }
00600 
00601   /* test all values before setting */
00602   while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
00603          (msg_ps->vb_idx < msg_ps->invb.count))
00604   {
00605     struct mib_node *mn;
00606     struct snmp_name_ptr np;
00607 
00608     if (msg_ps->vb_idx == 0)
00609     {
00610       msg_ps->vb_ptr = msg_ps->invb.head;
00611     }
00612     else
00613     {
00614       msg_ps->vb_ptr = msg_ps->vb_ptr->next;
00615     }
00616     /** test object identifier for .iso.org.dod.internet prefix */
00617     if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len,  msg_ps->vb_ptr->ident))
00618     {
00619       mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
00620                              msg_ps->vb_ptr->ident + 4, &np);
00621       if (mn != NULL)
00622       {
00623         if (mn->node_type == MIB_NODE_EX)
00624         {
00625           /* external object */
00626           struct mib_external_node *en = (struct mib_external_node*)mn;
00627 
00628           msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
00629           /* save en && args in msg_ps!! */
00630           msg_ps->ext_mib_node = en;
00631           msg_ps->ext_name_ptr = np;
00632 
00633           en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
00634         }
00635         else
00636         {
00637           /* internal object */
00638           struct obj_def object_def;
00639 
00640           msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
00641           mn->get_object_def(np.ident_len, np.ident, &object_def);
00642           if (object_def.instance != MIB_OBJECT_NONE)
00643           {
00644             mn = mn;
00645           }
00646           else
00647           {
00648             /* search failed, object id points to unknown object (nosuchname) */
00649             mn = NULL;
00650           }
00651           if (mn != NULL)
00652           {
00653             msg_ps->state = SNMP_MSG_INTERNAL_SET_TEST;
00654 
00655             if (object_def.access == MIB_OBJECT_READ_WRITE)
00656             {
00657               if ((object_def.asn_type == msg_ps->vb_ptr->value_type) &&
00658                   (mn->set_test(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0))
00659               {
00660                 msg_ps->state = SNMP_MSG_SEARCH_OBJ;
00661                 msg_ps->vb_idx += 1;
00662               }
00663               else
00664               {
00665                 /* bad value */
00666                 snmp_error_response(msg_ps,SNMP_ES_BADVALUE);
00667               }
00668             }
00669             else
00670             {
00671               /* object not available for set */
00672               snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
00673             }
00674           }
00675         }
00676       }
00677     }
00678     else
00679     {
00680       mn = NULL;
00681     }
00682     if (mn == NULL)
00683     {
00684       /* mn == NULL, noSuchName */
00685       snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
00686     }
00687   }
00688 
00689   if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
00690       (msg_ps->vb_idx == msg_ps->invb.count))
00691   {
00692     msg_ps->vb_idx = 0;
00693     msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
00694   }
00695 
00696   /* set all values "atomically" (be as "atomic" as possible) */
00697   while ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) &&
00698          (msg_ps->vb_idx < msg_ps->invb.count))
00699   {
00700     struct mib_node *mn;
00701     struct snmp_name_ptr np;
00702 
00703     if (msg_ps->vb_idx == 0)
00704     {
00705       msg_ps->vb_ptr = msg_ps->invb.head;
00706     }
00707     else
00708     {
00709       msg_ps->vb_ptr = msg_ps->vb_ptr->next;
00710     }
00711     /* skip iso prefix test, was done previously while settesting() */
00712     mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
00713                            msg_ps->vb_ptr->ident + 4, &np);
00714     /* check if object is still available
00715        (e.g. external hot-plug thingy present?) */
00716     if (mn != NULL)
00717     {
00718       if (mn->node_type == MIB_NODE_EX)
00719       {
00720         /* external object */
00721         struct mib_external_node *en = (struct mib_external_node*)mn;
00722 
00723         msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF_S;
00724         /* save en && args in msg_ps!! */
00725         msg_ps->ext_mib_node = en;
00726         msg_ps->ext_name_ptr = np;
00727 
00728         en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
00729       }
00730       else
00731       {
00732         /* internal object */
00733         struct obj_def object_def;
00734 
00735         msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF_S;
00736         mn->get_object_def(np.ident_len, np.ident, &object_def);
00737         msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
00738         mn->set_value(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value);
00739         msg_ps->vb_idx += 1;
00740       }
00741     }
00742   }
00743   if ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) &&
00744       (msg_ps->vb_idx == msg_ps->invb.count))
00745   {
00746     /* simply echo the input if we can set it
00747        @todo do we need to return the actual value?
00748        e.g. if value is silently modified or behaves sticky? */
00749     msg_ps->outvb = msg_ps->invb;
00750     msg_ps->invb.head = NULL;
00751     msg_ps->invb.tail = NULL;
00752     msg_ps->invb.count = 0;
00753     snmp_ok_response(msg_ps);
00754   }
00755 }
00756 
00757 
00758 /**
00759  * Handle one internal or external event.
00760  * Called for one async event. (recv external/private answer)
00761  *
00762  * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
00763  */
00764 void
00765 snmp_msg_event(u8_t request_id)
00766 {
00767   struct snmp_msg_pstat *msg_ps;
00768 
00769   if (request_id < SNMP_CONCURRENT_REQUESTS)
00770   {
00771     msg_ps = &msg_input_list[request_id];
00772     if (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ)
00773     {
00774       snmp_msg_getnext_event(request_id, msg_ps);
00775     }
00776     else if (msg_ps->rt == SNMP_ASN1_PDU_GET_REQ)
00777     {
00778       snmp_msg_get_event(request_id, msg_ps);
00779     }
00780     else if(msg_ps->rt == SNMP_ASN1_PDU_SET_REQ)
00781     {
00782       snmp_msg_set_event(request_id, msg_ps);
00783     }
00784   }
00785 }
00786 
00787 
00788 /* lwIP UDP receive callback function */
00789 static void
00790 snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port)
00791 {
00792   struct udp_hdr *udphdr;
00793 
00794   /* suppress unused argument warning */
00795   LWIP_UNUSED_ARG(arg);
00796   /* peek in the UDP header (goto IP payload) */
00797   if(pbuf_header(p, UDP_HLEN)){
00798     LWIP_ASSERT("Can't move to UDP header", 0);
00799     pbuf_free(p);
00800     return;
00801   }
00802   udphdr = p->payload;
00803 
00804   /* check if datagram is really directed at us (including broadcast requests) */
00805   if ((pcb == snmp1_pcb) && (ntohs(udphdr->dest) == SNMP_IN_PORT))
00806   {
00807     struct snmp_msg_pstat *msg_ps;
00808     u8_t req_idx;
00809 
00810     /* traverse input message process list, look for SNMP_MSG_EMPTY */
00811     msg_ps = &msg_input_list[0];
00812     req_idx = 0;
00813     while ((req_idx<SNMP_CONCURRENT_REQUESTS) && (msg_ps->state != SNMP_MSG_EMPTY))
00814     {
00815       req_idx++;
00816       msg_ps++;
00817     }
00818     if (req_idx != SNMP_CONCURRENT_REQUESTS)
00819     {
00820       err_t err_ret;
00821       u16_t payload_len;
00822       u16_t payload_ofs;
00823       u16_t varbind_ofs = 0;
00824 
00825       /* accepting request */
00826       snmp_inc_snmpinpkts();
00827       /* record used 'protocol control block' */
00828       msg_ps->pcb = pcb;
00829       /* source address (network order) */
00830       msg_ps->sip = *addr;
00831       /* source port (host order (lwIP oddity)) */
00832       msg_ps->sp = port;
00833       /* read UDP payload length from UDP header */
00834       payload_len = ntohs(udphdr->len) - UDP_HLEN;
00835 
00836       /* adjust to UDP payload */
00837       payload_ofs = UDP_HLEN;
00838 
00839       /* check total length, version, community, pdu type */
00840       err_ret = snmp_pdu_header_check(p, payload_ofs, payload_len, &varbind_ofs, msg_ps);
00841       if (((msg_ps->rt == SNMP_ASN1_PDU_GET_REQ) ||
00842            (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ) ||
00843            (msg_ps->rt == SNMP_ASN1_PDU_SET_REQ)) &&
00844           ((msg_ps->error_status == SNMP_ES_NOERROR) &&
00845            (msg_ps->error_index == 0)) )
00846       {
00847         /* Only accept requests and requests without error (be robust) */
00848         err_ret = err_ret;
00849       }
00850       else
00851       {
00852         /* Reject response and trap headers or error requests as input! */
00853         err_ret = ERR_ARG;
00854       }
00855       if (err_ret == ERR_OK)
00856       {
00857         LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv ok, community %s\n", msg_ps->community));
00858 
00859         /* Builds a list of variable bindings. Copy the varbinds from the pbuf
00860           chain to glue them when these are divided over two or more pbuf's. */
00861         err_ret = snmp_pdu_dec_varbindlist(p, varbind_ofs, &varbind_ofs, msg_ps);
00862         if ((err_ret == ERR_OK) && (msg_ps->invb.count > 0))
00863         {
00864           /* we've decoded the incoming message, release input msg now */
00865           pbuf_free(p);
00866 
00867           msg_ps->error_status = SNMP_ES_NOERROR;
00868           msg_ps->error_index = 0;
00869           /* find object for each variable binding */
00870           msg_ps->state = SNMP_MSG_SEARCH_OBJ;
00871           /* first variable binding from list to inspect */
00872           msg_ps->vb_idx = 0;
00873 
00874           LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv varbind cnt=%"U16_F"\n",(u16_t)msg_ps->invb.count));
00875 
00876           /* handle input event and as much objects as possible in one go */
00877           snmp_msg_event(req_idx);
00878         }
00879         else
00880         {
00881           /* varbind-list decode failed, or varbind list empty.
00882              drop request silently, do not return error!
00883              (errors are only returned for a specific varbind failure) */
00884           pbuf_free(p);
00885           LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_dec_varbindlist() failed\n"));
00886         }
00887       }
00888       else
00889       {
00890         /* header check failed
00891            drop request silently, do not return error! */
00892         pbuf_free(p);
00893         LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check() failed\n"));
00894       }
00895     }
00896     else
00897     {
00898       /* exceeding number of concurrent requests */
00899       pbuf_free(p);
00900     }
00901   }
00902   else
00903   {
00904     /* datagram not for us */
00905     pbuf_free(p);
00906   }
00907 }
00908 
00909 /**
00910  * Checks and decodes incoming SNMP message header, logs header errors.
00911  *
00912  * @param p points to pbuf chain of SNMP message (UDP payload)
00913  * @param ofs points to first octet of SNMP message
00914  * @param pdu_len the length of the UDP payload
00915  * @param ofs_ret returns the ofset of the variable bindings
00916  * @param m_stat points to the current message request state return
00917  * @return
00918  * - ERR_OK SNMP header is sane and accepted
00919  * - ERR_ARG SNMP header is either malformed or rejected
00920  */
00921 static err_t
00922 snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
00923 {
00924   err_t derr;
00925   u16_t len, ofs_base;
00926   u8_t  len_octets;
00927   u8_t  type;
00928   s32_t version;
00929 
00930   ofs_base = ofs;
00931   snmp_asn1_dec_type(p, ofs, &type);
00932   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
00933   if ((derr != ERR_OK) ||
00934       (pdu_len != (1 + len_octets + len)) ||
00935       (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
00936   {
00937     snmp_inc_snmpinasnparseerrs();
00938     return ERR_ARG;
00939   }
00940   ofs += (1 + len_octets);
00941   snmp_asn1_dec_type(p, ofs, &type);
00942   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
00943   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
00944   {
00945     /* can't decode or no integer (version) */
00946     snmp_inc_snmpinasnparseerrs();
00947     return ERR_ARG;
00948   }
00949   derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &version);
00950   if (derr != ERR_OK)
00951   {
00952     /* can't decode */
00953     snmp_inc_snmpinasnparseerrs();
00954     return ERR_ARG;
00955   }
00956   if (version != 0)
00957   {
00958     /* not version 1 */
00959     snmp_inc_snmpinbadversions();
00960     return ERR_ARG;
00961   }
00962   ofs += (1 + len_octets + len);
00963   snmp_asn1_dec_type(p, ofs, &type);
00964   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
00965   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)))
00966   {
00967     /* can't decode or no octet string (community) */
00968     snmp_inc_snmpinasnparseerrs();
00969     return ERR_ARG;
00970   }
00971   derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, SNMP_COMMUNITY_STR_LEN, m_stat->community);
00972   if (derr != ERR_OK)
00973   {
00974     snmp_inc_snmpinasnparseerrs();
00975     return ERR_ARG;
00976   }
00977   /* add zero terminator */
00978   len = ((len < (SNMP_COMMUNITY_STR_LEN))?(len):(SNMP_COMMUNITY_STR_LEN));
00979   m_stat->community[len] = 0;
00980   m_stat->com_strlen = len;
00981   if (strncmp(snmp_publiccommunity, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0)
00982   {
00983     /** @todo: move this if we need to check more names */
00984     snmp_inc_snmpinbadcommunitynames();
00985     snmp_authfail_trap();
00986     return ERR_ARG;
00987   }
00988   ofs += (1 + len_octets + len);
00989   snmp_asn1_dec_type(p, ofs, &type);
00990   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
00991   if (derr != ERR_OK)
00992   {
00993     snmp_inc_snmpinasnparseerrs();
00994     return ERR_ARG;
00995   }
00996   switch(type)
00997   {
00998     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_REQ):
00999       /* GetRequest PDU */
01000       snmp_inc_snmpingetrequests();
01001       derr = ERR_OK;
01002       break;
01003     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_NEXT_REQ):
01004       /* GetNextRequest PDU */
01005       snmp_inc_snmpingetnexts();
01006       derr = ERR_OK;
01007       break;
01008     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP):
01009       /* GetResponse PDU */
01010       snmp_inc_snmpingetresponses();
01011       derr = ERR_ARG;
01012       break;
01013     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ):
01014       /* SetRequest PDU */
01015       snmp_inc_snmpinsetrequests();
01016       derr = ERR_OK;
01017       break;
01018     case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP):
01019       /* Trap PDU */
01020       snmp_inc_snmpintraps();
01021       derr = ERR_ARG;
01022       break;
01023     default:
01024       snmp_inc_snmpinasnparseerrs();
01025       derr = ERR_ARG;
01026       break;
01027   }
01028   if (derr != ERR_OK)
01029   {
01030     /* unsupported input PDU for this agent (no parse error) */
01031     return ERR_ARG;
01032   }
01033   m_stat->rt = type & 0x1F;
01034   ofs += (1 + len_octets);
01035   if (len != (pdu_len - (ofs - ofs_base)))
01036   {
01037     /* decoded PDU length does not equal actual payload length */
01038     snmp_inc_snmpinasnparseerrs();
01039     return ERR_ARG;
01040   }
01041   snmp_asn1_dec_type(p, ofs, &type);
01042   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
01043   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
01044   {
01045     /* can't decode or no integer (request ID) */
01046     snmp_inc_snmpinasnparseerrs();
01047     return ERR_ARG;
01048   }
01049   derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->rid);
01050   if (derr != ERR_OK)
01051   {
01052     /* can't decode */
01053     snmp_inc_snmpinasnparseerrs();
01054     return ERR_ARG;
01055   }
01056   ofs += (1 + len_octets + len);
01057   snmp_asn1_dec_type(p, ofs, &type);
01058   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
01059   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
01060   {
01061     /* can't decode or no integer (error-status) */
01062     snmp_inc_snmpinasnparseerrs();
01063     return ERR_ARG;
01064   }
01065   /* must be noError (0) for incoming requests.
01066      log errors for mib-2 completeness and for debug purposes */
01067   derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_status);
01068   if (derr != ERR_OK)
01069   {
01070     /* can't decode */
01071     snmp_inc_snmpinasnparseerrs();
01072     return ERR_ARG;
01073   }
01074   switch (m_stat->error_status)
01075   {
01076     case SNMP_ES_TOOBIG:
01077       snmp_inc_snmpintoobigs();
01078       break;
01079     case SNMP_ES_NOSUCHNAME:
01080       snmp_inc_snmpinnosuchnames();
01081       break;
01082     case SNMP_ES_BADVALUE:
01083       snmp_inc_snmpinbadvalues();
01084       break;
01085     case SNMP_ES_READONLY:
01086       snmp_inc_snmpinreadonlys();
01087       break;
01088     case SNMP_ES_GENERROR:
01089       snmp_inc_snmpingenerrs();
01090       break;
01091   }
01092   ofs += (1 + len_octets + len);
01093   snmp_asn1_dec_type(p, ofs, &type);
01094   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
01095   if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
01096   {
01097     /* can't decode or no integer (error-index) */
01098     snmp_inc_snmpinasnparseerrs();
01099     return ERR_ARG;
01100   }
01101   /* must be 0 for incoming requests.
01102      decode anyway to catch bad integers (and dirty tricks) */
01103   derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_index);
01104   if (derr != ERR_OK)
01105   {
01106     /* can't decode */
01107     snmp_inc_snmpinasnparseerrs();
01108     return ERR_ARG;
01109   }
01110   ofs += (1 + len_octets + len);
01111   *ofs_ret = ofs;
01112   return ERR_OK;
01113 }
01114 
01115 static err_t
01116 snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
01117 {
01118   err_t derr;
01119   u16_t len, vb_len;
01120   u8_t  len_octets;
01121   u8_t type;
01122 
01123   /* variable binding list */
01124   snmp_asn1_dec_type(p, ofs, &type);
01125   derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &vb_len);
01126   if ((derr != ERR_OK) ||
01127       (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
01128   {
01129     snmp_inc_snmpinasnparseerrs();
01130     return ERR_ARG;
01131   }
01132   ofs += (1 + len_octets);
01133 
01134   /* start with empty list */
01135   m_stat->invb.count = 0;
01136   m_stat->invb.head = NULL;
01137   m_stat->invb.tail = NULL;
01138 
01139   while (vb_len > 0)
01140   {
01141     struct snmp_obj_id oid, oid_value;
01142     struct snmp_varbind *vb;
01143 
01144     snmp_asn1_dec_type(p, ofs, &type);
01145     derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
01146     if ((derr != ERR_OK) ||
01147         (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)) ||
01148         (len == 0) || (len > vb_len))
01149     {
01150       snmp_inc_snmpinasnparseerrs();
01151       /* free varbinds (if available) */
01152       snmp_varbind_list_free(&m_stat->invb);
01153       return ERR_ARG;
01154     }
01155     ofs += (1 + len_octets);
01156     vb_len -= (1 + len_octets);
01157 
01158     snmp_asn1_dec_type(p, ofs, &type);
01159     derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
01160     if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)))
01161     {
01162       /* can't decode object name length */
01163       snmp_inc_snmpinasnparseerrs();
01164       /* free varbinds (if available) */
01165       snmp_varbind_list_free(&m_stat->invb);
01166       return ERR_ARG;
01167     }
01168     derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid);
01169     if (derr != ERR_OK)
01170     {
01171       /* can't decode object name */
01172       snmp_inc_snmpinasnparseerrs();
01173       /* free varbinds (if available) */
01174       snmp_varbind_list_free(&m_stat->invb);
01175       return ERR_ARG;
01176     }
01177     ofs += (1 + len_octets + len);
01178     vb_len -= (1 + len_octets + len);
01179 
01180     snmp_asn1_dec_type(p, ofs, &type);
01181     derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
01182     if (derr != ERR_OK)
01183     {
01184       /* can't decode object value length */
01185       snmp_inc_snmpinasnparseerrs();
01186       /* free varbinds (if available) */
01187       snmp_varbind_list_free(&m_stat->invb);
01188       return ERR_ARG;
01189     }
01190 
01191     switch (type)
01192     {
01193       case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
01194         vb = snmp_varbind_alloc(&oid, type, sizeof(s32_t));
01195         if (vb != NULL)
01196         {
01197           s32_t *vptr = vb->value;
01198 
01199           derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, vptr);
01200           snmp_varbind_tail_add(&m_stat->invb, vb);
01201         }
01202         else
01203         {
01204           derr = ERR_ARG;
01205         }
01206         break;
01207       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
01208       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
01209       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
01210         vb = snmp_varbind_alloc(&oid, type, sizeof(u32_t));
01211         if (vb != NULL)
01212         {
01213           u32_t *vptr = vb->value;
01214 
01215           derr = snmp_asn1_dec_u32t(p, ofs + 1 + len_octets, len, vptr);
01216           snmp_varbind_tail_add(&m_stat->invb, vb);
01217         }
01218         else
01219         {
01220           derr = ERR_ARG;
01221         }
01222         break;
01223       case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
01224       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
01225         vb = snmp_varbind_alloc(&oid, type, len);
01226         if (vb != NULL)
01227         {
01228           derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, vb->value);
01229           snmp_varbind_tail_add(&m_stat->invb, vb);
01230         }
01231         else
01232         {
01233           derr = ERR_ARG;
01234         }
01235         break;
01236       case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
01237         vb = snmp_varbind_alloc(&oid, type, 0);
01238         if (vb != NULL)
01239         {
01240           snmp_varbind_tail_add(&m_stat->invb, vb);
01241           derr = ERR_OK;
01242         }
01243         else
01244         {
01245           derr = ERR_ARG;
01246         }
01247         break;
01248       case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
01249         derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid_value);
01250         if (derr == ERR_OK)
01251         {
01252           vb = snmp_varbind_alloc(&oid, type, oid_value.len * sizeof(s32_t));
01253           if (vb != NULL)
01254           {
01255             u8_t i = oid_value.len;
01256             s32_t *vptr = vb->value;
01257 
01258             while(i > 0)
01259             {
01260               i--;
01261               vptr[i] = oid_value.id[i];
01262             }
01263             snmp_varbind_tail_add(&m_stat->invb, vb);
01264             derr = ERR_OK;
01265           }
01266           else
01267           {
01268             derr = ERR_ARG;
01269           }
01270         }
01271         break;
01272       case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
01273         if (len == 4)
01274         {
01275           /* must be exactly 4 octets! */
01276           vb = snmp_varbind_alloc(&oid, type, 4);
01277           if (vb != NULL)
01278           {
01279             derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, vb->value);
01280             snmp_varbind_tail_add(&m_stat->invb, vb);
01281           }
01282           else
01283           {
01284             derr = ERR_ARG;
01285           }
01286         }
01287         else
01288         {
01289           derr = ERR_ARG;
01290         }
01291         break;
01292       default:
01293         derr = ERR_ARG;
01294         break;
01295     }
01296     if (derr != ERR_OK)
01297     {
01298       snmp_inc_snmpinasnparseerrs();
01299       /* free varbinds (if available) */
01300       snmp_varbind_list_free(&m_stat->invb);
01301       return ERR_ARG;
01302     }
01303     ofs += (1 + len_octets + len);
01304     vb_len -= (1 + len_octets + len);
01305   }
01306 
01307   if (m_stat->rt == SNMP_ASN1_PDU_SET_REQ)
01308   {
01309     snmp_add_snmpintotalsetvars(m_stat->invb.count);
01310   }
01311   else
01312   {
01313     snmp_add_snmpintotalreqvars(m_stat->invb.count);
01314   }
01315 
01316   *ofs_ret = ofs;
01317   return ERR_OK;
01318 }
01319 
01320 struct snmp_varbind*
01321 snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len)
01322 {
01323   struct snmp_varbind *vb;
01324 
01325   vb = (struct snmp_varbind *)mem_malloc(sizeof(struct snmp_varbind));
01326   LWIP_ASSERT("vb != NULL",vb != NULL);
01327   if (vb != NULL)
01328   {
01329     u8_t i;
01330 
01331     vb->next = NULL;
01332     vb->prev = NULL;
01333     i = oid->len;
01334     vb->ident_len = i;
01335     if (i > 0)
01336     {
01337       /* allocate array of s32_t for our object identifier */
01338       vb->ident = (s32_t*)mem_malloc(sizeof(s32_t) * i);
01339       LWIP_ASSERT("vb->ident != NULL",vb->ident != NULL);
01340       if (vb->ident == NULL)
01341       {
01342         mem_free(vb);
01343         return NULL;
01344       }
01345       while(i > 0)
01346       {
01347         i--;
01348         vb->ident[i] = oid->id[i];
01349       }
01350     }
01351     else
01352     {
01353       /* i == 0, pass zero length object identifier */
01354       vb->ident = NULL;
01355     }
01356     vb->value_type = type;
01357     vb->value_len = len;
01358     if (len > 0)
01359     {
01360       /* allocate raw bytes for our object value */
01361       vb->value = mem_malloc(len);
01362       LWIP_ASSERT("vb->value != NULL",vb->value != NULL);
01363       if (vb->value == NULL)
01364       {
01365         if (vb->ident != NULL)
01366         {
01367           mem_free(vb->ident);
01368         }
01369         mem_free(vb);
01370         return NULL;
01371       }
01372     }
01373     else
01374     {
01375       /* ASN1_NUL type, or zero length ASN1_OC_STR */
01376       vb->value = NULL;
01377     }
01378   }
01379   return vb;
01380 }
01381 
01382 void
01383 snmp_varbind_free(struct snmp_varbind *vb)
01384 {
01385   if (vb->value != NULL )
01386   {
01387     mem_free(vb->value);
01388   }
01389   if (vb->ident != NULL )
01390   {
01391     mem_free(vb->ident);
01392   }
01393   mem_free(vb);
01394 }
01395 
01396 void
01397 snmp_varbind_list_free(struct snmp_varbind_root *root)
01398 {
01399   struct snmp_varbind *vb, *prev;
01400 
01401   vb = root->tail;
01402   while ( vb != NULL )
01403   {
01404     prev = vb->prev;
01405     snmp_varbind_free(vb);
01406     vb = prev;
01407   }
01408   root->count = 0;
01409   root->head = NULL;
01410   root->tail = NULL;
01411 }
01412 
01413 void
01414 snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb)
01415 {
01416   if (root->count == 0)
01417   {
01418     /* add first varbind to list */
01419     root->head = vb;
01420     root->tail = vb;
01421   }
01422   else
01423   {
01424     /* add nth varbind to list tail */
01425     root->tail->next = vb;
01426     vb->prev = root->tail;
01427     root->tail = vb;
01428   }
01429   root->count += 1;
01430 }
01431 
01432 struct snmp_varbind*
01433 snmp_varbind_tail_remove(struct snmp_varbind_root *root)
01434 {
01435   struct snmp_varbind* vb;
01436 
01437   if (root->count > 0)
01438   {
01439     /* remove tail varbind */
01440     vb = root->tail;
01441     root->tail = vb->prev;
01442     vb->prev->next = NULL;
01443     root->count -= 1;
01444   }
01445   else
01446   {
01447     /* nothing to remove */
01448     vb = NULL;
01449   }
01450   return vb;
01451 }
01452 
01453 #endif /* LWIP_SNMP */