netsnmp 0.1.8 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +60 -27
- data/lib/netsnmp.rb +3 -21
- data/lib/netsnmp/client.rb +4 -5
- data/lib/netsnmp/encryption/aes.rb +1 -3
- data/lib/netsnmp/encryption/des.rb +0 -2
- data/lib/netsnmp/errors.rb +1 -0
- data/lib/netsnmp/extensions.rb +113 -0
- data/lib/netsnmp/loggable.rb +36 -0
- data/lib/netsnmp/message.rb +70 -28
- data/lib/netsnmp/mib.rb +172 -0
- data/lib/netsnmp/mib/parser.rb +750 -0
- data/lib/netsnmp/oid.rb +7 -12
- data/lib/netsnmp/pdu.rb +23 -12
- data/lib/netsnmp/scoped_pdu.rb +8 -2
- data/lib/netsnmp/security_parameters.rb +22 -14
- data/lib/netsnmp/session.rb +14 -16
- data/lib/netsnmp/v3_session.rb +21 -9
- data/lib/netsnmp/varbind.rb +27 -22
- data/lib/netsnmp/version.rb +1 -1
- data/sig/client.rbs +24 -0
- data/sig/loggable.rbs +16 -0
- data/sig/message.rbs +9 -0
- data/sig/mib.rbs +21 -0
- data/sig/mib/parser.rbs +7 -0
- data/sig/netsnmp.rbs +19 -0
- data/sig/oid.rbs +18 -0
- data/sig/openssl.rbs +20 -0
- data/sig/pdu.rbs +48 -0
- data/sig/scoped_pdu.rbs +15 -0
- data/sig/security_parameters.rbs +58 -0
- data/sig/session.rbs +38 -0
- data/sig/timeticks.rbs +7 -0
- data/sig/v3_session.rbs +21 -0
- data/sig/varbind.rbs +30 -0
- data/spec/client_spec.rb +26 -8
- data/spec/handlers/celluloid_spec.rb +4 -3
- data/spec/mib_spec.rb +13 -0
- data/spec/session_spec.rb +2 -2
- data/spec/spec_helper.rb +9 -5
- data/spec/support/request_examples.rb +2 -2
- data/spec/v3_session_spec.rb +4 -4
- data/spec/varbind_spec.rb +5 -3
- metadata +31 -71
- data/.coveralls.yml +0 -1
- data/.gitignore +0 -14
- data/.rspec +0 -2
- data/.rubocop.yml +0 -11
- data/.rubocop_todo.yml +0 -69
- data/.travis.yml +0 -28
- data/Gemfile +0 -22
- data/Rakefile +0 -30
- data/netsnmp.gemspec +0 -31
- data/spec/support/Dockerfile +0 -14
- data/spec/support/specs.sh +0 -51
- data/spec/support/stop_docker.sh +0 -5
    
        data/lib/netsnmp/message.rb
    CHANGED
    
    | @@ -2,41 +2,71 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module NETSNMP
         | 
| 4 4 | 
             
              # Factory for the SNMP v3 Message format
         | 
| 5 | 
            -
               | 
| 6 | 
            -
                 | 
| 5 | 
            +
              class Message
         | 
| 6 | 
            +
                using ASNExtensions
         | 
| 7 7 |  | 
| 8 | 
            -
                 | 
| 8 | 
            +
                prepend Loggable
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                AUTHNONE               = OpenSSL::ASN1::OctetString.new("\x00" * 12).with_label(:auth_mask)
         | 
| 9 11 | 
             
                PRIVNONE               = OpenSSL::ASN1::OctetString.new("")
         | 
| 10 | 
            -
                MSG_MAX_SIZE       = OpenSSL::ASN1::Integer.new(65507)
         | 
| 11 | 
            -
                MSG_SECURITY_MODEL = OpenSSL::ASN1::Integer.new(3) # usmSecurityModel
         | 
| 12 | 
            -
                MSG_VERSION        = OpenSSL::ASN1::Integer.new(3)
         | 
| 12 | 
            +
                MSG_MAX_SIZE       = OpenSSL::ASN1::Integer.new(65507).with_label(:max_message_size)
         | 
| 13 | 
            +
                MSG_SECURITY_MODEL = OpenSSL::ASN1::Integer.new(3).with_label(:security_model) # usmSecurityModel
         | 
| 14 | 
            +
                MSG_VERSION        = OpenSSL::ASN1::Integer.new(3).with_label(:message_version)
         | 
| 13 15 | 
             
                MSG_REPORTABLE     = 4
         | 
| 14 16 |  | 
| 17 | 
            +
                def initialize(**); end
         | 
| 18 | 
            +
             | 
| 15 19 | 
             
                # @param [String] payload of an snmp v3 message which can be decoded
         | 
| 16 20 | 
             
                # @param [NETSMP::SecurityParameters, #decode] security_parameters knowns how to decode the stream
         | 
| 17 21 | 
             
                #
         | 
| 18 22 | 
             
                # @return [NETSNMP::ScopedPDU] the decoded PDU
         | 
| 19 23 | 
             
                #
         | 
| 20 24 | 
             
                def decode(stream, security_parameters:)
         | 
| 21 | 
            -
                   | 
| 22 | 
            -
                   | 
| 25 | 
            +
                  log { "received encoded V3 message" }
         | 
| 26 | 
            +
                  log { Hexdump.dump(stream) }
         | 
| 27 | 
            +
                  asn_tree = OpenSSL::ASN1.decode(stream).with_label(:v3_message)
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  version, headers, sec_params, pdu_payload = asn_tree.value
         | 
| 30 | 
            +
                  version.with_label(:message_version)
         | 
| 31 | 
            +
                  headers.with_label(:headers)
         | 
| 32 | 
            +
                  sec_params.with_label(:security_params)
         | 
| 33 | 
            +
                  pdu_payload.with_label(:pdu)
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  _, _, message_flags, = headers.value
         | 
| 23 36 |  | 
| 24 | 
            -
                   | 
| 37 | 
            +
                  # get last byte
         | 
| 38 | 
            +
                  # discard the left-outermost bits and keep the remaining two
         | 
| 39 | 
            +
                  security_level = message_flags.with_label(:message_flags).value.unpack("C*").last & 3
         | 
| 25 40 |  | 
| 26 | 
            -
                   | 
| 41 | 
            +
                  sec_params_asn = OpenSSL::ASN1.decode(sec_params.value).with_label(:security_params)
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  engine_id, engine_boots, engine_time, username, auth_param, priv_param = sec_params_asn.value
         | 
| 44 | 
            +
                  engine_id.with_label(:engine_id)
         | 
| 45 | 
            +
                  engine_boots.with_label(:engine_boots)
         | 
| 46 | 
            +
                  engine_time.with_label(:engine_time)
         | 
| 47 | 
            +
                  username.with_label(:username)
         | 
| 48 | 
            +
                  auth_param.with_label(:auth_param)
         | 
| 49 | 
            +
                  priv_param.with_label(:priv_param)
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  log(level: 2) { asn_tree.to_hex }
         | 
| 52 | 
            +
                  log(level: 2) { sec_params_asn.to_hex }
         | 
| 27 53 |  | 
| 28 54 | 
             
                  # validate_authentication
         | 
| 29 | 
            -
                   | 
| 55 | 
            +
                  auth_param = auth_param.value
         | 
| 56 | 
            +
                  security_parameters.verify(stream.sub(auth_param, AUTHNONE.value), auth_param, security_level: security_level)
         | 
| 30 57 |  | 
| 31 | 
            -
                  engine_boots = engine_boots.to_i
         | 
| 32 | 
            -
                  engine_time = engine_time.to_i
         | 
| 58 | 
            +
                  engine_boots = engine_boots.value.to_i
         | 
| 59 | 
            +
                  engine_time = engine_time.value.to_i
         | 
