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:



67
68
69
# File 'lib/snmp/ber.rb', line 67

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

#build_integer(data, start, num_octets) ⇒ Object



140
141
142
143
144
# File 'lib/snmp/ber.rb', line 140

def build_integer(data, start, num_octets)
    number = 0
    num_octets.times { |i| number = number<<8 | data[start+i] }
    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:



111
112
113
114
115
# File 'lib/snmp/ber.rb', line 111

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



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

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

#decode_ip_address(data) ⇒ Object

Raises:



159
160
161
162
163
164
# File 'lib/snmp/ber.rb', line 159

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:



190
191
192
193
194
# File 'lib/snmp/ber.rb', line 190

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



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/snmp/ber.rb', line 196

def decode_object_id_value(value)
    if value.length == 0
        object_id = []
    else
        if value[0] == 0x2b
            object_id = [1,3]
        else
            second = value[0] % 40
            first = (value[0] - 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] & 0x7f)
            if value[i] < 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:



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

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:



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

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:



117
118
119
120
121
# File 'lib/snmp/ber.rb', line 117

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:



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/snmp/ber.rb', line 86

def decode_tlv(data)
    raise OutOfData if (data.length == 2 && data[1] != 0) || data.length < 2
    tag = data[0]
    length = data[1]
    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).



136
137
138
# File 'lib/snmp/ber.rb', line 136

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.



280
281
282
# File 'lib/snmp/ber.rb', line 280

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

#encode_integer(value) ⇒ Object

Encode integer



237
238
239
# File 'lib/snmp/ber.rb', line 237

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:



224
225
226
227
228
229
230
231
232
# File 'lib/snmp/ber.rb', line 224

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



272
273
274
# File 'lib/snmp/ber.rb', line 272

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:



312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'lib/snmp/ber.rb', line 312

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] &= 0x7f
                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.



297
298
299
# File 'lib/snmp/ber.rb', line 297

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

#encode_sequence(value) ⇒ Object

Wrap value in a sequence tag and length.



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

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

#encode_tagged_integer(tag, value) ⇒ Object



241
242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/snmp/ber.rb', line 241

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] > 0x7f
            data = "\000" << data 
        elsif value < 0 && data[0] < 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.



288
289
290
291
292
# File 'lib/snmp/ber.rb', line 288

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.



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

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