def validate_mechanism(self, mech):
        """Parse and validate a mechanism.
    Returns mech,m,arg,cidrlength,result

    Examples:
    >>> q = query(s='strong-bad@email.example.com.',
    ...           h='mx.example.org', i='192.0.2.3')
    >>> q.validate_mechanism('A')
    ('A', 'a', 'email.example.com', 32, 'pass')

    >>> q = query(s='strong-bad@email.example.com',
    ...           h='mx.example.org', i='192.0.2.3')
    >>> q.validate_mechanism('A//64')
    ('A//64', 'a', 'email.example.com', 32, 'pass')

    >>> q.validate_mechanism('A/24//64')
    ('A/24//64', 'a', 'email.example.com', 24, 'pass')
    
    >>> q.validate_mechanism('?mx:%{d}/27')
    ('?mx:%{d}/27', 'mx', 'email.example.com', 27, 'neutral')

    >>> try: q.validate_mechanism('ip4:1.2.3.4/247')
    ... except PermError as x: print(x)
    Invalid IP4 CIDR length: ip4:1.2.3.4/247
    
    >>> try: q.validate_mechanism('ip4:1.2.3.4/33')
    ... except PermError as x: print(x)
    Invalid IP4 CIDR length: ip4:1.2.3.4/33

    >>> try: q.validate_mechanism('a:example.com:8080')
    ... except PermError as x: print(x)
    Invalid domain found (use FQDN): example.com:8080
    
    >>> try: q.validate_mechanism('ip4:1.2.3.444/24')
    ... except PermError as x: print(x)
    Invalid IP4 address: ip4:1.2.3.444/24
    
    >>> try: q.validate_mechanism('ip4:1.2.03.4/24')
    ... except PermError as x: print(x)
    Invalid IP4 address: ip4:1.2.03.4/24
    
    >>> try: q.validate_mechanism('-all:3030')
    ... except PermError as x: print(x)
    Invalid all mechanism format - only qualifier allowed with all: -all:3030

    >>> q.validate_mechanism('-mx:%%%_/.Clara.de/27')
    ('-mx:%%%_/.Clara.de/27', 'mx', '% /.Clara.de', 27, 'fail')

    >>> q.validate_mechanism('~exists:%{i}.%{s1}.100/86400.rate.%{d}')
    ('~exists:%{i}.%{s1}.100/86400.rate.%{d}', 'exists', '192.0.2.3.com.100/86400.rate.email.example.com', 32, 'softfail')

    >>> q.validate_mechanism('a:mail.example.com.')
    ('a:mail.example.com.', 'a', 'mail.example.com', 32, 'pass')

    >>> try: q.validate_mechanism('a:mail.example.com,')
    ... except PermError as x: print(x)
    Do not separate mechnisms with commas: a:mail.example.com,

    >>> q = query(s='strong-bad@email.example.com',
    ...           h='mx.example.org', i='2001:db8:1234::face:b007')    
    >>> q.validate_mechanism('A//64')
    ('A//64', 'a', 'email.example.com', 64, 'pass')

    >>> q.validate_mechanism('A/16')
    ('A/16', 'a', 'email.example.com', 128, 'pass')

    >>> q.validate_mechanism('A/16//48')
    ('A/16//48', 'a', 'email.example.com', 48, 'pass')

    """
        if mech.endswith( "," ):
            self.note_error('Do not separate mechnisms with commas', mech)
            mech = mech[:-1]
        # a mechanism
        m, arg, cidrlength, cidr6length = parse_mechanism(mech, self.d)
        # map '?' '+' or '-' to 'neutral' 'pass' or 'fail'
        if m:
            result = RESULTS.get(m[0])
            if result:
                # eat '?' '+' or '-'
                m = m[1:]
            else:
                # default pass
                result = 'pass'
        if m in COMMON_MISTAKES:
            self.note_error('Unknown mechanism found', mech)
            m = COMMON_MISTAKES[m]

        if m == 'a' and RE_IP4.match(arg):
            x = self.note_error(
              'Use the ip4 mechanism for ip4 addresses', mech)
            m = 'ip4'


        # validate cidr and dual-cidr
        if m in ('a', 'mx'):
            if cidrlength is None:
                cidrlength = 32;
            elif cidrlength > 32:
                raise PermError('Invalid IP4 CIDR length', mech)
            if cidr6length is None:
                cidr6length = 128
            elif cidr6length > 128:
                raise PermError('Invalid IP6 CIDR length', mech)
            if self.v == 'ip6':
                cidrlength = cidr6length
        elif m == 'ip4' or RE_IP4.match(m):
            if m != 'ip4':
              self.note_error( 'Missing IP4' , mech)
              m,arg = 'ip4',m
            if cidr6length is not None:
                raise PermError('Dual CIDR not allowed', mech)
            if cidrlength is None:
                cidrlength = 32;
            elif cidrlength > 32:
                raise PermError('Invalid IP4 CIDR length', mech)
            if not RE_IP4.match(arg):
                raise PermError('Invalid IP4 address', mech)
        elif m == 'ip6':
            if cidr6length is not None:
                raise PermError('Dual CIDR not allowed', mech)
            if cidrlength is None:
                cidrlength = 128
            elif cidrlength > 128:
                raise PermError('Invalid IP6 CIDR length', mech)
            if not RE_IP6.match(arg):
                raise PermError('Invalid IP6 address', mech)
        else:
            if cidrlength is not None or cidr6length is not None:
              if m in ALL_MECHANISMS:
                raise PermError('CIDR not allowed', mech)
            cidrlength = self.cidrmax

        if m in ('a', 'mx', 'ptr', 'exists', 'include'):
            if m == 'exists' and not arg:
                raise PermError('implicit exists not allowed', mech)
            arg = self.expand_domain(arg)
            if not arg:
                raise PermError('empty domain:',mech)
            if m == 'include':
                if arg == self.d:
                    if mech != 'include':
                        raise PermError('include has trivial recursion', mech)
                    raise PermError('include mechanism missing domain', mech)
            return mech, m, arg, cidrlength, result

        # validate 'all' mechanism per RFC 4408 ABNF
        if m == 'all' and mech.count(':'):
            # print '|'+ arg + '|', mech, self.d,
            self.note_error(
            'Invalid all mechanism format - only qualifier allowed with all'
              , mech)
        if m in ALL_MECHANISMS:
            return mech, m, arg, cidrlength, result
        if m[1:] in ALL_MECHANISMS:
            x = self.note_error(
                'Unknown qualifier, RFC 4408 para 4.6.1, found in', mech)
        else:
            x = self.note_error('Unknown mechanism found', mech)
        return mech, m, arg, cidrlength, x
