Skip to content

DKIM / SPF / DMARC Builder & Checker — C++ source

Build the three email-authentication DNS records — SPF, DKIM, and DMARC — as ready-to-paste TXT values, or look up a domain's live records over DNS-over-HTTPS. Import existing records, catch publishing mistakes, share via URL. Entirely client-side.

This is the C++ implementation — the same logic the interactive tool runs, in a shareable, citable form.

// Email authentication (SPF / DKIM / DMARC) record parsing and lookup.
//
// Language: C++17 (standard library + OpenSSL for base64; DoH via injectable fetch)
// Ported from src/lib/dkim-spf-dmarc.ts (the canonical TypeScript
// implementation). display source — part of CosmoDev's polyglot tool pages.
//
// Two layers, mirroring the TS module:
//   - Pure parsers (parseSPF / parseDKIM / parseDMARC) — deterministic, never
//     throw, unit-testable without network.
//   - Check functions (checkSPF / checkDKIM / checkDMARC) — one DNS-over-HTTPS
//     fetch to Cloudflare's public resolver, then a pure parse. The transport
//     is injectable (FetchFn) so the logic stays testable without a socket.
//
// Specs: SPF RFC 7208, DKIM RFC 6376, DMARC RFC 7489.

#include <algorithm>
#include <cctype>
#include <cstdint>
#include <functional>
#include <map>
#include <optional>
#include <stdexcept>
#include <string>
#include <vector>

