netsnmp 0.4.0 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60a9c68e56e5ad0600d056eedd315d8639cfd3965aa5e1eb62f5f3f935b4ad6a
4
- data.tar.gz: ea560ac1131bff9ca6af0fec5e426bd37edbfbc7520ceb629f1de279a905ced0
3
+ metadata.gz: 93b380d735dc2250a9c3528e7ab1e6c70147124a6dc439fe7dd4eac1f4c0fd55
4
+ data.tar.gz: 52db8f559bc3485ab4802d72ccba98fb29429a0b9a0cceec13fc5a489409e35e
5
5
  SHA512:
6
- metadata.gz: 8410a3a38d7d7eab07692009ae4d870dbae8e7abc50dbc724d9ad3f8a4e029b441ffba62e9477ae587f8845a561cb0ba2e2c5613540d615bdce0cf62cb448098
7
- data.tar.gz: 85d0d898983f99dedb4a71cf6bd33f665cfda496bb8a60eb76be36a96de6f28a9984505c564701bfab6bf74e8367544ec2f5adbcf8faf152d9a71679b73b5df1
6
+ metadata.gz: 1a7573fe1ca2f0d78a3ca4d88d8054b9fd872078352d8ed3f700525e049719d601fe8f7dda94f0ff77b866e75961eb26af80a4acd332e04d390c433405970fd7
7
+ data.tar.gz: 3524298a40a89f5364a27cfe0444f7e298016822439020c04b203960cc4c77622453f9a48d6c01ea1e072c3a004705299b6a8f34a89cb1ce00f66a98f3919d7a
data/README.md CHANGED
@@ -31,6 +31,7 @@ $ gem install netsnmp
31
31
  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
+ * SNMPv3 USM supporting MD5/SHA/SHA256 auth and DES/AES128 privacy crypto algorithms.
34
35
  * Client/Manager API with simple interface for get, genext, set and walk.
35
36
  * Pure Ruby.
36
37
  * Support for concurrency and evented I/O.
@@ -80,7 +81,7 @@ manager.get(oid: "sysName.0") #=> 'tt'
80
81
  # SNMP walk
81
82
  # sysORDescr
82
83
  manager.walk(oid: "sysORDescr").each do |oid_code, value|
83
- # do something with them
84
+ # do something with them
84
85
  puts "for #{oid_code}: #{value}"
85
86
  end
86
87
 
@@ -214,8 +215,8 @@ gem 'netsnmp'
214
215
 
215
216
  # or, in the command line
216
217
 
217
- $ gem install netsnmp
218
- ```
218
+ $ gem install netsnmp
219
+ ```
219
220
 
220
221
  and `netsnmp` will automatically pick it up.
221
222
 
@@ -245,6 +246,8 @@ This library supports and is tested against ruby versions 2.1 or more recent, in
245
246
 
246
247
  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.
247
248
 
249
+ 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).
250
+
248
251
  ## Debugging
249
252
 
250
253
  You can either set the `NETSNMP_DEBUG` to the desided debug level (currently, 1 and 2). The logs will be written to stderr.
@@ -7,8 +7,7 @@ module NETSNMP
7
7
 
8
8
  prepend Loggable
9
9
 
10
- AUTHNONE = OpenSSL::ASN1::OctetString.new("\x00" * 12).with_label(:auth_mask)
11
- PRIVNONE = OpenSSL::ASN1::OctetString.new("")
10
+ PRIVNONE = OpenSSL::ASN1::OctetString.new("")
12
11
  MSG_MAX_SIZE = OpenSSL::ASN1::Integer.new(65507).with_label(:max_message_size)
13
12
  MSG_SECURITY_MODEL = OpenSSL::ASN1::Integer.new(3).with_label(:security_model) # usmSecurityModel
14
13
  MSG_VERSION = OpenSSL::ASN1::Integer.new(3).with_label(:message_version)
@@ -16,6 +15,10 @@ module NETSNMP
16
15
 
17
16
  def initialize(**); end
18
17
 
18
+ def verify(stream, auth_param, security_level, security_parameters:)
19
+ security_parameters.verify(stream.sub(auth_param, authnone(security_parameters.auth_protocol).value), auth_param, security_level: security_level)
20
+ end
21
+
19
22
  # @param [String] payload of an snmp v3 message which can be decoded
