module Rex::Ui::Interactive

This class implements the stubs that are needed to provide an interactive user interface that is backed against something arbitrary.

Attributes

completed[RW]

Whether or not the session has completed interaction

interacting[RW]

Whether or not the session is currently being interacted with

on_command_proc[RW]
on_print_proc[RW]
orig_suspend[RW]

The original suspend proc.

Public Instance Methods

detach() click to toggle source

Stops the current interaction

# File lib/rex/ui/interactive.rb, line 92
def detach
  if (self.interacting)
    self.interacting = false
    while(not self.completed)
      ::IO.select(nil, nil, nil, 0.25)
    end
  end
end
interact(user_input, user_output) click to toggle source

Starts interacting with the session at the most raw level, simply forwarding input from user_input to rstream and forwarding input from rstream to user_output.

# File lib/rex/ui/interactive.rb, line 23
def interact(user_input, user_output)

  # Detach from any existing console
  if(self.interacting)
    detach()
  end

  init_ui(user_input, user_output)

  self.interacting = true
  self.completed = false

  eof = false

  # Start the readline stdin monitor
  # XXX disabled
  # user_input.readline_start() if user_input.supports_readline

  # Handle suspend notifications
  handle_suspend

  # As long as we're interacting...
  while (self.interacting == true)

    begin
      _interact

    rescue Interrupt
      # If we get an interrupt exception, ask the user if they want to
      # abort the interaction.  If they do, then we return out of
      # the interact function and call it a day.
      eof = true if (_interrupt)

    rescue EOFError, Errno::ECONNRESET, IOError
      # If we reach EOF or the connection is reset...
      eof = true

    end

    break if eof
  end

  begin

    # Restore the suspend handler
    restore_suspend

    # If we've hit eof, call the interact complete handler
    _interact_complete if (eof == true)

    # Shutdown the readline thread
                      # XXX disabled
    # user_input.readline_stop() if user_input.supports_readline

    # Detach from the input/output handles
    reset_ui()

  ensure
    # Mark this as completed
    self.completed = true
  end

  # Return whether or not EOF was reached
  return eof
end

Protected Instance Methods

_interact() click to toggle source

Stub method that is meant to handler interaction

# File lib/rex/ui/interactive.rb, line 124
def _interact
end
_interact_complete() click to toggle source

Called when interaction has completed and one of the sides has closed.

# File lib/rex/ui/interactive.rb, line 144
def _interact_complete
  true
end
_interrupt() click to toggle source

Called when an interrupt is sent.

# File lib/rex/ui/interactive.rb, line 130
def _interrupt
  true
end
_local_fd() click to toggle source

The local file descriptor handle.

# File lib/rex/ui/interactive.rb, line 171
def _local_fd
  user_input.fd
end
_remote_fd(stream) click to toggle source

The remote file descriptor handle.

# File lib/rex/ui/interactive.rb, line 178
def _remote_fd(stream)
  stream.fd
end
_stream_read_local_write_remote(stream) click to toggle source

Read from local and write to remote.

# File lib/rex/ui/interactive.rb, line 161
def _stream_read_local_write_remote(stream)
  data = user_input.gets

  self.on_command_proc.call(data) if self.on_command_proc
  stream.put(data)
end
_stream_read_remote_write_local(stream) click to toggle source

Read from remote and write to local.

# File lib/rex/ui/interactive.rb, line 151
def _stream_read_remote_write_local(stream)
  data = stream.get

  self.on_print_proc.call(data) if self.on_print_proc
  user_output.print(data)
end
_suspend() click to toggle source

Called when a suspend is sent.

# File lib/rex/ui/interactive.rb, line 137
def _suspend
  false
end
handle_suspend() click to toggle source

Installs a signal handler to monitor suspend signal notifications.

# File lib/rex/ui/interactive.rb, line 254
def handle_suspend
  if (orig_suspend == nil)
    begin
      self.orig_suspend = Signal.trap("TSTP") {
        _suspend
      }
    rescue
    end
  end
end
interact_ring(ring) click to toggle source

Interacts between a local stream and a remote ring buffer. This has to use a secondary thread to prevent the select on the local stream from blocking

# File lib/rex/ui/interactive.rb, line 212
def interact_ring(ring)
  begin

  rdr = Rex::ThreadFactory.spawn("RingMonitor", false) do
    seq = nil
    while self.interacting

      # Look for any pending data from the remote ring
      nseq,data = ring.read_data(seq)

      # Update the sequence number if necessary
      seq = nseq || seq

      # Write output to the local stream if successful
      user_output.print(data) if data

      # Wait for new data to arrive on this session
      ring.wait(seq)
    end
  end

  while self.interacting

    # Look for any pending input from the local stream
    sd = Rex::ThreadSafe.select([ _local_fd ], nil, [_local_fd], 5.0)

    # Write input to the ring's input mechanism
    if sd
      data = user_input.gets
      ring.put(data)
    end
  end

  ensure
    rdr.kill
  end
end
interact_stream(stream) click to toggle source

Interacts with two streaming connections, reading data from one and writing it to the other. Both are expected to implement Rex::IO::Stream.

# File lib/rex/ui/interactive.rb, line 186
def interact_stream(stream)
  while self.interacting

    # Select input and rstream
    sd = Rex::ThreadSafe.select([ _local_fd, _remote_fd(stream) ], nil, nil, 0.25)

    # Cycle through the items that have data
    # From the stream?  Write to user_output.
    sd[0].each { |s|
      if (s == _remote_fd(stream))
        _stream_read_remote_write_local(stream)
      # From user_input?  Write to stream.
      elsif (s == _local_fd)
        _stream_read_local_write_remote(stream)
      end
    } if (sd)

    Thread.pass
  end
end
prompt(query) click to toggle source

Prompt the user for input if possible. XXX: This is not thread-safe on Windows

# File lib/rex/ui/interactive.rb, line 285
def prompt(query)
  if (user_output and user_input)
    user_output.print("\n" + query)
    user_input.sysread(2)
  end
end
prompt_yesno(query) click to toggle source

Check the return value of a yes/no prompt

# File lib/rex/ui/interactive.rb, line 295
def prompt_yesno(query)
  (prompt(query + " [y/N]  ") =~ /^y/i) ? true : false
end
restore_suspend() click to toggle source

Restores the previously installed signal handler for suspend notifications.

# File lib/rex/ui/interactive.rb, line 269
def restore_suspend
  begin
    if (orig_suspend)
      Signal.trap("TSTP", orig_suspend)
    else
      Signal.trap("TSTP", "DEFAULT")
    end
    self.orig_suspend = nil
  rescue
  end
end