Skip to content
Snippets Groups Projects
Commit ef413913 authored by Stuart Gathman's avatar Stuart Gathman
Browse files

Allow extended processing for MX count.

parent 8ad4b161
No related branches found
No related tags found
No related merge requests found
...@@ -47,6 +47,23 @@ For news, bugfixes, etc. visit the home page for this implementation at ...@@ -47,6 +47,23 @@ For news, bugfixes, etc. visit the home page for this implementation at
# Terrence is not responding to email. # Terrence is not responding to email.
# #
# $Log$ # $Log$
# Revision 1.11 2005/07/15 18:03:02 customdesigned
# Fix unknown Received-SPF header broken by result changes
#
# Revision 1.10 2005/07/15 16:17:05 customdesigned
# Start type99 support.
# Make Scott's "/" support in parse_mechanism more elegant as requested.
# Add test case for "/" support.
#
# Revision 1.9 2005/07/15 03:33:14 kitterma
# Fix for bug 1238403 - Crash if non-CIDR / present. Also added
# validation check for valid IPv4 CIDR range.
#
# Revision 1.8 2005/07/14 04:18:01 customdesigned
# Bring explanations and Received-SPF header into line with
# the unknown=PermErr and error=TempErr convention.
# Hope my case-sensitive mech fix doesn't clash with Scotts.
#
# Revision 1.7 2005/07/12 21:43:56 kitterma # Revision 1.7 2005/07/12 21:43:56 kitterma
# Added processing to clarify some cases of unknown # Added processing to clarify some cases of unknown
# qualifier errors (to distinguish between unknown qualifier and # qualifier errors (to distinguish between unknown qualifier and
...@@ -189,6 +206,8 @@ RE_CHAR = re.compile(r'%(%|_|-|(\{[a-zA-Z][0-9]*r?[^\}]*\}))') ...@@ -189,6 +206,8 @@ RE_CHAR = re.compile(r'%(%|_|-|(\{[a-zA-Z][0-9]*r?[^\}]*\}))')
# Regular expression to break up a macro expansion # Regular expression to break up a macro expansion
RE_ARGS = re.compile(r'([0-9]*)(r?)([^0-9a-zA-Z]*)') RE_ARGS = re.compile(r'([0-9]*)(r?)([^0-9a-zA-Z]*)')
RE_CIDR = re.compile(r'/(1[0-9]*|2[0-9]*|3[0-2]*)$')
# Local parts and senders have their delimiters replaced with '.' during # Local parts and senders have their delimiters replaced with '.' during
# macro expansion # macro expansion
# #
...@@ -196,11 +215,12 @@ JOINERS = {'l': '.', 's': '.'} ...@@ -196,11 +215,12 @@ JOINERS = {'l': '.', 's': '.'}
RESULTS = {'+': 'pass', '-': 'fail', '?': 'neutral', '~': 'softfail', RESULTS = {'+': 'pass', '-': 'fail', '?': 'neutral', '~': 'softfail',
'pass': 'pass', 'fail': 'fail', 'unknown': 'unknown', 'pass': 'pass', 'fail': 'fail', 'unknown': 'unknown',
'neutral': 'neutral', 'softfail': 'softfail', 'error': 'error', 'neutral': 'neutral', 'softfail': 'softfail',
'none': 'none', 'deny': 'fail' } 'none': 'none', 'deny': 'fail' }
EXPLANATIONS = {'pass': 'sender SPF verified', 'fail': 'access denied', EXPLANATIONS = {'pass': 'sender SPF verified', 'fail': 'access denied',
'unknown': 'SPF unknown (PermError)', 'unknown': 'permanent error in processing',
'error': 'temporary error in processing',
'softfail': 'domain in transition', 'softfail': 'domain in transition',
'neutral': 'access neither permitted nor denied', 'neutral': 'access neither permitted nor denied',
'none': '' 'none': ''
...@@ -345,11 +365,11 @@ class query(object): ...@@ -345,11 +365,11 @@ class query(object):
return ('error', 450, 'SPF Temporary Error: ' + str(x)) return ('error', 450, 'SPF Temporary Error: ' + str(x))
except PermError,x: except PermError,x:
self.prob = x.msg self.prob = x.msg
if x.mech:
self.mech.append(x.mech) self.mech.append(x.mech)
# Pre-Lentczner draft treats this as an unknown result # Pre-Lentczner draft treats this as an unknown result
# and equivalent to no SPF record. # and equivalent to no SPF record.
return ('unknown', 550, 'SPF Permanent Error: ' + str(x)) return ('unknown', 550, 'SPF Permanent Error: ' + str(x))
# return ('error', 550, 'SPF Permanent Error: ' + str(x))
def check1(self, spf, domain, recursion): def check1(self, spf, domain, recursion):
# spf rfc: 3.7 Processing Limits # spf rfc: 3.7 Processing Limits
...@@ -393,8 +413,7 @@ class query(object): ...@@ -393,8 +413,7 @@ class query(object):
if len(m) != 2: continue if len(m) != 2: continue
if m[0] == 'exp': if m[0] == 'exp':
exps['fail'] = exps['unknown'] = \ self.set_default_explanation(self.get_explanation(m[1]))
self.get_explanation(m[1])
elif m[0] == 'redirect': elif m[0] == 'redirect':
self.check_lookups() self.check_lookups()
redirect = self.expand(m[1]) redirect = self.expand(m[1])
...@@ -502,7 +521,7 @@ class query(object): ...@@ -502,7 +521,7 @@ class query(object):
# fine tune the error). # fine tune the error).
# eat one character and try again: # eat one character and try again:
m = m[1:] m = m[1:]
if m in ['a', 'mx', 'ptr', 'exists', 'include', 'ip4', 'ip6', 'all']: if m in ('a', 'mx', 'ptr', 'exists', 'include', 'ip4', 'ip6', 'all'):
raise PermError('Unknown qualifier, IETF draft para 4.6.1, found in',mech) raise PermError('Unknown qualifier, IETF draft para 4.6.1, found in',mech)
else: else:
raise PermError('Unknown mechanism found',mech) raise PermError('Unknown mechanism found',mech)
...@@ -636,13 +655,16 @@ class query(object): ...@@ -636,13 +655,16 @@ class query(object):
is found. is found.
""" """
a = [t for t in self.dns_txt(domain) if t.startswith('v=spf1')] a = [t for t in self.dns_txt(domain) if t.startswith('v=spf1')]
if not a: if len(a) == 1:
return a[0]
#a = [t for t in self.dns_99(domain) if t.startswith('v=spf1')]
#if len(a) == 1:
# return a[0]
if DELEGATE: if DELEGATE:
a = [t a = [t
for t in self.dns_txt(domain+'._spf.'+DELEGATE) for t in self.dns_txt(domain+'._spf.'+DELEGATE)
if t.startswith('v=spf1') if t.startswith('v=spf1')
] ]
if len(a) == 1: if len(a) == 1:
return a[0] return a[0]
return None return None
...@@ -652,6 +674,11 @@ class query(object): ...@@ -652,6 +674,11 @@ class query(object):
if domainname: if domainname:
return [''.join(a) for a in self.dns(domainname, 'TXT')] return [''.join(a) for a in self.dns(domainname, 'TXT')]
return [] return []
def dns_99(self, domainname):
"Get a list of TYPE99 records for a domain name."
if domainname:
return [''.join(a) for a in self.dns(domainname, 'TYPE99')]
return []
def dns_mx(self, domainname): def dns_mx(self, domainname):
"""Get a list of IP addresses for all MX exchanges for a """Get a list of IP addresses for all MX exchanges for a
...@@ -718,7 +745,9 @@ class query(object): ...@@ -718,7 +745,9 @@ class query(object):
result = self.dns(cname, qtype) result = self.dns(cname, qtype)
return result return result
def get_header(self,res,receiver): def get_header(self,res,receiver=None):
if not receiver:
receiver = self.r
if res in ('pass','fail','softfail'): if res in ('pass','fail','softfail'):
return '%s (%s: %s) client-ip=%s; envelope-from=%s; helo=%s;' % ( return '%s (%s: %s) client-ip=%s; envelope-from=%s; helo=%s;' % (
res,receiver,self.get_header_comment(res),self.i, res,receiver,self.get_header_comment(res),self.i,
...@@ -749,10 +778,10 @@ class query(object): ...@@ -749,10 +778,10 @@ class query(object):
% (self.i,sender) % (self.i,sender)
#"%s does not designate permitted sender hosts" % sender #"%s does not designate permitted sender hosts" % sender
elif res == 'unknown': return \ elif res == 'unknown': return \
"error in processing during lookup of domain of %s: %s" \ "permanent error in processing domain of %s: %s" \
% (sender, self.prob) % (sender, self.prob)
elif res == 'error': return \ elif res == 'error': return \
"error in processing during lookup of %s" % sender "temporary error in processing during lookup of %s" % sender
elif res == 'fail': return \ elif res == 'fail': return \
"domain of %s does not designate %s as permitted sender" \ "domain of %s does not designate %s as permitted sender" \
% (sender,self.i) % (sender,self.i)
...@@ -796,19 +825,19 @@ def parse_mechanism(str, d): ...@@ -796,19 +825,19 @@ def parse_mechanism(str, d):
>>> parse_mechanism('a/24', 'foo.com') >>> parse_mechanism('a/24', 'foo.com')
('a', 'foo.com', 24) ('a', 'foo.com', 24)
>>> parse_mechanism('a:bar.com/16', 'foo.com')
('a', 'bar.com', 16)
>>> parse_mechanism('A:bar.com/16', 'foo.com') >>> parse_mechanism('A:bar.com/16', 'foo.com')
('a', 'bar.com', 16) ('a', 'bar.com', 16)
>>> parse_mechanism('-exists:%{i}.%{s1}.100/86400.rate.%{d}','foo.com')
('-exists', '%{i}.%{s1}.100/86400.rate.%{d}', 32)
""" """
a = str.split('/') a = RE_CIDR.split(str)
if len(a) == 2: if len(a) == 3:
a, port = a[0], int(a[1]) a, port = a[0], int(a[1])
else: else:
a, port = str, 32 a, port = str, 32
b = a.split(':') b = a.split(':',1)
if len(b) == 2: if len(b) == 2:
return b[0].lower(), b[1], port return b[0].lower(), b[1], port
else: else:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment