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();
            }
        }
Example #2
0
        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();
            }
        }
Example #4
0
        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();
            }
        }
Example #6
0
        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();
            }
        }
Example #7
0
        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();
            }
        }
Example #8
0
        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);
        }
Example #9
0
        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());
        }
Example #10
0
        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);
        }
Example #11
0
        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");
        }
Example #12
0
        /// <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);
            }
        }
Example #13
0
        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");
        }
Example #14
0
        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);
                }
        }
Example #15
0
        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());
                }
            }
        }
Example #16
0
        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();
            }
        }
Example #17
0
        /// <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 _);
        }
Example #18
0
        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();
            }
        }
Example #19
0
        /// <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);
            }
        }
Example #20
0
        /// <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);
        }
Example #21
0
        /// <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();
        }
Example #22
0
        public void Subtypes()
        {
            var service = new ServiceProfile("x", "_sdtest._udp", 1024);

            Assert.AreEqual(0, service.Subtypes.Count);
        }
Example #23
0
        public void Defaults()
        {
            var service = new ServiceProfile();

            Assert.IsNotNull(service.Resources);
        }
Example #24
0
        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));
        }
Example #25
0
        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));
        }