コード例 #1
0
        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);
        }
コード例 #2
0
 public Ip4(SpfStatement spfStatement, SpfQualifier qualifier) : base(spfStatement, qualifier)
 {
     if (IpAddress.AddressFamily != AddressFamily.InterNetwork)
     {
         throw new Exception("IPv4 address expected.");
     }
 }
コード例 #3
0
 /// <summary>
 /// This mechanisms tests whether &lt;ip&gt; is contained within a given IP6 network.
 /// </summary>
 /// <param name="Term">Term</param>
 /// <param name="Qualifier">Qualifier</param>
 public Ip6(Term Term, SpfQualifier Qualifier)
     : base(Term, Qualifier)
 {
     if (this.address.AddressFamily != AddressFamily.InterNetworkV6)
     {
         throw new Exception("Expected IP6 address.");
     }
 }
コード例 #4
0
ファイル: Ip.cs プロジェクト: large/NetSPF
        public Ip(SpfStatement spfStatement, SpfQualifier qualifier) : base(spfStatement, qualifier)
        {
            if (SpfStatement.PeekNextCharacter() != ':')
            {
                throw new Exception(": expected.");
            }

            SpfStatement.NextCharacter();

            int  start = SpfStatement.Position;
            char ch;

            while (SpfStatement.Position < SpfStatement.Statement.Length &&
                   (ch = SpfStatement.Statement[SpfStatement.Position]) != '/' && ch > ' ')
            {
                SpfStatement.Position++;
            }

            if (!IPAddress.TryParse(SpfStatement.Statement.Substring(start, SpfStatement.Position - start),
                                    out IpAddress))
            {
                throw new Exception("IP Address expected.");
            }

            int max;

            switch (IpAddress.AddressFamily)
            {
            case AddressFamily.InterNetwork:
                max = 32;
                break;

            case AddressFamily.InterNetworkV6:
                max = 128;
                break;

            default:
                throw new Exception("IP Address expected.");
            }

            if (SpfStatement.PeekNextCharacter() == '/')
            {
                SpfStatement.NextCharacter();

                Cidr = SpfStatement.NextInteger();
                if (Cidr < 0 || Cidr > max)
                {
                    throw new Exception("Invalid CIDR");
                }
            }
            else
            {
                Cidr = max;
            }
        }
コード例 #5
0
 protected override bool TryLoadRecords(string domain, out SpfRecord record, out SpfQualifier errorResult)
 {
     if (!TryLoadRecords(domain, RecordType.Spf, out record, out errorResult))
     {
         return((errorResult == SpfQualifier.None) && TryLoadRecords(domain, RecordType.Txt, out record, out errorResult));
     }
     else
     {
         return(true);
     }
 }
コード例 #6
0
ファイル: Ip.cs プロジェクト: robinreigns/IoTGateway
        /// <summary>
        /// Abstract base class for IP-based SPF mechanisms.
        /// </summary>
        /// <param name="Term">Term</param>
        /// <param name="Qualifier">Qualifier</param>
        public Ip(Term Term, SpfQualifier Qualifier)
            : base(Term, Qualifier)
        {
            if (Term.PeekNextChar() != ':')
            {
                throw new Exception(": expected.");
            }

            Term.NextChar();

            int  Start = Term.pos;
            char ch;

            while (Term.pos < Term.len && (ch = Term.s[Term.pos]) != '/' && ch > ' ')
            {
                Term.pos++;
            }

            if (!IPAddress.TryParse(Term.s.Substring(Start, Term.pos - Start), out this.address))
            {
                throw new Exception("IP Address expected.");
            }

            int Max;

            switch (this.address.AddressFamily)
            {
            case AddressFamily.InterNetwork:
                Max = 32;
                break;

            case AddressFamily.InterNetworkV6:
                Max = 128;
                break;

            default:
                throw new Exception("IP Address expected.");
            }

            if (Term.PeekNextChar() == '/')
            {
                Term.NextChar();

                this.cidr = Term.NextInteger();
                if (this.cidr < 0 || this.cidr > Max)
                {
                    throw new Exception("Invalid CIDR");
                }
            }
            else
            {
                this.cidr = Max;
            }
        }
