Dangerous SNMP
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Pages
asn1.hpp
1 #pragma once
2 
3 #include "dangerous/snmp/types.hpp"
4 #include "dangerous/snmp/logger.hpp"
5 
6 #include <type_traits>
7 
8 #include <iostream>
9 
10 namespace dangerous { namespace snmp { namespace asn1 {
11 
12 namespace helper {
13 
14 class length {
15 public:
16  static unsigned int byteSize( unsigned int length ) {
17  unsigned int size = 1;
18  // Anything less than 128 (that is, 0-127) can fit in a single byte.
19  // Thus, their size will be "1".
20  // However, anything 128 or larger MUST be encoded in multiple bytes.
21  // The FIRST byte is "1xxxxxxx", where "xxxxxxx" is the number of bytes
22  // to read.
23  if( length >= 128 ) {
24  // So, in addition to the one byte that we already have (for the count of bytes),
25  // we're also going to need a byte for storing the actual size.
26  size++;
27  if( length > 0xFFFFFFFF ) {
28  size += 4;
29  } else if( length > 0xFFFFFF ) {
30  size += 3;
31  } else if( length > 0xFFFF ) {
32  size += 2;
33  } else if( length > 0xFF ) {
34  size += 1;
35  }
36  }
37  return size;
38  }
39 
40  static unsigned int bytes( unsigned int length, char* buffer, unsigned int bufferLength ) {
41  unsigned int size = byteSize( length );
42  if( logger.system( Logger::ENCODING ) ) {
43  logger.out() << "length::bytes: length=" << length << ": size=" << size << std::endl;
44  }
45  if( bufferLength < size ) {
46  return 0;
47  }
48 
49  if( length < 128 ) {
50  *buffer = length & 0xFF;
51  if( logger.system( Logger::ENCODING ) ) {
52  logger.out() << "length::bytes: length=" << length << ": A -> " << (int)( *buffer ) << std::endl;
53  }
54  buffer++;
55  } else {
56  *buffer = 0x80 | ( size - 1 );
57  if( logger.system( Logger::ENCODING ) ) {
58  logger.out() << "length::bytes: length=" << length << ": B -> " << (int)( *buffer ) << std::endl;
59  }
60  buffer++;
61 
62  for( unsigned int i = 0; i < size - 1; i++ ) {
63  *buffer = ( length >> ( ( (size-1) - 1 - i ) * 8 ) ) & 0xFF;
64  if( logger.system( Logger::ENCODING ) ) {
65  logger.out() << "length::bytes: length=" << length << ": C -> " << (int)( *buffer ) << std::endl;
66  }
67  buffer++;
68  }
69  }
70 
71  return size;
72  }
73 };
74 
75 class SEQUENCE {
76 public:
77  static const int TYPE = 0x30;
78 
79  static unsigned int byteSize( unsigned int contentLength ) {
80  unsigned int size = 1; //< For the type, which is 0x30.
81  size += helper::length::byteSize( contentLength );
82  return size;
83  }
84 
85  static unsigned int bytes( unsigned int contentLength, char* buffer, unsigned int bufferSize ) {
86  unsigned int size = byteSize( contentLength );
87  if( bufferSize < size ) {
88  return 0;
89  }
90 
91  *buffer = (char)TYPE;
92  buffer++;
93  bufferSize--;
94 
95  unsigned int increment = helper::length::bytes( contentLength, buffer, bufferSize );
96  buffer += increment;
97  bufferSize -= increment;
98 
99  return size;
100  }
101 };
102 
103 }
104 
105 class Class {
106 public:
107  static const int UNIVERSAL = 0b00;
108  static const int APPLICATION = 0b01;
109  static const int CONTEXT_SPECIFIC = 0b10;
110  static const int PRIVATE = 0b11;
111 };
112 
113 class Content {
114 public:
115  static const int PRIMITIVE = 0b0;
116  static const int CONSTRUCTED = 0b1;
117 };
118 
119 
120 
121 
122 
123 
124 
125 
126 template<typename T>
127 unsigned int encodedSize( const typename T::value_type& value ) {
128  unsigned int length = T::length( value );
129 
130  unsigned int size = 0;
131  size += 1;
132  size += helper::length::byteSize( length );
133  size += length;
134 
135  return size;
136 }
137 
138 
139 
140 
141 
142 
143 
144 
145 namespace type {
146 
147 template< int classBits, int contentBit, int tag, typename myType >
148 class INTEGER {
149 public:
150  static const int TYPE = ( classBits << 6 ) | ( contentBit << 5 ) | ( tag & 0b11111 );
151  typedef myType value_type;
152 
153  static unsigned int length( const value_type& value ) {
154  // Start at the front (most significant part of) the integer
155  // and work backward (toward the least-significant part).
156  //
157  // We'll be throwing out all-zero bytes if the the number is
158  // positive, and we'll be throwing out all-one bytes if it's
159  // negative.
160  //
161  // We'll stop as soon as we hit a significant value, since that
162  // will tell us the minimum number of bytes required to encode
163  // it.
164 
169  bool isNegative = std::is_signed<myType>::value && ( ( value >> ( ( sizeof(myType) - 1 ) * 8 ) ) & 0x80 ) == 0x80;
170  if( logger.system( Logger::ENCODING ) ) {
171  logger.out() << "Integer " << value << " is " << ( isNegative ? "negative" : "positive" ) << std::endl;
172  }
173 
176  unsigned int size = sizeof(myType);
177  for( unsigned int i = 0; i < sizeof(myType); i++ ) {
179  uint8_t byte = ( value >> ( ( sizeof(myType) - 1 - i ) * 8 ) ) & 0xFF;
180  if( logger.system( Logger::ENCODING ) ) {
181  logger.out() << "Integer " << value << ", byte [ " << i << " ] has a value of " << std::hex << (int)byte << std::dec << "." << std::endl;
182  }
183 
184  if( ( ! isNegative && byte == 0x00 ) || ( isNegative && byte == 0xFF ) ) {
185  size--;
186  continue;
187  }
188 
189  // We have hit a significant byte. We're done.
190 
191  // Positive values only:
192  // But, before we stop, we need to see if our significant
193  // byte begins with a "1". If it does, then we'll need to add
194  // another byte on at the beginning to demonstrate that this
195  // is not a negative number.
196  if( ! isNegative && ( byte & 0x80 ) == 0x80 ) {
197  size++;
198  }
199  break;
200  }
201  if( size == 0 ) {
202  size = 1;
203  }
204  if( logger.system( Logger::ENCODING ) ) {
205  logger.out() << "Integer " << value << " has an encoded length of " << size << "." << std::endl;
206  }
207 
208  return size;
209  }
210 
211  static bool write( const value_type& value, char* buffer, unsigned int bufferSize ) {
212  if( logger.system( Logger::ENCODING ) ) {
213  logger.out() << "INTEGER<...>::write: Value is " << value << "." << std::endl;
214  }
219  bool isNegative = std::is_signed<myType>::value && ( ( value >> ( ( sizeof(myType) - 1 ) * 8 ) ) & 0x80 ) == 0x80;
220  if( logger.system( Logger::ENCODING ) ) {
221  logger.out() << "INTEGER<...>::write: Sign is '" << ( isNegative ? "-" : "+" ) << "'." << std::endl;
222  }
223 
224  for( unsigned int i = 0; i < sizeof(myType); i++ ) {
226  uint8_t byte = ( value >> ( ( sizeof(myType) - 1 - i ) * 8 ) ) & 0xFF;
227 
228  if( ( ! isNegative && byte == 0x00 ) || ( isNegative && byte == 0xFF ) ) {
229  // We would ordinarily skip is byte, but this is the LAST
230  // byte that's going to happen, so we have to write it.
231  if( i + 1 == sizeof(myType) ) {
232  if( logger.system( Logger::ENCODING ) ) {
233  logger.out() << "INTEGER<...>::write: Adding last [trivial] byte " << ((int)byte) << "." << std::endl;
234  }
235  *buffer = (char)byte;
236  buffer++;
237  bufferSize--;
238  break;
239  }
240  continue;
241  }
242 
243  // We have hit a significant byte. It's time to actually start
244  // encoding this integer..
245 
246  // Positive values only:
247  // But, before we start, we need to see if our significant
248  // byte begins with a "1". If it does, then we'll need to add
249  // another byte on at the beginning to demonstrate that this
250  // is not a negative number.
251  if( ! isNegative && ( byte & 0x80 ) == 0x80 ) {
252  if( logger.system( Logger::ENCODING ) ) {
253  logger.out() << "INTEGER<...>::write: Adding first byte 0x00." << std::endl;
254  }
255  *buffer = 0x00;
256  buffer++;
257  bufferSize--;
258  }
259 
260  for( ; i < sizeof(myType); i++ ) {
262  uint8_t byte = ( value >> ( ( sizeof(myType) - 1 - i ) * 8 ) ) & 0xFF;
263  if( logger.system( Logger::ENCODING ) ) {
264  logger.out() << "INTEGER<...>::write: Adding byte " << ((int)byte) << "." << std::endl;
265  }
266 
267  *buffer = (char)byte;
268  buffer++;
269  bufferSize--;
270  }
271 
272  break;
273  }
274 
275  return true;
276  }
277 
278  static value_type read( value_type& value, const char* buffer, unsigned int bufferSize ) {
279  value = 0;
280 
281  bool isNegative = false;
282  for( unsigned int i = 0; i < bufferSize; i++ ) {
283  uint8_t byte = buffer[ i ];
284  if( logger.system( Logger::DECODING ) ) {
285  logger.out() << "INTEGER: read: byte[ " << i << " ]: " << (int)byte << std::endl;
286  }
287 
288  if( i == 0 ) {
289  isNegative = std::is_signed<myType>::value && ( byte & 0x80 ) == 0x80;
290  // If this value is negative, we're going to strip the leading bit from it.
291  // We'll use it at the end.
292  if( isNegative ) {
293  byte &= 0x7f;
294  }
295  }
296 
297  value <<= 8;
298  value |= ( byte & 0xFF );
299  }
300 
301  if( value != 0 && isNegative ) {
302  value -= 0x80 << ( ( bufferSize - 1 ) * 8 );
303  }
304 
305  return true;
306  }
307 };
308 
309 template< int classBits, int contentBit, int tag >
311 public:
312  static const int TYPE = ( classBits << 6 ) | ( contentBit << 5 ) | ( tag & 0b11111 );
313  typedef std::string value_type;
314 
315  static unsigned int length( const value_type& text ) {
316  return text.length();
317  }
318 
319  static bool write( const value_type& text, char* buffer, unsigned int bufferSize ) {
320  for( unsigned int i = 0, iSize = text.length(); i < iSize; i++ ) {
321  *buffer = text[i];
322  buffer++;
323  bufferSize--;
324  }
325 
326  return true;
327  }
328 
329  static bool read( value_type& value, const char* buffer, unsigned int bufferSize ) {
330  if( logger.system( Logger::DECODING ) ) {
331  logger.out() << "Clearing value." << std::endl;
332  }
333  value.clear();
334  if( logger.system( Logger::DECODING ) ) {
335  logger.out() << "Resizing string to length=" << bufferSize << "." << std::endl;
336  }
337  value.resize( bufferSize );
338 
339  for( unsigned int i = 0; i < bufferSize; i++ ) {
340  uint8_t byte = buffer[ i ];
341  if( logger.system( Logger::DECODING ) ) {
342  logger.out() << "OCTET_STRING: read: byte[ " << i << " ]: " << (int)byte << std::endl;
343  }
344 
345  value[ i ] = byte;
346  }
347 
348  return true;
349  }
350 };
351 
355 template< int classBits, int contentBit, int tag >
356 class NULL_TYPE {
357 public:
358  static const int TYPE = ( classBits << 6 ) | ( contentBit << 5 ) | ( tag & 0b11111 );
359 
360  typedef uint8_t value_type; //< This is actually bogus, but it must fit our model.
361 
362  static uint8_t throwaway; //< You can pass this as the valeu to all of the NULL_TYPE functions.
363 
364  static unsigned int length( const value_type& value ) {
365  return 0;
366  }
367 
368  static bool write( const value_type& value, char* buffer, unsigned int bufferSize ) {
369  return true;
370  }
371 
377  static bool read( value_type& value, const char* buffer, unsigned int bufferSize ) {
378  return true;
379  }
380 };
381 
382 template< int classBits, int contentBit, int tag >
383 uint8_t NULL_TYPE<classBits,contentBit,tag>::throwaway = 0;
384 
385 template< int classBits, int contentBit, int tag >
387 public:
388  static const int TYPE = ( classBits << 6 ) | ( contentBit << 5 ) | ( tag & 0b11111 );
389 
392 
393  static unsigned int length( const value_type& oid ) {
394  unsigned int size = 0;
395 
396  if( oid.numbers.size() >= 1 ) {
397  size += 1;
398  }
399  for( unsigned int i = 2, iSize = oid.numbers.size(); i < iSize; i++ ) {
400  unsigned int number = oid.numbers[ i ];
401  size += 1;
402  number >>= 7;
403  while( number > 0 ) {
404  size += 1;
405  number >>= 7;
406  }
407  }
408 
409  return size;
410  }
411 
412  static bool write( const value_type& oid, char* buffer, unsigned int bufferSize ) {
413  // If this OID, somehow, has zero length, then we truly cannot actually _write_
414  // anything. In this case, we will simply return with success.
415  if( oid.numbers.size() == 0 ) {
416  return true; //< TODO: Return false? Is this an error condition?
417  }
418 
419  // An OID MUST have at least two numbers in it in order to be encoded.
420  if( oid.numbers.size() < 2 ) {
421  return false;
422  }
423 
424  // The BER for an OID include a special provision for the first two numbers in
425  // the OID. They are to be encoded in a _single byte_.
426  // Byte #1 := ( 40 * number1 ) + ( number2 )
427  //
428  // To accomplish this, we will be first extracting those two numbers, handling
429  // them, and then moving on to the rest of the OID proper.
430 
432  unsigned int number1 = oid.numbers[ 0 ];
434  unsigned int number2 = oid.numbers[ 1 ];
435 
436  if( number1 >= 40 ) {
437  if( logger.system( Logger::ENCODING ) ) {
438  logger.out() << "OID component #1 (" << number1 << ") is too large (must be between 0 and 39)." << std::endl;
439  }
440  return false;
441  }
442  if( number2 >= 40 ) {
443  if( logger.system( Logger::ENCODING ) ) {
444  logger.out() << "OID component #2 (" << number2 << ") is too large (must be between 0 and 39)." << std::endl;
445  }
446  return false;
447  }
448 
449  //DEBUG:std::cout << "OBJECT_IDENTIFIER::write: buffer is " << (void*)buffer << "; bufferSize is " << bufferSize << std::endl;
450  *buffer = 40 * number1 + number2;
451 
452  buffer += 1;
453  bufferSize -= 1;
454 
455  for( unsigned int i = 2, iSize = oid.numbers.size(); i < iSize; i++ ) {
456  unsigned int number = oid.numbers[ i ];
457 
458  int length = 1;
459  number >>= 7;
460  while( number > 0 ) {
461  length += 1;
462  number >>= 7;
463  }
464 
465  number = oid.numbers[ i ];
466 
467  // We're going to start from the right-hand side of the number and
468  // move to the left, 7-bits at a time. For the farthest-right encoded
469  // byte, the first bit _must_ be 0, which means that there are _no more_
470  // bytes.
471  //
472  // As we move left (that is, "l" is greater than zero), the first bit
473  // _must_ be 1 in order to reflect the fact there are more encoded bytes
474  // to the right.
475  for( int l = 0; l < length; l++ ) {
476  // The current byte is equal to:
477  // {0,1} followed by the last 7-bits of the current number.
478  // * "0" if this is the farthest-right bit.
479  // * "1" otherwise.
480  buffer[ length - 1 - l ] = ( ( l > 0 ) ? 0x80 : 0x00 ) | ( number & 0x7F );
481  number >>= 7;
482  }
483 
484 
485  buffer += length;
486  bufferSize -= length;
487  }
488 
489  return true;
490  }
491 
492  static bool read( value_type& oid, const char* buffer, unsigned int bufferSize ) {
493  if( bufferSize < 1 ) {
494  return false;
495  }
496 
497  // Read the first two numbers, since they're both encoded on
498  // the first byte.
499  unsigned int number2 = (int)(char)(*buffer) % 40;
500  unsigned int number1 = ( (int)(char)(*buffer) - number2 ) / 40;
501  if( logger.system( Logger::DECODING ) ) {
502  logger.out() << "." << number1 << "." << number2 << std::endl;
503  }
504  oid.numbers.push_back( number1 );
505  oid.numbers.push_back( number2 );
506 
507  buffer++;
508  bufferSize--;
509 
510  while( bufferSize > 0 ) {
511  unsigned int number = 0;
512  while( *buffer & 0x80 ) {
513  number <<= 7;
514  number |= ( *buffer & 0x7F );
515 
516  buffer++;
517  bufferSize--;
518  if( bufferSize == 0 ) {
519  return false;
520  }
521  }
522  number <<= 7;
523  number |= ( *buffer & 0x7F );
524  oid.numbers.push_back( number );
525  if( logger.system( Logger::DECODING ) ) {
526  logger.out() << " ." << number << std::endl;
527  }
528 
529  buffer++;
530  bufferSize--;
531  }
532  if( logger.system( Logger::DECODING ) ) {
533  logger.out() << "done." << std::endl;
534  }
535 
536  return true;
537  }
538 };
539 
540 }
541 
542 } } }
543 
bool system(System system)
This returns the logging status for the given system.
Definition: logger.hpp:47
This class allows for the encoding and decoding of null values.
Definition: asn1.hpp:356
std::ostream & out()
This returns the output stream for the Logger.
Definition: logger.hpp:77
Definition: asn1.hpp:113
std::vector< unsigned int > numbers
This is the vector that represents the integers that make up the OID.
Definition: numericoid.hpp:57
Definition: asn1.hpp:148
A NumericOid represents an "true" OID; that is, one identified by a series of integers (as opposed to...
Definition: numericoid.hpp:22
static bool write(const value_type &value, char *buffer, unsigned int bufferSize)
Definition: asn1.hpp:211
static unsigned int length(const value_type &value)
Definition: asn1.hpp:153
Logger logger
We will expose a global Logger instance.
Logging related to decoding messages.
Definition: logger.hpp:21
static bool read(value_type &value, const char *buffer, unsigned int bufferSize)
In terms of reading an ASN.1 NULL value goes, by the time that we've gotten to this function...
Definition: asn1.hpp:377
NumericOid value_type
This class operates on a NumericType.
Definition: asn1.hpp:391
Definition: asn1.hpp:105
static bool write(const value_type &oid, char *buffer, unsigned int bufferSize)
Definition: asn1.hpp:412