netsnmp 0.4.2 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -3
  3. data/lib/netsnmp/client.rb +5 -2
  4. data/lib/netsnmp/encryption/aes.rb +3 -3
  5. data/lib/netsnmp/encryption/des.rb +4 -4
  6. data/lib/netsnmp/errors.rb +7 -4
  7. data/lib/netsnmp/extensions.rb +12 -1
  8. data/lib/netsnmp/loggable.rb +1 -2
  9. data/lib/netsnmp/message.rb +22 -11
  10. data/lib/netsnmp/mib/parser.rb +39 -39
  11. data/lib/netsnmp/mib.rb +17 -11
  12. data/lib/netsnmp/oid.rb +6 -3
  13. data/lib/netsnmp/pdu.rb +21 -24
  14. data/lib/netsnmp/scoped_pdu.rb +7 -6
  15. data/lib/netsnmp/security_parameters.rb +62 -54
  16. data/lib/netsnmp/session.rb +16 -14
  17. data/lib/netsnmp/timeticks.rb +1 -0
  18. data/lib/netsnmp/v3_session.rb +7 -7
  19. data/lib/netsnmp/varbind.rb +19 -11
  20. data/lib/netsnmp/version.rb +1 -1
  21. data/lib/netsnmp.rb +9 -9
  22. data/sig/client.rbs +8 -2
  23. data/sig/encryption/aes.rbs +23 -0
  24. data/sig/encryption/des.rbs +23 -0
  25. data/sig/errors.rbs +13 -0
  26. data/sig/extensions.rbs +12 -0
  27. data/sig/loggable.rbs +4 -7
  28. data/sig/message.rbs +13 -1
  29. data/sig/mib.rbs +20 -6
  30. data/sig/netsnmp.rbs +5 -5
  31. data/sig/oid.rbs +1 -0
  32. data/sig/pdu.rbs +14 -15
  33. data/sig/scoped_pdu.rbs +9 -4
  34. data/sig/security_parameters.rbs +29 -23
  35. data/sig/session.rbs +21 -6
  36. data/sig/timeticks.rbs +19 -0
  37. data/sig/v3_session.rbs +3 -2
  38. data/sig/varbind.rbs +13 -2
  39. data/spec/client_spec.rb +44 -35
  40. data/spec/handlers/celluloid_spec.rb +12 -12
  41. data/spec/pdu_spec.rb +2 -1
  42. data/spec/security_parameters_spec.rb +18 -0
  43. data/spec/support/celluloid.rb +11 -7
  44. data/spec/support/request_examples.rb +13 -4
  45. metadata +8 -4
  46. data/sig/openssl.rbs +0 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8b92d6d77d4874c6d3d76ed0bbbb3358a72fd33131d9a971e41b73cb5584cfbd
4
- data.tar.gz: c2ae854a2a1f8268115291447bdd1ede0686258840aa41ba1778e41bb6c94c22
3
+ metadata.gz: aa51b06e1b2a8050faff5e0e347627356bccb462377042dc4fd303e0f41a9b90
4
+ data.tar.gz: 5666b59c831b32d4bc8d71c9bccc177c511bd5362fc2ec3c6ac87a8ce72e5df6
5
5
  SHA512:
6
- metadata.gz: f439b2ebd5777390ed34ec9bd893e943918b201f81fcfa1f9e55cb5800d1eba20712d5ebd2e9660b291383ba6d413977691842124cb97dad909283cbf1483fff
7
- data.tar.gz: fde5a6b1ec4340505e65c2f8dc49f04b6621136f4fc48c366742d8acb32e8f157f25554790fe2e64d672801307289f0b00d7f9945c561970729bc326dd5af34d
6
+ metadata.gz: 3ee1ac34cf9edfd646f9ad0a21170dcc8201a1c20632f386214864fadecff851507423454b591bde488c6d2a5e2c56b903d37c5d6c57d8318823bf9a91ebb238
7
+ data.tar.gz: efed105215b6bd3e7164221eca63581c69dfe3025a365a5484554b29abb5c8f8ae6ab27f6bc4bfcc096105ec2ff9226b4c008d899bc2de449e71bcbee8c2975a
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
 
@@ -28,12 +28,14 @@ module NETSNMP
28
28
  when Integer then version # assume the use know what he's doing
29
29
  when /v?1/ then 0
30
30
  when /v?2c?/ then 1