コード例 #7
0
        /// <summary>
        /// Abstract base class for SPF mechanisms with a domain specification and
        /// an optional CIDR specification.
        /// </summary>
        /// <param name="Term">Term</param>
        /// <param name="Qualifier">Qualifier</param>
        public MechanismDomainCidrSpec(Term Term, SpfQualifier Qualifier)
            : base(Term, Qualifier)
        {
            if (Term.PeekNextChar() == '/')
            {
                Term.NextChar();

                bool HasIp4;

                if (HasIp4 = char.IsDigit(Term.PeekNextChar()))
                {
                    this.ip4Cidr = Term.NextInteger();
                    if (this.ip4Cidr < 0 || this.ip4Cidr > 32)
                    {
                        throw new Exception("Invalid IP4 CIDR");
                    }
                }

                if (Term.PeekNextChar() == '/')
                {
                    Term.NextChar();

                    if (HasIp4 && Term.PeekNextChar() == '/')
                    {
                        Term.NextChar();
                    }

                    if (char.IsDigit(Term.PeekNextChar()))
                    {
                        this.ip6Cidr = Term.NextInteger();
                        if (this.ip6Cidr < 0 || this.ip4Cidr > 128)
                        {
                            throw new Exception("Invalid IP6 CIDR");
                        }
                    }
                    else if (!HasIp4)
                    {
                        throw new Exception("IP4 or IP6 CIDR expected.");
                    }
                }
            }
        }
コード例 #8
0
        public DomainCidrSpecification(SpfStatement statement, SpfQualifier spfQualifier)
            : base(statement, spfQualifier)
        {
            if (statement.PeekNextCharacter() == '/')
            {
                statement.NextCharacter();
                bool hasIpv4;

                if (hasIpv4 = char.IsDigit(statement.PeekNextCharacter()))
                {
                    Ipv4Cidr = statement.NextInteger();
                    if (Ipv4Cidr < 0 || Ipv4Cidr > 32)
                    {
                        throw new Exception("Invalid IPv4 CIDR");
                    }
                }

                if (statement.PeekNextCharacter() == '/')
                {
                    statement.NextCharacter();

                    if (hasIpv4 && statement.PeekNextCharacter() == '/')
                    {
                        statement.NextCharacter();
                    }

                    if (char.IsDigit(statement.PeekNextCharacter()))
                    {
                        Ipv6Cidr = statement.NextInteger();
                        if (Ipv6Cidr < 0 || Ipv6Cidr > 128)
                        {
                            throw new Exception("Invalid IPv6 CIDR");
                        }
                    }
                    else if (!hasIpv4)
                    {
                        throw new Exception("IPv4 or IPv6 CIDR expected.");
                    }
                }
            }
        }
コード例 #9
0
ファイル: DomainSpecification.cs プロジェクト: large/NetSPF
        public DomainSpecification(SpfStatement spfStatement, SpfQualifier qualifier) : base(spfStatement, qualifier)
        {
            if (SpfStatement.PeekNextCharacter() == Separator)
            {
                SpfStatement.NextCharacter();

                var  start = SpfStatement.Position;
                char ch;

                while ((ch = SpfStatement.PeekNextCharacter()) > ' ' && ch != '/')
                {
                    SpfStatement.Position++;
                }

                Domain = SpfStatement.Statement.Substring(start, SpfStatement.Position - start);
            }
            else if (DomainRequired)
            {
                throw new Exception($"{Separator} expected");
            }
        }
コード例 #10
0
        /// <summary>
        /// Abstract base class for SPF mechanisms with a domain specification.
        /// </summary>
        /// <param name="Term">Term</param>
        /// <param name="Qualifier">Qualifier</param>
        public MechanismDomainSpec(Term Term, SpfQualifier Qualifier)
            : base(Term, Qualifier)
        {
            if (Term.PeekNextChar() == this.Separator)
            {
                Term.NextChar();

                int  Start = Term.pos;
                char ch;

                while ((ch = Term.PeekNextChar()) > ' ' && ch != '/')
                {
                    Term.pos++;
                }

                this.domain = Term.s.Substring(Start, Term.pos - Start);
            }
            else if (this.DomainRequired)
            {
                throw new Exception(this.Separator + " expected.");
            }
        }
