Пример #1
0
        /// <summary>
        /// Fetches SPF records, parses them, and
        /// evaluates them to determine whether a particular host is or is not
        /// permitted to send mail with a given identity.
        /// </summary>
        /// <param name="Term">Information about current query.</param>
        /// <param name="SpfExpressions">SPF Expressions that can be used, in case a domain lacks SPF records in the DNS.</param>
        /// <returns>Result of SPF evaluation, together with an optional explanation string,
        /// if one exists, and if the result indicates a failure.</returns>
        internal static async Task <KeyValuePair <SpfResult, string> > CheckHost(Term Term, SpfExpression[] SpfExpressions)
        {
            Exp Explanation = null;

            string[] TermStrings = null;
            string   s;

            try
            {
                string[] TXT = await DnsResolver.LookupText(Term.domain);

                foreach (string Row in TXT)
                {
                    s = Row.Trim();

                    if (s.Length > 1 && s[0] == '"' && s[s.Length - 1] == '"')
                    {
                        s = s.Substring(1, s.Length - 2);
                    }

                    if (!s.StartsWith("v=spf1"))
                    {
                        continue;
                    }

                    if (!(TermStrings is null))
                    {
                        return(new KeyValuePair <SpfResult, string>(SpfResult.PermanentError, "Multiple SPF records found for " + Term.domain + "."));
                    }

                    TermStrings = s.Substring(6).Trim().Split(space, StringSplitOptions.RemoveEmptyEntries);
                }
            }
            catch (Exception)
            {
                TermStrings = null;
            }

            if (TermStrings is null)
            {
                if (!(SpfExpressions is null))
                {
                    foreach (SpfExpression Expression in SpfExpressions)
                    {
                        if (Expression.IsApplicable(Term.domain))
                        {
                            if (Expression.Spf.StartsWith("v=spf1"))
                            {
                                TermStrings = Expression.Spf.Substring(6).Trim().Split(space, StringSplitOptions.RemoveEmptyEntries);
                                break;
                            }
                        }
                    }
                }

                if (TermStrings is null)
                {
                    return(new KeyValuePair <SpfResult, string>(SpfResult.None, "No SPF records found " + Term.domain + "."));
                }
            }

            // Syntax evaluation first, §4.6

            int c = TermStrings.Length;
            LinkedList <Mechanism> Mechanisms = new LinkedList <Mechanism>();
            Redirect Redirect = null;
            int      i;

            try
            {
                for (i = 0; i < c; i++)
                {
                    SpfQualifier Qualifier;

                    Term.Reset(TermStrings[i]);
                    Term.SkipWhiteSpace();

                    switch (Term.PeekNextChar())
                    {
                    case '+':
                        Term.pos++;
                        Qualifier = SpfQualifier.Pass;
                        break;

                    case '-':
                        Term.pos++;
                        Qualifier = SpfQualifier.Fail;
                        break;

                    case '~':
                        Term.pos++;
                        Qualifier = SpfQualifier.SoftFail;
                        break;

                    case '?':
                        Term.pos++;
                        Qualifier = SpfQualifier.Neutral;
                        break;

                    default:
                        Qualifier = SpfQualifier.Pass;
                        break;
                    }

                    switch (Term.NextLabel().ToLower())
                    {
                    case "all":
                        Mechanisms.AddLast(new All(Term, Qualifier));
                        break;

                    case "include":
                        Mechanisms.AddLast(new Include(Term, Qualifier, SpfExpressions));
                        break;

                    case "a":
                        Mechanisms.AddLast(new A(Term, Qualifier));
                        break;

                    case "mx":
                        Mechanisms.AddLast(new Mx(Term, Qualifier));
                        break;

                    case "ptr":
                        Mechanisms.AddLast(new Ptr(Term, Qualifier));
                        break;

                    case "ip4":
                        Mechanisms.AddLast(new Ip4(Term, Qualifier));
                        break;

                    case "ip6":
                        Mechanisms.AddLast(new Ip6(Term, Qualifier));
                        break;

                    case "exists":
                        Mechanisms.AddLast(new Exists(Term, Qualifier));
                        break;

                    case "redirect":
                        if (!(Redirect is null))
                        {
                            return(new KeyValuePair <SpfResult, string>(SpfResult.PermanentError, "Multiple redirect modifiers foundin SPF record."));
                        }

                        Redirect = new Redirect(Term, Qualifier);
                        break;

                    case "exp":
                        if (!(Explanation is null))
                        {
                            return(new KeyValuePair <SpfResult, string>(SpfResult.PermanentError, "Multiple exp modifiers foundin SPF record."));
                        }

                        Explanation = new Exp(Term, Qualifier);
                        break;

                    default:
                        throw new Exception("Syntax error.");
                    }
                }

                foreach (Mechanism Mechanism in Mechanisms)
                {
                    await Mechanism.Expand();

                    SpfResult Result = await Mechanism.Matches();

                    switch (Result)
                    {
                    case SpfResult.Pass:
                        switch (Mechanism.Qualifier)
                        {
                        case SpfQualifier.Pass: return(new KeyValuePair <SpfResult, string>(SpfResult.Pass, null));

                        case SpfQualifier.Fail: return(new KeyValuePair <SpfResult, string>(SpfResult.Fail, Explanation == null ? null : await Explanation.Evaluate()));

                        case SpfQualifier.Neutral: return(new KeyValuePair <SpfResult, string>(SpfResult.Neutral, null));

                        case SpfQualifier.SoftFail: return(new KeyValuePair <SpfResult, string>(SpfResult.SoftFail, Explanation == null ? null : await Explanation.Evaluate()));
                        }
                        break;

                    case SpfResult.TemporaryError:
                        return(new KeyValuePair <SpfResult, string>(SpfResult.TemporaryError, Explanation == null ? null : await Explanation.Evaluate()));

                    case SpfResult.None:
                    case SpfResult.PermanentError:
                        return(new KeyValuePair <SpfResult, string>(SpfResult.PermanentError, Explanation == null ? null : await Explanation.Evaluate()));
                    }
                }

                if (!(Redirect is null))
                {
                    await Redirect.Expand();

                    string Bak = Term.domain;
                    Term.domain = Redirect.Domain;
                    try
                    {
                        KeyValuePair <SpfResult, string> Result = await SpfResolver.CheckHost(Term, SpfExpressions);

                        if (Result.Key == SpfResult.None)
                        {
                            return(new KeyValuePair <SpfResult, string>(SpfResult.PermanentError, Explanation == null ? null : await Explanation.Evaluate()));
                        }
                        else if (Result.Key != SpfResult.Pass && Result.Key != SpfResult.Neutral &&
                                 string.IsNullOrEmpty(Result.Value))
                        {
                            return(new KeyValuePair <SpfResult, string>(Result.Key, Explanation == null ? null : await Explanation.Evaluate()));
                        }
                        else
                        {
                            return(Result);
                        }
                    }
                    finally
                    {
                        Term.domain = Bak;
                    }
                }
            }
            catch (Exception ex)
            {
                return(new KeyValuePair <SpfResult, string>(SpfResult.PermanentError, "Unable to evaluate SPF record: " + FirstRow(ex.Message)));
            }

            return(new KeyValuePair <SpfResult, string>(SpfResult.Neutral, null));
        }