Class: Msf::DataStore

Inherits:
Hash
  • Object
show all
Defined in:
lib/msf/core/data_store.rb

Overview

The data store is just a bitbucket that holds keyed values. It is used by various classes to hold option values and other state information.

Direct Known Subclasses

ModuleDataStore

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeDataStore

Initializes the data store's internal state.


15
16
17
18
19
20
# File 'lib/msf/core/data_store.rb', line 15

def initialize()
  @options     = Hash.new
  @aliases     = Hash.new
  @imported    = Hash.new
  @imported_by = Hash.new
end

Instance Attribute Details

#aliasesObject

Returns the value of attribute aliases


23
24
25
# File 'lib/msf/core/data_store.rb', line 23

def aliases
  @aliases
end

#importedObject

Returns the value of attribute imported


24
25
26
# File 'lib/msf/core/data_store.rb', line 24

def imported
  @imported
end

#imported_byObject

Returns the value of attribute imported_by


25
26
27
# File 'lib/msf/core/data_store.rb', line 25

def imported_by
  @imported_by
end

#optionsObject

Returns the value of attribute options


22
23
24
# File 'lib/msf/core/data_store.rb', line 22

def options
  @options
end

Instance Method Details

#[](k) ⇒ Object

Case-insensitive wrapper around hash lookup


56
57
58
59
60
61
62
63
# File 'lib/msf/core/data_store.rb', line 56

def [](k)
  k = find_key_case(k)
  if options[k].respond_to? :calculate_value
    options[k].calculate_value(self)
  else
    super(k)
  end
end

#[]=(k, v) ⇒ Object

Clears the imported flag for the supplied key since it's being set directly.


31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/msf/core/data_store.rb', line 31

def []=(k, v)
  k = find_key_case(k)
  @imported[k] = false
  @imported_by[k] = nil

  opt = @options[k]
  unless opt.nil?
    if opt.validate_on_assignment?
      unless opt.valid?(v, check_empty: false)
        raise OptionValidateError.new(["Value '#{v}' is not valid for option '#{k}'"])
      end
      v = opt.normalize(v)
    end
  end

  if v.is_a? Hash
    v.each { |key, value| self[key] = value }
  else
    super(k,v)
  end
end

#clearObject

Completely clear all values in the hash


301
302
303
304
# File 'lib/msf/core/data_store.rb', line 301

def clear
  self.keys.each {|k| self.delete(k) }
  self
end

#clear_non_user_definedObject

Remove all imported options from the data store.


287
288
289
290
291
292
293
294
295
296
# File 'lib/msf/core/data_store.rb', line 287

def clear_non_user_defined
  @imported.delete_if { |k, v|
    if (v and @imported_by[k] != 'self')
      self.delete(k)
      @imported_by.delete(k)
    end

    v
  }
end

#copyObject

Return a deep copy of this datastore.


244
245
246
247
248
249
250
251
# File 'lib/msf/core/data_store.rb', line 244

def copy
  ds = self.class.new
  self.keys.each do |k|
    ds.import_option(k, self[k].kind_of?(String) ? self[k].dup : self[k], @imported[k], @imported_by[k])
  end
  ds.aliases = self.aliases.dup
  ds
end

#delete(k) ⇒ Object

Case-insensitive wrapper around delete


75
76
77
78
# File 'lib/msf/core/data_store.rb', line 75

def delete(k)
  @aliases.delete_if { |_, v| v.casecmp(k) == 0 }
  super(find_key_case(k))
end

#each(&block) ⇒ Object

Overrides the builtin 'each' operator to avoid the following exception on Ruby 1.9.2+

"can't add a new key into hash during iteration"

310
311
312
313
314
315
316
# File 'lib/msf/core/data_store.rb', line 310

def each(&block)
  list = []
  self.keys.sort.each do |sidx|
    list << [sidx, self[sidx]]
  end
  list.each(&block)
end

#find_key_case(k) ⇒ Object

Case-insensitive key lookup


321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'lib/msf/core/data_store.rb', line 321

def find_key_case(k)

  # Scan each alias looking for a key
  search_k = k.downcase
  if self.aliases.has_key?(search_k)
    search_k = self.aliases[search_k]
  end

  # Scan each key looking for a match
  self.each_key do |rk|
    if rk.casecmp(search_k) == 0
      return rk
    end
  end

  # Fall through to the non-existent value
  return k
end

#from_file(path, name = 'global') ⇒ Object

Imports datastore values from the specified file path using the supplied name


229
230
231
232
233
234
235
236
237
238
239
# File 'lib/msf/core/data_store.rb', line 229

def from_file(path, name = 'global')
  begin
    ini = Rex::Parser::Ini.from_file(path)
  rescue
    return
  end

  if (ini.group?(name))
    import_options_from_hash(ini[name], false)
  end
end

#import_option(key, val, imported = true, imported_by = nil, option = nil) ⇒ Object


152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/msf/core/data_store.rb', line 152

