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:



79
80
81
# File 'lib/snmp/ber.rb', line 79

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

#build_integer(data, start, num_octets) ⇒ Object



152
153
154
155
156
# File 'lib/snmp/ber.rb', line 152

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:



123
124
125
126
127
# File 'lib/snmp/ber.rb', line 123

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



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

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:



171
172
173
174
175
176
# File 'lib/snmp/ber.rb', line 171

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:



202
203
204
205
206
# File 'lib/snmp/ber.rb', line 202

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



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

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:



165
166
167
168
169
# File 'lib/snmp/ber.rb', line 165

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:



186
187
188
189
190
# File 'lib/snmp/ber.rb', line 186

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:



129
130
131
132
133
# File 'lib/snmp/ber.rb', line 129

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:



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

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).



148
149
150
# File 'lib/snmp/ber.rb', line 148

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.



293
294
295
# File 'lib/snmp/ber.rb', line 293

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

#encode_integer(value) ⇒ Object

Encode integer



250
251
252
# File 'lib/snmp/ber.rb', line 250

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:



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

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



285
286
287
# File 'lib/snmp/ber.rb', line 285

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:



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

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.



310
311
312
# File 'lib/snmp/ber.rb', line 310

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

#encode_sequence(value) ⇒ Object

Wrap value in a sequence tag and length.



317
318
319
# File 'lib/snmp/ber.rb', line 317

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

#encode_tagged_integer(tag, value) ⇒ Object



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

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.



301
302
303
304
305
# File 'lib/snmp/ber.rb', line 301

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.



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

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