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:



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

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

#build_integer(data, start, num_octets) ⇒ Object



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

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:



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

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



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

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:



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

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:



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

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



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

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:



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

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:



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

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:



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

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:



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

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



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

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.



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

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

#encode_integer(value) ⇒ Object

Encode integer



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

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:



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

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



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

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:



324
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
# File 'lib/snmp/ber.rb', line 324

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.



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

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

#encode_sequence(value) ⇒ Object

Wrap value in a sequence tag and length.



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

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

#encode_tagged_integer(tag, value) ⇒ Object



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

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.



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

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.



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

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