diff --git a/src/MulticastService.cs b/src/MulticastService.cs index ce44a14..c3cd825 100644 --- a/src/MulticastService.cs +++ b/src/MulticastService.cs @@ -584,7 +584,7 @@ public void SendAnswer(Message answer, MessageEventArgs query, bool checkDuplica Send(answer, checkDuplicate, query.RemoteEndPoint); } - void Send(Message msg, bool checkDuplicate, IPEndPoint remoteEndPoint = null) + internal void Send(Message msg, bool checkDuplicate, IPEndPoint remoteEndPoint = null) { var packet = msg.ToByteArray(); if (packet.Length > maxPacketSize) diff --git a/src/ServiceDiscovery.cs b/src/ServiceDiscovery.cs index f2b3175..f270071 100644 --- a/src/ServiceDiscovery.cs +++ b/src/ServiceDiscovery.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Security.Claims; using System.Threading.Tasks; +using System.Xml.Linq; using Common.Logging; using Makaretu.Dns.Resolving; @@ -17,6 +19,7 @@ public class ServiceDiscovery : IDisposable static readonly ILog log = LogManager.GetLogger(typeof(ServiceDiscovery)); static readonly DomainName LocalDomain = new DomainName("local"); static readonly DomainName SubName = new DomainName("_sub"); + static readonly ushort transaction = (ushort)new Random().Next(10000, int.MaxValue); /// /// The service discovery service name. @@ -28,6 +31,7 @@ public class ServiceDiscovery : IDisposable readonly bool ownsMdns; List profiles = new List(); + bool conflict; /// /// Creates a new instance of the class. @@ -257,6 +261,40 @@ public void Advertise(ServiceProfile service) catalog.IncludeReverseLookupRecords(); } + /// + /// Probe the network to ensure the service is unique. + /// + /// + /// True if thise service conflicts with an existing network service + public bool Probe(ServiceProfile profile) + { + conflict = false; + Message msg = new Message + { + Opcode = MessageOperation.Query, + QR = false, + Id = transaction + }; + msg.Questions.Add(new Question + { + Name = profile.HostName, + Class = DnsClass.IN, + Type = DnsType.ANY + }); + + Task.Delay(new Random().Next(0, 250)).Wait(); + Mdns.Send(msg, false); + + Task.Delay(250).Wait(); + Mdns.Send(msg, false); + + Task.Delay(250).Wait(); + Mdns.Send(msg, false); + + Task.Delay(250).Wait(); + return conflict; + } + /// /// Sends an unsolicited MDNS response describing the /// service profile. @@ -335,6 +373,11 @@ void OnAnswer(object sender, MessageEventArgs e) } // Any DNS-SD answers? + if (msg.Id == transaction) + { + if (msg.Answers.Count > 0) + conflict = true; + } var sd = msg.Answers .OfType() diff --git a/test/ServiceDiscoveryTest.cs b/test/ServiceDiscoveryTest.cs index 30c8571..9f29842 100644 --- a/test/ServiceDiscoveryTest.cs +++ b/test/ServiceDiscoveryTest.cs @@ -473,7 +473,11 @@ public void Announce_ContainsSharedRecords() { using (var sd = new ServiceDiscovery(mdns)) { - mdns.NetworkInterfaceDiscovered += (s, e) => sd.Announce(service); + mdns.NetworkInterfaceDiscovered += (s, e) => + { + Assert.IsFalse(sd.Probe(service)); + sd.Announce(service); + }; mdns.Start(); Assert.IsTrue(done.WaitOne(TimeSpan.FromSeconds(3)), "announce timeout"); } @@ -507,7 +511,12 @@ public void Announce_ContainsResourceRecords() { using (var sd = new ServiceDiscovery(mdns)) { - mdns.NetworkInterfaceDiscovered += (s, e) => sd.Announce(service); + mdns.NetworkInterfaceDiscovered += + (s, e) => + { + Assert.IsFalse(sd.Probe(service)); + sd.Announce(service); + }; mdns.Start(); Assert.IsTrue(done.WaitOne(TimeSpan.FromSeconds(3)), "announce timeout"); } @@ -543,7 +552,11 @@ public void Announce_SentTwice() { using (var sd = new ServiceDiscovery(mdns)) { - mdns.NetworkInterfaceDiscovered += (s, e) => sd.Announce(service); + mdns.NetworkInterfaceDiscovered += (s, e) => + { + Assert.IsFalse(sd.Probe(service)); + sd.Announce(service); + }; mdns.Start(); Assert.IsTrue(done.WaitOne(TimeSpan.FromSeconds(4)), "announce timeout"); } diff --git a/test/ServiceProfileTest.cs b/test/ServiceProfileTest.cs index 9a7f009..5f17ade 100644 --- a/test/ServiceProfileTest.cs +++ b/test/ServiceProfileTest.cs @@ -1,17 +1,11 @@ -using Makaretu.Dns; -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using System; -using System.Collections.Generic; using System.Linq; using System.Net; -using System.Net.NetworkInformation; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; namespace Makaretu.Dns { - + [TestClass] public class ServiveProfileTest {