1081
1082
resolver.timeout = 1
1082
1083
resolver.lifetime = 5
1083
1084
txt_recs = resolver.query(dmarc_domain, dns.rdatatype.TXT)
1084
except dns.resolver.NXDOMAIN:
1085
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
1086
1087
except DNSException, e:
1087
1088
syslog('error', 'DNSException: Unable to query DMARC policy for %s (%s). %s',
1088
1089
email, dmarc_domain, e.__class__)
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.
1096
results_by_name = collections.defaultdict(list)
1098
want_names = set([dmarc_domain + '.'])
1091
1099
for txt_rec in txt_recs.response.answer:
1092
assert( txt_rec.rdtype == dns.rdatatype.TXT)
1093
if re.search(r"[^s]p=reject", "".join(txt_rec.items[0].strings), re.IGNORECASE):
1100
if txt_rec.rdtype == dns.rdatatype.CNAME:
1101
cnames[txt_rec.name.to_text()] = txt_rec.items[0].target.to_text()
1102
if txt_rec.rdtype != dns.rdatatype.TXT:
1104
results_by_name[txt_rec.name.to_text()].append("".join(txt_rec.items[0].strings))
1105
expands = list(want_names)
1108
item = expands.pop(0)
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)
1117
if len(want_names) != 1:
1118
syslog('error', 'multiple DMARC entries in results for %s, processing each to be strict',
1120
for name in want_names:
1121
if name not in results_by_name:
1123
dmarcs = filter(lambda n: n.startswith('v=DMARC1;'), results_by_name[name])
1124
if len(dmarcs) == 0:
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)