| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
import time |
|---|
| 7 |
|
|---|
| 8 |
from zope.interface import implements |
|---|
| 9 |
|
|---|
| 10 |
from twisted.names import dns |
|---|
| 11 |
from twisted.python import failure, log |
|---|
| 12 |
from twisted.internet import interfaces, defer |
|---|
| 13 |
|
|---|
| 14 |
import common |
|---|
| 15 |
|
|---|
| 16 |
class CacheResolver(common.ResolverBase): |
|---|
| 17 |
"""A resolver that serves records from a local, memory cache.""" |
|---|
| 18 |
|
|---|
| 19 |
implements(interfaces.IResolver) |
|---|
| 20 |
|
|---|
| 21 |
cache = None |
|---|
| 22 |
|
|---|
| 23 |
def __init__(self, cache = None, verbose = 0): |
|---|
| 24 |
common.ResolverBase.__init__(self) |
|---|
| 25 |
|
|---|
| 26 |
if cache is None: |
|---|
| 27 |
cache = {} |
|---|
| 28 |
self.cache = cache |
|---|
| 29 |
self.verbose = verbose |
|---|
| 30 |
self.cancel = {} |
|---|
| 31 |
|
|---|
| 32 |
|
|---|
| 33 |
def __setstate__(self, state): |
|---|
| 34 |
self.__dict__ = state |
|---|
| 35 |
|
|---|
| 36 |
now = time.time() |
|---|
| 37 |
for (k, (when, (ans, add, ns))) in self.cache.items(): |
|---|
| 38 |
diff = now - when |
|---|
| 39 |
for rec in ans + add + ns: |
|---|
| 40 |
if rec.ttl < diff: |
|---|
| 41 |
del self.cache[k] |
|---|
| 42 |
break |
|---|
| 43 |
|
|---|
| 44 |
|
|---|
| 45 |
def __getstate__(self): |
|---|
| 46 |
for c in self.cancel.values(): |
|---|
| 47 |
c.cancel() |
|---|
| 48 |
self.cancel.clear() |
|---|
| 49 |
return self.__dict__ |
|---|
| 50 |
|
|---|
| 51 |
|
|---|
| 52 |
def _lookup(self, name, cls, type, timeout): |
|---|
| 53 |
now = time.time() |
|---|
| 54 |
q = dns.Query(name, type, cls) |
|---|
| 55 |
try: |
|---|
| 56 |
when, (ans, auth, add) = self.cache[q] |
|---|
| 57 |
except KeyError: |
|---|
| 58 |
if self.verbose > 1: |
|---|
| 59 |
log.msg('Cache miss for ' + repr(name)) |
|---|
| 60 |
return defer.fail(failure.Failure(dns.DomainError(name))) |
|---|
| 61 |
else: |
|---|
| 62 |
if self.verbose: |
|---|
| 63 |
log.msg('Cache hit for ' + repr(name)) |
|---|
| 64 |
diff = now - when |
|---|
| 65 |
return defer.succeed(( |
|---|
| 66 |
[dns.RRHeader(str(r.name), r.type, r.cls, r.ttl - diff, r.payload) for r in ans], |
|---|
| 67 |
[dns.RRHeader(str(r.name), r.type, r.cls, r.ttl - diff, r.payload) for r in auth], |
|---|
| 68 |
[dns.RRHeader(str(r.name), r.type, r.cls, r.ttl - diff, r.payload) for r in add] |
|---|
| 69 |
)) |
|---|
| 70 |
|
|---|
| 71 |
|
|---|
| 72 |
def lookupAllRecords(self, name, timeout = None): |
|---|
| 73 |
return defer.fail(failure.Failure(dns.DomainError(name))) |
|---|
| 74 |
|
|---|
| 75 |
|
|---|
| 76 |
def cacheResult(self, query, payload): |
|---|
| 77 |
if self.verbose > 1: |
|---|
| 78 |
log.msg('Adding %r to cache' % query) |
|---|
| 79 |
|
|---|
| 80 |
self.cache[query] = (time.time(), payload) |
|---|
| 81 |
|
|---|
| 82 |
if self.cancel.has_key(query): |
|---|
| 83 |
self.cancel[query].cancel() |
|---|
| 84 |
|
|---|
| 85 |
s = list(payload[0]) + list(payload[1]) + list(payload[2]) |
|---|
| 86 |
m = s[0].ttl |
|---|
| 87 |
for r in s: |
|---|
| 88 |
m = min(m, r.ttl) |
|---|
| 89 |
|
|---|
| 90 |
from twisted.internet import reactor |
|---|
| 91 |
self.cancel[query] = reactor.callLater(m, self.clearEntry, query) |
|---|
| 92 |
|
|---|
| 93 |
|
|---|
| 94 |
def clearEntry(self, query): |
|---|
| 95 |
del self.cache[query] |
|---|
| 96 |
del self.cancel[query] |
|---|