Parse and validate a mechanism.
    Returns mech,m,arg,cidrlength,result

    Examples:
    >>> q = query(s='strong-bad@email.example.com.',
    ...           h='mx.example.org', i='192.0.2.3')
    >>> q.validate_mechanism('A')
    ('A', 'a', 'email.example.com', 32, 'pass')

    >>> q = query(s='strong-bad@email.example.com',
    ...           h='mx.example.org', i='192.0.2.3')
    >>> q.validate_mechanism('A//64')
    ('A//64', 'a', 'email.example.com', 32, 'pass')

    >>> q.validate_mechanism('A/24//64')
    ('A/24//64', 'a', 'email.example.com', 24, 'pass')
    
    >>> q.validate_mechanism('?mx:%{d}/27')
    ('?mx:%{d}/27', 'mx', 'email.example.com', 27, 'neutral')

    >>> try: q.validate_mechanism('ip4:1.2.3.4/247')
    ... except PermError as x: print(x)
    Invalid IP4 CIDR length: ip4:1.2.3.4/247
    
    >>> try: q.validate_mechanism('ip4:1.2.3.4/33')
    ... except PermError as x: print(x)
    Invalid IP4 CIDR length: ip4:1.2.3.4/33

    >>> try: q.validate_mechanism('a:example.com:8080')
    ... except PermError as x: print(x)
    Invalid domain found (use FQDN): example.com:8080
    
    >>> try: q.validate_mechanism('ip4:1.2.3.444/24')
    ... except PermError as x: print(x)
    Invalid IP4 address: ip4:1.2.3.444/24
    
    >>> try: q.validate_mechanism('ip4:1.2.03.4/24')
    ... except PermError as x: print(x)
    Invalid IP4 address: ip4:1.2.03.4/24
    
    >>> try: q.validate_mechanism('-all:3030')
    ... except PermError as x: print(x)
    Invalid all mechanism format - only qualifier allowed with all: -all:3030

    >>> q.validate_mechanism('-mx:%%%_/.Clara.de/27')
    ('-mx:%%%_/.Clara.de/27', 'mx', '% /.Clara.de', 27, 'fail')

    >>> q.validate_mechanism('~exists:%{i}.%{s1}.100/86400.rate.%{d}')
    ('~exists:%{i}.%{s1}.100/86400.rate.%{d}', 'exists', '192.0.2.3.com.100/86400.rate.email.example.com', 32, 'softfail')

    >>> q.validate_mechanism('a:mail.example.com.')
    ('a:mail.example.com.', 'a', 'mail.example.com', 32, 'pass')

    >>> try: q.validate_mechanism('a:mail.example.com,')
    ... except PermError as x: print(x)
    Do not separate mechnisms with commas: a:mail.example.com,

    >>> q = query(s='strong-bad@email.example.com',
    ...           h='mx.example.org', i='2001:db8:1234::face:b007')    
    >>> q.validate_mechanism('A//64')
    ('A//64', 'a', 'email.example.com', 64, 'pass')

    >>> q.validate_mechanism('A/16')
    ('A/16', 'a', 'email.example.com', 128, 'pass')

    >>> q.validate_mechanism('A/16//48')
    ('A/16//48', 'a', 'email.example.com', 48, 'pass')

    
python/spf.py