31
- when /v?3/, nil then 3
31
+ when /v?3/ then 3
32
+ else 3 # rubocop:disable Lint/DuplicateBranch
32
33
  end
33
34
 
34
- @retries = options.fetch(:retries, RETRIES)
35
+ @retries = options.fetch(:retries, RETRIES).to_i
35
36
  @session ||= version == 3 ? V3Session.new(**options) : Session.new(version: version, **options)
36
37
  return unless block_given?
38
+
37
39
  begin
38
40
  yield self
39
41
  ensure
@@ -155,6 +157,7 @@ module NETSNMP
155
157
  yield
156
158
  rescue Timeout::Error, IdNotInTimeWindowError => e
157
159
  raise e if retries.zero?
160
+
158
161
  retries -= 1
159
162
  retry
160
163
  end
@@ -9,7 +9,7 @@ module NETSNMP
9
9
  end
10
10
 
11
11
  def encrypt(decrypted_data, engine_boots:, engine_time:)
12
- cipher = OpenSSL::Cipher::AES128.new(:CFB)
12
+ cipher = OpenSSL::Cipher.new("aes-128-cfb")
13
13
 
14
14
  iv, salt = generate_encryption_key(engine_boots, engine_time)
15
15
 
@@ -29,7 +29,7 @@ module NETSNMP
29
29
  def decrypt(encrypted_data, salt:, engine_boots:, engine_time:)
30
30
  raise Error, "invalid priv salt received" unless !salt.empty? && (salt.length % 8).zero?
31
31
 
32
- cipher = OpenSSL::Cipher::AES128.new(:CFB)
32
+ cipher = OpenSSL::Cipher.new("aes-128-cfb")
33
33
  cipher.padding = 0
34
34
 
35
35
  iv = generate_decryption_key(engine_boots, engine_time, salt)
@@ -40,7 +40,7 @@ module NETSNMP
40
40
  decrypted_data = cipher.update(encrypted_data) + cipher.final
41
41
 
42
42
  hlen, bodylen = OpenSSL::ASN1.traverse(decrypted_data) { |_, _, x, y, *| break x, y }
43
- decrypted_data.byteslice(0, hlen + bodylen)
43
+ decrypted_data.byteslice(0, hlen + bodylen) || "".b
44
44
  end
45
45
 
46
46
  private
@@ -11,7 +11,7 @@ module NETSNMP
11
11
  end
12
12
 
13
13
  def encrypt(decrypted_data, engine_boots:, **)
14
- cipher = OpenSSL::Cipher::DES.new(:CBC)
14
+ cipher = OpenSSL::Cipher.new("des-cbc")
15
15
 
16
16
  iv, salt = generate_encryption_key(engine_boots)
17
17
 
@@ -31,7 +31,7 @@ module NETSNMP
31
31
  raise Error, "invalid priv salt received" unless (salt.length % 8).zero?
32
32
  raise Error, "invalid encrypted PDU received" unless (encrypted_data.length % 8).zero?
33
33
 
34
- cipher = OpenSSL::Cipher::DES.new(:CBC)
34
+ cipher = OpenSSL::Cipher.new("des-cbc")
35
35
  cipher.padding = 0
36
36
 
37
37
  iv = generate_decryption_key(salt)
@@ -42,7 +42,7 @@ module NETSNMP
42
42
  decrypted_data = cipher.update(encrypted_data) + cipher.final
43
43
 
44
44
  hlen, bodylen = OpenSSL::ASN1.traverse(decrypted_data) { |_, _, x, y, *| break x, y }
45
- decrypted_data.byteslice(0, hlen + bodylen)
45
+ decrypted_data.byteslice(0, hlen + bodylen) || "".b
46
46
  end
47
47
 
48
48
  private
@@ -70,7 +70,7 @@ module NETSNMP
70
70
  end
71
71
 
72
72
  def des_key
73
- @priv_key[0, 8]
73
+ @priv_key[0, 8] || "".b
74
74
  end
75
75
  end
76
76
  end
@@ -1,8 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NETSNMP
4
- Error = Class.new(StandardError)
5
- ConnectionFailed = Class.new(Error)
6
- AuthenticationFailed = Class.new(Error)
7
- IdNotInTimeWindowError = Class.new(Error)
4
+ class Error < StandardError; end
5
+
6
+ class ConnectionFailed < Error; end
7
+
8
+ class AuthenticationFailed < Error; end
9
+
10
+ class IdNotInTimeWindowError < Error; end
8
11
  end
