netsnmp 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- 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