#!/usr/bin/env ruby

require 'rex/post/meterpreter/channels/stream'
require 'rex/post/meterpreter/extensions/stdapi/tlv'

module Rex
module Post
module Meterpreter
module Extensions
module Stdapi
module Net
module SocketSubsystem

###
#
# This class represents a logical TCP client connection
# that is established from the remote machine and tunnelled
# through the established meterpreter connection, similar to an
# SSH port forward.
#
###
class TcpClientChannel < Rex::Post::Meterpreter::Stream

	module SocketInterface
		def type?
			'tcp'
		end

		def getsockname
			return super if not channel

			# Find the first host in our chain (our address)
			hops = 0
			csock = channel.client.sock
			while(csock.respond_to?('channel'))
				csock = csock.channel.client.sock
				hops += 1
			end

			tmp,caddr,cport = csock.getsockname
			tmp,raddr,rport = csock.getpeername
			maddr,mport = [ channel.params.localhost, channel.params.localport ]
			[ tmp, "#{caddr}#{(hops > 0) ? "-_#{hops}_" : ""}-#{raddr}", "#{mport}" ]
		end

		def getpeername
			return super if not channel
			tmp,caddr,cport = channel.client.sock.getpeername
			maddr,mport = [ channel.params.peerhost, channel.params.peerport ]
			[ tmp, "#{maddr}", "#{mport}" ]
		end

		attr_accessor :channel
	end

	##
	#
	# Factory
	#
	##

	#
	# Opens a TCP client channel using the supplied parameters.
	#
	def TcpClientChannel.open(client, params)
		c = Channel.create(client, 'stdapi_net_tcp_client', self, CHANNEL_FLAG_SYNCHRONOUS,
			[
				{
					'type'  => TLV_TYPE_PEER_HOST,
					'value' => params.peerhost
				},
				{
					'type'  => TLV_TYPE_PEER_PORT,
					'value' => params.peerport
				},
				{
					'type'  => TLV_TYPE_LOCAL_HOST,
					'value' => params.localhost
				},
				{
					'type'  => TLV_TYPE_LOCAL_PORT,
					'value' => params.localport
				},
				{
					'type'  => TLV_TYPE_CONNECT_RETRIES,
					'value' => params.retries
				}
			])
		c.params = params
		c
	end

	##
	#
	# Constructor
	#
	##

	#
	# Passes the channel initialization information up to the base class.
	#
	def initialize(client, cid, type, flags)
		super(client, cid, type, flags)

		# Implement some of the required socket interfaces on the local side of
		# the stream abstraction.
		lsock.extend(SocketInterface)
		rsock.extend(SocketInterface)
		lsock.channel = self
		rsock.channel = self
	end

	#
	# Closes the write half of the connection.
	#
	def close_write
		return shutdown(1)
	end

	#
	# Shutdown the connection
	#
	# 0 -> future reads
	# 1 -> future sends
	# 2 -> both
	#
	def shutdown(how = 1)
		request = Packet.create_request('stdapi_net_socket_tcp_shutdown')

		request.add_tlv(TLV_TYPE_SHUTDOWN_HOW, how)
		request.add_tlv(TLV_TYPE_CHANNEL_ID, self.cid)

		response = client.send_request(request)

		return true
	end

end

end; end; end; end; end; end; end