コード例 #11
0
ファイル: Mechanism.cs プロジェクト: robinreigns/IoTGateway
 /// <summary>
 /// Abstract base class for SPF Mechanisms.
 /// </summary>
 /// <param name="Term">Term</param>
 /// <param name="Qualifier">Qualifier</param>
 public Mechanism(Term Term, SpfQualifier Qualifier)
 {
     this.term      = Term;
     this.qualifier = Qualifier;
 }
コード例 #12
0
 /// <summary>
 /// This mechanism is used to construct an arbitrary domain name that is
 /// used for a DNS A record query.It allows for complicated schemes
 /// involving arbitrary parts of the mail envelope to determine what is
 /// permitted.
 /// </summary>
 /// <param name="Term">Term</param>
 /// <param name="Qualifier">Qualifier</param>
 public Exists(Term Term, SpfQualifier Qualifier)
     : base(Term, Qualifier)
 {
 }
コード例 #13
0
ファイル: Redirect.cs プロジェクト: large/NetSPF
 public Redirect(SpfStatement spfStatement, SpfQualifier qualifier) : base(spfStatement, qualifier)
 {
 }
コード例 #14
0
ファイル: Explanation.cs プロジェクト: large/NetSPF
 public Explanation(SpfStatement spfStatement, SpfQualifier qualifier) : base(spfStatement, qualifier)
 {
 }
コード例 #15
0
 public Ptr(SpfStatement spfStatement, SpfQualifier qualifier) : base(spfStatement, qualifier)
 {
 }
コード例 #16
0
ファイル: Include.cs プロジェクト: large/NetSPF
 public Include(SpfStatement spfStatement, SpfQualifier qualifier, params SpfExpression[] spfExpressions)
     : base(spfStatement, qualifier)
 {
     _spfExpressions = spfExpressions;
 }
コード例 #17
0
 public Exists(SpfStatement spfStatement, SpfQualifier qualifier) : base(spfStatement, qualifier)
 {
 }
コード例 #18
0
ファイル: A.cs プロジェクト: large/NetSPF
 public A(SpfStatement statement, SpfQualifier spfQualifier) : base(statement, spfQualifier)
 {
 }
コード例 #19
0
 public Mechanism(SpfStatement spfStatement, SpfQualifier qualifier)
 {
     Qualifier    = qualifier;
     SpfStatement = spfStatement;
 }
コード例 #20
0
ファイル: Ptr.cs プロジェクト: robinreigns/IoTGateway
 /// <summary>
 /// This mechanism tests whether the DNS reverse-mapping for &lt;ip&gt; exists
 /// and correctly points to a domain name within a particular domain.
 /// This mechanism SHOULD NOT be published.See the note at the end of
 /// this section for more information.
 /// </summary>
 /// <param name="Term">Term</param>
 /// <param name="Qualifier">Qualifier</param>
 public Ptr(Term Term, SpfQualifier Qualifier)
     : base(Term, Qualifier)
 {
 }
コード例 #21
0
 /// <summary>
 /// This mechanism matches if &lt;ip&gt; is one of the MX hosts for a domain name.
 /// </summary>
 /// <param name="Term">Term</param>
 /// <param name="Qualifier">Qualifier</param>
 internal Mx(Term Term, SpfQualifier Qualifier)
     : base(Term, Qualifier)
 {
 }
コード例 #22
0
ファイル: ValidatorBase.cs プロジェクト: mrtesoft/DNSAgent
 protected abstract bool TryLoadRecords(string domain, out T record, out SpfQualifier errorResult);
コード例 #23
0
        private bool TryLoadRecords(string domain, RecordType recordType, out SpfRecord record, out SpfQualifier errorResult)
        {
            DnsMessage dnsMessage = ResolveDns(domain, recordType);

            if ((dnsMessage == null) || ((dnsMessage.ReturnCode != ReturnCode.NoError) && (dnsMessage.ReturnCode != ReturnCode.NxDomain)))
            {
                record      = default(SpfRecord);
                errorResult = SpfQualifier.TempError;
                return(false);
            }

            var spfTextRecords =
                dnsMessage.AnswerRecords
                .Where(r => r.RecordType == recordType)
                .Cast <ITextRecord>()
                .Select(r => r.TextData)
                .Where(SpfRecord.IsSpfRecord).ToList();

            if (spfTextRecords.Count == 0)
            {
                record      = default(SpfRecord);
                errorResult = SpfQualifier.None;
                return(false);
            }
            else if ((spfTextRecords.Count > 1) || !SpfRecord.TryParse(spfTextRecords[0], out record))
            {
                record      = default(SpfRecord);
                errorResult = SpfQualifier.PermError;
                return(false);
            }
            else
            {
                errorResult = default(SpfQualifier);
                return(true);
            }
        }
