NetServices Stack source

Dependents:   HelloWorld ServoInterfaceBoardExample1 4180_Lab4

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