netsnmp 0.6.3 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +8 -29
- data/lib/netsnmp/encryption/aes.rb +35 -6
- data/lib/netsnmp/loggable.rb +1 -1
- data/lib/netsnmp/message.rb +11 -1
- data/lib/netsnmp/mib/parser.rb +4 -4
- data/lib/netsnmp/mib.rb +8 -2
- data/lib/netsnmp/security_parameters.rb +47 -10
- data/lib/netsnmp/version.rb +1 -1
- data/sig/encryption/aes.rbs +2 -1
- metadata +11 -34
- data/spec/client_spec.rb +0 -226
- data/spec/handlers/celluloid_spec.rb +0 -47
- data/spec/mib_spec.rb +0 -13
- data/spec/oid_spec.rb +0 -17
- data/spec/pdu_spec.rb +0 -37
- data/spec/security_parameters_spec.rb +0 -90
- data/spec/session_spec.rb +0 -14
- data/spec/spec_helper.rb +0 -67
- data/spec/support/celluloid.rb +0 -56
- data/spec/support/request_examples.rb +0 -56
- data/spec/timeticks_spec.rb +0 -14
- data/spec/v3_session_spec.rb +0 -25
- data/spec/varbind_spec.rb +0 -136
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dccc7490c2cbc05114eb97dbbf976a500939d3d4ac9d2fd7cc423c140201e809
|
4
|
+
data.tar.gz: 0b15cf923a202e51d27c000cce1187c4b63d68dd2dc1b354105a27ebb61846b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da6cc741eee319b0e07c4fd04f5bd55003bd761be7c5598695b4894c3d399e0519d81bbf7ef6c64d3dc355f3b512802372541ea10859aa96f701a29d88a604ad
|
7
|
+
data.tar.gz: a8b786a99c61cd722a789e06b6810545a54b18de47a08bf090f0251649fe20f22987294da9c54f8875783a29779817276b5368004adc52e80a6122b3cbbfcd9c
|
data/README.md
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# netsnmp
|
2
2
|
|
3
|
-
![
|
4
|
-
|
5
|
-
[![
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/netsnmp.svg)](http://rubygems.org/gems/netsnmp)
|
4
|
+
![Tests](https://github.com/HoneyryderChuck/ruby-netsnmp/workflows/Tests/badge.svg)
|
5
|
+
[![Code Climate](https://codeclimate.com/github/HoneyryderChuck/ruby-netsnmp/badges/gpa.svg)](https://codeclimate.com/github/HoneyryderChuck/ruby-netsnmp)
|
6
|
+
[![Docs](http://img.shields.io/badge/yard-docs-blue.svg)](https://www.rubydoc.info/github/HoneyryderChuck/ruby-netsnmp/master)
|
6
7
|
|
7
8
|
The `netsnmp` gem provides a ruby native implementation of the SNMP protocol (v1/2c abd v3).
|
8
9
|
|
@@ -31,36 +32,14 @@ $ gem install netsnmp
|
|
31
32
|
This gem provides:
|
32
33
|
|
33
34
|
* Implementation in ruby of the SNMP Protocol for v3, v2c and v1 (most notable the rfc3414 and 3826).
|
34
|
-
* SNMPv3 USM supporting MD5/
|
35
|
+
* SNMPv3 USM supporting MD5/SHA1/SHA224/SHA256/SHA384/SHA512 auth and DES/AES128/AES192/AES256 privacy crypto algorithms.
|
35
36
|
* Client/Manager API with simple interface for get, genext, set and walk.
|
36
|
-
* Pure Ruby.
|
37
|
-
* Support for concurrency and evented I/O.
|
38
|
-
|
39
|
-
## Why?
|
40
|
-
|
41
|
-
If you look for snmp gems in ruby toolbox, you'll find a bunch.
|
42
|
-
You may ask, why not just use one of them?
|
43
|
-
|
44
|
-
Most of them only implement v1 and v2, so if your requirement is to use v3, you're left with only 2 choices: [net-snmp](https://github.com/mixtli/net-snmp) (unmantained since 2013) and its follow-up [net-snmp2](https://github.com/jbreeden/net-snmp2), which started as a fork to fix some bugs left unattended. Both libraries wrap the C netsnmp library using FFI, which leaves them vulnerable to the following bugs (experienced in both libraries):
|
45
|
-
|
46
|
-
* Dependency of specific versions of netsnmp C package.
|
47
|
-
* Memory Leaks.
|
48
|
-
* Doesn't work reliable in ruby > 2.0.0-p576, crashing the VM.
|
49
|
-
* Network I/O done by the library, thereby blocking the GVL, thereby making all snmp calls block the whole ruby VM.
|
50
|
-
* This means, multi-threading is impossible.
|
51
|
-
* This means, evented I/O is impossible.
|
52
|
-
|
53
|
-
All of these issues are resolved here.
|
54
|
-
|
55
|
-
## Features
|
56
|
-
|
57
|
-
* Client Interface, which supports SNMP v3, v2c, and v1
|
58
|
-
* Supports get, getnext, set and walk calls
|
59
37
|
* MIB support
|
60
38
|
* Proxy IO object support (for eventmachine/celluloid-io)
|
61
|
-
* Ruby >= 2.1 support (modern)
|
62
39
|
* Pure Ruby (no FFI)
|
63
40
|
* Easy PDU debugging
|
41
|
+
* Support for concurrency and evented I/O.
|
42
|
+
|
64
43
|
|
65
44
|
## Examples
|
66
45
|
|
@@ -126,7 +105,7 @@ The `NETSNMP::Timeticks` type is internal to this library, but it is a ruby `Num
|
|
126
105
|
|
127
106
|
Counter32 and Counter64 types will map to plain integers.
|
128
107
|
|
129
|
-
You can find usage examples [here](https://github.com/
|
108
|
+
You can find usage examples [here](https://github.com/HoneyryderChuck/ruby-netsnmp/blob/master/spec/varbind_spec.rb). If you need support to a missing type, you have the following options:
|
130
109
|
|
131
110
|
* Use the `:type` parameter in `#set` calls:
|
132
111
|
```ruby
|
@@ -3,18 +3,30 @@
|
|
3
3
|
module NETSNMP
|
4
4
|
module Encryption
|
5
5
|
class AES
|
6
|
-
def initialize(priv_key, local: 0)
|
6
|
+
def initialize(priv_key, cipher:, local: 0)
|
7
7
|
@priv_key = priv_key
|
8
8
|
@local = local
|
9
|
+
# https://www.rfc-editor.org/rfc/rfc3826
|
10
|
+
# https://snmp.com/snmpv3/snmpv3_aes256.shtml
|
11
|
+
# Note: AES Blumental is not supported and not widely used
|
12
|
+
@cipher = cipher
|
9
13
|
end
|
10
14
|
|
11
15
|
def encrypt(decrypted_data, engine_boots:, engine_time:)
|
12
|
-
cipher =
|
16
|
+
cipher = case @cipher
|
17
|
+
when :aes, :aes128 then OpenSSL::Cipher.new("aes-128-cfb")
|
18
|
+
when :aes192 then OpenSSL::Cipher.new("aes-192-cfb")
|
19
|
+
when :aes256 then OpenSSL::Cipher.new("aes-256-cfb")
|
20
|
+
end
|
13
21
|
|
14
22
|
iv, salt = generate_encryption_key(engine_boots, engine_time)
|
15
23
|
|
16
24
|
cipher.encrypt
|
17
|
-
cipher.iv =
|
25
|
+
cipher.iv = case @cipher
|
26
|
+
when :aes, :aes128 then iv[0, 16]
|
27
|
+
when :aes192 then iv[0, 24]
|
28
|
+
when :aes256 then iv[0, 32]
|
29
|
+
end
|
18
30
|
cipher.key = aes_key
|
19
31
|
|
20
32
|
if (diff = decrypted_data.length % 8) != 0
|
@@ -29,14 +41,22 @@ module NETSNMP
|
|
29
41
|
def decrypt(encrypted_data, salt:, engine_boots:, engine_time:)
|
30
42
|
raise Error, "invalid priv salt received" unless !salt.empty? && (salt.length % 8).zero?
|
31
43
|
|
32
|
-
cipher =
|
44
|
+
cipher = case @cipher
|
45
|
+
when :aes, :aes128 then OpenSSL::Cipher.new("aes-128-cfb")
|
46
|
+
when :aes192 then OpenSSL::Cipher.new("aes-192-cfb")
|
47
|
+
when :aes256 then OpenSSL::Cipher.new("aes-256-cfb")
|
48
|
+
end
|
33
49
|
cipher.padding = 0
|
34
50
|
|
35
51
|
iv = generate_decryption_key(engine_boots, engine_time, salt)
|
36
52
|
|
37
53
|
cipher.decrypt
|
38
54
|
cipher.key = aes_key
|
39
|
-
cipher.iv =
|
55
|
+
cipher.iv = case @cipher
|
56
|
+
when :aes, :aes128 then iv[0..16]
|
57
|
+
when :aes192 then iv[0..24]
|
58
|
+
when :aes256 then iv[0..32]
|
59
|
+
end
|
40
60
|
decrypted_data = cipher.update(encrypted_data) + cipher.final
|
41
61
|
|
42
62
|
hlen, bodylen = OpenSSL::ASN1.traverse(decrypted_data) { |_, _, x, y, *| break x, y }
|
@@ -58,6 +78,11 @@ module NETSNMP
|
|
58
78
|
@local = @local == 0xffffffffffffffff ? 0 : @local + 1
|
59
79
|
|
60
80
|
iv = generate_decryption_key(boots, time, salt)
|
81
|
+
iv = case @cipher
|
82
|
+
when :aes, :aes128 then iv[0, 16]
|
83
|
+
when :aes192 then iv[0, 24]
|
84
|
+
when :aes256 then iv[0, 32]
|
85
|
+
end
|
61
86
|
|
62
87
|
[iv, salt]
|
63
88
|
end
|
@@ -74,7 +99,11 @@ module NETSNMP
|
|
74
99
|
end
|
75
100
|
|
76
101
|
def aes_key
|
77
|
-
@
|
102
|
+
case @cipher
|
103
|
+
when :aes, :aes128 then @priv_key[0, 16]
|
104
|
+
when :aes192 then @priv_key[0, 24]
|
105
|
+
when :aes256 then @priv_key[0, 32]
|
106
|
+
end
|
78
107
|
end
|
79
108
|
end
|
80
109
|
end
|
data/lib/netsnmp/loggable.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module NETSNMP
|
4
4
|
module Loggable
|
5
5
|
DEBUG = ENV.key?("NETSNMP_DEBUG") ? $stderr : nil
|
6
|
-
DEBUG_LEVEL = (
|
6
|
+
DEBUG_LEVEL = ENV.fetch("NETSNMP_DEBUG", 1).to_i
|
7
7
|
|
8
8
|
def initialize_logger(debug: DEBUG, debug_level: DEBUG_LEVEL, **)
|
9
9
|
@debug = debug
|
data/lib/netsnmp/message.rb
CHANGED
@@ -131,9 +131,19 @@ module NETSNMP
|
|
131
131
|
# https://datatracker.ietf.org/doc/html/rfc7860#section-4.2.2 part 3
|
132
132
|
# https://datatracker.ietf.org/doc/html/rfc3414#section-6.3.2 part 3
|
133
133
|
def authnone(auth_protocol)
|
134
|
+
# https://datatracker.ietf.org/doc/html/rfc3414#section-3.1 part 8b
|
135
|
+
return OpenSSL::ASN1::OctetString.new("").with_label(:auth_mask) unless auth_protocol
|
136
|
+
|
134
137
|
# The digest in the msgAuthenticationParameters field is replaced by the 12 zero octets.
|
135
138
|
# 24 octets for sha256
|
136
|
-
number_of_octets = auth_protocol
|
139
|
+
number_of_octets = case auth_protocol
|
140
|
+
when :sha512 then 48
|
141
|
+
when :sha384 then 32
|
142
|
+
when :sha256 then 24
|
143
|
+
when :sha224 then 16
|
144
|
+
else 12
|
145
|
+
end
|
146
|
+
|
137
147
|
OpenSSL::ASN1::OctetString.new("\x00" * number_of_octets).with_label(:auth_mask)
|
138
148
|
end
|
139
149
|
end
|
data/lib/netsnmp/mib/parser.rb
CHANGED
@@ -446,7 +446,7 @@ module NETSNMP::MIB
|
|
446
446
|
spaced("STATUS") >> spaced { status } >>
|
447
447
|
spaced("DESCRIPTION") >> spaced { text } >>
|
448
448
|
spaced { refer_part }.maybe >>
|
449
|
-
colon_colon_part >> curly(object_identifier)
|
449
|
+
colon_colon_part >> curly(object_identifier.as(:value))
|
450
450
|
end
|
451
451
|
|
452
452
|
rule(:units_part) do
|
@@ -557,7 +557,7 @@ module NETSNMP::MIB
|
|
557
557
|
|
558
558
|
rule(:choice_clause) do
|
559
559
|
# Ignoring choice syntax
|
560
|
-
spaced { str("CHOICE").as(:type) } >> curly(match("[
|
560
|
+
spaced { str("CHOICE").as(:type) } >> curly(match("[^}]").repeat)
|
561
561
|
end
|
562
562
|
|
563
563
|
rule(:syntax) do
|
@@ -689,11 +689,11 @@ module NETSNMP::MIB
|
|
689
689
|
rule(:status) { lowercase_identifier }
|
690
690
|
|
691
691
|
rule(:uppercase_identifier) do
|
692
|
-
match("[A-Z]") >> match("[A-Za-z0-9
|
692
|
+
match("[A-Z]") >> match("[A-Za-z0-9-]").repeat
|
693
693
|
end
|
694
694
|
|
695
695
|
rule(:lowercase_identifier) do
|
696
|
-
match("[a-z]") >> match("[A-Za-z0-9
|
696
|
+
match("[a-z]") >> match("[A-Za-z0-9-]").repeat
|
697
697
|
end
|
698
698
|
|
699
699
|
rule(:type_smi_and_sppi) do
|
data/lib/netsnmp/mib.rb
CHANGED
@@ -15,9 +15,9 @@ module NETSNMP
|
|
15
15
|
.flat_map { |dir| [dir, *Dir.glob(File.join(dir, "**", "*")).select(&File.method(:directory?))] }.uniq
|
16
16
|
PARSER = Parser.new
|
17
17
|
@parser_mutex = Mutex.new
|
18
|
+
|
18
19
|
@modules_loaded = []
|
19
20
|
@object_identifiers = {}
|
20
|
-
|
21
21
|
# Translates na identifier, such as "sysDescr", into an OID
|
22
22
|
def oid(identifier)
|
23
23
|
prefix, *suffix = case identifier
|
@@ -98,7 +98,7 @@ module NETSNMP
|
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
101
|
-
TYPES = ["OBJECT IDENTIFIER", "OBJECT-TYPE", "MODULE-IDENTITY"].freeze
|
101
|
+
TYPES = ["OBJECT IDENTIFIER", "OBJECT-TYPE", "MODULE-IDENTITY", "OBJECT-IDENTITY"].freeze
|
102
102
|
|
103
103
|
STATIC_MIB_TO_OID = {
|
104
104
|
"iso" => "1"
|
@@ -174,5 +174,11 @@ module NETSNMP
|
|
174
174
|
load("SNMPv2-MIB")
|
175
175
|
load("IF-MIB")
|
176
176
|
end
|
177
|
+
|
178
|
+
def freeze
|
179
|
+
super
|
180
|
+
@modules_loaded.each(&:freeze).freeze
|
181
|
+
@object_identifiers.each_key(&:freeze).each_value(&:freeze).freeze
|
182
|
+
end
|
177
183
|
end
|
178
184
|
end
|
@@ -130,13 +130,25 @@ module NETSNMP
|
|
130
130
|
|
131
131
|
key = auth_key.dup
|
132
132
|
|
133
|
-
|
134
|
-
|
135
|
-
|
133
|
+
case @auth_protocol
|
134
|
+
when :sha224
|
135
|
+
return OpenSSL::HMAC.digest("SHA224", key, message)[0, 16]
|
136
|
+
when :sha384
|
137
|
+
return OpenSSL::HMAC.digest("SHA384", key, message)[0, 32]
|
138
|
+
when :sha256
|
139
|
+
# The 24 first octets of HMAC are taken as the computed MAC value
|
140
|
+
# SHA256 => https://datatracker.ietf.org/doc/html/rfc7860#section-4.2.2
|
141
|
+
return OpenSSL::HMAC.digest("SHA256", key, message)[0, 24]
|
142
|
+
when :sha512
|
143
|
+
return OpenSSL::HMAC.digest("SHA512", key, message)[0, 48]
|
144
|
+
when :md5
|
145
|
+
# MD5 => https://datatracker.ietf.org/doc/html/rfc3414#section-6.3.2
|
146
|
+
key << ("\x00" * 48)
|
147
|
+
when :sha, :sha1
|
148
|
+
# SHA1 => https://datatracker.ietf.org/doc/html/rfc3414#section-7.3.2
|
149
|
+
key << ("\x00" * 44)
|
150
|
+
end
|
136
151
|
|
137
|
-
# MD5 => https://datatracker.ietf.org/doc/html/rfc3414#section-6.3.2
|
138
|
-
# SHA1 => https://datatracker.ietf.org/doc/html/rfc3414#section-7.3.2
|
139
|
-
key << ("\x00" * (@auth_protocol == :md5 ? 48 : 44))
|
140
152
|
k1 = key.xor(IPAD)
|
141
153
|
k2 = key.xor(OPAD)
|
142
154
|
|
@@ -177,7 +189,20 @@ module NETSNMP
|
|
177
189
|
end
|
178
190
|
|
179
191
|
def priv_key
|
180
|
-
@priv_key ||=
|
192
|
+
@priv_key ||= begin
|
193
|
+
key = localize_key(@priv_pass_key)
|
194
|
+
# AES-192, AES-256 require longer localized keys,
|
195
|
+
# which require adding of subsequent localized_priv_keys based on the previous until the length is satisfied
|
196
|
+
# The only hint to this is available in the python implementation called pysnmp
|
197
|
+
priv_key_size = case @priv_protocol
|
198
|
+
when :aes256 then 32
|
199
|
+
when :aes192 then 24
|
200
|
+
else 16
|
201
|
+
end
|
202
|
+
|
203
|
+
key += localize_key(passkey(key)) while key.size < priv_key_size
|
204
|
+
key
|
205
|
+
end
|
181
206
|
end
|
182
207
|
|
183
208
|
def localize_key(key)
|
@@ -205,15 +230,27 @@ module NETSNMP
|
|
205
230
|
end
|
206
231
|
|
207
232
|
dig = digest.digest
|
208
|
-
|
233
|
+
dig_size = case @auth_protocol
|
234
|
+
when :sha512 then 64
|
235
|
+
when :sha384 then 48
|
236
|
+
when :sha256 then 32
|
237
|
+
when :sha224 then 28
|
238
|
+
when :sha1, :sha then 20
|
239
|
+
else 16
|
240
|
+
end
|
241
|
+
|
242
|
+
dig[0, dig_size]
|
209
243
|
dig || ""
|
210
244
|
end
|
211
245
|
|
212
246
|
def digest
|
213
247
|
@digest ||= case @auth_protocol
|
214
248
|
when :md5 then OpenSSL::Digest.new("MD5")
|
215
|
-
when :sha then OpenSSL::Digest.new("SHA1")
|
249
|
+
when :sha, :sha1 then OpenSSL::Digest.new("SHA1")
|
250
|
+
when :sha224 then OpenSSL::Digest.new("SHA224")
|
216
251
|
when :sha256 then OpenSSL::Digest.new("SHA256")
|
252
|
+
when :sha384 then OpenSSL::Digest.new("SHA384")
|
253
|
+
when :sha512 then OpenSSL::Digest.new("SHA512")
|
217
254
|
else
|
218
255
|
raise Error, "unsupported auth protocol: #{@auth_protocol}"
|
219
256
|
end
|
@@ -222,7 +259,7 @@ module NETSNMP
|
|
222
259
|
def encryption
|
223
260
|
@encryption ||= case @priv_protocol
|
224
261
|
when :des then Encryption::DES.new(priv_key)
|
225
|
-
when :aes then Encryption::AES.new(priv_key)
|
262
|
+
when :aes, :aes192, :aes256 then Encryption::AES.new(priv_key, cipher: @priv_protocol)
|
226
263
|
end
|
227
264
|
end
|
228
265
|
|
data/lib/netsnmp/version.rb
CHANGED
data/sig/encryption/aes.rbs
CHANGED
@@ -4,6 +4,7 @@ module NETSNMP
|
|
4
4
|
|
5
5
|
@priv_key: String
|
6
6
|
@local: Integer
|
7
|
+
@cipher: Symbol
|
7
8
|
|
8
9
|
def encrypt: (String decrypted_data, engine_boots: Integer, engine_time: Integer) -> [String, String]
|
9
10
|
|
@@ -11,7 +12,7 @@ module NETSNMP
|
|
11
12
|
|
12
13
|
private
|
13
14
|
|
14
|
-
def initialize: (String priv_key, ?local: Integer) -> untyped
|
15
|
+
def initialize: (String priv_key, ?local: Integer, ?cipher: Symbol) -> untyped
|
15
16
|
|
16
17
|
def generate_encryption_key: (Integer boots, Integer time) -> [String, String]
|
17
18
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: netsnmp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tiago Cardoso
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-05-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parslet
|
@@ -73,24 +73,15 @@ files:
|
|
73
73
|
- sig/timeticks.rbs
|
74
74
|
- sig/v3_session.rbs
|
75
75
|
- sig/varbind.rbs
|
76
|
-
|
77
|
-
- spec/handlers/celluloid_spec.rb
|
78
|
-
- spec/mib_spec.rb
|
79
|
-
- spec/oid_spec.rb
|
80
|
-
- spec/pdu_spec.rb
|
81
|
-
- spec/security_parameters_spec.rb
|
82
|
-
- spec/session_spec.rb
|
83
|
-
- spec/spec_helper.rb
|
84
|
-
- spec/support/celluloid.rb
|
85
|
-
- spec/support/request_examples.rb
|
86
|
-
- spec/timeticks_spec.rb
|
87
|
-
- spec/v3_session_spec.rb
|
88
|
-
- spec/varbind_spec.rb
|
89
|
-
homepage: ''
|
76
|
+
homepage: https://github.com/HoneyryderChuck/ruby-netsnmp
|
90
77
|
licenses:
|
91
78
|
- Apache-2.0
|
92
79
|
metadata:
|
93
|
-
|
80
|
+
bug_tracker_uri: https://github.com/HoneyryderChuck/ruby-netsnmp/issues
|
81
|
+
changelog_uri: https://github.com/HoneyryderChuck/ruby-netsnmp/blob/master/CHANGELOG.md
|
82
|
+
documentation_uri: https://www.rubydoc.info/github/HoneyryderChuck/ruby-netsnmp
|
83
|
+
source_code_uri: https://github.com/HoneyryderChuck/ruby-netsnmp
|
84
|
+
homepage_uri: https://github.com/HoneyryderChuck/ruby-netsnmp
|
94
85
|
rubygems_mfa_required: 'true'
|
95
86
|
post_install_message:
|
96
87
|
rdoc_options: []
|
@@ -106,23 +97,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
97
|
- - ">="
|
107
98
|
- !ruby/object:Gem::Version
|
108
99
|
version: '0'
|
109
|
-
requirements:
|
110
|
-
|
111
|
-
rubygems_version: 3.2.32
|
100
|
+
requirements: []
|
101
|
+
rubygems_version: 3.3.7
|
112
102
|
signing_key:
|
113
103
|
specification_version: 4
|
114
104
|
summary: SNMP Client library
|
115
|
-
test_files:
|
116
|
-
- spec/client_spec.rb
|
117
|
-
- spec/handlers/celluloid_spec.rb
|
118
|
-
- spec/mib_spec.rb
|
119
|
-
- spec/oid_spec.rb
|
120
|
-
- spec/pdu_spec.rb
|
121
|
-
- spec/security_parameters_spec.rb
|
122
|
-
- spec/session_spec.rb
|
123
|
-
- spec/spec_helper.rb
|
124
|
-
- spec/support/celluloid.rb
|
125
|
-
- spec/support/request_examples.rb
|
126
|
-
- spec/timeticks_spec.rb
|
127
|
-
- spec/v3_session_spec.rb
|
128
|
-
- spec/varbind_spec.rb
|
105
|
+
test_files: []
|
data/spec/client_spec.rb
DELETED
@@ -1,226 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "support/request_examples"
|
4
|
-
|
5
|
-
RSpec.describe NETSNMP::Client do
|
6
|
-
let(:host) { SNMPHOST }
|
7
|
-
|
8
|
-
let(:device_options) do
|
9
|
-
{
|
10
|
-
peername: SNMPHOST,
|
11
|
-
port: SNMPPORT
|
12
|
-
}
|
13
|
-
end
|
14
|
-
describe "v1" do
|
15
|
-
it_behaves_like "an snmp client" do
|
16
|
-
let(:protocol_options) do
|
17
|
-
{
|
18
|
-
version: "1",
|
19
|
-
community: "public"
|
20
|
-
}
|
21
|
-
end
|
22
|
-
let(:get_oid) { "1.3.6.1.2.1.1.5.0" }
|
23
|
-
let(:next_oid) { "1.3.6.1.2.1.1.6.0" }
|
24
|
-
let(:walk_oid) { "1.3.6.1.2.1.1" }
|
25
|
-
let(:set_oid) { "sysUpTime.0" } # sysUpTimeInstance
|
26
|
-
let(:get_result) { "zeus.snmplabs.com (you can change this!)" }
|
27
|
-
let(:next_result) { "San Francisco, California, United States" }
|
28
|
-
let(:walk_result) do
|
29
|
-
{
|
30
|
-
"1.3.6.1.2.1.1.1.0" => "Linux zeus 4.8.6.5-smp #2 SMP Sun Nov 13 14:58:11 CDT 2016 i686",
|
31
|
-
"1.3.6.1.2.1.1.2.0" => "1.3.6.1.4.1.8072.3.2.10",
|
32
|
-
"1.3.6.1.2.1.1.3.0" => /Timeticks: \(\d+\) \d+ days, \d+:\d+:\d+\.\d+/,
|
33
|
-
"1.3.6.1.2.1.1.4.0" => "SNMP Laboratories, info@snmplabs.com",
|
34
|
-
"1.3.6.1.2.1.1.5.0" => "zeus.snmplabs.com (you can change this!)",
|
35
|
-
"1.3.6.1.2.1.1.6.0" => "San Francisco, California, United States",
|
36
|
-
"1.3.6.1.2.1.1.7.0" => "72",
|
37
|
-
"1.3.6.1.2.1.1.8.0" => /Timeticks: \(\d+\) \d+ days, \d+:\d+:\d+\.\d+/
|
38
|
-
}
|
39
|
-
end
|
40
|
-
let(:set_oid_result) { 43 }
|
41
|
-
end
|
42
|
-
end
|
43
|
-
describe "v2" do
|
44
|
-
it_behaves_like "an snmp client" do
|
45
|
-
let(:protocol_options) do
|
46
|
-
{
|
47
|
-
version: "2c",
|
48
|
-
community: "public"
|
49
|
-
}
|
50
|
-
end
|
51
|
-
let(:get_oid) { "sysName.0" }
|
52
|
-
let(:next_oid) { "1.3.6.1.2.1.1.6.0" }
|
53
|
-
let(:walk_oid) { "system" }
|
54
|
-
let(:set_oid) { "sysUpTime.0" }
|
55
|
-
let(:get_result) { "zeus.snmplabs.com (you can change this!)" }
|
56
|
-
let(:next_result) { "San Francisco, California, United States" }
|
57
|
-
let(:walk_result) do
|
58
|
-
{
|
59
|
-
"1.3.6.1.2.1.1.1.0" => "Linux zeus 4.8.6.5-smp #2 SMP Sun Nov 13 14:58:11 CDT 2016 i686",
|
60
|
-
"1.3.6.1.2.1.1.2.0" => "1.3.6.1.4.1.8072.3.2.10",
|
61
|
-
"1.3.6.1.2.1.1.3.0" => /Timeticks: \(\d+\) \d+ days, \d+:\d+:\d+\.\d+/,
|
62
|
-
"1.3.6.1.2.1.1.4.0" => "SNMP Laboratories, info@snmplabs.com",
|
63
|
-
"1.3.6.1.2.1.1.5.0" => "zeus.snmplabs.com (you can change this!)",
|
64
|
-
"1.3.6.1.2.1.1.6.0" => "San Francisco, California, United States",
|
65
|
-
"1.3.6.1.2.1.1.7.0" => "72",
|
66
|
-
"1.3.6.1.2.1.1.8.0" => /Timeticks: \(\d+\) \d+ days, \d+:\d+:\d+\.\d+/
|
67
|
-
}
|
68
|
-
end
|
69
|
-
let(:set_oid_result) { 43 }
|
70
|
-
|
71
|
-
context "when the returned value is a hex-string" do
|
72
|
-
let(:protocol_options) do
|
73
|
-
{
|
74
|
-
version: "2c",
|
75
|
-
community: "foreignformats/winxp1"
|
76
|
-
}
|
77
|
-
end
|
78
|
-
let(:hex_get_oid) { "1.3.6.1.2.1.25.3.7.1.3.10.1" }
|
79
|
-
let(:hex_get_result) { "\x01\x00\x00\x00" }
|
80
|
-
let(:hex_get_output) { "01 00 00 00" }
|
81
|
-
let(:value) { subject.get(oid: hex_get_oid) }
|
82
|
-
|
83
|
-
it "returns the string, which outputs the hex-representation" do
|
84
|
-
expect(value).to eq(hex_get_result)
|
85
|
-
expect(value.inspect).to include(hex_get_output)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
describe "v3" do
|
92
|
-
let(:extra_options) { {} }
|
93
|
-
let(:version_options) do
|
94
|
-
{
|
95
|
-
version: "3",
|
96
|
-
context: "a172334d7d97871b72241397f713fa12"
|
97
|
-
}
|
98
|
-
end
|
99
|
-
let(:get_oid) { "sysName.0" }
|
100
|
-
let(:next_oid) { "1.3.6.1.2.1.1.6.0" }
|
101
|
-
let(:set_oid) { "sysUpTime.0" } # sysUpTimeInstance
|
102
|
-
let(:walk_oid) { "1.3.6.1.2.1.1.9.1.3" }
|
103
|
-
let(:get_result) { "tt" }
|
104
|
-
let(:next_result) { "KK12 (edit /etc/snmp/snmpd.conf)" }
|
105
|
-
let(:walk_result) do
|
106
|
-
{
|
107
|
-
"1.3.6.1.2.1.1.9.1.3.1" => "The SNMP Management Architecture MIB.",
|
108
|
-
"1.3.6.1.2.1.1.9.1.3.2" => "The MIB for Message Processing and Dispatching.",
|
109
|
-
"1.3.6.1.2.1.1.9.1.3.3" => "The management information definitions for the SNMP User-based Security Model.",
|
110
|
-
"1.3.6.1.2.1.1.9.1.3.4" => "The MIB module for SNMPv2 entities",
|
111
|
-
"1.3.6.1.2.1.1.9.1.3.5" => "The MIB module for managing TCP implementations",
|
112
|
-
"1.3.6.1.2.1.1.9.1.3.6" => "The MIB module for managing IP and ICMP implementations",
|
113
|
-
"1.3.6.1.2.1.1.9.1.3.7" => "The MIB module for managing UDP implementations",
|
114
|
-
"1.3.6.1.2.1.1.9.1.3.8" => "View-based Access Control Model for SNMP."
|
115
|
-
}
|
116
|
-
end
|
117
|
-
let(:set_oid_result) { 43 }
|
118
|
-
context "with a no auth no priv policy" do
|
119
|
-
let(:user_options) { { username: "unsafe", security_level: :noauth } }
|
120
|
-
it_behaves_like "an snmp client" do
|
121
|
-
let(:protocol_options) { version_options.merge(user_options).merge(extra_options) }
|
122
|
-
# why is this here? that variation/notification community causes the simulagtor to go down
|
123
|
-
# until I find the origin of the issue and patched it with an appropriated community, this
|
124
|
-
# is here so that I test the set call at least once, although I'm sure it'll work always
|
125
|
-
# for v3
|
126
|
-
describe "#set" do
|
127
|
-
let(:extra_options) { { context: "0886e1397d572377c17c15036a1e6c66" } }
|
128
|
-
it "updates the value of the oid" do
|
129
|
-
prev_value = subject.get(oid: set_oid)
|
130
|
-
expect(prev_value).to be_a(Integer)
|
131
|
-
|
132
|
-
# without type
|
133
|
-
subject.set(oid: set_oid, value: set_oid_result)
|
134
|
-
expect(subject.get(oid: set_oid)).to eq(set_oid_result)
|
135
|
-
|
136
|
-
subject.set(oid: set_oid, value: prev_value)
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
context "with an only auth policy" do
|
142
|
-
context "speaking md5" do
|
143
|
-
let(:user_options) do
|
144
|
-
{ username: "authmd5", security_level: :auth_no_priv,
|
145
|
-
auth_password: "maplesyrup", auth_protocol: :md5 }
|
146
|
-
end
|
147
|
-
it_behaves_like "an snmp client" do
|
148
|
-
let(:protocol_options) { version_options.merge(user_options).merge(extra_options) }
|
149
|
-
end
|
150
|
-
end
|
151
|
-
context "speaking sha" do
|
152
|
-
let(:user_options) do
|
153
|
-
{ username: "authsha", security_level: :auth_no_priv,
|
154
|
-
auth_password: "maplesyrup", auth_protocol: :sha }
|
155
|
-
end
|
156
|
-
it_behaves_like "an snmp client" do
|
157
|
-
let(:protocol_options) { version_options.merge(user_options).merge(extra_options) }
|
158
|
-
end
|
159
|
-
end
|
160
|
-
context "speaking sha256" do
|
161
|
-
let(:user_options) do
|
162
|
-
{ username: "authsha256", security_level: :auth_no_priv,
|
163
|
-
auth_password: "maplesyrup", auth_protocol: :sha256 }
|
164
|
-
end
|
165
|
-
it_behaves_like "an snmp client" do
|
166
|
-
let(:protocol_options) { version_options.merge(user_options).merge(extra_options) }
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
170
|
-
context "with an auth priv policy" do
|
171
|
-
context "auth in md5, encrypting in des" do
|
172
|
-
let(:user_options) do
|
173
|
-
{ username: "authprivmd5des", auth_password: "maplesyrup",
|
174
|
-
auth_protocol: :md5, priv_password: "maplesyrup",
|
175
|
-
priv_protocol: :des }
|
176
|
-
end
|
177
|
-
it_behaves_like "an snmp client" do
|
178
|
-
let(:protocol_options) { version_options.merge(user_options).merge(extra_options) }
|
179
|
-
end
|
180
|
-
end
|
181
|
-
context "auth in sha, encrypting in des" do
|
182
|
-
let(:user_options) do
|
183
|
-
{ username: "authprivshades", auth_password: "maplesyrup",
|
184
|
-
auth_protocol: :sha, priv_password: "maplesyrup",
|
185
|
-
priv_protocol: :des }
|
186
|
-
end
|
187
|
-
it_behaves_like "an snmp client" do
|
188
|
-
let(:protocol_options) { version_options.merge(user_options).merge(extra_options) }
|
189
|
-
|
190
|
-
context "with wrong auth password and wrong encrypting password" do
|
191
|
-
let(:user_options) do
|
192
|
-
{ username: "authprivmd5des", auth_password: "wrongpassword",
|
193
|
-
auth_protocol: :md5, priv_password: "maplesyrup",
|
194
|
-
priv_protocol: :des }
|
195
|
-
end
|
196
|
-
let(:protocol_options) { version_options.merge(user_options).merge(extra_options) }
|
197
|
-
it "raises authentication error" do
|
198
|
-
expect { subject.get(oid: get_oid) }.to raise_error(NETSNMP::Error, "Authentication failure (incorrect password, community or key)")
|
199
|
-
end
|
200
|
-
end
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
|
-
context "auth in md5, encrypting in aes" do
|
205
|
-
let(:user_options) do
|
206
|
-
{ username: "authprivmd5aes", auth_password: "maplesyrup",
|
207
|
-
auth_protocol: :md5, priv_password: "maplesyrup",
|
208
|
-
priv_protocol: :aes }
|
209
|
-
end
|
210
|
-
it_behaves_like "an snmp client" do
|
211
|
-
let(:protocol_options) { version_options.merge(user_options).merge(extra_options) }
|
212
|
-
end
|
213
|
-
end
|
214
|
-
context "auth in sha, encrypting in aes" do
|
215
|
-
let(:user_options) do
|
216
|
-
{ username: "authprivshaaes", auth_password: "maplesyrup",
|
217
|
-
auth_protocol: :sha, priv_password: "maplesyrup",
|
218
|
-
priv_protocol: :aes }
|
219
|
-
end
|
220
|
-
it_behaves_like "an snmp client" do
|
221
|
-
let(:protocol_options) { version_options.merge(user_options).merge(extra_options) }
|
222
|
-
end
|
223
|
-
end
|
224
|
-
end
|
225
|
-
end
|
226
|
-
end
|
@@ -1,47 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "celluloid/io"
|
4
|
-
require_relative "../support/request_examples"
|
5
|
-
require_relative "../support/celluloid"
|
6
|
-
|
7
|
-
RSpec.describe "with cellulloid", type: :celluloid do
|
8
|
-
include CelluloidHelpers
|
9
|
-
let(:user_options) do
|
10
|
-
{ username: "authprivmd5des", auth_password: "maplesyrup",
|
11
|
-
auth_protocol: :md5, priv_password: "maplesyrup",
|
12
|
-
priv_protocol: :des }
|
13
|
-
end
|
14
|
-
|
15
|
-
let(:get_oid) { "1.3.6.1.2.1.1.5.0" }
|
16
|
-
let(:next_oid) { "1.3.6.1.2.1.1.6.0" }
|
17
|
-
let(:set_oid) { "1.3.6.1.2.1.1.3.0" } # sysUpTimeInstance
|
18
|
-
let(:walk_oid) { "1.3.6.1.2.1.1.9.1.3" }
|
19
|
-
let(:get_result) { "tt" }
|
20
|
-
let(:next_result) { "KK12 (edit /etc/snmp/snmpd.conf)" }
|
21
|
-
let(:walk_result) do
|
22
|
-
{
|
23
|
-
"1.3.6.1.2.1.1.9.1.3.1" => "The SNMP Management Architecture MIB.",
|
24
|
-
"1.3.6.1.2.1.1.9.1.3.2" => "The MIB for Message Processing and Dispatching.",
|
25
|
-
"1.3.6.1.2.1.1.9.1.3.3" => "The management information definitions for the SNMP User-based Security Model.",
|
26
|
-
"1.3.6.1.2.1.1.9.1.3.4" => "The MIB module for SNMPv2 entities",
|
27
|
-
"1.3.6.1.2.1.1.9.1.3.5" => "The MIB module for managing TCP implementations",
|
28
|
-
"1.3.6.1.2.1.1.9.1.3.6" => "The MIB module for managing IP and ICMP implementations",
|
29
|
-
"1.3.6.1.2.1.1.9.1.3.7" => "The MIB module for managing UDP implementations",
|
30
|
-
"1.3.6.1.2.1.1.9.1.3.8" => "View-based Access Control Model for SNMP."
|
31
|
-
}
|
32
|
-
end
|
33
|
-
|
34
|
-
before(:all) { Celluloid.boot }
|
35
|
-
around(:each) do |example|
|
36
|
-
within_io_actor { example.run }
|
37
|
-
end
|
38
|
-
let(:proxy) { CelluloidHelpers::Proxy.new(SNMPHOST, SNMPPORT) }
|
39
|
-
after(:each) { proxy.close }
|
40
|
-
|
41
|
-
it_behaves_like "an snmp client" do
|
42
|
-
subject { NETSNMP::Client.new(**options) }
|
43
|
-
let(:device_options) { { proxy: proxy } }
|
44
|
-
let(:protocol_options) { user_options }
|
45
|
-
let(:extra_options) { { version: 3, context: "a172334d7d97871b72241397f713fa12" } }
|
46
|
-
end
|
47
|
-
end
|
data/spec/mib_spec.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe NETSNMP::MIB do
|
4
|
-
describe ".oid" do
|
5
|
-
it { expect(described_class.oid("1.2.3.4")).to eq("1.2.3.4") }
|
6
|
-
it { expect(described_class.oid("ifTable")).to eq("1.3.6.1.2.1.2.2") }
|
7
|
-
it { expect(described_class.oid("sysDescr.0")).to eq("1.3.6.1.2.1.1.1.0") }
|
8
|
-
it { expect(described_class.oid("ifTable.1.23")).to eq("1.3.6.1.2.1.2.2.1.23") }
|
9
|
-
it { expect(described_class.oid("IF-MIB::ifTable.1.23")).to eq("1.3.6.1.2.1.2.2.1.23") }
|
10
|
-
it { expect(described_class.oid("IFMIB::ifTable.1.23")).to be_nil }
|
11
|
-
it { expect(described_class.oid("IF-MIB::")).to be_nil }
|
12
|
-
end
|
13
|
-
end
|
data/spec/oid_spec.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe NETSNMP::OID do
|
4
|
-
# let(:code) { "SNMPv2-MIB::sysDescr.0" }
|
5
|
-
let(:code) { "1.3.6.1.2.1.1.1.0" }
|
6
|
-
subject { described_class.build(code) }
|
7
|
-
|
8
|
-
describe ".build" do
|
9
|
-
it { expect(described_class.build([1, 3, 6, 1, 2, 1, 1, 1, 0]).to_s).to eq(code) }
|
10
|
-
it { expect(described_class.build(".#{code}").to_s).to eq(code) }
|
11
|
-
it { expect { described_class.build("blablabla") }.to raise_error(NETSNMP::Error) }
|
12
|
-
end
|
13
|
-
|
14
|
-
describe ".to_asn" do
|
15
|
-
it { expect(described_class.to_asn(subject).to_der).to eq("\x06\b+\x06\x01\x02\x01\x01\x01\x00".b) }
|
16
|
-
end
|
17
|
-
end
|
data/spec/pdu_spec.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe NETSNMP::PDU do
|
4
|
-
let(:get_request_oid) { ".1.3.6.1.2.1.1.1.0" }
|
5
|
-
let(:encoded_get_pdu) do
|
6
|
-
"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"
|
7
|
-
end
|
8
|
-
let(:encoded_response_pdu) do
|
9
|
-
"0+\002\001\000\004\006public\242\036\002\002'\017\002\001\000\002\001\0000\0220\020\006\b+\006\001\002\001\001\001\000\004\004test"
|
10
|
-
end
|
11
|
-
|
12
|
-
describe "#to_der" do
|
13
|
-
let(:pdu_get) do
|
14
|
-
described_class.build(:get, version: 0,
|
15
|
-
community: "public",
|
16
|
-
request_id: 16170)
|
17
|
-
end
|
18
|
-
|
19
|
-
context "v1" do
|
20
|
-
before { pdu_get.add_varbind(oid: get_request_oid) }
|
21
|
-
it { expect(pdu_get.to_der).to eq(encoded_get_pdu.b) }
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
describe "#decoding pdus" do
|
26
|
-
describe "v1" do
|
27
|
-
let(:pdu_response) { described_class.decode(encoded_response_pdu) }
|
28
|
-
it { expect(pdu_response.version).to be(0) }
|
29
|
-
it { expect(pdu_response.community).to eq("public") }
|
30
|
-
it { expect(pdu_response.request_id).to be(9999) }
|
31
|
-
|
32
|
-
it { expect(pdu_response.varbinds.length).to be(1) }
|
33
|
-
it { expect(pdu_response.varbinds[0].oid).to eq("1.3.6.1.2.1.1.1.0") }
|
34
|
-
it { expect(pdu_response.varbinds[0].value).to eq("test") }
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,90 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# FROM https://tools.ietf.org/html/rfc3414#appendix-A.2.1
|
4
|
-
RSpec.describe NETSNMP::SecurityParameters do
|
5
|
-
let(:engine_id) { "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02".b }
|
6
|
-
let(:password) { "maplesyrup" }
|
7
|
-
describe "#passkey" do
|
8
|
-
context "md5" do
|
9
|
-
subject { described_class.new(security_level: :auth_no_priv, auth_protocol: :md5, username: "username", engine_id: engine_id, auth_password: "maplesyrup") }
|
10
|
-
it { expect(subject.send(:passkey, password)).to eq("\x9f\xaf\x32\x83\x88\x4e\x92\x83\x4e\xbc\x98\x47\xd8\xed\xd9\x63".b) }
|
11
|
-
end
|
12
|
-
context "sha" do
|
13
|
-
subject { described_class.new(security_level: :auth_priv, auth_protocol: :sha, username: "username", engine_id: engine_id, auth_password: "maplesyrup", priv_password: "maplesyrup") }
|
14
|
-
it { expect(subject.send(:passkey, password).b).to eq("\x9f\xb5\xcc\x03\x81\x49\x7b\x37\x93\x52\x89\x39\xff\x78\x8d\x5d\x79\x14\x52\x11".b) }
|
15
|
-
end
|
16
|
-
context "sha256" do
|
17
|
-
subject do
|
18
|
-
described_class.new(security_level: :auth_priv, auth_protocol: :sha256, username: "username", engine_id: engine_id, auth_password: "maplesyrup", priv_password: "maplesyrup")
|
19
|
-
end
|
20
|
-
|
21
|
-
it { expect(subject.send(:passkey, password).b).to eq("\xABQ\x01M\x1E\a\x7F`\x17\xDF+\x12\xBE\xE5\xF5\xAAr\x991w\xE9\xBBV\x9CM\xFFZL\xA0\xB4\xAF\xAC".b) }
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
describe "keys" do
|
26
|
-
let(:md5_sec) do
|
27
|
-
described_class.new(security_level: :auth_priv,
|
28
|
-
auth_protocol: :md5,
|
29
|
-
priv_protocol: :des,
|
30
|
-
username: "username",
|
31
|
-
auth_password: password,
|
32
|
-
priv_password: password,
|
33
|
-
engine_id: engine_id)
|
34
|
-
end
|
35
|
-
let(:sha_sec) do
|
36
|
-
described_class.new(security_level: :auth_priv,
|
37
|
-
auth_protocol: :sha,
|
38
|
-
priv_protocol: :des,
|
39
|
-
username: "username",
|
40
|
-
auth_password: password,
|
41
|
-
priv_password: password,
|
42
|
-
engine_id: engine_id)
|
43
|
-
end
|
44
|
-
let(:sha256_sec) do
|
45
|
-
described_class.new(security_level: :auth_priv,
|
46
|
-
auth_protocol: :sha256,
|
47
|
-
priv_protocol: :des,
|
48
|
-
username: "username",
|
49
|
-
auth_password: password,
|
50
|
-
priv_password: password,
|
51
|
-
engine_id: engine_id)
|
52
|
-
end
|
53
|
-
it do
|
54
|
-
expect(md5_sec.send(:auth_key)).to eq("\x52\x6f\x5e\xed\x9f\xcc\xe2\x6f\x89\x64\xc2\x93\x07\x87\xd8\x2b".b)
|
55
|
-
expect(md5_sec.send(:priv_key)).to eq("\x52\x6f\x5e\xed\x9f\xcc\xe2\x6f\x89\x64\xc2\x93\x07\x87\xd8\x2b".b)
|
56
|
-
expect(sha_sec.send(:auth_key)).to eq("\x66\x95\xfe\xbc\x92\x88\xe3\x62\x82\x23\x5f\xc7\x15\x1f\x12\x84\x97\xb3\x8f\x3f".b)
|
57
|
-
expect(sha_sec.send(:priv_key)).to eq("\x66\x95\xfe\xbc\x92\x88\xe3\x62\x82\x23\x5f\xc7\x15\x1f\x12\x84\x97\xb3\x8f\x3f".b)
|
58
|
-
expect(sha256_sec.send(:auth_key)).to eq("\x89\x82\xE0\xE5I\xE8f\xDB6\x1Akb]\x84\xCC\xCC\x11\x16-E>\xE8\xCE:dE\xC2\xD6wo\x0F\x8B".b)
|
59
|
-
expect(sha256_sec.send(:priv_key)).to eq("\x89\x82\xE0\xE5I\xE8f\xDB6\x1Akb]\x84\xCC\xCC\x11\x16-E>\xE8\xCE:dE\xC2\xD6wo\x0F\x8B".b)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
context "#must_revalidate?" do
|
64
|
-
let(:security_options) do
|
65
|
-
{ username: "authprivmd5des", auth_password: "maplesyrup",
|
66
|
-
auth_protocol: :md5, priv_password: "maplesyrup",
|
67
|
-
priv_protocol: :des, security_level: :auth_priv }
|
68
|
-
end
|
69
|
-
subject { described_class.new(**security_options) }
|
70
|
-
context "for v3" do
|
71
|
-
context "when initialized" do
|
72
|
-
it { expect(subject.must_revalidate?).to be_truthy }
|
73
|
-
end
|
74
|
-
context "when given a new engine id" do
|
75
|
-
before { subject.engine_id = "NEWENGINE" }
|
76
|
-
it { expect(subject.must_revalidate?).to be_falsy }
|
77
|
-
context "when limit surpasses" do
|
78
|
-
before do
|
79
|
-
subject.instance_variable_set(:@timeliness, Process.clock_gettime(Process::CLOCK_MONOTONIC, :second) - 150)
|
80
|
-
end
|
81
|
-
it { expect(subject.must_revalidate?).to be_truthy }
|
82
|
-
context "when given a new engine id" do
|
83
|
-
before { subject.engine_id = "UPDATEDENGINE" }
|
84
|
-
it { expect(subject.must_revalidate?).to be_falsy }
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
data/spec/session_spec.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe NETSNMP::Session do
|
4
|
-
let(:host) { SNMPHOST }
|
5
|
-
let(:options) do
|
6
|
-
{
|
7
|
-
version: "2c",
|
8
|
-
context: "public",
|
9
|
-
port: SNMPPORT
|
10
|
-
}
|
11
|
-
end
|
12
|
-
subject { described_class.new(host: host, **options) }
|
13
|
-
after { subject.close }
|
14
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
GC.auto_compact = true if GC.respond_to?(:auto_compact=)
|
4
|
-
|
5
|
-
if ENV.key?("CI")
|
6
|
-
require "simplecov"
|
7
|
-
SimpleCov.command_name "#{RUBY_ENGINE}-#{RUBY_VERSION}"
|
8
|
-
SimpleCov.coverage_dir "coverage/#{RUBY_ENGINE}-#{RUBY_VERSION}"
|
9
|
-
end
|
10
|
-
|
11
|
-
if defined?(SimpleCov)
|
12
|
-
SimpleCov.start do
|
13
|
-
add_filter ".bundle"
|
14
|
-
add_filter "/spec/"
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
require "bundler/setup"
|
19
|
-
Bundler.require(:default, :test)
|
20
|
-
|
21
|
-
require "netsnmp"
|
22
|
-
|
23
|
-
SNMPPORT = ENV.fetch("SNMP_PORT", 1161).to_i
|
24
|
-
SNMPHOST = ENV.fetch("SNMP_HOST", "localhost")
|
25
|
-
|
26
|
-
# This file was generated by the `rspec --init` command. Conventionally, all
|
27
|
-
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
28
|
-
# The generated `.rspec` file contains `--require spec_helper` which will cause
|
29
|
-
# this file to always be loaded, without a need to explicitly require it in any
|
30
|
-
# files.
|
31
|
-
#
|
32
|
-
# Given that it is always loaded, you are encouraged to keep this file as
|
33
|
-
# light-weight as possible. Requiring heavyweight dependencies from this file
|
34
|
-
# will add to the boot time of your test suite on EVERY test run, even for an
|
35
|
-
# individual file that may not need all of that loaded. Instead, consider making
|
36
|
-
# a separate helper file that requires the additional dependencies and performs
|
37
|
-
# the additional setup, and require it from the spec files that actually need
|
38
|
-
# it.
|
39
|
-
#
|
40
|
-
# The `.rspec` file also contains a few flags that are not defaults but that
|
41
|
-
# users commonly want.
|
42
|
-
#
|
43
|
-
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
44
|
-
RSpec.configure do |config|
|
45
|
-
# rspec-expectations config goes here. You can use an alternate
|
46
|
-
# assertion/expectation library such as wrong or the stdlib/minitest
|
47
|
-
# assertions if you prefer.
|
48
|
-
config.expect_with :rspec do |expectations|
|
49
|
-
# This option will default to `true` in RSpec 4. It makes the `description`
|
50
|
-
# and `failure_message` of custom matchers include text for helper methods
|
51
|
-
# defined using `chain`, e.g.:
|
52
|
-
# be_bigger_than(2).and_smaller_than(4).description
|
53
|
-
# # => "be bigger than 2 and smaller than 4"
|
54
|
-
# ...rather than:
|
55
|
-
# # => "be bigger than 2"
|
56
|
-
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
57
|
-
end
|
58
|
-
|
59
|
-
# rspec-mocks config goes here. You can use an alternate test double
|
60
|
-
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
61
|
-
config.mock_with :rspec do |mocks|
|
62
|
-
# Prevents you from mocking or stubbing a method that does not exist on
|
63
|
-
# a real object. This is generally recommended, and will default to
|
64
|
-
# `true` in RSpec 4.
|
65
|
-
mocks.verify_partial_doubles = true
|
66
|
-
end
|
67
|
-
end
|
data/spec/support/celluloid.rb
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Copied from celluloid-io spec helpers
|
4
|
-
module CelluloidHelpers
|
5
|
-
class WrapperActor
|
6
|
-
include ::Celluloid::IO
|
7
|
-
execute_block_on_receiver :wrap
|
8
|
-
|
9
|
-
def wrap
|
10
|
-
yield
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def with_wrapper_actor
|
15
|
-
WrapperActor.new
|
16
|
-
end
|
17
|
-
|
18
|
-
def within_io_actor(&block)
|
19
|
-
actor = WrapperActor.new
|
20
|
-
actor.wrap(&block)
|
21
|
-
ensure
|
22
|
-
begin
|
23
|
-
actor.terminate if actor.alive?
|
24
|
-
rescue StandardError
|
25
|
-
nil
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
class Proxy
|
30
|
-
MAXPDUSIZE = 0xffff + 1
|
31
|
-
|
32
|
-
def initialize(host, port)
|
33
|
-
@socket = Celluloid::IO::UDPSocket.new
|
34
|
-
@socket.connect(host, port)
|
35
|
-
@timeout = 2
|
36
|
-
end
|
37
|
-
|
38
|
-
def send(payload)
|
39
|
-
@socket.send(payload, 0)
|
40
|
-
recv
|
41
|
-
end
|
42
|
-
|
43
|
-
def recv(bytesize = MAXPDUSIZE)
|
44
|
-
Celluloid.timeout(@timeout) do
|
45
|
-
datagram, = @socket.recvfrom(bytesize)
|
46
|
-
datagram
|
47
|
-
end
|
48
|
-
rescue Celluloid::TaskTimeout
|
49
|
-
raise Timeout::Error, "Timeout after #{@timeout} seconds"
|
50
|
-
end
|
51
|
-
|
52
|
-
def close
|
53
|
-
@socket.close
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
@@ -1,56 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.shared_examples "an snmp client" do
|
4
|
-
let(:device_options) do
|
5
|
-
{
|
6
|
-
host: SNMPHOST,
|
7
|
-
port: SNMPPORT
|
8
|
-
}
|
9
|
-
end
|
10
|
-
let(:protocol_options) { {} }
|
11
|
-
let(:extra_options) { {} }
|
12
|
-
let(:options) { protocol_options.merge(device_options).merge(extra_options) }
|
13
|
-
|
14
|
-
subject { described_class.new(**options) }
|
15
|
-
|
16
|
-
describe "#get" do
|
17
|
-
let(:value) { subject.get(oid: get_oid) }
|
18
|
-
it "fetches the varbinds for a given oid" do
|
19
|
-
expect(value).to eq(get_result)
|
20
|
-
end
|
21
|
-
context "with multiple oids" do
|
22
|
-
let(:value) { subject.get({ oid: get_oid }, oid: next_oid) }
|
23
|
-
it "returns the values for both" do
|
24
|
-
expect(value).to be_a(Array)
|
25
|
-
expect(value).to include(get_result)
|
26
|
-
expect(value).to include(next_result)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
describe "#get_next" do
|
32
|
-
let(:varbind) { subject.get_next(oid: get_oid) }
|
33
|
-
it "fetches the varbinds for the next oid" do
|
34
|
-
oid, value = varbind
|
35
|
-
expect(value).to start_with(next_result)
|
36
|
-
expect(oid).to eq(next_oid)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
describe "#walk" do
|
41
|
-
let(:value) { subject.walk(oid: walk_oid) }
|
42
|
-
it "fetches the varbinds for the next oid" do
|
43
|
-
value.each do |oid, val|
|
44
|
-
match = walk_result[oid]
|
45
|
-
case match
|
46
|
-
when String
|
47
|
-
expect(val.to_s).to eq(match)
|
48
|
-
when Regexp
|
49
|
-
expect(val.to_s).to match(match)
|
50
|
-
else
|
51
|
-
next
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
data/spec/timeticks_spec.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# from https://ask.wireshark.org/questions/14002/how-to-decode-timeticks-hundreds-seconds-to-readable-date-time
|
4
|
-
RSpec.describe NETSNMP::Timetick do
|
5
|
-
subject { described_class.new(1525917187) }
|
6
|
-
|
7
|
-
describe "as an integer" do
|
8
|
-
it { expect((1 + subject).to_i).to be(1525917188) }
|
9
|
-
end
|
10
|
-
|
11
|
-
describe "as an embedded string" do
|
12
|
-
it { expect(subject.to_s).to eq("Timeticks: (1525917187) 176 days, 14:39:31.87") }
|
13
|
-
end
|
14
|
-
end
|
data/spec/v3_session_spec.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe NETSNMP::V3Session do
|
4
|
-
let(:security_options) do
|
5
|
-
{ username: "authprivmd5des", auth_password: "maplesyrup",
|
6
|
-
auth_protocol: :md5, priv_password: "maplesyrup",
|
7
|
-
priv_protocol: :des, security_level: :auth_priv }
|
8
|
-
end
|
9
|
-
it "generates the security parameters handler" do
|
10
|
-
sess = described_class.new(**security_options.merge(host: SNMPHOST, port: SNMPPORT))
|
11
|
-
# not generated yet
|
12
|
-
expect(sess.instance_variable_get(:@security_parameters)).to be_a(NETSNMP::SecurityParameters)
|
13
|
-
end
|
14
|
-
|
15
|
-
it "allows to pass a custom one" do
|
16
|
-
sec_params = NETSNMP::SecurityParameters.new(**security_options)
|
17
|
-
sess = described_class.new(host: SNMPHOST, port: SNMPPORT, security_parameters: sec_params)
|
18
|
-
# not generated yet
|
19
|
-
expect(sess.instance_variable_get(:@security_parameters)).to be(sec_params)
|
20
|
-
end
|
21
|
-
|
22
|
-
it "fails if the pass object doesn't follow the expected api" do
|
23
|
-
expect { described_class.new(host: SNMPHOST, port: SNMPPORT, security_parameters: double) }.to raise_error(NETSNMP::Error)
|
24
|
-
end
|
25
|
-
end
|
data/spec/varbind_spec.rb
DELETED
@@ -1,136 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe NETSNMP::Varbind do
|
4
|
-
using NETSNMP::StringExtensions
|
5
|
-
|
6
|
-
describe "#to_der" do
|
7
|
-
it { expect(described_class.new(".1.3.6.1.2.1.1.1.0").to_der).to eq("0\f\006\b+\006\001\002\001\001\001\000\005\000".b) }
|
8
|
-
|
9
|
-
context "application specific" do
|
10
|
-
it "converts ip addresses" do
|
11
|
-
ipaddr = IPAddr.new("10.11.104.2")
|
12
|
-
varbind = described_class.new(".1.3.6.1.4.1.2011.6.3.1.1.0", value: ipaddr)
|
13
|
-
expect(varbind.to_der).to end_with("@\x04\n\vh\x02".b)
|
14
|
-
asn = varbind.to_asn.value.last
|
15
|
-
expect(varbind.convert_application_asn(asn)).to eq(ipaddr)
|
16
|
-
end
|
17
|
-
it "converts custom timeticks" do
|
18
|
-
timetick = NETSNMP::Timetick.new(1) # yes, one timetick
|
19
|
-
varbind = described_class.new(".1.3.6.1.2.1.1.3.0", value: timetick)
|
20
|
-
expect(varbind.to_der).to end_with("\x04\x00\x00\x00\x01".b) # ends with an octet string rep of 1 timetick
|
21
|
-
asn = varbind.to_asn.value.last
|
22
|
-
expect(varbind.convert_application_asn(asn)).to eq(timetick)
|
23
|
-
end
|
24
|
-
|
25
|
-
context "when passed a type" do
|
26
|
-
it "converts gauge32 without a leading byte" do
|
27
|
-
gauge = 127
|
28
|
-
varbind = described_class.new(".1.3.6.1.2.1.1.3.0", type: :gauge, value: gauge)
|
29
|
-
value_str = varbind.to_der[12..-1]
|
30
|
-
header = value_str[0].unpack1("B8")
|
31
|
-
|
32
|
-
# Class: Primitive Application
|
33
|
-
expect(header[0..1]).to eq("01")
|
34
|
-
expect(header[2]).to eq("0")
|
35
|
-
# Type: Integer
|
36
|
-
expect(header[3..-1].to_i(2)).to eq(2)
|
37
|
-
# Length & Value
|
38
|
-
expect(varbind.to_der).to end_with("\x01\x7F".b) # 2 Bytes
|
39
|
-
|
40
|
-
# Original Value
|
41
|
-
asn = varbind.to_asn.value.last
|
42
|
-
expect(varbind.convert_application_asn(asn)).to eq(gauge)
|
43
|
-
end
|
44
|
-
it "converts gauge32 with a leading byte" do
|
45
|
-
gauge = 128
|
46
|
-
varbind = described_class.new(".1.3.6.1.2.1.1.3.0", type: :gauge, value: gauge)
|
47
|
-
value_str = varbind.to_der[12..-1]
|
48
|
-
header = value_str[0].unpack1("B8")
|
49
|
-
|
50
|
-
# Class: Primitive Application
|
51
|
-
expect(header[0..1]).to eq("01")
|
52
|
-
expect(header[2]).to eq("0")
|
53
|
-
# Type: Integer
|
54
|
-
expect(header[3..-1].to_i(2)).to eq(2)
|
55
|
-
# Length & Value
|
56
|
-
expect(varbind.to_der).to end_with("\x02\x00\x80".b) # 4 Bytes, all FF
|
57
|
-
|
58
|
-
# Original Value
|
59
|
-
asn = varbind.to_asn.value.last
|
60
|
-
expect(varbind.convert_application_asn(asn)).to eq(gauge)
|
61
|
-
end
|
62
|
-
it "converts gauge32" do
|
63
|
-
gauge = 805
|
64
|
-
varbind = described_class.new(".1.3.6.1.2.1.1.3.0", type: :gauge, value: gauge)
|
65
|
-
value_str = varbind.to_der[12..-1]
|
66
|
-
header = value_str[0].unpack1("B8")
|
67
|
-
|
68
|
-
# Class: Primitive Application
|
69
|
-
expect(header[0..1]).to eq("01")
|
70
|
-
expect(header[2]).to eq("0")
|
71
|
-
# Type: Integer
|
72
|
-
expect(header[3..-1].to_i(2)).to eq(2)
|
73
|
-
# Length & Value
|
74
|
-
expect(varbind.to_der).to end_with("\x02\x03%".b)
|
75
|
-
|
76
|
-
# Original Value
|
77
|
-
asn = varbind.to_asn.value.last
|
78
|
-
expect(varbind.convert_application_asn(asn)).to eq(gauge)
|
79
|
-
end
|
80
|
-
it "converts counter32 without a leading byte" do
|
81
|
-
counter = 127
|
82
|
-
varbind = described_class.new(".1.3.6.1.2.1.1.3.0", type: :counter32, value: counter)
|
83
|
-
expect(varbind.to_der).to end_with("\x01\x7F".b)
|
84
|
-
asn = varbind.to_asn.value.last
|
85
|
-
expect(varbind.convert_application_asn(asn)).to eq(counter)
|
86
|
-
end
|
87
|
-
it "converts counter32 with a leading byte" do
|
88
|
-
counter = 128
|
89
|
-
varbind = described_class.new(".1.3.6.1.2.1.1.3.0", type: :counter32, value: counter)
|
90
|
-
expect(varbind.to_der).to end_with("\x02\x00\x80".b)
|
91
|
-
asn = varbind.to_asn.value.last
|
92
|
-
expect(varbind.convert_application_asn(asn)).to eq(counter)
|
93
|
-
end
|
94
|
-
it "converts counter32" do
|
95
|
-
counter = 998932
|
96
|
-
varbind = described_class.new(".1.3.6.1.2.1.1.3.0", type: :counter32, value: counter)
|
97
|
-
expect(varbind.to_der).to end_with("\x0F>\x14".b)
|
98
|
-
asn = varbind.to_asn.value.last
|
99
|
-
expect(varbind.convert_application_asn(asn)).to eq(counter)
|
100
|
-
end
|
101
|
-
it "converts counter64" do
|
102
|
-
counter = 998932
|
103
|
-
varbind = described_class.new(".1.3.6.1.2.1.1.3.0", type: :counter64, value: counter)
|
104
|
-
expect(varbind.to_der).to end_with("\x0F>\x14".b)
|
105
|
-
asn = varbind.to_asn.value.last
|
106
|
-
expect(varbind.convert_application_asn(asn)).to eq(counter)
|
107
|
-
|
108
|
-
counter = 4294967296
|
109
|
-
varbind = described_class.new(".1.3.6.1.2.1.1.3.0", type: :counter64, value: counter)
|
110
|
-
expect(varbind.to_der).to end_with("\x01\x00\x00\x00\x00".b)
|
111
|
-
asn = varbind.to_asn.value.last
|
112
|
-
expect(varbind.convert_application_asn(asn)).to eq(counter)
|
113
|
-
|
114
|
-
counter = 309084502
|
115
|
-
varbind = described_class.new(".1.3.6.1.2.1.1.3.0", type: :counter64, value: counter)
|
116
|
-
expect(varbind.to_der).to include("F\x04".b)
|
117
|
-
asn = varbind.to_asn.value.last
|
118
|
-
expect(varbind.convert_application_asn(asn)).to eq(counter)
|
119
|
-
|
120
|
-
counter = 2_613_579_752_238
|
121
|
-
varbind = described_class.new(".1.3.6.1.2.1.1.3.0", type: :counter64, value: counter)
|
122
|
-
expect(varbind.to_der).to include("F\x06".b)
|
123
|
-
asn = varbind.to_asn.value.last
|
124
|
-
expect(varbind.convert_application_asn(asn)).to eq(counter)
|
125
|
-
end
|
126
|
-
it "converts integer ticks" do
|
127
|
-
timetick = 1
|
128
|
-
varbind = described_class.new(".1.3.6.1.2.1.1.3.0", type: :timetick, value: timetick)
|
129
|
-
expect(varbind.to_der).to end_with("\x04\x00\x00\x00\x01".b)
|
130
|
-
asn = varbind.to_asn.value.last
|
131
|
-
expect(varbind.convert_application_asn(asn)).to eq(timetick)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|