20
23
  # @param [NETSMP::SecurityParameters, #decode] security_parameters knowns how to decode the stream
21
24
  #
@@ -51,9 +54,7 @@ module NETSNMP
51
54
  log(level: 2) { asn_tree.to_hex }
52
55
  log(level: 2) { sec_params_asn.to_hex }
53
56
 
54
- # validate_authentication
55
57
  auth_param = auth_param.value
56
- security_parameters.verify(stream.sub(auth_param, AUTHNONE.value), auth_param, security_level: security_level)
57
58
 
58
59
  engine_boots = engine_boots.value.to_i
59
60
  engine_time = engine_time.value.to_i
@@ -65,6 +66,9 @@ module NETSNMP
65
66
 
66
67
  log { "received response PDU" }
67
68
  pdu = ScopedPDU.decode(encoded_pdu)
69
+ pdu.auth_param = auth_param
70
+ pdu.security_level = security_level
71
+
68
72
  log(level: 2) { pdu.to_hex }
69
73
  [pdu, engine_id.value, engine_boots, engine_time]
70
74
  end
@@ -86,7 +90,7 @@ module NETSNMP
86
90
  OpenSSL::ASN1::Integer.new(engine_boots).with_label(:engine_boots),
87
91
  OpenSSL::ASN1::Integer.new(engine_time).with_label(:engine_time),
88
92
  OpenSSL::ASN1::OctetString.new(security_parameters.username).with_label(:username),
89
- AUTHNONE,
93
+ authnone(security_parameters.auth_protocol),
90
94
  salt_param
91
95
  ]).with_label(:security_params)
92
96
  log(level: 2) { sec_params.to_hex }
@@ -115,10 +119,22 @@ module NETSNMP
115
119
  log { "signing V3 message..." }
116
120
  auth_salt = OpenSSL::ASN1::OctetString.new(signature).with_label(:auth)
117
121
  log(level: 2) { auth_salt.to_hex }
118
- encoded.sub!(AUTHNONE.to_der, auth_salt.to_der)
122
+ none_der = authnone(security_parameters.auth_protocol).to_der
123
+ encoded[encoded.index(none_der), none_der.size] = auth_salt.to_der
119
124
  log { Hexdump.dump(encoded) }
120
125
  end
121
126
  encoded
122
127
  end
128
+
129
+ private
130
+
131
+ # https://datatracker.ietf.org/doc/html/rfc7860#section-4.2.2 part 3
132
+ # https://datatracker.ietf.org/doc/html/rfc3414#section-6.3.2 part 3
133
+ def authnone(auth_protocol)
134
+ # The digest in the msgAuthenticationParameters field is replaced by the 12 zero octets.
135
+ # 24 octets for sha256
136
+ number_of_octets = auth_protocol == :sha256 ? 24 : 12
137
+ OpenSSL::ASN1::OctetString.new("\x00" * number_of_octets).with_label(:auth_mask)
138
+ end
123
139
  end
124
140
  end
data/lib/netsnmp/mib.rb CHANGED
@@ -33,7 +33,9 @@ module NETSNMP
33
33
  if idx
34
34
  mod = prefix[0..(idx - 1)]
35
35
  type = prefix[(idx + 2)..-1]
36
- return unless load(mod)
36
+ unless module_loaded?(mod)
37
+ return unless load(mod)
38
+ end
37
39
  else
38
40
  type = prefix
39
41
  end
@@ -84,6 +86,14 @@ module NETSNMP
84
86
  true
85
87
  end
86
88
 
89
+ def module_loaded?(mod)
90
+ if File.file?(mod)
91
+ @modules_loaded.include?(mod)
92
+ else
93
+ @modules_loaded.map { |path| File.basename(path, ".*") }.include?(mod)
94
+ end
95
+ end
96
+
87
97
  TYPES = ["OBJECT IDENTIFIER", "OBJECT-TYPE", "MODULE-IDENTITY"].freeze
88
98
 
