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; } }
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 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(); }