Module: SNMP::BER

Defined in:
lib/snmp/ber.rb

Overview

:nodoc:all

Defined Under Namespace

Classes: InvalidLength, InvalidObjectId, InvalidTag, OutOfData

Constant Summary collapse

SNMP_V1 =

SNMP version codes

0
SNMP_V2C =
1
SNMP_V3 =

not supported

3
GetRequest_PDU_TAG =

SNMP context-specific data types See RFC 1157 for SNMPv1 See RFC 1905 for SNMPv2c

0xa0
GetNextRequest_PDU_TAG =
0xa1
Response_PDU_TAG =
0xa2
SetRequest_PDU_TAG =
0xa3
SNMPv1_Trap_PDU_TAG =

Note: valid for SNMPv1 only

0xa4
GetBulkRequest_PDU_TAG =
0xa5
InformRequest_PDU_TAG =
0xa6
SNMPv2_Trap_PDU_TAG =
0xa7
Report_PDU_TAG =

Note: Usage not defined - not supported

0xa8
INTEGER_TAG =

Primitive ASN.1 data types

0x02
OCTET_STRING_TAG =
0x04
NULL_TAG =
0x05
OBJECT_IDENTIFIER_TAG =
0x06
SEQUENCE_TAG =

Constructed ASN.1 data type

0x30
IpAddress_TAG =

SNMP application data types See RFC 1155 for SNMPv1 See RFC 1902 for SNMPv2c

0x40
Counter32_TAG =

Counter in SNMPv1

0x41
Gauge32_TAG =

Gauge in SNMPv1

0x42
Unsigned32_TAG =

Note: same as Gauge32

0x42
TimeTicks_TAG =
0x43
Opaque_TAG =
0x44
Counter64_TAG =
0x46
NoSuchObject_TAG =

VarBind response exceptions

0x80
NoSuchInstance_TAG =
0x81
EndOfMibView_TAG =
0x82

Instance Method Summary collapse

Instance Method Details

#assert_no_remainder(remainder) ⇒ Object

Raises:


81
82
83
# File 'lib/snmp/ber.rb', line 81

def assert_no_remainder(remainder)
  raise ParseError, remainder.inspect if remainder != ""
end

#build_integer(data, start, num_octets) ⇒ Object


154
155
156
157
158
# File 'lib/snmp/ber.rb', line 154

def build_integer(data, start, num_octets)
  number = 0
  num_octets.times { |i| number = number<<8 | data[start+i].ord }
  return number
end

#decode_integer(data) ⇒ Object

Decode TLV data for an ASN.1 integer.

Throws an InvalidTag exception if the tag is incorrect.

Returns a tuple containing an integer and any remaining unprocessed data.

Raises:


125
126
127
128
129
# File 'lib/snmp/ber.rb', line 125

def decode_integer(data)
  tag, value, remainder = decode_tlv(data)
  raise InvalidTag, tag.to_s if tag != INTEGER_TAG
  return decode_integer_value(value), remainder
end

#decode_integer_value(value) ⇒ Object


137
138
139
140
141
142
143
# File 'lib/snmp/ber.rb', line 137

def decode_integer_value(value)
  result = build_integer(value, 0, value.length)
  if value[0].ord[7] == 1
    result -= (1 << (8 * value.length))
  end
  result
end

#decode_ip_address(data) ⇒ Object

Raises:


173
174
175
176
177
178
# File 'lib/snmp/ber.rb', line 173

def decode_ip_address(data)
  tag, value, remainder = decode_tlv(data)
  raise InvalidTag, tag.to_s if tag != IpAddress_TAG
  raise InvalidLength, tag.to_s if value.length != 4
  return value, remainder
end

#decode_object_id(data) ⇒ Object

Unwrap TLV data for an ASN.1 object identifier. This method extracts the OID value as a character string but does not decode it further.

Throws an InvalidTag exception if the tag is incorrect.

Returns a tuple containing the object identifier (OID) and any remaining unprocessed data. The OID is represented as an array of integers.

Raises:


204
205
206
207
208
# File 'lib/snmp/ber.rb', line 204

def decode_object_id(data)
  tag, value, remainder = decode_tlv(data)
  raise InvalidTag, tag.to_s if tag != OBJECT_IDENTIFIER_TAG
  return decode_object_id_value(value), remainder
end

#decode_object_id_value(value) ⇒ Object


210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/snmp/ber.rb', line 210

def decode_object_id_value(value)
  if value.length == 0
    object_id = []
  else
    value0 = value[0].ord
    if value0 == 0x2b
      object_id = [1,3]
    else
      second = value0 % 40
      first = (value0 - second) / 40
      raise InvalidObjectId, value.to_s if first > 2
      object_id = [first, second]
    end
    n = 0
    for i in 1...value.length
      n = (n<<7) + (value[i].ord & 0x7f)
      if value[i].ord < 0x80
        object_id << n
        n = 0
      end
    end
  end
  return object_id
end

#decode_octet_string(data) ⇒ Object

Decode TLV data for an ASN.1 octet string.

Throws an InvalidTag exception if the tag is incorrect.

Returns a tuple containing a string and any remaining unprocessed data.

Raises:


167
168
169
170
171
# File 'lib/snmp/ber.rb', line 167

def decode_octet_string(data)
  tag, value, remainder = decode_tlv(data)
  raise InvalidTag, tag.to_s if tag != OCTET_STRING_TAG
  return value, remainder
end

#decode_sequence(data) ⇒ Object

Decode TLV data for an ASN.1 sequence.

Throws an InvalidTag exception if the tag is incorrect.

Returns a tuple containing the sequence data and any remaining unprocessed data that follows the sequence.

Raises:


