Skip to content

Releases: domainaware/checkdmarc

5.15.2

23 Apr 02:53
55cbf7b

Choose a tag to compare

Changes

  • Cap the per-query UDP timeout at min(1.0, timeout) for single-nameserver
    configurations as well as multi-nameserver ones. Previously, when only one
    nameserver was configured (or the system default list had a single entry),
    resolver.timeout and resolver.lifetime were both set to the full
    timeout budget, which collapses dnspython's UDP retry loop to a single
    attempt — a single dropped UDP datagram then consumed the whole lifetime
    and raised LifetimeTimeout, while dig (which defaults to +tries=3)
    would mask the same blip by retrying. dnspython now retries UDP within
    the lifetime window (~2 attempts at the default 2s budget), matching
    dig's behavior in spirit and eliminating spurious single-NS timeouts
    on paths with occasional packet loss.

5.15.1

23 Apr 02:21
4311a9b

Choose a tag to compare

Changes

  • Revert the 5.15.0 default of auto-configuring public nameservers
    (1.1.1.1, 8.8.8.8) when no nameservers are passed. When nameservers
    is None, checkdmarc now falls back to the system-configured resolvers
    again (/etc/resolv.conf on Linux/macOS, the OS resolver on Windows),
    matching the 5.14.x and earlier behavior. The auto-configured default would
    surprise users running split-horizon or internal DNS and broke workflows
    that previously relied on the system resolver.
  • Rename the exposed constant from DEFAULT_DNS_NAMESERVERS to
    RECOMMENDED_DNS_NAMESERVERS to reflect that it is an opt-in
    recommendation, not an automatic default. It is re-exported from the
    package root as checkdmarc.RECOMMENDED_DNS_NAMESERVERS so callers can
    easily opt in with
    check_domains(..., nameservers=RECOMMENDED_DNS_NAMESERVERS).
  • Documentation now calls out mixing public resolvers from different
    providers as a best practice for public-internet checks.

5.15.0

22 Apr 20:55
9015116

Choose a tag to compare

Changes (breaking)

  • Rename the timeout_retries kwarg to retries across all public APIs, and
    rename the CLI flag --timeout-retries to --retries. The retry logic now
    covers transient failures beyond timeouts — dns.resolver.LifetimeTimeout,
    dns.resolver.NoNameservers (typically a SERVFAIL from upstream), and
    OSError during TCP fallback. NXDOMAIN and NoAnswer remain
    non-retryable (definitive negative answers).
  • Change the default retry count from 2 to 0. The retry loop tripled
    worst-case query time without helping when stalls were caused by
    misbehaving authoritative nameservers. Callers that want retries can pass
    retries=2 or use --retries 2 on the CLI.
  • Cap the per-nameserver query budget at min(1.0, timeout) seconds, with
    an overall lifetime = timeout * len(nameservers). When multiple
    nameservers are configured, dnspython now falls through to the next one
    after at most 1s instead of letting a single slow or broken nameserver
    consume the whole lifetime before any fallback is attempted. Failover
    across the configured list happens inside each attempt; retries retries
    the whole attempt after all configured nameservers have been tried.
  • Default to a mix of public DNS providers (1.1.1.1, 8.8.8.8) when no
    nameservers are passed, instead of falling back to /etc/resolv.conf.
    Combined with the per-nameserver cap above, this gives cross-provider
    failover out of the box — a slow or broken path at one resolver falls
    through to the other within ~1s. Exposed as
    checkdmarc._constants.DEFAULT_DNS_NAMESERVERS. Users that need
    system-configured or internal resolvers can still pass them explicitly
    via nameservers=... or --nameserver.
  • Centralize default timeouts and retry counts as constants in
    checkdmarc._constants (DEFAULT_DNS_TIMEOUT, DEFAULT_DNS_MAX_RETRIES,
    DEFAULT_SMTP_TIMEOUT), matching the existing DEFAULT_HTTP_TIMEOUT
    pattern. Function defaults now reference these constants so tuning is a
    one-file change.

