Module: Rex::Ui::Text::DispatcherShell::CommandDispatcher

Overview

Empty template base class for command dispatchers.

Defined Under Namespace

Modules: ClassMethods

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#shellObject

No tab completion items by default



279
280
281
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 279

def shell
  @shell
end

#tab_complete_itemsObject

No tab completion items by default



279
280
281
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 279

def tab_complete_items
  @tab_complete_items
end

Class Method Details

.included(base) ⇒ Object



49
50
51
52
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 49

def self.included(base)
  # Install class methods so they are inheritable
  base.extend(ClassMethods)
end

Instance Method Details

#cmd_help(cmd = nil, *ignored) ⇒ Object Also known as: cmd_?

Displays the help banner. With no arguments, this is just a list of all commands grouped by dispatcher. Otherwise, tries to use a method named cmd_#<code>cmd</code>_help for the first dispatcher that has a command named cmd. If no such method exists, uses cmd as a regex to compare against each enstacked dispatcher’s name and dumps commands of any that match.



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 172

def cmd_help(cmd=nil, *ignored)
  if cmd
    help_found = false
    cmd_found = false
    shell.dispatcher_stack.each do |dispatcher|
      next unless dispatcher.respond_to?(:commands)
      next if (dispatcher.commands.nil?)
      next if (dispatcher.commands.length == 0)

      if dispatcher.respond_to?("cmd_#{cmd}", true)
        cmd_found = true
        break unless dispatcher.respond_to?("cmd_#{cmd}_help", true)
        dispatcher.send("cmd_#{cmd}_help")
        help_found = true
        break
      end
    end

    unless cmd_found
      # We didn't find a cmd, try it as a dispatcher name
      shell.dispatcher_stack.each do |dispatcher|
        if dispatcher.name =~ /#{cmd}/i
          print_line(dispatcher.help_to_s)
          cmd_found = help_found = true
        end
      end
    end

    if docs_dir && File.exist?(File.join(docs_dir, cmd + '.md'))
      print_line
      print(File.read(File.join(docs_dir, cmd + '.md')))
    end
    print_error("No help for #{cmd}, try -h") if cmd_found and not help_found
    print_error("No such command") if not cmd_found
  else
    print(shell.help_to_s)
    if docs_dir && File.exist?(File.join(docs_dir + '.md'))
      print_line
      print(File.read(File.join(docs_dir + '.md')))
    end
  end
end

#cmd_help_helpObject



160
161
162
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 160

def cmd_help_help
  print_line "There's only so much I can do"
end

#cmd_help_tabs(str, words) ⇒ Object

Tab completion for the help command

By default just returns a list of all commands in all dispatchers.



220
221
222
223
224
225
226
227
228
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 220

def cmd_help_tabs(str, words)
  return [] if words.length > 1

  tabs = []
  shell.dispatcher_stack.each { |dispatcher|
    tabs += dispatcher.commands.keys
  }
  return tabs
end

#commandsObject

Returns nil for an empty set of commands.

This method should be overridden to return a Hash with command names for keys and brief help text for values.



68
69
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 68

def commands
end

#deprecated_cmd(method = nil, *args) ⇒ Object

Print a warning that the called command is deprecated and optionally forward to the replacement method (useful for when commands are renamed).



131
132
133
134
135
136
137
138
139
140
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 131

