Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/snmp/ber.rb
Views: 11766
#1# Copyright (c) 2004 David R. Halliday2# All rights reserved.3#4# This SNMP library is free software. Redistribution is permitted under the5# same terms and conditions as the standard Ruby distribution. See the6# COPYING file in the Ruby distribution for details.7#89#10# Add ord method to Integer for forward compatibility with Ruby 1.911#12if "a"[0].kind_of? Integer13unless Integer.methods.include? :ord14class Integer15def ord; self; end16end17end18end1920#21# This module implements methods for encoding and decoding SNMP packets22# using the ASN.1 BER (Basic Encoding Rules).23#24module SNMP25module BER #:nodoc:all2627# SNMP version codes28SNMP_V1 = 029SNMP_V2C = 130SNMP_V3 = 3 # not supported3132# SNMP context-specific data types33# See RFC 1157 for SNMPv134# See RFC 1905 for SNMPv2c35GetRequest_PDU_TAG = 0xa036GetNextRequest_PDU_TAG = 0xa137Response_PDU_TAG = 0xa238SetRequest_PDU_TAG = 0xa339SNMPv1_Trap_PDU_TAG = 0xa4 # Note: valid for SNMPv1 only40GetBulkRequest_PDU_TAG = 0xa541InformRequest_PDU_TAG = 0xa642SNMPv2_Trap_PDU_TAG = 0xa743Report_PDU_TAG = 0xa8 # Note: Usage not defined - not supported4445# Primitive ASN.1 data types46INTEGER_TAG = 0x0247OCTET_STRING_TAG = 0x0448NULL_TAG = 0x0549OBJECT_IDENTIFIER_TAG = 0x065051# Constructed ASN.1 data type52SEQUENCE_TAG = 0x305354# SNMP application data types55# See RFC 1155 for SNMPv156# See RFC 1902 for SNMPv2c57IpAddress_TAG = 0x4058Counter32_TAG = 0x41 # Counter in SNMPv159Gauge32_TAG = 0x42 # Gauge in SNMPv160Unsigned32_TAG = 0x42 # Note: same as Gauge3261TimeTicks_TAG = 0x4362Opaque_TAG = 0x4463Counter64_TAG = 0x466465# VarBind response exceptions66NoSuchObject_TAG = 0x8067NoSuchInstance_TAG = 0x8168EndOfMibView_TAG = 0x826970# Exceptions thrown in this module71class OutOfData < RuntimeError; end72class InvalidLength < RuntimeError; end73class InvalidTag < RuntimeError; end74class InvalidObjectId < RuntimeError; end7576def assert_no_remainder(remainder)77raise ParseError, remainder.inspect if (remainder and remainder != "")78end7980#81# Decode tag-length-value data. The data is assumed to be a string of82# bytes in network byte order. This format is returned by Socket#recv.83#84# Returns a tuple containing the tag, the value, and any remaining85# unprocessed data.86#87# The data is not interpreted by this method. Use one of the other88# decoding methods to interpret the data.89#90# Note that ASN.1 supports an indefinite length format where the end of91# content is marked by a pair of 0 octets. SNMP does not support this92# format, so only the two definite forms are implemented (single byte and93# multi-byte).94#95def decode_tlv(data)96raise OutOfData if (data.length == 2 && data[1].ord != 0) || data.length < 297tag = data[0].ord98length = data[1].ord99if length < 0x80100value = data[2, length]101remainder = data[length+2..-1]102else103# ASN.1 says this octet can't be 0xff104raise InvalidLength, length.to_s if length == 0xff105num_octets = length & 0x7f106length = build_integer(data, 2, num_octets)107value = data[num_octets+2, length]108remainder = data[num_octets+2+length..-1]109end110return tag, value, remainder111end112113#114# Decode TLV data for an ASN.1 integer.115#116# Throws an InvalidTag exception if the tag is incorrect.117#118# Returns a tuple containing an integer and any remaining unprocessed data.119#120def decode_integer(data)121tag, value, remainder = decode_tlv(data)122raise InvalidTag, tag.to_s if tag != INTEGER_TAG123return decode_integer_value(value), remainder124end125126def decode_timeticks(data)127tag, value, remainder = decode_tlv(data)128raise InvalidTag, tag.to_s if tag != TimeTicks_TAG129return decode_uinteger_value(value), remainder130end131132def decode_integer_value(value)133result = build_integer(value, 0, value.length)134if value[0].ord[7] == 1135result -= (1 << (8 * value.length))136end137result138end139140##141# Decode an integer, ignoring the sign bit. Some agents insist on142# encoding 32 bit unsigned integers with four bytes even though it143# should be 5 bytes (at least the way I read it).144#145def decode_uinteger_value(value)146build_integer(value, 0, value.length)147end148149def build_integer(data, start, num_octets)150number = 0151num_octets.times { |i| number = number<<8 | data[start+i].ord }152return number153end154155#156# Decode TLV data for an ASN.1 octet string.157#158# Throws an InvalidTag exception if the tag is incorrect.159#160# Returns a tuple containing a string and any remaining unprocessed data.161#162def decode_octet_string(data)163tag, value, remainder = decode_tlv(data)164raise InvalidTag, tag.to_s if tag != OCTET_STRING_TAG165return value, remainder166end167168def decode_ip_address(data)169tag, value, remainder = decode_tlv(data)170raise InvalidTag, tag.to_s if tag != IpAddress_TAG171raise InvalidLength, tag.to_s if value.length != 4172return value, remainder173end174175#176# Decode TLV data for an ASN.1 sequence.177#178# Throws an InvalidTag exception if the tag is incorrect.179#180# Returns a tuple containing the sequence data and any remaining181# unprocessed data that follows the sequence.182#183def decode_sequence(data)184tag, value, remainder = decode_tlv(data)185raise InvalidTag, tag.to_s if tag != SEQUENCE_TAG186return value, remainder187end188189#190# Unwrap TLV data for an ASN.1 object identifier. This method extracts191# the OID value as a character string but does not decode it further.192#193# Throws an InvalidTag exception if the tag is incorrect.194#195# Returns a tuple containing the object identifier (OID) and any196# remaining unprocessed data. The OID is represented as an array197# of integers.198#199def decode_object_id(data)200tag, value, remainder = decode_tlv(data)201raise InvalidTag, tag.to_s if tag != OBJECT_IDENTIFIER_TAG202return decode_object_id_value(value), remainder203end204205def decode_object_id_value(value)206if value.length == 0207object_id = []208else209value0 = value[0].ord210if value0 == 0x2b211object_id = [1,3]212else213second = value0 % 40214first = (value0 - second) / 40215raise InvalidObjectId, value.to_s if first > 2216object_id = [first, second]217end218n = 0219for i in 1...value.length220n = (n<<7) + (value[i].ord & 0x7f)221if value[i].ord < 0x80222object_id << n223n = 0224end225end226end227return object_id228end229230#231# Encode the length field for TLV data. Returns the length octets232# as a string.233#234def encode_length(length)235raise InvalidLength, length.to_s if length < 0236if length < 0x80237length.chr238else239data = integer_to_octets(length)240(data.size | 0x80).chr << data241end242end243244#245# Encode integer246#247def encode_integer(value)248encode_tagged_integer(INTEGER_TAG, value)249end250251def encode_tagged_integer(tag, value)252if value > 0 && value < 0x80253data = value.chr254else255data = integer_to_octets(value)256if value > 0 && data[0].ord > 0x7f257data = "\000" << data258elsif value < 0 && data[0].ord < 0x80259data = "\377" << data260end261end262encode_tlv(tag, data)263end264265#266# Helper method for encoding integer-like things.267#268def integer_to_octets(i)269if i >= 0270done = 0271else272done = -1273end274octets = ""275begin276octets = (i & 0xff).chr << octets277i = i >> 8278end until i == done279octets280end281282def encode_null283NULL_TAG.chr << "\000"284end285286#287# Encode an exception. The encoding is simply the exception tag with288# no data, similar to NULL.289#290def encode_exception(tag)291tag.chr << "\000"292end293294#295# Wraps value in a tag and length. This method expects an296# integer tag and a string value.297#298def encode_tlv(tag, value)299data = tag.chr << encode_length(value.length)300data = data << value if value.length > 0301data302end303304#305# Wrap string in a octet string tag and length.306#307def encode_octet_string(value)308encode_tlv(OCTET_STRING_TAG, value)309end310311#312# Wrap value in a sequence tag and length.313#314def encode_sequence(value)315encode_tlv(SEQUENCE_TAG, value)316end317318#319# Encode an object id. The input is assumed to be an array of integers320# representing the object id.321#322def encode_object_id(value)323raise InvalidObjectId, value.to_s if value.length < 1324raise InvalidObjectId, value.to_s if value[0] > 2325data = ""326if (value.length > 1)327raise InvalidObjectId if value[0] < 2 && value[1] > 40328data << (40 * value[0] + value[1]).chr329for i in 2...value.length330if value[i] < 0x80331data << value[i].chr332else333octets = ""334n = value[i]335begin336octets = (n & 0x7f | 0x80).chr << octets337n = n >> 7338end until n == 0339octets[-1] = (octets[-1].ord & 0x7f).chr340data << octets341end342end343elsif (value.length == 1)344data << (40 * value[0]).chr345end346encode_tlv(OBJECT_IDENTIFIER_TAG, data)347end348349end350end351352353354