| 33 60 |  | 
| 34 | 
            -
                  encoded_pdu = security_parameters.decode(pdu_payload, salt: priv_param,
         | 
| 61 | 
            +
                  encoded_pdu = security_parameters.decode(pdu_payload, salt: priv_param.value,
         | 
| 35 62 | 
             
                                                                        engine_boots: engine_boots,
         | 
| 36 | 
            -
                                                                        engine_time: engine_time | 
| 63 | 
            +
                                                                        engine_time: engine_time,
         | 
| 64 | 
            +
                                                                        security_level: security_level)
         | 
| 37 65 |  | 
| 66 | 
            +
                  log { "received response PDU" }
         | 
| 38 67 | 
             
                  pdu = ScopedPDU.decode(encoded_pdu)
         | 
| 39 | 
            -
                   | 
| 68 | 
            +
                  log(level: 2) { pdu.to_hex }
         | 
| 69 | 
            +
                  [pdu, engine_id.value, engine_boots, engine_time]
         | 
| 40 70 | 
             
                end
         | 
| 41 71 |  | 
| 42 72 | 
             
                # @param [NETSNMP::ScopedPDU] the PDU to encode in the message
         | 
| @@ -45,36 +75,48 @@ module NETSNMP | |
| 45 75 | 
             
                # @return [String] the byte representation of an SNMP v3 Message
         | 
| 46 76 | 
             
                #
         | 
| 47 77 | 
             
                def encode(pdu, security_parameters:, engine_boots: 0, engine_time: 0)
         | 
| 78 | 
            +
                  log(level: 2) { pdu.to_hex }
         | 
| 79 | 
            +
                  log { "encoding PDU in V3 message..." }
         | 
| 48 80 | 
             
                  scoped_pdu, salt_param = security_parameters.encode(pdu, salt: PRIVNONE,
         | 
| 49 81 | 
             
                                                                           engine_boots: engine_boots,
         | 
| 50 82 | 
             
                                                                           engine_time: engine_time)
         | 
| 51 83 |  | 
| 52 84 | 
             
                  sec_params = OpenSSL::ASN1::Sequence.new([
         | 
| 53 | 
            -
                                                             OpenSSL::ASN1::OctetString.new(security_parameters.engine_id),
         | 
| 54 | 
            -
                                                             OpenSSL::ASN1::Integer.new(engine_boots),
         | 
| 55 | 
            -
                                                             OpenSSL::ASN1::Integer.new(engine_time),
         | 
| 56 | 
            -
                                                             OpenSSL::ASN1::OctetString.new(security_parameters.username),
         | 
| 85 | 
            +
                                                             OpenSSL::ASN1::OctetString.new(security_parameters.engine_id).with_label(:engine_id),
         | 
| 86 | 
            +
                                                             OpenSSL::ASN1::Integer.new(engine_boots).with_label(:engine_boots),
         | 
| 87 | 
            +
                                                             OpenSSL::ASN1::Integer.new(engine_time).with_label(:engine_time),
         | 
| 88 | 
            +
                                                             OpenSSL::ASN1::OctetString.new(security_parameters.username).with_label(:username),
         | 
| 57 89 | 
             
                                                             AUTHNONE,
         | 
| 58 90 | 
             
                                                             salt_param
         | 
| 59 | 
            -
                                                           ])
         | 
| 91 | 
            +
                                                           ]).with_label(:security_params)
         | 
| 92 | 
            +
                  log(level: 2) { sec_params.to_hex }
         | 
| 93 | 
            +
             | 
| 60 94 | 
             
                  message_flags = MSG_REPORTABLE | security_parameters.security_level
         | 
| 61 | 
            -
                  message_id    = OpenSSL::ASN1::Integer.new(SecureRandom.random_number(2147483647))
         | 
| 95 | 
            +
                  message_id    = OpenSSL::ASN1::Integer.new(SecureRandom.random_number(2147483647)).with_label(:message_id)
         | 
| 62 96 | 
             
                  headers = OpenSSL::ASN1::Sequence.new([
         | 
| 63 | 
            -
                                                          message_id, | 
| 64 | 
            -
                                                           | 
| 97 | 
            +
                                                          message_id,
         | 
| 98 | 
            +
                                                          MSG_MAX_SIZE,
         | 
| 99 | 
            +
                                                          OpenSSL::ASN1::OctetString.new([String(message_flags)].pack("h*")).with_label(:message_flags),
         | 
| 65 100 | 
             
                                                          MSG_SECURITY_MODEL
         | 
| 66 | 
            -
                                                        ])
         | 
| 101 | 
            +
                                                        ]).with_label(:headers)
         | 
| 67 102 |  | 
| 68 103 | 
             
                  encoded = OpenSSL::ASN1::Sequence([
         | 
| 69 104 | 
             
                                                      MSG_VERSION,
         | 
| 70 105 | 
             
                                                      headers,
         | 
| 71 | 
            -
                                                      OpenSSL::ASN1::OctetString.new(sec_params.to_der),
         | 
| 106 | 
            +
                                                      OpenSSL::ASN1::OctetString.new(sec_params.to_der).with_label(:security_params),
         | 
| 72 107 | 
             
                                                      scoped_pdu
         | 
| 73 | 
            -
                                                    ]). | 
| 108 | 
            +
                                                    ]).with_label(:v3_message)
         | 
| 109 | 
            +
                  log(level: 2) { encoded.to_hex }
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                  encoded = encoded.to_der
         | 
| 112 | 
            +
                  log { Hexdump.dump(encoded) }
         | 
| 74 113 | 
             
                  signature = security_parameters.sign(encoded)
         | 
| 75 114 | 
             
                  if signature
         | 
| 76 | 
            -
                     | 
| 115 | 
            +
                    log { "signing V3 message..." }
         | 
| 116 | 
            +
                    auth_salt = OpenSSL::ASN1::OctetString.new(signature).with_label(:auth)
         | 
| 117 | 
            +
                    log(level: 2) { auth_salt.to_hex }
         | 
| 77 118 | 
             
                    encoded.sub!(AUTHNONE.to_der, auth_salt.to_der)
         | 
| 119 | 
            +
                    log { Hexdump.dump(encoded) }
         | 
| 78 120 | 
             
                  end
         | 
| 79 121 | 
             
                  encoded
         | 
| 80 122 | 
             
                end
         | 
    
        data/lib/netsnmp/mib.rb
    ADDED
    
    | @@ -0,0 +1,172 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative "mib/parser"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module NETSNMP
         | 
| 6 | 
            +
              module MIB
         | 
| 7 | 
            +
                using IsNumericExtensions
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                OIDREGEX = /^[\d\.]*$/
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                module_function
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                MIBDIRS = ENV.fetch("MIBDIRS", File.join("/usr", "share", "snmp", "mibs")).split(":")
         | 
| 14 | 
            +
                PARSER = Parser.new
         | 
| 15 | 
            +
                @parser_mutex = Mutex.new
         | 
| 16 | 
            +
                @modules_loaded = []
         | 
| 17 | 
            +
                @object_identifiers = {}
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                # Translates na identifier, such as "sysDescr", into an OID
         | 
| 20 | 
            +
                def oid(identifier)
         | 
| 21 | 
            +
                  prefix, *suffix = case identifier
         | 
| 22 | 
            +
                                    when Array
         | 
| 23 | 
            +
                                      identifier
         | 
| 24 | 
            +
                                    else
         | 
| 25 | 
            +
                                      identifier.split(".", 2)
         | 
| 26 | 
            +
                                    end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  # early exit if it's an OID already
         | 
| 29 | 
            +
                  unless prefix.integer?
         | 
| 30 | 
            +
                    load_defaults
         | 
| 31 | 
            +
                    # load module if need be
         | 
| 32 | 
            +
                    idx = prefix.index("::")
         | 
| 33 | 
            +
                    if idx
         | 
| 34 | 
            +
                      mod = prefix[0..(idx - 1)]
         | 
| 35 | 
            +
                      type = prefix[(idx + 2)..-1]
         | 
| 36 | 
            +
                      unless module_loaded?(mod)
         | 
| 37 | 
            +
                        return unless load(mod)
         | 
| 38 | 
            +
                      end
         | 
| 39 | 
            +
                    else
         | 
| 40 | 
            +
                      type = prefix
         | 
| 41 | 
            +
                    end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                    return if type.nil? || type.empty?
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    prefix = @object_identifiers[type] ||
         | 
| 46 | 
            +
                             raise(Error, "can't convert #{type} to OID")
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  [prefix, *suffix].join(".")
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                def identifier(oid)
         | 
| 54 | 
            +
                  @object_identifiers.select do |_, ids_oid|
         | 