def deprecated_cmd(method=nil, *args)
  cmd = caller[0].match(/`cmd_(.*)'/)[1]
  print_error "The #{cmd} command is DEPRECATED"
  if cmd == "db_autopwn"
    print_error "See http://r-7.co/xY65Zr instead"
  elsif method and self.respond_to?("cmd_#{method}", true)
    print_error "Use #{method} instead"
    self.send("cmd_#{method}", *args)
  end
end

#deprecated_commandsObject

Returns an empty set of commands.

This method should be overridden if the dispatcher has commands that should be treated as deprecated. Deprecated commands will not show up in help and will not tab-complete, but will still be callable.



78
79
80
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 78

def deprecated_commands
  []
end

#deprecated_help(method = nil) ⇒ Object



142
143
144
145
146
147
148
149
150
151
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 142

def deprecated_help(method=nil)
  cmd = caller[0].match(/`cmd_(.*)_help'/)[1]
  print_error "The #{cmd} command is DEPRECATED"
  if cmd == "db_autopwn"
    print_error "See http://r-7.co/xY65Zr instead"
  elsif method and self.respond_to?("cmd_#{method}_help", true)
    print_error "Use 'help #{method}' instead"
    self.send("cmd_#{method}_help")
  end
end

#docs_dirObject

Return the subdir of the ‘documentation/` directory that should be used to find usage documentation

TODO: get this value from somewhere that doesn’t invert a bunch of dependencies



272
273
274
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 272

def docs_dir
  File.expand_path(File.join(__FILE__, '..', '..', '..', '..', '..', 'documentation', 'cli'))
end

#help_to_s(opts = {}) ⇒ Object

Return a pretty, user-readable table of commands provided by this dispatcher. The command column width can be modified by passing in :command_width.



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 237

def help_to_s(opts={})
  # If this dispatcher has no commands, we can't do anything useful.
  return "" if commands.nil? or commands.length == 0

  # Display the commands
  tbl = Rex::Text::Table.new(
    'Header'  => "#{self.name} Commands",
    'Indent'  => opts['Indent'] || 4,
    'Columns' =>
      [
        'Command',
        'Description'
      ],
    'ColProps' =>
      {
        'Command' =>
          {
            'Width' => opts[:command_width]
          }
      })

  commands.sort.each { |c|
    tbl << c
  }

  return "\n" + tbl.to_s + "\n"
end

#initialize(shell) ⇒ Object

Initializes the command dispatcher mixin.



57
58
59
60
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 57

def initialize(shell)
  self.shell = shell
  self.tab_complete_items = []
end

Wraps shell.print



122
123
124
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 122

def print(msg = '')
  shell.print(msg)
end

Wraps shell.print_error



85
86
87
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 85

def print_error(msg = '')
  shell.print_error(msg)
end

Wraps shell.print_good



108
109
110
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 108

def print_good(msg = '')
  shell.print_good(msg)
end

Wraps shell.print_line



101
102
103
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 101

def print_line(msg = '')
  shell.print_line(msg)
end

Wraps shell.print_status



94
95
96
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 94

def print_status(msg = '')
  shell.print_status(msg)
end

Wraps shell.print_warning



115
116
117
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 115

def print_warning(msg = '')
  shell.print_warning(msg)
end

#tab_complete_directory(str, words) ⇒ Object

Return a list of possible directory for tab completion.



300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 300

def tab_complete_directory(str, words)
  directory = str[-1] == File::SEPARATOR ? str : File.dirname(str)
  filename = str[-1] == File::SEPARATOR ? '' : File.basename(str)
  entries = Dir.entries(directory).select { |fp| fp.start_with?(filename) }
  dirs = entries - ['.', '..']
  dirs = dirs.map { |fp| File.join(directory, fp).gsub(/\A\.\//, '') }
  dirs = dirs.select { |x| File.directory?(x) }
  dirs = dirs.map { |x| x + File::SEPARATOR }
  if dirs.length == 1 && dirs[0] != str && dirs[0].end_with?(File::SEPARATOR)
    # If Readline receives a single value from this function, it will assume we're done with the tab
    # completing, and add an extra space at the end.
    # This is annoying if we're recursively tab-traversing our way through subdirectories -
    # we may want to continue traversing, but MSF will add a space, requiring us to back up to continue
    # tab-completing our way through successive subdirectories.
    ::Readline.completion_append_character = nil
  end

  if dirs.length == 0 && File.directory?(str)
    # we've hit the end of the road
    dirs = [str]
  end

  dirs
end

#tab_complete_filenames(str, words) ⇒ Object

Provide a generic tab completion for file names.

If the only completion is a directory, this descends into that directory and continues completions with filenames contained within.



287
288
289
290
291
292
293
294
295
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 287

def tab_complete_filenames(str, words)
  matches = ::Readline::FILENAME_COMPLETION_PROC.call(str)
  if matches and matches.length == 1 and File.directory?(matches[0])
    dir = matches[0]
    dir += File::SEPARATOR if dir[-1,1] != File::SEPARATOR
    matches = ::Readline::FILENAME_COMPLETION_PROC.call(dir)
  end
  matches.nil? ? [] : matches
end

#tab_complete_generic(fmt, str, words) ⇒ Object

Provide a generic tab completion function based on the specification pass as fmt. The fmt argument in a hash where values are an array defining how the command should be completed. The first element of the array can be one of:

nil      - This argument is a flag and takes no option.
true     - This argument takes an option with no suggestions.
:address - This option is a source address.
:bool    - This option is a boolean.
:file    - This option is a file path.
Array    - This option is an array of possible values.


337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 337

def tab_complete_generic(fmt, str, words)
  last_word = words[-1]
  fmt = fmt.select { |key, value| last_word == key || !words.include?(key) }

  val = fmt[last_word]
  return fmt.keys if !val  # the last word does not look like a fmtspec
  arg = val[0]
  return fmt.keys if !arg  # the last word is a fmtspec that takes no argument

  tabs = []
  if arg.to_s.to_sym == :address
    tabs = tab_complete_source_address
  elsif arg.to_s.to_sym == :bool
    tabs = ['true', 'false']
  elsif arg.to_s.to_sym == :file
    tabs = tab_complete_filenames(str, words)
  elsif arg.kind_of?(Array)
    tabs = arg.map {|a| a.to_s}
  end
  tabs
end

#tab_complete_source_addressObject

Return a list of possible source addresses for tab completion.



362
363
364
365
366
367
368
369
370
371
372
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 362

def tab_complete_source_address
  addresses = [Rex::Socket.source_address]
  # getifaddrs was introduced in 2.1.2
  if ::Socket.respond_to?(:getifaddrs)
    ifaddrs = ::Socket.getifaddrs.select do |ifaddr|
      ifaddr.addr && ifaddr.addr.ip?
    end
    addresses += ifaddrs.map { |ifaddr| ifaddr.addr.ip_address }
  end
  addresses
end

#unknown_command(method, line) ⇒ Symbol?

A callback that can be used to handle unknown commands. This can for example, allow a dispatcher to mark a command as being disabled.

Returns:

  • (Symbol, nil)

    Returns a symbol specifying the action that was taken by the handler or 'nil` if no action was taken. The only supported action at this time is `:handled`, signifying that the unknown command was handled by this dispatcher and no additional dispatchers should receive it.



381
382
383
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 381

def unknown_command(method, line)
  nil
end

#update_prompt(*args) ⇒ Object

Wraps shell.update_prompt



156
157
158
# File 'lib/rex/ui/text/dispatcher_shell.rb', line 156

def update_prompt(*args)
  shell.update_prompt(*args)
end