msnmp 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ *~
2
+ .ruby-version
3
+ pkg/*.gem
data/MIT-LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013, Internet Initiative Japan Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # mini SNMP library for ruby/mruby
2
+
3
+ ## Requirements
4
+ * ruby 1.8, 1.9, 2.0 or mruby
5
+
6
+ ## Usage
7
+
8
+ ### mruby
9
+
10
+ Add the following lines to build_conf.rb and make.
11
+
12
+ ```ruby
13
+ conf.gem :git => 'https://github.com/iij/mruby-io.git'
14
+ conf.gem :git => 'https://github.com/iij/mruby-socket.git'
15
+ conf.gem :git => 'https://github.com/iij/mruby-pack.git'
16
+ conf.gem :git => 'https://github.com/iij/msnmp.git'
17
+ ```
18
+
19
+ There is a sample implementation of a snmpbulkwalk command.
20
+
21
+ mruby bin/msnmpwalk -v 2c -c public host.example.com system
22
+
23
+ ### ruby
24
+
25
+ install
26
+
27
+ gem install msnmp
28
+
29
+ run
30
+
31
+ msnmpwalk -v 2c -c public host.example.com system
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_helper"
2
+ require "rake/testtask"
3
+
4
+ Bundler::GemHelper.install_tasks :name=>'msnmp'
5
+ Rake::TestTask.new
data/bin/msnmpwalk ADDED
@@ -0,0 +1,29 @@
1
+ if Object.const_defined?(:RUBY_VERSION)
2
+ require 'socket'
3
+ require 'rubygems'
4
+ require 'msnmp'
5
+ end
6
+
7
+ ver = '1'
8
+ community = 'public'
9
+ nonopts = []
10
+ while arg = ARGV.shift
11
+ case arg
12
+ when '-c'; community = ARGV.shift
13
+ when '-v'; ver = ARGV.shift
14
+ else nonopts.push arg
15
+ end
16
+ end
17
+
18
+ snmp = SNMP.new nonopts.first
19
+ snmp.version = ver
20
+ snmp.community = community
21
+ oid = nonopts[1]
22
+ snmp.walk(oid) {|oid, tag, val|
23
+ #puts "#{oid} = #{val.inspect}"
24
+ case tag
25
+ when 6
26
+ val = BER.dec_oid(val).join('.')
27
+ end
28
+ p [oid, tag, val]
29
+ }
data/lib/msnmp.rb ADDED
@@ -0,0 +1,599 @@
1
+ # coding: us-ascii
2
+
3
+ class String
4
+ unless ''.respond_to? :getbyte
5
+ def getbyte idx
6
+ self[idx]
7
+ end
8
+ end
9
+
10
+ unless ''.respond_to? :slice!
11
+ def slice!(arg1, arg2 = 1)
12
+ return nil if arg2 < 0
13
+ if arg1.class == Fixnum
14
+ rval = self[arg1, arg2]
15
+ len = self.length
16
+ rpos = arg1 + arg2
17
+ rpos += len if arg1 < 0
18
+ rlen = len - rpos
19
+ region_l = self[0...arg1]
20
+ region_r = self[rpos, rlen]
21
+ region_r = '' if region_r == nil
22
+ self.replace(region_l + region_r)
23
+ elsif arg1.class == String
24
+ rval = arg1
25
+ self.gsub!(arg1, "")
26
+ else
27
+ return nil
28
+ end
29
+ rval
30
+ end
31
+ end
32
+ end
33
+
34
+ module BER
35
+ class C64
36
+ def initialize s
37
+ @s = s
38
+ @a = [0, 0, 0, 0, 0, 0, 0, 0]
39
+ j = 7
40
+ (s.size-1).downto(0) {|i| @a[j] = s.getbyte(i); j -= 1}
41
+ end
42
+
43
+ def to_s
44
+ '0x' + @a.map {|b| sprintf "%02x", b}.join('')
45
+ end
46
+
47
+ def to_binary
48
+ @a.pack('C*')
49
+ end
50
+
51
+ def to_msgpack
52
+ "\xcf" + @a.pack('C*')
53
+ end
54
+
55
+ def to_i
56
+ n = 0
57
+ 0.upto(7) {|i| n = n * 256 + @a[i]}
58
+ n
59
+ end
60
+ end
61
+
62
+ def enc tag, val
63
+ case tag
64
+ when 0x02
65
+ return enc_int(val)
66
+ when 0x04
67
+ return enc_str(val)
68
+ end
69
+ end
70
+
71
+ def enc_len n
72
+ if n < 128 then
73
+ return n.chr
74
+ end
75
+ e_len = ''
76
+ while n > 0
77
+ e_len = (n & 0xff).chr + e_len
78
+ n = n >> 8
79
+ end
80
+ return (e_len.size | 0x80).chr + e_len
81
+ end
82
+
83
+ def enc_int n
84
+ ebuf = ''
85
+ if n < 0 then
86
+ begin
87
+ ebuf = (n & 0xff).chr + ebuf
88
+ n = n >> 8
89
+ end until (n == -1) and (ebuf.getbyte(0) & 0x80 == 0x80)
90
+ else
91
+ begin
92
+ ebuf = (n & 0xff).chr + ebuf
93
+ n = n >> 8
94
+ end while n > 0
95
+ end
96
+ return "\x02" + enc_len(ebuf.size) + ebuf
97
+ end
98
+
99
+ def enc_str s
100
+ return "\x04" + enc_len(s.size) + s
101
+ end
102
+
103
+ def enc_a_oid a
104
+ a.shift; a.shift
105
+ e = '+' # 1.3
106
+ until a.empty?
107
+ subid = a.shift.to_i
108
+ s = ''
109
+ if subid == 0 then
110
+ e += "\x00"
111
+ else
112
+ while subid > 0
113
+ if s.empty?
114
+ s = (subid & 0x7f).chr + s
115
+ else
116
+ s = (subid & 0x7f | 0x80).chr + s
117
+ end
118
+ subid = subid >> 7
119
+ end
120
+ e += s
121
+ end
122
+ end
123
+ e
124
+ end
125
+
126
+ def enc_v_oid oid
127
+ a = oid.split('.')
128
+ enc_a_oid a
129
+ end
130
+
131
+ def enc_oid oid
132
+ e = enc_v_oid oid
133
+ return "\x06" + enc_len(e.size) + e
134
+ end
135
+
136
+ def enc_oid_list oid_list
137
+ s = ''
138
+ for e in oid_list
139
+ vb = enc_oid(e) + "\x05\x00" # BER.enc_null
140
+ s = s + "\x30" + enc_len(vb.size) + vb
141
+ end
142
+ varbind = "\x30" + enc_len(s.size) + s
143
+ end
144
+
145
+ def enc_varbind enoid
146
+ s = "\x06" + enc_len(enoid.size) + enoid + "\x05\x00"
147
+ s = "\x30" + enc_len(s.size) + s
148
+ return "\x30" + enc_len(s.size) + s
149
+ end
150
+
151
+ def cat_enoid enoids
152
+ s = ''
153
+ for enoid in enoids
154
+ vb = "\x06" + enc_len(enoid.size) + enoid + "\x05\x00"
155
+ s = s + "\x30" + enc_len(vb.size) + vb
156
+ end
157
+ varbind = "\x30" + enc_len(s.size) + s
158
+ end
159
+
160
+ def dec_oid msg
161
+ msg = msg.clone
162
+ if msg.getbyte(0) == 0x2b
163
+ oid = [1, 3]
164
+ msg.slice! 0
165
+ else
166
+ oid = []
167
+ end
168
+ until msg.empty?
169
+ c = msg.getbyte 0
170
+ msg.slice! 0
171
+ if c > 127
172
+ id = 0
173
+ while c > 127
174
+ id = (id << 7) + (c & 0x7f)
175
+ c = msg.getbyte 0
176
+ msg.slice! 0
177
+ end
178
+ id = (id << 7) + c
179
+ oid.push id
180
+ else
181
+ oid.push c
182
+ end
183
+ end
184
+ return oid
185
+ end
186
+
187
+ def dec_int s
188
+ if (s.getbyte(0) & 0x80) == 0x80
189
+ n = -1
190
+ else
191
+ n = 0
192
+ end
193
+ while s.size > 0
194
+ n = n << 8 | s.getbyte(0)
195
+ s = s[1..-1]
196
+ end
197
+ return n
198
+ end
199
+
200
+ def dec_cnt s
201
+ n = 0
202
+ while s.size > 0
203
+ n = n << 8 | s.getbyte(0)
204
+ s = s[1..-1]
205
+ end
206
+ return n
207
+ end
208
+
209
+ def dec_cnt64 s
210
+ C64.new(s)
211
+ end
212
+
213
+ Object.const_defined?(:RUBY_VERSION) and
214
+ alias_method(:dec_cnt64, :dec_cnt)
215
+
216
+ def tlv data
217
+ tag = data.getbyte 0
218
+ if (len = data.getbyte(1)) < 128
219
+ val = data[2, len]
220
+ remain = data[(len + 2)..-1]
221
+ elsif len == 0x81
222
+ len = data.getbyte 2
223
+ val = data[3, len]
224
+ remain = data[(len + 3)..-1]
225
+ else
226
+ n = len & 0x7f
227
+ len = 0
228
+ for i in 1..n
229
+ len = len * 256 + data.getbyte(i + 1)
230
+ end
231
+ val = data[n + 2, len]
232
+ remain = data[(len + n + 2).. -1]
233
+ end
234
+ return tag, val, remain
235
+ end
236
+
237
+ def dec_msg msg
238
+ if msg[1, 1] < "\x80"
239
+ idx = msg.getbyte(6) + 7
240
+ pdutype, pdu, msg = tlv msg[idx..-1]
241
+ elsif msg[1, 1] == "\x81"
242
+ idx = msg.getbyte(7) + 8
243
+ pdutype, pdu, msg = tlv msg[idx..-1]
244
+ else
245
+ tag, val, msg = tlv msg
246
+ tag, ver, msg = tlv val
247
+ tag, comm, msg = tlv msg
248
+ pdutype, pdu, msg = tlv msg
249
+ end
250
+ idlen = pdu.getbyte 1
251
+ enc_reqid = pdu[0, idlen + 2]
252
+ error_status = pdu.getbyte(idlen + 4)
253
+ error_index = pdu.getbyte(idlen + 7)
254
+ tag, varbind, msg = tlv pdu[idlen+8..-1]
255
+ return enc_reqid, error_status, error_index, varbind
256
+ end
257
+
258
+ def dec_varbind_block msg
259
+ while msg.size > 0
260
+ tag,val,msg=tlv(msg)
261
+ tag,val1,msg0=tlv(val)
262
+ tag,val2,msg1=tlv(msg0)
263
+ case tag
264
+ when 0x02
265
+ val2=dec_int(val2)
266
+ when 0x05 #Null
267
+ val2=nil
268
+ when 0x06 #OID
269
+ #val2=dec_oid(val2)
270
+ when 0x40 # IP Address
271
+ val2 = val2.unpack('CCCC').join('.')
272
+ when 0x41, 0x42, 0x43 #Counter or Gauge or Tick
273
+ val2 = dec_cnt val2
274
+ when 0x46 #Counter64
275
+ val2 = dec_cnt64 val2
276
+ when 0x80
277
+ next # skip
278
+ end
279
+ yield val1, tag, val2
280
+ end
281
+ end
282
+
283
+ def dec_varbind msg
284
+ list=[]
285
+ val1 = tag = val2 = nil #XXX
286
+ dec_varbind_block(msg) {|val1, tag, val2|
287
+ list.push([val1,tag,val2])
288
+ }
289
+ return list
290
+ end
291
+
292
+ extend BER
293
+ end
294
+
295
+ class SNMP
296
+ OIDS = {
297
+ 'iso'=>'1',
298
+ 'org'=>'1.3',
299
+ 'dod'=>'1.3.6',
300
+ 'internet'=>'1.3.6.1',
301
+ 'directory'=>'1.3.6.1.1',
302
+ 'mgmt'=>'1.3.6.1.2',
303
+ 'mib-2'=>'1.3.6.1.2.1',
304
+ 'system'=>'1.3.6.1.2.1.1',
305
+ 'sysDescr'=>'1.3.6.1.2.1.1.1',
306
+ 'sysObjectID'=>'1.3.6.1.2.1.1.2',
307
+ 'sysUpTime'=>'1.3.6.1.2.1.1.3',
308
+ 'sysContact'=>'1.3.6.1.2.1.1.4',
309
+ 'sysName'=>'1.3.6.1.2.1.1.5',
310
+ 'sysLocation'=>'1.3.6.1.2.1.1.6',
311
+ 'sysServices'=>'1.3.6.1.2.1.1.7',
312
+
313
+ 'interfaces'=>'1.3.6.1.2.1.2',
314
+ 'ifNumber'=>'1.3.6.1.2.1.2.1',
315
+ 'ifTable'=>'1.3.6.1.2.1.2.2',
316
+ 'ifEntry'=>'1.3.6.1.2.1.2.2.1',
317
+ 'ifIndex'=>'1.3.6.1.2.1.2.2.1.1',
318
+ 'ifInOctets'=>'1.3.6.1.2.1.2.2.1.10',
319
+ 'ifInUcastPkts'=>'1.3.6.1.2.1.2.2.1.11',
320
+ 'ifInNUcastPkts'=>'1.3.6.1.2.1.2.2.1.12',
321
+ 'ifInDiscards'=>'1.3.6.1.2.1.2.2.1.13',
322
+ 'ifInErrors'=>'1.3.6.1.2.1.2.2.1.14',
323
+ 'ifInUnknownProtos'=>'1.3.6.1.2.1.2.2.1.15',
324
+ 'ifOutOctets'=>'1.3.6.1.2.1.2.2.1.16',
325
+ 'ifOutUcastPkts'=>'1.3.6.1.2.1.2.2.1.17',
326
+ 'ifOutNUcastPkts'=>'1.3.6.1.2.1.2.2.1.18',
327
+ 'ifOutDiscards'=>'1.3.6.1.2.1.2.2.1.19',
328
+ 'ifDescr'=>'1.3.6.1.2.1.2.2.1.2',
329
+ 'ifOutErrors'=>'1.3.6.1.2.1.2.2.1.20',
330
+ 'ifOutQLen'=>'1.3.6.1.2.1.2.2.1.21',
331
+ 'ifSpecific'=>'1.3.6.1.2.1.2.2.1.22',
332
+ 'ifType'=>'1.3.6.1.2.1.2.2.1.3',
333
+ 'ifMtu'=>'1.3.6.1.2.1.2.2.1.4',
334
+ 'ifSpeed'=>'1.3.6.1.2.1.2.2.1.5',
335
+ 'ifPhysAddress'=>'1.3.6.1.2.1.2.2.1.6',
336
+ 'ifAdminStatus'=>'1.3.6.1.2.1.2.2.1.7',
337
+ 'ifOperStatus'=>'1.3.6.1.2.1.2.2.1.8',
338
+ 'ifLastChange'=>'1.3.6.1.2.1.2.2.1.9',
339
+
340
+ 'ipAddrTable'=>'1.3.6.1.2.1.4.20',
341
+ 'ipAddrEntry'=>'1.3.6.1.2.1.4.20.1',
342
+ 'ipAdEntIfIndex'=>'1.3.6.1.2.1.4.20.1.2',
343
+ 'ipAdEntNetMask'=>'1.3.6.1.2.1.4.20.1.3',
344
+ 'ipNetToMediaIfIndex'=>'1.3.6.1.2.1.4.22.1.1',
345
+ 'ipNetToMediaPhysAddress'=>'1.3.6.1.2.1.4.22.1.2',
346
+ 'ipNetToMediaNetAddress'=>'1.3.6.1.2.1.4.22.1.3',
347
+ 'ipNetToMediaType'=>'1.3.6.1.2.1.4.22.1.4',
348
+
349
+ 'tcp'=>'1.3.6.1.2.1.6',
350
+ 'tcpCurrEstab'=>'1.3.6.1.2.1.6.9.0',
351
+ 'tcpActiveOpens'=>'1.3.6.1.2.1.6.5.0',
352
+ 'tcpPassiveOpens'=>'1.3.6.1.2.1.6.6.0',
353
+ 'tcpAttemptFails'=>'1.3.6.1.2.1.6.7.0',
354
+ 'tcpEstabResets'=>'1.3.6.1.2.1.6.8.0',
355
+ 'tcpInSegs'=>'1.3.6.1.2.1.6.10.0',
356
+ 'tcpOutSegs'=>'1.3.6.1.2.1.6.11.0',
357
+ 'tcpRetransSegs'=>'1.3.6.1.2.1.6.12.0',
358
+ 'tcpInErrs'=>'1.3.6.1.2.1.6.14.0',
359
+ 'tcpOutRsts'=>'1.3.6.1.2.1.6.15.0',
360
+
361
+ 'udp'=>'1.3.6.1.2.1.7',
362
+ 'udpInDatagrams' => '1.3.6.1.2.1.7.1.0',
363
+ 'udpNoPorts' => '1.3.6.1.2.1.7.2.0',
364
+ 'udpInErrors' => '1.3.6.1.2.1.7.3.0',
365
+ 'udpOutDatagrams' => '1.3.6.1.2.1.7.4.0',
366
+
367
+ 'host'=>'1.3.6.1.2.1.25',
368
+ 'hrSystem'=>'1.3.6.1.2.1.25.1',
369
+ 'hrSystemNumUsers'=>'1.3.6.1.2.1.25.1.5.0',
370
+ 'hrSystemProcesses'=>'1.3.6.1.2.1.25.1.6.0',
371
+ 'hrSystemMaxProcesses'=>'1.3.6.1.2.1.25.1.7.0',
372
+ 'hrSystem'=>'1.3.6.1.2.1.25.1',
373
+ 'hrStorage'=>'1.3.6.1.2.1.25.2',
374
+ 'hrStorageTypes'=>'1.3.6.1.2.1.25.2.1',
375
+ 'hrStorageFixedDisk'=>'1.3.6.1.2.1.25.2.1.4',
376
+ 'hrMemorySize'=>'1.3.6.1.2.1.25.2.2.0',
377
+ 'hrStorageTable'=>'1.3.6.1.2.1.25.2.3',
378
+ 'hrStorageEntry'=>'1.3.6.1.2.1.25.2.3.1',
379
+ 'hrStorageIndex'=>'1.3.6.1.2.1.25.2.3.1.1',
380
+ 'hrStorageType'=>'1.3.6.1.2.1.25.2.3.1.2',
381
+ 'hrStorageDescr'=>'1.3.6.1.2.1.25.2.3.1.3',
382
+ 'hrStorageAllocationUnits'=>'1.3.6.1.2.1.25.2.3.1.4',
383
+ 'hrStorageSize'=>'1.3.6.1.2.1.25.2.3.1.5',
384
+ 'hrStorageUsed'=>'1.3.6.1.2.1.25.2.3.1.6',
385
+
386
+ 'hrSWRunName'=>'1.3.6.1.2.1.25.4.2.1.2',
387
+ 'hrSWRunPath'=>'1.3.6.1.2.1.25.4.2.1.4',
388
+ 'hrSWRunParameters'=>'1.3.6.1.2.1.25.4.2.1.5',
389
+ 'hrSWRunPerfCPU'=>'1.3.6.1.2.1.25.5.1.1.1',
390
+ 'hrSWRunPerfMem'=>'1.3.6.1.2.1.25.5.1.1.2',
391
+
392
+ 'ifMIB'=>'1.3.6.1.2.1.31',
393
+ 'ifXEntry'=>'1.3.6.1.2.1.31.1.1.1',
394
+ 'ifName'=>'1.3.6.1.2.1.31.1.1.1.1',
395
+ 'ifInMulticastPkts'=>'1.3.6.1.2.1.31.1.1.1.2',
396
+ 'ifInBroadcastPkts'=>'1.3.6.1.2.1.31.1.1.1.3',
397
+ 'ifOutMulticastPkts'=>'1.3.6.1.2.1.31.1.1.1.4',
398
+ 'ifOutBroadcastPkts'=>'1.3.6.1.2.1.31.1.1.1.5',
399
+ 'ifHCInOctets'=>'1.3.6.1.2.1.31.1.1.1.6',
400
+ 'ifHCInUcastPkts'=>'1.3.6.1.2.1.31.1.1.1.7',
401
+ 'ifHCInMulticastPkts'=>'1.3.6.1.2.1.31.1.1.1.8',
402
+ 'ifHCInBroadcastPkts'=>'1.3.6.1.2.1.31.1.1.1.9',
403
+ 'ifHCOutOctets'=>'1.3.6.1.2.1.31.1.1.1.10',
404
+ 'ifHCOutUcastPkts'=>'1.3.6.1.2.1.31.1.1.1.11',
405
+ 'ifHCOutMulticastPkts'=>'1.3.6.1.2.1.31.1.1.1.12',
406
+ 'ifHCOutBroadcastPkts'=>'1.3.6.1.2.1.31.1.1.1.13',
407
+ 'ifLinkUpDownTrapEnable'=>'1.3.6.1.2.1.31.1.1.1.14',
408
+ 'ifHighSpeed'=>'1.3.6.1.2.1.31.1.1.1.15',
409
+ 'ifPromiscuousMode'=>'1.3.6.1.2.1.31.1.1.1.16',
410
+ 'ifConnectorPresent'=>'1.3.6.1.2.1.31.1.1.1.17',
411
+ 'ifAlias'=>'1.3.6.1.2.1.31.1.1.1.18',
412
+
413
+ 'entityMIB'=>'1.3.6.1.2.1.47',
414
+ 'entPhysicalEntry'=>'1.3.6.1.2.1.47.1.1.1.1',
415
+ 'entPhysicalDescr'=>'1.3.6.1.2.1.47.1.1.1.1.2',
416
+ 'entPhysicalName'=>'1.3.6.1.2.1.47.1.1.1.1.7',
417
+
418
+ 'experimental'=>'1.3.6.1.3',
419
+ 'private'=>'1.3.6.1.4',
420
+ 'enterprises'=>'1.3.6.1.4.1',
421
+ }
422
+ ROIDS = {}
423
+ RECV_SIZE = 2000
424
+
425
+ attr_reader :sock
426
+ attr_accessor :state, :numeric
427
+
428
+ def self.add_oid sym, oid
429
+ OIDS[sym] = oid
430
+ end
431
+
432
+ def self.update h
433
+ OIDS.merge! h
434
+ end
435
+
436
+ def initialize host, port=nil
437
+ port ||= 161
438
+ if ROIDS.empty?
439
+ for sym, oid in OIDS
440
+ ROIDS[BER.enc_v_oid(oid)] = sym
441
+ end
442
+ end
443
+ @host = host
444
+ @port = port
445
+ @request_id = 1000
446
+ @retries = 3
447
+ @timeout = 4
448
+ @ver = "\x02\x01\x00" # INT: 0 (ver 1)
449
+ @community = "\x04\x06public"
450
+ @numeric = false
451
+ @state = :IDLE
452
+ @sock = nil
453
+ end
454
+
455
+ def connect
456
+ @sock = UDPSocket.new
457
+ @sock.connect @host, @port
458
+ end
459
+
460
+ def close
461
+ @sock.close
462
+ end
463
+
464
+ def version=(ver)
465
+ ver = {'1'=>0, '2c'=>1}[ver] || 0
466
+ @ver = BER.enc_int ver
467
+ end
468
+
469
+ def community=(community)
470
+ @community = BER.enc_str community
471
+ end
472
+
473
+ def enoid2name enoid
474
+ a = BER.dec_oid enoid
475
+ n = a.size
476
+ n0 = n
477
+
478
+ while n > 1
479
+ if (s = ROIDS[BER.enc_a_oid(a[0, n])])
480
+ return ([s] + a[n, n0-n]).join('.')
481
+ end
482
+ n -= 1
483
+ end
484
+ s || a.join('.')
485
+ end
486
+
487
+ def make_msg cmd, err_index, varbind
488
+ @request_id += 1
489
+ @enc_request_id = BER.enc_int @request_id
490
+ s = @enc_request_id + "\x02\x01\x00" + err_index + varbind
491
+ s = @ver + @community + cmd + BER.enc_len(s.size) + s
492
+ s = "\x30" + BER.enc_len(s.size) + s
493
+ end
494
+
495
+ def make_req st, arg
496
+ case st
497
+ when :GETBULK_REQ
498
+ vb = "\x06" + BER.enc_len(arg.size) + arg + "\x05\x00"
499
+ vb = "\x30" + BER.enc_len(vb.size) + vb
500
+ varbind = "\x30" + BER.enc_len(vb.size) + vb
501
+ s = make_msg "\xa5", "\x02\x01\x0c", varbind
502
+ when :GETNEXT_REQ
503
+ vb = "\x06" + BER.enc_len(arg.size) + arg + "\x05\x00"
504
+ vb = "\x30" + BER.enc_len(vb.size) + vb
505
+ varbind = "\x30" + BER.enc_len(vb.size) + vb
506
+ s = make_msg "\xa1", "\x02\x01\x00", varbind
507
+ when :GET_REQ
508
+ s = make_msg "\xa0", "\x02\x01\x00", arg
509
+ end
510
+ s
511
+ end
512
+
513
+ def walk_start enoid, &cb
514
+ @req_enoid = enoid
515
+ @cb = cb
516
+ @state = (@ver == "\002\001\001") ? :GETBULK_REQ : :GETNEXT_REQ
517
+ end
518
+
519
+ def _walk enoid, &cb
520
+ walk_start enoid, &cb
521
+ req_loop enoid
522
+ end
523
+
524
+ def walk oid, &cb
525
+ noid = OIDS[oid] || oid
526
+ enoid = BER.enc_v_oid noid
527
+ _walk(enoid) {|enoid, tag, val|
528
+ oid = @numeric ? (BER.dec_oid(enoid).join('.')) : (enoid2name enoid)
529
+ cb.call oid, tag, val
530
+ }
531
+ end
532
+
533
+ def get_start varbind, &cb
534
+ @req_enoid = ''
535
+ @cb = cb
536
+ @state = :GET_REQ
537
+ end
538
+
539
+ def get_varbind varbind, &cb
540
+ get_start varbind, &cb
541
+ req_loop varbind
542
+ end
543
+
544
+ def get oid_list, &cb
545
+ oids = oid_list.map {|s| BER.enc_v_oid(OIDS[s]||s)}
546
+ varbind = BER.cat_enoid oids
547
+ get_varbind(varbind) {|enoid, tag, val|
548
+ oid = @numeric ? (BER.dec_oid(enoid).join('.')) : (enoid2name enoid)
549
+ cb.call oid, tag, val
550
+ }
551
+ end
552
+
553
+ def req_loop arg
554
+ connect unless @sock
555
+ until @state == :SUCCESS or @state == :IDLE
556
+ s = make_req @state, arg
557
+ @retry_count = 0
558
+ while true
559
+ @sock.send s, 0
560
+ if (a = IO.select([@sock], nil, nil, @timeout))
561
+ msg = @sock.recv RECV_SIZE, 0
562
+ arg = recv_msg msg
563
+ if arg
564
+ @state = :SUCCESS if @state == :GET_REQ
565
+ break
566
+ end
567
+ else
568
+ if (@retry_count += 1) >= @retries
569
+ @state = :IDLE
570
+ return
571
+ end
572
+ end
573
+ end
574
+ end
575
+ end
576
+
577
+ def recv_msg msg
578
+ enc_request_id, @error_status, @error_index, varbind = BER.dec_msg msg
579
+ return unless enc_request_id == @enc_request_id
580
+ unless @error_status == 0
581
+ @state = :IDLE
582
+ return
583
+ end
584
+
585
+ vars = BER.dec_varbind varbind
586
+ if vars.empty?
587
+ @state = :SUCCESS
588
+ return
589
+ end
590
+ for var in vars
591
+ if (var[1] != 130) and var[0].index(@req_enoid) #130=endOfMibView
592
+ @cb.call(*var)
593
+ else
594
+ @state = :SUCCESS
595
+ end
596
+ end
597
+ enoid = vars[-1][0]
598
+ end
599
+ end
data/mrbgem.rake ADDED
@@ -0,0 +1,4 @@
1
+ MRuby::Gem::Specification.new('mruby-msnmp') do |spec|
2
+ spec.license = 'MIT'
3
+ spec.authors = 'Internet Initiative Japan'
4
+ end
data/mrblib/msnmp.rb ADDED
@@ -0,0 +1,599 @@
1
+ # coding: us-ascii
2
+
3
+ class String
4
+ unless ''.respond_to? :getbyte
5
+ def getbyte idx
6
+ self[idx]
7
+ end
8
+ end
9
+
10
+ unless ''.respond_to? :slice!
11
+ def slice!(arg1, arg2 = 1)
12
+ return nil if arg2 < 0
13
+ if arg1.class == Fixnum
14
+ rval = self[arg1, arg2]
15
+ len = self.length
16
+ rpos = arg1 + arg2
17
+ rpos += len if arg1 < 0
18
+ rlen = len - rpos
19
+ region_l = self[0...arg1]
20
+ region_r = self[rpos, rlen]
21
+ region_r = '' if region_r == nil
22
+ self.replace(region_l + region_r)
23
+ elsif arg1.class == String
24
+ rval = arg1
25
+ self.gsub!(arg1, "")
26
+ else
27
+ return nil
28
+ end
29
+ rval
30
+ end
31
+ end
32
+ end
33
+
34
+ module BER
35
+ class C64
36
+ def initialize s
37
+ @s = s
38
+ @a = [0, 0, 0, 0, 0, 0, 0, 0]
39
+ j = 7
40
+ (s.size-1).downto(0) {|i| @a[j] = s.getbyte(i); j -= 1}
41
+ end
42
+
43
+ def to_s
44
+ '0x' + @a.map {|b| sprintf "%02x", b}.join('')
45
+ end
46
+
47
+ def to_binary
48
+ @a.pack('C*')
49
+ end
50
+
51
+ def to_msgpack
52
+ "\xcf" + @a.pack('C*')
53
+ end
54
+
55
+ def to_i
56
+ n = 0
57
+ 0.upto(7) {|i| n = n * 256 + @a[i]}
58
+ n
59
+ end
60
+ end
61
+
62
+ def enc tag, val
63
+ case tag
64
+ when 0x02
65
+ return enc_int(val)
66
+ when 0x04
67
+ return enc_str(val)
68
+ end
69
+ end
70
+
71
+ def enc_len n
72
+ if n < 128 then
73
+ return n.chr
74
+ end
75
+ e_len = ''
76
+ while n > 0
77
+ e_len = (n & 0xff).chr + e_len
78
+ n = n >> 8
79
+ end
80
+ return (e_len.size | 0x80).chr + e_len
81
+ end
82
+
83
+ def enc_int n
84
+ ebuf = ''
85
+ if n < 0 then
86
+ begin
87
+ ebuf = (n & 0xff).chr + ebuf
88
+ n = n >> 8
89
+ end until (n == -1) and (ebuf.getbyte(0) & 0x80 == 0x80)
90
+ else
91
+ begin
92
+ ebuf = (n & 0xff).chr + ebuf
93
+ n = n >> 8
94
+ end while n > 0
95
+ end
96
+ return "\x02" + enc_len(ebuf.size) + ebuf
97
+ end
98
+
99
+ def enc_str s
100
+ return "\x04" + enc_len(s.size) + s
101
+ end
102
+
103
+ def enc_a_oid a
104
+ a.shift; a.shift
105
+ e = '+' # 1.3
106
+ until a.empty?
107
+ subid = a.shift.to_i
108
+ s = ''
109
+ if subid == 0 then
110
+ e += "\x00"
111
+ else
112
+ while subid > 0
113
+ if s.empty?
114
+ s = (subid & 0x7f).chr + s
115
+ else
116
+ s = (subid & 0x7f | 0x80).chr + s
117
+ end
118
+ subid = subid >> 7
119
+ end
120
+ e += s
121
+ end
122
+ end
123
+ e
124
+ end
125
+
126
+ def enc_v_oid oid
127
+ a = oid.split('.')
128
+ enc_a_oid a
129
+ end
130
+
131
+ def enc_oid oid
132
+ e = enc_v_oid oid
133
+ return "\x06" + enc_len(e.size) + e
134
+ end
135
+
136
+ def enc_oid_list oid_list
137
+ s = ''
138
+ for e in oid_list
139
+ vb = enc_oid(e) + "\x05\x00" # BER.enc_null
140
+ s = s + "\x30" + enc_len(vb.size) + vb
141
+ end
142
+ varbind = "\x30" + enc_len(s.size) + s
143
+ end
144
+
145
+ def enc_varbind enoid
146
+ s = "\x06" + enc_len(enoid.size) + enoid + "\x05\x00"
147
+ s = "\x30" + enc_len(s.size) + s
148
+ return "\x30" + enc_len(s.size) + s
149
+ end
150
+
151
+ def cat_enoid enoids
152
+ s = ''
153
+ for enoid in enoids
154
+ vb = "\x06" + enc_len(enoid.size) + enoid + "\x05\x00"
155
+ s = s + "\x30" + enc_len(vb.size) + vb
156
+ end
157
+ varbind = "\x30" + enc_len(s.size) + s
158
+ end
159
+
160
+ def dec_oid msg
161
+ msg = msg.clone
162
+ if msg.getbyte(0) == 0x2b
163
+ oid = [1, 3]
164
+ msg.slice! 0
165
+ else
166
+ oid = []
167
+ end
168
+ until msg.empty?
169
+ c = msg.getbyte 0
170
+ msg.slice! 0
171
+ if c > 127
172
+ id = 0
173
+ while c > 127
174
+ id = (id << 7) + (c & 0x7f)
175
+ c = msg.getbyte 0
176
+ msg.slice! 0
177
+ end
178
+ id = (id << 7) + c
179
+ oid.push id
180
+ else
181
+ oid.push c
182
+ end
183
+ end
184
+ return oid
185
+ end
186
+
187
+ def dec_int s
188
+ if (s.getbyte(0) & 0x80) == 0x80
189
+ n = -1
190
+ else
191
+ n = 0
192
+ end
193
+ while s.size > 0
194
+ n = n << 8 | s.getbyte(0)
195
+ s = s[1..-1]
196
+ end
197
+ return n
198
+ end
199
+
200
+ def dec_cnt s
201
+ n = 0
202
+ while s.size > 0
203
+ n = n << 8 | s.getbyte(0)
204
+ s = s[1..-1]
205
+ end
206
+ return n
207
+ end
208
+
209
+ def dec_cnt64 s
210
+ C64.new(s)
211
+ end
212
+
213
+ Object.const_defined?(:RUBY_VERSION) and
214
+ alias_method(:dec_cnt64, :dec_cnt)
215
+
216
+ def tlv data
217
+ tag = data.getbyte 0
218
+ if (len = data.getbyte(1)) < 128
219
+ val = data[2, len]
220
+ remain = data[(len + 2)..-1]
221
+ elsif len == 0x81
222
+ len = data.getbyte 2
223
+ val = data[3, len]
224
+ remain = data[(len + 3)..-1]
225
+ else
226
+ n = len & 0x7f
227
+ len = 0
228
+ for i in 1..n
229
+ len = len * 256 + data.getbyte(i + 1)
230
+ end
231
+ val = data[n + 2, len]
232
+ remain = data[(len + n + 2).. -1]
233
+ end
234
+ return tag, val, remain
235
+ end
236
+
237
+ def dec_msg msg
238
+ if msg[1, 1] < "\x80"
239
+ idx = msg.getbyte(6) + 7
240
+ pdutype, pdu, msg = tlv msg[idx..-1]
241
+ elsif msg[1, 1] == "\x81"
242
+ idx = msg.getbyte(7) + 8
243
+ pdutype, pdu, msg = tlv msg[idx..-1]
244
+ else
245
+ tag, val, msg = tlv msg
246
+ tag, ver, msg = tlv val
247
+ tag, comm, msg = tlv msg
248
+ pdutype, pdu, msg = tlv msg
249
+ end
250
+ idlen = pdu.getbyte 1
251
+ enc_reqid = pdu[0, idlen + 2]
252
+ error_status = pdu.getbyte(idlen + 4)
253
+ error_index = pdu.getbyte(idlen + 7)
254
+ tag, varbind, msg = tlv pdu[idlen+8..-1]
255
+ return enc_reqid, error_status, error_index, varbind
256
+ end
257
+
258
+ def dec_varbind_block msg
259
+ while msg.size > 0
260
+ tag,val,msg=tlv(msg)
261
+ tag,val1,msg0=tlv(val)
262
+ tag,val2,msg1=tlv(msg0)
263
+ case tag
264
+ when 0x02
265
+ val2=dec_int(val2)
266
+ when 0x05 #Null
267
+ val2=nil
268
+ when 0x06 #OID
269
+ #val2=dec_oid(val2)
270
+ when 0x40 # IP Address
271
+ val2 = val2.unpack('CCCC').join('.')
272
+ when 0x41, 0x42, 0x43 #Counter or Gauge or Tick
273
+ val2 = dec_cnt val2
274
+ when 0x46 #Counter64
275
+ val2 = dec_cnt64 val2
276
+ when 0x80
277
+ next # skip
278
+ end
279
+ yield val1, tag, val2
280
+ end
281
+ end
282
+
283
+ def dec_varbind msg
284
+ list=[]
285
+ val1 = tag = val2 = nil #XXX
286
+ dec_varbind_block(msg) {|val1, tag, val2|
287
+ list.push([val1,tag,val2])
288
+ }
289
+ return list
290
+ end
291
+
292
+ extend BER
293
+ end
294
+
295
+ class SNMP
296
+ OIDS = {
297
+ 'iso'=>'1',
298
+ 'org'=>'1.3',
299
+ 'dod'=>'1.3.6',
300
+ 'internet'=>'1.3.6.1',
301
+ 'directory'=>'1.3.6.1.1',
302
+ 'mgmt'=>'1.3.6.1.2',
303
+ 'mib-2'=>'1.3.6.1.2.1',
304
+ 'system'=>'1.3.6.1.2.1.1',
305
+ 'sysDescr'=>'1.3.6.1.2.1.1.1',
306
+ 'sysObjectID'=>'1.3.6.1.2.1.1.2',
307
+ 'sysUpTime'=>'1.3.6.1.2.1.1.3',
308
+ 'sysContact'=>'1.3.6.1.2.1.1.4',
309
+ 'sysName'=>'1.3.6.1.2.1.1.5',
310
+ 'sysLocation'=>'1.3.6.1.2.1.1.6',
311
+ 'sysServices'=>'1.3.6.1.2.1.1.7',
312
+
313
+ 'interfaces'=>'1.3.6.1.2.1.2',
314
+ 'ifNumber'=>'1.3.6.1.2.1.2.1',
315
+ 'ifTable'=>'1.3.6.1.2.1.2.2',
316
+ 'ifEntry'=>'1.3.6.1.2.1.2.2.1',
317
+ 'ifIndex'=>'1.3.6.1.2.1.2.2.1.1',
318
+ 'ifInOctets'=>'1.3.6.1.2.1.2.2.1.10',
319
+ 'ifInUcastPkts'=>'1.3.6.1.2.1.2.2.1.11',
320
+ 'ifInNUcastPkts'=>'1.3.6.1.2.1.2.2.1.12',
321
+ 'ifInDiscards'=>'1.3.6.1.2.1.2.2.1.13',
322
+ 'ifInErrors'=>'1.3.6.1.2.1.2.2.1.14',
323
+ 'ifInUnknownProtos'=>'1.3.6.1.2.1.2.2.1.15',
324
+ 'ifOutOctets'=>'1.3.6.1.2.1.2.2.1.16',
325
+ 'ifOutUcastPkts'=>'1.3.6.1.2.1.2.2.1.17',
326
+ 'ifOutNUcastPkts'=>'1.3.6.1.2.1.2.2.1.18',
327
+ 'ifOutDiscards'=>'1.3.6.1.2.1.2.2.1.19',
328
+ 'ifDescr'=>'1.3.6.1.2.1.2.2.1.2',
329
+ 'ifOutErrors'=>'1.3.6.1.2.1.2.2.1.20',
330
+ 'ifOutQLen'=>'1.3.6.1.2.1.2.2.1.21',
331
+ 'ifSpecific'=>'1.3.6.1.2.1.2.2.1.22',
332
+ 'ifType'=>'1.3.6.1.2.1.2.2.1.3',
333
+ 'ifMtu'=>'1.3.6.1.2.1.2.2.1.4',
334
+ 'ifSpeed'=>'1.3.6.1.2.1.2.2.1.5',
335
+ 'ifPhysAddress'=>'1.3.6.1.2.1.2.2.1.6',
336
+ 'ifAdminStatus'=>'1.3.6.1.2.1.2.2.1.7',
337
+ 'ifOperStatus'=>'1.3.6.1.2.1.2.2.1.8',
338
+ 'ifLastChange'=>'1.3.6.1.2.1.2.2.1.9',
339
+
340
+ 'ipAddrTable'=>'1.3.6.1.2.1.4.20',
341
+ 'ipAddrEntry'=>'1.3.6.1.2.1.4.20.1',
342
+ 'ipAdEntIfIndex'=>'1.3.6.1.2.1.4.20.1.2',
343
+ 'ipAdEntNetMask'=>'1.3.6.1.2.1.4.20.1.3',
344
+ 'ipNetToMediaIfIndex'=>'1.3.6.1.2.1.4.22.1.1',
345
+ 'ipNetToMediaPhysAddress'=>'1.3.6.1.2.1.4.22.1.2',
346
+ 'ipNetToMediaNetAddress'=>'1.3.6.1.2.1.4.22.1.3',
347
+ 'ipNetToMediaType'=>'1.3.6.1.2.1.4.22.1.4',
348
+
349
+ 'tcp'=>'1.3.6.1.2.1.6',
350
+ 'tcpCurrEstab'=>'1.3.6.1.2.1.6.9.0',
351
+ 'tcpActiveOpens'=>'1.3.6.1.2.1.6.5.0',
352
+ 'tcpPassiveOpens'=>'1.3.6.1.2.1.6.6.0',
353
+ 'tcpAttemptFails'=>'1.3.6.1.2.1.6.7.0',
354
+ 'tcpEstabResets'=>'1.3.6.1.2.1.6.8.0',
355
+ 'tcpInSegs'=>'1.3.6.1.2.1.6.10.0',
356
+ 'tcpOutSegs'=>'1.3.6.1.2.1.6.11.0',
357
+ 'tcpRetransSegs'=>'1.3.6.1.2.1.6.12.0',
358
+ 'tcpInErrs'=>'1.3.6.1.2.1.6.14.0',
359
+ 'tcpOutRsts'=>'1.3.6.1.2.1.6.15.0',
360
+
361
+ 'udp'=>'1.3.6.1.2.1.7',
362
+ 'udpInDatagrams' => '1.3.6.1.2.1.7.1.0',
363
+ 'udpNoPorts' => '1.3.6.1.2.1.7.2.0',
364
+ 'udpInErrors' => '1.3.6.1.2.1.7.3.0',
365
+ 'udpOutDatagrams' => '1.3.6.1.2.1.7.4.0',
366
+
367
+ 'host'=>'1.3.6.1.2.1.25',
368
+ 'hrSystem'=>'1.3.6.1.2.1.25.1',
369
+ 'hrSystemNumUsers'=>'1.3.6.1.2.1.25.1.5.0',
370
+ 'hrSystemProcesses'=>'1.3.6.1.2.1.25.1.6.0',
371
+ 'hrSystemMaxProcesses'=>'1.3.6.1.2.1.25.1.7.0',
372
+ 'hrSystem'=>'1.3.6.1.2.1.25.1',
373
+ 'hrStorage'=>'1.3.6.1.2.1.25.2',
374
+ 'hrStorageTypes'=>'1.3.6.1.2.1.25.2.1',
375
+ 'hrStorageFixedDisk'=>'1.3.6.1.2.1.25.2.1.4',
376
+ 'hrMemorySize'=>'1.3.6.1.2.1.25.2.2.0',
377
+ 'hrStorageTable'=>'1.3.6.1.2.1.25.2.3',
378
+ 'hrStorageEntry'=>'1.3.6.1.2.1.25.2.3.1',
379
+ 'hrStorageIndex'=>'1.3.6.1.2.1.25.2.3.1.1',
380
+ 'hrStorageType'=>'1.3.6.1.2.1.25.2.3.1.2',
381
+ 'hrStorageDescr'=>'1.3.6.1.2.1.25.2.3.1.3',
382
+ 'hrStorageAllocationUnits'=>'1.3.6.1.2.1.25.2.3.1.4',
383
+ 'hrStorageSize'=>'1.3.6.1.2.1.25.2.3.1.5',
384
+ 'hrStorageUsed'=>'1.3.6.1.2.1.25.2.3.1.6',
385
+
386
+ 'hrSWRunName'=>'1.3.6.1.2.1.25.4.2.1.2',
387
+ 'hrSWRunPath'=>'1.3.6.1.2.1.25.4.2.1.4',
388
+ 'hrSWRunParameters'=>'1.3.6.1.2.1.25.4.2.1.5',
389
+ 'hrSWRunPerfCPU'=>'1.3.6.1.2.1.25.5.1.1.1',
390
+ 'hrSWRunPerfMem'=>'1.3.6.1.2.1.25.5.1.1.2',
391
+
392
+ 'ifMIB'=>'1.3.6.1.2.1.31',
393
+ 'ifXEntry'=>'1.3.6.1.2.1.31.1.1.1',
394
+ 'ifName'=>'1.3.6.1.2.1.31.1.1.1.1',
395
+ 'ifInMulticastPkts'=>'1.3.6.1.2.1.31.1.1.1.2',
396
+ 'ifInBroadcastPkts'=>'1.3.6.1.2.1.31.1.1.1.3',
397
+ 'ifOutMulticastPkts'=>'1.3.6.1.2.1.31.1.1.1.4',
398
+ 'ifOutBroadcastPkts'=>'1.3.6.1.2.1.31.1.1.1.5',
399
+ 'ifHCInOctets'=>'1.3.6.1.2.1.31.1.1.1.6',
400
+ 'ifHCInUcastPkts'=>'1.3.6.1.2.1.31.1.1.1.7',
401
+ 'ifHCInMulticastPkts'=>'1.3.6.1.2.1.31.1.1.1.8',
402
+ 'ifHCInBroadcastPkts'=>'1.3.6.1.2.1.31.1.1.1.9',
403
+ 'ifHCOutOctets'=>'1.3.6.1.2.1.31.1.1.1.10',
404
+ 'ifHCOutUcastPkts'=>'1.3.6.1.2.1.31.1.1.1.11',
405
+ 'ifHCOutMulticastPkts'=>'1.3.6.1.2.1.31.1.1.1.12',
406
+ 'ifHCOutBroadcastPkts'=>'1.3.6.1.2.1.31.1.1.1.13',
407
+ 'ifLinkUpDownTrapEnable'=>'1.3.6.1.2.1.31.1.1.1.14',
408
+ 'ifHighSpeed'=>'1.3.6.1.2.1.31.1.1.1.15',
409
+ 'ifPromiscuousMode'=>'1.3.6.1.2.1.31.1.1.1.16',
410
+ 'ifConnectorPresent'=>'1.3.6.1.2.1.31.1.1.1.17',
411
+ 'ifAlias'=>'1.3.6.1.2.1.31.1.1.1.18',
412
+
413
+ 'entityMIB'=>'1.3.6.1.2.1.47',
414
+ 'entPhysicalEntry'=>'1.3.6.1.2.1.47.1.1.1.1',
415
+ 'entPhysicalDescr'=>'1.3.6.1.2.1.47.1.1.1.1.2',
416
+ 'entPhysicalName'=>'1.3.6.1.2.1.47.1.1.1.1.7',
417
+
418
+ 'experimental'=>'1.3.6.1.3',
419
+ 'private'=>'1.3.6.1.4',
420
+ 'enterprises'=>'1.3.6.1.4.1',
421
+ }
422
+ ROIDS = {}
423
+ RECV_SIZE = 2000
424
+
425
+ attr_reader :sock
426
+ attr_accessor :state, :numeric
427
+
428
+ def self.add_oid sym, oid
429
+ OIDS[sym] = oid
430
+ end
431
+
432
+ def self.update h
433
+ OIDS.merge! h
434
+ end
435
+
436
+ def initialize host, port=nil
437
+ port ||= 161
438
+ if ROIDS.empty?
439
+ for sym, oid in OIDS
440
+ ROIDS[BER.enc_v_oid(oid)] = sym
441
+ end
442
+ end
443
+ @host = host
444
+ @port = port
445
+ @request_id = 1000
446
+ @retries = 3
447
+ @timeout = 4
448
+ @ver = "\x02\x01\x00" # INT: 0 (ver 1)
449
+ @community = "\x04\x06public"
450
+ @numeric = false
451
+ @state = :IDLE
452
+ @sock = nil
453
+ end
454
+
455
+ def connect
456
+ @sock = UDPSocket.new
457
+ @sock.connect @host, @port
458
+ end
459
+
460
+ def close
461
+ @sock.close
462
+ end
463
+
464
+ def version=(ver)
465
+ ver = {'1'=>0, '2c'=>1}[ver] || 0
466
+ @ver = BER.enc_int ver
467
+ end
468
+
469
+ def community=(community)
470
+ @community = BER.enc_str community
471
+ end
472
+
473
+ def enoid2name enoid
474
+ a = BER.dec_oid enoid
475
+ n = a.size
476
+ n0 = n
477
+
478
+ while n > 1
479
+ if (s = ROIDS[BER.enc_a_oid(a[0, n])])
480
+ return ([s] + a[n, n0-n]).join('.')
481
+ end
482
+ n -= 1
483
+ end
484
+ s || a.join('.')
485
+ end
486
+
487
+ def make_msg cmd, err_index, varbind
488
+ @request_id += 1
489
+ @enc_request_id = BER.enc_int @request_id
490
+ s = @enc_request_id + "\x02\x01\x00" + err_index + varbind
491
+ s = @ver + @community + cmd + BER.enc_len(s.size) + s
492
+ s = "\x30" + BER.enc_len(s.size) + s
493
+ end
494
+
495
+ def make_req st, arg
496
+ case st
497
+ when :GETBULK_REQ
498
+ vb = "\x06" + BER.enc_len(arg.size) + arg + "\x05\x00"
499
+ vb = "\x30" + BER.enc_len(vb.size) + vb
500
+ varbind = "\x30" + BER.enc_len(vb.size) + vb
501
+ s = make_msg "\xa5", "\x02\x01\x0c", varbind
502
+ when :GETNEXT_REQ
503
+ vb = "\x06" + BER.enc_len(arg.size) + arg + "\x05\x00"
504
+ vb = "\x30" + BER.enc_len(vb.size) + vb
505
+ varbind = "\x30" + BER.enc_len(vb.size) + vb
506
+ s = make_msg "\xa1", "\x02\x01\x00", varbind
507
+ when :GET_REQ
508
+ s = make_msg "\xa0", "\x02\x01\x00", arg
509
+ end
510
+ s
511
+ end
512
+
513
+ def walk_start enoid, &cb
514
+ @req_enoid = enoid
515
+ @cb = cb
516
+ @state = (@ver == "\002\001\001") ? :GETBULK_REQ : :GETNEXT_REQ
517
+ end
518
+
519
+ def _walk enoid, &cb
520
+ walk_start enoid, &cb
521
+ req_loop enoid
522
+ end
523
+
524
+ def walk oid, &cb
525
+ noid = OIDS[oid] || oid
526
+ enoid = BER.enc_v_oid noid
527
+ _walk(enoid) {|enoid, tag, val|
528
+ oid = @numeric ? (BER.dec_oid(enoid).join('.')) : (enoid2name enoid)
529
+ cb.call oid, tag, val
530
+ }
531
+ end
532
+
533
+ def get_start varbind, &cb
534
+ @req_enoid = ''
535
+ @cb = cb
536
+ @state = :GET_REQ
537
+ end
538
+
539
+ def get_varbind varbind, &cb
540
+ get_start varbind, &cb
541
+ req_loop varbind
542
+ end
543
+
544
+ def get oid_list, &cb
545
+ oids = oid_list.map {|s| BER.enc_v_oid(OIDS[s]||s)}
546
+ varbind = BER.cat_enoid oids
547
+ get_varbind(varbind) {|enoid, tag, val|
548
+ oid = @numeric ? (BER.dec_oid(enoid).join('.')) : (enoid2name enoid)
549
+ cb.call oid, tag, val
550
+ }
551
+ end
552
+
553
+ def req_loop arg
554
+ connect unless @sock
555
+ until @state == :SUCCESS or @state == :IDLE
556
+ s = make_req @state, arg
557
+ @retry_count = 0
558
+ while true
559
+ @sock.send s, 0
560
+ if (a = IO.select([@sock], nil, nil, @timeout))
561
+ msg = @sock.recv RECV_SIZE, 0
562
+ arg = recv_msg msg
563
+ if arg
564
+ @state = :SUCCESS if @state == :GET_REQ
565
+ break
566
+ end
567
+ else
568
+ if (@retry_count += 1) >= @retries
569
+ @state = :IDLE
570
+ return
571
+ end
572
+ end
573
+ end
574
+ end
575
+ end
576
+
577
+ def recv_msg msg
578
+ enc_request_id, @error_status, @error_index, varbind = BER.dec_msg msg
579
+ return unless enc_request_id == @enc_request_id
580
+ unless @error_status == 0
581
+ @state = :IDLE
582
+ return
583
+ end
584
+
585
+ vars = BER.dec_varbind varbind
586
+ if vars.empty?
587
+ @state = :SUCCESS
588
+ return
589
+ end
590
+ for var in vars
591
+ if (var[1] != 130) and var[0].index(@req_enoid) #130=endOfMibView
592
+ @cb.call(*var)
593
+ else
594
+ @state = :SUCCESS
595
+ end
596
+ end
597
+ enoid = vars[-1][0]
598
+ end
599
+ end
data/msnmp.gemspec ADDED
@@ -0,0 +1,14 @@
1
+ # -*- encoding: utf-8 -*-
2
+ Gem::Specification.new do |s|
3
+ s.name = "msnmp"
4
+ s.version = "0.0.1"
5
+ s.authors = ["maebashi"]
6
+ s.homepage = ""
7
+ s.summary = %q{msnmp}
8
+ s.description = %q{msnmp}
9
+ s.files = `git ls-files`.split("\n").select {|e| /^tmp/!~e}
10
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
11
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f
12
+ ) }
13
+ s.require_paths = ["lib"]
14
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: msnmp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - maebashi
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-08-29 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: msnmp
15
+ email:
16
+ executables:
17
+ - msnmpwalk
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - MIT-LICENSE.txt
23
+ - README.md
24
+ - Rakefile
25
+ - bin/msnmpwalk
26
+ - lib/msnmp.rb
27
+ - mrbgem.rake
28
+ - mrblib/msnmp.rb
29
+ - msnmp.gemspec
30
+ homepage: ''
31
+ licenses: []
32
+ post_install_message:
33
+ rdoc_options: []
34
+ require_paths:
35
+ - lib
36
+ required_ruby_version: !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ! '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ requirements: []
49
+ rubyforge_project:
50
+ rubygems_version: 1.8.23
51
+ signing_key:
52
+ specification_version: 3
53
+ summary: msnmp
54
+ test_files: []