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