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