| 55 | 
            +
                    oid.start_with?(ids_oid)
         | 
| 56 | 
            +
                  end.sort_by(&:size).first
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                #
         | 
| 60 | 
            +
                # Loads a MIB. Can be called multiple times, as it'll load it once.
         | 
| 61 | 
            +
                #
         | 
| 62 | 
            +
                # Accepts the MIB name in several ways:
         | 
| 63 | 
            +
                #
         | 
| 64 | 
            +
                #     MIB.load("SNMPv2-MIB")
         | 
| 65 | 
            +
                #     MIB.load("SNMPv2-MIB.txt")
         | 
| 66 | 
            +
                #     MIB.load("/path/to/SNMPv2-MIB.txt")
         | 
| 67 | 
            +
                #
         | 
| 68 | 
            +
                def load(mod)
         | 
| 69 | 
            +
                  unless File.file?(mod)
         | 
| 70 | 
            +
                    moddir = nil
         | 
| 71 | 
            +
                    MIBDIRS.each do |mibdir|
         | 
| 72 | 
            +
                      if File.exist?(File.join(mibdir, mod))
         | 
| 73 | 
            +
                        moddir = File.join(mibdir, mod)
         | 
| 74 | 
            +
                        break
         | 
| 75 | 
            +
                      elsif File.extname(mod).empty? && File.exist?(File.join(mibdir, "#{mod}.txt"))
         | 
| 76 | 
            +
                        moddir = File.join(mibdir, "#{mod}.txt")
         | 
| 77 | 
            +
                        break
         | 
| 78 | 
            +
                      end
         | 
| 79 | 
            +
                    end
         | 
| 80 | 
            +
                    return false unless moddir
         | 
| 81 | 
            +
                    mod = moddir
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
                  return true if @modules_loaded.include?(mod)
         | 
| 84 | 
            +
                  do_load(mod)
         | 
| 85 | 
            +
                  @modules_loaded << mod
         | 
| 86 | 
            +
                  true
         | 
| 87 | 
            +
                end
         | 
| 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 | 
            +
             | 
| 97 | 
            +
                TYPES = ["OBJECT IDENTIFIER", "OBJECT-TYPE", "MODULE-IDENTITY"].freeze
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                STATIC_MIB_TO_OID = {
         | 
| 100 | 
            +
                  "iso" => "1"
         | 
| 101 | 
            +
                }.freeze
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                #
         | 
| 104 | 
            +
                # Loads the MIB all the time, where +mod+ is the absolute path to the MIB.
         | 
| 105 | 
            +
                #
         | 
| 106 | 
            +
                def do_load(mod)
         | 
| 107 | 
            +
                  data = @parser_mutex.synchronize { PARSER.parse(File.read(mod)) }
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                  imports = load_imports(data[:imports])
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                  declarations = Hash[
         | 
| 112 | 
            +
                    data[:declarations].reject { |dec| !dec.key?(:name) || !TYPES.include?(dec[:type]) }
         | 
| 113 | 
            +
                                       .map { |dec| [String(dec[:name]), String(dec[:value]).split(/ +/)] }
         | 
| 114 | 
            +
                  ]
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                  declarations.each do |nme, value|
         | 
| 117 | 
            +
                    store_oid_in_identifiers(nme, value, imports: imports, declarations: declarations)
         | 
| 118 | 
            +
                  end
         | 
| 119 | 
            +
                end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                def store_oid_in_identifiers(nme, value, imports:, declarations:)
         | 
| 122 | 
            +
                  oid = value.flat_map do |cp|
         | 
| 123 | 
            +
                    if cp.integer?
         | 
| 124 | 
            +
                      cp
         | 
| 125 | 
            +
                    elsif @object_identifiers.key?(cp)
         | 
| 126 | 
            +
                      @object_identifiers[cp]
         | 
| 127 | 
            +
                    elsif declarations.key?(cp)
         | 
| 128 | 
            +
                      store_oid_in_identifiers(cp, declarations[cp], imports: imports, declarations: declarations)
         | 
| 129 | 
            +
                      @object_identifiers[cp]
         | 
| 130 | 
            +
                    else
         | 
| 131 | 
            +
                      STATIC_MIB_TO_OID[cp] || begin
         | 
| 132 | 
            +
                        imported_mod, = imports.find do |_, identifiers|
         | 
| 133 | 
            +
                          identifiers.include?(cp)
         | 
| 134 | 
            +
                        end
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                        raise Error, "didn't find a module to import \"#{cp}\" from" unless imported_mod
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                        load(imported_mod)
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                        @object_identifiers[cp]
         | 
| 141 | 
            +
                      end
         | 
| 142 | 
            +
                    end
         | 
| 143 | 
            +
                  end.join(".")
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                  @object_identifiers[nme] = oid
         | 
| 146 | 
            +
                end
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                #
         | 
| 149 | 
            +
                # Reformats the import lists into an hash indexed by module name, to a list of
         | 
| 150 | 
            +
                # imported names
         | 
| 151 | 
            +
                #
         | 
| 152 | 
            +
                def load_imports(imports)
         | 
| 153 | 
            +
                  return unless imports
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                  imports = [imports] unless imports.respond_to?(:to_ary)
         | 
| 156 | 
            +
                  imports.each_with_object({}) do |import, imp|
         | 
| 157 | 
            +
                    imp[String(import[:name])] = case import[:ids]
         | 
| 158 | 
            +
                                                 when Hash
         | 
| 159 | 
            +
                                                   [String(import[:ids][:name])]
         | 
| 160 | 
            +
                                                 else
         | 
| 161 | 
            +
                                                   import[:ids].map { |id| String(id[:name]) }
         | 
| 162 | 
            +
                                                 end
         | 
| 163 | 
            +
                  end
         | 
| 164 | 
            +
                end
         | 
| 165 | 
            +
             | 
| 166 | 
            +
                def load_defaults
         | 
| 167 | 
            +
                  # loading the defaults MIBS
         | 
| 168 | 
            +
                  load("SNMPv2-MIB")
         | 
| 169 | 
            +
                  load("IF-MIB")
         | 
| 170 | 
            +
                end
         | 
| 171 | 
            +
              end
         | 
| 172 | 
            +
            end
         | 
| @@ -0,0 +1,750 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "parslet"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module NETSNMP::MIB
         | 
| 6 | 
            +
              class Parser < Parslet::Parser
         | 
| 7 | 
            +
                root :mibfile
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def spaced(character = nil)
         | 
| 10 | 
            +
                  if character.nil? && block_given?
         | 
| 11 | 
            +
                    yield >> space.repeat
         | 
| 12 | 
            +
                  else
         | 
| 13 | 
            +
                    str(character) >> space.repeat
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def curly(atom)
         | 
| 18 | 
            +
                  str("{") >> space.repeat >> atom >> space.repeat >> str("}")
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def bracketed(atom)
         | 
| 22 | 
            +
                  str("(") >> space.repeat >> atom >> space.repeat >> str(")")
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def square_bracketed(atom)
         | 
| 26 | 
            +
                  str("[") >> space.repeat >> atom >> space.repeat >> str("]")
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                def with_separator(atom, separator = nil)
         | 
| 30 | 
            +
                  if separator
         | 
| 31 | 
            +
                    sep = if separator.is_a?(String)
         | 
| 32 | 
            +
                            space.repeat >> str(separator) >> space.repeat
         | 
| 33 | 
            +
                          else
         | 
| 34 | 
            +
                            separator
         | 
| 35 | 
            +
                          end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    atom >> (sep >> atom).repeat
         | 
| 38 | 
            +
                  else
         | 
| 39 | 
            +
                    atom >> (space.repeat >> atom).repeat
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                rule(:mibfile) do
         | 
| 44 | 
            +
                  space.repeat >> modules.maybe
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                rule(:modules) do
         | 
| 48 | 
            +
                  with_separator(mod)
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                rule(:mod) do
         | 
| 52 | 
            +
                  spaced { module_name.as(:name) } >> module_oid >>
         | 
| 53 | 
            +
                    spaced("DEFINITIONS") >> colon_colon_part >>
         | 
| 54 | 
            +
                    spaced("BEGIN") >>
         | 
| 55 | 
            +
                    exports_part.as(:exports) >>
         | 
