def check0(self, spf, recursion):
        """Test this query information against SPF text.

        Returns (result, mta-status-code, explanation) where
        result in ['fail', 'unknown', 'pass', 'none']
        """

        if not spf:
            return ('none', 250, EXPLANATIONS['none'])

        # split string by whitespace, drop the 'v=spf1'
        spf = spf.split()
        # Catch case where SPF record has no spaces.
        # Can never happen with conforming dns_spf(), however
        # in the future we might want to give warnings
        # for common mistakes like IN TXT "v=spf1" "mx" "-all"
        # in relaxed mode.
        if spf[0].lower() != 'v=spf1':
            if self.strict > 1:
                raise AmbiguityWarning('Invalid SPF record in', self.d)
            return ('none', 250, EXPLANATIONS['none'])
        spf = spf[1:]

        # copy of explanations to be modified by exp=
        exps = self.exps
        redirect = None

        # no mechanisms at all cause unknown result, unless
        # overridden with 'default=' modifier
        #
        default = 'neutral'
        mechs = []

        modifiers = []
        # Look for modifiers
        #
        for mech in spf:
            m = RE_MODIFIER.split(mech)[1:]
            if len(m) != 2:
                mechs.append(self.validate_mechanism(mech))
                continue

            mod,arg = m
            if mod in modifiers:
                if mod == 'redirect':
                    raise PermError('redirect= MUST appear at most once',mech)
                self.note_error('%s= MUST appear at most once'%mod,mech)
                # just use last one in lax mode
            modifiers.append(mod)
            if mod == 'exp':
                # always fetch explanation to check permerrors
                if not arg:
                    raise PermError('exp has empty domain-spec:',arg)
                arg = self.expand_domain(arg)
                if arg:
                    try:
                        exp = self.get_explanation(arg)
                        if exp and not recursion:
                            # only set explanation in base recursion level
                            self.set_explanation(exp)
                    except: pass
            elif mod == 'redirect':
                self.check_lookups()
                redirect = self.expand_domain(arg)
                if not redirect:
                    raise PermError('redirect has empty domain:',arg)
            elif mod == 'default':
                # default modifier is obsolete
                if self.strict > 1:
                    raise AmbiguityWarning('The default= modifier is obsolete.')
                if not self.strict and self.default_modifier:
                    # might be an old policy, so do it anyway
                    arg = self.expand(arg)
                    # default=- is the same as default=fail
                    default = RESULTS.get(arg, default)
            elif mod == 'op':
                if not recursion:
                    for v in arg.split('.'):
                        if v: self.options[v] = True
            else:
                # spf rfc: 3.6 Unrecognized Mechanisms and Modifiers
                self.expand(m[1])       # syntax error on invalid macro

        # Evaluate mechanisms
        #
        for mech, m, arg, cidrlength, result in mechs:

            if m == 'include':
                self.check_lookups()
                d = self.dns_spf(arg)
                if self.verbose: self.log("include",arg,d)
                res, code, txt = self.check1(d,arg, recursion + 1)
                if res == 'pass':
                    break
                if res == 'none':
                    self.note_error(
                        'No valid SPF record for included domain: %s' %arg,
                      mech)
                res = 'neutral'
                continue
            elif m == 'all':
                break

            elif m == 'exists':
                self.check_lookups()
                try:
                    if len(self.dns_a(arg,'A')) > 0:
                        break
                except AmbiguityWarning:
                    # Exists wants no response sometimes so don't raise
                    # the warning.
                    pass

            elif m == 'a':
                self.check_lookups()
                if self.cidrmatch(self.dns_a(arg,self.A), cidrlength):
                    break

            elif m == 'mx':
                self.check_lookups()
                if self.cidrmatch(self.dns_mx(arg), cidrlength):
                    break

            elif m == 'ip4':
                if self.v == 'in-addr': # match own connection type only
                    try:
                        if self.cidrmatch([arg], cidrlength): break
                    except socket.error:
                        raise PermError('syntax error', mech)

            elif m == 'ip6':
                if self.v == 'ip6': # match own connection type only
                    try:
                        if self.cidrmatch([arg], cidrlength): break
                    except socket.error:
                        raise PermError('syntax error', mech)

            elif m == 'ptr':
                self.check_lookups()
                if domainmatch(self.validated_ptrs(), arg):
                    break

        else:
            # no matches
            if redirect:
                #Catch redirect to a non-existant SPF record.
                redirect_record = self.dns_spf(redirect)
                if not redirect_record:
                    raise PermError('redirect domain has no SPF record',
                        redirect)
                if self.verbose: self.log("redirect",redirect,redirect_record)
                # forget modifiers on redirect
                if not recursion:
                  self.exps = dict(self.defexps)
                  self.options = {}
                return self.check1(redirect_record, redirect, recursion)
            result = default
            mech = None

        if not recursion:       # record matching mechanism at base level
            self.mechanism = mech
        if result == 'fail':
            return (result, 550, exps[result])
        else:
            return (result, 250, exps[result])
Test this query information against SPF text.

        Returns (result, mta-status-code, explanation) where
        result in ['fail', 'unknown', 'pass', 'none']
        
python/spf.py