diff options
author | 2025-05-28 22:06:11 +0100 | |
---|---|---|
committer | 2025-05-28 22:06:11 +0100 | |
commit | c54de5a972fc4fa5d0f6c07dea4337ac06cd3255 (patch) | |
tree | facc4ae8c77408bd8db7ebd68ebfe68fd0fd8272 | |
parent | Add utility module for fetching DNS data (diff) |
Add DMARC module root
-rw-r--r-- | lib/dmarc.ex | 41 |
1 files changed, 41 insertions, 0 deletions
diff --git a/lib/dmarc.ex b/lib/dmarc.ex new file mode 100644 index 0000000..9c74471 --- /dev/null +++ b/lib/dmarc.ex @@ -0,0 +1,41 @@ +defmodule Lithium.DMARC do + require Logger + + def get_dmarc_record(domain) do + with {:ok, records} <- Lithium.DNS.fetch_txt("_dmarc." <> domain) do + filtered = + records + |> Enum.map(&String.trim/1) + |> Enum.filter(fn found_record -> + # As per Section 7.1, DMARC report authorisations also use a format of "v=DMARC1" + # We should check when we find a tag that it is not *just* a version record. + + # It would technically be invalid to serve this DMARC report authorisation from + # _dmarc.domain.com, however from testing some people do peculiar deployments + # using wildcards and it ends up showing there. + + # For now, we should probably be lenient and just ignore the report authorisation + # instead of tossing the entire DMARC validation process. + trimmed = + found_record + |> String.replace(" ", "") + + String.starts_with?(trimmed, "v=DMARC1;") and trimmed != "v=DMARC1;" + end) + + case filtered do + [] -> + {:error, :nxdomain} + + [record] -> + {:ok, record} + + _ -> + {:error, :multiple_records} + end + else + error -> + error + end + end +end |