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

module Msf

###
#
# This module exposes methods for accessing NDMP services
#
###
module Exploit::Remote::NDMP

	include Exploit::Remote::Tcp

	#
	# Creates an instance of a NDMP exploit module.
	#
	def initialize(info = {})
		super

		# Register the options that all NDMP exploits may make use of.
		register_options(
			[
				Opt::RHOST,
				Opt::RPORT(10000),
			], Msf::Exploit::Remote::NDMP)

		self.recv_buff = ''
	end

	#
	# Flush the receive buffer on a new connection
	#
	def connect
		super
		self.recv_buff = ''
	end

	#
	# This method dumps ndmp version information
	#
	def ndmp_info
		connect
		req = [
				1,               # Sequence number
				Time.now.to_i,   # Current time
				0,               # Message type (request)
				0x108,           # Message name (version)
				0,               # Reply sequence number
				0,               # Error status
			].pack('NNNNNN')

		resp = ndmp_recv()
		ndmp_send(req)
		resp = ndmp_recv()
		disconnect

		if !(resp and resp.length > 28)
			return false
		end

		info = { }

		i = 32

		vend_len = resp[i, 4].unpack('N')[0]
		vend     = resp[i + 4, vend_len]
		i += vend_len + 4 + 1

		prod_len = resp[i, 4].unpack('N')[0]
		prod     = resp[i + 4, prod_len]
		i += prod_len + 4 + 1

		vers_len = resp[i, 4].unpack('N')[0]
		vers     = resp[i + 4, vers_len]
		i += vers_len + 4 + 1

		info['Version'] = vers
		info['Product'] = prod
		info['Vendor']  = vend

		return info
	end

	#
	# This method reads from the socket and parses out a single
	# NDMP response, buffering the rest
	#
	def ndmp_recv(nsock = self.sock)
		# Attempt to read at least four bytes (the length value)
		if (self.recv_buff.length < 4)
			self.recv_buff << ( sock.get_once( 4 - self.recv_buff.length, 5) || '' )
		end

		# If we did not recieve a full length value, return early
		if (self.recv_buff.length < 4)
			return false
		end

		# Read the length header out of the message
		dlen = self.recv_buff[0, 4].unpack('N')[0] & 0x7fffffff

		# Read any pending data and append it to the buffer
		self.recv_buff << ( sock.get_once || '' )

		# Do we have the entire response message?
		if (self.recv_buff.length >= dlen + 4)
			return self.recv_buff.slice!(0, dlen + 4)
		end

		return false
	end

	#
	# This method tacks a length header on a packet then sends
	# it out the socket
	#
	def ndmp_send(data, nsock = self.sock)
		nsock.put( [ data.length + 0x80000000 ].pack('N') + data )
	end

	attr_accessor	:recv_buff
end
end

