First run at integrating LWIP into the tree (again)
[akaros.git] / user / lwip / core / snmp / asn1_enc.c
1 /**
2  * @file
3  * Abstract Syntax Notation One (ISO 8824, 8825) encoding
4  *
5  * @todo not optimised (yet), favor correctness over speed, favor speed over size
6  */
7
8 /*
9  * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without modification,
13  * are permitted provided that the following conditions are met:
14  *
15  * 1. Redistributions of source code must retain the above copyright notice,
16  *    this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright notice,
18  *    this list of conditions and the following disclaimer in the documentation
19  *    and/or other materials provided with the distribution.
20  * 3. The name of the author may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
24  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
26  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
28  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
31  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
32  * OF SUCH DAMAGE.
33  *
34  * Author: Christiaan Simons <christiaan.simons@axon.tv>
35  */
36
37 #include "lwip/opt.h"
38
39 #if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
40
41 #include "lwip/snmp_asn1.h"
42
43 /**
44  * Returns octet count for length.
45  *
46  * @param length
47  * @param octets_needed points to the return value
48  */
49 void
50 snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
51 {
52   if (length < 0x80U)
53   {
54     *octets_needed = 1;
55   }
56   else if (length < 0x100U)
57   {
58     *octets_needed = 2;
59   }
60   else
61   {
62     *octets_needed = 3;
63   }
64 }
65
66 /**
67  * Returns octet count for an u32_t.
68  *
69  * @param value
70  * @param octets_needed points to the return value
71  *
72  * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
73  * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
74  * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
75  */
76 void
77 snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
78 {
79   if (value < 0x80UL)
80   {
81     *octets_needed = 1;
82   }
83   else if (value < 0x8000UL)
84   {
85     *octets_needed = 2;
86   }
87   else if (value < 0x800000UL)
88   {
89     *octets_needed = 3;
90   }
91   else if (value < 0x80000000UL)
92   {
93     *octets_needed = 4;
94   }
95   else
96   {
97     *octets_needed = 5;
98   }
99 }
100
101 /**
102  * Returns octet count for an s32_t.
103  *
104  * @param value
105  * @param octets_needed points to the return value
106  *
107  * @note ASN coded integers are _always_ signed.
108  */
109 void
110 snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
111 {
112   if (value < 0)
113   {
114     value = ~value;
115   }
116   if (value < 0x80L)
117   {
118     *octets_needed = 1;
119   }
120   else if (value < 0x8000L)
121   {
122     *octets_needed = 2;
123   }
124   else if (value < 0x800000L)
125   {
126     *octets_needed = 3;
127   }
128   else
129   {
130     *octets_needed = 4;
131   }
132 }
133
134 /**
135  * Returns octet count for an object identifier.
136  *
137  * @param ident_len object identifier array length
138  * @param ident points to object identifier array
139  * @param octets_needed points to the return value
140  */
141 void
142 snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed)
143 {
144   s32_t sub_id;
145   u8_t cnt;
146
147   cnt = 0;
148   if (ident_len > 1)
149   {
150     /* compressed prefix in one octet */
151     cnt++;
152     ident_len -= 2;
153     ident += 2;
154   }
155   while(ident_len > 0)
156   {
157     ident_len--;
158     sub_id = *ident;
159
160     sub_id >>= 7;
161     cnt++;
162     while(sub_id > 0)
163     {
164       sub_id >>= 7;
165       cnt++;
166     }
167     ident++;
168   }
169   *octets_needed = cnt;
170 }
171
172 /**
173  * Encodes ASN type field into a pbuf chained ASN1 msg.
174  *
175  * @param p points to output pbuf to encode value into
176  * @param ofs points to the offset within the pbuf chain
177  * @param type input ASN1 type
178  * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
179  */
180 err_t
181 snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type)
182 {
183   u16_t plen, base;
184   u8_t *msg_ptr;
185
186   plen = 0;
187   while (p != NULL)
188   {
189     base = plen;
190     plen += p->len;
191     if (ofs < plen)
192     {
193       msg_ptr = p->payload;
194       msg_ptr += ofs - base;
195       *msg_ptr = type;
196       return ERR_OK;
197     }
198     p = p->next;
199   }
200   /* p == NULL, ofs >= plen */
201   return ERR_ARG;
202 }
203
204 /**
205  * Encodes host order length field into a pbuf chained ASN1 msg.
206  *
207  * @param p points to output pbuf to encode length into
208  * @param ofs points to the offset within the pbuf chain
209  * @param length is the host order length to be encoded
210  * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
211  */
212 err_t
213 snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length)
214 {
215   u16_t plen, base;
216   u8_t *msg_ptr;
217
218   plen = 0;
219   while (p != NULL)
220   {
221     base = plen;
222     plen += p->len;
223     if (ofs < plen)
224     {
225       msg_ptr = p->payload;
226       msg_ptr += ofs - base;
227
228       if (length < 0x80)
229       {
230         *msg_ptr = length;
231         return ERR_OK;
232       }
233       else if (length < 0x100)
234       {
235         *msg_ptr = 0x81;
236         ofs += 1;
237         if (ofs >= plen)
238         {
239           /* next octet in next pbuf */
240           p = p->next;
241           if (p == NULL) { return ERR_ARG; }
242           msg_ptr = p->payload;
243         }
244         else
245         {
246           /* next octet in same pbuf */
247           msg_ptr++;
248         }
249         *msg_ptr = length;
250         return ERR_OK;
251       }
252       else
253       {
254         u8_t i;
255
256         /* length >= 0x100 && length <= 0xFFFF */
257         *msg_ptr = 0x82;
258         i = 2;
259         while (i > 0)
260         {
261           i--;
262           ofs += 1;
263           if (ofs >= plen)
264           {
265             /* next octet in next pbuf */
266             p = p->next;
267             if (p == NULL) { return ERR_ARG; }
268             msg_ptr = p->payload;
269             plen += p->len;
270           }
271           else
272           {
273             /* next octet in same pbuf */
274             msg_ptr++;
275           }
276           if (i == 0)
277           {
278             /* least significant length octet */
279             *msg_ptr = length;
280           }
281           else
282           {
283             /* most significant length octet */
284             *msg_ptr = length >> 8;
285           }
286         }
287         return ERR_OK;
288       }
289     }
290     p = p->next;
291   }
292   /* p == NULL, ofs >= plen */
293   return ERR_ARG;
294 }
295
296 /**
297  * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg.
298  *
299  * @param p points to output pbuf to encode value into
300  * @param ofs points to the offset within the pbuf chain
301  * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
302  * @param value is the host order u32_t value to be encoded
303  * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
304  *
305  * @see snmp_asn1_enc_u32t_cnt()
306  */
307 err_t
308 snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u8_t octets_needed, u32_t value)
309 {
310   u16_t plen, base;
311   u8_t *msg_ptr;
312
313   plen = 0;
314   while (p != NULL)
315   {
316     base = plen;
317     plen += p->len;
318     if (ofs < plen)
319     {
320       msg_ptr = p->payload;
321       msg_ptr += ofs - base;
322
323       if (octets_needed == 5)
324       {
325         /* not enough bits in 'value' add leading 0x00 */
326         octets_needed--;
327         *msg_ptr = 0x00;
328         ofs += 1;
329         if (ofs >= plen)
330         {
331           /* next octet in next pbuf */
332           p = p->next;
333           if (p == NULL) { return ERR_ARG; }
334           msg_ptr = p->payload;
335           plen += p->len;
336         }
337         else
338         {
339           /* next octet in same pbuf */
340           msg_ptr++;
341         }
342       }
343       while (octets_needed > 1)
344       {
345         octets_needed--;
346         *msg_ptr = value >> (octets_needed << 3);
347         ofs += 1;
348         if (ofs >= plen)
349         {
350           /* next octet in next pbuf */
351           p = p->next;
352           if (p == NULL) { return ERR_ARG; }
353           msg_ptr = p->payload;
354           plen += p->len;
355         }
356         else
357         {
358           /* next octet in same pbuf */
359           msg_ptr++;
360         }
361       }
362       /* (only) one least significant octet */
363       *msg_ptr = value;
364       return ERR_OK;
365     }
366     p = p->next;
367   }
368   /* p == NULL, ofs >= plen */
369   return ERR_ARG;
370 }
371
372 /**
373  * Encodes s32_t integer into a pbuf chained ASN1 msg.
374  *
375  * @param p points to output pbuf to encode value into
376  * @param ofs points to the offset within the pbuf chain
377  * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt())
378  * @param value is the host order s32_t value to be encoded
379  * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
380  *
381  * @see snmp_asn1_enc_s32t_cnt()
382  */
383 err_t
384 snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u8_t octets_needed, s32_t value)
385 {
386   u16_t plen, base;
387   u8_t *msg_ptr;
388
389   plen = 0;
390   while (p != NULL)
391   {
392     base = plen;
393     plen += p->len;
394     if (ofs < plen)
395     {
396       msg_ptr = p->payload;
397       msg_ptr += ofs - base;
398
399       while (octets_needed > 1)
400       {
401         octets_needed--;
402         *msg_ptr = value >> (octets_needed << 3);
403         ofs += 1;
404         if (ofs >= plen)
405         {
406           /* next octet in next pbuf */
407           p = p->next;
408           if (p == NULL) { return ERR_ARG; }
409           msg_ptr = p->payload;
410           plen += p->len;
411         }
412         else
413         {
414           /* next octet in same pbuf */
415           msg_ptr++;
416         }
417       }
418       /* (only) one least significant octet */
419       *msg_ptr = value;
420       return ERR_OK;
421     }
422     p = p->next;
423   }
424   /* p == NULL, ofs >= plen */
425   return ERR_ARG;
426 }
427
428 /**
429  * Encodes object identifier into a pbuf chained ASN1 msg.
430  *
431  * @param p points to output pbuf to encode oid into
432  * @param ofs points to the offset within the pbuf chain
433  * @param ident_len object identifier array length
434  * @param ident points to object identifier array
435  * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
436  */
437 err_t
438 snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident)
439 {
440   u16_t plen, base;
441   u8_t *msg_ptr;
442
443   plen = 0;
444   while (p != NULL)
445   {
446     base = plen;
447     plen += p->len;
448     if (ofs < plen)
449     {
450       msg_ptr = p->payload;
451       msg_ptr += ofs - base;
452
453       if (ident_len > 1)
454       {
455         if ((ident[0] == 1) && (ident[1] == 3))
456         {
457           /* compressed (most common) prefix .iso.org */
458           *msg_ptr = 0x2b;
459         }
460         else
461         {
462           /* calculate prefix */
463           *msg_ptr = (ident[0] * 40) + ident[1];
464         }
465         ofs += 1;
466         if (ofs >= plen)
467         {
468           /* next octet in next pbuf */
469           p = p->next;
470           if (p == NULL) { return ERR_ARG; }
471           msg_ptr = p->payload;
472           plen += p->len;
473         }
474         else
475         {
476           /* next octet in same pbuf */
477           msg_ptr++;
478         }
479         ident_len -= 2;
480         ident += 2;
481       }
482       else
483       {
484 /* @bug:  allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression??  */
485         /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */
486         return ERR_ARG;
487       }
488       while (ident_len > 0)
489       {
490         s32_t sub_id;
491         u8_t shift, tail;
492
493         ident_len--;
494         sub_id = *ident;
495         tail = 0;
496         shift = 28;
497         while(shift > 0)
498         {
499           u8_t code;
500
501           code = sub_id >> shift;
502           if ((code != 0) || (tail != 0))
503           {
504             tail = 1;
505             *msg_ptr = code | 0x80;
506             ofs += 1;
507             if (ofs >= plen)
508             {
509               /* next octet in next pbuf */
510               p = p->next;
511               if (p == NULL) { return ERR_ARG; }
512               msg_ptr = p->payload;
513               plen += p->len;
514             }
515             else
516             {
517               /* next octet in same pbuf */
518               msg_ptr++;
519             }
520           }
521           shift -= 7;
522         }
523         *msg_ptr = (u8_t)sub_id & 0x7F;
524         if (ident_len > 0)
525         {
526           ofs += 1;
527           if (ofs >= plen)
528           {
529             /* next octet in next pbuf */
530             p = p->next;
531             if (p == NULL) { return ERR_ARG; }
532             msg_ptr = p->payload;
533             plen += p->len;
534           }
535           else
536           {
537             /* next octet in same pbuf */
538             msg_ptr++;
539           }
540         }
541         /* proceed to next sub-identifier */
542         ident++;
543       }
544       return ERR_OK;
545     }
546     p = p->next;
547   }
548   /* p == NULL, ofs >= plen */
549   return ERR_ARG;
550 }
551
552 /**
553  * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg.
554  *
555  * @param p points to output pbuf to encode raw data into
556  * @param ofs points to the offset within the pbuf chain
557  * @param raw_len raw data length
558  * @param raw points raw data
559  * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
560  */
561 err_t
562 snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u8_t raw_len, u8_t *raw)
563 {
564   u16_t plen, base;
565   u8_t *msg_ptr;
566
567   plen = 0;
568   while (p != NULL)
569   {
570     base = plen;
571     plen += p->len;
572     if (ofs < plen)
573     {
574       msg_ptr = p->payload;
575       msg_ptr += ofs - base;
576
577       while (raw_len > 1)
578       {
579         /* copy raw_len - 1 octets */
580         raw_len--;
581         *msg_ptr = *raw;
582         raw++;
583         ofs += 1;
584         if (ofs >= plen)
585         {
586           /* next octet in next pbuf */
587           p = p->next;
588           if (p == NULL) { return ERR_ARG; }
589           msg_ptr = p->payload;
590           plen += p->len;
591         }
592         else
593         {
594           /* next octet in same pbuf */
595           msg_ptr++;
596         }
597       }
598       if (raw_len > 0)
599       {
600         /* copy last or single octet */
601         *msg_ptr = *raw;
602       }
603       return ERR_OK;
604     }
605     p = p->next;
606   }
607   /* p == NULL, ofs >= plen */
608   return ERR_ARG;
609 }
610
611 #endif /* LWIP_SNMP */