@@ -14,6 +14,17 @@ module NETSNMP
14
14
 
15
15
  module StringExtensions
16
16
  refine(String) do
17
+ unless String.method_defined?(:delete_prefix)
18
+ def delete_prefix(prefix)
19
+ prefix = String(prefix)
20
+ if rindex(prefix, 0)
21
+ self[prefix.length..-1]
22
+ else
23
+ dup
24
+ end
25
+ end
26
+ end
27
+
17
28
  unless String.method_defined?(:match?)
18
29
  def match?(*args)
19
30
  !match(*args).nil?
@@ -107,7 +118,7 @@ module NETSNMP
107
118
  # Like a string, but it prints an hex-string version of itself
108
119
  class HexString < String
109
120
  def inspect
110
- Hexdump.dump(self, in_groups_of: 2, separator: " ")
121
+ Hexdump.dump(to_s, in_groups_of: 2, separator: " ")
111
122
  end
112
123
  end
113
124
  end
@@ -5,8 +5,7 @@ module NETSNMP
5
5
  DEBUG = ENV.key?("NETSNMP_DEBUG") ? $stderr : nil
6
6
  DEBUG_LEVEL = (ENV["NETSNMP_DEBUG"] || 1).to_i
7
7
 
8
- def initialize(debug: DEBUG, debug_level: DEBUG_LEVEL, **opts)
9
- super(**opts)
8
+ def initialize_logger(debug: DEBUG, debug_level: DEBUG_LEVEL, **)
10
9
  @debug = debug
11
10
  @debug_level = debug_level
12
11
  end
@@ -5,19 +5,20 @@ module NETSNMP
5
5
  class Message
6
6
  using ASNExtensions
7
7
 
8
- prepend Loggable
8
+ include 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)
15
14
  MSG_REPORTABLE = 4
16
15
 
17
- def initialize(**); end
16
+ def initialize(**args)
17
+ initialize_logger(**args)
18
+ end
18
19
 
19
20
  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
+ security_parameters.verify(stream.sub(auth_param, authnone(security_parameters.auth_protocol).value), auth_param, security_level: security_level)
21
22
  end
22
23
 
23
24
  # @param [String] payload of an snmp v3 message which can be decoded
@@ -66,12 +67,10 @@ module NETSNMP
66
67
  security_level: security_level)
67
68
 
68
69
  log { "received response PDU" }
69
- pdu = ScopedPDU.decode(encoded_pdu)
70
- pdu.auth_param = auth_param
71
- pdu.security_level = security_level
70
+ pdu = ScopedPDU.decode(encoded_pdu, auth_param: auth_param, security_level: security_level)
72
71
 
73
72
  log(level: 2) { pdu.to_hex }
74
- [pdu, engine_id.value, engine_boots, engine_time]
73
+ [pdu, engine_id.value.to_s, engine_boots, engine_time]
75
74
  end
76
75
 
77
76
  # @param [NETSNMP::ScopedPDU] the PDU to encode in the message
@@ -91,7 +90,7 @@ module NETSNMP
91
90
  OpenSSL::ASN1::Integer.new(engine_boots).with_label(:engine_boots),
92
91
  OpenSSL::ASN1::Integer.new(engine_time).with_label(:engine_time),
93
92
  OpenSSL::ASN1::OctetString.new(security_parameters.username).with_label(:username),
94
- AUTHNONE,
93
+ authnone(security_parameters.auth_protocol),
95
94
  salt_param
96
95
  ]).with_label(:security_params)
97
96
  log(level: 2) { sec_params.to_hex }
@@ -120,10 +119,22 @@ module NETSNMP
120
119
  log { "signing V3 message..." }
121
120
  auth_salt = OpenSSL::ASN1::OctetString.new(signature).with_label(:auth)
122
121
  log(level: 2) { auth_salt.to_hex }
123
- 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
124
124
  log { Hexdump.dump(encoded) }
125
125
  end
126
126
  encoded
127
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
128
139
  end
129
140
  end
@@ -194,7 +194,7 @@ module NETSNMP::MIB
194
194
  end
195
195
 
196
196
  rule(:module_name_capabilities) do
197
- spaced { uppercase_identifier } >> object_identifier | uppercase_identifier
197
+ (spaced { uppercase_identifier } >> object_identifier) | uppercase_identifier
198
198
  end