89
99
  STATIC_MIB_TO_OID = {
data/lib/netsnmp/pdu.rb CHANGED
@@ -7,7 +7,7 @@ module NETSNMP
7
7
  class PDU
8
8
  using ASNExtensions
9
9
 
10
- MAXREQUESTID = 2147483647
10
+ MAXREQUESTID = 0x7fffffff
11
11
 
12
12
  using ASNExtensions
13
13
  class << self
@@ -6,6 +6,8 @@ module NETSNMP
6
6
 
7
7
  attr_reader :engine_id
8
8
 
9
+ attr_accessor :security_level, :auth_param
10
+
9
11
  def initialize(type:, headers:, **options)
10
12
  @engine_id, @context = headers
11
13
  super(type: type, headers: [3, nil], **options)
@@ -22,13 +22,13 @@ module NETSNMP
22
22
  # The 150 Seconds is specified in https://www.ietf.org/rfc/rfc2574.txt 2.2.3
23
23
  TIMELINESS_THRESHOLD = 150
24
24
 
25
- attr_reader :security_level, :username
25
+ attr_reader :security_level, :username, :auth_protocol
26
26
  attr_reader :engine_id
27
27
 
28
28
  # @param [String] username the snmp v3 username
29
29
  # @param [String] engine_id the device engine id (initialized to '' for report)
30
30
  # @param [Symbol, integer] security_level allowed snmp v3 security level (:auth_priv, :auth_no_priv, etc)
31
- # @param [Symbol, nil] auth_protocol a supported authentication protocol (currently supported: :md5, :sha)
31
+ # @param [Symbol, nil] auth_protocol a supported authentication protocol (currently supported: :md5, :sha, :sha256)
32
32
  # @param [Symbol, nil] priv_protocol a supported privacy protocol (currently supported: :des, :aes)
33
33
  # @param [String, nil] auth_password the authentication password
34
34
  # @param [String, nil] priv_password the privacy password
@@ -110,6 +110,12 @@ module NETSNMP
110
110
 
111
111
  key = auth_key.dup
112
112
 
113
+ # SHA256 => https://datatracker.ietf.org/doc/html/rfc7860#section-4.2.2
114
+ # The 24 first octets of HMAC are taken as the computed MAC value
115
+ return OpenSSL::HMAC.digest("SHA256", key, message)[0, 24] if @auth_protocol == :sha256
116
+
117
+ # MD5 => https://datatracker.ietf.org/doc/html/rfc3414#section-6.3.2
118
+ # SHA1 => https://datatracker.ietf.org/doc/html/rfc3414#section-7.3.2
113
119
  key << "\x00" * (@auth_protocol == :md5 ? 48 : 44)
114
120
  k1 = key.xor(IPAD)
115
121
  k2 = key.xor(OPAD)
@@ -120,6 +126,7 @@ module NETSNMP
120
126
 
121
127
  digest.reset
122
128
  digest << (k2 + d1)
129
+ # The 12 first octets of the digest are taken as the computed MAC value
123
130
  digest.digest[0, 12]
124
131
  end
125
132
 
@@ -204,6 +211,7 @@ module NETSNMP
204
211
  @digest ||= case @auth_protocol
205
212
  when :md5 then OpenSSL::Digest::MD5.new
206
213
  when :sha then OpenSSL::Digest::SHA1.new
214
+ when :sha256 then OpenSSL::Digest::SHA256.new
207
215
  else
208
216
  raise Error, "unsupported auth protocol: #{@auth_protocol}"
209
217
  end
@@ -75,7 +75,7 @@ module NETSNMP
75
75
 
76
76
  def initialize(host, port, timeout:)
77
77
  @socket = UDPSocket.new
78
- @socket.connect(host, port)
78
+ @destaddr = Socket.sockaddr_in(port, host)
79
79
  @timeout = timeout
80
80
  end
81
81
 
@@ -90,13 +90,13 @@ module NETSNMP
90
90
 
91
91
  def write(payload)
92
92
  perform_io do
93
- @socket.send(payload, 0)
93
+ @socket.sendmsg(payload, Socket::MSG_DONTWAIT | Socket::MSG_NOSIGNAL, @destaddr)
94
94
  end
95
95
  end
96
96
 
97
97
  def recv(bytesize = MAXPDUSIZE)
98
98
  perform_io do
99
- datagram, = @socket.recvfrom_nonblock(bytesize)
99
+ datagram, = @socket.recvmsg_nonblock(bytesize, Socket::MSG_DONTWAIT)
100
100
  datagram
101
101
  end
102
102
  end
@@ -24,14 +24,7 @@ module NETSNMP
24
24
  log { "sending request..." }
25
25
  encoded_request = encode(pdu)
26
26
  encoded_response = @transport.send(encoded_request)
27
- response_pdu, *args = decode(encoded_response)
28
- if response_pdu.type == 8
29
- varbind = response_pdu.varbinds.first
30
- if varbind.oid == "1.3.6.1.6.3.15.1.1.2.0" # IdNotInTimeWindow
31
- _, @engine_boots, @engine_time = args
32
- raise IdNotInTimeWindowError, "request timestamp is already out of time window"
33
- end
34
- end
27
+ response_pdu, * = decode(encoded_response)
35
28
  response_pdu
36
29
  end
37
30
 
@@ -85,7 +78,33 @@ module NETSNMP
85
78
  end
86
79
 
87
80
  def decode(stream, security_parameters: @security_parameters)
88
- @message_serializer.decode(stream, security_parameters: security_parameters)
81
+ return_pdu = @message_serializer.decode(stream, security_parameters: security_parameters)
82
+
83
+ pdu, *args = return_pdu
84
+
85
+ # usmStats: http://oidref.com/1.3.6.1.6.3.15.1.1
86
+ if pdu.type == 8
87
+ case pdu.varbinds.first.oid
88
+ when "1.3.6.1.6.3.15.1.1.1.0" # usmStatsUnsupportedSecLevels
89
+ raise Error, "Unsupported security level"
90
+ when "1.3.6.1.6.3.15.1.1.2.0" # usmStatsNotInTimeWindows
91
+ _, @engine_boots, @engine_time = args
92
+ raise IdNotInTimeWindowError, "Not in time window"
93
+ when "1.3.6.1.6.3.15.1.1.3.0" # usmStatsUnknownUserNames
94
+ raise Error, "Unknown user name"
95
+ when "1.3.6.1.6.3.15.1.1.4.0" # usmStatsUnknownEngineIDs
96
+ raise Error, "Unknown engine ID" unless @security_parameters.must_revalidate?
97
+ when "1.3.6.1.6.3.15.1.1.5.0" # usmStatsWrongDigests
98
+ raise Error, "Authentication failure (incorrect password, community or key)"
99
+ when "1.3.6.1.6.3.15.1.1.6.0" # usmStatsDecryptionErrors
100
+ raise Error, "Decryption error"
101
+ end
102
+ end
103
+
104
+ # validate_authentication
105
+ @message_serializer.verify(stream, pdu.auth_param, pdu.security_level, security_parameters: @security_parameters)
106
+
107
+ return_pdu
89
108
  end
90
109
  end
91
110
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NETSNMP
4
- VERSION = "0.4.0"
4
+ VERSION = "0.6.0"
5
5
  end
data/sig/message.rbs CHANGED
@@ -2,8 +2,14 @@ module NETSNMP
2
2
  class Message
3
3
  prepend Loggable
4
4
 
5
+ def verify: (String stream, String auth_param, Integer? security_level, security_parameters: SecurityParameters) -> void
6
+
5
7
  def decode: (String stream, security_parameters: SecurityParameters) -> [ScopedPDU, String, Integer, Integer]
6
8
 
7
9
  def encode: (ScopedPDU pdu, security_parameters: SecurityParameters, ?engine_boots: Integer, ?engine_time: Integer) -> String
10
+
11
+ private
12
+
13
+ def authnone: (SecurityParameters::auth_protocol?) -> OpenSSL::ASN1::ASN1Data
8
14
  end
9
15
  end
data/sig/scoped_pdu.rbs CHANGED
@@ -1,6 +1,8 @@
1
1
  module NETSNMP
2
2
  class ScopedPDU < PDU
3
3
  attr_reader engine_id: String
4
+ attr_reader auth_param: String?
5
+ attr_reader security_level: Integer?
4
6
 
5
7
  private
6
8
 
@@ -4,11 +4,10 @@ module NETSNMP
4
4
 
5
5
  type security_level = :noauth | :auth_no_priv | :auth_priv | 0 | 1 | 3 | nil
6
6
 
7
- type auth_protocol = :md5 | :sha
7
+ type auth_protocol = :md5 | :sha | :sha256
8
8
  type priv_protocol = :des | :aes
9
9
 
10
10
 
11
- @auth_protocol: auth_protocol?
12
11
  @auth_password: String?
13
12
  @priv_protocol: priv_protocol?
14
13
  @priv_password: String?
@@ -18,6 +17,7 @@ module NETSNMP
18
17
  attr_reader security_level: security_level
19
18
  attr_reader username: String
20
19
  attr_reader engine_id: String
20
+ attr_reader auth_protocol: auth_protocol?
21
21
 
22
22
  def engine_id=: (String id) -> void
23
23
 
data/spec/client_spec.rb CHANGED
@@ -23,19 +23,19 @@ RSpec.describe NETSNMP::Client do
23
23
  let(:next_oid) { "1.3.6.1.2.1.1.6.0" }
24
24
  let(:walk_oid) { "1.3.6.1.2.1.1" }
25
25
  let(:set_oid) { "sysUpTime.0" } # sysUpTimeInstance
26
- let(:get_result) { "DEVICE-192.168.1.1" }
27
- let(:next_result) { "The Cloud" }
26
+ let(:get_result) { "zeus.snmplabs.com (you can change this!)" }
27
+ let(:next_result) { "San Francisco, California, United States" }
28
28
  let(:walk_result) do
29
- <<-WALK
30
- 1.3.6.1.2.1.1.1.0: Device description
31
- 1.3.6.1.2.1.1.2.0: 1.3.6.1.4.1.3454
32
- 1.3.6.1.2.1.1.3.0: Timeticks: (78171676) 9 days, 1:8:36.76
33
- 1.3.6.1.2.1.1.4.0: The Owner
34
- 1.3.6.1.2.1.1.5.0: DEVICE-192.168.1.1
35
- 1.3.6.1.2.1.1.6.0: The Cloud
36
- 1.3.6.1.2.1.1.7.0: 72
37
- 1.3.6.1.2.1.1.8.0: Timeticks: (0) 0 days, 0:0:0.0
38
- WALK
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
39
  end
40
40
  let(:set_oid_result) { 43 }
41
41
  end
@@ -52,19 +52,19 @@ RSpec.describe NETSNMP::Client do
52
52
  let(:next_oid) { "1.3.6.1.2.1.1.6.0" }
53
53
  let(:walk_oid) { "system" }
54
54
  let(:set_oid) { "sysUpTime.0" }
55
- let(:get_result) { "DEVICE-192.168.1.1" }
56
- let(:next_result) { "The Cloud" }
55
+ let(:get_result) { "zeus.snmplabs.com (you can change this!)" }
56
+ let(:next_result) { "San Francisco, California, United States" }
57
57
  let(:walk_result) do
58
- <<-WALK
59
- 1.3.6.1.2.1.1.1.0: Device description
60
- 1.3.6.1.2.1.1.2.0: 1.3.6.1.4.1.3454
61
- 1.3.6.1.2.1.1.3.0: Timeticks: (78171676) 9 days, 1:8:36.76
62
- 1.3.6.1.2.1.1.4.0: The Owner
63
- 1.3.6.1.2.1.1.5.0: DEVICE-192.168.1.1
64
- 1.3.6.1.2.1.1.6.0: The Cloud
65
- 1.3.6.1.2.1.1.7.0: 72
66
- 1.3.6.1.2.1.1.8.0: Timeticks: (0) 0 days, 0:0:0.0
67
- WALK
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
68
  end
69
69
  let(:set_oid_result) { 43 }
70
70
 
@@ -101,18 +101,18 @@ RSpec.describe NETSNMP::Client do
101
101
  let(:set_oid) { "sysUpTime.0" } # sysUpTimeInstance
102
102
  let(:walk_oid) { "1.3.6.1.2.1.1.9.1.3" }
103
103
  let(:get_result) { "tt" }
104
- let(:next_result) { "KK12" }
104
+ let(:next_result) { "KK12 (edit /etc/snmp/snmpd.conf)" }
105
105
  let(:walk_result) do
106
- <<-WALK
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
- WALK
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
116
  end
117
117
  let(:set_oid_result) { 43 }
118
118
  context "with a no auth no priv policy" do
@@ -157,6 +157,15 @@ RSpec.describe NETSNMP::Client do
157
157
  let(:protocol_options) { version_options.merge(user_options).merge(extra_options) }
158
158
  end
159
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
160
169
  end
161
170
  context "with an auth priv policy" do
162
171
  context "auth in md5, encrypting in des" do
@@ -177,6 +186,18 @@ RSpec.describe NETSNMP::Client do
177
186
  end
178
187
  it_behaves_like "an snmp client" do
179
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
180
201
  end
181
202
  end
182
203
 
@@ -4,7 +4,7 @@ require "celluloid/io"
4
4
  require_relative "../support/request_examples"
5
5
  require_relative "../support/celluloid"
6
6
 
7
- RSpec.describe "with cellulloid", type: :celluloid, if: RUBY_ENGINE == "truffleruby" do
7
+ RSpec.describe "with cellulloid", type: :celluloid do
8
8
  include CelluloidHelpers
9
9
  let(:user_options) do
10
10
  { username: "authprivmd5des", auth_password: "maplesyrup",
@@ -17,18 +17,18 @@ RSpec.describe "with cellulloid", type: :celluloid, if: RUBY_ENGINE == "truffler
17
17
  let(:set_oid) { "1.3.6.1.2.1.1.3.0" } # sysUpTimeInstance
18
18
  let(:walk_oid) { "1.3.6.1.2.1.1.9.1.3" }
19
19
  let(:get_result) { "tt" }
20
- let(:next_result) { "KK12" }
20
+ let(:next_result) { "KK12 (edit /etc/snmp/snmpd.conf)" }
21
21
  let(:walk_result) do
22
- <<-WALK
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
- WALK
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
32
  end
33
33
 
34
34
  before(:all) { Celluloid.boot }
@@ -13,6 +13,13 @@ RSpec.describe NETSNMP::SecurityParameters do
13
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
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
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
16
23
  end
17
24
 
18
25
  describe "keys" do
@@ -34,11 +41,22 @@ RSpec.describe NETSNMP::SecurityParameters do
34
41
  priv_password: password,
35
42
  engine_id: engine_id)
36
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
37
53
  it do
38
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)
39
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)
40
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)
41
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)
42
60
  end
