#ifndef __SPFChecker_h__ #define __SPFChecker_h__ #include #include namespace mail { /// /// SPFChecker evaluates RFC 7208 Sender Policy Framework: whether /// the IP address a connection actually came from is authorized to /// send mail claiming to be from a given domain. /// /// Deliberately does not implement two parts of the RFC: macro /// expansion (7208 7) and the ptr mechanism (7208 5.5) - macros are /// rare outside large/complex senders, and the RFC itself says ptr /// "SHOULD NOT be published" and "SHOULD be used as a last resort" /// by checkers, precisely because it is unreliable and expensive (a /// reverse-then-forward DNS round trip per candidate name). A /// mechanism using either is simply skipped (treated as /// non-matching) rather than failing the whole check. /// class SPFChecker { public: enum class Result { Pass, Fail, SoftFail, Neutral, None, PermError, TempError }; /// /// domain is the identity being checked (the envelope MAIL FROM's /// domain, or the HELO/EHLO domain when MAIL FROM is the null /// reverse-path <> - RFC 7208 2.3/2.4). clientIP is the IP /// address of the SMTP client that actually connected. /// static Result check(const std::string &domain, const std::string &clientIP); static std::string resultName(Result result); /// /// A standard Received-SPF: header (RFC 7208 9.1, the convention /// essentially every inbound MTA follows) recording the result - /// downstream processing (a user's mail client, a later /// spam-scoring pass) can key off this without redoing the DNS /// work itself. /// static std::string receivedSPFHeader(Result result, const std::string &domain, const std::string &clientIP, const std::string &helo, const std::string &receivingHost); private: // mechanismCount is threaded by reference through every // recursive/redirected call so the RFC 7208 4.6.4 limit (10 // DNS-querying terms total across the whole evaluation, not per // record) is enforced globally, not reset at each level. static Result checkRecursive(const std::string &domain, const std::string &clientIP, int &mechanismCount); static std::vector lookupSPFRecords(const std::string &domain); static std::vector lookupA(const std::string &domain); static std::vector lookupMX(const std::string &domain); static bool ipInCIDR(const std::string &ip, const std::string &cidrSpec, int defaultPrefixV4, int defaultPrefixV6); }; } #endif