public void Discover_AllServices() { var service = new ServiceProfile("x", "_sdtest-2._udp", 1024); var done = new ManualResetEvent(false); var mdns = new MulticastService(); var sd = new ServiceDiscovery(mdns); mdns.NetworkInterfaceDiscovered += (s, e) => sd.QueryAllServices(); sd.ServiceDiscovered += (s, serviceName) => { if (serviceName == service.QualifiedServiceName) { done.Set(); } }; try { sd.Advertise(service); mdns.Start(); Assert.IsTrue(done.WaitOne(TimeSpan.FromSeconds(1)), "DNS-SD query timeout"); } finally { sd.Dispose(); mdns.Stop(); } }
public void TTLs() { var service = new ServiceProfile("x", "_sdtest._udp", 1024); Assert.AreEqual(TimeSpan.FromMinutes(75), service.Resources.OfType <TXTRecord>().First().TTL); Assert.AreEqual(TimeSpan.FromSeconds(120), service.Resources.OfType <AddressRecord>().First().TTL); }
public void Discover_ServiceInstance() { var service = new ServiceProfile("y", "_sdtest-2._udp", 1024); var done = new ManualResetEvent(false); var mdns = new MulticastService(); var sd = new ServiceDiscovery(mdns); mdns.NetworkInterfaceDiscovered += (s, e) => { sd.QueryServiceInstances(service.ServiceName); }; sd.ServiceInstanceDiscovered += (s, e) => { if (e.ServiceInstanceName == service.FullyQualifiedName) { Assert.IsNotNull(e.Message); done.Set(); } }; try { sd.Advertise(service); mdns.Start(); Assert.IsTrue(done.WaitOne(TimeSpan.FromSeconds(1)), "instance not found"); } finally { sd.Dispose(); mdns.Stop(); } }
public void Announce_SentTwice() { var service = new ServiceProfile("z", "_sdtest-4._udp", 1024, new[] { IPAddress.Loopback }); var done = new ManualResetEvent(false); var nanswers = 0; var mdns = new MulticastService { IgnoreDuplicateMessages = false }; mdns.AnswerReceived += (s, e) => { var msg = e.Message; if (msg.Answers.OfType <PTRRecord>().Any(p => p.DomainName == service.FullyQualifiedName)) { if (++nanswers == 2) { done.Set(); } } }; try { using (var sd = new ServiceDiscovery(mdns)) { mdns.NetworkInterfaceDiscovered += (s, e) => sd.Announce(service); mdns.Start(); Assert.IsTrue(done.WaitOne(TimeSpan.FromSeconds(4)), "announce timeout"); } } finally { mdns.Stop(); } }
public void Advertises_ServiceInstance_Address() { var service = new ServiceProfile("x2", "_sdtest-1._udp", 1024, new[] { IPAddress.Loopback }); var done = new ManualResetEvent(false); var mdns = new MulticastService(); mdns.NetworkInterfaceDiscovered += (s, e) => mdns.SendQuery(service.HostName, DnsClass.IN, DnsType.A); mdns.AnswerReceived += (s, e) => { var msg = e.Message; if (msg.Answers.OfType <ARecord>().Any(p => p.Name == service.HostName)) { done.Set(); } }; try { using (var sd = new ServiceDiscovery(mdns)) { sd.Advertise(service); mdns.Start(); Assert.IsTrue(done.WaitOne(TimeSpan.FromSeconds(1)), "query timeout"); } } finally { mdns.Stop(); } }
public void Unadvertise() { var service = new ServiceProfile("z", "_sdtest-7._udp", 1024); var done = new ManualResetEvent(false); var mdns = new MulticastService(); var sd = new ServiceDiscovery(mdns); mdns.NetworkInterfaceDiscovered += (s, e) => sd.QueryAllServices(); sd.ServiceInstanceShutdown += (s, e) => { if (e.ServiceInstanceName == service.FullyQualifiedName) { done.Set(); } }; try { sd.Advertise(service); mdns.Start(); sd.Unadvertise(service); Assert.IsTrue(done.WaitOne(TimeSpan.FromSeconds(1)), "goodbye timeout"); } finally { sd.Dispose(); mdns.Stop(); } }
public void Announce_ContainsResourceRecords() { var service = new ServiceProfile("z", "_sdtest-4._udp", 1024, new[] { IPAddress.Loopback }); var done = new ManualResetEvent(false); var mdns = new MulticastService(); mdns.AnswerReceived += (s, e) => { var msg = e.Message; foreach (var r in service.Resources) { if (!msg.Answers.Contains(r)) { return; } } done.Set(); }; try { using (var sd = new ServiceDiscovery(mdns)) { mdns.NetworkInterfaceDiscovered += (s, e) => sd.Announce(service); mdns.Start(); Assert.IsTrue(done.WaitOne(TimeSpan.FromSeconds(3)), "announce timeout"); } } finally { mdns.Stop(); } }
public void QualifiedNames() { var service = new ServiceProfile("x", "_sdtest._udp", 1024, new [] { IPAddress.Loopback }); Assert.AreEqual("_sdtest._udp.local", service.QualifiedServiceName); Assert.AreEqual("x._sdtest._udp.local", service.FullyQualifiedName); }
public void ResourceRecords() { var service = new ServiceProfile("x", "_sdtest._udp", 1024, new[] { IPAddress.Loopback }); Assert.IsTrue(service.Resources.OfType <SRVRecord>().Any()); Assert.IsTrue(service.Resources.OfType <TXTRecord>().Any()); Assert.IsTrue(service.Resources.OfType <ARecord>().Any()); }
public void HostName() { var service = new ServiceProfile("fred", "_foo._tcp", 1024); Assert.AreEqual("fred.foo.local", service.HostName); service = new ServiceProfile("fred", "_foo_bar._tcp", 1024); Assert.AreEqual("fred.foo-bar.local", service.HostName); }
public void TXTRecords() { var service = new ServiceProfile("x", "_sdtest._udp", 1024); var txt = service.Resources.OfType <TXTRecord>().First(); txt.Strings.AddRange(new [] { "a=1", "b=2" }); CollectionAssert.Contains(txt.Strings, "txtvers=1"); CollectionAssert.Contains(txt.Strings, "a=1"); CollectionAssert.Contains(txt.Strings, "b=2"); }
/// <summary> /// Advertise a service profile. /// </summary> /// <param name="service"> /// The service profile. /// </param> /// <remarks> /// Any queries for the service or service instance will be answered with /// information from the profile. /// </remarks> public void Advertise(ServiceProfile service) { profiles.Add(service); var catalog = NameServer.Catalog; catalog.Add(service.instancePtrRecord, authoritative: true); catalog.Add(service.servicePtrRecord, authoritative: true); foreach (var r in service.Resources) { catalog.Add(r, authoritative: true); } }
public void AddProperty() { var service = new ServiceProfile { InstanceName = "x", ServiceName = "_sdtest._udp" }; service.AddProperty("a", "1"); var txt = service.Resources.OfType <TXTRecord>().First(); Assert.AreEqual(service.FullyQualifiedName, txt.Name); CollectionAssert.Contains(txt.Strings, "a=1"); }
public void Discover_ServiceInstance_WithAnswersContainingAdditionRecords() { var service = new ServiceProfile("y", "_sdtest-2._udp", 1024, new[] { IPAddress.Parse("127.1.1.1") }); var done = new ManualResetEvent(false); using (var mdns = new MulticastService()) using (var sd = new ServiceDiscovery(mdns) { AnswersContainsAdditionalRecords = true }) { Message discovered = null; mdns.NetworkInterfaceDiscovered += (s, e) => { sd.QueryServiceInstances(service.ServiceName); }; sd.ServiceInstanceDiscovered += (s, e) => { if (e.ServiceInstanceName == service.FullyQualifiedName) { Assert.IsNotNull(e.Message); discovered = e.Message; done.Set(); } }; sd.Advertise(service); mdns.Start(); Assert.IsTrue(done.WaitOne(TimeSpan.FromSeconds(3)), "instance not found"); var additionalRecordsCount = 1 + // SRVRecord 1 + // TXTRecord 1; // AddressRecord var answersCount = additionalRecordsCount + 1; // PTRRecord Assert.AreEqual(0, discovered.AdditionalRecords.Count); Assert.AreEqual(answersCount, discovered.Answers.Count); } }
public void ResourceRecords() { var profile = new ServiceProfile("me", "_myservice._udp", 1234, new IPAddress[] { IPAddress.Loopback }); profile.Subtypes.Add("apiv2"); profile.AddProperty("someprop", "somevalue"); using (var sd = new ServiceDiscovery()) { sd.Advertise(profile); var resourceRecords = sd.NameServer.Catalog.Values.SelectMany(node => node.Resources); foreach (var r in resourceRecords) { Console.WriteLine(r.ToString()); } } }
public void ReverseAddressMapping() { var service = new ServiceProfile("x9", "_sdtest-1._udp", 1024, new[] { IPAddress.Loopback, IPAddress.IPv6Loopback }); var arpaAddress = IPAddress.Loopback.GetArpaName(); var done = new ManualResetEvent(false); var mdns = new MulticastService(); Message response = null; mdns.NetworkInterfaceDiscovered += (s, e) => mdns.SendQuery(arpaAddress, DnsClass.IN, DnsType.PTR); mdns.AnswerReceived += (s, e) => { var msg = e.Message; if (msg.Answers.OfType <PTRRecord>().Any(p => p.Name == arpaAddress)) { response = msg; done.Set(); } }; try { using (var sd = new ServiceDiscovery(mdns)) { sd.Advertise(service); mdns.Start(); Assert.IsTrue(done.WaitOne(TimeSpan.FromSeconds(1)), "query timeout"); var answers = response.Answers .OfType <PTRRecord>() .Where(ptr => service.HostName == ptr.DomainName); foreach (var answer in answers) { Assert.AreEqual(arpaAddress, answer.Name); Assert.IsTrue(answer.TTL > TimeSpan.Zero); Assert.AreEqual(DnsClass.IN, answer.Class); } } } finally { mdns.Stop(); } }
/// <summary> /// Sends a goodbye message for the provided /// profile and removes its pointer from the name sever. /// </summary> /// <param name="profile">The profile to send a goodbye message for.</param> public void Unadvertise(ServiceProfile profile) { var message = new Message { QR = true }; var ptrRecord = new PTRRecord { Name = profile.QualifiedServiceName, DomainName = profile.FullyQualifiedName }; ptrRecord.TTL = TimeSpan.Zero; message.Answers.Add(ptrRecord); profile.Resources.ForEach((resource) => { resource.TTL = TimeSpan.Zero; message.AdditionalRecords.Add(resource); }); Mdns.SendAnswer(message); NameServer.Catalog.TryRemove(profile.QualifiedServiceName, out Node _); }
public void Discover_ServiceInstance_WithAnswersContainingAdditionRecords() { var service = new ServiceProfile("y", "_sdtest-2._udp", 1024); var done = new ManualResetEvent(false); var mdns = new MulticastService(); var sd = new ServiceDiscovery(mdns) { AnswersContainsAdditionalRecords = true }; Message discovered = null; mdns.NetworkInterfaceDiscovered += (s, e) => { sd.QueryServiceInstances(service.ServiceName); }; sd.ServiceInstanceDiscovered += (s, e) => { if (e.ServiceInstanceName == service.FullyQualifiedName) { Assert.IsNotNull(e.Message); discovered = e.Message; done.Set(); } }; try { sd.Advertise(service); mdns.Start(); Assert.IsTrue(done.WaitOne(TimeSpan.FromSeconds(1)), "instance not found"); Assert.AreEqual(0, discovered.AdditionalRecords.Count); Assert.IsTrue(discovered.Answers.Count > 1); } finally { sd.Dispose(); mdns.Stop(); } }
/// <summary> /// Advertise a service profile. /// </summary> /// <param name="service"> /// The service profile. /// </param> /// <remarks> /// Any queries for the service or service instance will be answered with /// information from the profile. /// </remarks> public void Advertise(ServiceProfile service) { profiles.Add(service); var catalog = NameServer.Catalog; catalog.Add( new PTRRecord { Name = ServiceName, DomainName = service.QualifiedServiceName }, authoritative: true); catalog.Add( new PTRRecord { Name = service.QualifiedServiceName, DomainName = service.FullyQualifiedName }, authoritative: true); foreach (var r in service.Resources) { catalog.Add(r, authoritative: true); } }
/// <summary> /// Sends an unsolicited MDNS response describing the /// service profile. /// </summary> /// <param name="profile"> /// The profile to describe. /// </param> /// <remarks> /// Sends a MDNS response <see cref="Message"/> containing the pointer /// and resource records of the <paramref name="profile"/>. /// <para> /// To provide increased robustness against packet loss, /// two unsolicited responses are sent one second apart. /// </para> /// </remarks> public void Announce(ServiceProfile profile) { var message = new Message { QR = true }; // Add the shared records. var ptrRecord = new PTRRecord { Name = profile.QualifiedServiceName, DomainName = profile.FullyQualifiedName }; message.Answers.Add(ptrRecord); // Add the resource records. profile.Resources.ForEach((resource) => { message.Answers.Add(resource); }); Mdns.SendAnswer(message, checkDuplicate: false); Task.Delay(1000).Wait(); Mdns.SendAnswer(message, checkDuplicate: false); }
/// <summary> /// Advertise a service profile. /// </summary> /// <param name="service"> /// The service profile. /// </param> /// <remarks> /// Any queries for the service or service instance will be answered with /// information from the profile. /// <para> /// Besides adding the profile's resource records to the <see cref="Catalog"/> PTR records are /// created to support DNS-SD and reverse address mapping (DNS address lookup). /// </para> /// </remarks> public void Advertise(ServiceProfile service) { profiles.Add(service); var catalog = NameServer.Catalog; catalog.Add( new PTRRecord { Name = ServiceName, DomainName = service.QualifiedServiceName }, authoritative: true); catalog.Add( new PTRRecord { Name = service.QualifiedServiceName, DomainName = service.FullyQualifiedName }, authoritative: true); foreach (var subtype in service.Subtypes) { var ptr = new PTRRecord { Name = DomainName.Join( new DomainName(subtype), SubName, service.QualifiedServiceName), DomainName = service.FullyQualifiedName }; catalog.Add(ptr, authoritative: true); } foreach (var r in service.Resources) { catalog.Add(r, authoritative: true); } catalog.IncludeReverseLookupRecords(); }
public void Subtypes() { var service = new ServiceProfile("x", "_sdtest._udp", 1024); Assert.AreEqual(0, service.Subtypes.Count); }
public void Defaults() { var service = new ServiceProfile(); Assert.IsNotNull(service.Resources); }
public void Addresses_Default() { var service = new ServiceProfile("x", "_sdtest._udp", 1024); Assert.IsTrue(service.Resources.Any(r => r.Type == DnsType.A || r.Type == DnsType.AAAA)); }
public void Addresses_IPv6() { var service = new ServiceProfile("x", "_sdtest._udp", 1024, new[] { IPAddress.IPv6Loopback }); Assert.IsTrue(service.Resources.Any(r => r.Type == DnsType.AAAA)); }