def import_option(key, val, imported = true, imported_by = nil, option = nil)
  self.store(key, val)

  if option
    option.aliases.each do |a|
      @aliases[a.downcase] = key.downcase
    end
  end
  @options[key] = option
  @imported[key] = imported
  @imported_by[key] = imported_by
end

#import_options(options, imported_by = nil, overwrite = false) ⇒ Object

This method is a helper method that imports the default value for all of the supplied options


94
95
96
97
98
99
100
# File 'lib/msf/core/data_store.rb', line 94

def import_options(options, imported_by = nil, overwrite = false)
  options.each_option do |name, opt|
    if self[name].nil? || overwrite
      import_option(name, opt.default, true, imported_by, opt)
    end
  end
end

#import_options_from_hash(option_hash, imported = true, imported_by = nil) ⇒ Object

Imports options from a hash and stores them in the datastore.


146
147
148
149
150
# File 'lib/msf/core/data_store.rb', line 146

def import_options_from_hash(option_hash, imported = true, imported_by = nil)
  option_hash.each_pair { |key, val|
    import_option(key, val, imported, imported_by)
  }
end

#import_options_from_s(option_str, delim = nil) ⇒ Object

Imports option values from a whitespace separated string in VAR=VAL format.


106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/msf/core/data_store.rb', line 106

def import_options_from_s(option_str, delim = nil)
  hash = {}

  # Figure out the delimeter, default to space.
  if (delim.nil?)
    delim = /\s/

    if (option_str.split('=').length <= 2 or option_str.index(',') != nil)
      delim = ','
    end
  end

  # Split on the delimeter
  option_str.split(delim).each { |opt|
    var, val = opt.split('=')

    next if (var =~ /^\s+$/)


    # Invalid parse?  Raise an exception and let those bastards know.
    if (var == nil or val == nil)
      var = "unknown" if (!var)

      raise Rex::ArgumentParseError, "Invalid option specified: #{var}",
        caller
    end

    # Remove trailing whitespaces from the value
    val.gsub!(/\s+$/, '')

    # Store the value
    hash[var] = val
  }

  import_options_from_hash(hash)
end

#merge(other) ⇒ Object

Override merge to ensure we merge the aliases and imported hashes


269
270
271
272
# File 'lib/msf/core/data_store.rb', line 269

def merge(other)
  ds = self.copy
  ds.merge!(other)
end

#merge!(other) ⇒ Object

Override merge! so that we merge the aliases and imported hashes


256
257
258
259
260
261
262
263
264
# File 'lib/msf/core/data_store.rb', line 256

def merge!(other)
  if other.is_a? DataStore
    self.aliases.merge!(other.aliases)
    self.imported.merge!(other.imported)
    self.imported_by.merge!(other.imported_by)
  end
  # call super last so that we return a reference to ourselves
  super
end

#store(k, v) ⇒ Object

Case-insensitive wrapper around store


68
69
70
# File 'lib/msf/core/data_store.rb', line 68

def store(k,v)
  super(find_key_case(k), v)
end

#to_file(path, name = 'global') ⇒ Object

Persists the contents of the data store to a file


212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/msf/core/data_store.rb', line 212

def to_file(path, name = 'global')
  ini = Rex::Parser::Ini.new(path)

  ini.add_group(name)

  # Save all user-defined options to the file.
  user_defined.each_pair { |k, v|
    ini[name][k] = v
  }

  ini.to_file(path)
end

#to_hObject


178
179
180
181
182
183
184
# File 'lib/msf/core/data_store.rb', line 178

def to_h
  datastore_hash = {}
  self.keys.each do |k|
    datastore_hash[k.to_s] = self[k].to_s
  end
  datastore_hash
end

#to_nested_valuesObject

Hack on a hack for the external modules


187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/msf/core/data_store.rb', line 187

def to_nested_values
  datastore_hash = {}

  array_nester = ->(arr) do
    if arr.first.is_a? Array
      arr.map &array_nester
    else
      arr.map &:to_s
    end
  end

  self.keys.each do |k|
    # TODO arbitrary depth
    if self[k].is_a? Array
      datastore_hash[k.to_s] = array_nester.call(self[k])
    else
      datastore_hash[k.to_s] = self[k].to_s
    end
  end
  datastore_hash
end

#to_s(delim = ' ') ⇒ Object

Serializes the options in the datastore to a string.


168
169
170
171
172
173
174
175
176
# File 'lib/msf/core/data_store.rb', line 168

def to_s(delim = ' ')
  str = ''

  keys.sort.each { |key|
    str << "#{key}=#{self[key]}" + ((str.length) ? delim : '')
  }

  return str
end

#update_value(k, v) ⇒ Object

Updates a value in the datastore with the specified name, k, to the specified value, v. This update does not alter the imported status of the value.


86
87
88
# File 'lib/msf/core/data_store.rb', line 86

def update_value(k, v)
  self.store(k, v)
end

#user_definedObject

Returns a hash of user-defined datastore values. The returned hash does not include default option values.


278
279
280
281
282
# File 'lib/msf/core/data_store.rb', line 278

def user_defined
  reject { |k, v|
    @imported[k] == true
  }
end