namespace email_auth {

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

enum class CheckStatus { Pass, Warn, Fail };

enum class SpfQualifier { Pass, Fail, SoftFail, Neutral }; // + - ~ ?

enum class SpfKind { All, Include, A, Mx, Ip4, Ip6, Exists, Ptr };

struct SpfMechanism {
  SpfQualifier qualifier = SpfQualifier::Pass;
  SpfKind kind = SpfKind::All;
  /** Domain, IP, or CIDR argument after the colon ("" when absent). */
  std::string value;
  bool hasValue = false;
};

struct SpfRecord {
  bool valid = false;
  std::string version;
  std::vector<SpfMechanism> mechanisms;
  bool hasRedirect = false;
  std::string redirect;
  std::string exp;
  /** Mechanisms that cost a DNS query under RFC 7208 §4.6.4 (limit 10). */
  int lookupCount = 0;
  /** Number of v=spf1 records found (more than one is a hard error). */
  int recordCount = 0;
  std::vector<std::string> warnings;
};

struct DkimRecord {
  bool valid = false;
  std::string version;
  std::string keyType = "rsa";
  std::string publicKey; ///< base64, whitespace-stripped ("" until decoded)
  /** Approximate modulus size in bits (RSA only; derived from DER byte length). */
  int keyBits = -1;
  bool hasKeyBits = false;
  std::vector<std::string> hashes;
  std::vector<std::string> services;
  std::vector<std::string> flags;
  std::vector<std::string> warnings;
};

enum class DmarcPolicy { None, Quarantine, Reject };

struct DmarcRecord {
  bool valid = false;
  std::optional<DmarcPolicy> policy;
  std::optional<DmarcPolicy> subdomainPolicy;
  std::vector<std::string> aggregateUris;
  std::vector<std::string> forensicUris;
  std::optional<int> percent;
  std::optional<char> dkimAlignment;  ///< 'r' | 's'
  std::optional<char> spfAlignment;   ///< 'r' | 's'
  std::vector<std::string> warnings;
};

template <typename Record>
struct CheckResult {
  CheckStatus status = CheckStatus::Fail;
  bool found = false;
  std::optional<Record> record;
  std::string message;
};

using SpfCheck = CheckResult<SpfRecord>;
using DkimCheck = CheckResult<DkimRecord>;
using DmarcCheck = CheckResult<DmarcRecord>;

// ---------------------------------------------------------------------------
// Shared helpers
// ---------------------------------------------------------------------------

static std::string toLower(std::string s) {
  std::transform(s.begin(), s.end(), s.begin(),
                 [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
  return s;
}

static std::string toUpper(std::string s) {
  std::transform(s.begin(), s.end(), s.begin(),
                 [](unsigned char c) { return static_cast<char>(std::toupper(c)); });
  return s;
}

static std::string trim(const std::string& s) {
  size_t b = s.find_first_not_of(" \t\r\n");
  if (b == std::string::npos) return "";
  size_t e = s.find_last_not_of(" \t\r\n");
  return s.substr(b, e - b + 1);
}

static bool startsWith(const std::string& s, const std::string& prefix) {
  return s.size() >= prefix.size() && s.compare(0, prefix.size(), prefix) == 0;
}

static std::vector<std::string> splitOn(const std::string& s, char sep) {
  std::vector<std::string> out;
  std::string cur;
  for (char c : s) {
    if (c == sep) {
      out.push_back(cur);
      cur.clear();
    } else {
      cur += c;
    }
  }
  out.push_back(cur);
  return out;
}

static std::vector<std::string> splitOnSpaces(const std::string& s) {
  std::vector<std::string> out;
  std::string cur;
  for (char c : s) {
    if (std::isspace(static_cast<unsigned char>(c))) {
      if (!cur.empty()) out.push_back(cur);
      cur.clear();
    } else {
      cur += c;
    }
  }
  if (!cur.empty()) out.push_back(cur);
  return out;
}

static std::vector<std::string> filterNonEmpty(const std::vector<std::string>& in) {
  std::vector<std::string> out;
  for (const std::string& s : in) {
    if (!s.empty()) out.push_back(s);
  }
  return out;
}

static std::vector<std::string> mapTrimmed(const std::vector<std::string>& in) {
  std::vector<std::string> out;
  out.reserve(in.size());
  for (const std::string& s : in) out.push_back(trim(s));
  return out;
}

static std::vector<std::string> splitLines(const std::string& body) {
  std::vector<std::string> lines;
  std::string cur;
  for (char c : body) {
    if (c == '\n') {
      if (!cur.empty() && cur.back() == '\r') cur.pop_back();
      lines.push_back(cur);
      cur.clear();
    } else {
      cur += c;
    }
  }
  lines.push_back(cur);
  return lines;
}

/** Strip a leading scheme, userinfo, path, query, port, and trailing dot. */
std::string normalizeDomain(const std::string& raw) {
  std::string s = trim(raw);
  // scheme://
  size_t schemeEnd = s.find("://");
  if (schemeEnd != std::string::npos) {
    const std::string scheme = toLower(s.substr(0, schemeEnd));
    const bool isScheme = !scheme.empty() && std::isalpha(static_cast<unsigned char>(scheme[0])) &&
                          std::all_of(scheme.begin() + 1, scheme.end(), [](unsigned char c) {
                            return std::isalnum(c) != 0 || c == '+' || c == '.' || c == '-';
                          });
    if (isScheme) s = s.substr(schemeEnd + 3);
  }
  if (startsWith(toLower(s), "mailto:")) s = s.substr(7); // mailto:user@domain
  if (s.find('@') != std::string::npos) s = s.substr(s.rfind('@') + 1); // host of user@host
  s = splitOn(s, '/')[0];   // drop path
  s = splitOn(s, '?')[0];   // drop query
  s = splitOn(s, ':')[0];   // drop port
  while (!s.empty() && s.back() == '.') s.pop_back(); // trailing dot(s)
  return toLower(s);
}

/** True when the string looks like a plausible multi-label domain (example.com):
 *  ^(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z]{2,}$ and length <= 253. */
bool isDomainLike(const std::string& domain) {
  if (domain.empty() || domain.size() > 253) return false;
  const std::vector<std::string> labels = splitOn(domain, '.');
  if (labels.size() < 2) return false;
  for (size_t i = 0; i < labels.size(); i++) {
    const std::string& label = labels[i];
    if (label.empty()) return false;
    const bool ok = std::all_of(label.begin(), label.end(), [](unsigned char c) {
      return std::isalnum(c) != 0 || c == '-';
    });
    if (!ok) return false;
    if (label.front() == '-' || label.back() == '-') return false;
    if (i == labels.size() - 1 && label.size() < 2) return false; // TLD needs 2+ chars
    if (i == labels.size() - 1 &&
        !std::all_of(label.begin(), label.end(),
                     [](unsigned char c) { return std::isalpha(c) != 0; })) {
      return false; // the TLD is letters only per the TS regex
    }
  }
  return true;
}

/** True when the selector is a safe single DNS label chain: ^[a-z0-9][a-z0-9._-]*$,
 *  1-100 chars, no "..". */
bool isValidSelector(const std::string& selector) {
  const std::string s = trim(selector);
  if (s.empty() || s.size() > 100) return false;
  if (!std::isalnum(static_cast<unsigned char>(s[0]))) return false;
  for (char c : s) {
    if (!(std::isalnum(static_cast<unsigned char>(c)) != 0 || c == '.' || c == '_' || c == '-')) {
      return false;
    }
  }
  return s.find("..") == std::string::npos;
}

// ---------------------------------------------------------------------------
// SPF — RFC 7208
// ---------------------------------------------------------------------------

static bool isLookupKind(SpfKind kind) {
  return kind == SpfKind::Include || kind == SpfKind::A || kind == SpfKind::Mx ||
         kind == SpfKind::Exists || kind == SpfKind::Ptr;
}

static SpfKind spfKindByName(const std::string& name) {
  if (name == "all") return SpfKind::All;
  if (name == "include") return SpfKind::Include;
  if (name == "a") return SpfKind::A;
  if (name == "mx") return SpfKind::Mx;
  if (name == "ip4") return SpfKind::Ip4;
  if (name == "ip6") return SpfKind::Ip6;
  if (name == "exists") return SpfKind::Exists;
  return SpfKind::Ptr; // "ptr"
}

static char qualifierChar(SpfQualifier q) {
  switch (q) {
    case SpfQualifier::Pass: return '+';
    case SpfQualifier::Fail: return '-';
    case SpfQualifier::SoftFail: return '~';
    case SpfQualifier::Neutral: return '?';
  }
  return '+';
}

static std::string unquote(const std::string& t) {
  if (t.size() >= 2 && t.front() == '"' && t.back() == '"') return t.substr(1, t.size() - 2);
  return t;
}

/** Parse one or more (newline-joined) TXT record strings for SPF. Pure. */
SpfRecord parseSPF(const std::string& txt) {
  std::vector<std::string> spfLines;
  for (const std::string& line : splitOn(txt, '\n')) {
    std::string l = trim(unquote(trim(line)));
    if (l.empty()) continue;
    const std::string lower = toLower(l);
    if (lower == "v=spf1" || startsWith(lower, "v=spf1 ")) spfLines.push_back(l);
  }

  SpfRecord record;
  if (spfLines.empty()) {
    record.warnings.push_back("No v=spf1 record found in the supplied text.");
    return record;
  }
  if (spfLines.size() > 1) {
    record.warnings.push_back(std::to_string(spfLines.size()) +
                              " SPF records found — RFC 7208 allows exactly one. Receivers treat this as a permanent error and ignore SPF for the whole domain.");
  }

  const std::vector<std::string> terms = splitOnSpaces(spfLines[0]);
  record.version = terms[0];

  for (size_t i = 1; i < terms.size(); i++) {
    const std::string& term = terms[i];
    // Modifiers use '=': redirect= and exp=
    const size_t eq = term.find('=');
    if (eq != std::string::npos && eq > 0 &&
        std::isalpha(static_cast<unsigned char>(term[0]))) {
      bool nameIsModifierShape = true;
      for (size_t c = 1; c < eq && nameIsModifierShape; c++) {
        const char ch = term[c];
        if (!(std::isalnum(static_cast<unsigned char>(ch)) != 0 || ch == '-')) nameIsModifierShape = false;
      }
      if (nameIsModifierShape) {
        const std::string name = toLower(term.substr(0, eq));
        if (name == "redirect") {
          record.redirect = term.substr(eq + 1);
          record.hasRedirect = true;
        } else if (name == "exp") {
          record.exp = term.substr(eq + 1);
        } else {
          record.warnings.push_back("Unknown modifier \"" + term + "\" ignored.");
        }
        continue;
      }
    }
    // Mechanism: optional qualifier + name + optional ":value"
    size_t pos = 0;
    SpfQualifier qualifier = SpfQualifier::Pass;
    if (term.size() > 1 && (term[0] == '+' || term[0] == '-' || term[0] == '~' || term[0] == '?')) {
      qualifier = term[0] == '+' ? SpfQualifier::Pass
                : term[0] == '-' ? SpfQualifier::Fail
                : term[0] == '~' ? SpfQualifier::SoftFail
                                 : SpfQualifier::Neutral;
      pos = 1;
    }
    const size_t colon = term.find(':', pos);
    const std::string name = toLower(term.substr(pos, colon == std::string::npos ? std::string::npos : colon - pos));
    if (name.empty() ||
        !std::all_of(name.begin(), name.end(), [](unsigned char c) { return std::isalnum(c) != 0; })) {
      record.warnings.push_back("Unrecognized term \"" + term + "\" ignored.");
      continue;
    }
    if (name != "all" && name != "include" && name != "a" && name != "mx" &&
        name != "ip4" && name != "ip6" && name != "exists" && name != "ptr") {
      record.warnings.push_back("Unknown mechanism \"" + term + "\" ignored.");
      continue;
    }
    SpfMechanism mech;
    mech.qualifier = qualifier;
    mech.kind = spfKindByName(name);
    if (colon != std::string::npos) {
      mech.value = term.substr(colon + 1);
      mech.hasValue = true;
    }
    if (!mech.hasValue && (mech.kind == SpfKind::Include || mech.kind == SpfKind::Exists)) {
      record.warnings.push_back("Mechanism \"" + term + "\" is missing its required value.");
      continue;
    }
    record.mechanisms.push_back(mech);
  }

  int lookups = 0;
  for (const SpfMechanism& mech : record.mechanisms) {
    if (isLookupKind(mech.kind)) lookups++;
  }
  if (record.hasRedirect) lookups++;
  record.lookupCount = lookups;

  const SpfMechanism* all = nullptr;
  for (const SpfMechanism& mech : record.mechanisms) {
    if (mech.kind == SpfKind::All) {
      all = &mech;
      break;
    }
  }
  if (all == nullptr && !record.hasRedirect) {
    record.warnings.push_back("No \"all\" mechanism and no \"redirect=\" — unmatched senders get a Neutral result, so anyone can still send mail that looks like this domain.");
  }
  if (all != nullptr && all->qualifier == SpfQualifier::Pass) {
    record.warnings.push_back("\"+all\" explicitly allows every host on the internet to send mail as this domain — this defeats SPF entirely.");
  } else if (all != nullptr && all->qualifier == SpfQualifier::Neutral) {
    record.warnings.push_back("\"?all\" (Neutral) lets unmatched senders through with no protection. Prefer \"~all\" or \"-all\".");
  }
  for (const SpfMechanism& mech : record.mechanisms) {
    if (mech.kind == SpfKind::Ptr) {
      record.warnings.push_back("The \"ptr\" mechanism is deprecated (RFC 7208 §5.5) and should not be used.");
      break;
    }
  }
  if (record.hasRedirect && all != nullptr) {
    record.warnings.push_back("A \"redirect=\" modifier is ignored when an \"all\" mechanism is present.");
  }
  if (record.lookupCount > 10) {
    record.warnings.push_back(std::to_string(record.lookupCount) +
                              " DNS-lookup mechanisms — RFC 7208 §4.6.4 caps SPF at 10. Receivers that hit the cap return permerror and ignore the record.");
  }

  record.valid = true;
  record.recordCount = static_cast<int>(spfLines.size());
  return record;
}

// ---------------------------------------------------------------------------
// DKIM — RFC 6376
// ---------------------------------------------------------------------------

/** Length in bytes of a base64 string decoded leniently; -1 when invalid. */
static int lenientBase64Length(const std::string& b64Raw) {
  std::string clean;
  for (char c : b64Raw) {
    if (std::isspace(static_cast<unsigned char>(c))) continue;
    clean += c;
  }
  if (clean.empty()) return -1;
  for (char c : clean) {
    if (!(std::isalnum(static_cast<unsigned char>(c)) != 0 || c == '+' || c == '/')) return -1;
  }
  int padding = 0;
  while (clean.size() > padding && clean[clean.size() - 1 - padding] == '=') padding++;
  if (padding > 2) return -1;
  const size_t data = clean.size() - padding;
  if (data % 4 == 1) return -1;
  return static_cast<int>(data / 4 * 3 + (data % 4 == 2 ? 1 : data % 4 == 3 ? 2 : 0));
}

/** Split "k=v; k2=v2" tag lists (shared by DKIM and DMARC). */
static std::map<std::string, std::string> parseTags(const std::string& txt,
                                                    std::vector<std::string>& warnings,
                                                    const char* malformedPrefix) {
  std::map<std::string, std::string> tags;
  for (const std::string& part : splitOn(txt, ';')) {
    const std::string term = trim(unquote(trim(part)));
    if (term.empty()) continue;
    const size_t eq = term.find('=');
    if (eq == std::string::npos || eq == 0) {
      warnings.push_back(std::string(malformedPrefix) + " \"" + term + "\" ignored.");
      continue;
    }
    tags[toLower(trim(term.substr(0, eq)))] = trim(term.substr(eq + 1));
  }
  return tags;
}

/** Parse a `<selector>._domainkey` TXT record. Pure. */
DkimRecord parseDKIM(const std::string& txt) {
  DkimRecord record;
  const std::map<std::string, std::string> tags = parseTags(txt, record.warnings, "Malformed tag");

  const auto it = [&](const std::string& key) { return tags.find(key) != tags.end(); };
  std::string version = it("v") ? tags.at("v") : "";
  if (!version.empty() && toUpper(version) != "DKIM1") {
    record.warnings.push_back("Unusual version tag v=" + version + " (expected DKIM1).");
  }
  record.keyType = it("k") ? tags.at("k") : "rsa";
  const bool hasP = it("p");
  const std::string p = hasP ? tags.at("p") : "";
  const std::string h = it("h") ? tags.at("h") : "";
  const std::string s = it("s") ? tags.at("s") : "";
  const std::string t = it("t") ? tags.at("t") : "";
  record.hashes = filterNonEmpty(mapTrimmed(splitOn(h, ':')));
  record.services = filterNonEmpty(mapTrimmed(splitOn(s, ':')));
  record.flags = filterNonEmpty(mapTrimmed(splitOn(t, ':')));

  record.valid = true;
  if (!version.empty()) record.version = toUpper(version);

  if (!hasP) {
    record.valid = false;
    record.warnings.push_back("No p= tag — this record is not a usable DKIM key.");
    return record;
  }
  if (p.empty()) {
    record.warnings.push_back("p= is empty — the key is revoked. Receivers will treat mail signed with this selector as unsigned.");
    return record;
  }

  const int decodedLen = lenientBase64Length(p);
  if (decodedLen < 0) {
    record.warnings.push_back("The p= value is not valid base64 — the key could not be read.");
    return record;
  }
  std::string compact;
  for (char c : p) {
    if (!std::isspace(static_cast<unsigned char>(c))) compact += c;
  }
  record.publicKey = compact;

  if (record.keyType == "rsa") {
    // SubjectPublicKeyInfo DER ≈ modulus bits/8 + ~24 bytes of ASN.1 overhead.
    // The estimate is close enough to classify 512/1024/2048/4096-bit keys.
    const int bits = std::max(0, decodedLen - 24) * 8;
    record.keyBits = bits;
    record.hasKeyBits = true;
    if (bits < 1024) {
      record.warnings.push_back("Weak RSA key (~" + std::to_string(bits) + " bits). Keys under 1024 bits are considered breakable; RFC 8301 discourages short keys.");
    } else if (bits < 2048) {
      record.warnings.push_back("RSA key of ~" + std::to_string(bits) + " bits works today but is below the recommended 2048 bits (RFC 8301).");
    }
  }
  if (std::find(record.flags.begin(), record.flags.end(), "y") != record.flags.end()) {
    record.warnings.push_back("t=y — the key is in test mode: receivers must treat signatures as if unsigned.");
  }
  if (std::find(record.flags.begin(), record.flags.end(), "s") != record.flags.end()) {
    record.warnings.push_back("t=s — strict domain matching: the key cannot be used for subdomain signatures (informational).");
  }
  return record;
}

// ---------------------------------------------------------------------------
// DMARC — RFC 7489
// ---------------------------------------------------------------------------

static std::optional<DmarcPolicy> dmarcPolicyByName(const std::string& name) {
  if (name == "none") return DmarcPolicy::None;
  if (name == "quarantine") return DmarcPolicy::Quarantine;
  if (name == "reject") return DmarcPolicy::Reject;
  return std::nullopt;
}

/** Parse a `_dmarc` TXT record. Pure. */
DmarcRecord parseDMARC(const std::string& txt) {
  DmarcRecord record;
  const std::map<std::string, std::string> tags = parseTags(txt, record.warnings, "Malformed tag");
  const auto has = [&](const std::string& key) { return tags.find(key) != tags.end(); };

  if (!has("v")) {
    record.warnings.push_back("No v= tag — this is not a DMARC record.");
    return record;
  }
  if (toUpper(tags.at("v")) != "DMARC1") {
    record.warnings.push_back("Unknown version v=" + tags.at("v") + " (expected DMARC1).");
    return record;
  }
  if (!has("p")) {
    record.warnings.push_back("No p= policy tag — DMARC requires it.");
    return record;
  }
  const std::string p = toLower(tags.at("p"));
  const std::optional<DmarcPolicy> policy = dmarcPolicyByName(p);
  if (!policy.has_value()) {
    record.warnings.push_back("Invalid policy p=" + p + " (expected none, quarantine, or reject).");
    return record;
  }
  record.policy = policy;
  record.valid = true;

  if (has("sp")) {
    const std::string sp = toLower(tags.at("sp"));
    const std::optional<DmarcPolicy> sub = dmarcPolicyByName(sp);
    if (sub.has_value()) record.subdomainPolicy = sub;
    else record.warnings.push_back("Invalid sp=" + sp + " ignored (expected none, quarantine, or reject).");
  }

  if (has("rua")) {
    record.aggregateUris = filterNonEmpty(mapTrimmed(splitOn(tags.at("rua"), ',')));
  }
  if (has("ruf")) {
    record.forensicUris = filterNonEmpty(mapTrimmed(splitOn(tags.at("ruf"), ',')));
  }

  if (has("pct")) {
    const std::string pct = tags.at("pct");
    bool numeric = !pct.empty() &&
                   std::all_of(pct.begin(), pct.end(),
                               [](unsigned char c) { return std::isdigit(c) != 0; });
    const long long n = numeric ? std::stoll(pct) : -1;
    if (numeric && n >= 0 && n <= 100) {
      record.percent = static_cast<int>(n);
    } else {
      record.warnings.push_back("Invalid pct=" + pct + " ignored (must be 0-100).");
    }
  }

  if (has("adkim")) {
    const std::string adkim = toLower(tags.at("adkim"));
    if (adkim == "r" || adkim == "s") record.dkimAlignment = adkim[0];
    else record.warnings.push_back("Invalid adkim=" + adkim + " ignored (expected r or s).");
  }
  if (has("aspf")) {
    const std::string aspf = toLower(tags.at("aspf"));
    if (aspf == "r" || aspf == "s") record.spfAlignment = aspf[0];
    else record.warnings.push_back("Invalid aspf=" + aspf + " ignored (expected r or s).");
  }

  // Policy guidance
  if (record.policy == DmarcPolicy::None) {
    record.warnings.push_back("p=none is monitor-only — no mail is quarantined or rejected, but you still need SPF/DKIM to pass for reports to be useful.");
  }
  if (record.aggregateUris.empty()) {
    record.warnings.push_back("No rua= address — without aggregate reports you cannot see who is failing authentication. Add rua=mailto:reports@example.com.");
  } else if (!record.forensicUris.empty()) {
    record.warnings.push_back("ruf= (forensic reports) is supported by few receivers and may leak message content to the report address (informational).");
  }
  if (record.percent.has_value() && *record.percent < 100 && record.policy != DmarcPolicy::None) {
    record.warnings.push_back("pct=" + std::to_string(*record.percent) + " applies the policy to only " +
                              std::to_string(*record.percent) + "% of mail — the other " +
                              std::to_string(100 - *record.percent) + "% is unaffected.");
  }
  return record;
}

// ---------------------------------------------------------------------------
// DNS-over-HTTPS lookup
// ---------------------------------------------------------------------------

constexpr const char* DOH_ENDPOINT = "https://cloudflare-dns.com/dns-query";

/** Minimal response shape the injected fetch must provide (the TS `Response`). */
struct FetchResponse {
  bool ok = false;
  int status = 0;
  std::string body;
};

/// Injectable transport: URL in, response out. Throw to signal a network error.
using FetchFn = std::function<FetchResponse(const std::string& url)>;

/** Query TXT records for a name via Cloudflare's DoH JSON API. Returns unquoted
 *  strings. The injected transport answers with one TXT `data` field per line
 *  (the JSON envelope's type-16 answers); surrounding quotes are stripped and
 *  the `" "` separator between multi-chunk TXT strings is joined, matching the
 *  TS `.replace(/^"(.*)"$/, '$1').replace(/" "/g, '')`. */
std::vector<std::string> queryTxt(const FetchFn& fetchFn, const std::string& name) {
  const std::string url = std::string(DOH_ENDPOINT) + "?name=" + name + "&type=TXT";
  const FetchResponse res = fetchFn(url); // throws on transport error
  if (!res.ok) {
    throw std::runtime_error("DNS resolver responded with HTTP " + std::to_string(res.status) + ".");
  }
  std::vector<std::string> out;
  for (const std::string& raw : splitLines(res.body)) {
    if (trim(raw).empty()) continue;
    std::string data = trim(raw);
    std::string unquoted = unquote(data);      // strip the outer "..." if present
    std::string joined;
    std::string chunkSep = "\" \"";            // merge multi-chunk "a" "b" -> ab
    size_t pos = 0;
    while (pos < unquoted.size()) {
      const size_t hit = unquoted.find(chunkSep, pos);
      if (hit == std::string::npos) {
        joined += unquoted.substr(pos);
        break;
      }
      joined += unquoted.substr(pos, hit - pos);
      pos = hit + chunkSep.size();
    }
    out.push_back(joined);
  }
  return out;
}

static CheckStatus statusForWarnings(const std::vector<std::string>& warnings) {
  return warnings.empty() ? CheckStatus::Pass : CheckStatus::Warn;
}

/** Look up and evaluate a domain's SPF record. */
SpfCheck checkSPF(const std::string& domain, const FetchFn& fetchFn) {
  const std::string host = normalizeDomain(domain);
  if (!isDomainLike(host)) {
    return SpfCheck{CheckStatus::Fail, false, std::nullopt, "Enter a valid domain, e.g. example.com."};
  }
  try {
    const std::vector<std::string> txts = queryTxt(fetchFn, host);
    std::string joined;
    for (size_t i = 0; i < txts.size(); i++) {
      if (i > 0) joined += '\n';
      joined += txts[i];
    }
    const SpfRecord record = parseSPF(joined);
    if (!record.valid) {
      return SpfCheck{CheckStatus::Fail, false, std::nullopt,
                      "No SPF record found for " + host + ". Receivers cannot verify which servers may send mail for it."};
    }
    const CheckStatus status = statusForWarnings(record.warnings);
    std::string message = status == CheckStatus::Pass ? "SPF record found and looks healthy." : "";
    return SpfCheck{status, true, record, message};
  } catch (const std::exception& err) {
    return SpfCheck{CheckStatus::Fail, false, std::nullopt, err.what()};
  } catch (...) {
    return SpfCheck{CheckStatus::Fail, false, std::nullopt, "DNS lookup failed."};
  }
}

/** Look up and evaluate a domain's DKIM public key for one selector. */
DkimCheck checkDKIM(const std::string& domain, const std::string& selector, const FetchFn& fetchFn) {
  const std::string host = normalizeDomain(domain);
  if (!isDomainLike(host)) {
    return DkimCheck{CheckStatus::Fail, false, std::nullopt, "Enter a valid domain, e.g. example.com."};
  }
  const std::string sel = toLower(trim(selector));
  if (!isValidSelector(sel)) {
    return DkimCheck{CheckStatus::Fail, false, std::nullopt,
                     "Enter a valid selector (letters, digits, dots, hyphens, underscores)."};
  }
  try {
    const std::vector<std::string> txts = queryTxt(fetchFn, sel + "._domainkey." + host);
    std::string joined;
    for (size_t i = 0; i < txts.size(); i++) {
      if (i > 0) joined += '\n';
      joined += txts[i];
    }
    const DkimRecord record = parseDKIM(joined);
    if (!record.valid) {
      return DkimCheck{CheckStatus::Fail, false, std::nullopt,
                      "No DKIM record found at " + sel + "._domainkey." + host + ". Try another selector — only one is checked per lookup."};
    }
    bool revoked = false;
    for (const std::string& w : record.warnings) {
      if (w.find("revoked") != std::string::npos) revoked = true;
    }
    const CheckStatus status = revoked ? CheckStatus::Fail : statusForWarnings(record.warnings);
    return DkimCheck{status, true, record, ""};
  } catch (const std::exception& err) {
    return DkimCheck{CheckStatus::Fail, false, std::nullopt, err.what()};
  } catch (...) {
    return DkimCheck{CheckStatus::Fail, false, std::nullopt, "DNS lookup failed."};
  }
}

/** Look up and evaluate a domain's DMARC policy. */
DmarcCheck checkDMARC(const std::string& domain, const FetchFn& fetchFn) {
  const std::string host = normalizeDomain(domain);
  if (!isDomainLike(host)) {
    return DmarcCheck{CheckStatus::Fail, false, std::nullopt, "Enter a valid domain, e.g. example.com."};
  }
  try {
    const std::vector<std::string> txts = queryTxt(fetchFn, "_dmarc." + host);
    std::string joined;
    for (size_t i = 0; i < txts.size(); i++) {
      if (i > 0) joined += '\n';
      joined += txts[i];
    }
    const DmarcRecord record = parseDMARC(joined);
    if (!record.valid) {
      return DmarcCheck{CheckStatus::Fail, false, std::nullopt,
                      "No DMARC record found at _dmarc." + host + ". Receivers have no policy to apply when SPF or DKIM fails."};
    }
    return DmarcCheck{statusForWarnings(record.warnings), true, record, ""};
  } catch (const std::exception& err) {
    return DmarcCheck{CheckStatus::Fail, false, std::nullopt, err.what()};
  } catch (...) {
    return DmarcCheck{CheckStatus::Fail, false, std::nullopt, "DNS lookup failed."};
  }
}

} // namespace email_auth

Also available in 9 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →