#!/usr/bin/env ruby
# -*- coding: binary -*-

require 'socket'
require 'fcntl'

module Rex
module IO

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

	###
	#
	# Extension information for required Stream interface.
	#
	###
	module Ext

		#
		# Initializes peer information.
		#
		def initinfo(peer,local)
			@peer = peer
			@local = local
		end

		#
		# Symbolic peer information.
		#
		def peerinfo
			(@peer || "Remote Pipe")
		end

		#
		# Symbolic local information.
		#
		def localinfo
			(@local || "Local Pipe")
		end
	end

	#
	# This method creates a streaming socket pair and initializes it.
	#
	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

	#
	# This method cleans up the abstraction layer.
	#
	def cleanup_abstraction
		self.lsock.close if (self.lsock)
		self.rsock.close if (self.rsock)

		self.lsock = nil
		self.rsock = nil
	end

	#
	# Low-level write to the local side.
	#
	def syswrite(buffer)
		lsock.syswrite(buffer)
	end

	#
	# Low-level read from the local side.
	#
	def sysread(length)
		lsock.sysread(length)
	end

	#
	# Shuts down the local side of the stream abstraction.
	#
	def shutdown(how)
		lsock.shutdown(how)
	end

	#
	# Closes both sides of the stream abstraction.
	#
	def close
		cleanup_abstraction
	end

	#
	# Symbolic peer information.
	#
	def peerinfo
		"Remote-side of Pipe"
	end

	#
	# Symbolic local information.
	#
	def localinfo
		"Local-side of Pipe"
	end

	#
	# The left side of the stream.
	#
	attr_reader :lsock
	#
	# The right side of the stream.
	#
	attr_reader :rsock

protected

	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

protected
	attr_accessor :monitor_thread
	attr_writer :lsock
	attr_writer :rsock

end

end; end