43
61
  end
44
62
 
@@ -26,7 +26,7 @@ module CelluloidHelpers
26
26
  end
27
27
  end
28
28
 
29
- class Proxy < NETSNMP::Session::Transport
29
+ class Proxy
30
30
  MAXPDUSIZE = 0xffff + 1
31
31
 
32
32
  def initialize(host, port)
@@ -35,18 +35,22 @@ module CelluloidHelpers
35
35
  @timeout = 2
36
36
  end
37
37
 
38
- def close
39
- @socket.close
38
+ def send(payload)
39
+ @socket.send(payload, 0)
40
+ recv
40
41
  end
41
42
 
42
- private
43
-
44
- def wait(mode)
43
+ def recv(bytesize = MAXPDUSIZE)
45
44
  Celluloid.timeout(@timeout) do
46
- @socket.__send__(mode)
45
+ datagram, = @socket.recvfrom(bytesize)
46
+ datagram
47
47
  end
48
48
  rescue Celluloid::TaskTimeout
49
49
  raise Timeout::Error, "Timeout after #{@timeout} seconds"
50
50
  end
51
+
52
+ def close
53
+ @socket.close
54
+ end
51
55
  end
52
56
  end
@@ -22,8 +22,8 @@ RSpec.shared_examples "an snmp client" do
22
22
  let(:value) { subject.get({ oid: get_oid }, oid: next_oid) }
23
23
  it "returns the values for both" do
24
24
  expect(value).to be_a(Array)
25
- expect(value).to include(/#{get_result}/)
26
- expect(value).to include(/#{next_result}/)
25
+ expect(value).to include(get_result)
26
+ expect(value).to include(next_result)
27
27
  end
28
28
  end
29
29
  end
@@ -40,8 +40,17 @@ RSpec.shared_examples "an snmp client" do
40
40
  describe "#walk" do
41
41
  let(:value) { subject.walk(oid: walk_oid) }
42
42
  it "fetches the varbinds for the next oid" do
43
- values = value.map { |oid, val| "#{oid}: #{val}" }.join("\n") << "\n"
44
- expect(values).to eq(walk_result)
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
45
54
  end
46
55
  end
47
56
  end
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.0
4
+ version: 0.6.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: 2021-02-26 00:00:00.000000000 Z
11
+ date: 2021-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parslet
@@ -104,7 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
104
104
  version: '0'
105
105
  requirements:
106
106
  - net-snmp
107
- rubygems_version: 3.2.3
107
+ rubygems_version: 3.2.15
108
108
  signing_key:
109
109
  specification_version: 4
110
110
  summary: SNMP Client library