module Msf

###
#
# This module provides methods for sending raw 802.11 frames using the
# ruby-lorco2n extension.
# Please see the ruby-lorcon documentation for more information.
#
###

module Exploit::Lorcon2

	#
	# Initializes an instance of an exploit module that accesses a 802.11 network
	#

	def initialize(info = {})
		super

		default_intf   = 'wlan0'
		default_driver = 'autodetect'

		if (Rex::Compat.is_windows())
			# Default to the the first airpcap device on Windows
			default_intf = "\\\\.\\airpcap00"

			# Default to the airpcap driver on Windows
			default_driver = 'airpcap'
		end

		register_options(
			[
				OptString.new('INTERFACE', [true, 'The name of the wireless interface', default_intf]),
				OptString.new('DRIVER', [true, 'The name of the wireless driver for lorcon', default_driver]),
				OptInt.new('CHANNEL', [true, 'The initial channel', 11]),
			], Msf::Exploit::Lorcon2
		)


		begin

			if(Rex::Compat.is_windows())
				airpcap = Rex::FileUtils.find_full_path("airpcap.dll")
				if (not airpcap)
					raise RuntimeError, "The airpcap.dll library must be installed"
				end
			end

			require 'Lorcon2'
			@lorcon_loaded = true

		rescue ::Exception => e
			@lorcon_loaded = false
			@lorcon_error  = e
		end

	end

	#
	# Opens a handle to the specified wireless device
	#
	def open_wifi

		if (not @lorcon_loaded)
			print_status("The Lorcon2 module is not available: #{@lorcon_error}")
			raise RuntimeError, "Lorcon2 not available"
		end

		if (datastore['DRIVER'] == "autodetect")
			self.wifi = ::Lorcon::Device.new(datastore['INTERFACE'])
		else
			self.wifi = ::Lorcon::Device.new(datastore['INTERFACE'], datastore['DRIVER'])
		end

		if (not self.wifi)
			raise RuntimeError, "Could not initialize the wireless device interface"
		end

		# Configure for injmon
		self.wifi.openinjmon() or raise RuntimeError, "Could not open device in inject/monitor combo mode: " + self.wifi.error

		# Configure channel
		self.wifi.channel = datastore['CHANNEL']

		# TODO - add mod/rate once lorcon2 supports it

		self.wifi
	end

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

		@wifi_count
	end

	def close_wifi
		self.wifi = nil
	end

	#
	# Converts ethernet addresses to binary
	#
	def eton(addr)
		addr.split(':').map { |c| c.hex.chr }.join
	end

	def channel
		self.wifi.channel
	end

	def next_channel
		cur = self.wifi.channel
		nxt = (cur > 10) ? 1 : cur + 1
		self.wifi.channel = nxt
	end

	attr_accessor :wifi

end

end

