public void ResolveKnownGoodHostFromDefaultLocalDnsServerAndReturnIpAddress() { const string host = "host1"; const string shimAddress = "127.0.0.1"; using (ShimsContext.Create()) { ShimDnsClient.AllInstances.ResolveStringRecordTypeRecordClass = (dnsClient, s, arg3, arg4) => new DnsMessage { IsEDnsEnabled = true, IsRecursionAllowed = true, IsRecursionDesired = true, ReturnCode = ReturnCode.NoError, AnswerRecords = new List<DnsRecordBase> { new ARecord(host, 60, IPAddress.Parse(shimAddress)) } }; var client = new DnsResolver(); var address = client.Resolve(host); var expectedResults = IPAddress.Parse(shimAddress); Assert.AreEqual(expectedResults, address); } }
public void ResolveBadHsotFromLocalDnsServerAndReturnNull() { const string host = "host1.notadomain.com"; using (ShimsContext.Create()) { ShimDnsClient.AllInstances.ResolveStringRecordTypeRecordClass = (dnsClient, s, arg3, arg4) => new DnsMessage { IsEDnsEnabled = true, IsRecursionAllowed = true, IsRecursionDesired = true, ReturnCode = ReturnCode.NxDomain, AnswerRecords = new List<DnsRecordBase>() }; var client = new DnsResolver(); var address = client.Resolve(host); Assert.IsNull(address); } }
/// <summary> /// Evaluates the function. /// </summary> /// <param name="Arguments">Function arguments.</param> /// <param name="Variables">Variables collection.</param> /// <returns>Function result.</returns> public override IElement Evaluate(IElement[] Arguments, Variables Variables) { string Name = Arguments[0].AssociatedObjectValue?.ToString() ?? string.Empty; string s; object Obj; QTYPE TYPE; QCLASS CLASS; int c = Arguments.Length; if (c < 2) { TYPE = QTYPE.A; } else { Obj = Arguments[1].AssociatedObjectValue; if (Obj is QTYPE Type2) { TYPE = Type2; } else { s = Obj?.ToString() ?? string.Empty; if (!Enum.TryParse <QTYPE>(s, out TYPE)) { throw new ScriptRuntimeException("Invalid QTYPE: " + s, this); } } } if (c < 3) { CLASS = QCLASS.IN; } else { Obj = Arguments[2].AssociatedObjectValue; if (Obj is QCLASS Class2) { CLASS = Class2; } else { s = Obj?.ToString() ?? string.Empty; if (!Enum.TryParse <QCLASS>(s, out CLASS)) { throw new ScriptRuntimeException("Invalid QCLASS: " + s, this); } } } ResourceRecord[] Records = DnsResolver.Resolve(Name, TYPE, CLASS).Result; if (Records.Length == 0) { throw new ScriptRuntimeException("Unable to resolve name.", this); } else if (Records.Length == 1) { return(new ObjectValue(Records[0])); } else { return(new ObjectVector(Records)); } }
public void DynDnsHandler_Udp_CNAME() { // Start a Dynamic DNS service handler and a client, register CNAME // records and confirm that CNAME queries return the proper results. DynDnsClientSettings clientSettings; DynDnsHandler handler = null; DynDnsClient client = null; DnsResponse response; CNAME_RR record; try { handler = new DynDnsHandler(); handler.Start(router, null, null, null); clientSettings = new DynDnsClientSettings() { Mode = DynDnsMode.Udp, NameServers = new NetworkBinding[] { new NetworkBinding("127.0.0.1:DYNAMIC-DNS") }, UdpRegisterInterval = TimeSpan.FromSeconds(1) }; client = new DynDnsClient(); client.Register(new DynDnsHostEntry("test1.com,server.test1.com,1000,CNAME")); client.Register(new DynDnsHostEntry("test2.com,server.test2.com,2000,CNAME")); client.Open(router, clientSettings); // Wait for everything to spin up. Thread.Sleep(3000); // Verify the single result for a CNAME query on test1.com response = DnsResolver.Query(IPAddress.Loopback, new DnsRequest(DnsFlag.NONE, "test1.com.", DnsQType.CNAME), TimeSpan.FromSeconds(2)); Assert.AreEqual("test1.com.", response.QName); Assert.AreEqual(DnsQType.CNAME, response.QType); Assert.AreEqual(1, response.Answers.Count); record = (CNAME_RR)response.Answers[0]; Assert.AreEqual("test1.com.", record.RName); Assert.AreEqual("server.test1.com.", record.CName); Assert.AreEqual(1000, record.TTL); Thread.Sleep(2000); // Verify the single result for a CNAME query on test2.com response = DnsResolver.Query(IPAddress.Loopback, new DnsRequest(DnsFlag.NONE, "test2.com.", DnsQType.CNAME), TimeSpan.FromSeconds(2)); Assert.AreEqual("test2.com.", response.QName); Assert.AreEqual(DnsQType.CNAME, response.QType); Assert.AreEqual(1, response.Answers.Count); record = (CNAME_RR)response.Answers[0]; Assert.AreEqual("test2.com.", record.RName); Assert.AreEqual("server.test2.com.", record.CName); Assert.AreEqual(2000, record.TTL); } finally { if (client != null) { client.Close(); } if (handler != null) { handler.Stop(); } } }
public void DynDnsHandler_Udp_ADDRESS() { // Start a Dynamic DNS service handler and a client, register two ADDRESS // records for TEST1.COM and confirm that A queries load balance across the two entries. // I'm also going to add an ADDRESS record for TEST2.COM and verify that this address // is never returned. DynDnsClientSettings clientSettings; DynDnsHandler handler = null; DynDnsClient client = null; DnsResponse response; A_RR record; bool found1; bool found2; try { handler = new DynDnsHandler(); handler.Start(router, null, null, null); clientSettings = new DynDnsClientSettings() { Mode = DynDnsMode.Udp, NameServers = new NetworkBinding[] { new NetworkBinding("127.0.0.1:DYNAMIC-DNS") }, UdpRegisterInterval = TimeSpan.FromSeconds(1) }; client = new DynDnsClient(); client.Register(new DynDnsHostEntry("test1.com,10.0.0.1,1000,ADDRESS")); client.Register(new DynDnsHostEntry("test1.com,10.0.0.2,1000,ADDRESS")); client.Register(new DynDnsHostEntry("test2.com,10.0.0.3,1000,ADDRESS")); client.Open(router, clientSettings); // Wait for everything to spin up. Thread.Sleep(3000); // Loop 1000 times or until we got responses for both entry IP addresses. Note // that there's a 1000:1 chance that this could be working properly and the test // still fail. found1 = false; found2 = false; for (int i = 0; i < 1000; i++) { response = DnsResolver.Query(IPAddress.Loopback, new DnsRequest(DnsFlag.NONE, "test1.com.", DnsQType.A), TimeSpan.FromSeconds(2)); Assert.AreEqual("test1.com.", response.QName); Assert.AreEqual(DnsQType.A, response.QType); Assert.AreEqual(1, response.Answers.Count); record = (A_RR)response.Answers[0]; Assert.AreNotEqual(IPAddress.Parse("10.0.0.3"), record.Address); if (IPAddress.Parse("10.0.0.1").Equals(record.Address)) { found1 = true; } else if (IPAddress.Parse("10.0.0.2").Equals(record.Address)) { found2 = true; } else { Assert.Fail(); } if (found1 && found2) { break; } } Assert.IsTrue(found1); Assert.IsTrue(found2); } finally { if (client != null) { client.Close(); } if (handler != null) { handler.Stop(); } } }
public void DynDnsHandler_Cluster_MX() { // Start a Dynamic DNS service handler and a client, register MX // records and confirm that MX queries return the proper results. DynDnsHandler handler = null; DynDnsClient client = null; DnsResponse response; MX_RR record; try { handler = new DynDnsHandler(); handler.Start(router, null, null, null); client = new DynDnsClient(); client.Register(new DynDnsHostEntry("test1.com,mail1.test1.com,1000,MX")); client.Register(new DynDnsHostEntry("test2.com,mail1.test2.com,2000,MX")); client.Register(new DynDnsHostEntry("test2.com,mail2.test2.com,3000,MX")); client.Open(router, new DynDnsClientSettings("DynDnsClient")); // Wait for all cluster members to come online and for // the host registrations to be replicated. WaitForOnline(handler.Cluster, waitTime); WaitForOnline(client.Cluster, waitTime); Thread.Sleep(2000); // Verify the single result for a MX query on test1.com response = DnsResolver.Query(IPAddress.Loopback, new DnsRequest(DnsFlag.NONE, "test1.com.", DnsQType.MX), TimeSpan.FromSeconds(2)); Assert.AreEqual("test1.com.", response.QName); Assert.AreEqual(DnsQType.MX, response.QType); Assert.AreEqual(1, response.Answers.Count); record = (MX_RR)response.Answers[0]; Assert.AreEqual("test1.com.", record.RName); Assert.AreEqual("mail1.test1.com.", record.Exchange); Assert.AreEqual(0, record.Preference); Assert.AreEqual(1000, record.TTL); Thread.Sleep(2000); // Verify the two results for a MX query on test2.com response = DnsResolver.Query(IPAddress.Loopback, new DnsRequest(DnsFlag.NONE, "test2.com.", DnsQType.MX), TimeSpan.FromSeconds(2)); Assert.AreEqual("test2.com.", response.QName); Assert.AreEqual(DnsQType.MX, response.QType); Assert.AreEqual(2, response.Answers.Count); record = (MX_RR)response.Answers.Single(r => ((MX_RR)r).Exchange == "mail1.test2.com."); Assert.AreEqual("test2.com.", record.RName); Assert.AreEqual("mail1.test2.com.", record.Exchange); Assert.AreEqual(0, record.Preference); Assert.AreEqual(2000, record.TTL); record = (MX_RR)response.Answers.Single(r => ((MX_RR)r).Exchange == "mail2.test2.com."); Assert.AreEqual("test2.com.", record.RName); Assert.AreEqual("mail2.test2.com.", record.Exchange); Assert.AreEqual(0, record.Preference); Assert.AreEqual(3000, record.TTL); } finally { if (client != null) { client.Close(); } if (handler != null) { handler.Stop(); } } }
public void DynDnsHandler_AddressCache() { // Verify that the server properly maintains a cache of specified hosts. DynDnsHandler handler = null; try { handler = new DynDnsHandler(); handler.Start(router, "LillTek.Datacenter.DynDNS.AddressCache", null, null); // Wait for everything to spin up. Thread.Sleep(10000); // Verify that we have cached addresses for www.lilltek.com and www.google.com. var addressCache = handler.GetCachedAddresses(); bool foundLillTek = false; bool foundGoogle = false; foreach (var entry in addressCache) { switch (entry.HostName.ToLower()) { case "www.lilltek.com.": foundLillTek = entry.Addresses != null && entry.Addresses.Length > 0; break; case "www.google.com.": foundGoogle = entry.Addresses != null && entry.Addresses.Length > 0; break; } } Assert.IsTrue(foundLillTek); Assert.IsTrue(foundGoogle); // Now verify that CNAME lookups that alias to these hosts include A records. DnsResponse response; bool hasARecords; // test1.lilltek.com is an alias for www.lilltek.com response = DnsResolver.Query(IPAddress.Loopback, new DnsRequest(DnsFlag.NONE, "test1.lilltek.com.", DnsQType.CNAME), TimeSpan.FromSeconds(2)); hasARecords = false; foreach (var record in response.Additional) { if (record.RRType == DnsRRType.A) { hasARecords = true; break; } } Assert.IsTrue(hasARecords); // test1.lilltek.com is an alias for www.google.com response = DnsResolver.Query(IPAddress.Loopback, new DnsRequest(DnsFlag.NONE, "test2.lilltek.com.", DnsQType.CNAME), TimeSpan.FromSeconds(2)); hasARecords = false; foreach (var record in response.Additional) { if (record.RRType == DnsRRType.A) { hasARecords = true; break; } } Assert.IsTrue(hasARecords); } finally { if (handler != null) { handler.Stop(); } } }
private void ProcessLocalConnection(object state) { var connection = (ConnectionInfo)state; var buffer = new byte[BufferSize]; int bytesRead; try { bytesRead = connection.LocalSocket.Receive(buffer); if (bytesRead < 1 || buffer[0] != Protocol.Socks4.Version) { connection.LocalSocket.Close(); return; } OnLogMessage?.Invoke(this, $"LocalSocket.Receive {bytesRead}"); } catch (SocketException ex) { OnLogMessage?.Invoke(this, $"Caught SocketException in ProcessLocalConnection with error code {ex.SocketErrorCode.ToString()}"); } try { switch (buffer[1]) { case Protocol.Socks4.CommandStreamConnection: { var portBuffer = new[] { buffer[2], buffer[3] }; var port = (ushort)(portBuffer[0] << 8 | portBuffer[1]); var ipBuffer = new[] { buffer[4], buffer[5], buffer[6], buffer[7] }; var ip = new IPAddress(ipBuffer); var destinationEndPoint = new IPEndPoint(ip, port); if (IsSocks4AProtocol(ipBuffer)) { var hostBuffer = new byte[256]; Buffer.BlockCopy(buffer, 9, hostBuffer, 0, 100); // Resolve hostname, fallback to remote proxy dns resolution var hostname = Encoding.ASCII.GetString(hostBuffer).TrimEnd((char)0); var destinationIp = ResolveHostnamesRemotely ? null : DnsResolver.TryResolve(hostname); connection.RemoteSocket = Socks5Client.Connect( RemotEndPoint.Address.ToString(), RemotEndPoint.Port, destinationIp == null ? hostname : destinationIp.ToString(), port, Username, Password, SendTimeout, ReceiveTimeout ); OnRemoteConnect?.Invoke(this, destinationEndPoint); } else { destinationEndPoint = new IPEndPoint(new IPAddress(ipBuffer), port); connection.RemoteSocket = Socks5Client.Connect( RemotEndPoint.Address.ToString(), RemotEndPoint.Port, destinationEndPoint.Address.ToString(), port, Username, Password, SendTimeout, ReceiveTimeout ); OnRemoteConnect?.Invoke(this, destinationEndPoint); } if (connection.RemoteSocket.Connected) { SendSocks4Reply(connection.LocalSocket, Protocol.Socks4.StatusRequestGranted, ipBuffer, portBuffer); // Create the thread for the receives. connection.RemoteThread = new Thread(ProcessRemoteConnection) { IsBackground = true }; connection.RemoteThread.Start(connection); } else { OnLogMessage?.Invoke(this, "RemoteSocket connection failed"); SendSocks4Reply(connection.LocalSocket, Protocol.Socks4.StatusRequestFailed, ipBuffer, portBuffer); connection.LocalSocket.Close(); } break; } case Protocol.Socks4.CommandBindingConnection: { var portBuffer = new[] { buffer[2], buffer[3] }; var ipBuffer = new[] { buffer[4], buffer[5], buffer[6], buffer[7] }; // TCP/IP port binding not supported SendSocks4Reply(connection.LocalSocket, Protocol.Socks4.StatusRequestFailed, ipBuffer, portBuffer); connection.LocalSocket.Close(); break; } default: OnLogMessage?.Invoke(this, "Unknown protocol on LocalSocket"); connection.LocalSocket.Close(); break; } // start receiving actual data if the socket still open while (true) { if (!connection.LocalSocket.Connected || !connection.RemoteSocket.Connected) { break; } bytesRead = connection.LocalSocket.Receive(buffer); if (bytesRead == 0) { break; } if (connection.RemoteSocket.Connected) { connection.RemoteSocket.Send(buffer, bytesRead, SocketFlags.None); OnLogMessage?.Invoke(this, $"Forwarded {bytesRead} bytes from LocalSocket to RemoteSocket"); } } } catch (SocketException ex) { OnLogMessage?.Invoke(this, $"Caught SocketException in ProcessLocalConnection with error code {ex.SocketErrorCode.ToString()}"); } catch (Socks5Exception ex) { var portBuffer = new[] { buffer[2], buffer[3] }; var ipBuffer = new[] { buffer[4], buffer[5], buffer[6], buffer[7] }; SendSocks4Reply(connection.LocalSocket, Protocol.Socks4.StatusRequestFailed, ipBuffer, portBuffer); connection.LocalSocket.Close(); OnLogMessage?.Invoke(this, $"Caught Socks5Exception in ProcessLocalConnection with message {ex.Message}"); } if (connection.LocalSocket.Connected) { OnLogMessage?.Invoke(this, "Closing LocalSocket"); connection.LocalSocket.Close(); } lock (_connections) { _connections.Remove(connection); } }
public void start() { foreach (string item in F_main.diclist) { ThreadPool.QueueUserWorkItem(_ => { if (F_main.isrun) { string openPort = ""; count += 1; F_main.FM.lbl_state.BeginInvoke(new Action(() => { F_main.FM.lbl_state.Text = "状态:枚举模式," + "已发现" + F_main.FM.LV_result.Items.Count + "个域名,进度" + count + "/" + F_main.diclist.Length + " 当前" + item + "." + ReqDns.Domain; })); DnsResolver Dns = new DnsResolver(item + "." + ReqDns.Domain, ReqDns.DnsServer, ReqDns.TimeOut); if (Dns.IsSuccess && F_main.blackIp.Contains(Dns.Record[0]) == false) { //避免接口查询的和枚举的重复 if (!F_main.DomainList.Contains(item + "." + ReqDns.Domain)) { F_main.DomainList.Add(item + "." + ReqDns.Domain); //加入到ListView ListViewItem list = new ListViewItem(item + "." + ReqDns.Domain); if (Dns.Record.Count > 1) { list.SubItems.Add(string.Join(",", Dns.Record.ToArray())); } else { list.SubItems.Add(string.Join(",", Dns.Record[0])); } if (F_main.isGetPorts) { //扫描开放端口 openPort = Tools.ScanPort(Dns.Record[0], ReqDns.Ports); } if (openPort != "") { list.SubItems.Add(openPort); } else { list.SubItems.Add("-"); } list.SubItems.Add("暴力枚举"); F_main.FM.LV_result.BeginInvoke(new Action(() => { F_main.FM.LV_result.Items.Add(list); })); } } } F_main.handler.Signal(); }); } }
public void Cleanup() { DnsResolver.Bind(); NetTrace.Stop(); }
public void DnsResolver_Multiple_Lookup_Bind() { // This test repeats the A_Multiple test, but this time // after binding the DnsResolver to 5 client side sockets. IPAddress dns = GetDns(); string[] gtld = new string[] { "a.gtld-servers.net.", "b.gtld-servers.net.", "c.gtld-servers.net.", "d.gtld-servers.net.", "e.gtld-servers.net.", "f.gtld-servers.net.", "g.gtld-servers.net.", "h.gtld-servers.net.", "i.gtld-servers.net.", "j.gtld-servers.net.", "k.gtld-servers.net.", "l.gtld-servers.net.", "m.gtld-servers.net.", }; DnsRequest[] requests = new DnsRequest[gtld.Length]; DnsResponse[] responses = new DnsResponse[gtld.Length]; IAsyncResult[] rgAR = new IAsyncResult[gtld.Length]; IPEndPoint[] clientEPs; clientEPs = new IPEndPoint[10]; for (int i = 0; i < clientEPs.Length; i++) { clientEPs[i] = new IPEndPoint(IPAddress.Any, 0); } DnsResolver.Bind(clientEPs, 128 * 1024, 128 * 1024); try { for (int j = 0; j < 10; j++) { // Repeat the test 10 times just for fun for (int i = 0; i < gtld.Length; i++) { requests[i] = new DnsRequest(DnsFlag.RD, gtld[i], DnsQType.A); } for (int i = 0; i < gtld.Length; i++) { rgAR[i] = DnsResolver.BeginQuery(dns, requests[i], timeout, null, null); } for (int i = 0; i < gtld.Length; i++) { responses[i] = DnsResolver.EndQuery(rgAR[i]); } for (int i = 0; i < gtld.Length; i++) { Assert.AreEqual(requests[i].QID, responses[i].QID); Assert.AreEqual(requests[i].QName, responses[i].QName); } } } finally { DnsResolver.Bind(); } }
/// <summary> /// Creates a new DnsResolver and tells it to start /// listening for traffic. /// </summary> /// <returns></returns> public async static Task CreateAndStartDnsResolver() { DnsResolver dnsResolver = new DnsResolver(); await dnsResolver.Listen(); }
/// <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)); }
/// <summary> /// Expands any macros in the domain specification. /// </summary> public override async Task Expand() { if (this.expanded) { return; } this.expanded = true; this.term.Reset(this.domain); StringBuilder sb = new StringBuilder(); char ch; while ((ch = this.term.PeekNextChar()) > ' ') { this.term.pos++; if (ch == '%') { switch (ch = this.term.PeekNextChar()) { case (char)0: sb.Append('%'); break; case '%': this.term.pos++; sb.Append('%'); break; case '_': this.term.pos++; sb.Append(' '); break; case '-': this.term.pos++; sb.Append("%20"); break; case '{': this.term.pos++; char MacroLetter = char.ToLower(this.term.NextChar()); int? Digit; bool Reverse; if (char.IsDigit(this.term.PeekNextChar())) { Digit = this.term.NextInteger(); if (Digit == 0) { throw new Exception("Invalid number of digits."); } } else { Digit = null; } if (char.ToLower(this.term.PeekNextChar()) == 'r') { this.term.pos++; Reverse = true; } else { Reverse = false; } int Start = this.term.pos; while ((ch = this.term.PeekNextChar()) == '.' || ch == '-' || ch == '+' || ch == ',' || ch == '/' || ch == '_' || ch == '=') { this.term.pos++; } string Delimiter = this.term.s.Substring(Start, this.term.pos - Start); ch = this.term.NextChar(); if (ch != '}') { throw new Exception("Expected }"); } string s; switch (MacroLetter) { case 's': // sender s = this.term.sender; break; case 'l': // local-part of sender s = this.term.sender; int i = s.IndexOf('@'); if (i < 0) { s = string.Empty; } else { s = s.Substring(0, i); } break; case 'o': // domain of sender s = this.term.sender; i = s.IndexOf('@'); if (i >= 0) { s = s.Substring(i + 1); } break; case 'd': // domain s = this.term.domain; break; case 'i': switch (this.term.ip.AddressFamily) { case AddressFamily.InterNetwork: s = this.term.ip.ToString(); break; case AddressFamily.InterNetworkV6: byte[] Bin = this.term.ip.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 (this.term.dnsLookupsLeft-- <= 0) { throw new Exception("DNS Lookup maximum reached."); } string[] DomainNames = await DnsResolver.LookupDomainName(this.term.ip); // First check if domain is found. s = null; foreach (string DomainName in DomainNames) { if (string.Compare(DomainName, this.term.domain, true) == 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("." + this.term.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 (this.term.ip.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 = this.term.helloDomain; break; case 'c': this.AssertExp(); s = this.term.ip.ToString(); break; case 'r': this.AssertExp(); s = this.term.hostDomain; break; case 't': this.AssertExp(); 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: this.term.pos++; sb.Append('%'); sb.Append(ch); break; } } else { sb.Append(ch); } } this.domain = sb.ToString(); }