"""A cache for resource records This module contains a class which implements a cache for DNS resource records, you still have to do most of the implementation. The module also provides a class and a function for converting ResourceRecords from and to JSON strings. It is highly recommended to use these. """ import json import time from dns.resource import ResourceRecord, RecordData from dns.types import Type from dns.classes import Class class ResourceEncoder(json.JSONEncoder): """ Conver ResourceRecord to JSON Usage: string = json.dumps(records, cls=ResourceEncoder, indent=4) """ def default(self, obj): if isinstance(obj, ResourceRecord): return { "name": obj.name, "type": Type.to_string(obj.type_), "class": Class.to_string(obj.class_), "ttl": obj.ttl, "rdata": obj.rdata.data, "timestamp": obj.timestamp } return json.JSONEncoder.default(self, obj) def resource_from_json(dct): """ Convert JSON object to ResourceRecord Usage: records = json.loads(string, object_hook=resource_from_json) """ name = dct["name"] type_ = Type.from_string(dct["type"]) class_ = Class.from_string(dct["class"]) ttl = dct["ttl"] rdata = RecordData.create(type_, dct["rdata"]) timestamp = dct["timestamp"] return ResourceRecord(name, type_, class_, ttl, rdata, timestamp) class RecordCache(object): """ Cache for ResourceRecords """ FILE = '.dns.cache' def __init__(self): """ Initialize the RecordCache Args: ttl (int): TTL of cached entries (if > 0) """ self.records = [] self.read_cache_file() def __del__(self): self.write_cache_file() def remove_old(self): """Remove entries for which the TTL has expired""" now = int(time.clock()) for record in reversed(self.records): if record.ttl + record.timestamp < now: self.records.remove(record) def lookup(self, dname, type_, class_): """ Lookup resource records in cache Lookup for the resource records for a domain name with a specific type and class. Args: dname (str): domain name type_ (Type): type class_ (Class): class """ self.remove_old() for record in self.records: if record.name == dname and \ record.type_ == type_ and \ record.class_ == class_: yield record def add_record(self, record): """ Add a new Record to the cache Args: record (ResourceRecord): the record added to the cache """ self.records.append(record) def read_cache_file(self): """ Read the cache file from disk """ try: with open(self.FILE, 'r') as jsonfile: self.records = json.load( jsonfile, object_hook=resource_from_json) except IOError: pass def write_cache_file(self): """ Write the cache file to disk """ self.remove_old() with open(self.FILE, 'w') as jsonfile: json.dump(self.records, jsonfile, cls=ResourceEncoder, indent=4)