コード例 #24
0
        private async Task <ValidationResult> CheckHostInternalAsync(IPAddress ip, DomainName domain, string sender, bool expandExplanation, State state, CancellationToken token)
        {
            if ((domain == null) || (domain.Equals(DomainName.Root)))
            {
                return(new ValidationResult()
                {
                    Result = SpfQualifier.None, Explanation = String.Empty
                });
            }

            if (String.IsNullOrEmpty(sender))
            {
                sender = "postmaster@unknown";
            }
            else if (!sender.Contains('@'))
            {
                sender = "postmaster@" + sender;
            }

            LoadRecordResult loadResult = await LoadRecordsAsync(domain, token);

            if (!loadResult.CouldBeLoaded)
            {
                return(new ValidationResult()
                {
                    Result = loadResult.ErrorResult, Explanation = String.Empty
                });
            }

            T record = loadResult.Record;

            if ((record.Terms == null) || (record.Terms.Count == 0))
            {
                return new ValidationResult()
                       {
                           Result = SpfQualifier.Neutral, Explanation = String.Empty
                       }
            }
            ;

            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 new ValidationResult()
                       {
                           Result = SpfQualifier.PermError, Explanation = String.Empty
                       }
            }
            ;

            ValidationResult result = new ValidationResult()
            {
                Result = loadResult.ErrorResult
            };

            #region Evaluate mechanism
            foreach (SpfMechanism mechanism in record.Terms.OfType <SpfMechanism>())
            {
                if (state.DnsLookupCount > DnsLookupLimit)
                {
                    return new ValidationResult()
                           {
                               Result = SpfQualifier.PermError, Explanation = String.Empty
                           }
                }
                ;

                SpfQualifier qualifier = await CheckMechanismAsync(mechanism, ip, domain, sender, state, token);

                if (qualifier != SpfQualifier.None)
                {
                    result.Result = qualifier;

                    break;
                }
            }
            #endregion

            #region Evaluate modifiers
            if (result.Result == SpfQualifier.None)
            {
                SpfModifier redirectModifier = record.Terms.OfType <SpfModifier>().FirstOrDefault(m => m.Type == SpfModifierType.Redirect);
                if (redirectModifier != null)
                {
                    if (++state.DnsLookupCount > 10)
                    {
                        return new ValidationResult()
                               {
                                   Result = SpfQualifier.PermError, Explanation = String.Empty
                               }
                    }
                    ;

                    DomainName redirectDomain = await ExpandDomainAsync(redirectModifier.Domain ?? String.Empty, ip, domain, sender, token);

                    if ((redirectDomain == null) || (redirectDomain == DomainName.Root) || (redirectDomain.Equals(domain)))
                    {
                        result.Result = SpfQualifier.PermError;
                    }
                    else
                    {
                        result = await CheckHostInternalAsync(ip, redirectDomain, sender, expandExplanation, state, token);

                        if (result.Result == SpfQualifier.None)
                        {
                            result.Result = SpfQualifier.PermError;
                        }
                    }
                }
            }
            else if ((result.Result == SpfQualifier.Fail) && expandExplanation)
            {
                SpfModifier expModifier = record.Terms.OfType <SpfModifier>().FirstOrDefault(m => m.Type == SpfModifierType.Exp);
                if (expModifier != null)
                {
                    DomainName target = await ExpandDomainAsync(expModifier.Domain, ip, domain, sender, token);

                    if ((target == null) || (target.Equals(DomainName.Root)))
                    {
                        result.Explanation = String.Empty;
                    }
                    else
                    {
                        DnsResolveResult <TxtRecord> dnsResult = await ResolveDnsAsync <TxtRecord>(target, RecordType.Txt, token);

                        if ((dnsResult != null) && (dnsResult.ReturnCode == ReturnCode.NoError))
                        {
                            TxtRecord txtRecord = dnsResult.Records.FirstOrDefault();
                            if (txtRecord != null)
                            {
                                result.Explanation = (await ExpandMacroAsync(txtRecord.TextData, ip, domain, sender, token)).ToString();
                            }
                        }
                    }
                }
            }
            #endregion

            if (result.Result == SpfQualifier.None)
            {
                result.Result = SpfQualifier.Neutral;
            }

            return(result);
        }