199
199
 
200
200
  rule(:capabilities_groups) do
@@ -368,7 +368,7 @@ module NETSNMP::MIB
368
368
  end
369
369
 
370
370
  rule(:category_id) do
371
- spaced { lowercase_identifier } >> bracketed(number) | lowercase_identifier
371
+ (spaced { lowercase_identifier } >> bracketed(number)) | lowercase_identifier
372
372
  end
373
373
 
374
374
  rule(:notification_type_clause) do
@@ -407,8 +407,8 @@ module NETSNMP::MIB
407
407
  end
408
408
 
409
409
  rule(:enterprise_part) do
410
- spaced("ENTERPRISE") >> object_identifier |
411
- spaced("ENTERPRISE") >> curly(object_identifier)
410
+ (spaced("ENTERPRISE") >> object_identifier) |
411
+ (spaced("ENTERPRISE") >> curly(object_identifier))
412
412
  end
413
413
 
414
414
  rule(:var_part) do
@@ -454,7 +454,7 @@ module NETSNMP::MIB
454
454
  end
455
455
 
456
456
  rule(:max_access_part) do
457
- spaced("MAX-ACCESS") >> access | spaced("ACCESS") >> access
457
+ (spaced("MAX-ACCESS") >> access) | (spaced("ACCESS") >> access)
458
458
  end
459
459
 
460
460
  rule(:access) { lowercase_identifier }
@@ -484,7 +484,7 @@ module NETSNMP::MIB
484
484
  end
485
485
 
486
486
  rule(:object_identifier_defval) do
487
- spaced { lowercase_identifier } >> bracketed(number) |
487
+ (spaced { lowercase_identifier } >> bracketed(number)) |
488
488
  number
489
489
  end
490
490
 
@@ -493,7 +493,7 @@ module NETSNMP::MIB
493
493
  end
494
494
 
495
495
  rule(:index_type) do
496
- spaced("IMPLIED") >> idx | idx
496
+ (spaced("IMPLIED") >> idx) | idx
497
497
  end
498
498
 
499
499
  rule(:idx) do
@@ -525,7 +525,7 @@ module NETSNMP::MIB
525
525
  rule(:sub_identifier) do
526
526
  fuzzy_lowercase_identifier |
527
527
  number |
528
- spaced { lowercase_identifier } >> bracketed(number)
528
+ (spaced { lowercase_identifier } >> bracketed(number))
529
529
  end
530
530
 
531
531
  rule(:type_declaration) do
@@ -542,12 +542,12 @@ module NETSNMP::MIB
542
542
 
543
543
  rule(:type_declaration_rhs) do
544
544
  spaced { choice_clause } |
545
- spaced { str("TEXTUAL-CONVENTION") } >>
545
+ (spaced { str("TEXTUAL-CONVENTION") } >>
546
546
  spaced { display_part }.maybe >>
547
547
  spaced("STATUS") >> spaced { status } >>
548
548
  spaced("DESCRIPTION") >> spaced { text } >>
549
549
  spaced { refer_part }.maybe >>
550
- spaced("SYNTAX") >> syntax |
550
+ spaced("SYNTAX") >> syntax) |
551
551
  syntax
552
552
  end
553
553
 
@@ -561,7 +561,7 @@ module NETSNMP::MIB
561
561
  end
562
562
 
563
563
  rule(:syntax) do
564
- object_syntax | spaced("BITS").as(:type) >> curly(named_bits)
564
+ object_syntax | (spaced("BITS").as(:type) >> curly(named_bits))
565
565
  end
566
566
 
567
567
  rule(:display_part) do
@@ -581,27 +581,27 @@ module NETSNMP::MIB
581
581
  entry_type |
582
582
  simple_syntax |
583
583
  application_syntax |
584
- type_tag >> simple_syntax |
584
+ (type_tag >> simple_syntax) |
585
585
  row.as(:value)
586
586
  end
587
587
 
588
588
  rule(:simple_syntax) do
