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/varbind.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#89require 'snmp/ber'1011include SNMP::BER1213module SNMP1415class UnsupportedValueTag < RuntimeError; end16class InvalidIpAddress < ArgumentError; end1718class VarBindList < Array19def self.decode(data)20list = VarBindList.new21varbind_data, remainder = decode_sequence(data)22while varbind_data != ""23varbind, varbind_data = VarBind.decode(varbind_data)24list << varbind25end26return list, remainder27end2829def initialize(varbind_list=[])30super()31if varbind_list.respond_to? :to_str32self << ObjectId.new(varbind_list.to_str).to_varbind33elsif varbind_list.respond_to? :to_varbind34self << varbind_list.to_varbind35else36varbind_list.each do |item|37if item.respond_to? :to_str38self << ObjectId.new(item.to_str).to_varbind39else40self << item.to_varbind41end42end43end44end4546def asn1_type47"VarBindList"48end4950def encode51varbind_data = ""52self.each do |varbind|53varbind_data << varbind.encode54end55encode_sequence(varbind_data)56end57end5859class Integer60include Comparable6162def self.decode(value_data)63Integer.new(decode_integer_value(value_data))64end6566def asn1_type67"INTEGER"68end6970def initialize(value)71@value = value.to_i72end7374def <=>(other)75@value <=> other.to_i76end7778def coerce(other)79if other.kind_of? Integer80return [other, @value]81else82return [other.to_f, self.to_f]83end84end8586def to_s87@value.to_s88end8990def to_i91@value92end9394def to_f95@value.to_f96end9798def encode99encode_integer(@value)100end101102def to_oid103raise RangeError, "@{value} cannot be an OID (must be >0)" if @value < 0104ObjectId.new([@value])105end106end107108class Integer32 < Integer109def initialize(value)110super(value)111raise ArgumentError, "Out of range: #{value}" if value < -2147483648112raise ArgumentError, "Out of range: #{value}" if value > 2147483647113end114end115116class OctetString < String117def self.decode(value_data)118OctetString.new(value_data)119end120121def asn1_type122"OCTET STRING"123end124125def encode126encode_octet_string(self)127end128129def to_oid130oid = ObjectId.new131each_byte { |b| oid << b }132oid133end134end135136class ObjectId < Array137include Comparable138139def self.decode(value_data)140ObjectId.new(decode_object_id_value(value_data))141end142143def asn1_type144"OBJECT IDENTIFIER"145end146147##148# Create an object id. The input is expected to be either a string149# in the format "n.n.n.n.n.n" or an array of integers.150#151def initialize(id=[])152if id.nil?153raise ArgumentError154elsif id.respond_to? :to_str155super(make_integers(id.to_str.split(".")))156else157super(make_integers(id.to_ary))158end159rescue ArgumentError160raise ArgumentError, "#{id.inspect}:#{id.class} not a valid object ID"161end162163def to_varbind164VarBind.new(self, Null)165end166167def to_oid168self169end170171def to_s172self.join('.')173end174175def inspect176"[#{self.to_s}]"177end178179def encode180encode_object_id(self)181end182183##184# Returns true if this ObjectId is a subtree of the provided parent tree185# ObjectId. For example, "1.3.6.1.5" is a subtree of "1.3.6.1".186#187def subtree_of?(parent_tree)188parent_tree = make_object_id(parent_tree)189if parent_tree.length > self.length190false191else192parent_tree.each_index do |i|193return false if parent_tree[i] != self[i]194end195true196end197end198199##200# Returns an index based on the difference between this ObjectId201# and the provided parent ObjectId.202#203# For example, ObjectId.new("1.3.6.1.5").index("1.3.6.1") returns an204# ObjectId of "5".205#206def index(parent_tree)207parent_tree = make_object_id(parent_tree)208if not subtree_of?(parent_tree)209raise ArgumentError, "#{self.to_s} not a subtree of #{parent_tree.to_s}"210elsif self.length == parent_tree.length211raise ArgumentError, "OIDs are the same"212else213ObjectId.new(self[parent_tree.length..-1])214end215end216217private218219def make_integers(list)220list.collect{|n| Integer(n)}221end222223def make_object_id(oid)224oid.kind_of?(ObjectId) ? oid : ObjectId.new(oid)225end226227end228229class IpAddress230class << self231def decode(value_data)232IpAddress.new(value_data)233end234end235236def asn1_type237"IpAddress"238end239240##241# Create an IpAddress object. The constructor accepts either a raw242# four-octet string or a formatted string of integers separated by dots243# (i.e. "10.1.2.3").244#245def initialize(value_data)246ip = value_data.to_str247if ip.length > 4248ip = parse_string(ip)249elsif ip.length != 4250raise InvalidIpAddress, "Expected 4 octets or formatted string, got #{value_data.inspect}"251end252@value = ip253end254255##256# Returns a raw four-octet string representing this IpAddress.257#258def to_str259@value.dup260end261262##263# Returns a formatted, dot-separated string representing this IpAddress.264#265def to_s266octets = []267@value.each_byte { |b| octets << b.to_s }268octets.join('.')269end270271def to_oid272oid = ObjectId.new273@value.each_byte { |b| oid << b }274oid275end276277def ==(other)278if other.respond_to? :to_str279return @value.eql?(other.to_str)280else281return false282end283end284285def eql?(other)286self == other287end288289def hash290@value.hash291end292293def encode294encode_tlv(IpAddress_TAG, @value)295end296297private298def parse_string(ip_string)299parts = ip_string.split(".")300raise InvalidIpAddress, ip_string.inspect if parts.length != 4301value_data = ""302parts.each do |s|303octet = s.to_i304raise InvalidIpAddress, ip_string.inspect if octet > 255305raise InvalidIpAddress, ip_string.inspect if octet < 0306value_data << octet.chr307end308value_data309end310311end312313class UnsignedInteger < Integer314def initialize(value)315super(value)316raise ArgumentError, "Negative integer invalid: #{value}" if value < 0317raise ArgumentError, "Out of range: #{value}" if value > 4294967295318end319320def self.decode(value_data)321self.new(decode_uinteger_value(value_data))322end323end324325class Counter32 < UnsignedInteger326def asn1_type327"Counter32"328end329330def encode331encode_tagged_integer(Counter32_TAG, @value)332end333end334335class Gauge32 < UnsignedInteger336def asn1_type337"Gauge32"338end339340def encode341encode_tagged_integer(Gauge32_TAG, @value)342end343end344345class Unsigned32 < UnsignedInteger346def asn1_type347"Unsigned32"348end349350def encode351encode_tagged_integer(Unsigned32_TAG, @value)352end353end354355class TimeTicks < UnsignedInteger356def asn1_type357"TimeTicks"358end359360def encode361encode_tagged_integer(TimeTicks_TAG, @value)362end363364def to_s365days, remainder = @value.divmod(8640000)366hours, remainder = remainder.divmod(360000)367minutes, remainder = remainder.divmod(6000)368seconds, hundredths = remainder.divmod(100)369case370when days < 1371sprintf('%02d:%02d:%02d.%02d',372hours, minutes, seconds, hundredths)373when days == 1374sprintf('1 day, %02d:%02d:%02d.%02d',375hours, minutes, seconds, hundredths)376when days > 1377sprintf('%d days, %02d:%02d:%02d.%02d',378days, hours, minutes, seconds, hundredths)379end380end381end382383class Opaque < OctetString384def self.decode(value_data)385Opaque.new(value_data)386end387388def asn1_type389"Opaque"390end391392def encode393encode_tlv(Opaque_TAG, self)394end395end396397class Counter64 < Integer398def self.decode(value_data)399Counter64.new(decode_integer_value(value_data))400end401402def asn1_type403"Counter64"404end405406def initialize(value)407super(value)408raise ArgumentError, "Negative integer invalid: #{value}" if value < 0409raise ArgumentError, "Out of range: #{value}" if value > 18446744073709551615410end411412def encode413encode_tagged_integer(Counter64_TAG, @value)414end415end416417class Null418class << self419def decode(value_data)420Null421end422423def encode424encode_null425end426427def asn1_type428'Null'429end430431def to_s432asn1_type433end434end435end436437class NoSuchObject438class << self439def decode(value_data)440NoSuchObject441end442443def encode444encode_exception(NoSuchObject_TAG)445end446447def asn1_type448'noSuchObject'449end450451def to_s452asn1_type453end454end455end456457class NoSuchInstance458class << self459def decode(value_data)460NoSuchInstance461end462463def encode464encode_exception(NoSuchInstance_TAG)465end466467def asn1_type468'noSuchInstance'469end470471def to_s472asn1_type473end474end475end476477class EndOfMibView478class << self479def decode(value_data)480EndOfMibView481end482483def encode484encode_exception(EndOfMibView_TAG)485end486487def asn1_type488'endOfMibView'489end490491def to_s492asn1_type493end494end495end496497class VarBind498attr_accessor :name499attr_accessor :value500501alias :oid :name502503class << self504def decode(data)505varbind_data, remaining_varbind_data = decode_sequence(data)506name, remainder = decode_object_id(varbind_data)507value, remainder = decode_value(remainder)508assert_no_remainder(remainder)509return VarBind.new(name, value), remaining_varbind_data510end511512ValueDecoderMap = {513INTEGER_TAG => Integer,514OCTET_STRING_TAG => OctetString,515NULL_TAG => Null,516OBJECT_IDENTIFIER_TAG => ObjectId,517IpAddress_TAG => IpAddress,518Counter32_TAG => Counter32,519Gauge32_TAG => Gauge32,520# note Gauge32 tag same as Unsigned32521TimeTicks_TAG => TimeTicks,522Opaque_TAG => Opaque,523Counter64_TAG => Counter64,524NoSuchObject_TAG => NoSuchObject,525NoSuchInstance_TAG => NoSuchInstance,526EndOfMibView_TAG => EndOfMibView527}528529def decode_value(data)530value_tag, value_data, remainder = decode_tlv(data)531decoder_class = ValueDecoderMap[value_tag]532if decoder_class533value = decoder_class.decode(value_data)534else535raise UnsupportedValueTag, value_tag.to_s536end537return value, remainder538end539end540541def initialize(name, value=Null)542if name.kind_of? ObjectId543@name = name544else545@name = ObjectName.new(name)546end547@value = value548end549550def asn1_type551"VarBind"552end553554def to_varbind555self556end557558def to_s559"[name=#{@name.to_s}, value=#{@value.to_s} (#{@value.asn1_type})]"560end561562def each563yield self564end565566def encode567data = encode_object_id(@name) << value.encode568encode_sequence(data)569end570end571572class ObjectName < ObjectId573def asn1_type574"ObjectName"575end576end577578end579580581