| 56 | 
            +
                    linkage_part.as(:imports) >>
         | 
| 57 | 
            +
                    declaration_part.as(:declarations) >>
         | 
| 58 | 
            +
                    spaced("END")
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                rule(:module_name) { uppercase_identifier }
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                rule(:module_oid) do
         | 
| 64 | 
            +
                  spaced { curly(object_identifier) }.maybe
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                rule(:declaration_part) do
         | 
| 68 | 
            +
                  spaced { declarations }.maybe
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                rule(:exports_part) do
         | 
| 72 | 
            +
                  spaced { exports_clause }.maybe
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                rule(:exports_clause) do
         | 
| 76 | 
            +
                  spaced("EXPORTS") >> spaced { import_identifiers } >> str(";")
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                rule(:linkage_part) do
         | 
| 80 | 
            +
                  spaced { linkage_clause }.maybe
         | 
| 81 | 
            +
                end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                rule(:linkage_clause) do
         | 
| 84 | 
            +
                  spaced("IMPORTS") >> spaced { import_part } >> str(";")
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                rule(:import_part) do
         | 
| 88 | 
            +
                  imports.maybe
         | 
| 89 | 
            +
                end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                rule(:imports) { with_separator(import) }
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                rule(:import) do
         | 
| 94 | 
            +
                  spaced { import_identifiers.as(:ids) } >> spaced("FROM") >> module_name.as(:name)
         | 
| 95 | 
            +
                end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                rule(:import_identifiers) { with_separator(import_identifier.as(:name), ",") }
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                rule(:import_identifier) do
         | 
| 100 | 
            +
                  lowercase_identifier | uppercase_identifier | imported_keyword
         | 
| 101 | 
            +
                end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                rule(:imported_keyword) do
         | 
| 104 | 
            +
                  imported_smi_keyword |
         | 
| 105 | 
            +
                    str("BITS") |
         | 
| 106 | 
            +
                    str("Integer32")  |
         | 
| 107 | 
            +
                    str("IpAddress")  |
         | 
| 108 | 
            +
                    str("MANDATORY-GROUPS") |
         | 
| 109 | 
            +
                    str("MODULE-COMPLIANCE") |
         | 
| 110 | 
            +
                    str("MODULE-IDENTITY") |
         | 
| 111 | 
            +
                    str("OBJECT-GROUP") |
         | 
| 112 | 
            +
                    str("OBJECT-IDENTITY") |
         | 
| 113 | 
            +
                    str("OBJECT-TYPE") |
         | 
| 114 | 
            +
                    str("Opaque") |
         | 
| 115 | 
            +
                    str("TEXTUAL-CONVENTION") |
         | 
| 116 | 
            +
                    str("TimeTicks") |
         | 
| 117 | 
            +
                    str("Unsigned32")
         | 
| 118 | 
            +
                end
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                rule(:imported_smi_keyword) do
         | 
| 121 | 
            +
                  str("AGENT-CAPABILITIES") |
         | 
| 122 | 
            +
                    str("Counter32") |
         | 
| 123 | 
            +
                    str("Counter64") |
         | 
| 124 | 
            +
                    str("Gauge32") |
         | 
| 125 | 
            +
                    str("NOTIFICATION-GROUP") |
         | 
| 126 | 
            +
                    str("NOTIFICATION-TYPE") |
         | 
| 127 | 
            +
                    str("TRAP-TYPE")
         | 
| 128 | 
            +
                end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                rule(:declarations) do
         | 
| 131 | 
            +
                  with_separator(declaration)
         | 
| 132 | 
            +
                end
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                rule(:declaration) do
         | 
| 135 | 
            +
                  type_declaration |
         | 
| 136 | 
            +
                    value_declaration |
         | 
| 137 | 
            +
                    object_identity_clause |
         | 
| 138 | 
            +
                    object_type_clause |
         | 
| 139 | 
            +
                    traptype_clause |
         | 
| 140 | 
            +
                    notification_type_clause |
         | 
| 141 | 
            +
                    module_identity_clause |
         | 
| 142 | 
            +
                    module_compliance_clause |
         | 
| 143 | 
            +
                    object_group_clause |
         | 
| 144 | 
            +
                    notification_group_clause |
         | 
| 145 | 
            +
                    agent_capabilities_clause |
         | 
| 146 | 
            +
                    macro_clause
         | 
| 147 | 
            +
                end
         | 
| 148 | 
            +
             | 
| 149 | 
            +
                rule(:macro_clause) do
         | 
| 150 | 
            +
                  spaced { macro_name.as(:name) } >> spaced { str("MACRO").as(:type) } >> colon_colon_part >>
         | 
| 151 | 
            +
                    spaced("BEGIN") >>
         | 
| 152 | 
            +
                    # ignoring macro clauses
         | 
| 153 | 
            +
                    match("^(?!END)").repeat >>
         | 
| 154 | 
            +
                    spaced("END")
         | 
| 155 | 
            +
                end
         | 
| 156 | 
            +
             | 
| 157 | 
            +
                rule(:macro_name) do
         | 
| 158 | 
            +
                  str("MODULE-IDENTITY") |
         | 
| 159 | 
            +
                    str("OBJECT-TYPE") |
         | 
| 160 | 
            +
                    str("TRAP-TYPE") |
         | 
| 161 | 
            +
                    str("NOTIFICATION-TYPE") |
         | 
| 162 | 
            +
                    str("OBJECT-IDENTITY") |
         | 
| 163 | 
            +
                    str("TEXTUAL-CONVENTION") |
         | 
| 164 | 
            +
                    str("OBJECT-GROUP") |
         | 
| 165 | 
            +
                    str("NOTIFICATION-GROUP") |
         | 
| 166 | 
            +
                    str("MODULE-COMPLIANCE") |
         | 
| 167 | 
            +
                    str("AGENT-CAPABILITIES")
         | 
| 168 | 
            +
                end
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                rule(:agent_capabilities_clause) do
         | 
| 171 | 
            +
                  spaced { lowercase_identifier.as(:name) } >>
         | 
| 172 | 
            +
                    spaced { str("AGENT-CAPABILITIES").as(:type) } >>
         | 
| 173 | 
            +
                    spaced("PRODUCT-RELEASE") >> spaced { text } >>
         | 
| 174 | 
            +
                    spaced("STATUS") >> spaced { status } >>
         | 
| 175 | 
            +
                    spaced("DESCRIPTION") >> spaced { text } >>
         | 
| 176 | 
            +
                    spaced { refer_part }.maybe >>
         | 
| 177 | 
            +
                    spaced { module_part_capabilities }.maybe >>
         | 
| 178 | 
            +
                    colon_colon_part >> curly(object_identifier)
         | 
| 179 | 
            +
                end
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                rule(:module_part_capabilities) do
         | 
| 182 | 
            +
                  modules_capabilities
         | 
| 183 | 
            +
                end
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                rule(:modules_capabilities) do
         | 
| 186 | 
            +
                  with_separator(module_capabilities)
         | 
| 187 | 
            +
                end
         | 
| 188 | 
            +
             | 
| 189 | 
            +
                rule(:module_capabilities) do
         | 
| 190 | 
            +
                  spaced("SUPPORTS") >>
         | 
| 191 | 
            +
                    module_name_capabilities >>
         | 
| 192 | 
            +
                    spaced("INCLUDES") >> curly(capabilities_groups) >>
         | 
| 193 | 
            +
                    spaced { variation_part }.maybe
         | 
| 194 | 
            +
                end
         | 
| 195 | 
            +
             | 
| 196 | 
            +
                rule(:module_name_capabilities) do
         | 
| 197 | 
            +
                  spaced { uppercase_identifier } >> object_identifier | uppercase_identifier
         | 
| 198 | 
            +
                end
         | 
| 199 | 
            +
             | 
| 200 | 
            +
                rule(:capabilities_groups) do
         | 
| 201 | 
            +
                  with_separator(capabilities_group)
         | 
| 202 | 
            +
                end
         | 
| 203 | 
            +
             | 
| 204 | 
            +
                rule(:capabilities_group) { objectIdentifier }
         | 
| 205 | 
            +
             | 
| 206 | 
            +
                rule(:variation_part) do
         | 
| 207 | 
            +
                  variations
         | 
| 208 | 
            +
                end
         | 
| 209 | 
            +
             | 
| 210 | 
            +
                rule(:variations) do
         | 
| 211 | 
            +
                  with_separator(variation)
         | 
| 212 | 
            +
                end
         | 
| 213 | 
            +
             | 
| 214 | 
            +
                rule(:variation) do
         | 
| 215 | 
            +
                  spaced("VARIATION") >> object_identifier >>
         | 
| 216 | 
            +
                    spaced { syntax_part }.maybe >>
         | 
| 217 | 
            +
                    spaced { write_syntax_part } >>
         | 
| 218 | 
            +
                    spaced { variation_access_part }.maybe >>
         | 
| 219 | 
            +
                    spaced { creation_part }.maybe >>
         | 
| 220 | 
            +
                    spaced { def_val_part }.maybe >>
         | 
| 221 | 
            +
                    spaced("DESCRIPTION") >> text
         | 
| 222 | 
            +
                end
         | 
| 223 | 
            +
             | 
| 224 | 
            +
                rule(:variation_access_part) do
         | 
| 225 | 
            +
                  spaced("ACCESS") >> variation_access
         | 
| 226 | 
            +
                end
         | 
| 227 | 
            +
             | 
| 228 | 
            +
                rule(:variation_access) { lowercase_identifier }
         | 
| 229 | 
            +
             | 
| 230 | 
            +
                rule(:creation_part) do
         | 
| 231 | 
            +
                  spaced("CREATION-REQUIRES") >> curly(cells)
         | 
| 232 | 
            +
                end
         | 
| 233 | 
            +
             | 
| 234 | 
            +
                rule(:cells) { with_separator(cell, ",") }
         | 
| 235 | 
            +
             | 
| 236 | 
            +
                rule(:cell) { object_identifier }
         | 
| 237 | 
            +
             | 
| 238 | 
            +
                rule(:notification_group_clause) do
         | 
| 239 | 
            +
                  spaced { lowercase_identifier.as(:name) } >>
         | 
| 240 | 
            +
                    spaced { str("NOTIFICATION-GROUP").as(:type) } >>
         | 
| 241 | 
            +
                    spaced { notifications_part } >>
         | 
| 242 | 
            +
                    spaced("STATUS") >> spaced { status } >>
         | 
| 243 | 
            +
                    spaced("DESCRIPTION") >> spaced { text } >>
         | 
| 244 | 
            +
                    spaced { refer_part }.maybe >>
         | 
| 245 | 
            +
                    colon_colon_part >> curly(object_identifier)
         | 
| 246 | 
            +
                end
         | 
| 247 | 
            +
             | 
| 248 | 
            +
                rule(:notifications_part) do
         | 
| 249 | 
            +
                  spaced("NOTIFICATIONS") >> curly(notifications)
         | 
| 250 | 
            +
                end
         | 
| 251 | 
            +
             | 
| 252 | 
            +
                rule(:notifications) do
         | 
| 253 | 
            +
                  with_separator(notification, ",")
         | 
| 254 | 
            +
                end
         | 
| 255 | 
            +
             | 
| 256 | 
            +
                rule(:notification) do
         | 
| 257 | 
            +
                  notification_name
         | 
| 258 | 
            +
                end
         | 
| 259 | 
            +
             | 
| 260 | 
            +
                rule(:object_group_clause) do
         | 
| 261 | 
            +
                  spaced { lowercase_identifier.as(:name) } >>
         | 
| 262 | 
            +
                    spaced { str("OBJECT-GROUP").as(:type) } >>
         | 
| 263 | 
            +
                    spaced { object_group_objects_part } >>
         | 
| 264 | 
            +
                    spaced("STATUS") >> spaced { status } >>
         | 
| 265 | 
            +
                    spaced("DESCRIPTION") >> spaced { text } >>
         | 
| 266 | 
            +
                    spaced { refer_part }.maybe >>
         | 
| 267 | 
            +
                    colon_colon_part >> curly(object_identifier)
         | 
| 268 | 
            +
                end
         | 
| 269 | 
            +
             | 
| 270 | 
            +
                rule(:object_group_objects_part) do
         | 
| 271 | 
            +
                  spaced("OBJECTS") >> curly(objects)
         | 
| 272 | 
            +
                end
         | 
| 273 | 
            +
             | 
| 274 | 
            +
                rule(:module_compliance_clause) do
         | 
| 275 | 
            +
                  spaced { lowercase_identifier.as(:name) } >>
         | 
| 276 | 
            +
                    spaced { str("MODULE-COMPLIANCE").as(:type) } >>
         | 
| 277 | 
            +
                    spaced("STATUS") >> spaced { status } >>
         | 
| 278 | 
            +
                    spaced("DESCRIPTION") >> spaced { text } >>
         | 
| 279 | 
            +
                    spaced { refer_part }.maybe >>
         | 
| 280 | 
            +
                    spaced { compliance_modules } >>
         | 
| 281 | 
            +
                    colon_colon_part >> curly(object_identifier)
         | 
| 282 | 
            +
                end
         | 
| 283 | 
            +
             | 
| 284 | 
            +
                rule(:compliance_modules) do
         | 
| 285 | 
            +
                  with_separator(compliance_module)
         | 
| 286 | 
            +
                end
         | 
| 287 | 
            +
             | 
| 288 | 
            +
                rule(:compliance_module) do
         | 
| 289 | 
            +
                  spaced { str("MODULE") >> (space_in_line.repeat(1) >> compliance_module_name).maybe } >>
         | 
| 290 | 
            +
                    spaced { mandatory_part }.maybe >>
         | 
| 291 | 
            +
                    compliances.maybe
         | 
| 292 | 
            +
                end
         | 
| 293 | 
            +
             | 
| 294 | 
            +
                rule(:compliance_module_name) do
         | 
| 295 | 
            +
                  uppercase_identifier
         | 
| 296 | 
            +
                end
         | 
| 297 | 
            +
             | 
| 298 | 
            +
                rule(:mandatory_part) do
         | 
| 299 | 
            +
                  spaced("MANDATORY-GROUPS") >> curly(mandatory_groups)
         | 
| 300 | 
            +
                end
         | 
| 301 | 
            +
             | 
| 302 | 
            +
                rule(:compliances) do
         | 
| 303 | 
            +
                  with_separator(compliance).as(:compliances)
         | 
| 304 | 
            +
                end
         | 
| 305 | 
            +
             | 
| 306 | 
            +
                rule(:compliance) do
         | 
| 307 | 
            +
                  compliance_group | compliance_object
         | 
| 308 | 
            +
                end
         | 
| 309 | 
            +
             | 
| 310 | 
            +
                rule(:compliance_group) do
         | 
| 311 | 
            +
                  spaced { str("GROUP").as(:type) } >> spaced { object_identifier.as(:name) } >>
         | 
| 312 | 
            +
                    spaced("DESCRIPTION") >> text
         | 
| 313 | 
            +
                end
         | 
| 314 | 
            +
             | 
| 315 | 
            +
                rule(:compliance_object) do
         | 
| 316 | 
            +
                  spaced { str("OBJECT").as(:type) } >>
         | 
| 317 | 
            +
                    spaced { object_identifier.as(:name) } >>
         | 
| 318 | 
            +
                    spaced { syntax_part }.maybe >>
         | 
| 319 | 
            +
                    spaced { write_syntax_part }.maybe >>
         | 
| 320 | 
            +
                    spaced { access_part }.maybe >>
         | 
| 321 | 
            +
                    spaced("DESCRIPTION") >> text
         | 
| 322 | 
            +
                end
         | 
| 323 | 
            +
             | 
| 324 | 
            +
                rule(:syntax_part) do
         | 
| 325 | 
            +
                  spaced("SYNTAX") >> syntax.as(:syntax)
         | 
| 326 | 
            +
                end
         | 
| 327 | 
            +
             | 
| 328 | 
            +
                rule(:write_syntax_part) do
         | 
| 329 | 
            +
                  (spaced("WRITE-SYNTAX") >> spaced { syntax }).maybe
         | 
| 330 | 
            +
                end
         | 
| 331 | 
            +
             | 
| 332 | 
            +
                rule(:access_part) do
         | 
| 333 | 
            +
                  (spaced("MIN-ACCESS") >> spaced { access }).maybe
         | 
| 334 | 
            +
                end
         | 
| 335 | 
            +
             | 
| 336 | 
            +
                rule(:mandatory_groups) do
         | 
| 337 | 
            +
                  with_separator(mandatory_group, ",").as(:groups)
         | 
| 338 | 
            +
                end
         | 
| 339 | 
            +
             | 
| 340 | 
            +
                rule(:mandatory_group) { object_identifier.as(:name) }
         | 
| 341 | 
            +
             | 
| 342 | 
            +
                rule(:module_identity_clause) do
         | 
| 343 | 
            +
                  spaced { lowercase_identifier.as(:name) } >>
         | 
| 344 | 
            +
                    spaced { str("MODULE-IDENTITY").as(:type) } >>
         | 
| 345 | 
            +
                    (spaced("SUBJECT-CATEGORIES") >> curly(category_ids)).maybe >>
         | 
| 346 | 
            +
                    spaced("LAST-UPDATED") >> spaced { ext_utc_time } >>
         | 
| 347 | 
            +
                    spaced("ORGANIZATION") >> spaced { text.as(:organization) } >>
         | 
| 348 | 
            +
                    spaced("CONTACT-INFO") >> spaced { text.as(:contact_info) } >>
         | 
| 349 | 
            +
                    spaced("DESCRIPTION") >> spaced { text } >>
         | 
| 350 | 
            +
                    spaced { revisions }.maybe >>
         | 
| 351 | 
            +
                    colon_colon_part >> curly(object_identifier.as(:value))
         | 
| 352 | 
            +
                end
         | 
| 353 | 
            +
             | 
| 354 | 
            +
                rule(:ext_utc_time) { text }
         | 
| 355 | 
            +
             | 
| 356 | 
            +
                rule(:revisions) do
         | 
| 357 | 
            +
                  with_separator(revision)
         | 
| 358 | 
            +
                end
         | 
| 359 | 
            +
             | 
| 360 | 
            +
                rule(:revision) do
         | 
| 361 | 
            +
                  spaced("REVISION") >> spaced { ext_utc_time } >>
         | 
| 362 | 
            +
                    spaced("DESCRIPTION") >>
         | 
| 363 | 
            +
                    text
         | 
| 364 | 
            +
                end
         | 
| 365 | 
            +
             | 
| 366 | 
            +
                rule(:category_ids) do
         | 
| 367 | 
            +
                  with_separator(category_id, ",")
         | 
| 368 | 
            +
                end
         | 
| 369 | 
            +
             | 
| 370 | 
            +
                rule(:category_id) do
         | 
| 371 | 
            +
                  spaced { lowercase_identifier } >> bracketed(number) | lowercase_identifier
         | 
| 372 | 
            +
                end
         | 
| 373 | 
            +
             | 
| 374 | 
            +
                rule(:notification_type_clause) do
         | 
| 375 | 
            +
                  spaced { lowercase_identifier.as(:name) } >>
         | 
| 376 | 
            +
                    spaced { str("NOTIFICATION-TYPE").as(:type) } >>
         | 
| 377 | 
            +
                    spaced { notification_objects_part }.maybe >>
         | 
| 378 | 
            +
                    spaced("STATUS") >> spaced { status } >>
         | 
| 379 | 
            +
                    spaced("DESCRIPTION") >> spaced { text } >>
         | 
| 380 | 
            +
                    spaced { refer_part }.maybe >>
         | 
| 381 | 
            +
                    colon_colon_part >> curly(notification_name)
         | 
| 382 | 
            +
                end
         | 
| 383 | 
            +
             | 
| 384 | 
            +
                rule(:notification_objects_part) do
         | 
| 385 | 
            +
                  spaced("OBJECTS") >> curly(objects)
         | 
| 386 | 
            +
                end
         | 
| 387 | 
            +
             | 
| 388 | 
            +
                rule(:objects) do
         | 
| 389 | 
            +
                  with_separator(object, ",")
         | 
| 390 | 
            +
                end
         | 
| 391 | 
            +
             | 
| 392 | 
            +
                rule(:object) do
         | 
| 393 | 
            +
                  object_identifier
         | 
| 394 | 
            +
                end
         | 
| 395 | 
            +
             | 
| 396 | 
            +
                rule(:notification_name) do
         | 
| 397 | 
            +
                  object_identifier
         | 
| 398 | 
            +
                end
         | 
| 399 | 
            +
             | 
| 400 | 
            +
                rule(:traptype_clause) do
         | 
| 401 | 
            +
                  spaced { fuzzy_lowercase_identifier.as(:name) } >>
         | 
| 402 | 
            +
                    spaced { str("TRAP-TYPE").as(:type) } >> spaced { enterprise_part } >>
         | 
| 403 | 
            +
                    spaced { var_part }.maybe >>
         | 
| 404 | 
            +
                    spaced { descr_part }.maybe >>
         | 
| 405 | 
            +
                    spaced { refer_part }.maybe >>
         | 
| 406 | 
            +
                    colon_colon_part >> number
         | 
| 407 | 
            +
                end
         | 
| 408 | 
            +
             | 
| 409 | 
            +
                rule(:enterprise_part) do
         | 
| 410 | 
            +
                  spaced("ENTERPRISE") >> object_identifier |
         | 
| 411 | 
            +
                    spaced("ENTERPRISE") >> curly(object_identifier)
         | 
| 412 | 
            +
                end
         | 
| 413 | 
            +
             | 
| 414 | 
            +
                rule(:var_part) do
         | 
| 415 | 
            +
                  spaced("VARIABLES") >> curly(var_types)
         | 
| 416 | 
            +
                end
         | 
| 417 | 
            +
             | 
| 418 | 
            +
                rule(:var_types) do
         | 
| 419 | 
            +
                  with_separator(var_type, ",")
         | 
| 420 | 
            +
                end
         | 
| 421 | 
            +
             | 
| 422 | 
            +
                rule(:var_type) { object_identifier }
         | 
| 423 | 
            +
             | 
| 424 | 
            +
                rule(:descr_part) do
         | 
| 425 | 
            +
                  spaced("DESCRIPTION") >> text
         | 
| 426 | 
            +
                end
         | 
| 427 | 
            +
             | 
| 428 | 
            +
                rule(:object_type_clause) do
         | 
| 429 | 
            +
                  spaced { lowercase_identifier.as(:name) } >>
         | 
| 430 | 
            +
                    spaced { str("OBJECT-TYPE").as(:type) } >>
         | 
| 431 | 
            +
                    spaced { syntax_part }.maybe >>
         | 
| 432 | 
            +
                    spaced { units_part }.maybe >>
         | 
| 433 | 
            +
                    spaced { max_access_part }.maybe >>
         | 
| 434 | 
            +
                    (spaced("STATUS") >> spaced { status }).maybe >>
         | 
| 435 | 
            +
                    spaced { description_clause }.maybe >>
         | 
| 436 | 
            +
                    spaced { refer_part }.maybe >>
         | 
| 437 | 
            +
                    spaced { index_part }.maybe >>
         | 
| 438 | 
            +
                    spaced { mib_index }.maybe >>
         | 
| 439 | 
            +
                    spaced { def_val_part }.maybe >>
         | 
| 440 | 
            +
                    colon_colon_part >> curly(object_identifier.as(:value))
         | 
| 441 | 
            +
                end
         | 
| 442 | 
            +
             | 
| 443 | 
            +
                rule(:object_identity_clause) do
         | 
| 444 | 
            +
                  spaced { lowercase_identifier.as(:name) } >>
         | 
| 445 | 
            +
                    spaced { str("OBJECT-IDENTITY").as(:type) } >>
         | 
| 446 | 
            +
                    spaced("STATUS") >> spaced { status } >>
         | 
| 447 | 
            +
                    spaced("DESCRIPTION") >> spaced { text } >>
         | 
| 448 | 
            +
                    spaced { refer_part }.maybe >>
         | 
| 449 | 
            +
                    colon_colon_part >> curly(object_identifier)
         | 
| 450 | 
            +
                end
         | 
| 451 | 
            +
             | 
| 452 | 
            +
                rule(:units_part) do
         | 
| 453 | 
            +
                  spaced("UNITS") >> text.as(:units)
         | 
