#!/usr/bin/env ruby

require 'thread'
require 'rex/socket'
require 'rex/post/meterpreter/extensions/stdapi/tlv'
require 'rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/tcp_client_channel'
require 'rex/logging'

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

###
#
# This class provides an interface to interacting with sockets
# on the remote machine.  It allows callers to open TCP, UDP,
# and other arbitrary socket-based connections as channels that
# can then be interacted with through the established 
# meterpreter connection.
#
###
class Socket

	##
	#
	# Constructor
	#
	##

	#
	# Initialize the socket subsystem and start monitoring sockets
	# as they come in.
	#
	def initialize(client)
		self.client                    = client
		self.monitored_sockets         = []
		self.monitored_socket_channels = {}

		# Start monitoring shit like the business
		self.monitor_sockets
	end

	#
	# Terminate the monitor thread.
	#
	def shutdown
		monitor_thread.kill
	end

	##
	#
	# Factory
	#
	##

	#
	# Creates an arbitrary client socket channel using the information
	# supplied in the socket parameters instance.  The 'params' argument
	# is expected to be of type Rex::Socket::Parameters.
	#
	def create(params)
		channel = nil
		res     = nil

	#	begin
			if (params.tcp?)
				if (params.server?)
					channel = create_tcp_server(params)
				else
					channel = create_tcp_client(params)
				end

				# Add this channel's right socket to the socket monitor
				add_monitored_socket(channel.rsock, channel)

				# If we get a valid channel back, create a stream 
				# representation of the left side of the socket for
				# the caller to use
				if (channel != nil)
					res = channel.lsock
				end
			elsif (params.udp?)
				if (params.server?)
					res = create_udp_server(params)
				else
					res = create_udp_client(params)
				end

				# TODO: Datagram wrapper
			end
	#	rescue
	#	end

		return res
	end

	#
	# Create a TCP server channel.
	#
	def create_tcp_server(params)
	end

	#
	# Creates a TCP client channel.
	#
	def create_tcp_client(params)
		return SocketSubsystem::TcpClientChannel.open(client, params)
	end

	#
	# Creates a UDP server channel.
	#
	def create_udp_server(params)
	end

	#
	# Creates a UDP client channel.
	#
	def create_udp_client(params)
	end

protected

	##
	#
	# Socket monitoring
	#
	##

	#
	# Monitors zero or more sockets and handles forwarding traffic
	# to the remote half of the associated channel.
	#
	def monitor_sockets
		self.monitor_thread = ::Thread.new {

			loop do
		
				# Watch for data
				begin
					socks = select(monitored_sockets, nil, nil, 1)
				rescue StreamClosedError => e
					channel = monitored_socket_channels[e.stream.object_id]

					dlog("monitor_channels: channel #{channel} closed (#{e.stream})", 
							'rex', LEV_3)

					if (channel)
						begin
							channel.close
						rescue IOError
						end

						remove_monitored_socket(e.stream)
					end

					next
				end

				# No data?
				if (socks == nil || socks[0] == nil)
					next
				end

				# Enumerate through each of the indicated sockets
				socks[0].each { |sock|
					channel = monitored_socket_channels[sock.object_id]
					closed  = false
					data    = nil

					if (channel == nil)
						remove_monitored_socket(sock)

						next
					end

					begin
						data = sock.sysread(16384)
					rescue
						closed = true
					end

					if (data == nil)
						closed = true
					end

					# If the socket closed, notify the other side and remove
					# this socket from the monitored socket list
					if (closed)
						channel.close_write

						remove_monitored_socket(sock)
					# Otherwise, write the data to the remote side
					else
						channel.write(data)
					end
				}

			end

		}
	end

	#
	# Adds a socket to the list of monitored sockets.
	#
	def add_monitored_socket(sock, channel)
		monitored_sockets << sock
		monitored_socket_channels[sock.object_id] = channel
	end

	#
	# Removes a socket from the list of monitored sockets.
	#
	def remove_monitored_socket(sock)
		monitored_socket_channels.delete(sock.object_id)
		monitored_sockets.delete(sock)
	end

	attr_accessor :monitored_sockets, :monitored_socket_channels # :nodoc:
	attr_accessor :monitor_thread # :nodoc:
	attr_accessor :client # :nodoc:

end

end; end; end; end; end; end
