diff --git a/src/MulticastService.cs b/src/MulticastService.cs
index b01917a..7b10ecf 100644
--- a/src/MulticastService.cs
+++ b/src/MulticastService.cs
@@ -371,6 +371,43 @@ public void SendQuery(string name, DnsClass klass = DnsClass.IN, DnsType type =
SendQuery(msg);
}
+ ///
+ /// Ask for answers about a name and accept unicast and/or broadcast response.
+ ///
+ ///
+ /// A domain name that should end with ".local", e.g. "myservice.local".
+ ///
+ ///
+ /// The class, defaults to .
+ ///
+ ///
+ /// The question type, defaults to .
+ ///
+ ///
+ /// Send a "QU" question (unicast). The most significat bit of the Class is set.
+ /// Answers to any query are obtained on the
+ /// event.
+ ///
+ ///
+ /// When the service has not started.
+ ///
+ public void SendUnicastQuery(string name, DnsClass klass = DnsClass.IN, DnsType type = DnsType.ANY)
+ {
+ var msg = new Message
+ {
+ Opcode = MessageOperation.Query,
+ QR = false
+ };
+ msg.Questions.Add(new Question
+ {
+ Name = name,
+ Class = (DnsClass) ((ushort)klass | 0x8000),
+ Type = type
+ });
+
+ SendQuery(msg);
+ }
+
///
/// Ask for answers.
///
diff --git a/src/ServiceDiscovery.cs b/src/ServiceDiscovery.cs
index 8938131..307d3ff 100644
--- a/src/ServiceDiscovery.cs
+++ b/src/ServiceDiscovery.cs
@@ -127,6 +127,18 @@ public void QueryAllServices()
Mdns.SendQuery(ServiceName, type: DnsType.PTR);
}
+ ///
+ /// Asks other MDNS services to send their service names;
+ /// accepts unicast and/or broadcast answers.
+ ///
+ ///
+ /// When an answer is received the event is raised.
+ ///
+ public void QueryUnicastAllServices()
+ {
+ Mdns.SendUnicastQuery(ServiceName, type: DnsType.PTR);
+ }
+
///
/// Asks instances of the specified service to send details.
///
@@ -142,6 +154,22 @@ public void QueryServiceInstances(string service)
Mdns.SendQuery(service + ".local", type: DnsType.PTR);
}
+ ///
+ /// Asks instances of the specified service to send details.
+ /// accepts unicast and/or broadcast answers.
+ ///
+ ///
+ /// The service name to query. Typically of the form "_service._tcp".
+ ///
+ ///
+ /// When an answer is received the event is raised.
+ ///
+ ///
+ public void QueryUnicastServiceInstances(string service)
+ {
+ Mdns.SendUnicastQuery(service + ".local", type: DnsType.PTR);
+ }
+
///
/// Advertise a service profile.
///
@@ -200,7 +228,19 @@ void OnQuery(object sender, MessageEventArgs e)
if (log.IsDebugEnabled)
{
- log.Debug($"got query from: {e.RemoteEndPoint.Address}, for {request.Questions[0].Name} {request.Questions[0].Type}");
+ log.Debug($"got query from: {e.RemoteEndPoint}, for {request.Questions[0].Name} {request.Questions[0].Type}");
+ }
+
+ // Determine if this query is requesting a unicast response
+ // and normalise the Class.
+ var QU = false; // unicast query response?
+ foreach (var r in request.Questions)
+ {
+ if (((ushort)r.Class & 0x8000) != 0)
+ {
+ QU = true;
+ r.Class = (DnsClass)((ushort)r.Class & 0x7fff);
+ }
}
var response = NameServer.ResolveAsync(request).Result;
@@ -222,7 +262,15 @@ void OnQuery(object sender, MessageEventArgs e)
response.Answers.AddRange(response.AdditionalRecords);
}
- Mdns.SendAnswer(response);
+ if (QU)
+ {
+ // TODO: Send a Unicast response if required.
+ Mdns.SendAnswer(response);
+ }
+ else
+ {
+ Mdns.SendAnswer(response);
+ }
if (log.IsDebugEnabled)
{
diff --git a/test/MulticastServiceTest.cs b/test/MulticastServiceTest.cs
index 27df7f5..83aad39 100644
--- a/test/MulticastServiceTest.cs
+++ b/test/MulticastServiceTest.cs
@@ -58,6 +58,35 @@ public void SendQuery()
}
}
+ [TestMethod]
+ public void SendUnicastQuery()
+ {
+ var ready = new ManualResetEvent(false);
+ var done = new ManualResetEvent(false);
+ Message msg = null;
+
+ var mdns = new MulticastService();
+ mdns.NetworkInterfaceDiscovered += (s, e) => ready.Set();
+ mdns.QueryReceived += (s, e) =>
+ {
+ msg = e.Message;
+ done.Set();
+ };
+ try
+ {
+ mdns.Start();
+ Assert.IsTrue(ready.WaitOne(TimeSpan.FromSeconds(1)), "ready timeout");
+ mdns.SendUnicastQuery("some-service.local");
+ Assert.IsTrue(done.WaitOne(TimeSpan.FromSeconds(1)), "query timeout");
+ Assert.AreEqual("some-service.local", msg.Questions.First().Name);
+ Assert.AreEqual(DnsClass.IN + 0x8000, msg.Questions.First().Class);
+ }
+ finally
+ {
+ mdns.Stop();
+ }
+ }
+
[TestMethod]
public void ReceiveAnswer()
{
diff --git a/test/ServiceDiscoveryTest.cs b/test/ServiceDiscoveryTest.cs
index ca29166..e677b9a 100644
--- a/test/ServiceDiscoveryTest.cs
+++ b/test/ServiceDiscoveryTest.cs
@@ -150,6 +150,35 @@ public void Discover_AllServices()
}
}
+ [TestMethod]
+ public void Discover_AllServices_Unicast()
+ {
+ var service = new ServiceProfile("x", "_sdtest-5._udp", 1024);
+ var done = new ManualResetEvent(false);
+ var mdns = new MulticastService();
+ var sd = new ServiceDiscovery(mdns);
+
+ mdns.NetworkInterfaceDiscovered += (s, e) => sd.QueryUnicastAllServices();
+ 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();
+ }
+ }
+
[TestMethod]
public void Discover_ServiceInstance()
{
@@ -184,6 +213,40 @@ public void Discover_ServiceInstance()
}
}
+ [TestMethod]
+ public void Discover_ServiceInstance_Unicast()
+ {
+ var service = new ServiceProfile("y", "_sdtest-5._udp", 1024);
+ var done = new ManualResetEvent(false);
+ var mdns = new MulticastService();
+ var sd = new ServiceDiscovery(mdns);
+
+ mdns.NetworkInterfaceDiscovered += (s, e) =>
+ {
+ sd.QueryUnicastServiceInstances(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();
+ }
+ }
+
[TestMethod]
public void Discover_ServiceInstance_WithAnswersContainingAdditionRecords()
{