Skip to content

Commit

Permalink
5.1.0 (#119)
Browse files Browse the repository at this point in the history
- Add support for parsing SMTP TLS Reporting ([RFC8460](https://www.rfc-editor.org/rfc/rfc8460.html)) DNS records

Co-authored-by: Sean Whalen <[email protected]>
  • Loading branch information
seanthegeek and seanthegeek authored Dec 26, 2023
1 parent d55fb7d commit 268a2e1
Show file tree
Hide file tree
Showing 9 changed files with 391 additions and 13 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Changelog
=========

5.1.0
-----

- Add support for parsing SMTP TLS Reporting ([RFC8460](https://www.rfc-editor.org/rfc/rfc8460.html)) DNS records

5.0.2
-----

Expand Down
23 changes: 22 additions & 1 deletion checkdmarc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from checkdmarc.spf import check_spf
from checkdmarc.dmarc import check_dmarc
from checkdmarc.bimi import check_bimi
from checkdmarc.smtp_tls_reporting import check_smtp_tls_reporting

"""Copyright 2019-2023 Sean Whalen
Expand Down Expand Up @@ -148,6 +149,13 @@ def check_domains(domains: list[str], parked: bool = False,
timeout=timeout
)

domain_results["smtp_tls_reporting"] = check_smtp_tls_reporting(
domain,
nameservers=nameservers,
resolver=resolver,
timeout=timeout
)

if bimi_selector is not None:
domain_results["bimi"] = check_bimi(
domain,
Expand Down Expand Up @@ -247,6 +255,7 @@ def results_to_csv_rows(results: Union[dict, list[dict]]) -> list[dict]:
row["base_domain"] = result["base_domain"]
row["dnssec"] = result["dnssec"]
row["ns"] = "|".join(ns["hostnames"])
_smtp_tls_reporting = result["smtp_tls_reporting"]
if "error" in ns:
row["ns_error"] = ns["error"]
else:
Expand Down Expand Up @@ -337,6 +346,16 @@ def results_to_csv_rows(results: Union[dict, list[dict]]) -> list[dict]:
u["address"]), addresses))
row["dmarc_ruf"] = "|".join(addresses)
row["dmarc_warnings"] = "|".join(_dmarc["warnings"])
if "error" in _smtp_tls_reporting:
row["smtp_tls_reporting_valid"] = False
row["smtp_tls_reporting_error"] = _smtp_tls_reporting["error"]
else:
row["smtp_tls_reporting_valid"] = True
row["smtp_tls_reporting_rua"] = "|".join(_smtp_tls_reporting[
"tags"]["rua"][
"value"])
row["smtp_tls_reporting_warnings"] = _smtp_tls_reporting[
"warnings"]
rows.append(row)
return rows

Expand All @@ -358,9 +377,11 @@ def results_to_csv(results: dict) -> str:
"tls", "starttls", "spf_record", "dmarc_record",
"dmarc_record_location", "mx", "mx_error", "mx_warnings",
"mta_sts_id", "mta_sts_mode", "mta_sts_max_age",
"smtp_tls_reporting_valid", "smtp_tls_reporting_rua",
"mta_sts_mx", "mta_sts_error", "mta_sts_warnings", "spf_error",
"spf_warnings", "dmarc_error", "dmarc_warnings",
"ns", "ns_error", "ns_warnings"]
"ns", "ns_error", "ns_warnings",
"smtp_tls_reporting_error", "smtp_tls_reporting_warnings"]
output = StringIO(newline="\n")
writer = DictWriter(output, fieldnames=fields)
writer.writeheader()
Expand Down
2 changes: 1 addition & 1 deletion checkdmarc/_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
See the License for the specific language governing permissions and
limitations under the License."""

__version__ = "5.0.2"
__version__ = "5.1.0"

OS = platform.system()
OS_RELEASE = platform.release()
Expand Down
5 changes: 3 additions & 2 deletions checkdmarc/bimi.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ def _query_bimi_record(domain: str, selector: str = "default",
"""
domain = domain.lower()
target = f"{selector}._bimi.{domain}"
txt_prefix = "v=BIMI1"
bimi_record = None
bimi_record_count = 0
unrelated_records = []
Expand All @@ -176,7 +177,7 @@ def _query_bimi_record(domain: str, selector: str = "default",
records = query_dns(target, "TXT", nameservers=nameservers,
resolver=resolver, timeout=timeout)
for record in records:
if record.startswith("v=BIMI1"):
if record.startswith(txt_prefix):
bimi_record_count += 1
else:
unrelated_records.append(record)
Expand All @@ -199,7 +200,7 @@ def _query_bimi_record(domain: str, selector: str = "default",
nameservers=nameservers, resolver=resolver,
timeout=timeout)
for record in records:
if record.startswith("v=BIMI1"):
if record.startswith(txt_prefix):
raise BIMIRecordInWrongLocation(
"The BIMI record must be located at "
f"{target}, not {domain}")
Expand Down
10 changes: 5 additions & 5 deletions checkdmarc/dmarc.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,18 +394,18 @@ def _query_dmarc_record(domain: str, nameservers: list[str] = None,
"""
domain = domain.lower()
target = f"_dmarc.{domain}"
txt_prefix = "v=DMARC1"
dmarc_record = None
dmarc_record_count = 0
unrelated_records = []
dmarc_prefix = "v=DMARC1"

try:
records = query_dns(target, "TXT", nameservers=nameservers,
resolver=resolver, timeout=timeout)
for record in records:
if record.startswith(dmarc_prefix):
if record.startswith(txt_prefix):
dmarc_record_count += 1
elif record.strip().startswith("v=DMARC1"):
elif record.strip().startswith(txt_prefix):
raise DMARCRecordStartsWithWhitespace(
"Found a DMARC record that starts with whitespace. "
"Please remove the whitespace, as some implementations "
Expand All @@ -426,15 +426,15 @@ def _query_dmarc_record(domain: str, nameservers: list[str] = None,
"removed, as some receivers may not expect to find "
f"unrelated TXT records at {target}\n\n{ur_str}")
dmarc_record = [record for record in records if record.startswith(
dmarc_prefix)][0]
txt_prefix)][0]

except dns.resolver.NoAnswer:
try:
records = query_dns(domain, "TXT",
nameservers=nameservers, resolver=resolver,
timeout=timeout)
for record in records:
if record.startswith("v=DMARC1"):
if record.startswith(txt_prefix):
raise DMARCRecordInWrongLocation(
"The DMARC record must be located at "
f"{target}, not {domain}")
Expand Down
7 changes: 4 additions & 3 deletions checkdmarc/mta_sts.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,10 @@ def query_mta_sts_record(domain: str,
"""
domain = domain.lower()
logging.debug(f"Checking for a MTA-STS record on {domain}")
logging.debug(f"Checking for an MTA-STS record on {domain}")
warnings = []
target = f"_mta-sts.{domain}"
txt_prefix = "v=STSv1"
sts_record = None
sts_record_count = 0
unrelated_records = []
Expand All @@ -170,7 +171,7 @@ def query_mta_sts_record(domain: str,
records = query_dns(target, "TXT", nameservers=nameservers,
resolver=resolver, timeout=timeout)
for record in records:
if record.startswith("v=STSv1"):
if record.startswith(txt_prefix):
sts_record_count += 1
else:
unrelated_records.append(record)
Expand All @@ -193,7 +194,7 @@ def query_mta_sts_record(domain: str,
nameservers=nameservers, resolver=resolver,
timeout=timeout)
for record in records:
if record.startswith("v=STS1"):
if record.startswith(txt_prefix):
raise MTASTSRecordInWrongLocation(
"The MTA-STS record must be located at "
f"{target}, not {domain}")
Expand Down
Loading

0 comments on commit 268a2e1

Please sign in to comment.