CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/snmp/mib.rb
Views: 1904
1
#
2
# Copyright (c) 2004 David R. Halliday
3
# All rights reserved.
4
#
5
# This SNMP library is free software. Redistribution is permitted under the
6
# same terms and conditions as the standard Ruby distribution. See the
7
# COPYING file in the Ruby distribution for details.
8
#
9
10
require 'snmp/varbind'
11
require 'fileutils'
12
require 'yaml'
13
14
module SNMP
15
16
class MIB
17
18
DEFAULT_MIB_PATH = File.expand_path(
19
File.join(File.dirname(__FILE__), "..", "..", "data", "snmp", "mibs")
20
)
21
22
#:startdoc:
23
24
MODULE_EXT = 'yaml'
25
26
class ModuleNotLoadedError < RuntimeError; end
27
28
class << self
29
##
30
# Import an SMIv2 MIB file for later loading. A module only needs to
31
# be installed once.
32
#
33
# module_file - the filename of the module to be imported
34
# mib_dir - the output directory for the serialized MIB data
35
#
36
# NOTE: This implementation requires that the 'smidump' tool is available
37
# in the PATH. This tool can be obtained from the libsmi website at
38
# http://http://www.ibr.cs.tu-bs.de/projects/libsmi/ .
39
#
40
# ALSO NOTE: The file format in future releases is subject to
41
# change. For now, it is a simple YAML hash with the MIB symbol
42
# as the key and the OID as the value. These files could be
43
# generated manually if 'smidump' is not available.
44
#
45
# Here is an example of the contents of an output file:
46
#
47
# ---
48
# ipDefaultTTL: 1.3.6.1.2.1.4.2
49
# ipForwDatagrams: 1.3.6.1.2.1.4.6
50
# ipOutRequests: 1.3.6.1.2.1.4.10
51
# ipOutNoRoutes: 1.3.6.1.2.1.4.12
52
# ipReasmTimeout: 1.3.6.1.2.1.4.13
53
# icmpInDestUnreachs: 1.3.6.1.2.1.5.3
54
#
55
def import_module(module_file, mib_dir=DEFAULT_MIB_PATH)
56
raise "smidump tool must be installed" unless import_supported?
57
FileUtils.makedirs mib_dir
58
mib_hash = `smidump -f python #{module_file}`
59
mib = eval_mib_data(mib_hash)
60
if mib
61
module_name = mib["moduleName"]
62
raise "#{module_file}: invalid file format; no module name" unless module_name
63
if mib["nodes"]
64
oid_hash = {}
65
mib["nodes"].each { |key, value| oid_hash[key] = value["oid"] }
66
if mib["notifications"]
67
mib["notifications"].each { |key, value| oid_hash[key] = value["oid"] }
68
end
69
File.open(module_file_name(module_name, mib_dir), 'w') do |file|
70
YAML.dump(oid_hash, file)
71
file.puts
72
end
73
module_name
74
else
75
warn "*** No nodes defined in: #{module_file} ***"
76
nil
77
end
78
else
79
warn "*** Import failed for: #{module_file} ***"
80
nil
81
end
82
end
83
84
##
85
# Returns the full filename of the imported MIB file for the given
86
# module name.
87
#
88
def module_file_name(module_name, mib_dir=DEFAULT_MIB_PATH)
89
File.join(mib_dir, module_name + "." + MODULE_EXT)
90
end
91
92
##
93
# The MIB.import_module method is only supported if the external
94
# 'smidump' tool is available. This method returns true if a
95
# known version of the tool is available.
96
#
97
def import_supported?
98
`smidump --version` =~ /^smidump 0.4/ && $? == 0
99
end
100
101
##
102
# Returns a list of MIB modules that have been imported. All of
103
# the current IETF MIBs should be available from the default
104
# MIB directory.
105
#
106
# If a regex is provided, then the module names are matched
107
# against that pattern.
108
#
109
def list_imported(regex=//, mib_dir=DEFAULT_MIB_PATH)
110
list = []
111
Dir["#{mib_dir}/*.#{MODULE_EXT}"].each do |name|
112
module_name = File.basename(name, ".*")
113
list << module_name if module_name =~ regex
114
end
115
list
116
end
117
118
private
119
120
def eval_mib_data(mib_hash)
121
ruby_hash = mib_hash.
122
gsub(':', '=>'). # fix hash syntax
123
gsub('(', '[').gsub(')', ']'). # fix tuple syntax
124
sub('FILENAME =', 'filename ='). # get rid of constants
125
sub('MIB =', 'mib =')
126
mib = nil
127
eval(ruby_hash)
128
mib
129
end
130
end # class methods
131
132
def initialize
133
@by_name = {}
134
@by_module_by_name = {}
135
end
136
137
##
138
# Loads a module into this MIB. The module must be imported before it
139
# can be loaded. See MIB.import_module .
140
#
141
def load_module(module_name, mib_dir=DEFAULT_MIB_PATH)
142
oid_hash = nil
143
File.open(MIB.module_file_name(module_name, mib_dir)) do |file|
144
oid_hash = YAML.load(file.read)
145
end
146
@by_name.merge!(oid_hash) do |key, old, value|
147
warn "warning: overwriting old MIB name '#{key}'"
148
end
149
@by_module_by_name[module_name] = {}
150
@by_module_by_name[module_name].merge!(oid_hash)
151
end
152
153
##
154
# Returns a VarBindList for the provided list of objects. If a
155
# string is provided it is interpreted as a symbolic OID.
156
#
157
# This method accepts many different kinds of objects:
158
# - single string object IDs e.g. "1.3.6.1" or "IF-MIB::ifTable.1.1"
159
# - single ObjectId
160
# - list of string object IDs
161
# - list of ObjectIds
162
# - list of VarBinds
163
#
164
def varbind_list(object_list, option=:KeepValue)
165
vb_list = VarBindList.new
166
if object_list.respond_to? :to_str
167
vb_list << oid(object_list).to_varbind
168
elsif object_list.respond_to? :to_varbind
169
vb_list << apply_option(object_list.to_varbind, option)
170
else
171
object_list.each do |item|
172
if item.respond_to? :to_str
173
varbind = oid(item).to_varbind
174
else
175
varbind = item.to_varbind
176
end
177
vb_list << apply_option(varbind, option)
178
end
179
end
180
vb_list
181
end
182
183
def apply_option(varbind, option)
184
if option == :NullValue
185
varbind.value = Null
186
elsif option != :KeepValue
187
raise ArgumentError, "invalid option: #{option.to_s}", caller
188
end
189
varbind
190
end
191
private :apply_option
192
193
##
194
# Returns a VarBind object for the given name and value. The name
195
# can be a String, ObjectId, or anything that responds to :to_varbind.
196
#
197
# String names are in the format <ModuleName>::<NodeName>.<Index> with
198
# ModuleName and Index being optional.
199
#
200
def varbind(name, value=Null)
201
if name.respond_to? :to_str
202
vb = VarBind.new(oid(name), value)
203
else
204
vb = name.to_varbind
205
vb.value = value
206
end
207
vb
208
end
209
210
##
211
# Returns an ObjectId for the given name. Names are in the format
212
# <ModuleName>::<NodeName>.<Index> with ModuleName and Index being
213
# optional.
214
#
215
def oid(name)
216
module_parts = name.to_str.split("::")
217
if module_parts.length == 1
218
parse_oid(@by_name, name.to_str)
219
elsif module_parts.length == 2
220
module_name = module_parts[0]
221
oid = module_parts[1]
222
module_hash = @by_module_by_name[module_name]
223
if module_hash
224
parse_oid(module_hash, oid)
225
else
226
raise ModuleNotLoadedError, "module '#{module_name}' not loaded"
227
end
228
else
229
raise ArgumentError, "invalid format: #{name.to_str}"
230
end
231
end
232
233
def parse_oid(node_hash, name)
234
oid_parts = name.split(".")
235
first_part = oid_parts.shift
236
oid_string = node_hash[first_part]
237
if oid_string
238
oid_array = oid_string.split(".")
239
else
240
oid_array = [first_part]
241
end
242
oid_array.concat(oid_parts)
243
ObjectId.new(oid_array)
244
end
245
private :parse_oid
246
247
end
248
249
end # module SNMP
250
251