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 */
49void
50snmp_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 */
76void
77snmp_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 */
109void
110snmp_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 */
141void
142snmp_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 */
180err_t
181snmp_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 = (u8_t*)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 */
212err_t
213snmp_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 = (u8_t*)p->payload;
226      msg_ptr += ofs - base;
227
228      if (length < 0x80)
229      {
230        *msg_ptr = (u8_t)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 = (u8_t*)p->payload;
243        }
244        else
245        {
246          /* next octet in same pbuf */
247          msg_ptr++;
248        }
249        *msg_ptr = (u8_t)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 = (u8_t*)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 = (u8_t)length;
280          }
281          else
282          {
283            /* most significant length octet */
284            *msg_ptr = (u8_t)(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 */
307err_t
308snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u16_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 = (u8_t*)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 = (u8_t*)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 = (u8_t)(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 = (u8_t*)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 = (u8_t)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 */
383err_t
384snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u16_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 = (u8_t*)p->payload;
397      msg_ptr += ofs - base;
398
399      while (octets_needed > 1)
400      {
401        octets_needed--;
402        *msg_ptr = (u8_t)(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 = (u8_t*)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 = (u8_t)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 */
437err_t
438snmp_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 = (u8_t*)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 = (u8_t)((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 = (u8_t*)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 = (u8_t)(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 = (u8_t*)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 = (u8_t*)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 */
561err_t
562snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u16_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 = (u8_t*)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 = (u8_t*)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 */
612