# -*- coding: binary -*-
require 'msf/core'

module Msf

###
#
# This module exposes methods for talking to WDBRPC daemons
#
###
module Exploit::Remote::WDBRPC_Client

	include Exploit::Remote::WDBRPC
	include Auxiliary::Report

	attr_accessor :wdbrpc_info, :udp_sock

	def initialize(info = {})
		super
		register_options(
			[
				Opt::RHOST,
				Opt::RPORT(17185),
			], Msf::Exploit::Remote::WDBRPC_Client)
	end


	def wdbrpc_client_connect
		self.wdbrpc_info = {}

		wdbrpc_client_disconnect()

		self.udp_sock = Rex::Socket::Udp.create(
			{
				'Context' => {'Msf' => framework, 'MsfExploit' => self}
			}
		)
		add_socket(self.udp_sock)

		wdbrpc_client_send_disconnect()

		udp_sock.sendto(wdbrpc_request_connect(rhost), rhost, rport, 0)
		res,src = udp_sock.recvfrom(65535, 5)
		if not res
			print_error("No response to TARGET_CONNECT (WDB4)")
			return
		end


		if res.length > 0 and res.length < 80
			print_status("#{rhost}: Unknown response: '#{res.unpack("H*")[0]}'")
			return
		end

		if res.empty?
			print_error("#{rhost}: No response from the target")
			return
		end

		self.wdbrpc_info = wdbrpc_parse_connect_reply(res)
		print_status("#{rhost} Connected to #{self.wdbrpc_info[:rt_vers]} - #{self.wdbrpc_info[:rt_bsp_name]} (#{self.wdbrpc_info[:rt_bootline]})")

		report_note(
			:host   => rhost,
			:port   => rport,
			:proto  => 'udp',
			:type   => 'vxworks.target_info',
			:data   => res,
			:update => :unique
		)
	end

	def wdbrpc_client_connect2
		self.wdbrpc_info = {}

		wdbrpc_client_disconnect()

		self.udp_sock = Rex::Socket::Udp.create(
			{
				'Context' => {'Msf' => framework, 'MsfExploit' => self}
			}
		)
		add_socket(self.udp_sock)

		wdbrpc_client_send_disconnect()

		udp_sock.sendto(wdbrpc_request_connect2(rhost), rhost, rport, 0)
		res,src = udp_sock.recvfrom(65535, 5)
		if not res
			print_error("No response to TARGET_CONNECT2")
			return
		end

		if res.length < 80
			print_status("#{rhost}: Unknown response: '#{res.unpack("H*")[0]}'")
			return
		end

		self.wdbrpc_info = wdbrpc_parse_connect_reply(res)
		print_status("#{rhost} Connected to #{self.wdbrpc_info[:rt_vers]} - #{self.wdbrpc_info[:rt_bsp_name]} (#{self.wdbrpc_info[:rt_bootline]})")

		report_note(
			:host   => rhost,
			:port   => rport,
			:proto  => 'udp',
			:type   => 'vxworks.target_info',
			:data   => res,
			:update => :unique
		)
	end

	def wdbrpc_client_memread(offset, length, params=0)
		pkt = wdbrpc_request_memread(offset, length, params)
		cnt = 0
		res = nil

		begin
			udp_sock.sendto(pkt, rhost, rport, 0)
			res,src = udp_sock.recvfrom(65535, 0.5)
			if not res and src
				raise RuntimeError, "no reply"
			end

			if res.length <= 48
				raise RuntimeError, "short read"
			end

		rescue ::Interrupt
			raise $!
		rescue ::Exception
			if cnt < 120
				cnt += 1
				retry
			end
		end

		res[48,res.length-48]
	end

	def wdbrpc_client_memwrite(offset, buffer, params=0)
		pkt = wdbrpc_request_memwrite(offset, buffer, params)
		cnt = 0
		res = nil

		udp_sock.sendto(pkt, rhost, rport, 0)
		res,src = udp_sock.recvfrom(65535, 5.0)

		if not res and src
			raise RuntimeError, "no reply"
		end
		res[-4,4].unpack("N")[0]
	end


	def wdbrpc_client_memscan(offset, depth, buffer, params=0)
		pkt = wdbrpc_request_memscan(offset, depth, buffer, params)
		cnt = 0
		res = nil

		udp_sock.sendto(pkt, rhost, rport, 0)
		res,src = udp_sock.recvfrom(65535, 5.0)

		if not res and src
			raise RuntimeError, "no reply"
		end
		p res
		res
	end


	def wdbrpc_client_context_kill(ctx_type=0, ctx=0)
		pkt = wdbrpc_request_context_kill(ctx_type, ctx)
		res = nil

		begin
			udp_sock.sendto(pkt, rhost, rport, 0)
			res,src = udp_sock.recvfrom(65535, 0.5)

		rescue ::Interrupt
			raise $!
		rescue ::Exception
		end
		res
	end

	def wdbrpc_client_send_disconnect
		pkt = wdbrpc_request_disconnect
		begin
			if self.udp_sock
				self.udp_sock.sendto(pkt, rhost, rport, 0)
				self.udp_sock.recvfrom(65535, 5)
			end
		rescue ::Interrupt
			raise $!
		rescue ::Exception
		end
	end

	def wdbrpc_client_disconnect
		wdbrpc_client_send_disconnect

		if self.udp_sock
			self.udp_sock.close rescue nil
		end
		self.udp_sock = nil

	end

	def rhost
		datastore['RHOST']
	end

	def rport
		datastore['RPORT'].to_i
	end

end
end

