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
Whether or not the session has completed interaction
Whether or not the session is currently being interacted with
The original suspend proc.
Public Instance Methods
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
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
Stub method that is meant to handler interaction
# File lib/rex/ui/interactive.rb, line 124 def _interact end
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
Called when an interrupt is sent.
# File lib/rex/ui/interactive.rb, line 130 def _interrupt true end
The local file descriptor handle.
# File lib/rex/ui/interactive.rb, line 171 def _local_fd user_input.fd end
The remote file descriptor handle.
# File lib/rex/ui/interactive.rb, line 178 def _remote_fd(stream) stream.fd end
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
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
Called when a suspend is sent.
# File lib/rex/ui/interactive.rb, line 137 def _suspend false end
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
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
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 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
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
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