netsnmp 0.6.4 → 0.7.0
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.
- checksums.yaml +4 -4
- data/README.md +8 -29
- data/lib/netsnmp/encryption/aes.rb +35 -6
- data/lib/netsnmp/message.rb +7 -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
|
-
](http://rubygems.org/gems/netsnmp)
|
4
|
+

|
5
|
+
[](https://codeclimate.com/github/HoneyryderChuck/ruby-netsnmp)
|
6
|
+
[](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/message.rb
CHANGED
@@ -136,7 +136,13 @@ module NETSNMP
|
|
136
136
|
|
137
137
|
# The digest in the msgAuthenticationParameters field is replaced by the 12 zero octets.
|
138
138
|
# 24 octets for sha256
|
139
|
-
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
|
140
146
|
|
141
147
|
OpenSSL::ASN1::OctetString.new("\x00" * number_of_octets).with_label(:auth_mask)
|
142
148
|
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
|