589
- spaced { str("INTEGER").as(:type) } >> (integer_subtype | enum_spec).maybe |
590
- spaced { str("Integer32").as(:type) >> space } >> integer_subtype.maybe |
591
- spaced { str("OCTET STRING").as(:type) } >> octetstring_subtype.maybe |
592
- spaced { str("OBJECT IDENTIFIER").as(:type) } >> any_subtype |
593
- spaced { uppercase_identifier.as(:type) } >> (integer_subtype | enum_spec | octetstring_subtype)
589
+ (spaced { str("INTEGER").as(:type) } >> (integer_subtype | enum_spec).maybe) |
590
+ (spaced { str("Integer32").as(:type) >> space } >> integer_subtype.maybe) |
591
+ (spaced { str("OCTET STRING").as(:type) } >> octetstring_subtype.maybe) |
592
+ (spaced { str("OBJECT IDENTIFIER").as(:type) } >> any_subtype) |
593
+ (spaced { uppercase_identifier.as(:type) } >> (integer_subtype | enum_spec | octetstring_subtype))
594
594
  end
595
595
 
596
596
  rule(:application_syntax) do
597
- spaced { str("IpAddress").as(:type) >> space } >> any_subtype |
598
- spaced { str("NetworkAddress").as(:type) >> space } >> any_subtype |
599
- spaced { str("Counter32").as(:type) >> space } >> integer_subtype.maybe |
600
- spaced { str("Gauge32").as(:type) >> space } >> integer_subtype.maybe |
601
- spaced { str("Unsigned32").as(:type) >> space } >> integer_subtype.maybe |
602
- spaced { str("TimeTicks").as(:type) >> space } >> any_subtype |
603
- spaced { str("Opaque").as(:type) >> space } >> octetstring_subtype.maybe |
604
- spaced { str("Counter64").as(:type) >> space } >> integer_subtype.maybe
597
+ (spaced { str("IpAddress").as(:type) >> space } >> any_subtype) |
598
+ (spaced { str("NetworkAddress").as(:type) >> space } >> any_subtype) |
599
+ (spaced { str("Counter32").as(:type) >> space } >> integer_subtype.maybe) |
600
+ (spaced { str("Gauge32").as(:type) >> space } >> integer_subtype.maybe) |
601
+ (spaced { str("Unsigned32").as(:type) >> space } >> integer_subtype.maybe) |
602
+ (spaced { str("TimeTicks").as(:type) >> space } >> any_subtype) |
603
+ (spaced { str("Opaque").as(:type) >> space } >> octetstring_subtype.maybe) |
604
+ (spaced { str("Counter64").as(:type) >> space } >> integer_subtype.maybe)
605
605
  end
606
606
 
607
607
  rule(:conceptual_table) do
@@ -613,8 +613,8 @@ module NETSNMP::MIB
613
613
  end
614
614
 
615
615
  rule(:type_tag) do
616
- spaced { square_bracketed(spaced("APPLICATION") >> number.as(:application_type)) } >> spaced("IMPLICIT") |
617
- spaced { square_bracketed(spaced("UNIVERSAL") >> number.as(:universal_type)) } >> spaced("IMPLICIT")
616
+ (spaced { square_bracketed(spaced("APPLICATION") >> number.as(:application_type)) } >> spaced("IMPLICIT")) |
617
+ (spaced { square_bracketed(spaced("UNIVERSAL") >> number.as(:universal_type)) } >> spaced("IMPLICIT"))
618
618
  end
619
619
 
620
620
  rule(:sequence_items) do
@@ -628,7 +628,7 @@ module NETSNMP::MIB
628
628
  rule(:sequence_syntax) do
629
629
  str("BITS") |
630
630
  sequence_object_syntax |
631
- spaced { uppercase_identifier } >> any_subtype
631
+ (spaced { uppercase_identifier } >> any_subtype)
632
632
  end
633
633
 
634
634
  rule(:sequence_object_syntax) do
@@ -636,20 +636,20 @@ module NETSNMP::MIB
636
636
  end
637
637
 
638
638
  rule(:sequence_simple_syntax) do
639
- spaced("INTEGER") >> any_subtype |
640
- spaced("Integer32") >> any_subtype |
641
- spaced("OCTET STRING") >> any_subtype |
642
- spaced("OBJECT IDENTIFIER") >> any_subtype
639
+ (spaced("INTEGER") >> any_subtype) |
640
+ (spaced("Integer32") >> any_subtype) |
641
+ (spaced("OCTET STRING") >> any_subtype) |
642
+ (spaced("OBJECT IDENTIFIER") >> any_subtype)
643
643
  end
644
644
 
645
645
  rule(:sequence_application_syntax) do
