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