188
189
190
191
192
# File 'lib/snmp/ber.rb', line 188

def decode_sequence(data)
  tag, value, remainder = decode_tlv(data)
  raise InvalidTag, tag.to_s if tag != SEQUENCE_TAG
  return value, remainder
end

#decode_timeticks(data) ⇒ Object

Raises:


131
132
133
134
135
# File 'lib/snmp/ber.rb', line 131

def decode_timeticks(data)
  tag, value, remainder = decode_tlv(data)
  raise InvalidTag, tag.to_s if tag != TimeTicks_TAG
  return decode_uinteger_value(value), remainder
end

#decode_tlv(data) ⇒ Object

Decode tag-length-value data. The data is assumed to be a string of bytes in network byte order. This format is returned by Socket#recv.

Returns a tuple containing the tag, the value, and any remaining unprocessed data.

The data is not interpretted by this method. Use one of the other decoding methods to interpret the data.

Note that ASN.1 supports an indefinite length format where the end of content is marked by a pair of 0 octets. SNMP does not support this format, so only the two definite forms are implemented (single byte and multi-byte).

Raises:


100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/snmp/ber.rb', line 100

def decode_tlv(data)
  raise OutOfData if (data.length == 2 && data[1].ord != 0) || data.length < 2
  tag = data[0].ord
  length = data[1].ord
  if length < 0x80
    value = data[2, length]
    remainder = data[length+2..-1]
  else
    # ASN.1 says this octet can't be 0xff
    raise InvalidLength, length.to_s if length == 0xff
    num_octets = length & 0x7f
    length = build_integer(data, 2, num_octets)
    value = data[num_octets+2, length]
    remainder = data[num_octets+2+length..-1]
  end
  return tag, value, remainder
end

#decode_uinteger_value(value) ⇒ Object

Decode an integer, ignoring the sign bit. Some agents insist on encoding 32 bit unsigned integers with four bytes even though it should be 5 bytes (at least the way I read it).


150
151
152
# File 'lib/snmp/ber.rb', line 150

def decode_uinteger_value(value)
  build_integer(value, 0, value.length)
end

#encode_exception(tag) ⇒ Object

Encode an exception. The encoding is simply the exception tag with no data, similar to NULL.


295
296
297
# File 'lib/snmp/ber.rb', line 295

def encode_exception(tag)
  tag.chr << "\000"
end

#encode_integer(value) ⇒ Object

Encode integer


252
253
254
# File 'lib/snmp/ber.rb', line 252

def encode_integer(value)
  encode_tagged_integer(INTEGER_TAG, value)
end

#encode_length(length) ⇒ Object

Encode the length field for TLV data. Returns the length octets as a string.

Raises:


239
240
241
242
243
244
245
246
247
# File 'lib/snmp/ber.rb', line 239

def encode_length(length)
  raise InvalidLength, length.to_s if length < 0
  if length < 0x80
    length.chr
  else
    data = integer_to_octets(length)
    (data.size | 0x80).chr << data
  end
end

#encode_nullObject


287
288
289
# File 'lib/snmp/ber.rb', line 287

def encode_null
  NULL_TAG.chr << "\000"
end

#encode_object_id(value) ⇒ Object

Encode an object id. The input is assumed to be an array of integers representing the object id.

Raises:


327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/snmp/ber.rb', line 327

def encode_object_id(value)
  raise InvalidObjectId, value.to_s if value.length < 1
  raise InvalidObjectId, value.to_s if value[0] > 2
  data = ""
  if (value.length > 1)
    raise InvalidObjectId if value[0] < 2 && value[1] > 40
    data << (40 * value[0] + value[1]).chr
    for i in 2...value.length
      if value[i] < 0x80
        data << value[i].chr
      else
        octets = ""
        n = value[i]
        begin
          octets = (n & 0x7f | 0x80).chr << octets
          n = n >> 7
        end until n == 0
        octets[-1] = (octets[-1].ord & 0x7f).chr
        data << octets
      end
    end
  elsif (value.length == 1)
    data << (40 * value[0]).chr
  end
  encode_tlv(OBJECT_IDENTIFIER_TAG, data)
end

#encode_octet_string(value) ⇒ Object

Wrap string in a octet string tag and length.


312
313
314
# File 'lib/snmp/ber.rb', line 312

def encode_octet_string(value)
  encode_tlv(OCTET_STRING_TAG, value)
end

#encode_sequence(value) ⇒ Object

Wrap value in a sequence tag and length.


319
320
321
# File 'lib/snmp/ber.rb', line 319

def encode_sequence(value)
  encode_tlv(SEQUENCE_TAG, value)
end

#encode_tagged_integer(tag, value) ⇒ Object


256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/snmp/ber.rb', line 256

def encode_tagged_integer(tag, value)
  if value > 0 && value < 0x80
    data = value.chr
  else
    data = integer_to_octets(value)
    if value > 0 && data[0].ord > 0x7f
      data = "\000" << data
    elsif value < 0 && data[0].ord < 0x80
      data = "\377" << data
    end
  end
  encode_tlv(tag, data)
end

#encode_tlv(tag, value) ⇒ Object

Wraps value in a tag and length. This method expects an integer tag and a string value.


303
304
305
306
307
# File 'lib/snmp/ber.rb', line 303

def encode_tlv(tag, value)
  data = tag.chr << encode_length(value.length)
  data = data << value if value.length > 0
  data
end

#integer_to_octets(i) ⇒ Object

Helper method for encoding integer-like things.


273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/snmp/ber.rb', line 273

def integer_to_octets(i)
  if i >= 0
    done = 0
  else
    done = -1
  end
  octets = ""
  begin
    octets = (i & 0xff).chr << octets
    i = i >> 8
  end until i == done
  octets
end