646
- spaced { str("IpAddress") >> space } >> any_subtype |
647
- spaced { str("COUNTER32") } >> any_subtype |
648
- spaced { str("Gauge32") >> space } >> any_subtype |
649
- spaced { str("Unsigned32") >> space } >> any_subtype |
650
- spaced { str("TimeTicks") >> space } >> any_subtype |
646
+ (spaced { str("IpAddress") >> space } >> any_subtype) |
647
+ (spaced { str("COUNTER32") } >> any_subtype) |
648
+ (spaced { str("Gauge32") >> space } >> any_subtype) |
649
+ (spaced { str("Unsigned32") >> space } >> any_subtype) |
650
+ (spaced { str("TimeTicks") >> space } >> any_subtype) |
651
651
  str("Opaque") |
652
- spaced { str("Counter64") >> space } >> any_subtype
652
+ (spaced { str("Counter64") >> space } >> any_subtype)
653
653
  end
654
654
 
655
655
  rule(:row) { uppercase_identifier }
@@ -743,7 +743,7 @@ module NETSNMP::MIB
743
743
 
744
744
  rule(:text) do
745
745
  str('"') >> (
746
- str('\\') >> any | str('"').absent? >> any
746
+ (str("\\") >> any) | (str('"').absent? >> any)
747
747
  ).repeat >> str('"')
748
748
  end
749
749
  end
data/lib/netsnmp/mib.rb CHANGED
@@ -6,11 +6,13 @@ module NETSNMP
6
6
  module MIB
7
7
  using IsNumericExtensions
8
8
 
9
- OIDREGEX = /^[\d\.]*$/
9
+ OIDREGEX = /^[\d.]*$/.freeze
10
10
 
11
11
  module_function
12
12
 
13
- MIBDIRS = ENV.fetch("MIBDIRS", File.join("/usr", "share", "snmp", "mibs")).split(":")
13
+ MIBDIRS = ENV.fetch("MIBDIRS", File.join("/usr", "share", "snmp", "mibs"))
14
+ .split(":")
15
+ .flat_map { |dir| [dir, *Dir.glob(File.join(dir, "**", "*")).select(&File.method(:directory?))] }.uniq
14
16
  PARSER = Parser.new
15
17
  @parser_mutex = Mutex.new
16
18
  @modules_loaded = []
@@ -20,11 +22,13 @@ module NETSNMP
20
22
  def oid(identifier)
21
23
  prefix, *suffix = case identifier
22
24
  when Array
23
- identifier
25
+ identifier.map(&:to_s)
24
26
  else
25
- identifier.split(".", 2)
27
+ identifier.split(".", 2).map(&:to_s)
26
28
  end
27
29
 
30
+ return unless prefix
31
+
28
32
  # early exit if it's an OID already
29
33
  unless prefix.integer?
30
34
  load_defaults
@@ -33,9 +37,7 @@ module NETSNMP
33
37
  if idx
34
38
  mod = prefix[0..(idx - 1)]
35
39
  type = prefix[(idx + 2)..-1]
36
- unless module_loaded?(mod)
37
- return unless load(mod)
38
- end
40
+ return if mod && !module_loaded?(mod) && !load(mod)
39
41
  else
40
42
  type = prefix
41
43
  end
@@ -53,7 +55,7 @@ module NETSNMP
53
55
  def identifier(oid)
54
56
  @object_identifiers.select do |_, ids_oid|
55
57
  oid.start_with?(ids_oid)
56
- end.sort_by(&:size).first
58
+ end.min_by(&:size)
57
59
  end
58
60
 
59
61
  #
@@ -78,9 +80,11 @@ module NETSNMP
78
80
  end
79
81
  end
80
82
  return false unless moddir
83
+
81
84
  mod = moddir
82
85
  end
83
86
  return true if @modules_loaded.include?(mod)
87
+
84
88
  do_load(mod)
85
89
  @modules_loaded << mod
86
90
  true
@@ -129,9 +133,11 @@ module NETSNMP
129
133
  @object_identifiers[cp]
130
134
  else
131
135
  STATIC_MIB_TO_OID[cp] || begin
132
- imported_mod, = imports.find do |_, identifiers|
133
- identifiers.include?(cp)
134
- end
136
+ imported_mod, = if imports
137
+ imports.find do |_, identifiers|
138
+ identifiers.include?(cp)
139
+ end
140
+ end
135
141
 
136
142
  raise Error, "didn't find a module to import \"#{cp}\" from" unless imported_mod
