/// <summary> /// Returns the textual representation of the SenderID record /// </summary> /// <returns> Textual representation </returns> public override string ToString() { StringBuilder res = new StringBuilder(); if (Version == 1) { res.Append("v=spf1"); } else { res.Append("v=spf"); res.Append(Version); res.Append("."); res.Append(MinorVersion); res.Append("/"); res.Append(String.Join(",", Scopes.Where(s => s != SenderIDScope.Unknown).Select(s => EnumHelper <SenderIDScope> .ToString(s).ToLower()).ToArray())); } if ((Terms != null) && (Terms.Count > 0)) { foreach (SpfTerm term in Terms) { SpfModifier modifier = term as SpfModifier; if ((modifier == null) || (modifier.Type != SpfModifierType.Unknown)) { res.Append(" "); res.Append(term.ToString()); } } } return(res.ToString()); }
/// <summary> /// Returns the textual representation of a SPF record /// </summary> /// <returns> Textual representation </returns> public override string ToString() { StringBuilder res = new StringBuilder(); res.Append("v=spf1"); if ((Terms != null) && (Terms.Count > 0)) { foreach (SpfTerm term in Terms) { SpfModifier modifier = term as SpfModifier; if ((modifier == null) || (modifier.Type != SpfModifierType.Unknown)) { res.Append(" "); res.Append(term.ToString()); } } } return(res.ToString()); }
internal static bool TryParse(string s, out SpfTerm value) { if (String.IsNullOrEmpty(s)) { value = null; return(false); } #region Parse Mechanism Regex regex = new Regex(@"^(\s)*(?<qualifier>[~+?-]?)(?<type>[a-z0-9]+)(:(?<domain>[^/]+))?(/(?<prefix>[0-9]+)(/(?<prefix6>[0-9]+))?)?(\s)*$", RegexOptions.Compiled | RegexOptions.IgnoreCase); Match match = regex.Match(s); if (match.Success) { SpfMechanism mechanism = new SpfMechanism(); switch (match.Groups["qualifier"].Value) { case "+": mechanism.Qualifier = SpfQualifier.Pass; break; case "-": mechanism.Qualifier = SpfQualifier.Fail; break; case "~": mechanism.Qualifier = SpfQualifier.SoftFail; break; case "?": mechanism.Qualifier = SpfQualifier.Neutral; break; default: mechanism.Qualifier = SpfQualifier.Pass; break; } SpfMechanismType type; mechanism.Type = EnumHelper <SpfMechanismType> .TryParse(match.Groups["type"].Value, true, out type) ? type : SpfMechanismType.Unknown; mechanism.Domain = match.Groups["domain"].Value; string tmpPrefix = match.Groups["prefix"].Value; int prefix; if (!String.IsNullOrEmpty(tmpPrefix) && Int32.TryParse(tmpPrefix, out prefix)) { mechanism.Prefix = prefix; } tmpPrefix = match.Groups["prefix6"].Value; if (!String.IsNullOrEmpty(tmpPrefix) && Int32.TryParse(tmpPrefix, out prefix)) { mechanism.Prefix6 = prefix; } value = mechanism; return(true); } #endregion #region Parse Modifier regex = new Regex(@"^(\s)*(?<type>[a-z]+)=(?<domain>[^\s]+)(\s)*$", RegexOptions.Compiled | RegexOptions.IgnoreCase); match = regex.Match(s); if (match.Success) { SpfModifier modifier = new SpfModifier(); SpfModifierType type; modifier.Type = EnumHelper <SpfModifierType> .TryParse(match.Groups["type"].Value, true, out type) ? type : SpfModifierType.Unknown; modifier.Domain = match.Groups["domain"].Value; value = modifier; return(true); } #endregion value = null; return(false); }
private SpfQualifier CheckHostInternal(IPAddress ip, string sender, string domain, bool expandExplanation, out string explanation) { explanation = String.Empty; if (String.IsNullOrEmpty(domain)) { return(SpfQualifier.None); } if (String.IsNullOrEmpty(sender)) { sender = "postmaster@unknown"; } else if (!sender.Contains('@')) { sender = "postmaster@" + sender; } SpfQualifier result; T record; if (!TryLoadRecords(domain, out record, out result)) { return(result); } if ((record.Terms == null) || (record.Terms.Count == 0)) { return(SpfQualifier.Neutral); } if (record.Terms.OfType <SpfModifier>().GroupBy(m => m.Type).Where(g => (g.Key == SpfModifierType.Exp) || (g.Key == SpfModifierType.Redirect)).Any(g => g.Count() > 1)) { return(SpfQualifier.PermError); } #region Evaluate mechanism foreach (SpfMechanism mechanism in record.Terms.OfType <SpfMechanism>()) { if (LookupCount > DnsLookupLimit) { return(SpfQualifier.PermError); } SpfQualifier qualifier = CheckMechanism(mechanism, ip, sender, domain); if (qualifier != SpfQualifier.None) { result = qualifier; break; } } #endregion #region Evaluate modifiers if (result == SpfQualifier.None) { SpfModifier redirectModifier = record.Terms.OfType <SpfModifier>().FirstOrDefault(m => m.Type == SpfModifierType.Redirect); if (redirectModifier != null) { string redirectDomain = ExpandDomain(redirectModifier.Domain ?? String.Empty, ip, sender, domain); if (String.IsNullOrEmpty(redirectDomain) || (redirectDomain.Equals(domain, StringComparison.InvariantCultureIgnoreCase))) { result = SpfQualifier.PermError; } else { result = CheckHostInternal(ip, sender, redirectDomain, expandExplanation, out explanation); if (result == SpfQualifier.None) { result = SpfQualifier.PermError; } } } } else if ((result == SpfQualifier.Fail) && expandExplanation) { SpfModifier expModifier = record.Terms.OfType <SpfModifier>().Where(m => m.Type == SpfModifierType.Exp).FirstOrDefault(); if (expModifier != null) { string target = ExpandDomain(expModifier.Domain, ip, sender, domain); if (String.IsNullOrEmpty(target)) { explanation = String.Empty; } else { DnsMessage dnsMessage = ResolveDns(target, RecordType.Txt); if ((dnsMessage != null) && (dnsMessage.ReturnCode == ReturnCode.NoError)) { TxtRecord txtRecord = dnsMessage.AnswerRecords.OfType <TxtRecord>().FirstOrDefault(); if (txtRecord != null) { explanation = ExpandDomain(txtRecord.TextData, ip, sender, domain); } } } } } #endregion return((result != SpfQualifier.None) ? result : SpfQualifier.Neutral); }
internal static bool TryParse(string s, out SpfTerm value) { if (String.IsNullOrEmpty(s)) { value = null; return false; } #region Parse Mechanism Regex regex = new Regex(@"^(\s)*(?<qualifier>[~+?-]?)(?<type>[a-z0-9]+)(:(?<domain>[^/]+))?(/(?<prefix>[0-9]+)(/(?<prefix6>[0-9]+))?)?(\s)*$", RegexOptions.Compiled | RegexOptions.IgnoreCase); Match match = regex.Match(s); if (match.Success) { SpfMechanism mechanism = new SpfMechanism(); switch (match.Groups["qualifier"].Value) { case "+": mechanism.Qualifier = SpfQualifier.Pass; break; case "-": mechanism.Qualifier = SpfQualifier.Fail; break; case "~": mechanism.Qualifier = SpfQualifier.SoftFail; break; case "?": mechanism.Qualifier = SpfQualifier.Neutral; break; default: mechanism.Qualifier = SpfQualifier.Pass; break; } SpfMechanismType type; mechanism.Type = EnumHelper<SpfMechanismType>.TryParse(match.Groups["type"].Value, true, out type) ? type : SpfMechanismType.Unknown; mechanism.Domain = match.Groups["domain"].Value; string tmpPrefix = match.Groups["prefix"].Value; int prefix; if (!String.IsNullOrEmpty(tmpPrefix) && Int32.TryParse(tmpPrefix, out prefix)) { mechanism.Prefix = prefix; } tmpPrefix = match.Groups["prefix6"].Value; if (!String.IsNullOrEmpty(tmpPrefix) && Int32.TryParse(tmpPrefix, out prefix)) { mechanism.Prefix6 = prefix; } value = mechanism; return true; } #endregion #region Parse Modifier regex = new Regex(@"^(\s)*(?<type>[a-z]+)=(?<domain>[^\s]+)(\s)*$", RegexOptions.Compiled | RegexOptions.IgnoreCase); match = regex.Match(s); if (match.Success) { SpfModifier modifier = new SpfModifier(); SpfModifierType type; modifier.Type = EnumHelper<SpfModifierType>.TryParse(match.Groups["type"].Value, true, out type) ? type : SpfModifierType.Unknown; modifier.Domain = match.Groups["domain"].Value; value = modifier; return true; } #endregion value = null; return false; }