require 'rex' module Net module SSH class CommandStream attr_accessor :channel, :thread, :error, :ssh attr_accessor :lsock, :rsock, :monitor module PeerInfo include ::Rex::IO::Stream attr_accessor :peerinfo attr_accessor :localinfo end def initialize(ssh, cmd, cleanup = false) self.lsock, self.rsock = Rex::Socket.tcp_socket_pair() self.lsock.extend(Rex::IO::Stream) self.lsock.extend(PeerInfo) self.rsock.extend(Rex::IO::Stream) self.ssh = ssh self.thread = Thread.new(ssh,cmd,cleanup) do |rssh,rcmd,rcleanup| begin info = rssh.transport.socket.getpeername self.lsock.peerinfo = "#{info[1]}:#{info[2]}" info = rssh.transport.socket.getsockname self.lsock.localinfo = "#{info[1]}:#{info[2]}" rssh.open_channel do |rch| rch.exec(rcmd) do |c, success| raise "could not execute command: #{rcmd.inspect}" unless success c[:data] = '' c.on_eof do self.rsock.close rescue nil self.ssh.close rescue nil self.thread.kill end c.on_close do self.rsock.close rescue nil self.ssh.close rescue nil self.thread.kill end c.on_data do |ch,data| self.rsock.write(data) end c.on_extended_data do |ch, ctype, data| self.rsock.write(data) end self.channel = c end end self.monitor = Thread.new do while(true) next if not self.rsock.has_read_data?(1.0) buff = self.rsock.sysread(16384) break if not buff verify_channel self.channel.send_data(buff) if buff end end while true rssh.process(0.5) { true } end rescue ::Exception => e self.error = e #::Kernel.warn "BOO: #{e.inspect}" #::Kernel.warn e.backtrace.join("\n") ensure self.monitor.kill if self.monitor end # Shut down the SSH session if requested if(rcleanup) rssh.close end end end # # Prevent a race condition # def verify_channel while ! self.channel raise EOFError if ! self.thread.alive? ::IO.select(nil, nil, nil, 0.10) end end end end end