コード例 #25
0
 /// <summary>
 /// The "all" mechanism is a test that always matches.  It is used as the
 /// rightmost mechanism in a record to provide an explicit default.
 /// </summary>
 /// <param name="Term">Term</param>
 /// <param name="Qualifier">Qualifier</param>
 public All(Term Term, SpfQualifier Qualifier)
     : base(Term, Qualifier)
 {
 }
コード例 #26
0
ファイル: ValidatorBase.cs プロジェクト: mrtesoft/DNSAgent
        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);
        }
コード例 #27
0
        private bool TryLoadRecords(string domain, RecordType recordType, out SenderIDRecord record, out SpfQualifier errorResult)
        {
            DnsMessage dnsMessage = ResolveDns(domain, recordType);

            if ((dnsMessage == null) || ((dnsMessage.ReturnCode != ReturnCode.NoError) && (dnsMessage.ReturnCode != ReturnCode.NxDomain)))
            {
                record      = default(SenderIDRecord);
                errorResult = SpfQualifier.TempError;
                return(false);
            }
            else if ((Scope == SenderIDScope.Pra) && (dnsMessage.ReturnCode == ReturnCode.NxDomain))
            {
                record      = default(SenderIDRecord);
                errorResult = SpfQualifier.Fail;
                return(false);
            }

            var senderIDTextRecords =
                dnsMessage.AnswerRecords
                .Where(r => r.RecordType == recordType)
                .Cast <ITextRecord>()
                .Select(r => r.TextData)
                .Where(t => SenderIDRecord.IsSenderIDRecord(t, Scope)).ToList();

            if (senderIDTextRecords.Count >= 1)
            {
                var potentialRecords = new List <SenderIDRecord>();
                foreach (var senderIDTextRecord in senderIDTextRecords)
                {
                    SenderIDRecord tmpRecord;
                    if (SenderIDRecord.TryParse(senderIDTextRecord, out tmpRecord))
                    {
                        potentialRecords.Add(tmpRecord);
                    }
                    else
                    {
                        record      = default(SenderIDRecord);
                        errorResult = SpfQualifier.PermError;
                        return(false);
                    }
                }

                if (potentialRecords.GroupBy(r => r.Version).Any(g => g.Count() > 1))
                {
                    record      = default(SenderIDRecord);
                    errorResult = SpfQualifier.PermError;
                    return(false);
                }
                else
                {
                    record      = potentialRecords.OrderByDescending(r => r.Version).First();
                    errorResult = default(SpfQualifier);
                    return(true);
                }
            }
            else
            {
                record      = default(SenderIDRecord);
                errorResult = SpfQualifier.None;
                return(false);
            }
        }
コード例 #28
0
 /// <summary>
 /// The "redirect" modifier is intended for consolidating both
 /// authorizations and policy into a common set to be shared within a
 /// single ADMD.It is possible to control both authorized hosts and
 /// policy for an arbitrary number of domains from a single record.
 /// </summary>
 /// <param name="Term">Term</param>
 /// <param name="Qualifier">Qualifier</param>
 public Redirect(Term Term, SpfQualifier Qualifier)
     : base(Term, Qualifier)
 {
 }
コード例 #29
0
ファイル: Include.cs プロジェクト: robinreigns/IoTGateway
 /// <summary>
 /// The "include" mechanism triggers a recursive evaluation of check_host().
 /// </summary>
 /// <param name="Term">Term</param>
 /// <param name="Qualifier">Qualifier</param>
 /// <param name="SpfExpressions">SPF Expressions that can be used, in case a domain lacks SPF records in the DNS.</param>
 public Include(Term Term, SpfQualifier Qualifier, params SpfExpression[] SpfExpressions)
     : base(Term, Qualifier)
 {
     this.spfExpressions = SpfExpressions;
 }