public void DynDnsHandler_Udp_ADDRESSLIST() { // Start a Dynamic DNS service handler and a client, register two ADDRESSLIST // records for TEST1.COM and confirm that A queries return both 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; 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,ADDRESSLIST")); client.Register(new DynDnsHostEntry("test1.com,10.0.0.2,1000,ADDRESSLIST")); client.Register(new DynDnsHostEntry("test2.com,10.0.0.3,1000,ADDRESSLIST")); client.Open(router, clientSettings); // Wait for everything to spin up. Thread.Sleep(3000); // Perform an A query and verify that we get both addresses in response. 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(2, response.Answers.Count); Assert.IsNotNull(response.Answers.SingleOrDefault(r => ((A_RR)r).Address.Equals(IPAddress.Parse("10.0.0.1")))); Assert.IsNotNull(response.Answers.SingleOrDefault(r => ((A_RR)r).Address.Equals(IPAddress.Parse("10.0.0.2")))); } finally { if (client != null) { client.Close(); } if (handler != null) { handler.Stop(); } } }
public void DynDnsHandler_Cluster_ADDRESSLIST() { // Start a Dynamic DNS service handler and a client, register two ADDRESSLIST // records for TEST1.COM and confirm that A queries return both entries. // I'm also going to add an ADDRESS record for TEST2.COM and verify that this address // is never returned. DynDnsHandler handler = null; DynDnsClient client = null; DnsResponse response; try { handler = new DynDnsHandler(); handler.Start(router, null, null, null); client = new DynDnsClient(); client.Register(new DynDnsHostEntry("test1.com,10.0.0.1,1000,ADDRESSLIST")); client.Register(new DynDnsHostEntry("test1.com,10.0.0.2,1000,ADDRESSLIST")); client.Register(new DynDnsHostEntry("test2.com,10.0.0.3,1000,ADDRESSLIST")); 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); // Perform an A query and verify that we get both addresses in response. 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(2, response.Answers.Count); Assert.IsNotNull(response.Answers.SingleOrDefault(r => ((A_RR)r).Address.Equals(IPAddress.Parse("10.0.0.1")))); Assert.IsNotNull(response.Answers.SingleOrDefault(r => ((A_RR)r).Address.Equals(IPAddress.Parse("10.0.0.2")))); } finally { if (client != null) { client.Close(); } if (handler != null) { handler.Stop(); } } }
/// <summary> /// Starts the service, associating it with an <see cref="IServiceHost" /> instance. /// </summary> /// <param name="serviceHost">The service user interface.</param> /// <param name="args">Command line arguments.</param> public void Start(IServiceHost serviceHost, string[] args) { lock (syncLock) { if (router != null) { return; // Already started } // Global initialization NetTrace.Start(); Program.Config = new Config("LillTek.Datacenter.DynDNSClient"); Program.InstallPerfCounters(); // Service initialization this.serviceHost = serviceHost; SysLog.LogInformation("Dynamic DNS Client v{0} Start", Helper.GetVersionString()); try { router = new LeafRouter(); router.Start(); client = new DynDnsClient(); client.Open(router, new DynDnsClientSettings("LillTek.Datacenter.DynDNSClient")); state = ServiceState.Running; } catch (Exception e) { if (client != null) { client.Close(); client = null; } if (router != null) { router.Stop(); router = null; } SysLog.LogException(e); throw; } } }
public void DynDnsService_EndToEnd() { LeafRouter router = null; Process svcProcess = null; Assembly assembly = typeof(LillTek.Datacenter.DynDnsService.Program).Assembly; DynDnsClient client = null; Helper.InitializeApp(assembly); try { Config.SetConfig(@" §ion MsgRouter AppName = LillTek.DynDNS Service AppDescription = Dynamic DNS RouterEP = physical://DETACHED/$(LillTek.DC.DefHubName)/$(Guid) CloudEP = $(LillTek.DC.CloudEP) CloudAdapter = ANY UdpEP = ANY:0 TcpEP = ANY:0 TcpBacklog = 100 TcpDelay = off BkInterval = 1s MaxIdle = 5m EnableP2P = yes AdvertiseTime = 1m DefMsgTTL = 5 SharedKey = PLAINTEXT SessionCacheTime = 2m SessionRetries = 3 SessionTimeout = 10s MaxLogicalAdvertiseEPs = 256 DeadRouterTTL = 2s // This maps the abstract Dynamic DNS endpoints to their default logical endpoints. AbstractMap[abstract://LillTek/DataCenter/DynDNS] = logical://LillTek/DataCenter/DynDNS &endsection §ion MsgRouter AppName = LillTek.DynDNS Service AppDescription = Dynamic DNS RouterEP = physical://DETACHED/$(LillTek.DC.DefHubName)/$(Guid) CloudEP = $(LillTek.DC.CloudEP) CloudAdapter = ANY UdpEP = ANY:0 TcpEP = ANY:0 TcpBacklog = 100 TcpDelay = off BkInterval = 1s MaxIdle = 5m EnableP2P = yes AdvertiseTime = 1m DefMsgTTL = 5 SharedKey = PLAINTEXT SessionCacheTime = 2m SessionRetries = 3 SessionTimeout = 10s MaxLogicalAdvertiseEPs = 256 DeadRouterTTL = 2s // This maps the abstract Dynamic DNS endpoints to their default logical endpoints. AbstractMap[abstract://LillTek/DataCenter/DynDNS] = logical://LillTek/DataCenter/DynDNS &endsection //----------------------------------------------------------------------------- // Dynamic DNS Service Settings §ion LillTek.Datacenter.DynDNS // Specifies the network binding the DNS server should listen on. NetworkBinding = ANY:DNS // Specifies the NetworkBinding the DNS server // should listen on to receive UDP host registration messages // from DynDnsClients. UdpBinding = ANY:DYNAMIC-DNS // Controls how the server is to be configured to obtain host // registrations from dynamic DNS clients. The possible values // are UDP, CLUSTER, or BOTH. Mode = UDP // Shared symmetric encryption key used to decrypt UDP registration messages // sent by DNS clients while in UDP or BOTH mode. This key must match the shared // key configured for the client. This defaults to the same reasonable default // used by the DNS client class. SharedKey = aes:BcskocQ2W4aIGEemkPsy5dhAxuWllweKLVToK1NoYzg=:5UUVxRPml8L4WH82unR74A== // The maximum delta to be allowed between the timestamp of messages received from // UDP broadcast clients and servers and the current system time. // // Messages transmitted between clients and servers in the UDP broadcast cluster are // timestamped with the time they were sent (UTC) to avoid replay attacks. This // setting controls which messages will be discarded for being having a timestamp // too far in the past or too far into the future. // // Ideally, this value would represent the maximum time a message could realistically // remain in transit on the network (a few seconds), but this setting also needs to // account for the possibility that the server system clocks may be out of sync. So, // this value is a tradeoff between security and reliability. MessageTTL = 15m // Specifies the time-to-live setting to use when replying to // DNS queries. This indicates how long the operating system // on the client side should cache the response. ResponseTTL = 5s // Indicates whether DNS host lookup failures should be logged // as warnings. LogFailures = yes §ion Cluster // The cluster's logical base endpoint. Instance endpoints will be constructed // by appending a GUID segment to this and the cluster broadcast endpoint // will be generated by appending '/*'. This setting is required. ClusterBaseEP = abstract://LillTek/DataCenter/DynDNS // Specifies the startup mode for the instance. This can be one of // the following values: // // NORMAL Indicates that the cluster member should go through the normal // master election cycle and eventually enter into the MASTER or SLAVE // state. // // OBSERVER Indicates that the cluster member should immediately enter the // OBSERVER state and remain there. Cluster observer state information // is replicated across the cluster so other instances know // about these instances but observers will never be elected // as the master. // // MONITOR Indicates that the cluster member should immediately enter the // MONITOR state and remain there. Monitors collect and maintain // cluster status but do not actively participate in the cluster. // No member status information about a monitor will be replicated // across the cluster. // // PREFERSLAVE Indicates that the cluster member prefers to be started as a // cluster slave // // PREFERMASTER Indicates that the cluster member prefers to be started as the // cluster master. If a master is already running and it does not // have this preference then a master election will be called. Mode = Normal // The interval at which the cluster master should broadcast cluster update // messages. Default is 1m. MasterBroadcastInterval = 10s // The interval at which slaves send their status updates to the master. // Default is 1m. SlaveUpdateInterval = 10s // The time period cluster members will wait while collecting member status // broadcasts from peers before concluding a master election. Default // is 5s. ElectionInterval = 10s // The number of times a cluster slave member should allow for missed // cluster status messages before calling for a master election. // Default is 3. MissingMasterCount = 2 // The number of times the cluster master should allow for missed // slave status transmissions before removing the slave from the cluster // state. Default is 3. MissingSlaveCount = 2 // The interval at which the cluster master instance should raise its // ClusterMember.MasterTask event so that derived classes can implement // custom background processing behavior. Default is 1s. MasterBkInterval = 1s // The interval at which the cluster slaves should raise their // ClusterMember.SlaveTask event so that derived classes // can implement custom background processing behavior. // Default is 1s. SlaveBkInterval = 1s // The background task polling interval. This value should be less than // or equal to the minimum of MasterBroadcastInterval, MasterBkInterval, // and SlaveBkInterval. Default is 1s. BkInterval = 1s &endsection &endsection ".Replace('&', '#')); router = new LeafRouter(); router.Start();; // Start the Dynamic DNS service svcProcess = Helper.StartProcess(assembly, "-mode:form -start"); Thread.Sleep(10000); // Give the process a chance to spin up // Open a dynamic DNS client and register a host and then // verify that it worked. DynDnsClientSettings clientSettings; clientSettings = new DynDnsClientSettings(); clientSettings.Mode = DynDnsMode.Udp; clientSettings.NameServers = new NetworkBinding[] { new NetworkBinding(NetHelper.GetActiveAdapter(), NetworkPort.DynamicDns) }; clientSettings.UdpRegisterInterval = TimeSpan.FromSeconds(1); client = new DynDnsClient(); client.Register(new DynDnsHostEntry("test.com", IPAddress.Parse("10.1.2.3"))); client.Register(new DynDnsHostEntry("www.lilltek.com", IPAddress.Parse("192.168.1.202"))); client.Open(router, clientSettings); Thread.Sleep(3000); Assert.AreEqual(IPAddress.Parse("10.1.2.3"), DnsLookup("test.com")); // Uncomment this for manual interop testing // Thread.Sleep(10000000); } finally { if (client != null) { client.Close(); } if (svcProcess != null) { svcProcess.Kill(); svcProcess.Close(); } if (router != null) { router.Stop(); } Config.SetConfig(null); } }
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_Cluster_Basic() { // Start a Dynamic DNS service handler and then two enabled dynamic DNS // clients and one disabled client. // // Register 2 hosts with different IP addresses on each client and // then query the DNS several times for each host to verify that // everything worked. // // Then stop one of the clients and verify that its host registrations // are no longer returned in DNS responses. DynDnsHandler handler = null; DynDnsClient client1 = null; DynDnsClient client2 = null; DynDnsClient client3 = null; try { handler = new DynDnsHandler(); handler.Start(router, null, null, null); client1 = new DynDnsClient(); client1.Register(new DynDnsHostEntry("test1.com", IPAddress.Parse("10.0.0.1"))); client1.Register(new DynDnsHostEntry("test2.com", IPAddress.Parse("10.0.0.2"))); client1.Open(router, new DynDnsClientSettings("DynDnsClient")); client2 = new DynDnsClient(); client2.Open(router, new DynDnsClientSettings("DynDnsClient")); client2.Register(new DynDnsHostEntry("test1.com", IPAddress.Parse("10.0.1.1"))); client2.Register(new DynDnsHostEntry("test2.com", IPAddress.Parse("10.0.1.2"))); client3 = new DynDnsClient(); client3.Open(router, new DynDnsClientSettings("DynDnsClient.Disabled")); client3.Register(new DynDnsHostEntry("test1.com", IPAddress.Parse("10.0.3.1"))); client3.Register(new DynDnsHostEntry("test2.com", IPAddress.Parse("10.0.3.2"))); // Wait for all cluster members to come online and for // the host registrations to be replicated. WaitForOnline(handler.Cluster, waitTime); WaitForOnline(client1.Cluster, waitTime); WaitForOnline(client2.Cluster, waitTime); Thread.Sleep(2000); // Perform the DNS lookups bool found; // Test1.com: 10.0.0.1 found = false; for (int i = 0; i < 100; i++) { if (IPAddress.Parse("10.0.0.1").Equals(DnsLookup("test1.com"))) { found = true; break; } } Assert.IsTrue(found); // Test1.com: 10.0.1.1 found = false; for (int i = 0; i < 100; i++) { if (IPAddress.Parse("10.0.1.1").Equals(DnsLookup("test1.com"))) { found = true; break; } } Assert.IsTrue(found); // Test2.com: 10.0.0.2 found = false; for (int i = 0; i < 100; i++) { if (IPAddress.Parse("10.0.0.2").Equals(DnsLookup("test2.com"))) { found = true; break; } } Assert.IsTrue(found); // Test2.com: 10.0.1.2 found = false; for (int i = 0; i < 100; i++) { if (IPAddress.Parse("10.0.1.2").Equals(DnsLookup("test2.com"))) { found = true; break; } } Assert.IsTrue(found); // Test case insensitivity found = false; for (int i = 0; i < 100; i++) { if (IPAddress.Parse("10.0.1.2").Equals(DnsLookup("TEST2.COM"))) { found = true; break; } } Assert.IsTrue(found); // Test for name not found. try { Assert.IsNull(DnsLookup("error.com")); Assert.Fail("Expected a DnsException"); } catch (Exception e) { Assert.IsInstanceOfType(e, typeof(DnsException)); } // Make sure that we don't see hosts from client3 since it // is disabled. // Test1.com: 10.0.3.1 found = false; for (int i = 0; i < 100; i++) { if (IPAddress.Parse("10.0.3.1").Equals(DnsLookup("test1.com"))) { found = true; break; } } Assert.IsFalse(found); // Close client1 and verify that its registrations are no // longer returned but that client2's still are. client1.Close(); client1 = null; Thread.Sleep(2000); // Test1.com: 10.0.0.1 found = false; for (int i = 0; i < 100; i++) { if (IPAddress.Parse("10.0.0.1").Equals(DnsLookup("test1.com"))) { found = true; break; } } Assert.IsFalse(found); // Test1.com: 10.0.1.1 found = false; for (int i = 0; i < 100; i++) { if (IPAddress.Parse("10.0.1.1").Equals(DnsLookup("test1.com"))) { found = true; break; } } Assert.IsTrue(found); // Test2.com: 10.0.0.2 found = false; for (int i = 0; i < 100; i++) { if (IPAddress.Parse("10.0.0.2").Equals(DnsLookup("test2.com"))) { found = true; break; } } Assert.IsFalse(found); // Test2.com: 10.0.1.2 found = false; for (int i = 0; i < 100; i++) { if (IPAddress.Parse("10.0.1.2").Equals(DnsLookup("test2.com"))) { found = true; break; } } Assert.IsTrue(found); } finally { if (client1 != null) { client1.Close(); } if (client2 != null) { client2.Close(); } if (handler != null) { handler.Stop(); } } }