Module: Rex::Ui::Interactive

Overview

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

Instance Attribute Summary collapse

Attributes included from Subscriber::Input

#user_input

Attributes included from Subscriber::Output

#user_output

Instance Method Summary collapse

Methods included from Subscriber

#copy_ui, #init_ui, #reset_ui

Methods included from Subscriber::Input

#gets

Methods included from Subscriber::Output

#flush, #print, #print_blank_line, #print_error, #print_good, #print_line, #print_status, #print_warning

Instance Attribute Details

#completedObject

Whether or not the session has completed interaction


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

def completed
  @completed
end

#interactingObject

Whether or not the session is currently being interacted with


112
113
114
# File 'lib/rex/ui/interactive.rb', line 112

def interacting
  @interacting
end

#next_sessionObject

If another session needs interaction, this is where it goes


117
118
119
# File 'lib/rex/ui/interactive.rb', line 117

def next_session
  @next_session
end

#on_command_procObject

Returns the value of attribute on_command_proc


125
126
127
# File 'lib/rex/ui/interactive.rb', line 125

def on_command_proc
  @on_command_proc
end

#on_print_procObject

Returns the value of attribute on_print_proc


124
125
126
# File 'lib/rex/ui/interactive.rb', line 124

def on_print_proc
  @on_print_proc
end

#orig_suspendObject (protected)

The original suspend proc.


132
133
134
# File 'lib/rex/ui/interactive.rb', line 132

def orig_suspend
  @orig_suspend
end

#orig_usr1Object (protected)

Returns the value of attribute orig_usr1


133
134
135
# File 'lib/rex/ui/interactive.rb', line 133

def orig_usr1
  @orig_usr1
end

Instance Method Details

#_interactObject (protected)

Stub method that is meant to handler interaction


138
139
# File 'lib/rex/ui/interactive.rb', line 138

def _interact
end

#_interact_completeObject (protected)

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


158
159
160
# File 'lib/rex/ui/interactive.rb', line 158

def _interact_complete
  true
end

#_interruptObject (protected)

Called when an interrupt is sent.


144
145
146
# File 'lib/rex/ui/interactive.rb', line 144

def _interrupt
  true
end

#_local_fdObject (protected)

The local file descriptor handle.


185
186
187
# File 'lib/rex/ui/interactive.rb', line 185

def _local_fd
  user_input.fd
end

#_remote_fd(stream) ⇒ Object (protected)

The remote file descriptor handle.


192
193
194
# File 'lib/rex/ui/interactive.rb', line 192

def _remote_fd(stream)
  stream.fd
end

#_stream_read_local_write_remote(stream) ⇒ Object (protected)

Read from local and write to remote.


175
176
177
178
179
180
# File 'lib/rex/ui/interactive.rb', line 175

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) ⇒ Object (protected)

Read from remote and write to local.


165
166
167
168
169
170
# File 'lib/rex/ui/interactive.rb', line 165

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

#_suspendObject (protected)

Called when a suspend is sent.


151
152
153
# File 'lib/rex/ui/interactive.rb', line 151

def _suspend
  false
end

#detachObject

Stops the current interaction


100
101
102
103
104
105
106
107
# File 'lib/rex/ui/interactive.rb', line 100

def detach
  if (self.interacting)
    self.interacting = false
    while(not self.completed)
      ::IO.select(nil, nil, nil, 0.25)
    end
  end
end

#handle_suspendObject (protected)

Installs a signal handler to monitor suspend signal notifications.


225
226
227
228
229
230
231
232
233
234
# File 'lib/rex/ui/interactive.rb', line 225

def handle_suspend
  if orig_suspend.nil?
    begin
      self.orig_suspend = Signal.trap("TSTP") do
        Thread.new { _suspend }.join
      end
    rescue
    end
  end
end

#handle_usr1Object (protected)


253
254
255
256
257
258
259
260
261
262
# File 'lib/rex/ui/interactive.rb', line 253

def handle_usr1
  if orig_usr1.nil?
    begin
      self.orig_usr1 = Signal.trap("USR1") do
        Thread.new { _usr1 }.join
      end
    rescue
    end
  end
end

#interact(user_input, user_output) ⇒ Object

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.


24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/rex/ui/interactive.rb', line 24

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

  handle_usr1

  # 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

  # if another session was requested, store it
  next_session = self.next_session
  # clear the value from the object
  self.next_session = nil

  # return this session id
  return next_session
end

#interact_stream(stream) ⇒ Object (protected)

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


200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/rex/ui/interactive.rb', line 200

def interact_stream(stream)
  while self.interacting && _remote_fd(stream)

    # 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) ⇒ Object (protected)

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


281
282
283
284
285
286
# File 'lib/rex/ui/interactive.rb', line 281

def prompt(query)
  if (user_output and user_input)
    user_output.print("\n" + query)
    user_input.sysread(2)
  end
end

#prompt_yesno(query) ⇒ Object (protected)

Check the return value of a yes/no prompt


291
292
293
# File 'lib/rex/ui/interactive.rb', line 291

def prompt_yesno(query)
  (prompt(query + " [y/N]  ") =~ /^y/i) ? true : false
end

#restore_suspendObject (protected)

Restores the previously installed signal handler for suspend notifications.


241
242
243
244
245
246
247
248
249
250
251
# File 'lib/rex/ui/interactive.rb', line 241

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

#restore_usr1Object (protected)


265
266
267
268
269
270
271
272
273
274
275
# File 'lib/rex/ui/interactive.rb', line 265

def restore_usr1
  begin
    if orig_usr1
      Signal.trap("USR1", orig_usr1)
    else
      Signal.trap("USR1", "DEFAULT")
    end
    self.orig_usr1 = nil
  rescue
  end
end