module Rex::IO::StreamAbstraction

This class provides an abstraction to a stream based connection through the use of a streaming socketpair.

Attributes

lsock[RW]

The left side of the stream.

monitor_thread[RW]
rsock[RW]

The right side of the stream.

Public Instance Methods

cleanup_abstraction() click to toggle source

This method cleans up the abstraction layer.

# File lib/rex/io/stream_abstraction.rb, line 61
def cleanup_abstraction
  self.lsock.close if (self.lsock)
  self.rsock.close if (self.rsock)

  self.lsock = nil
  self.rsock = nil
end
close() click to toggle source

Closes both sides of the stream abstraction.

# File lib/rex/io/stream_abstraction.rb, line 93
def close
  cleanup_abstraction
end
initialize_abstraction() click to toggle source

This method creates a streaming socket pair and initializes it.

# File lib/rex/io/stream_abstraction.rb, line 49
def initialize_abstraction
  self.lsock, self.rsock = Rex::Socket.tcp_socket_pair()
  self.lsock.extend(Rex::IO::Stream)
  self.lsock.extend(Ext)
  self.rsock.extend(Rex::IO::Stream)

  self.monitor_rsock
end
localinfo() click to toggle source

Symbolic local information.

# File lib/rex/io/stream_abstraction.rb, line 107
def localinfo
  "Local-side of Pipe"
end
peerinfo() click to toggle source

Symbolic peer information.

# File lib/rex/io/stream_abstraction.rb, line 100
def peerinfo
  "Remote-side of Pipe"
end
shutdown(how) click to toggle source

Shuts down the local side of the stream abstraction.

# File lib/rex/io/stream_abstraction.rb, line 86
def shutdown(how)
  lsock.shutdown(how)
end
sysread(length) click to toggle source

Low-level read from the local side.

# File lib/rex/io/stream_abstraction.rb, line 79
def sysread(length)
  lsock.sysread(length)
end
syswrite(buffer) click to toggle source

Low-level write to the local side.

# File lib/rex/io/stream_abstraction.rb, line 72
def syswrite(buffer)
  lsock.syswrite(buffer)
end

Protected Instance Methods

monitor_rsock() click to toggle source
# File lib/rex/io/stream_abstraction.rb, line 122
def monitor_rsock
  self.monitor_thread = Rex::ThreadFactory.spawn("StreamMonitorRemote", false) {
    loop do
      closed = false
      buf    = nil

      if not self.rsock
        wlog("monitor_rsock: the remote socket is nil, exiting loop")
        break
      end

      begin
        s = Rex::ThreadSafe.select( [ self.rsock ], nil, nil, 0.2 )
        if( s == nil || s[0] == nil )
          next
        end
      rescue Exception => e
        wlog("monitor_rsock: exception during select: #{e.class} #{e}")
        closed = true
      end

      if( closed == false )
        begin
          buf = self.rsock.sysread( 32768 )
          if buf == nil
            closed = true
            wlog("monitor_rsock: closed remote socket due to nil read")
          end
        rescue EOFError => e
          closed = true
          dlog("monitor_rsock: EOF in rsock")
        rescue ::Exception => e
          closed = true
          wlog("monitor_rsock: exception during read: #{e.class} #{e}")
        end
      end

      if( closed == false )
        total_sent   = 0
        total_length = buf.length
        while( total_sent < total_length )
          begin
            data = buf[total_sent, buf.length]

            # Note that this must be write() NOT syswrite() or put() or anything like it.
            # Using syswrite() breaks SSL streams.
            sent = self.write( data )

            # sf: Only remove the data off the queue is write was successfull.
            #     This way we naturally perform a resend if a failure occured.
            #     Catches an edge case with meterpreter TCP channels where remote send
            #     failes gracefully and a resend is required.
            if (sent.nil?)
              closed = true
              wlog("monitor_rsock: failed writing, socket must be dead")
              break
            elsif (sent > 0)
              total_sent += sent
            end
          rescue ::IOError, ::EOFError => e
            closed = true
            wlog("monitor_rsock: exception during write: #{e.class} #{e}")
            break
          end
        end
      end

      if( closed )
        begin
          self.close_write if self.respond_to?('close_write')
        rescue IOError
        end
        break
      end
    end
  }
end