public Ip4(SpfStatement spfStatement, SpfQualifier qualifier) : base(spfStatement, qualifier) { if (IpAddress.AddressFamily != AddressFamily.InterNetwork) { throw new Exception("IPv4 address expected."); } }
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; } }
internal static bool Matches(IPAddress[] addresses, SpfStatement statement, int cidr) { byte[] statementAddressBytes = statement.IpAddress.GetAddressBytes(); int c = statementAddressBytes.Length; foreach (IPAddress addr in addresses) { byte[] addressBytes = addr.GetAddressBytes(); if (addressBytes.Length != c) { continue; } int bitsLeft = cidr; int pos = 0; while (bitsLeft > 0 && pos < c) { if (bitsLeft >= 8) { if (statementAddressBytes[pos] != addressBytes[pos]) { break; } bitsLeft -= 8; } else { byte mask = (byte)(0xff << (8 - bitsLeft)); if ((statementAddressBytes[pos] & mask) != (addressBytes[pos] & mask)) { break; } bitsLeft = 0; } pos++; } if (bitsLeft == 0) { return(true); } } return(false); }
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."); } } } }
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"); } }
internal static async Task <bool> Matches(string domain, SpfStatement statement, int cidr4, int cidr6) { IPAddress[] addresses; int cidr; switch (statement.IpAddress.AddressFamily) { case AddressFamily.InterNetwork: if (statement.RemainingQueries-- <= 0) { throw new Exception("DNS Lookup maximum reached."); } addresses = await DnsResolver.LookupIp4Addresses(domain); cidr = cidr4; break; case AddressFamily.InterNetworkV6: if (statement.RemainingQueries-- <= 0) { throw new Exception("DNS Lookup maximum reached."); } addresses = await DnsResolver.LookupIp6Addresses(domain); cidr = cidr6; break; default: return(false); } return(Matches(addresses, statement, cidr)); }
public async Task <string> Evaluate() { try { await Expand(); StringBuilder sb = new StringBuilder(); foreach (string text in await DnsResolver.LookupText(Domain)) { sb.Append(text); // No white-space delimiter } SpfStatement.Reset("=" + sb.ToString()); Explanation explanation = new Explanation(SpfStatement, Qualifier); await explanation.Expand(); return(explanation.Domain); } catch (Exception) { return(null); } }
public override async Task Expand() { if (_expanded) { return; } _expanded = true; SpfStatement.Reset(Domain); StringBuilder sb = new StringBuilder(); char ch; while ((ch = SpfStatement.PeekNextCharacter()) > ' ') { SpfStatement.Position++; if (ch == '%') { switch (ch = SpfStatement.PeekNextCharacter()) { case (char)0: sb.Append('%'); break; case '%': SpfStatement.Position++; sb.Append('%'); break; case '_': SpfStatement.Position++; sb.Append(' '); break; case '-': SpfStatement.Position++; sb.Append("%20"); break; case '{': SpfStatement.Position++; char macroLetter = char.ToLower(SpfStatement.NextCharacter()); int? digit; bool reverse; if (char.IsDigit(SpfStatement.PeekNextCharacter())) { digit = SpfStatement.NextInteger(); if (digit == 0) { throw new Exception("Invalid number of digits."); } } else { digit = null; } if (char.ToLower(SpfStatement.PeekNextCharacter()) == 'r') { SpfStatement.Position++; reverse = true; } else { reverse = false; } int start = SpfStatement.Position; while ((ch = SpfStatement.PeekNextCharacter()) == '.' || ch == '-' || ch == '+' || ch == ',' || ch == '/' || ch == '_' || ch == '=') { SpfStatement.Position++; } string delimiter = SpfStatement.Statement.Substring(start, SpfStatement.Position - start); ch = SpfStatement.NextCharacter(); if (ch != '}') { throw new Exception("Expected }"); } string s; switch (macroLetter) { case 's': // sender s = SpfStatement.Sender; break; case 'l': // local-part of sender s = SpfStatement.Sender; int i = s.IndexOf('@'); if (i < 0) { s = string.Empty; } else { s = s.Substring(0, i); } break; case 'o': // domain of sender s = SpfStatement.Sender; i = s.IndexOf('@'); if (i >= 0) { s = s.Substring(i + 1); } break; case 'd': // domain s = SpfStatement.Domain; break; case 'i': switch (SpfStatement.IpAddress.AddressFamily) { case AddressFamily.InterNetwork: s = SpfStatement.IpAddress.ToString(); break; case AddressFamily.InterNetworkV6: byte[] bin = SpfStatement.IpAddress.GetAddressBytes(); StringBuilder sb2 = new StringBuilder(); byte b, b2; for (i = 0; i < 16; i++) { b = bin[i]; b2 = (byte)(b >> 4); if (b2 < 10) { sb2.Append((char)('0' + b2)); } else { sb2.Append((char)('a' + b2 - 10)); } sb2.Append('.'); b2 = (byte)(b & 15); if (b2 < 10) { sb2.Append((char)('0' + b2)); } else { sb2.Append((char)('a' + b2 - 10)); } if (i < 15) { sb2.Append('.'); } } s = sb2.ToString(); break; default: throw new Exception("Invalid client address."); } break; case 'p': try { if (SpfStatement.RemainingQueries-- <= 0) { throw new Exception("DNS Lookup maximum reached."); } string[] domainNames = await DnsResolver.LookupDomainName(SpfStatement.IpAddress); // First check if domain is found. s = null; foreach (string domainName in domainNames) { if (String.Compare(domainName, SpfStatement.Domain, StringComparison.OrdinalIgnoreCase) == 0 && await this.MatchReverseIp(domainName)) { s = domainName; break; } } if (s is null) { // Second, check if sub-domain is found. foreach (string domainName in domainNames) { if (domainName.EndsWith("." + SpfStatement.Domain, StringComparison.CurrentCultureIgnoreCase) && await this.MatchReverseIp(domainName)) { s = domainName; break; } } if (s is null) { if (domainNames.Length == 0) { s = "unknown"; } else { s = domainNames[DnsResolver.Next(domainNames.Length)]; } } } } catch (ArgumentException) { s = "unknown"; } catch (TimeoutException) { s = "unknown"; } break; case 'v': switch (SpfStatement.IpAddress.AddressFamily) { case AddressFamily.InterNetwork: s = "in-addr"; break; case AddressFamily.InterNetworkV6: s = "ip6"; break; default: throw new Exception("Invalid client address."); } break; case 'h': s = SpfStatement.HeloDomain; break; case 'c': this.AssertExplanation(); s = SpfStatement.IpAddress.ToString(); break; case 'r': this.AssertExplanation(); s = SpfStatement.HostDomain; break; case 't': this.AssertExplanation(); int seconds = (int)Math.Round((DateTime.UtcNow - UnixEpoch).TotalSeconds); s = seconds.ToString(); break; default: throw new Exception("Unknown macro."); } if (reverse || digit.HasValue || !string.IsNullOrEmpty(delimiter)) { if (string.IsNullOrEmpty(delimiter)) { delimiter = "."; } string[] parts = s.Split(new string[] { delimiter }, StringSplitOptions.None); int i = parts.Length; if (reverse) { Array.Reverse(parts); } if (digit.HasValue && digit.Value < i) { i = digit.Value; } bool first = true; int j = parts.Length - i; while (i-- > 0) { if (first) { first = false; } else { sb.Append('.'); } sb.Append(parts[j++]); } } else { sb.Append(s); } break; default: SpfStatement.Position++; sb.Append('%'); sb.Append(ch); break; } } else { sb.Append(ch); } } Domain = sb.ToString(); }
public Redirect(SpfStatement spfStatement, SpfQualifier qualifier) : base(spfStatement, qualifier) { }
public Ptr(SpfStatement spfStatement, SpfQualifier qualifier) : base(spfStatement, qualifier) { }
public Exists(SpfStatement spfStatement, SpfQualifier qualifier) : base(spfStatement, qualifier) { }
public A(SpfStatement statement, SpfQualifier spfQualifier) : base(statement, spfQualifier) { }
public Mechanism(SpfStatement spfStatement, SpfQualifier qualifier) { Qualifier = qualifier; SpfStatement = spfStatement; }
public Include(SpfStatement spfStatement, SpfQualifier qualifier, params SpfExpression[] spfExpressions) : base(spfStatement, qualifier) { _spfExpressions = spfExpressions; }
public Explanation(SpfStatement spfStatement, SpfQualifier qualifier) : base(spfStatement, qualifier) { }