Merge lp:~phil.pennock/mailman/dmarc-reject into lp:~jimpop/mailman/dmarc-reject

Proposed by Phil Pennock
Status: Merged
Approved by: Jim Popovitch
Approved revision: 1376
Merged at revision: 1376
Proposed branch: lp:~phil.pennock/mailman/dmarc-reject
Merge into: lp:~jimpop/mailman/dmarc-reject
Diff against target: 71 lines (+44/-5)
1 file modified
Mailman/Utils.py (+44/-5)
To merge this branch: bzr merge lp:~phil.pennock/mailman/dmarc-reject
Reviewer Review Type Date Requested Status
Jim Popovitch Approve
Review via email: mp+153947@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Jim Popovitch (jimpop) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'Mailman/Utils.py'
--- Mailman/Utils.py 2013-03-03 08:04:37 +0000
+++ Mailman/Utils.py 2013-03-18 22:17:21 +0000
@@ -35,6 +35,7 @@
35import base6435import base64
36import random36import random
37import urlparse37import urlparse
38import collections
38import htmlentitydefs39import htmlentitydefs
39import email.Header40import email.Header
40import email.Iterators41import email.Iterators
@@ -1081,18 +1082,56 @@
1081 resolver.timeout = 11082 resolver.timeout = 1
1082 resolver.lifetime = 51083 resolver.lifetime = 5
1083 txt_recs = resolver.query(dmarc_domain, dns.rdatatype.TXT)1084 txt_recs = resolver.query(dmarc_domain, dns.rdatatype.TXT)
1084 except dns.resolver.NXDOMAIN:1085 except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
1085 return False1086 return False
1086 except DNSException, e:1087 except DNSException, e:
1087 syslog('error', 'DNSException: Unable to query DMARC policy for %s (%s). %s',1088 syslog('error', 'DNSException: Unable to query DMARC policy for %s (%s). %s',
1088 email, dmarc_domain, e.__class__)1089 email, dmarc_domain, e.__class__)
1089 return False1090 return False
1090 else:1091 else:
1092# people are already being dumb, don't trust them to provide honest DNS
1093# where the answer section only contains what was asked for, nor to include
1094# CNAMEs before the values they point to.
1095 full_record = ""
1096 results_by_name = collections.defaultdict(list)
1097 cnames = {}
1098 want_names = set([dmarc_domain + '.'])
1091 for txt_rec in txt_recs.response.answer:1099 for txt_rec in txt_recs.response.answer:
1092 assert( txt_rec.rdtype == dns.rdatatype.TXT)1100 if txt_rec.rdtype == dns.rdatatype.CNAME:
1093 if re.search(r"[^s]p=reject", "".join(txt_rec.items[0].strings), re.IGNORECASE):1101 cnames[txt_rec.name.to_text()] = txt_rec.items[0].target.to_text()
1094 return True1102 if txt_rec.rdtype != dns.rdatatype.TXT:
1095 1103 continue
1104 results_by_name[txt_rec.name.to_text()].append("".join(txt_rec.items[0].strings))
1105 expands = list(want_names)
1106 seen = set(expands)
1107 while expands:
1108 item = expands.pop(0)
1109 if item in cnames:
1110 if cnames[item] in seen:
1111 continue # cname loop
1112 expands.append(cnames[item])
1113 seen.add(cnames[item])
1114 want_names.add(cnames[item])
1115 want_names.discard(item)
1116
1117 if len(want_names) != 1:
1118 syslog('error', 'multiple DMARC entries in results for %s, processing each to be strict',
1119 dmarc_domain)
1120 for name in want_names:
1121 if name not in results_by_name:
1122 continue
1123 dmarcs = filter(lambda n: n.startswith('v=DMARC1;'), results_by_name[name])
1124 if len(dmarcs) == 0:
1125 return False
1126 if len(dmarcs) > 1:
1127 syslog('error', 'RRset of TXT records for %s has %d v=DMARC1 entries; testing them all',
1128 dmarc_domain, len(dmarc))
1129 for entry in dmarcs:
1130 if re.search(r'\bp=reject\b', entry, re.IGNORECASE):
1131 syslog('info', 'DMARC lookup for %s (%s) found p=reject in %s = %s',
1132 email, dmarc_domain, name, entry)
1133 return True
1134
1096 return False1135 return False
10971136
10981137

Subscribers

People subscribed via source and target branches

to all changes: