Example #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="spfStatement">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(SpfStatement spfStatement,
                                                                                 SpfExpression[] spfExpressions)
        {
            Explanation explanation = null;

            string[] spfStatementStrings = null;
            string   s;

            try
            {
                string[] txt = await DnsResolver.LookupText(spfStatement.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 (!(spfStatementStrings is null))
                    {
                        return(new KeyValuePair <SpfResult, string>(SpfResult.PermanentError,
                                                                    "Multiple SPF records found for " + spfStatement.Domain + "."));
                    }

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

            if (spfStatementStrings is null)
            {
                if (!(spfExpressions is null))
                {
                    foreach (SpfExpression expression in spfExpressions)
                    {
                        if (expression.IsApplicable(spfStatement.Domain))
                        {
                            if (expression.Spf.StartsWith("v=spf1"))
                            {
                                spfStatementStrings = expression.Spf.Substring(6).Trim()
                                                      .Split(Space, StringSplitOptions.RemoveEmptyEntries);
                                break;
                            }
                        }
                    }
                }

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

            // Syntax evaluation first, ยง4.6

            int c = spfStatementStrings.Length;
            LinkedList <Mechanism> mechanisms = new LinkedList <Mechanism>();
            Redirect redirect = null;
            int      i;

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

                    spfStatement.Reset(spfStatementStrings[i]);
                    spfStatement.SkipWhitespace();

                    switch (spfStatement.PeekNextCharacter())
                    {
                    case '+':
                        spfStatement.Position++;
                        qualifier = SpfQualifier.Pass;
                        break;

                    case '-':
                        spfStatement.Position++;
                        qualifier = SpfQualifier.Fail;
                        break;

                    case '~':
                        spfStatement.Position++;
                        qualifier = SpfQualifier.SoftFail;
                        break;

                    case '?':
                        spfStatement.Position++;
                        qualifier = SpfQualifier.Neutral;
                        break;

                    default:
                        qualifier = SpfQualifier.Pass;
                        break;
                    }

                    switch (spfStatement.NextLabel().ToLower())
                    {
                    case "all":
                        mechanisms.AddLast(new All(spfStatement, qualifier));
                        break;

                    case "include":
                        mechanisms.AddLast(new Include(spfStatement, qualifier, spfExpressions));
                        break;

                    case "a":
                        mechanisms.AddLast(new A(spfStatement, qualifier));
                        break;

                    case "mx":
                        mechanisms.AddLast(new Mx(spfStatement, qualifier));
                        break;

                    case "ptr":
                        mechanisms.AddLast(new Ptr(spfStatement, qualifier));
                        break;

                    case "ip4":
                        mechanisms.AddLast(new Ip4(spfStatement, qualifier));
                        break;

                    case "ip6":
                        mechanisms.AddLast(new Ip6(spfStatement, qualifier));
                        break;

                    case "exists":
                        mechanisms.AddLast(new Exists(spfStatement, qualifier));
                        break;

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

                        redirect = new Redirect(spfStatement, qualifier);
                        break;

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

                        explanation = new Explanation(spfStatement, 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 = spfStatement.Domain;
                    spfStatement.Domain = redirect.Domain;
                    try
                    {
                        KeyValuePair <SpfResult, string> result =
                            await SpfResolver.CheckHost(spfStatement, 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
                    {
                        spfStatement.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));
        }