From c49e1f0c940a8d6c24ce71f5b4010f544f1657d8 Mon Sep 17 00:00:00 2001 From: blackhedd Date: Fri, 15 Dec 2006 11:22:41 +0000 Subject: [PATCH] SNMP GetRequest parsing --- lib/net/snmp.rb | 52 +++++++++++++++++++++++++++++++++++++++++++++++ tests/testsnmp.rb | 26 ++++++++++++++++++++++-- 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/lib/net/snmp.rb b/lib/net/snmp.rb index 8607a07..7f3fb18 100644 --- a/lib/net/snmp.rb +++ b/lib/net/snmp.rb @@ -51,5 +51,57 @@ module Net end + class SnmpPdu + class Error < Exception; end + + attr_reader :version, :community, :pdu_type, :request_id, :variables + + #-- + # TODO, improve the error-trapping. + # We want to wrap up Ruby errors like array-ranges, which can appear if we get bad data. + # We should probably do the whole parse under a catch-all block. + def initialize ber_object + begin + parse_ber_object ber_object + rescue RuntimeError + # Wrap any basic parsing error so it becomes a PDU-format error + raise Error.new( "snmp-pdu format error" ) + end + end + + def parse_ber_object ber_object + @version = ber_object[0].to_i + unless [0,2].include?(@version) + raise Error.new("unknown snmp-version: #{@version}") + end + + @community = ber_object[1].to_s + + data = ber_object[2] + app_tag = data.ber_identifier & 31 + case app_tag + when 0 + @pdu_type = :get_request + parse_get_request data + else + raise Error.new( "unknown snmp-pdu type: #{app_tag}" ) + end + end + + #-- + # Defined in RFC1157, pgh 4.1.2. + def parse_get_request data + @request_id = data[0].to_i + # data[1] is error-status, always 0. + # data[2] is error-index, always 0. + @variables = data[3].map {|v| + # A variable-binding, of which there may be several, + # consists of an OID and a BER null. + # We're ignoring the null, we might want to verify it instead. + v[0] + } + end + + end end diff --git a/tests/testsnmp.rb b/tests/testsnmp.rb index 4bafcc4..afe59e7 100644 --- a/tests/testsnmp.rb +++ b/tests/testsnmp.rb @@ -11,7 +11,7 @@ require 'stringio' class TestSnmp < Test::Unit::TestCase - SnmpRequest = "0'\002\001\000\004\006public\240\032\002\002?*\002\001\000\002\001\0000\0160\f\006\b+\006\001\002\001\001\001\000\005\000" + SnmpGetRequest = "0'\002\001\000\004\006public\240\032\002\002?*\002\001\000\002\001\0000\0160\f\006\b+\006\001\002\001\001\001\000\005\000" def setup end @@ -27,18 +27,40 @@ class TestSnmp < Test::Unit::TestCase end + # The method String#read_ber! added by Net::BER consumes a well-formed BER object + # from the head of a string. If it doesn't find a complete, well-formed BER object, + # it returns nil and leaves the string unchanged. If it finds an object, it returns + # the object and removes it from the head of the string. This is good for handling + # partially-received data streams, such as from network connections. def test_consume_string data = "xxx" assert_equal( nil, data.read_ber! ) assert_equal( "xxx", data ) - data = SnmpRequest + "!!!" + data = SnmpGetRequest + "!!!" ary = data.read_ber!( Net::SNMP::AsnSyntax ) assert_equal( "!!!", data ) assert ary.is_a?(Array) assert ary.is_a?(Net::BER::BerIdentifiedArray) end + def test_weird_packet + assert_raise( Net::SnmpPdu::Error ) { + Net::SnmpPdu.new("aaaaaaaaaaaaaa") + } + end + + def test_packet + data = SnmpGetRequest.dup + pkt = data.read_ber(Net::SNMP::AsnSyntax) + assert pkt.is_a?(Net::BER::BerIdentifiedArray) + assert_equal( 48, pkt.ber_identifier) # Constructed [0], signifies GetRequest + + pdu = Net::SnmpPdu.new(pkt) + assert_equal(:get_request, pdu.pdu_type ) + assert_equal(16170, pdu.request_id ) # whatever was in the test data. 16170 is not magic. + assert_equal( [[1,3,6,1,2,1,1,1,0]], pdu.variables ) + end end