msnmp 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []