netsnmp 0.1.3 → 0.1.4
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 +5 -5
- data/.rubocop.yml +11 -0
- data/.rubocop_todo.yml +69 -0
- data/.travis.yml +6 -14
- data/Gemfile +7 -5
- data/README.md +66 -32
- data/Rakefile +7 -9
- data/lib/netsnmp/client.rb +42 -39
- data/lib/netsnmp/encryption/aes.rb +22 -22
- data/lib/netsnmp/encryption/des.rb +20 -21
- data/lib/netsnmp/encryption/none.rb +3 -3
- data/lib/netsnmp/errors.rb +1 -0
- data/lib/netsnmp/message.rb +31 -30
- data/lib/netsnmp/oid.rb +5 -4
- data/lib/netsnmp/pdu.rb +66 -69
- data/lib/netsnmp/scoped_pdu.rb +5 -7
- data/lib/netsnmp/security_parameters.rb +73 -54
- data/lib/netsnmp/session.rb +22 -24
- data/lib/netsnmp/timeticks.rb +8 -10
- data/lib/netsnmp/v3_session.rb +11 -13
- data/lib/netsnmp/varbind.rb +53 -49
- data/lib/netsnmp/version.rb +2 -1
- data/lib/netsnmp.rb +32 -12
- data/netsnmp.gemspec +10 -10
- data/spec/client_spec.rb +69 -49
- data/spec/handlers/celluloid_spec.rb +14 -10
- data/spec/oid_spec.rb +5 -3
- data/spec/pdu_spec.rb +14 -7
- data/spec/security_parameters_spec.rb +50 -18
- data/spec/session_spec.rb +9 -6
- data/spec/spec_helper.rb +14 -65
- data/spec/support/Dockerfile +3 -3
- data/spec/support/celluloid.rb +12 -6
- data/spec/support/request_examples.rb +19 -8
- data/spec/support/specs.sh +39 -0
- data/spec/support/{start_docker.sh → start-docker.sh} +4 -4
- data/spec/timeticks_spec.rb +2 -0
- data/spec/v3_session_spec.rb +8 -4
- data/spec/varbind_spec.rb +12 -0
- metadata +13 -9
@@ -1,8 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module NETSNMP
|
3
4
|
# This module encapsulates the public API for encrypting/decrypting and signing/verifying.
|
4
|
-
#
|
5
|
-
# It doesn't interact with other layers from the library, rather it is used and passed all
|
5
|
+
#
|
6
|
+
# It doesn't interact with other layers from the library, rather it is used and passed all
|
6
7
|
# the arguments (consisting mostly of primitive types).
|
7
8
|
# It also provides validation of the security options passed with a client is initialized in v3 mode.
|
8
9
|
class SecurityParameters
|
@@ -11,8 +12,15 @@ module NETSNMP
|
|
11
12
|
IPAD = "\x36" * 64
|
12
13
|
OPAD = "\x5c" * 64
|
13
14
|
|
15
|
+
# Timeliness is part of SNMP V3 Security
|
16
|
+
# The topic is described very nice here https://www.snmpsharpnet.com/?page_id=28
|
17
|
+
# https://www.ietf.org/rfc/rfc2574.txt 1.4.1 Timeliness
|
18
|
+
# The probe is outdated after 150 seconds which results in a PDU Error, therefore it should expire before that and be renewed
|
19
|
+
# The 150 Seconds is specified in https://www.ietf.org/rfc/rfc2574.txt 2.2.3
|
20
|
+
TIMELINESS_THRESHOLD = 150
|
21
|
+
|
14
22
|
attr_reader :security_level, :username
|
15
|
-
|
23
|
+
attr_reader :engine_id
|
16
24
|
|
17
25
|
# @param [String] username the snmp v3 username
|
18
26
|
# @param [String] engine_id the device engine id (initialized to '' for report)
|
@@ -28,13 +36,14 @@ module NETSNMP
|
|
28
36
|
# not explicitly set), and :priv_password becomes mandatory.
|
29
37
|
#
|
30
38
|
def initialize(
|
31
|
-
username
|
39
|
+
username:,
|
32
40
|
engine_id: "",
|
33
|
-
security_level: nil,
|
34
|
-
auth_protocol: nil,
|
35
|
-
auth_password: nil,
|
36
|
-
priv_protocol: nil,
|
37
|
-
priv_password: nil
|
41
|
+
security_level: nil,
|
42
|
+
auth_protocol: nil,
|
43
|
+
auth_password: nil,
|
44
|
+
priv_protocol: nil,
|
45
|
+
priv_password: nil
|
46
|
+
)
|
38
47
|
@security_level = security_level
|
39
48
|
@username = username
|
40
49
|
@engine_id = engine_id
|
@@ -47,21 +56,25 @@ module NETSNMP
|
|
47
56
|
@priv_pass_key = passkey(@priv_password) unless @priv_password.nil?
|
48
57
|
end
|
49
58
|
|
59
|
+
def engine_id=(id)
|
60
|
+
@timeliness = Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
|
61
|
+
@engine_id = id
|
62
|
+
end
|
50
63
|
|
51
64
|
# @param [#to_asn, #to_der] pdu the pdu to encode (must quack like a asn1 type)
|
52
65
|
# @param [String] salt the salt to use
|
53
66
|
# @param [Integer] engine_time the reported engine time
|
54
67
|
# @param [Integer] engine_boots the reported boots time
|
55
|
-
#
|
56
|
-
# @return [Array] a pair, where the first argument in the asn structure with the encoded pdu,
|
68
|
+
#
|
69
|
+
# @return [Array] a pair, where the first argument in the asn structure with the encoded pdu,
|
57
70
|
# and the second is the calculated salt (if it has been encrypted)
|
58
|
-
def encode(pdu, salt
|
71
|
+
def encode(pdu, salt:, engine_time:, engine_boots:)
|
59
72
|
if encryption
|
60
|
-
encrypted_pdu, salt = encryption.encrypt(pdu.to_der, engine_boots: engine_boots,
|
73
|
+
encrypted_pdu, salt = encryption.encrypt(pdu.to_der, engine_boots: engine_boots,
|
61
74
|
engine_time: engine_time)
|
62
|
-
[OpenSSL::ASN1::OctetString.new(encrypted_pdu), OpenSSL::ASN1::OctetString.new(salt)
|
75
|
+
[OpenSSL::ASN1::OctetString.new(encrypted_pdu), OpenSSL::ASN1::OctetString.new(salt)]
|
63
76
|
else
|
64
|
-
[
|
77
|
+
[pdu.to_asn, salt]
|
65
78
|
end
|
66
79
|
end
|
67
80
|
|
@@ -69,12 +82,12 @@ module NETSNMP
|
|
69
82
|
# @param [String] salt the salt from the incoming der
|
70
83
|
# @param [Integer] engine_time the reported engine time
|
71
84
|
# @param [Integer] engine_boots the reported engine boots
|
72
|
-
def decode(der, salt
|
85
|
+
def decode(der, salt:, engine_time:, engine_boots:)
|
73
86
|
asn = OpenSSL::ASN1.decode(der)
|
74
87
|
if encryption
|
75
88
|
encrypted_pdu = asn.value
|
76
89
|
pdu_der = encryption.decrypt(encrypted_pdu, salt: salt, engine_time: engine_time, engine_boots: engine_boots)
|
77
|
-
OpenSSL::ASN1.decode(pdu_der)
|
90
|
+
OpenSSL::ASN1.decode(pdu_der)
|
78
91
|
else
|
79
92
|
asn
|
80
93
|
end
|
@@ -86,7 +99,7 @@ module NETSNMP
|
|
86
99
|
# @note this method is used in the process of authenticating a message
|
87
100
|
def sign(message)
|
88
101
|
# don't sign unless you have to
|
89
|
-
return nil
|
102
|
+
return nil unless @auth_protocol
|
90
103
|
|
91
104
|
key = auth_key.dup
|
92
105
|
|
@@ -95,24 +108,30 @@ module NETSNMP
|
|
95
108
|
k2 = key.xor(OPAD)
|
96
109
|
|
97
110
|
digest.reset
|
98
|
-
digest << (
|
111
|
+
digest << (k1 + message)
|
99
112
|
d1 = digest.digest
|
100
113
|
|
101
114
|
digest.reset
|
102
|
-
digest << (
|
103
|
-
digest.digest[0,12]
|
115
|
+
digest << (k2 + d1)
|
116
|
+
digest.digest[0, 12]
|
104
117
|
end
|
105
118
|
|
106
119
|
# @param [String] stream the encoded incoming payload
|
107
120
|
# @param [String] salt the incoming payload''s salt
|
108
121
|
#
|
109
|
-
# @raise [NETSNMP::Error] if the message's integration has been violated
|
122
|
+
# @raise [NETSNMP::Error] if the message's integration has been violated
|
110
123
|
def verify(stream, salt)
|
111
124
|
return if @security_level < 1
|
112
125
|
verisalt = sign(stream)
|
113
126
|
raise Error, "invalid message authentication salt" unless verisalt == salt
|
114
127
|
end
|
115
128
|
|
129
|
+
def must_revalidate?
|
130
|
+
return @engine_id.empty? unless authorizable?
|
131
|
+
return true if @engine_id.empty? || @timeliness.nil?
|
132
|
+
(Process.clock_gettime(Process::CLOCK_MONOTONIC, :second) - @timeliness) >= TIMELINESS_THRESHOLD
|
133
|
+
end
|
134
|
+
|
116
135
|
private
|
117
136
|
|
118
137
|
def auth_key
|
@@ -125,46 +144,43 @@ module NETSNMP
|
|
125
144
|
|
126
145
|
def check_parameters
|
127
146
|
@security_level = case @security_level
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
if @security_level
|
147
|
+
when Integer then @security_level
|
148
|
+
when /no_?auth/ then 0
|
149
|
+
when /auth_?no_?priv/ then 1
|
150
|
+
when /auth_?priv/, nil then 3
|
151
|
+
else
|
152
|
+
raise Error, "security level not supported: #{@security_level}"
|
153
|
+
end
|
154
|
+
|
155
|
+
if @security_level.positive?
|
137
156
|
@auth_protocol ||= :md5 # this is the default
|
138
157
|
raise "security level requires an auth password" if @auth_password.nil?
|
139
|
-
raise "auth password must have between 8 to 32 characters"
|
140
|
-
end
|
141
|
-
if @security_level > 1
|
142
|
-
@priv_protocol ||= :des
|
143
|
-
raise "security level requires a priv password" if @priv_password.nil?
|
144
|
-
raise "priv password must have between 8 to 32 characters" if not (8..32).include?(@priv_password.length)
|
158
|
+
raise "auth password must have between 8 to 32 characters" unless (8..32).cover?(@auth_password.length)
|
145
159
|
end
|
160
|
+
return unless @security_level > 1
|
161
|
+
@priv_protocol ||= :des
|
162
|
+
raise "security level requires a priv password" if @priv_password.nil?
|
163
|
+
raise "priv password must have between 8 to 32 characters" unless (8..32).cover?(@priv_password.length)
|
146
164
|
end
|
147
165
|
|
148
166
|
def localize_key(key)
|
149
|
-
|
150
167
|
digest.reset
|
151
168
|
digest << key
|
152
|
-
digest << @engine_id
|
169
|
+
digest << @engine_id
|
153
170
|
digest << key
|
154
171
|
|
155
172
|
digest.digest
|
156
173
|
end
|
157
174
|
|
158
175
|
def passkey(password)
|
159
|
-
|
160
176
|
digest.reset
|
161
177
|
password_index = 0
|
162
178
|
|
163
|
-
buffer =
|
179
|
+
# buffer = +""
|
164
180
|
password_length = password.length
|
165
181
|
while password_index < 1048576
|
166
182
|
initial = password_index % password_length
|
167
|
-
rotated = password[initial..-1] + password[0,initial]
|
183
|
+
rotated = password[initial..-1] + password[0, initial]
|
168
184
|
buffer = rotated * (64 / rotated.length) + rotated[0, 64 % rotated.length]
|
169
185
|
password_index += 64
|
170
186
|
digest << buffer
|
@@ -172,27 +188,30 @@ module NETSNMP
|
|
172
188
|
end
|
173
189
|
|
174
190
|
dig = digest.digest
|
175
|
-
dig = dig[0,16] if @auth_protocol == :md5
|
191
|
+
dig = dig[0, 16] if @auth_protocol == :md5
|
176
192
|
dig
|
177
193
|
end
|
178
194
|
|
179
|
-
def digest
|
195
|
+
def digest
|
180
196
|
@digest ||= case @auth_protocol
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
197
|
+
when :md5 then OpenSSL::Digest::MD5.new
|
198
|
+
when :sha then OpenSSL::Digest::SHA1.new
|
199
|
+
else
|
200
|
+
raise Error, "unsupported auth protocol: #{@auth_protocol}"
|
201
|
+
end
|
186
202
|
end
|
187
203
|
|
188
204
|
def encryption
|
189
205
|
@encryption ||= case @priv_protocol
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
206
|
+
when :des
|
207
|
+
Encryption::DES.new(priv_key)
|
208
|
+
when :aes
|
209
|
+
Encryption::AES.new(priv_key)
|
210
|
+
end
|
195
211
|
end
|
196
212
|
|
213
|
+
def authorizable?
|
214
|
+
@auth_protocol && @auth_protocol != :none
|
215
|
+
end
|
197
216
|
end
|
198
217
|
end
|
data/lib/netsnmp/session.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module NETSNMP
|
3
|
-
# Let's just remind that there is no session in snmp, this is just an abstraction.
|
4
|
-
#
|
4
|
+
# Let's just remind that there is no session in snmp, this is just an abstraction.
|
5
|
+
#
|
5
6
|
class Session
|
6
7
|
TIMEOUT = 2
|
7
8
|
|
8
|
-
# @param [Hash] opts the options set
|
9
|
+
# @param [Hash] opts the options set
|
9
10
|
def initialize(version: 1, community: "public", **options)
|
10
|
-
@version =
|
11
|
+
@version = version
|
11
12
|
@community = community
|
12
13
|
validate(options)
|
13
14
|
end
|
@@ -25,7 +26,7 @@ module NETSNMP
|
|
25
26
|
# @return [NETSNMP::PDU] a pdu
|
26
27
|
#
|
27
28
|
def build_pdu(type, *vars)
|
28
|
-
PDU.build(type, headers: [
|
29
|
+
PDU.build(type, headers: [@version, @community], varbinds: vars)
|
29
30
|
end
|
30
31
|
|
31
32
|
# send a pdu, receives a pdu
|
@@ -35,7 +36,7 @@ module NETSNMP
|
|
35
36
|
# @return [NETSNMP::PDU] the response pdu
|
36
37
|
#
|
37
38
|
def send(pdu)
|
38
|
-
encoded_request = encode(pdu)
|
39
|
+
encoded_request = encode(pdu)
|
39
40
|
encoded_response = @transport.send(encoded_request)
|
40
41
|
decode(encoded_response)
|
41
42
|
end
|
@@ -46,7 +47,7 @@ module NETSNMP
|
|
46
47
|
proxy = options[:proxy]
|
47
48
|
if proxy
|
48
49
|
@proxy = true
|
49
|
-
@transport = proxy
|
50
|
+
@transport = proxy
|
50
51
|
else
|
51
52
|
host, port = options.values_at(:host, :port)
|
52
53
|
raise "you must provide an hostname/ip under :host" unless host
|
@@ -54,16 +55,15 @@ module NETSNMP
|
|
54
55
|
@transport = Transport.new(host, port.to_i, timeout: options.fetch(:timeout, TIMEOUT))
|
55
56
|
end
|
56
57
|
@version = case @version
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
58
|
+
when Integer then @version # assume the use know what he's doing
|
59
|
+
when /v?1/ then 0
|
60
|
+
when /v?2c?/ then 1
|
61
|
+
when /v?3/ then 3
|
62
|
+
else
|
63
|
+
raise "unsupported snmp version (#{@version})"
|
64
|
+
end
|
64
65
|
end
|
65
66
|
|
66
|
-
|
67
67
|
def encode(pdu)
|
68
68
|
pdu.to_der
|
69
69
|
end
|
@@ -75,13 +75,13 @@ module NETSNMP
|
|
75
75
|
class Transport
|
76
76
|
MAXPDUSIZE = 0xffff + 1
|
77
77
|
|
78
|
-
def initialize(host, port, timeout:
|
78
|
+
def initialize(host, port, timeout:)
|
79
79
|
@socket = UDPSocket.new
|
80
|
-
@socket.connect(
|
80
|
+
@socket.connect(host, port)
|
81
81
|
@timeout = timeout
|
82
82
|
end
|
83
83
|
|
84
|
-
def close
|
84
|
+
def close
|
85
85
|
@socket.close
|
86
86
|
end
|
87
87
|
|
@@ -96,9 +96,9 @@ module NETSNMP
|
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
99
|
-
def recv(bytesize=MAXPDUSIZE)
|
99
|
+
def recv(bytesize = MAXPDUSIZE)
|
100
100
|
perform_io do
|
101
|
-
datagram,
|
101
|
+
datagram, = @socket.recvfrom_nonblock(bytesize)
|
102
102
|
datagram
|
103
103
|
end
|
104
104
|
end
|
@@ -118,11 +118,9 @@ module NETSNMP
|
|
118
118
|
end
|
119
119
|
|
120
120
|
def wait(mode)
|
121
|
-
|
122
|
-
|
123
|
-
end
|
121
|
+
return if @socket.__send__(mode, @timeout)
|
122
|
+
raise Timeout::Error, "Timeout after #{@timeout} seconds"
|
124
123
|
end
|
125
|
-
|
126
124
|
end
|
127
125
|
end
|
128
126
|
end
|
data/lib/netsnmp/timeticks.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module NETSNMP
|
2
4
|
class Timetick < Numeric
|
3
|
-
|
4
5
|
# @param [Integer] ticks number of microseconds since the time it was read
|
5
6
|
def initialize(ticks)
|
6
7
|
@ticks = ticks
|
7
8
|
end
|
8
9
|
|
9
|
-
|
10
10
|
def to_s
|
11
11
|
days = days_since
|
12
12
|
hours = hours_since(days)
|
@@ -24,7 +24,7 @@ module NETSNMP
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def coerce(other)
|
27
|
-
[
|
27
|
+
[Timetick.new(other), self]
|
28
28
|
end
|
29
29
|
|
30
30
|
def <=>(other)
|
@@ -32,24 +32,23 @@ module NETSNMP
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def +(other)
|
35
|
-
Timetick.new(
|
35
|
+
Timetick.new((to_i + other.to_i))
|
36
36
|
end
|
37
37
|
|
38
38
|
def -(other)
|
39
|
-
Timetick.new(
|
39
|
+
Timetick.new((to_i - other.to_i))
|
40
40
|
end
|
41
41
|
|
42
42
|
def *(other)
|
43
|
-
Timetick.new(
|
43
|
+
Timetick.new((to_i * other.to_i))
|
44
44
|
end
|
45
45
|
|
46
46
|
def /(other)
|
47
|
-
Timetick.new(
|
47
|
+
Timetick.new((to_i / other.to_i))
|
48
48
|
end
|
49
49
|
|
50
50
|
private
|
51
51
|
|
52
|
-
|
53
52
|
def days_since
|
54
53
|
Rational(@ticks, 8_640_000)
|
55
54
|
end
|
@@ -61,10 +60,9 @@ module NETSNMP
|
|
61
60
|
def minutes_since(hours)
|
62
61
|
Rational((hours.to_f - hours.to_i) * 60)
|
63
62
|
end
|
64
|
-
|
63
|
+
|
65
64
|
def milliseconds_since(minutes)
|
66
65
|
Rational((minutes.to_f - minutes.to_i) * 60)
|
67
66
|
end
|
68
|
-
|
69
67
|
end
|
70
68
|
end
|
data/lib/netsnmp/v3_session.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module NETSNMP
|
3
4
|
# Abstraction for the v3 semantics.
|
4
5
|
class V3Session < Session
|
5
|
-
|
6
6
|
# @param [String, Integer] version SNMP version (always 3)
|
7
7
|
def initialize(version: 3, context: "", **opts)
|
8
8
|
@context = context
|
9
|
-
@security_parameters = opts.delete(:security_parameters)
|
9
|
+
@security_parameters = opts.delete(:security_parameters)
|
10
10
|
super
|
11
11
|
end
|
12
12
|
|
@@ -15,12 +15,12 @@ module NETSNMP
|
|
15
15
|
# @return [NETSNMP::ScopedPDU] a pdu
|
16
16
|
def build_pdu(type, *vars)
|
17
17
|
engine_id = security_parameters.engine_id
|
18
|
-
|
18
|
+
ScopedPDU.build(type, headers: [engine_id, @context], varbinds: vars)
|
19
19
|
end
|
20
20
|
|
21
21
|
# @see {NETSNMP::Session#send}
|
22
22
|
def send(*)
|
23
|
-
pdu,
|
23
|
+
pdu, = super
|
24
24
|
pdu
|
25
25
|
end
|
26
26
|
|
@@ -28,29 +28,27 @@ module NETSNMP
|
|
28
28
|
|
29
29
|
def validate(**options)
|
30
30
|
super
|
31
|
-
if s = @security_parameters
|
31
|
+
if (s = @security_parameters)
|
32
32
|
# inspect public API
|
33
33
|
unless s.respond_to?(:encode) &&
|
34
34
|
s.respond_to?(:decode) &&
|
35
35
|
s.respond_to?(:sign) &&
|
36
36
|
s.respond_to?(:verify)
|
37
|
-
raise Error, "#{s} doesn't respect the sec params public API (#encode, #decode, #sign)"
|
38
|
-
end
|
37
|
+
raise Error, "#{s} doesn't respect the sec params public API (#encode, #decode, #sign)"
|
38
|
+
end
|
39
39
|
else
|
40
|
-
@security_parameters = SecurityParameters.new(security_level: options[:security_level],
|
40
|
+
@security_parameters = SecurityParameters.new(security_level: options[:security_level],
|
41
41
|
username: options[:username],
|
42
42
|
auth_protocol: options[:auth_protocol],
|
43
43
|
priv_protocol: options[:priv_protocol],
|
44
44
|
auth_password: options[:auth_password],
|
45
45
|
priv_password: options[:priv_password])
|
46
|
-
|
46
|
+
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
50
|
def security_parameters
|
51
|
-
if @security_parameters.
|
52
|
-
@security_parameters.engine_id = probe_for_engine
|
53
|
-
end
|
51
|
+
@security_parameters.engine_id = probe_for_engine if @security_parameters.must_revalidate?
|
54
52
|
@security_parameters
|
55
53
|
end
|
56
54
|
|
@@ -69,7 +67,7 @@ module NETSNMP
|
|
69
67
|
end
|
70
68
|
|
71
69
|
def encode(pdu)
|
72
|
-
Message.encode(pdu, security_parameters: @security_parameters,
|
70
|
+
Message.encode(pdu, security_parameters: @security_parameters,
|
73
71
|
engine_boots: @engine_boots,
|
74
72
|
engine_time: @engine_time)
|
75
73
|
end
|
data/lib/netsnmp/varbind.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module NETSNMP
|
3
4
|
# Abstracts the PDU variable structure into a ruby object
|
4
5
|
#
|
5
6
|
class Varbind
|
6
|
-
|
7
7
|
attr_reader :oid, :value
|
8
8
|
|
9
|
-
def initialize(oid
|
9
|
+
def initialize(oid, value: nil, type: nil, **_opts)
|
10
10
|
@oid = OID.build(oid)
|
11
11
|
@type = type
|
12
12
|
@value = convert_val(value) if value
|
@@ -23,29 +23,28 @@ module NETSNMP
|
|
23
23
|
def to_asn
|
24
24
|
asn_oid = OID.to_asn(@oid)
|
25
25
|
asn_val = if @type
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
OpenSSL::ASN1::Sequence.new(
|
26
|
+
convert_to_asn(@type, @value)
|
27
|
+
else
|
28
|
+
case @value
|
29
|
+
when String
|
30
|
+
OpenSSL::ASN1::OctetString.new(@value)
|
31
|
+
when Integer
|
32
|
+
OpenSSL::ASN1::Integer.new(@value)
|
33
|
+
when true, false
|
34
|
+
OpenSSL::ASN1::Boolean.new(@value)
|
35
|
+
when nil
|
36
|
+
OpenSSL::ASN1::Null.new(nil)
|
37
|
+
when IPAddr
|
38
|
+
OpenSSL::ASN1::ASN1Data.new(@value.hton, 0, :APPLICATION)
|
39
|
+
when Timetick
|
40
|
+
@value.to_asn
|
41
|
+
else
|
42
|
+
raise Error, "#{@value}: unsupported varbind type"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
OpenSSL::ASN1::Sequence.new([asn_oid, asn_val])
|
46
46
|
end
|
47
47
|
|
48
|
-
|
49
48
|
def convert_val(asn_value)
|
50
49
|
case asn_value
|
51
50
|
when OpenSSL::ASN1::OctetString
|
@@ -68,10 +67,10 @@ module NETSNMP
|
|
68
67
|
when OpenSSL::ASN1::ASN1Data
|
69
68
|
# application data
|
70
69
|
convert_application_asn(asn_value)
|
71
|
-
when OpenSSL::BN
|
70
|
+
# when OpenSSL::BN
|
72
71
|
else
|
73
|
-
|
74
|
-
end
|
72
|
+
asn_value # assume it's already primitive
|
73
|
+
end
|
75
74
|
end
|
76
75
|
|
77
76
|
def convert_to_asn(typ, value)
|
@@ -79,35 +78,40 @@ module NETSNMP
|
|
79
78
|
asn_val = value
|
80
79
|
if typ.is_a?(Symbol)
|
81
80
|
asn_type = case typ
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
81
|
+
when :ipaddress then 0
|
82
|
+
when :counter32
|
83
|
+
asn_val = [value].pack("n*")
|
84
|
+
1
|
85
|
+
when :gauge
|
86
|
+
asn_val = [value].pack("n*")
|
87
|
+
2
|
88
|
+
when :timetick
|
89
|
+
return Timetick.new(value).to_asn
|
90
|
+
when :opaque then 4
|
91
|
+
when :nsap then 5
|
92
|
+
when :counter64 then 6
|
93
|
+
when :uinteger then 7
|
94
|
+
else
|
95
|
+
raise Error, "#{typ}: unsupported application type"
|
96
|
+
end
|
94
97
|
end
|
95
98
|
OpenSSL::ASN1::ASN1Data.new(asn_val, asn_type, :APPLICATION)
|
96
99
|
end
|
97
100
|
|
98
101
|
def convert_application_asn(asn)
|
99
102
|
case asn.tag
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
when
|
109
|
-
when
|
110
|
-
when
|
103
|
+
when 0 # IP Address
|
104
|
+
IPAddr.new_ntoh(asn.value)
|
105
|
+
when 1 # ASN counter 32
|
106
|
+
asn.value.unpack("n*")[0] || 0
|
107
|
+
when 2 # gauge
|
108
|
+
asn.value.unpack("n*")[0] || 0
|
109
|
+
when 3 # timeticks
|
110
|
+
Timetick.new(asn.value.unpack("N*")[0] || 0)
|
111
|
+
# when 4 # opaque
|
112
|
+
# when 5 # NSAP
|
113
|
+
# when 6 # ASN Counter 64
|
114
|
+
# when 7 # ASN UInteger
|
111
115
|
end
|
112
116
|
end
|
113
117
|
end
|
data/lib/netsnmp/version.rb
CHANGED