#!/usr/bin/env
import sys, time
import dnsconfig

import scapy
from scapy import srp, srp1, arping, Ether, IP, ICMP, ltoa
from IPy import IP as IPy

from twisted.internet.protocol import Factory, Protocol
from twisted.internet import reactor, defer
from twisted.names import client, server, dns, common
from twisted.internet.threads import deferToThread

deferred = deferToThread.__get__

scapy.conf.iface = dnsconfig.iface

class DeferCache:
	def __init__(self, op, cachefor=300):
		self.op = op
		self.reqcache = {}
		self.rescache = {}
		self.cachefor = cachefor

	def fakeresponse(self, res, deferred):
		deferred.callback(res)
		return res
	
	def realresponse(self, res, args):
		self.rescache[args] = (time.time(),res)
		self.reqcache.pop(args)
		return res
	
	def __call__(self, *args):
		if self.rescache.has_key(args):
			(t, res) = self.rescache[args]
			if time.time() - t < self.cachefor:
				return defer.succeed(res)
			else:
				self.rescache.pop(args)

		if not self.reqcache.has_key(args): # Currently not in progress - start it
			opdef = self.op(*args)
			self.reqcache[args] = opdef
			opdef.addCallback(lambda x: self.realresponse(x, args))

		userdef = defer.Deferred()
		self.reqcache[args].addCallback(lambda x: 
									self.fakeresponse(x, userdef))
		return userdef 

cached = lambda **kwargs: lambda *args, **kwarg2: ((kwarg2.update(kwargs)), DeferCache(*args, **(kwarg2)))[1]

@cached(cachefor=420)
@deferred
def lookupMAC(name, mac):
	bcast_route = filter(lambda x: x[2] == '0.0.0.0' and x[3] == dnsconfig.iface, scapy.conf.route.routes)
	if len(bcast_route) != 1:
		print "Error !"
	bcast_route = bcast_route[0]
	
	i = IPy(ltoa(bcast_route[0]) + "/" + ltoa(bcast_route[1]))

	ans = arping(str(i)) 
	ans = filter(lambda x : x[1].src == mac, ans[0])
	if(len(ans)) == 0:
		return ([(), (), ()])
	res = ans[0][1].psrc
	return([
			(dns.RRHeader(name, dns.A, dns.IN, dnsconfig.ttl, 
							  dns.Record_A(res, dnsconfig.ttl)),), (), ()
			])

class MACResolver(common.ResolverBase):
	def _lookup(self, name, cls, type, timeout):
		print "resolve(%s)" % name
		if(cls == dns.A and type == dns.IN and dnsconfig.hosts.has_key(name)):
			return lookupMAC(name, dnsconfig.hosts[name])

		return defer.fail(NotImplementedError("MACResolver._lookup"))
	
verbosity = 0
resolver = MACResolver()
f = server.DNSServerFactory(clients=[resolver], verbose=verbosity)
p = dns.DNSDatagramProtocol(f)
f.noisy = p.noisy = verbosity

reactor.listenUDP(53, p, interface="127.0.0.2")
reactor.run()


# $Id: dnsmac.py,v 1.5 2007/05/23 14:33:28 gopalv Exp $
