Contiki-NG
snmp-ber.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2019-2020 Yago Fontoura do Rosario <yago.rosario@hotmail.com.br>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  *
14  * 3. Neither the name of the copyright holder nor the names of its
15  * contributors may be used to endorse or promote products derived
16  * from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29  * OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 /*---------------------------------------------------------------------------*/
32 
33 /**
34  * \file
35  * SNMP Implementation of the BER encoding
36  * \author
37  * Yago Fontoura do Rosario <yago.rosario@hotmail.com.br
38  */
39 
40 #include "contiki.h"
41 
42 #include "snmp.h"
43 #include "snmp-ber.h"
44 
45 #define LOG_MODULE "SNMP [ber]"
46 #define LOG_LEVEL LOG_LEVEL_SNMP
47 
48 /*---------------------------------------------------------------------------*/
49 static inline int
50 snmp_ber_encode_unsigned_integer(snmp_packet_t *snmp_packet, uint8_t type, uint32_t number)
51 {
52  uint16_t original_out_len;
53 
54  original_out_len = snmp_packet->used;
55  do {
56  if(snmp_packet->used == snmp_packet->max) {
57  return 0;
58  }
59 
60  *snmp_packet->out-- = (uint8_t)number & 0xFF;
61  snmp_packet->used++;
62  /* I'm not sure why but on MSPGCC the >> 8 operation goes haywire here */
63 #ifdef __MSPGCC__
64  number >>= 4;
65  number >>= 4;
66 #else /* __MSPGCC__ */
67  number >>= 8;
68 #endif /* __MSPGCC__ */
69  } while(number);
70 
71  if(!snmp_ber_encode_length(snmp_packet, snmp_packet->used - original_out_len)) {
72  return 0;
73  }
74 
75  if(!snmp_ber_encode_type(snmp_packet, type)) {
76  return 0;
77  }
78 
79  return 1;
80 }
81 /*---------------------------------------------------------------------------*/
82 int
83 snmp_ber_encode_type(snmp_packet_t *snmp_packet, uint8_t type)
84 {
85  if(snmp_packet->used == snmp_packet->max) {
86  return 0;
87  }
88 
89  *snmp_packet->out-- = type;
90  snmp_packet->used++;
91 
92  return 1;
93 }
94 /*---------------------------------------------------------------------------*/
95 int
96 snmp_ber_encode_length(snmp_packet_t *snmp_packet, uint16_t length)
97 {
98  if(length > 0xFF) {
99  if(snmp_packet->used == snmp_packet->max) {
100  return 0;
101  }
102 
103  *snmp_packet->out-- = (uint8_t)length & 0xFF;
104  snmp_packet->used++;
105 
106  if(snmp_packet->used == snmp_packet->max) {
107  return 0;
108  }
109 
110  *snmp_packet->out-- = (uint8_t)(length >> 8) & 0xFF;
111  snmp_packet->used++;
112 
113  if(snmp_packet->used == snmp_packet->max) {
114  return 0;
115  }
116 
117  *snmp_packet->out-- = 0x82;
118  snmp_packet->used++;
119  } else if(length > 0x7F) {
120  if(snmp_packet->used == snmp_packet->max) {
121  return 0;
122  }
123 
124  *snmp_packet->out-- = (uint8_t)length & 0xFF;
125  snmp_packet->used++;
126 
127  if(snmp_packet->used == snmp_packet->max) {
128  return 0;
129  }
130 
131  *snmp_packet->out-- = 0x81;
132  snmp_packet->used++;
133  } else {
134  if(snmp_packet->used == snmp_packet->max) {
135  return 0;
136  }
137 
138  *snmp_packet->out-- = (uint8_t)length & 0x7F;
139  snmp_packet->used++;
140  }
141 
142  return 1;
143 }
144 /*---------------------------------------------------------------------------*/
145 int
146 snmp_ber_encode_timeticks(snmp_packet_t *snmp_packet, uint32_t timeticks)
147 {
148  return snmp_ber_encode_unsigned_integer(snmp_packet, BER_DATA_TYPE_TIMETICKS, timeticks);
149 }
150 /*---------------------------------------------------------------------------*/
151 int
152 snmp_ber_encode_integer(snmp_packet_t *snmp_packet, uint32_t number)
153 {
154  return snmp_ber_encode_unsigned_integer(snmp_packet, BER_DATA_TYPE_INTEGER, number);
155 }
156 /*---------------------------------------------------------------------------*/
157 int
158 snmp_ber_encode_string_len(snmp_packet_t *snmp_packet, const char *str, uint32_t length)
159 {
160  uint32_t i;
161 
162  if(length > 0) {
163  str += length - 1;
164  for(i = 0; i < length; ++i) {
165  if(snmp_packet->used == snmp_packet->max) {
166  return 0;
167  }
168 
169  *snmp_packet->out-- = (uint8_t)*str--;
170  snmp_packet->used++;
171  }
172  }
173 
174  if(!snmp_ber_encode_length(snmp_packet, length)) {
175  return 0;
176  }
177 
179  return 0;
180  }
181 
182  return 1;
183 }
184 /*---------------------------------------------------------------------------*/
185 int
187 {
188  uint32_t val;
189  uint16_t original_out_len;
190  uint8_t pos;
191 
192  original_out_len = snmp_packet->used;
193 
194  pos = oid->length - 1;
195  while(pos) {
196  val = oid->data[pos];
197 
198  if(snmp_packet->used == snmp_packet->max) {
199  return 0;
200  }
201 
202  *snmp_packet->out-- = (uint8_t)(val & 0x7F);
203  snmp_packet->used++;
204  val >>= 7;
205 
206  while(val) {
207  if(snmp_packet->used == snmp_packet->max) {
208  return 0;
209  }
210 
211  *snmp_packet->out-- = (uint8_t)((val & 0x7F) | 0x80);
212  snmp_packet->used++;
213 
214  val >>= 7;
215  }
216  pos--;
217  }
218 
219  if(snmp_packet->used == snmp_packet->max) {
220  return 0;
221  }
222 
223  val = *(snmp_packet->out + 1) + 40 * oid->data[pos];
224  snmp_packet->used--;
225  snmp_packet->out++;
226 
227  if(snmp_packet->used == snmp_packet->max) {
228  return 0;
229  }
230 
231  *snmp_packet->out-- = (uint8_t)(val & 0x7F);
232  snmp_packet->used++;
233 
234  val >>= 7;
235 
236  while(val) {
237  if(snmp_packet->used == snmp_packet->max) {
238  return 0;
239  }
240 
241  *snmp_packet->out-- = (uint8_t)((val & 0x7F) | 0x80);
242  snmp_packet->used++;
243 
244  val >>= 7;
245  }
246 
247  if(!snmp_ber_encode_length(snmp_packet, snmp_packet->used - original_out_len)) {
248  return 0;
249  }
250 
252  return 0;
253  }
254 
255  return 1;
256 }
257 /*---------------------------------------------------------------------------*/
258 int
259 snmp_ber_encode_null(snmp_packet_t *snmp_packet, uint8_t type)
260 {
261 
262  if(snmp_packet->used == snmp_packet->max) {
263  return 0;
264  }
265 
266  *snmp_packet->out-- = 0x00;
267  snmp_packet->used++;
268 
269  return snmp_ber_encode_type(snmp_packet, type);
270 }
271 /*---------------------------------------------------------------------------*/
272 static inline int
273 snmp_ber_decode_unsigned_integer(snmp_packet_t *snmp_packet, uint8_t expected_type, uint32_t *num)
274 {
275  uint8_t i, len, type;
276 
277  if(!snmp_ber_decode_type(snmp_packet, &type)) {
278  return 0;
279  }
280 
281  if(type != expected_type) {
282  /*
283  * Sanity check
284  * Invalid type in buffer
285  */
286  return 0;
287  }
288 
289  if(!snmp_ber_decode_length(snmp_packet, &len)) {
290  return 0;
291  }
292 
293  if(len > 4) {
294  /*
295  * Sanity check
296  * It will not fit in the uint32_t
297  */
298  return 0;
299  }
300 
301  if(snmp_packet->used == 0) {
302  return 0;
303  }
304 
305  *num = (uint32_t)(*snmp_packet->in++ & 0xFF);
306  snmp_packet->used--;
307 
308  for(i = 1; i < len; ++i) {
309  *num <<= 8;
310  if(snmp_packet->used == 0) {
311  return 0;
312  }
313  *num |= (uint8_t)(*snmp_packet->in++ & 0xFF);
314  snmp_packet->used--;
315  }
316 
317  return 1;
318 }
319 /*---------------------------------------------------------------------------*/
320 int
321 snmp_ber_decode_type(snmp_packet_t *snmp_packet, uint8_t *type)
322 {
323  if(snmp_packet->used == 0) {
324  return 0;
325  }
326 
327  *type = *snmp_packet->in++;
328  snmp_packet->used--;
329 
330  return 1;
331 }
332 /*---------------------------------------------------------------------------*/
333 int
334 snmp_ber_decode_length(snmp_packet_t *snmp_packet, uint8_t *length)
335 {
336  if(snmp_packet->used == 0) {
337  return 0;
338  }
339 
340  *length = *snmp_packet->in++;
341  snmp_packet->used--;
342 
343  return 1;
344 }
345 /*---------------------------------------------------------------------------*/
346 int
347 snmp_ber_decode_timeticks(snmp_packet_t *snmp_packet, uint32_t *timeticks)
348 {
349  return snmp_ber_decode_unsigned_integer(snmp_packet, BER_DATA_TYPE_TIMETICKS, timeticks);
350 }
351 /*---------------------------------------------------------------------------*/
352 int
353 snmp_ber_decode_integer(snmp_packet_t *snmp_packet, uint32_t *num)
354 {
355  return snmp_ber_decode_unsigned_integer(snmp_packet, BER_DATA_TYPE_INTEGER, num);
356 }
357 /*---------------------------------------------------------------------------*/
358 int
359 snmp_ber_decode_string_len_buffer(snmp_packet_t *snmp_packet, const char **str, uint32_t *length)
360 {
361  uint8_t type, i, length_bytes;
362 
363  if(!snmp_ber_decode_type(snmp_packet, &type)) {
364  return 0;
365  }
366 
367  if(type != BER_DATA_TYPE_OCTET_STRING) {
368  /*
369  * Sanity check
370  * Invalid type in buffer
371  */
372  return 0;
373  }
374 
375  if((*snmp_packet->in & 0x80) == 0) {
376 
377  if(snmp_packet->used == 0) {
378  return 0;
379  }
380 
381  *length = (uint32_t)*snmp_packet->in++;
382  snmp_packet->used--;
383  } else {
384 
385  if(snmp_packet->used == 0) {
386  return 0;
387  }
388 
389  length_bytes = (uint8_t)(*snmp_packet->in++ & 0x7F);
390  snmp_packet->used--;
391 
392  if(length_bytes > 4) {
393  /*
394  * Sanity check
395  * It will not fit in the uint32_t
396  */
397  return 0;
398  }
399 
400  if(snmp_packet->used == 0) {
401  return 0;
402  }
403 
404  *length = (uint32_t)*snmp_packet->in++;
405  snmp_packet->used--;
406 
407  for(i = 1; i < length_bytes; ++i) {
408  *length <<= 8;
409 
410  if(snmp_packet->used == 0) {
411  return 0;
412  }
413 
414  *length |= *snmp_packet->in++;
415  snmp_packet->used--;
416  }
417  }
418 
419  *str = (const char *)snmp_packet->in;
420 
421  if(snmp_packet->used == 0 || snmp_packet->used < *length) {
422  return 0;
423  }
424 
425  snmp_packet->used -= *length;
426  snmp_packet->in += *length;
427 
428  return 1;
429 }
430 /*---------------------------------------------------------------------------*/
431 int
433 {
434  uint8_t *buf_end, type;
435  uint8_t len, j;
436  div_t first;
437 
438  if(!snmp_ber_decode_type(snmp_packet, &type)) {
439  return 0;
440  }
441 
442  if(type != BER_DATA_TYPE_OBJECT_IDENTIFIER) {
443  return 0;
444  }
445 
446  if(!snmp_ber_decode_length(snmp_packet, &len)) {
447  return 0;
448  }
449 
450  buf_end = snmp_packet->in + len;
451 
452  if(snmp_packet->used == 0) {
453  return 0;
454  }
455 
456  snmp_packet->used--;
457  first = div(*snmp_packet->in++, 40);
458 
459  oid->length = 0;
460 
461  oid->data[oid->length++] = (uint32_t)first.quot;
462  oid->data[oid->length++] = (uint32_t)first.rem;
463 
464  while(snmp_packet->in != buf_end) {
465  if(oid->length >= SNMP_MSG_OID_MAX_LEN) {
466  return 0;
467  }
468 
469  if(snmp_packet->used == 0) {
470  return 0;
471  }
472  oid->data[oid->length] = (uint32_t)(*snmp_packet->in & 0x7F);
473  for(j = 0; j < 4; j++) {
474  snmp_packet->used--;
475  if((*snmp_packet->in++ & 0x80) == 0) {
476  break;
477  }
478 
479  if(snmp_packet->used == 0) {
480  return 0;
481  }
482 
483  oid->data[oid->length] <<= 7;
484  oid->data[oid->length] |= (*snmp_packet->in & 0x7F);
485  }
486 
487  oid->length++;
488  }
489 
490  return 1;
491 }
492 /*---------------------------------------------------------------------------*/
493 int
495 {
496  if(snmp_packet->used == 0) {
497  return 0;
498  }
499 
500  snmp_packet->in++;
501  snmp_packet->used--;
502 
503  if(snmp_packet->used == 0) {
504  return 0;
505  }
506 
507  snmp_packet->in++;
508  snmp_packet->used--;
509 
510  return 1;
511 }
512 /*---------------------------------------------------------------------------*/
SNMP Implementation of the process
#define BER_DATA_TYPE_TIMETICKS
TimeTicks.
Definition: snmp-ber.h:102
int snmp_ber_decode_null(snmp_packet_t *snmp_packet)
Decodes a null.
Definition: snmp-ber.c:494
The packet struct.
Definition: snmp.h:206
int snmp_ber_decode_integer(snmp_packet_t *snmp_packet, uint32_t *num)
Decodes an integer.
Definition: snmp-ber.c:353
int snmp_ber_decode_type(snmp_packet_t *snmp_packet, uint8_t *type)
Decodes a type.
Definition: snmp-ber.c:321
int snmp_ber_encode_integer(snmp_packet_t *snmp_packet, uint32_t number)
Encodes an integer.
Definition: snmp-ber.c:152
#define BER_DATA_TYPE_OCTET_STRING
Octet String.
Definition: snmp-ber.h:78
int snmp_ber_encode_timeticks(snmp_packet_t *snmp_packet, uint32_t timeticks)
Encodes a timeticks.
Definition: snmp-ber.c:146
SNMP Implementation of the BER encoding
int snmp_ber_encode_type(snmp_packet_t *snmp_packet, uint8_t type)
Encodes a type.
Definition: snmp-ber.c:83
int snmp_ber_decode_length(snmp_packet_t *snmp_packet, uint8_t *length)
Decodes a length.
Definition: snmp-ber.c:334
int snmp_ber_decode_string_len_buffer(snmp_packet_t *snmp_packet, const char **str, uint32_t *length)
Decodes a string.
Definition: snmp-ber.c:359
int snmp_ber_decode_oid(snmp_packet_t *snmp_packet, snmp_oid_t *oid)
Decodes an OID.
Definition: snmp-ber.c:432
int snmp_ber_encode_string_len(snmp_packet_t *snmp_packet, const char *str, uint32_t length)
Encodes a string.
Definition: snmp-ber.c:158
uint8_t * in
The pointer used for the incoming packet.
Definition: snmp.h:221
int snmp_ber_encode_oid(snmp_packet_t *snmp_packet, snmp_oid_t *oid)
Encodes a Oid.
Definition: snmp-ber.c:186
uint8_t * out
The pointer used for the outgoing packet.
Definition: snmp.h:226
The OID struct.
Definition: snmp.h:144
int snmp_ber_encode_length(snmp_packet_t *snmp_packet, uint16_t length)
Encodes the length.
Definition: snmp-ber.c:96
#define SNMP_MSG_OID_MAX_LEN
Default maximum number of IDs in one OID.
Definition: snmp-conf.h:86
uint32_t data[SNMP_MSG_OID_MAX_LEN]
The OID.
Definition: snmp.h:148
uint16_t used
The number of bytes used.
Definition: snmp.h:211
#define BER_DATA_TYPE_INTEGER
Integer.
Definition: snmp-ber.h:72
uint16_t max
The maximum number of bytes.
Definition: snmp.h:216
snmp_oid_t oid
A OID struct.
Definition: snmp-mib.h:85
#define BER_DATA_TYPE_OBJECT_IDENTIFIER
Object Identifier.
Definition: snmp-ber.h:90
int snmp_ber_encode_null(snmp_packet_t *snmp_packet, uint8_t type)
Encodes a null.
Definition: snmp-ber.c:259
int snmp_ber_decode_timeticks(snmp_packet_t *snmp_packet, uint32_t *timeticks)
Decodes a timeticks.
Definition: snmp-ber.c:347
uint8_t length
The OID length.
Definition: snmp.h:153