Skip to content

Commit c49e1f0

Browse files
author
blackhedd
committed
SNMP GetRequest parsing
1 parent 79e59cf commit c49e1f0

File tree

2 files changed

+76
-2
lines changed

2 files changed

+76
-2
lines changed

lib/net/snmp.rb

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,57 @@ class SNMP
5151

5252
end
5353

54+
class SnmpPdu
55+
class Error < Exception; end
56+
57+
attr_reader :version, :community, :pdu_type, :request_id, :variables
58+
59+
#--
60+
# TODO, improve the error-trapping.
61+
# We want to wrap up Ruby errors like array-ranges, which can appear if we get bad data.
62+
# We should probably do the whole parse under a catch-all block.
63+
def initialize ber_object
64+
begin
65+
parse_ber_object ber_object
66+
rescue RuntimeError
67+
# Wrap any basic parsing error so it becomes a PDU-format error
68+
raise Error.new( "snmp-pdu format error" )
69+
end
70+
end
71+
72+
def parse_ber_object ber_object
73+
@version = ber_object[0].to_i
74+
unless [0,2].include?(@version)
75+
raise Error.new("unknown snmp-version: #{@version}")
76+
end
77+
78+
@community = ber_object[1].to_s
79+
80+
data = ber_object[2]
81+
app_tag = data.ber_identifier & 31
82+
case app_tag
83+
when 0
84+
@pdu_type = :get_request
85+
parse_get_request data
86+
else
87+
raise Error.new( "unknown snmp-pdu type: #{app_tag}" )
88+
end
89+
end
90+
91+
#--
92+
# Defined in RFC1157, pgh 4.1.2.
93+
def parse_get_request data
94+
@request_id = data[0].to_i
95+
# data[1] is error-status, always 0.
96+
# data[2] is error-index, always 0.
97+
@variables = data[3].map {|v|
98+
# A variable-binding, of which there may be several,
99+
# consists of an OID and a BER null.
100+
# We're ignoring the null, we might want to verify it instead.
101+
v[0]
102+
}
103+
end
104+
105+
end
54106
end
55107

tests/testsnmp.rb

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
class TestSnmp < Test::Unit::TestCase
1313

14-
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"
14+
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"
1515

1616
def setup
1717
end
@@ -27,18 +27,40 @@ def test_invalid_packet
2727

2828
end
2929

30+
# The method String#read_ber! added by Net::BER consumes a well-formed BER object
31+
# from the head of a string. If it doesn't find a complete, well-formed BER object,
32+
# it returns nil and leaves the string unchanged. If it finds an object, it returns
33+
# the object and removes it from the head of the string. This is good for handling
34+
# partially-received data streams, such as from network connections.
3035
def test_consume_string
3136
data = "xxx"
3237
assert_equal( nil, data.read_ber! )
3338
assert_equal( "xxx", data )
3439

35-
data = SnmpRequest + "!!!"
40+
data = SnmpGetRequest + "!!!"
3641
ary = data.read_ber!( Net::SNMP::AsnSyntax )
3742
assert_equal( "!!!", data )
3843
assert ary.is_a?(Array)
3944
assert ary.is_a?(Net::BER::BerIdentifiedArray)
4045
end
4146

47+
def test_weird_packet
48+
assert_raise( Net::SnmpPdu::Error ) {
49+
Net::SnmpPdu.new("aaaaaaaaaaaaaa")
50+
}
51+
end
52+
53+
def test_packet
54+
data = SnmpGetRequest.dup
55+
pkt = data.read_ber(Net::SNMP::AsnSyntax)
56+
assert pkt.is_a?(Net::BER::BerIdentifiedArray)
57+
assert_equal( 48, pkt.ber_identifier) # Constructed [0], signifies GetRequest
58+
59+
pdu = Net::SnmpPdu.new(pkt)
60+
assert_equal(:get_request, pdu.pdu_type )
61+
assert_equal(16170, pdu.request_id ) # whatever was in the test data. 16170 is not magic.
62+
assert_equal( [[1,3,6,1,2,1,1,1,0]], pdu.variables )
63+
end
4264
end
4365

4466

0 commit comments

Comments
 (0)