netsnmp 0.2.0 → 0.5.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 +45 -6
- data/lib/netsnmp.rb +3 -33
- data/lib/netsnmp/client.rb +2 -2
- data/lib/netsnmp/encryption/aes.rb +1 -3
- data/lib/netsnmp/encryption/des.rb +0 -2
- data/lib/netsnmp/errors.rb +1 -0
- data/lib/netsnmp/extensions.rb +113 -0
- data/lib/netsnmp/loggable.rb +36 -0
- data/lib/netsnmp/message.rb +78 -30
- data/lib/netsnmp/mib.rb +172 -0
- data/lib/netsnmp/mib/parser.rb +750 -0
- data/lib/netsnmp/oid.rb +4 -10
- data/lib/netsnmp/pdu.rb +20 -9
- data/lib/netsnmp/scoped_pdu.rb +8 -2
- data/lib/netsnmp/security_parameters.rb +19 -11
- data/lib/netsnmp/session.rb +13 -4
- data/lib/netsnmp/v3_session.rb +36 -7
- data/lib/netsnmp/varbind.rb +11 -12
- data/lib/netsnmp/version.rb +1 -1
- data/sig/loggable.rbs +16 -0
- data/sig/message.rbs +7 -3
- data/sig/mib.rbs +21 -0
- data/sig/mib/parser.rbs +7 -0
- data/sig/netsnmp.rbs +0 -4
- data/sig/oid.rbs +1 -1
- data/sig/pdu.rbs +1 -1
- data/sig/scoped_pdu.rbs +2 -0
- data/sig/security_parameters.rbs +4 -2
- data/sig/session.rbs +2 -0
- data/spec/client_spec.rb +36 -6
- data/spec/mib_spec.rb +13 -0
- data/spec/spec_helper.rb +2 -1
- data/spec/support/celluloid.rb +11 -7
- data/spec/varbind_spec.rb +5 -3
- metadata +34 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5119f7e6ec751a4d3ba54076e5597b0667de904eda86802e5e3544e40a753172
|
4
|
+
data.tar.gz: 35924abe6afc24d8a41c8b6429a0566644d6bf4674b2b83704ac3f215e217487
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7662ff27d3242840034b7e72f0a85132fa7007ef44ab4e207ac79f13cfdc29c3709884b098bba97d83e0f157618a8ca8c4dab5d8919bc6686aba4452b990213f
|
7
|
+
data.tar.gz: 673c3ce56ca0154f5efc6247204ffebbd9d0966d43ba4371ba2cee98d10f1496e579eef84acf24349109315d35febb7c42716a8b88c432200cee3eeddd63cca3
|
data/README.md
CHANGED
@@ -32,7 +32,7 @@ This gem provides:
|
|
32
32
|
|
33
33
|
* Implementation in ruby of the SNMP Protocol for v3, v2c and v1 (most notable the rfc3414 and 3826).
|
34
34
|
* Client/Manager API with simple interface for get, genext, set and walk.
|
35
|
-
*
|
35
|
+
* Pure Ruby.
|
36
36
|
* Support for concurrency and evented I/O.
|
37
37
|
|
38
38
|
## Why?
|
@@ -54,10 +54,12 @@ All of these issues are resolved here.
|
|
54
54
|
## Features
|
55
55
|
|
56
56
|
* Client Interface, which supports SNMP v3, v2c, and v1
|
57
|
-
* Supports get, getnext, set and walk calls
|
57
|
+
* Supports get, getnext, set and walk calls
|
58
|
+
* MIB support
|
58
59
|
* Proxy IO object support (for eventmachine/celluloid-io)
|
59
60
|
* Ruby >= 2.1 support (modern)
|
60
61
|
* Pure Ruby (no FFI)
|
62
|
+
* Easy PDU debugging
|
61
63
|
|
62
64
|
## Examples
|
63
65
|
|
@@ -73,12 +75,11 @@ manager = NETSNMP::Client.new(host: "localhost", port: 33445, username: "simulat
|
|
73
75
|
context: "a172334d7d97871b72241397f713fa12")
|
74
76
|
|
75
77
|
# SNMP get
|
76
|
-
|
77
|
-
manager.get(oid: "1.3.6.1.2.1.1.0") #=> 'tt'
|
78
|
+
manager.get(oid: "sysName.0") #=> 'tt'
|
78
79
|
|
79
80
|
# SNMP walk
|
80
81
|
# sysORDescr
|
81
|
-
manager.walk(oid: "
|
82
|
+
manager.walk(oid: "sysORDescr").each do |oid_code, value|
|
82
83
|
# do something with them
|
83
84
|
puts "for #{oid_code}: #{value}"
|
84
85
|
end
|
@@ -135,6 +136,29 @@ manager.set("somecounteroid", value: 999999, type: 6)
|
|
135
136
|
```
|
136
137
|
* Fork this library, extend support, write a test and submit a PR (the desired solution ;) )
|
137
138
|
|
139
|
+
## MIB
|
140
|
+
|
141
|
+
`netsnmp` will load the default MIBs from known or advertised (via `MIBDIRS`) directories (provided that they're installed in the system). These will be used for the OID conversion.
|
142
|
+
|
143
|
+
Sometimes you'll need to load more, your own MIBs, in which case, you can use the following API:
|
144
|
+
|
145
|
+
```ruby
|
146
|
+
require "netsnmp"
|
147
|
+
|
148
|
+
NETSNMP::MIB.load("MY-MIB")
|
149
|
+
# or, if it's not in any of the known locations
|
150
|
+
NETSNMP::MIB.load("/path/to/MY-MIB.txt")
|
151
|
+
```
|
152
|
+
|
153
|
+
You can install common SNMP mibs by using your package manager:
|
154
|
+
|
155
|
+
```
|
156
|
+
# using apt-get
|
157
|
+
> apt-get install snmp-mibs-downloader
|
158
|
+
# using apk
|
159
|
+
> apk --update add net-snmp-libs
|
160
|
+
```
|
161
|
+
|
138
162
|
## Concurrency
|
139
163
|
|
140
164
|
In ruby, you are usually adviced not to share IO objects across threads. The same principle applies here to `NETSNMP::Client`: provided you use it within a thread of execution, it should behave safely. So, something like this would be possible:
|
@@ -213,10 +237,26 @@ NETSNMP::Client.new(share_options.merge(proxy: router_proxy, security_parameters
|
|
213
237
|
end
|
214
238
|
```
|
215
239
|
|
240
|
+
## Compatibility
|
241
|
+
|
242
|
+
This library supports and is tested against ruby versions 2.1 or more recent, including ruby 3. It also supports and tests against Truffleruby.
|
243
|
+
|
216
244
|
## OpenSSL
|
217
245
|
|
218
246
|
All encoding/decoding/encryption/decryption/digests are done using `openssl`, which is (still) a part of the standard library. If at some point `openssl` is removed and not specifically distributed, you'll have to install it yourself. Hopefully this will never happen.
|
219
247
|
|
248
|
+
It also uses the `openssl` ASN.1 API to encode/decode BERs, which is known to be strict, and [may not be able to decode PDUs if not compliant with the supported RFC](https://github.com/swisscom/ruby-netsnmp/issues/47).
|
249
|
+
|
250
|
+
## Debugging
|
251
|
+
|
252
|
+
You can either set the `NETSNMP_DEBUG` to the desided debug level (currently, 1 and 2). The logs will be written to stderr.
|
253
|
+
|
254
|
+
You can also set it for a specific client:
|
255
|
+
|
256
|
+
```ruby
|
257
|
+
manager2 = NETSNMP::Client.new(debug: $stderr, debug_level: 2, ....)
|
258
|
+
```
|
259
|
+
|
220
260
|
|
221
261
|
## Tests
|
222
262
|
|
@@ -272,7 +312,6 @@ The job of the CI is:
|
|
272
312
|
|
273
313
|
There are some features which this gem doesn't support. It was built to provide a client (or manager, in SNMP language) implementation only, and the requirements were fulfilled. However, these notable misses will stand-out:
|
274
314
|
|
275
|
-
* No MIB support (you can only work with OIDs)
|
276
315
|
* No server (Agent, in SNMP-ish) implementation.
|
277
316
|
* No getbulk support.
|
278
317
|
|
data/lib/netsnmp.rb
CHANGED
@@ -33,46 +33,16 @@ rescue LoadError
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
module NETSNMP
|
37
|
-
module StringExtensions
|
38
|
-
# If you wonder why this is there: the oauth feature uses a refinement to enhance the
|
39
|
-
# Regexp class locally with #match? , but this is never tested, because ActiveSupport
|
40
|
-
# monkey-patches the same method... Please ActiveSupport, stop being so intrusive!
|
41
|
-
# :nocov:
|
42
|
-
refine(String) do
|
43
|
-
def match?(*args)
|
44
|
-
!match(*args).nil?
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def self.debug=(io)
|
50
|
-
@debug_output = io
|
51
|
-
end
|
52
|
-
|
53
|
-
def self.debug(&blk)
|
54
|
-
@debug_output << blk.call + "\n" if @debug_output
|
55
|
-
end
|
56
|
-
|
57
|
-
unless defined?(Hexdump) # support the hexdump gem
|
58
|
-
module Hexdump
|
59
|
-
def self.dump(data, width: 8)
|
60
|
-
pairs = data.unpack("H*").first.scan(/.{4}/)
|
61
|
-
pairs.each_slice(width).map do |row|
|
62
|
-
row.join(" ")
|
63
|
-
end.join("\n")
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
36
|
require "netsnmp/errors"
|
37
|
+
require "netsnmp/extensions"
|
38
|
+
require "netsnmp/loggable"
|
70
39
|
|
71
40
|
require "netsnmp/timeticks"
|
72
41
|
|
73
42
|
require "netsnmp/oid"
|
74
43
|
require "netsnmp/varbind"
|
75
44
|
require "netsnmp/pdu"
|
45
|
+
require "netsnmp/mib"
|
76
46
|
require "netsnmp/session"
|
77
47
|
|
78
48
|
require "netsnmp/scoped_pdu"
|
data/lib/netsnmp/client.rb
CHANGED
@@ -77,7 +77,7 @@ module NETSNMP
|
|
77
77
|
# @return [Enumerator] the enumerator-collection of the oid-value pairs
|
78
78
|
#
|
79
79
|
def walk(oid:)
|
80
|
-
walkoid = oid
|
80
|
+
walkoid = OID.build(oid)
|
81
81
|
Enumerator.new do |y|
|
82
82
|
code = walkoid
|
83
83
|
first_response_code = nil
|
@@ -153,7 +153,7 @@ module NETSNMP
|
|
153
153
|
retries = @retries
|
154
154
|
begin
|
155
155
|
yield
|
156
|
-
rescue Timeout::Error => e
|
156
|
+
rescue Timeout::Error, IdNotInTimeWindowError => e
|
157
157
|
raise e if retries.zero?
|
158
158
|
retries -= 1
|
159
159
|
retry
|
@@ -22,13 +22,12 @@ module NETSNMP
|
|
22
22
|
end
|
23
23
|
|
24
24
|
encrypted_data = cipher.update(decrypted_data) + cipher.final
|
25
|
-
NETSNMP.debug { "encrypted:\n#{Hexdump.dump(encrypted_data)}" }
|
26
25
|
|
27
26
|
[encrypted_data, salt]
|
28
27
|
end
|
29
28
|
|
30
29
|
def decrypt(encrypted_data, salt:, engine_boots:, engine_time:)
|
31
|
-
raise Error, "invalid priv salt received" unless (salt.length % 8).zero?
|
30
|
+
raise Error, "invalid priv salt received" unless !salt.empty? && (salt.length % 8).zero?
|
32
31
|
|
33
32
|
cipher = OpenSSL::Cipher::AES128.new(:CFB)
|
34
33
|
cipher.padding = 0
|
@@ -39,7 +38,6 @@ module NETSNMP
|
|
39
38
|
cipher.key = aes_key
|
40
39
|
cipher.iv = iv
|
41
40
|
decrypted_data = cipher.update(encrypted_data) + cipher.final
|
42
|
-
NETSNMP.debug { "decrypted:\n#{Hexdump.dump(decrypted_data)}" }
|
43
41
|
|
44
42
|
hlen, bodylen = OpenSSL::ASN1.traverse(decrypted_data) { |_, _, x, y, *| break x, y }
|
45
43
|
decrypted_data.byteslice(0, hlen + bodylen)
|
@@ -24,7 +24,6 @@ module NETSNMP
|
|
24
24
|
end
|
25
25
|
|
26
26
|
encrypted_data = cipher.update(decrypted_data) + cipher.final
|
27
|
-
NETSNMP.debug { "encrypted:\n#{Hexdump.dump(encrypted_data)}" }
|
28
27
|
[encrypted_data, salt]
|
29
28
|
end
|
30
29
|
|
@@ -41,7 +40,6 @@ module NETSNMP
|
|
41
40
|
cipher.key = des_key
|
42
41
|
cipher.iv = iv
|
43
42
|
decrypted_data = cipher.update(encrypted_data) + cipher.final
|
44
|
-
NETSNMP.debug { "decrypted:\n#{Hexdump.dump(decrypted_data)}" }
|
45
43
|
|
46
44
|
hlen, bodylen = OpenSSL::ASN1.traverse(decrypted_data) { |_, _, x, y, *| break x, y }
|
47
45
|
decrypted_data.byteslice(0, hlen + bodylen)
|
data/lib/netsnmp/errors.rb
CHANGED
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NETSNMP
|
4
|
+
module IsNumericExtensions
|
5
|
+
refine String do
|
6
|
+
def integer?
|
7
|
+
each_byte do |byte|
|
8
|
+
return false unless byte >= 48 && byte <= 57
|
9
|
+
end
|
10
|
+
true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module StringExtensions
|
16
|
+
refine(String) do
|
17
|
+
unless String.method_defined?(:match?)
|
18
|
+
def match?(*args)
|
19
|
+
!match(*args).nil?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
unless String.method_defined?(:unpack1)
|
24
|
+
def unpack1(format)
|
25
|
+
unpack(format).first
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module ASNExtensions
|
32
|
+
ASN_COLORS = {
|
33
|
+
OpenSSL::ASN1::Sequence => 34, # blue
|
34
|
+
OpenSSL::ASN1::OctetString => 32, # green
|
35
|
+
OpenSSL::ASN1::Integer => 33, # yellow
|
36
|
+
OpenSSL::ASN1::ObjectId => 35, # magenta
|
37
|
+
OpenSSL::ASN1::ASN1Data => 36 # cyan
|
38
|
+
}.freeze
|
39
|
+
|
40
|
+
# basic types
|
41
|
+
ASN_COLORS.each_key do |klass|
|
42
|
+
refine(klass) do
|
43
|
+
def to_hex
|
44
|
+
"#{colorize_hex} (#{value.to_s.inspect})"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# composite types
|
50
|
+
refine(OpenSSL::ASN1::Sequence) do
|
51
|
+
def to_hex
|
52
|
+
values = value.map(&:to_der).join
|
53
|
+
hex_values = value.map(&:to_hex).map { |s| s.gsub(/(\t+)/) { "\t#{Regexp.last_match(1)}" } }.map { |s| "\n\t#{s}" }.join
|
54
|
+
der = to_der
|
55
|
+
der = der.sub(values, "")
|
56
|
+
|
57
|
+
"#{colorize_hex(der)}#{hex_values}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
refine(OpenSSL::ASN1::ASN1Data) do
|
62
|
+
attr_reader :label
|
63
|
+
|
64
|
+
def with_label(label)
|
65
|
+
@label = label
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_hex
|
70
|
+
case value
|
71
|
+
when Array
|
72
|
+
values = value.map(&:to_der).join
|
73
|
+
hex_values = value.map(&:to_hex)
|
74
|
+
.map { |s| s.gsub(/(\t+)/) { "\t#{Regexp.last_match(1)}" } }
|
75
|
+
.map { |s| "\n\t#{s}" }.join
|
76
|
+
der = to_der
|
77
|
+
der = der.sub(values, "")
|
78
|
+
else
|
79
|
+
der = to_der
|
80
|
+
hex_values = nil
|
81
|
+
end
|
82
|
+
|
83
|
+
"#{colorize_hex(der)}#{hex_values}"
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def colorize_hex(der = to_der)
|
89
|
+
hex = Hexdump.dump(der, separator: " ")
|
90
|
+
lbl = @label || self.class.name.split("::").last
|
91
|
+
"#{lbl}: \e[#{ASN_COLORS[self.class]}m#{hex}\e[0m"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
module Hexdump
|
97
|
+
using StringExtensions
|
98
|
+
|
99
|
+
def self.dump(data, width: 8, in_groups_of: 4, separator: "\n")
|
100
|
+
pairs = data.unpack1("H*").scan(/.{#{in_groups_of}}/)
|
101
|
+
pairs.each_slice(width).map do |row|
|
102
|
+
row.join(" ")
|
103
|
+
end.join(separator)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Like a string, but it prints an hex-string version of itself
|
108
|
+
class HexString < String
|
109
|
+
def inspect
|
110
|
+
Hexdump.dump(self, in_groups_of: 2, separator: " ")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NETSNMP
|
4
|
+
module Loggable
|
5
|
+
DEBUG = ENV.key?("NETSNMP_DEBUG") ? $stderr : nil
|
6
|
+
DEBUG_LEVEL = (ENV["NETSNMP_DEBUG"] || 1).to_i
|
7
|
+
|
8
|
+
def initialize(debug: DEBUG, debug_level: DEBUG_LEVEL, **opts)
|
9
|
+
super(**opts)
|
10
|
+
@debug = debug
|
11
|
+
@debug_level = debug_level
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
COLORS = {
|
17
|
+
black: 30,
|
18
|
+
red: 31,
|
19
|
+
green: 32,
|
20
|
+
yellow: 33,
|
21
|
+
blue: 34,
|
22
|
+
magenta: 35,
|
23
|
+
cyan: 36,
|
24
|
+
white: 37
|
25
|
+
}.freeze
|
26
|
+
|
27
|
+
def log(level: @debug_level)
|
28
|
+
return unless @debug
|
29
|
+
return unless @debug_level >= level
|
30
|
+
|
31
|
+
debug_stream = @debug
|
32
|
+
|
33
|
+
debug_stream << (+"\n" << yield << "\n")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/netsnmp/message.rb
CHANGED
@@ -2,41 +2,76 @@
|
|
2
2
|
|
3
3
|
module NETSNMP
|
4
4
|
# Factory for the SNMP v3 Message format
|
5
|
-
|
6
|
-
|
5
|
+
class Message
|
6
|
+
using ASNExtensions
|
7
7
|
|
8
|
-
|
8
|
+
prepend Loggable
|
9
|
+
|
10
|
+
AUTHNONE = OpenSSL::ASN1::OctetString.new("\x00" * 12).with_label(:auth_mask)
|
9
11
|
PRIVNONE = OpenSSL::ASN1::OctetString.new("")
|
10
|
-
MSG_MAX_SIZE = OpenSSL::ASN1::Integer.new(65507)
|
11
|
-
MSG_SECURITY_MODEL = OpenSSL::ASN1::Integer.new(3) # usmSecurityModel
|
12
|
-
MSG_VERSION = OpenSSL::ASN1::Integer.new(3)
|
12
|
+
MSG_MAX_SIZE = OpenSSL::ASN1::Integer.new(65507).with_label(:max_message_size)
|
13
|
+
MSG_SECURITY_MODEL = OpenSSL::ASN1::Integer.new(3).with_label(:security_model) # usmSecurityModel
|
14
|
+
MSG_VERSION = OpenSSL::ASN1::Integer.new(3).with_label(:message_version)
|
13
15
|
MSG_REPORTABLE = 4
|
14
16
|
|
17
|
+
def initialize(**); end
|
18
|
+
|
19
|
+
def verify(stream, auth_param, security_level, security_parameters:)
|
20
|
+
security_parameters.verify(stream.sub(auth_param, AUTHNONE.value), auth_param, security_level: security_level)
|
21
|
+
end
|
22
|
+
|
15
23
|
# @param [String] payload of an snmp v3 message which can be decoded
|
16
24
|
# @param [NETSMP::SecurityParameters, #decode] security_parameters knowns how to decode the stream
|
17
25
|
#
|
18
26
|
# @return [NETSNMP::ScopedPDU] the decoded PDU
|
19
27
|
#
|
20
28
|
def decode(stream, security_parameters:)
|
21
|
-
|
22
|
-
|
29
|
+
log { "received encoded V3 message" }
|
30
|
+
log { Hexdump.dump(stream) }
|
31
|
+
asn_tree = OpenSSL::ASN1.decode(stream).with_label(:v3_message)
|
32
|
+
|
33
|
+
version, headers, sec_params, pdu_payload = asn_tree.value
|
34
|
+
version.with_label(:message_version)
|
35
|
+
headers.with_label(:headers)
|
36
|
+
sec_params.with_label(:security_params)
|
37
|
+
pdu_payload.with_label(:pdu)
|
38
|
+
|
39
|
+
_, _, message_flags, = headers.value
|
23
40
|
|
24
|
-
|
41
|
+
# get last byte
|
42
|
+
# discard the left-outermost bits and keep the remaining two
|
43
|
+
security_level = message_flags.with_label(:message_flags).value.unpack("C*").last & 3
|
25
44
|
|
26
|
-
|
45
|
+
sec_params_asn = OpenSSL::ASN1.decode(sec_params.value).with_label(:security_params)
|
27
46
|
|
28
|
-
|
29
|
-
|
47
|
+
engine_id, engine_boots, engine_time, username, auth_param, priv_param = sec_params_asn.value
|
48
|
+
engine_id.with_label(:engine_id)
|
49
|
+
engine_boots.with_label(:engine_boots)
|
50
|
+
engine_time.with_label(:engine_time)
|
51
|
+
username.with_label(:username)
|
52
|
+
auth_param.with_label(:auth_param)
|
53
|
+
priv_param.with_label(:priv_param)
|
30
54
|
|
31
|
-
|
32
|
-
|
55
|
+
log(level: 2) { asn_tree.to_hex }
|
56
|
+
log(level: 2) { sec_params_asn.to_hex }
|
33
57
|
|
34
|
-
|
58
|
+
auth_param = auth_param.value
|
59
|
+
|
60
|
+
engine_boots = engine_boots.value.to_i
|
61
|
+
engine_time = engine_time.value.to_i
|
62
|
+
|
63
|
+
encoded_pdu = security_parameters.decode(pdu_payload, salt: priv_param.value,
|
35
64
|
engine_boots: engine_boots,
|
36
|
-
engine_time: engine_time
|
65
|
+
engine_time: engine_time,
|
66
|
+
security_level: security_level)
|
37
67
|
|
68
|
+
log { "received response PDU" }
|
38
69
|
pdu = ScopedPDU.decode(encoded_pdu)
|
39
|
-
|
70
|
+
pdu.auth_param = auth_param
|
71
|
+
pdu.security_level = security_level
|
72
|
+
|
73
|
+
log(level: 2) { pdu.to_hex }
|
74
|
+
[pdu, engine_id.value, engine_boots, engine_time]
|
40
75
|
end
|
41
76
|
|
42
77
|
# @param [NETSNMP::ScopedPDU] the PDU to encode in the message
|
@@ -45,36 +80,49 @@ module NETSNMP
|
|
45
80
|
# @return [String] the byte representation of an SNMP v3 Message
|
46
81
|
#
|
47
82
|
def encode(pdu, security_parameters:, engine_boots: 0, engine_time: 0)
|
83
|
+
log(level: 2) { pdu.to_hex }
|
84
|
+
log { "encoding PDU in V3 message..." }
|
48
85
|
scoped_pdu, salt_param = security_parameters.encode(pdu, salt: PRIVNONE,
|
49
86
|
engine_boots: engine_boots,
|
50
87
|
engine_time: engine_time)
|
51
88
|
|
52
89
|
sec_params = OpenSSL::ASN1::Sequence.new([
|
53
|
-
OpenSSL::ASN1::OctetString.new(security_parameters.engine_id),
|
54
|
-
OpenSSL::ASN1::Integer.new(engine_boots),
|
55
|
-
OpenSSL::ASN1::Integer.new(engine_time),
|
56
|
-
OpenSSL::ASN1::OctetString.new(security_parameters.username),
|
90
|
+
OpenSSL::ASN1::OctetString.new(security_parameters.engine_id).with_label(:engine_id),
|
91
|
+
OpenSSL::ASN1::Integer.new(engine_boots).with_label(:engine_boots),
|
92
|
+
OpenSSL::ASN1::Integer.new(engine_time).with_label(:engine_time),
|
93
|
+
OpenSSL::ASN1::OctetString.new(security_parameters.username).with_label(:username),
|
57
94
|
AUTHNONE,
|
58
95
|
salt_param
|
59
|
-
])
|
96
|
+
]).with_label(:security_params)
|
97
|
+
log(level: 2) { sec_params.to_hex }
|
98
|
+
|
60
99
|
message_flags = MSG_REPORTABLE | security_parameters.security_level
|
61
|
-
message_id = OpenSSL::ASN1::Integer.new(SecureRandom.random_number(2147483647))
|
100
|
+
message_id = OpenSSL::ASN1::Integer.new(SecureRandom.random_number(2147483647)).with_label(:message_id)
|
62
101
|
headers = OpenSSL::ASN1::Sequence.new([
|
63
|
-
message_id,
|
64
|
-
|
102
|
+
message_id,
|
103
|
+
MSG_MAX_SIZE,
|
104
|
+
OpenSSL::ASN1::OctetString.new([String(message_flags)].pack("h*")).with_label(:message_flags),
|
65
105
|
MSG_SECURITY_MODEL
|
66
|
-
])
|
106
|
+
]).with_label(:headers)
|
67
107
|
|
68
108
|
encoded = OpenSSL::ASN1::Sequence([
|
69
109
|
MSG_VERSION,
|
70
110
|
headers,
|
71
|
-
OpenSSL::ASN1::OctetString.new(sec_params.to_der),
|
111
|
+
OpenSSL::ASN1::OctetString.new(sec_params.to_der).with_label(:security_params),
|
72
112
|
scoped_pdu
|
73
|
-
]).
|
113
|
+
]).with_label(:v3_message)
|
114
|
+
log(level: 2) { encoded.to_hex }
|
115
|
+
|
116
|
+
encoded = encoded.to_der
|
117
|
+
log { Hexdump.dump(encoded) }
|
74
118
|
signature = security_parameters.sign(encoded)
|
75
119
|
if signature
|
76
|
-
|
77
|
-
|
120
|
+
log { "signing V3 message..." }
|
121
|
+
auth_salt = OpenSSL::ASN1::OctetString.new(signature).with_label(:auth)
|
122
|
+
log(level: 2) { auth_salt.to_hex }
|
123
|
+
none_der = AUTHNONE.to_der
|
124
|
+
encoded[encoded.index(none_der), none_der.size] = auth_salt.to_der
|
125
|
+
log { Hexdump.dump(encoded) }
|
78
126
|
end
|
79
127
|
encoded
|
80
128
|
end
|