def dns(self, name, qtype, cnames=None):
        """DNS query.

        If the result is in cache, return that.  Otherwise pull the
        result from DNS, and cache ALL answers, so additional info
        is available for further queries later.

        CNAMEs are followed.

        If there is no data, [] is returned.

        pre: qtype in ['A', 'AAAA', 'MX', 'PTR', 'TXT', 'SPF']
        post: isinstance(__return__, types.ListType)
        """
        if name.endswith('.'): name = name[:-1]
        if not reduce(lambda x,y:x and 0 < len(y) < 64, name.split('.'),True):
            return []   # invalid DNS name (too long or empty)
        result = self.cache.get( (name, qtype), [])
        if result: return result
        cnamek = (name,'CNAME')
        cname = self.cache.get( cnamek )

        if cname:
            cname = cname[0]
        else:
            safe2cache = query.SAFE2CACHE
            if self.querytime < 0:
                 raise TempError('DNS Error: exceeded max query lookup time')
            if self.querytime < self.timeout and self.querytime > 0:
                timeout = self.querytime
            else:
                timeout = self.timeout
            timethen = time.time()
            for k, v in DNSLookup(name, qtype, self.strict, timeout):
                if k == cnamek:
                    cname = v
                if k[1] == 'CNAME' or (qtype,k[1]) in safe2cache:
                    self.cache.setdefault(k, []).append(v)
                    #if ans and qtype == k[1]:
                    #    self.cache.setdefault((name,qtype), []).append(v)
            result = self.cache.get( (name, qtype), [])
            if self.querytime > 0:
                self.querytime = self.querytime - (time.time()-timethen)
        if not result and cname:
            if not cnames:
                cnames = {}
            elif len(cnames) >= MAX_CNAME:
                #return result    # if too many == NX_DOMAIN
                raise PermError('Length of CNAME chain exceeds %d' % MAX_CNAME)
            cnames[name] = cname
            if cname in cnames:
                raise PermError('CNAME loop')
            result = self.dns(cname, qtype, cnames=cnames)
            if result:
                self.cache[(name,qtype)] = result
        return result
DNS query.

        If the result is in cache, return that.  Otherwise pull the
        result from DNS, and cache ALL answers, so additional info
        is available for further queries later.

        CNAMEs are followed.

        If there is no data, [] is returned.

        pre: qtype in ['A', 'AAAA', 'MX', 'PTR', 'TXT', 'SPF']
        post: isinstance(__return__, types.ListType)
        
python/spf.py