internal SpfQualifier CheckHost(SpfCheckHostParameter parameters) { if (String.IsNullOrEmpty(parameters.Sender)) { return(SpfQualifier.PermError); } if (!parameters.Sender.Contains('@')) { parameters.Sender = "postmaster@" + parameters.Sender; } if (parameters.LoopCount > 15) { return(SpfQualifier.PermError); } if ((Terms == null) || (Terms.Count == 0)) { return(SpfQualifier.Neutral); } foreach (SpfTerm term in Terms) { SpfQualifier qualifier = term.CheckHost(parameters); if (qualifier != SpfQualifier.None) { return(qualifier); } } return(SpfQualifier.Neutral); }
public SpfCheckHostParameter(string newDomain, SpfCheckHostParameter oldParameters) { LoopCount = oldParameters.LoopCount + 1; ClientAddress = oldParameters.ClientAddress; ClientName = oldParameters.ClientName; HeloName = oldParameters.HeloName; CurrentDomain = newDomain; Sender = oldParameters.Sender; }
internal static SpfQualifier CheckHost(string spfDomain, SpfCheckHostParameter parameters) { #region First try "real" spf records List <SpfRecord> spfRecords = GetValidSpfRecords(spfDomain, RecordType.Spf); if (spfRecords == null) { return(SpfQualifier.TempError); } else if (spfRecords.Count > 1) { return(SpfQualifier.PermError); } else if (spfRecords.Count == 1) { return(spfRecords[0].CheckHost(parameters)); } #endregion #region Then fallback to TXT records spfRecords = GetValidSpfRecords(spfDomain, RecordType.Txt); if (spfRecords == null) { return(SpfQualifier.TempError); } else if (spfRecords.Count > 1) { return(SpfQualifier.PermError); } else if (spfRecords.Count == 1) { return(spfRecords[0].CheckHost(parameters)); } else { return(SpfQualifier.None); } #endregion }
internal static SpfQualifier CheckHost(string spfDomain, SpfCheckHostParameter parameters) { #region First try "real" spf records List<SpfRecord> spfRecords = GetValidSpfRecords(spfDomain, RecordType.Spf); if (spfRecords == null) { return SpfQualifier.TempError; } else if (spfRecords.Count > 1) { return SpfQualifier.PermError; } else if (spfRecords.Count == 1) { return spfRecords[0].CheckHost(parameters); } #endregion #region Then fallback to TXT records spfRecords = GetValidSpfRecords(spfDomain, RecordType.Txt); if (spfRecords == null) { return SpfQualifier.TempError; } else if (spfRecords.Count > 1) { return SpfQualifier.PermError; } else if (spfRecords.Count == 1) { return spfRecords[0].CheckHost(parameters); } else { return SpfQualifier.None; } #endregion }
internal SpfQualifier CheckHost(SpfCheckHostParameter parameters) { if (String.IsNullOrEmpty(parameters.Sender)) return SpfQualifier.PermError; if (!parameters.Sender.Contains('@')) parameters.Sender = "postmaster@" + parameters.Sender; if (parameters.LoopCount > 15) return SpfQualifier.PermError; if ((Terms == null) || (Terms.Count == 0)) return SpfQualifier.Neutral; foreach (SpfTerm term in Terms) { SpfQualifier qualifier = term.CheckHost(parameters); if (qualifier != SpfQualifier.None) return qualifier; } return SpfQualifier.Neutral; }
private static string ExpandMacro(Match pattern, SpfCheckHostParameter parameters) { switch (pattern.Value) { case "%%": return("%"); case "%_": return("_"); case "%-": return("-"); default: string letter; switch (pattern.Groups["letter"].Value) { case "s": letter = parameters.Sender; break; case "l": // no boundary check needed, sender is validated on start of CheckHost letter = parameters.Sender.Split('@')[0]; break; case "o": // no boundary check needed, sender is validated on start of CheckHost letter = parameters.Sender.Split('@')[1]; break; case "d": letter = parameters.CurrentDomain; break; case "i": letter = String.Join(".", parameters.ClientAddress.GetAddressBytes().Select(b => b.ToString()).ToArray()); break; case "p": letter = parameters.ClientName; break; case "v": letter = (parameters.ClientAddress.AddressFamily == AddressFamily.InterNetworkV6) ? "ip6" : "in-addr"; break; case "h": letter = parameters.HeloName; break; case "c": letter = parameters.ClientAddress.ToString(); break; case "r": letter = System.Net.Dns.GetHostName(); break; case "t": letter = ((int)(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) - DateTime.Now).TotalSeconds).ToString(); break; default: return(null); } // only letter if (pattern.Value.Length == 4) { return(letter); } char[] delimiters = pattern.Groups["delimiter"].Value.ToCharArray(); if (delimiters.Length == 0) { delimiters = new char[] { '.' } } ; string[] parts = letter.Split(delimiters); if (pattern.Groups["reverse"].Value == "r") { parts = parts.Reverse().ToArray(); } int count = Int32.MaxValue; if (!String.IsNullOrEmpty(pattern.Groups["count"].Value)) { count = Int32.Parse(pattern.Groups["count"].Value); } if (count < 1) { return(null); } count = Math.Min(count, parts.Length); return(String.Join(".", parts, (parts.Length - count), count)); } }
private static string ExpandDomain(string pattern, SpfCheckHostParameter parameters) { Regex regex = new Regex(@"(%%|%_|%-|%\{(?<letter>[slodiphcrtv])(?<count>\d*)(?<reverse>r?)(?<delimiter>[\.\-+,/=]*)})", RegexOptions.Compiled); return(regex.Replace(pattern, match => ExpandMacro(match, parameters))); }
internal SpfQualifier CheckHost(SpfCheckHostParameter parameters) { DnsMessage dnsMessage; SpfMechanism spfMechanism = this as SpfMechanism; if (spfMechanism != null) { switch (spfMechanism.Type) { case SpfMechanismType.All: return(spfMechanism.Qualifier); case SpfMechanismType.A: bool?isAMatch = IsIpMatch(String.IsNullOrEmpty(spfMechanism.Domain) ? parameters.CurrentDomain : spfMechanism.Domain, parameters.ClientAddress, spfMechanism.Prefix, spfMechanism.Prefix6); if (!isAMatch.HasValue) { return(SpfQualifier.TempError); } if (isAMatch.Value) { return(spfMechanism.Qualifier); } break; case SpfMechanismType.Mx: dnsMessage = DnsClient.Default.Resolve(ExpandDomain(String.IsNullOrEmpty(spfMechanism.Domain) ? parameters.CurrentDomain : spfMechanism.Domain, parameters), RecordType.Mx); if ((dnsMessage == null) || ((dnsMessage.ReturnCode != ReturnCode.NoError) && (dnsMessage.ReturnCode != ReturnCode.NxDomain))) { return(SpfQualifier.TempError); } int mxCheckedCount = 0; foreach (DnsRecordBase dnsRecord in dnsMessage.AnswerRecords) { MxRecord mxRecord = dnsRecord as MxRecord; if (mxRecord != null) { if (++mxCheckedCount == 10) { break; } bool?isMxMatch = IsIpMatch(mxRecord.ExchangeDomainName, parameters.ClientAddress, spfMechanism.Prefix, spfMechanism.Prefix6); if (!isMxMatch.HasValue) { return(SpfQualifier.TempError); } if (isMxMatch.Value) { return(spfMechanism.Qualifier); } } } break; case SpfMechanismType.Ip4: case SpfMechanismType.Ip6: IPAddress compareAddress; if (IPAddress.TryParse(spfMechanism.Domain, out compareAddress)) { if (spfMechanism.Prefix.HasValue) { if (parameters.ClientAddress.GetNetworkAddress(spfMechanism.Prefix.Value).Equals(compareAddress.GetNetworkAddress(spfMechanism.Prefix.Value))) { return(spfMechanism.Qualifier); } } else if (parameters.ClientAddress.Equals(compareAddress)) { return(spfMechanism.Qualifier); } } else { return(SpfQualifier.PermError); } break; case SpfMechanismType.Ptr: dnsMessage = DnsClient.Default.Resolve(parameters.ClientAddress.GetReverseLookupAddress(), RecordType.Ptr); if ((dnsMessage == null) || ((dnsMessage.ReturnCode != ReturnCode.NoError) && (dnsMessage.ReturnCode != ReturnCode.NxDomain))) { return(SpfQualifier.TempError); } string ptrCompareName = String.IsNullOrEmpty(spfMechanism.Domain) ? parameters.CurrentDomain : spfMechanism.Domain; int ptrCheckedCount = 0; if ((from ptrRecord in dnsMessage.AnswerRecords.OfType <PtrRecord>().TakeWhile(ptrRecord => ++ ptrCheckedCount != 10) let isPtrMatch = IsIpMatch(ptrRecord.PointerDomainName, parameters.ClientAddress, 0, 0) where isPtrMatch.HasValue && isPtrMatch.Value select ptrRecord).Any(ptrRecord => ptrRecord.PointerDomainName.Equals(ptrCompareName, StringComparison.InvariantCultureIgnoreCase) || (ptrRecord.PointerDomainName.EndsWith("." + ptrCompareName, StringComparison.InvariantCultureIgnoreCase)))) { return(spfMechanism.Qualifier); } break; case SpfMechanismType.Exist: if (String.IsNullOrEmpty(spfMechanism.Domain)) { return(SpfQualifier.PermError); } dnsMessage = DnsClient.Default.Resolve(ExpandDomain(spfMechanism.Domain, parameters), RecordType.A); if ((dnsMessage == null) || ((dnsMessage.ReturnCode != ReturnCode.NoError) && (dnsMessage.ReturnCode != ReturnCode.NxDomain))) { return(SpfQualifier.TempError); } if (dnsMessage.AnswerRecords.Count(record => (record.RecordType == RecordType.A)) > 0) { return(spfMechanism.Qualifier); } break; case SpfMechanismType.Include: if (String.IsNullOrEmpty(spfMechanism.Domain)) { return(SpfQualifier.PermError); } string includeDomain = ExpandDomain(spfMechanism.Domain, parameters); switch (SpfRecord.CheckHost(includeDomain, new SpfCheckHostParameter(includeDomain, parameters))) { case SpfQualifier.Pass: return(spfMechanism.Qualifier); case SpfQualifier.Fail: case SpfQualifier.SoftFail: case SpfQualifier.Neutral: return(SpfQualifier.None); case SpfQualifier.TempError: return(SpfQualifier.TempError); case SpfQualifier.PermError: case SpfQualifier.None: return(SpfQualifier.PermError); } break; default: return(SpfQualifier.PermError); } } SpfModifier spfModifier = this as SpfModifier; if (spfModifier != null) { switch (spfModifier.Type) { case SpfModifierType.Redirect: if (String.IsNullOrEmpty(spfModifier.Domain)) { return(SpfQualifier.PermError); } string redirectDomain = ExpandDomain(spfModifier.Domain, parameters); return(SpfRecord.CheckHost(redirectDomain, new SpfCheckHostParameter(redirectDomain, parameters))); case SpfModifierType.Exp: break; default: return(SpfQualifier.PermError); } } return(SpfQualifier.None); }
private static string ExpandMacro(Match pattern, SpfCheckHostParameter parameters) { switch (pattern.Value) { case "%%": return "%"; case "%_": return "_"; case "%-": return "-"; default: string letter; switch (pattern.Groups["letter"].Value) { case "s": letter = parameters.Sender; break; case "l": // no boundary check needed, sender is validated on start of CheckHost letter = parameters.Sender.Split('@')[0]; break; case "o": // no boundary check needed, sender is validated on start of CheckHost letter = parameters.Sender.Split('@')[1]; break; case "d": letter = parameters.CurrentDomain; break; case "i": letter = String.Join(".", parameters.ClientAddress.GetAddressBytes().Select(b => b.ToString()).ToArray()); break; case "p": letter = parameters.ClientName; break; case "v": letter = (parameters.ClientAddress.AddressFamily == AddressFamily.InterNetworkV6) ? "ip6" : "in-addr"; break; case "h": letter = parameters.HeloName; break; case "c": letter = parameters.ClientAddress.ToString(); break; case "r": letter = System.Net.Dns.GetHostName(); break; case "t": letter = ((int) (new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) - DateTime.Now).TotalSeconds).ToString(); break; default: return null; } // only letter if (pattern.Value.Length == 4) return letter; char[] delimiters = pattern.Groups["delimiter"].Value.ToCharArray(); if (delimiters.Length == 0) delimiters = new char[] { '.' }; string[] parts = letter.Split(delimiters); if (pattern.Groups["reverse"].Value == "r") parts = parts.Reverse().ToArray(); int count = Int32.MaxValue; if (!String.IsNullOrEmpty(pattern.Groups["count"].Value)) { count = Int32.Parse(pattern.Groups["count"].Value); } if (count < 1) return null; count = Math.Min(count, parts.Length); return String.Join(".", parts, (parts.Length - count), count); } }
private static string ExpandDomain(string pattern, SpfCheckHostParameter parameters) { Regex regex = new Regex(@"(%%|%_|%-|%\{(?<letter>[slodiphcrtv])(?<count>\d*)(?<reverse>r?)(?<delimiter>[\.\-+,/=]*)})", RegexOptions.Compiled); return regex.Replace(pattern, match => ExpandMacro(match, parameters)); }
internal SpfQualifier CheckHost(SpfCheckHostParameter parameters) { DnsMessage dnsMessage; SpfMechanism spfMechanism = this as SpfMechanism; if (spfMechanism != null) { switch (spfMechanism.Type) { case SpfMechanismType.All: return spfMechanism.Qualifier; case SpfMechanismType.A: bool? isAMatch = IsIpMatch(String.IsNullOrEmpty(spfMechanism.Domain) ? parameters.CurrentDomain : spfMechanism.Domain, parameters.ClientAddress, spfMechanism.Prefix, spfMechanism.Prefix6); if (!isAMatch.HasValue) return SpfQualifier.TempError; if (isAMatch.Value) { return spfMechanism.Qualifier; } break; case SpfMechanismType.Mx: dnsMessage = DnsClient.Default.Resolve(ExpandDomain(String.IsNullOrEmpty(spfMechanism.Domain) ? parameters.CurrentDomain : spfMechanism.Domain, parameters), RecordType.Mx); if ((dnsMessage == null) || ((dnsMessage.ReturnCode != ReturnCode.NoError) && (dnsMessage.ReturnCode != ReturnCode.NxDomain))) return SpfQualifier.TempError; int mxCheckedCount = 0; foreach (DnsRecordBase dnsRecord in dnsMessage.AnswerRecords) { MxRecord mxRecord = dnsRecord as MxRecord; if (mxRecord != null) { if (++mxCheckedCount == 10) break; bool? isMxMatch = IsIpMatch(mxRecord.ExchangeDomainName, parameters.ClientAddress, spfMechanism.Prefix, spfMechanism.Prefix6); if (!isMxMatch.HasValue) return SpfQualifier.TempError; if (isMxMatch.Value) { return spfMechanism.Qualifier; } } } break; case SpfMechanismType.Ip4: case SpfMechanismType.Ip6: IPAddress compareAddress; if (IPAddress.TryParse(spfMechanism.Domain, out compareAddress)) { if (spfMechanism.Prefix.HasValue) { if (parameters.ClientAddress.GetNetworkAddress(spfMechanism.Prefix.Value).Equals(compareAddress.GetNetworkAddress(spfMechanism.Prefix.Value))) { return spfMechanism.Qualifier; } } else if (parameters.ClientAddress.Equals(compareAddress)) { return spfMechanism.Qualifier; } } else { return SpfQualifier.PermError; } break; case SpfMechanismType.Ptr: dnsMessage = DnsClient.Default.Resolve(parameters.ClientAddress.GetReverseLookupAddress(), RecordType.Ptr); if ((dnsMessage == null) || ((dnsMessage.ReturnCode != ReturnCode.NoError) && (dnsMessage.ReturnCode != ReturnCode.NxDomain))) return SpfQualifier.TempError; string ptrCompareName = String.IsNullOrEmpty(spfMechanism.Domain) ? parameters.CurrentDomain : spfMechanism.Domain; int ptrCheckedCount = 0; if ((from ptrRecord in dnsMessage.AnswerRecords.OfType<PtrRecord>().TakeWhile(ptrRecord => ++ptrCheckedCount != 10) let isPtrMatch = IsIpMatch(ptrRecord.PointerDomainName, parameters.ClientAddress, 0, 0) where isPtrMatch.HasValue && isPtrMatch.Value select ptrRecord).Any(ptrRecord => ptrRecord.PointerDomainName.Equals(ptrCompareName, StringComparison.InvariantCultureIgnoreCase) || (ptrRecord.PointerDomainName.EndsWith("." + ptrCompareName, StringComparison.InvariantCultureIgnoreCase)))) { return spfMechanism.Qualifier; } break; case SpfMechanismType.Exist: if (String.IsNullOrEmpty(spfMechanism.Domain)) return SpfQualifier.PermError; dnsMessage = DnsClient.Default.Resolve(ExpandDomain(spfMechanism.Domain, parameters), RecordType.A); if ((dnsMessage == null) || ((dnsMessage.ReturnCode != ReturnCode.NoError) && (dnsMessage.ReturnCode != ReturnCode.NxDomain))) return SpfQualifier.TempError; if (dnsMessage.AnswerRecords.Count(record => (record.RecordType == RecordType.A)) > 0) { return spfMechanism.Qualifier; } break; case SpfMechanismType.Include: if (String.IsNullOrEmpty(spfMechanism.Domain)) return SpfQualifier.PermError; string includeDomain = ExpandDomain(spfMechanism.Domain, parameters); switch (SpfRecord.CheckHost(includeDomain, new SpfCheckHostParameter(includeDomain, parameters))) { case SpfQualifier.Pass: return spfMechanism.Qualifier; case SpfQualifier.Fail: case SpfQualifier.SoftFail: case SpfQualifier.Neutral: return SpfQualifier.None; case SpfQualifier.TempError: return SpfQualifier.TempError; case SpfQualifier.PermError: case SpfQualifier.None: return SpfQualifier.PermError; } break; default: return SpfQualifier.PermError; } } SpfModifier spfModifier = this as SpfModifier; if (spfModifier != null) { switch (spfModifier.Type) { case SpfModifierType.Redirect: if (String.IsNullOrEmpty(spfModifier.Domain)) return SpfQualifier.PermError; string redirectDomain = ExpandDomain(spfModifier.Domain, parameters); return SpfRecord.CheckHost(redirectDomain, new SpfCheckHostParameter(redirectDomain, parameters)); case SpfModifierType.Exp: break; default: return SpfQualifier.PermError; } } return SpfQualifier.None; }