| 454 | 
            +
                end
         | 
| 455 | 
            +
             | 
| 456 | 
            +
                rule(:max_access_part) do
         | 
| 457 | 
            +
                  spaced("MAX-ACCESS") >> access | spaced("ACCESS") >> access
         | 
| 458 | 
            +
                end
         | 
| 459 | 
            +
             | 
| 460 | 
            +
                rule(:access) { lowercase_identifier }
         | 
| 461 | 
            +
             | 
| 462 | 
            +
                rule(:description_clause) do
         | 
| 463 | 
            +
                  spaced("DESCRIPTION") >> text
         | 
| 464 | 
            +
                end
         | 
| 465 | 
            +
             | 
| 466 | 
            +
                rule(:index_part) do
         | 
| 467 | 
            +
                  spaced("AUGMENTS") >> curly(entry)
         | 
| 468 | 
            +
                end
         | 
| 469 | 
            +
             | 
| 470 | 
            +
                rule(:mib_index) do
         | 
| 471 | 
            +
                  spaced("INDEX") >> curly(index_types)
         | 
| 472 | 
            +
                end
         | 
| 473 | 
            +
             | 
| 474 | 
            +
                rule(:def_val_part) do
         | 
| 475 | 
            +
                  spaced("DEFVAL") >> curly(valueof_simple_syntax)
         | 
| 476 | 
            +
                end
         | 
| 477 | 
            +
             | 
| 478 | 
            +
                rule(:valueof_simple_syntax) do
         | 
| 479 | 
            +
                  value | lowercase_identifier | text | curly(object_identifiers_defval)
         | 
| 480 | 
            +
                end
         | 
| 481 | 
            +
             | 
| 482 | 
            +
                rule(:object_identifiers_defval) do
         | 
| 483 | 
            +
                  with_separator(object_identifier_defval)
         | 
| 484 | 
            +
                end
         | 
| 485 | 
            +
             | 
| 486 | 
            +
                rule(:object_identifier_defval) do
         | 
| 487 | 
            +
                  spaced { lowercase_identifier } >> bracketed(number) |
         | 
| 488 | 
            +
                    number
         | 
| 489 | 
            +
                end
         | 
| 490 | 
            +
             | 
| 491 | 
            +
                rule(:index_types) do
         | 
| 492 | 
            +
                  with_separator(index_type, ",")
         | 
| 493 | 
            +
                end
         | 
| 494 | 
            +
             | 
| 495 | 
            +
                rule(:index_type) do
         | 
| 496 | 
            +
                  spaced("IMPLIED") >> idx | idx
         | 
| 497 | 
            +
                end
         | 
| 498 | 
            +
             | 
| 499 | 
            +
                rule(:idx) do
         | 
| 500 | 
            +
                  object_identifier
         | 
| 501 | 
            +
                end
         | 
| 502 | 
            +
             | 
| 503 | 
            +
                rule(:entry) do
         | 
| 504 | 
            +
                  object_identifier
         | 
| 505 | 
            +
                end
         | 
| 506 | 
            +
             | 
| 507 | 
            +
                rule(:value_declaration) do
         | 
| 508 | 
            +
                  spaced { fuzzy_lowercase_identifier.as(:name) } >>
         | 
| 509 | 
            +
                    spaced { str("OBJECT IDENTIFIER").as(:type) } >>
         | 
| 510 | 
            +
                    colon_colon_part >> curly(object_identifier.as(:value))
         | 
| 511 | 
            +
                end
         | 
| 512 | 
            +
             | 
| 513 | 
            +
                rule(:fuzzy_lowercase_identifier) do
         | 
| 514 | 
            +
                  lowercase_identifier | uppercase_identifier
         | 
| 515 | 
            +
                end
         | 
| 516 | 
            +
             | 
| 517 | 
            +
                rule(:object_identifier) do
         | 
| 518 | 
            +
                  sub_identifiers
         | 
| 519 | 
            +
                end
         | 
| 520 | 
            +
             | 
| 521 | 
            +
                rule(:sub_identifiers) do
         | 
| 522 | 
            +
                  with_separator(sub_identifier, space_in_line.repeat)
         | 
| 523 | 
            +
                end
         | 
| 524 | 
            +
             | 
| 525 | 
            +
                rule(:sub_identifier) do
         | 
| 526 | 
            +
                  fuzzy_lowercase_identifier |
         | 
| 527 | 
            +
                    number |
         | 
| 528 | 
            +
                    spaced { lowercase_identifier } >> bracketed(number)
         | 
| 529 | 
            +
                end
         | 
| 530 | 
            +
             | 
| 531 | 
            +
                rule(:type_declaration) do
         | 
| 532 | 
            +
                  spaced { type_name.as(:vartype) } >> colon_colon_part >> type_declaration_rhs
         | 
| 533 | 
            +
                end
         | 
| 534 | 
            +
             | 
| 535 | 
            +
                rule(:type_name) do
         | 
| 536 | 
            +
                  uppercase_identifier | type_smi
         | 
| 537 | 
            +
                end
         | 
| 538 | 
            +
             | 
| 539 | 
            +
                rule(:type_smi) do
         | 
| 540 | 
            +
                  type_smi_and_sppi | type_smi_only
         | 
| 541 | 
            +
                end
         | 
| 542 | 
            +
             | 
| 543 | 
            +
                rule(:type_declaration_rhs) do
         | 
| 544 | 
            +
                  spaced { choice_clause } |
         | 
| 545 | 
            +
                    spaced { str("TEXTUAL-CONVENTION") } >>
         | 
| 546 | 
            +
                    spaced { display_part }.maybe >>
         | 
| 547 | 
            +
                    spaced("STATUS") >> spaced { status } >>
         | 
| 548 | 
            +
                    spaced("DESCRIPTION") >> spaced { text } >>
         | 
| 549 | 
            +
                    spaced { refer_part }.maybe >>
         | 
| 550 | 
            +
                    spaced("SYNTAX") >> syntax |
         | 
| 551 | 
            +
                    syntax
         | 
| 552 | 
            +
                end
         | 
| 553 | 
            +
             | 
| 554 | 
            +
                rule(:refer_part) do
         | 
| 555 | 
            +
                  spaced("REFERENCE") >> text
         | 
| 556 | 
            +
                end
         | 
| 557 | 
            +
             | 
| 558 | 
            +
                rule(:choice_clause) do
         | 
| 559 | 
            +
                  # Ignoring choice syntax
         | 
| 560 | 
            +
                  spaced { str("CHOICE").as(:type) } >> curly(match("[^\}]").repeat)
         | 
| 561 | 
            +
                end
         | 
| 562 | 
            +
             | 
| 563 | 
            +
                rule(:syntax) do
         | 
| 564 | 
            +
                  object_syntax | spaced("BITS").as(:type) >> curly(named_bits)
         | 
| 565 | 
            +
                end
         | 
| 566 | 
            +
             | 
| 567 | 
            +
                rule(:display_part) do
         | 
| 568 | 
            +
                  spaced("DISPLAY-HINT") >> text
         | 
| 569 | 
            +
                end
         | 
| 570 | 
            +
             | 
| 571 | 
            +
                rule(:named_bits) do
         | 
| 572 | 
            +
                  with_separator(named_bit, ",")
         | 
| 573 | 
            +
                end
         | 
| 574 | 
            +
             | 
| 575 | 
            +
                rule(:named_bit) do
         | 
| 576 | 
            +
                  spaced { lowercase_identifier } >> bracketed(number)
         | 
| 577 | 
            +
                end
         | 
| 578 | 
            +
             | 
| 579 | 
            +
                rule(:object_syntax) do
         | 
| 580 | 
            +
                  conceptual_table |
         | 
| 581 | 
            +
                    entry_type |
         | 
| 582 | 
            +
                    simple_syntax |
         | 
| 583 | 
            +
                    application_syntax |
         | 
| 584 | 
            +
                    type_tag >> simple_syntax |
         | 
| 585 | 
            +
                    row.as(:value)
         | 
| 586 | 
            +
                end
         | 
| 587 | 
            +
             | 
| 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)
         | 
| 594 | 
            +
                end
         | 
| 595 | 
            +
             | 
| 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
         | 
| 605 | 
            +
                end
         | 
| 606 | 
            +
             | 
