22 kCheckForPruneEvery = 20
23 kMaxTTL = 60 * 60 * 24 * 7
26 kMinTTL = 24 * 60 * 60 * 1
27 kPruneThreshold = 5000
34 def __init__(self, qType, answer, question, expiresAt, now):
47 """Sort the sequence of objects by object's attribute
50 seq - the list or any sequence (including immutable one) of objects to sort.
51 attr - the name of attribute to sort by
54 the sorted list of objects.
63 intermed = map(
None, map(getattr, seq, (attr,)*len(seq)), xrange(len(seq)), seq)
65 return map(operator.getitem, intermed, (-1,) * len(intermed))
69 def __init__(self, dnsServer=None, cachefile=""):
71 self.printStatsAtEnd =
False
84 self.returnSinglePTR =
True
87 self.cacheErrorSecs=5*60
94 self.cachefile = os.path.expanduser(cachefile)
97 if self.cachefile
and os.path.exists(self.cachefile):
101 os.unlink(self.cachefile)
103 if self.caches
is None:
104 self.caches = {
"A": {},
"PTR": {}}
106 if options[
"globals",
"verbose"]:
107 if self.caches[
"A"]
or self.caches[
"PTR"]:
108 print >> sys.stderr,
"opened existing cache with",
109 print >> sys.stderr, len(self.caches[
"A"]),
"A records",
110 print >> sys.stderr,
"and", len(self.caches[
"PTR"]),
111 print >> sys.stderr,
"PTR records"
113 print >> sys.stderr,
"opened new cache"
119 if dnsServer ==
None:
120 DNS.DiscoverNameServers()
121 self.queryObj = DNS.DnsRequest()
123 self.queryObj = DNS.DnsRequest(server=dnsServer)
127 if self.printStatsAtEnd:
133 for key,val
in self.caches.items():
135 for item
in val.values():
136 totAnswers+=len(item)
137 print >> sys.stderr,
"cache", key,
"has", len(self.caches[key]),
138 print >> sys.stderr,
"question(s) and", totAnswers,
"answer(s)"
140 print >> sys.stderr,
"No queries"
142 print >> sys.stderr, self.hits,
"hits,", self.
misses,
"misses",
143 print >> sys.stderr,
"(%.1f%% hits)" % \
144 (self.hits/float(self.hits+self.
misses)*100)
151 for cache
in self.caches.values():
152 for val
in cache.values():
159 if allAnswers[-1].expiresAt > now:
161 answer = allAnswers.pop()
162 c = self.caches[answer.qType]
163 c[answer.question].remove(answer)
164 if not c[answer.question]:
165 del c[answer.question]
167 if options[
"globals",
"verbose"]:
170 if len(allAnswers)<=kPruneDownTo:
179 numToDelete = len(allAnswers)-kPruneDownTo
180 for _count
in xrange(numToDelete):
181 answer = allAnswers.pop()
182 c = self.caches[answer.qType]
183 c[answer.question].remove(answer)
184 if not c[answer.question]:
185 del c[answer.question]
191 if len(listOfObjs) == 1
and listOfObjs[0].answer ==
None:
194 if listOfObjs[0].qType ==
"PTR" and self.returnSinglePTR:
195 return listOfObjs[0].answer
197 return [ obj.answer
for obj
in listOfObjs ]
201 qType = qType.upper()
202 if qType
not in (
"A",
"PTR"):
203 raise ValueError,
"Query type must be one of A, PTR"
205 now = int(time.time())
212 if len(self.caches[
"A"])+len(self.caches[
"PTR"])>kPruneThreshold:
215 cacheToLookIn = self.caches[qType]
218 answers = cacheToLookIn[question]
225 while ind<len(answers):
226 thisAnswer = answers[ind]
227 if thisAnswer.expiresAt<now:
230 thisAnswer.lastUsed = now
233 print >> sys.stderr,
"lookup failure:", question
236 del cacheToLookIn[question]
245 qList = question.split(
".")
247 queryQuestion =
".".join(qList)+
".in-addr.arpa"
249 queryQuestion = question
253 reply = self.queryObj.req(queryQuestion, qtype=qType,
254 timeout=self.dnsTimeout)
255 except DNS.Base.DNSError,detail:
256 if detail.args[0]
not in (
"Timeout",
"nothing to lookup"):
257 print >> sys.stderr, detail.args[0]
258 print >> sys.stderr,
"Error, fixme", detail
259 print >> sys.stderr,
"Question was", queryQuestion
260 print >> sys.stderr,
"Original question was", question
261 print >> sys.stderr,
"Type was", qType
263 self.cacheErrorSecs+now, now)]
264 cacheToLookIn[question] = objs
266 except socket.gaierror,detail:
267 print >> sys.stderr,
"DNS connection failure:", self.queryObj.ns, detail
268 print >> sys.stderr,
"Defaults:", DNS.defaults
271 for answer
in reply.answers:
272 if answer[
"typename"] == qType:
280 ttl = max(min(int(answer[
"ttl"]), kMaxTTL), kMinTTL)
290 cacheToLookIn[question] = objs
294 if not reply.authority:
296 self.cacheErrorSecs+now, now)]
297 cacheToLookIn[question] = objs
308 auth = reply.authority[0]
309 auTTL = int(auth[
"ttl"])
310 for item
in auth[
"data"]:
311 if type(item) == types.TupleType
and item[0] ==
"minimum":
313 cacheNeg = min(auMin,auTTL)
317 objs = [
lookupResult(qType,
None, question, cacheNeg+now, now)]
319 cacheToLookIn[question] = objs
325 c =
cache(cachefile=os.path.expanduser(
"~/.dnscache"))
326 c.printStatsAtEnd =
True
327 for host
in [
"www.python.org",
"www.timsbloggers.com",
328 "www.seeputofor.com",
"www.completegarbage.tv",
329 "www.tradelinkllc.com"]:
330 print >> sys.stderr,
"checking", host
333 print >> sys.stderr, ips, time.time()-now
336 print >> sys.stderr, ips, time.time()-now
341 name = c.lookup(ip, qType=
"PTR")
342 print >> sys.stderr, name, time.time()-now
344 name = c.lookup(ip, qType=
"PTR")
345 print >> sys.stderr, name, time.time()-now
347 print >> sys.stderr,
"unknown"
353 if __name__ ==
"__main__":