5.14.3

21 Apr 15:45

Choose a tag to compare

Fixes

  • Fix type of approved_mx_hostnames parameter in check_domains (closes #238)
  • Resolve Pyright/Pylance type errors and warnings across the project:
    • Use Literal[True]/Literal[False] for valid in DMARCResults /
      DMARCErrorResults to enable discriminated union narrowing
    • Make nameservers parameter Optional in check_mx, get_mx_hosts, and
      get_tlsa_records to match call sites and the underlying query_dns
    • Type get_nameservers return as NameserverResultOk; type the error path
      in check_ns as NameserverResultError
    • Type parsed_dmarc_record in parse_bimi_record / check_bimi as
      Optional[Union[DMARCResults, DMARCErrorResults]]
    • Type results_to_json, results_to_csv, and results_to_csv_rows inputs
      as DomainCheckResult | list[DomainCheckResult]
    • Remove stray tuple wrapper around an add_argument call in the CLI

5.14.2

05 Apr 02:10

Choose a tag to compare

Fixes

  • Fix unclosed socket ResourceWarning in test_tls and test_starttls by
    using SMTP context managers and replacing timeout-decorator with smtplib's
    built-in timeout parameter
  • Remove timeout-decorator dependency

5.13.4

18 Feb 19:00

Choose a tag to compare

Improvements

  • More lax SPF record verification for parked domains. (PR #325)

Fixes

  • Fix regression in 1b19d14 that caused DMARC record output to be None if a syntax error was found (Close #234)

5.13.2

11 Jan 01:45

Choose a tag to compare

  • Skip undecodable TXT records instead of raising exception during SPF lookup (PR #232 fixes issue #231)

5.13.1

29 Dec 17:39

Choose a tag to compare

  • Fix check_domains return type annotation (PR # 229 fixed issue #228)
  • Fix Dockerfile COPY destination path (PR #230 fixed issue #227)

5.13.0

20 Dec 01:55

Choose a tag to compare

What's Changed

  • Updated dependencies to allow cryptography 46 to be used by @matthiasn-cdgnm in #187
  • fix: dynamically select timeout method to avoid PicklingError on macOS (#185) by @sbarbett in #186
  • Rewrite of the SPF module by @seanthegeek in #190
  • Remove return statement from the finally block by @anze3db in #188
  • Retry DNS queries if they time out by @seanthegeek in #193
  • Rename caught exception value in spf parsing by @gudjonragnar in #192
  • Create and use Docker image by @lbiemans in #194
  • bug fixes by @seanthegeek in #201
  • Validate DMARC sp tag like p tag (fixes #207) by @aharpour in #208
  • 211 spf void lookup miscount by @aharpour in #212
  • Reject SPF records with concatenated 'all' mechanisms without whitesp… by @aharpour in #210
  • 5.12.27 by @seanthegeek in #214
  • Fix uncaught ValueError and incorrect PTR void lookup handling by @aharpour in #216
  • Add comprehensive type hints with TypedDict definitions by @Copilot in #219
  • Revert "Add comprehensive type hints with TypedDict definitions" by @seanthegeek in #221
  • Add comprehensive typing to dmarc.py using TypedDicts and overloads by @Copilot in #223
  • Convert dict() calls to literals and improve typing with TypedDicts by @Copilot in #224
  • Correctly handle and validate SPF macros in mechanisms and modifiers by @aharpour in #218
  • Finalize typing improvements for spf.py and bimi.by by @seanthegeek in #226

New Contributors

Full Changelog: 5.10.12...5.13.0

5.10.12

19 Sep 20:52

Choose a tag to compare

  • Proper checking for the start of an SPF record (PR #184)
  • Improve error messages and fix typos (Close issue #182)
  • Remove warning when no MX records are found