module Msf

###
#
# This module provides methods for receiving raw packets.
# Please see the pcaprub documentation for more information.
#
###

module Exploit::Capture

	#
	# Initializes an instance of an exploit module that captures traffic
	#

	def initialize(info = {})
		super

		register_options(
			[
				OptPath.new('PCAPFILE', [false, 'The name of the PCAP capture file to process']),
				OptString.new('INTERFACE', [false, 'The name of the interface']),
				OptString.new('FILTER', [false, 'The filter string for capturing traffic']),
				OptInt.new('SNAPLEN', [true, 'The number of bytes to capture', 65535]),
				OptInt.new('TIMEOUT', [true, 'The number of seconds to wait for new data', 1])

			], Msf::Exploit::Capture
		)


		require 'racket'

		begin
			require 'pcaprub'
			@pcaprub_loaded = true
		rescue ::Exception => e
			@pcaprub_loaded = false
			@pcaprub_error  = e
		end

	end

	def stats_recv
		return(0) if not self.capture
		self.capture.stats['recv']
	end

	def stats_drop
		return(0) if not self.capture
		self.capture.stats['drop']
	end

	def stats_ifdrop
		return(0) if not self.capture
		self.capture.stats['ifdrop']
	end

	#
	# Opens a handle to the specified device
	#
	def open_pcap(opts={})

		if (not @pcaprub_loaded)
			print_status("The Pcaprub module is not available: #{@pcaprub_error}")
			raise RuntimeError, "Pcaprub not available"
		end

		# Capture device
		dev = nil
		len = (opts['SNAPLEN'] || datastore['SNAPLEN']  || 65535).to_i
		tim = (opts['TIMEOUT'] || datastore['TIMEOUT']  || 0).to_i
		fil = opts['FILTER'] || datastore['FILTER']

		# Look for a PCAP file
		cap = datastore['PCAPFILE'] || ''

		if(not cap.empty?)
			if(not File.exists?(cap))
				raise RuntimeError, "The PCAP file #{cap} could not be found"
			end
			self.capture = ::Pcap.open_offline(cap)
		else
			dev = datastore['INTERFACE'] || ::Pcap.lookupdev
			system("ifconfig", dev, "up")
			self.capture = ::Pcap.open_live(dev, len, true, tim)
		end

		if (not self.capture)
			raise RuntimeError, "Could not start the capture process"
		end

		self.capture.setfilter(fil) if fil
	end

	def close_pcap
		return if not self.capture
		self.capture = nil
		GC.start()
	end

	def capture_extract_ies(raw)
		set = {}
		ret = 0
		idx = 0
		len = 0

		while (idx < raw.length)
			len = raw[idx+1]
			return set if not len
			set[ raw[idx] ] ||= []
			set[ raw[idx] ].push(raw[idx + 2, len])
			idx += len + 2
		end

		return set
	end

	#
	# This monstrosity works around a series of bugs in the interrupt
	# signal handling of Ruby 1.9
	#
	def each_packet
		return if not capture
		begin
			@capture_count = 0
			reader = Thread.new do
				capture.each do |pkt|
					yield(pkt)
					@capture_count += 1
				end
			end
			reader.join
		rescue ::Exception
			raise $!
		ensure
			reader.kill if reader.alive?
		end

		@capture_count
	end

	attr_accessor :capture

end

end