137
143
 
data/lib/netsnmp/oid.rb CHANGED
@@ -4,15 +4,18 @@ module NETSNMP
4
4
  # Abstracts the OID structure
5
5
  #
6
6
  module OID
7
- using StringExtensions unless String.method_defined?(:match?)
7
+ using StringExtensions
8
8
 
9
- OIDREGEX = /^[\d\.]*$/
9
+ OIDREGEX = /^[\d.]*$/.freeze
10
10
 
11
11
  module_function
12
12
 
13
13
  def build(id)
14
14
  oid = MIB.oid(id)
15
- oid = oid[1..-1] if oid.start_with?(".")
15
+
16
+ raise Error, "no OID found for #{id}" unless oid
17
+
18
+ oid = oid.delete_prefix(".") if oid.start_with?(".")
16
19
  oid
17
20
  end
18
21
 
data/lib/netsnmp/pdu.rb CHANGED
@@ -7,21 +7,14 @@ 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
14
- def decode(der)
15
- asn_tree = case der
16
- when String
17
- OpenSSL::ASN1.decode(der)
18
- when OpenSSL::ASN1::ASN1Data
19
- der
20
- else
21
- raise "#{der}: unexpected data"
22
- end
23
-
24
- *headers, request = asn_tree.value
14
+ def decode(der, **args)
15
+ der = OpenSSL::ASN1.decode(der) if der.is_a?(String)
16
+
17
+ *headers, request = der.value
25
18
 
26
19
  version, community = headers.map(&:value)
27
20
 
@@ -37,11 +30,14 @@ module NETSNMP
37
30
  { oid: oid, value: val_asn }
38
31
  end
39
32
 
40
- new(type: type, headers: [version, community],
41
- error_status: error_status,
42
- error_index: error_index,
43
- request_id: request_id,
44
- varbinds: varbs)
33
+ new(type: type,
34
+ version: version.to_i,
35
+ community: community,
36
+ error_status: error_status.to_i,
37
+ error_index: error_index.to_i,
38
+ request_id: request_id.to_i,
39
+ varbinds: varbs,
40
+ **args)
45
41
  end
46
42
 
47
43
  # factory method that abstracts initialization of the pdu types that the library supports.
@@ -64,19 +60,19 @@ module NETSNMP
64
60
  end
65
61
  end
66
62
 
67
- attr_reader :varbinds, :type
68
-
69
- attr_reader :version, :community, :request_id
63
+ attr_reader :varbinds, :type, :version, :community, :request_id
70
64
 
71
- def initialize(type:, headers:,
65
+ def initialize(type:,
66
+ version:,
67
+ community:,
72
68
  request_id: SecureRandom.random_number(MAXREQUESTID),
73
69
  error_status: 0,
74
70
  error_index: 0,
75
71
  varbinds: [])
76
- @version, @community = headers
77
- @version = @version.to_i
72
+ @version = version.to_i
73
+ @community = community
78
74
  @error_status = error_status
79
- @error_index = error_index
75
+ @error_index = error_index
80
76
  @type = type
81
77
  @varbinds = []
82
78
  varbinds.each do |varbind|
@@ -131,6 +127,7 @@ module NETSNMP
131
127
  # http://www.tcpipguide.com/free/t_SNMPVersion2SNMPv2MessageFormats-5.htm#Table_219
132
128
  def check_error_status(status)
133
129
  return if status.zero?
130
+
134
131
  message = case status
135
132
  when 1 then "Response-PDU too big"
136
133
  when 2 then "No such name"
@@ -4,13 +4,14 @@ module NETSNMP
4
4
  class ScopedPDU < PDU
5
5
  using ASNExtensions
6
6
 
7
- attr_reader :engine_id
7
+ attr_reader :engine_id, :security_level, :auth_param
8
8
 
9
- attr_accessor :security_level, :auth_param
10
-
11
- def initialize(type:, headers:, **options)
12
- @engine_id, @context = headers
13
- super(type: type, headers: [3, nil], **options)
9
+ def initialize(type:, auth_param: "", security_level: 3, engine_id: nil, context: nil, **options)
10
+ @auth_param = auth_param
11
+ @security_level = security_level
12
+ @engine_id = engine_id
13
+ @context = context
14
+ super(type: type, version: 3, community: nil, **options)
14
15
  end
15
16
 
16
17
  private