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 async Task <SpfQualifier> CheckMechanismAsync(SpfMechanism mechanism, IPAddress ip, DomainName domain, string sender, State state, CancellationToken token) { switch (mechanism.Type) { case SpfMechanismType.All: return(mechanism.Qualifier); case SpfMechanismType.A: if (++state.DnsLookupCount > 10) { return(SpfQualifier.PermError); } DomainName aMechanismDomain = String.IsNullOrEmpty(mechanism.Domain) ? domain : await ExpandDomainAsync(mechanism.Domain, ip, domain, sender, token); bool?isAMatch = await IsIpMatchAsync(aMechanismDomain, ip, mechanism.Prefix, mechanism.Prefix6, token); if (!isAMatch.HasValue) { return(SpfQualifier.TempError); } if (isAMatch.Value) { return(mechanism.Qualifier); } break; case SpfMechanismType.Mx: if (++state.DnsLookupCount > 10) { return(SpfQualifier.PermError); } DomainName mxMechanismDomain = String.IsNullOrEmpty(mechanism.Domain) ? domain : await ExpandDomainAsync(mechanism.Domain, ip, domain, sender, token); DnsResolveResult <MxRecord> dnsMxResult = await ResolveDnsAsync <MxRecord>(mxMechanismDomain, RecordType.Mx, token); if ((dnsMxResult == null) || ((dnsMxResult.ReturnCode != ReturnCode.NoError) && (dnsMxResult.ReturnCode != ReturnCode.NxDomain))) { return(SpfQualifier.TempError); } int mxCheckedCount = 0; foreach (MxRecord mxRecord in dnsMxResult.Records) { if (++mxCheckedCount == 10) { break; } bool?isMxMatch = await IsIpMatchAsync(mxRecord.ExchangeDomainName, ip, mechanism.Prefix, mechanism.Prefix6, token); if (!isMxMatch.HasValue) { return(SpfQualifier.TempError); } if (isMxMatch.Value) { return(mechanism.Qualifier); } } break; case SpfMechanismType.Ip4: case SpfMechanismType.Ip6: IPAddress compareAddress; if (IPAddress.TryParse(mechanism.Domain, out compareAddress)) { if (ip.AddressFamily != compareAddress.AddressFamily) { return(SpfQualifier.None); } if (mechanism.Prefix.HasValue) { if ((mechanism.Prefix.Value < 0) || (mechanism.Prefix.Value > (compareAddress.AddressFamily == AddressFamily.InterNetworkV6 ? 128 : 32))) { return(SpfQualifier.PermError); } if (ip.GetNetworkAddress(mechanism.Prefix.Value).Equals(compareAddress.GetNetworkAddress(mechanism.Prefix.Value))) { return(mechanism.Qualifier); } } else if (ip.Equals(compareAddress)) { return(mechanism.Qualifier); } } else { return(SpfQualifier.PermError); } break; case SpfMechanismType.Ptr: if (++state.DnsLookupCount > 10) { return(SpfQualifier.PermError); } DnsResolveResult <PtrRecord> dnsPtrResult = await ResolveDnsAsync <PtrRecord>(ip.GetReverseLookupDomain(), RecordType.Ptr, token); if ((dnsPtrResult == null) || ((dnsPtrResult.ReturnCode != ReturnCode.NoError) && (dnsPtrResult.ReturnCode != ReturnCode.NxDomain))) { return(SpfQualifier.TempError); } DomainName ptrMechanismDomain = String.IsNullOrEmpty(mechanism.Domain) ? domain : await ExpandDomainAsync(mechanism.Domain, ip, domain, sender, token); int ptrCheckedCount = 0; foreach (PtrRecord ptrRecord in dnsPtrResult.Records) { if (++ptrCheckedCount == 10) { break; } bool?isPtrMatch = await IsIpMatchAsync(ptrRecord.PointerDomainName, ip, 0, 0, token); if (isPtrMatch.HasValue && isPtrMatch.Value) { if (ptrRecord.PointerDomainName.Equals(ptrMechanismDomain) || (ptrRecord.PointerDomainName.IsSubDomainOf(ptrMechanismDomain))) { return(mechanism.Qualifier); } } } break; case SpfMechanismType.Exist: if (++state.DnsLookupCount > 10) { return(SpfQualifier.PermError); } if (String.IsNullOrEmpty(mechanism.Domain)) { return(SpfQualifier.PermError); } DomainName existsMechanismDomain = String.IsNullOrEmpty(mechanism.Domain) ? domain : await ExpandDomainAsync(mechanism.Domain, ip, domain, sender, token); DnsResolveResult <ARecord> dnsAResult = await ResolveDnsAsync <ARecord>(existsMechanismDomain, RecordType.A, token); if ((dnsAResult == null) || ((dnsAResult.ReturnCode != ReturnCode.NoError) && (dnsAResult.ReturnCode != ReturnCode.NxDomain))) { return(SpfQualifier.TempError); } if (dnsAResult.Records.Count(record => (record.RecordType == RecordType.A)) > 0) { return(mechanism.Qualifier); } break; case SpfMechanismType.Include: if (++state.DnsLookupCount > 10) { return(SpfQualifier.PermError); } if (String.IsNullOrEmpty(mechanism.Domain)) { return(SpfQualifier.PermError); } DomainName includeMechanismDomain = String.IsNullOrEmpty(mechanism.Domain) ? domain : await ExpandDomainAsync(mechanism.Domain, ip, domain, sender, token); if (includeMechanismDomain.Equals(domain)) { return(SpfQualifier.PermError); } var includeResult = await CheckHostInternalAsync(ip, includeMechanismDomain, sender, false, state, token); switch (includeResult.Result) { case SpfQualifier.Pass: return(mechanism.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); } return(SpfQualifier.None); }
internal static bool TryParse(string s, out SpfTerm value) { if (String.IsNullOrEmpty(s)) { value = null; return(false); } #region Parse Mechanism Match match = _parseMechanismRegex.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 match = _parseModifierRegex.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 CheckMechanism(SpfMechanism mechanism, IPAddress ip, string sender, string domain) { DnsMessage dnsMessage; switch (mechanism.Type) { case SpfMechanismType.All: return(mechanism.Qualifier); case SpfMechanismType.A: bool?isAMatch = IsIpMatch(String.IsNullOrEmpty(mechanism.Domain) ? domain : mechanism.Domain, ip, mechanism.Prefix, mechanism.Prefix6); if (!isAMatch.HasValue) { return(SpfQualifier.TempError); } if (isAMatch.Value) { return(mechanism.Qualifier); } break; case SpfMechanismType.Mx: dnsMessage = ResolveDns(ExpandDomain(String.IsNullOrEmpty(mechanism.Domain) ? domain : mechanism.Domain, ip, sender, domain), RecordType.Mx); if ((dnsMessage == null) || ((dnsMessage.ReturnCode != ReturnCode.NoError) && (dnsMessage.ReturnCode != ReturnCode.NxDomain))) { return(SpfQualifier.TempError); } int mxCheckedCount = 0; foreach (MxRecord mxRecord in dnsMessage.AnswerRecords.OfType <MxRecord>()) { if (++mxCheckedCount == 10) { break; } bool?isMxMatch = IsIpMatch(mxRecord.ExchangeDomainName, ip, mechanism.Prefix, mechanism.Prefix6); if (!isMxMatch.HasValue) { return(SpfQualifier.TempError); } if (isMxMatch.Value) { return(mechanism.Qualifier); } } break; case SpfMechanismType.Ip4: case SpfMechanismType.Ip6: IPAddress compareAddress; if (IPAddress.TryParse(mechanism.Domain, out compareAddress)) { if (ip.AddressFamily != compareAddress.AddressFamily) { return(SpfQualifier.None); } if (mechanism.Prefix.HasValue) { if ((mechanism.Prefix.Value < 0) || (mechanism.Prefix.Value > (compareAddress.AddressFamily == AddressFamily.InterNetworkV6 ? 128 : 32))) { return(SpfQualifier.PermError); } if (ip.GetNetworkAddress(mechanism.Prefix.Value).Equals(compareAddress.GetNetworkAddress(mechanism.Prefix.Value))) { return(mechanism.Qualifier); } } else if (ip.Equals(compareAddress)) { return(mechanism.Qualifier); } } else { return(SpfQualifier.PermError); } break; case SpfMechanismType.Ptr: dnsMessage = ResolveDns(ip.GetReverseLookupAddress(), RecordType.Ptr); if ((dnsMessage == null) || ((dnsMessage.ReturnCode != ReturnCode.NoError) && (dnsMessage.ReturnCode != ReturnCode.NxDomain))) { return(SpfQualifier.TempError); } string ptrCompareName = String.IsNullOrEmpty(mechanism.Domain) ? domain : mechanism.Domain; int ptrCheckedCount = 0; foreach (PtrRecord ptrRecord in dnsMessage.AnswerRecords.OfType <PtrRecord>()) { if (++ptrCheckedCount == 10) { break; } bool?isPtrMatch = IsIpMatch(ptrRecord.PointerDomainName, ip, 0, 0); if (isPtrMatch.HasValue && isPtrMatch.Value) { if (ptrRecord.PointerDomainName.Equals(ptrCompareName, StringComparison.InvariantCultureIgnoreCase) || (ptrRecord.PointerDomainName.EndsWith("." + ptrCompareName, StringComparison.InvariantCultureIgnoreCase))) { return(mechanism.Qualifier); } } } break; case SpfMechanismType.Exist: if (String.IsNullOrEmpty(mechanism.Domain)) { return(SpfQualifier.PermError); } dnsMessage = ResolveDns(ExpandDomain(mechanism.Domain, ip, sender, domain), 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(mechanism.Qualifier); } break; case SpfMechanismType.Include: if (String.IsNullOrEmpty(mechanism.Domain) || (mechanism.Domain.Equals(domain, StringComparison.InvariantCultureIgnoreCase))) { return(SpfQualifier.PermError); } string includeDomain = ExpandDomain(mechanism.Domain, ip, sender, domain); string explanation; switch (CheckHostInternal(ip, sender, includeDomain, false, out explanation)) { case SpfQualifier.Pass: return(mechanism.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); } return(SpfQualifier.None); }
internal static bool TryParse(string s, out SpfTerm value) { if (String.IsNullOrEmpty(s)) { value = null; return false; } #region Parse Mechanism Match match = _parseMechanismRegex.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 match = _parseModifierRegex.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; }
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-z]+)(:(?<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); }
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); }