| 607 | 
            +
                rule(:conceptual_table) do
         | 
| 608 | 
            +
                  spaced { str("SEQUENCE OF").as(:type) } >> row.as(:value)
         | 
| 609 | 
            +
                end
         | 
| 610 | 
            +
             | 
| 611 | 
            +
                rule(:entry_type) do
         | 
| 612 | 
            +
                  spaced { str("SEQUENCE").as(:type) } >> curly(sequence_items)
         | 
| 613 | 
            +
                end
         | 
| 614 | 
            +
             | 
| 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")
         | 
| 618 | 
            +
                end
         | 
| 619 | 
            +
             | 
| 620 | 
            +
                rule(:sequence_items) do
         | 
| 621 | 
            +
                  with_separator(sequence_item, ",")
         | 
| 622 | 
            +
                end
         | 
| 623 | 
            +
             | 
| 624 | 
            +
                rule(:sequence_item) do
         | 
| 625 | 
            +
                  spaced { lowercase_identifier } >> spaced { sequence_syntax }
         | 
| 626 | 
            +
                end
         | 
| 627 | 
            +
             | 
| 628 | 
            +
                rule(:sequence_syntax) do
         | 
| 629 | 
            +
                  str("BITS") |
         | 
| 630 | 
            +
                    sequence_object_syntax |
         | 
| 631 | 
            +
                    spaced { uppercase_identifier } >> any_subtype
         | 
| 632 | 
            +
                end
         | 
| 633 | 
            +
             | 
| 634 | 
            +
                rule(:sequence_object_syntax) do
         | 
| 635 | 
            +
                  sequence_simple_syntax | sequence_application_syntax
         | 
| 636 | 
            +
                end
         | 
| 637 | 
            +
             | 
| 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
         | 
| 643 | 
            +
                end
         | 
| 644 | 
            +
             | 
| 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 |
         | 
| 651 | 
            +
                    str("Opaque") |
         | 
| 652 | 
            +
                    spaced { str("Counter64") >> space } >> any_subtype
         | 
| 653 | 
            +
                end
         | 
| 654 | 
            +
             | 
| 655 | 
            +
                rule(:row) { uppercase_identifier }
         | 
| 656 | 
            +
             | 
| 657 | 
            +
                rule(:integer_subtype) { bracketed(ranges) }
         | 
| 658 | 
            +
             | 
| 659 | 
            +
                rule(:octetstring_subtype) do
         | 
| 660 | 
            +
                  bracketed(spaced("SIZE") >> bracketed(ranges))
         | 
| 661 | 
            +
                end
         | 
| 662 | 
            +
             | 
| 663 | 
            +
                rule(:any_subtype) do
         | 
| 664 | 
            +
                  (integer_subtype | octetstring_subtype | enum_spec).maybe
         | 
| 665 | 
            +
                end
         | 
| 666 | 
            +
             | 
| 667 | 
            +
                rule(:enum_spec) { curly(enum_items) }
         | 
| 668 | 
            +
             | 
| 669 | 
            +
                rule(:enum_items) do
         | 
| 670 | 
            +
                  with_separator(enum_item.as(:enum), ",")
         | 
| 671 | 
            +
                end
         | 
| 672 | 
            +
             | 
| 673 | 
            +
                rule(:enum_item) do
         | 
| 674 | 
            +
                  fuzzy_lowercase_identifier.as(:name) >> space.repeat >> bracketed(number.as(:value))
         | 
| 675 | 
            +
                end
         | 
| 676 | 
            +
             | 
| 677 | 
            +
                rule(:ranges) do
         | 
| 678 | 
            +
                  with_separator(range.as(:range), "|")
         | 
| 679 | 
            +
                end
         | 
| 680 | 
            +
             | 
| 681 | 
            +
                rule(:range) do
         | 
| 682 | 
            +
                  value.as(:min) >> space.repeat >> (str("..") >> space.repeat >> value.as(:max)).maybe
         | 
| 683 | 
            +
                end
         | 
| 684 | 
            +
             | 
| 685 | 
            +
                rule(:value) do
         | 
| 686 | 
            +
                  number | hexstring | binstring
         | 
| 687 | 
            +
                end
         | 
| 688 | 
            +
             | 
| 689 | 
            +
                rule(:status) { lowercase_identifier }
         | 
| 690 | 
            +
             | 
| 691 | 
            +
                rule(:uppercase_identifier) do
         | 
| 692 | 
            +
                  match("[A-Z]") >> match("[A-Za-z0-9\-]").repeat
         | 
| 693 | 
            +
                end
         | 
| 694 | 
            +
             | 
| 695 | 
            +
                rule(:lowercase_identifier) do
         | 
| 696 | 
            +
                  match("[a-z]") >> match("[A-Za-z0-9\-]").repeat
         | 
| 697 | 
            +
                end
         | 
| 698 | 
            +
             | 
| 699 | 
            +
                rule(:type_smi_and_sppi) do
         | 
| 700 | 
            +
                  str("IpAddress") | str("TimeTicks") | str("Opaque") | str("Integer32") | str("Unsigned32")
         | 
| 701 | 
            +
                end
         | 
| 702 | 
            +
             | 
| 703 | 
            +
                rule(:type_smi_only) do
         | 
| 704 | 
            +
                  str("Counter") | str("Gauge32") | str("Counter64")
         | 
| 705 | 
            +
                end
         | 
| 706 | 
            +
             | 
| 707 | 
            +
                rule(:colon_colon_part) { spaced("::=") }
         | 
| 708 | 
            +
             | 
| 709 | 
            +
                rule(:space_in_line) { match('[ \t]').repeat(1) }
         | 
| 710 | 
            +
                rule(:cr) { match("\n") }
         | 
| 711 | 
            +
                rule(:space) do
         | 
| 712 | 
            +
                  # this rule match all not important text
         | 
| 713 | 
            +
                  (match('[ \t\r\n]') | comment_line).repeat(1)
         | 
| 714 | 
            +
                end
         | 
| 715 | 
            +
             | 
| 716 | 
            +
                rule(:comment_line) do
         | 
| 717 | 
            +
                  (match('\-\-') >> match('[^\n]').repeat >> match('\n'))
         | 
| 718 | 
            +
                end
         | 
| 719 | 
            +
             | 
| 720 | 
            +
                rule(:space?) { space.maybe }
         | 
| 721 | 
            +
                rule(:digit) { match["0-9"] }
         | 
| 722 | 
            +
                rule(:hexchar) { match("[0-9a-fA-F]") }
         | 
| 723 | 
            +
                rule(:empty) { str("") }
         | 
| 724 | 
            +
                rule(:number) do
         | 
| 725 | 
            +
                  (
         | 
| 726 | 
            +
                    str("-").maybe >> (
         | 
| 727 | 
            +
                      str("0") | (match("[1-9]") >> digit.repeat)
         | 
| 728 | 
            +
                    ) >> (
         | 
| 729 | 
            +
                      str(".") >> digit.repeat(1)
         | 
| 730 | 
            +
                    ).maybe >> (
         | 
| 731 | 
            +
                      match("[eE]") >> (str("+") | str("-")).maybe >> digit.repeat(1)
         | 
| 732 | 
            +
                    ).maybe
         | 
| 733 | 
            +
                  ).repeat(1)
         | 
| 734 | 
            +
                end
         | 
| 735 | 
            +
             | 
| 736 | 
            +
                rule(:hexstring) do
         | 
| 737 | 
            +
                  str("'") >> hexchar.repeat >> str("'") >> match("[hH]")
         | 
| 738 | 
            +
                end
         | 
| 739 | 
            +
             | 
| 740 | 
            +
                rule(:binstring) do
         | 
| 741 | 
            +
                  str("'") >> match["0-1"].repeat >> str("'")
         | 
| 742 | 
            +
                end
         | 
| 743 | 
            +
             | 
| 744 | 
            +
                rule(:text) do
         | 
| 745 | 
            +
                  str('"') >> (
         | 
| 746 | 
            +
                    str('\\') >> any | str('"').absent? >> any
         | 
| 747 | 
            +
                  ).repeat >> str('"')
         | 
| 748 | 
            +
                end
         | 
| 749 | 
            +
              end
         | 
| 750 | 
            +
            end
         |