Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: process questions requesting unicast responses #50 #51

Merged
merged 1 commit into from
Mar 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions src/MulticastService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,43 @@ public void SendQuery(string name, DnsClass klass = DnsClass.IN, DnsType type =
SendQuery(msg);
}

/// <summary>
/// Ask for answers about a name and accept unicast and/or broadcast response.
/// </summary>
/// <param name="name">
/// A domain name that should end with ".local", e.g. "myservice.local".
/// </param>
/// <param name="klass">
/// The class, defaults to <see cref="DnsClass.IN"/>.
/// </param>
/// <param name="type">
/// The question type, defaults to <see cref="DnsType.ANY"/>.
/// </param>
/// <remarks>
/// Send a "QU" question (unicast). The most significat bit of the Class is set.
/// Answers to any query are obtained on the <see cref="AnswerReceived"/>
/// event.
/// </remarks>
/// <exception cref="InvalidOperationException">
/// When the service has not started.
/// </exception>
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);
}

/// <summary>
/// Ask for answers.
/// </summary>
Expand Down
52 changes: 50 additions & 2 deletions src/ServiceDiscovery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,18 @@ public void QueryAllServices()
Mdns.SendQuery(ServiceName, type: DnsType.PTR);
}

/// <summary>
/// Asks other MDNS services to send their service names;
/// accepts unicast and/or broadcast answers.
/// </summary>
/// <remarks>
/// When an answer is received the <see cref="ServiceDiscovered"/> event is raised.
/// </remarks>
public void QueryUnicastAllServices()
{
Mdns.SendUnicastQuery(ServiceName, type: DnsType.PTR);
}

/// <summary>
/// Asks instances of the specified service to send details.
/// </summary>
Expand All @@ -142,6 +154,22 @@ public void QueryServiceInstances(string service)
Mdns.SendQuery(service + ".local", type: DnsType.PTR);
}

/// <summary>
/// Asks instances of the specified service to send details.
/// accepts unicast and/or broadcast answers.
/// </summary>
/// <param name="service">
/// The service name to query. Typically of the form "_<i>service</i>._tcp".
/// </param>
/// <remarks>
/// When an answer is received the <see cref="ServiceInstanceDiscovered"/> event is raised.
/// </remarks>
/// <seealso cref="ServiceProfile.ServiceName"/>
public void QueryUnicastServiceInstances(string service)
{
Mdns.SendUnicastQuery(service + ".local", type: DnsType.PTR);
}

/// <summary>
/// Advertise a service profile.
/// </summary>
Expand Down Expand Up @@ -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;
Expand All @@ -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)
{
Expand Down
29 changes: 29 additions & 0 deletions test/MulticastServiceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down
63 changes: 63 additions & 0 deletions test/ServiceDiscoveryTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down Expand Up @@ -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()
{
Expand Down