diff --git a/src/Mdns.csproj b/src/Mdns.csproj index b713e58..368893a 100644 --- a/src/Mdns.csproj +++ b/src/Mdns.csproj @@ -20,10 +20,9 @@ A simple Multicast Domain Name Service based on RFC 6762. Can be used as both a client (sending queries) or a server (responding to queries). false https://github.com/richardschneider/net-mdns/releases - © 2018 Richard Schneider + © 2018-2019 Richard Schneider multicast mdns dns zeroconf True - https://github.com/richardschneider/net-mdns/blob/master/LICENSE https://github.com/richardschneider/net-mdns @@ -34,7 +33,7 @@ - + diff --git a/src/MulticastService.cs b/src/MulticastService.cs index 2a9a348..287a137 100644 --- a/src/MulticastService.cs +++ b/src/MulticastService.cs @@ -364,7 +364,7 @@ void checkResponse(object s, MessageEventArgs e) /// /// When the service has not started. /// - public void SendQuery(string name, DnsClass klass = DnsClass.IN, DnsType type = DnsType.ANY) + public void SendQuery(DomainName name, DnsClass klass = DnsClass.IN, DnsType type = DnsType.ANY) { var msg = new Message { @@ -401,7 +401,7 @@ public void SendQuery(string name, DnsClass klass = DnsClass.IN, DnsType type = /// /// When the service has not started. /// - public void SendUnicastQuery(string name, DnsClass klass = DnsClass.IN, DnsType type = DnsType.ANY) + public void SendUnicastQuery(DomainName name, DnsClass klass = DnsClass.IN, DnsType type = DnsType.ANY) { var msg = new Message { diff --git a/src/ServiceDiscovery.cs b/src/ServiceDiscovery.cs index 1a88698..2ca89a0 100644 --- a/src/ServiceDiscovery.cs +++ b/src/ServiceDiscovery.cs @@ -14,6 +14,8 @@ namespace Makaretu.Dns 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"); /// /// The service discovery service name. @@ -21,7 +23,8 @@ public class ServiceDiscovery : IDisposable /// /// The service name used to enumerate other services. /// - public const string ServiceName = "_services._dns-sd._udp.local"; + public static readonly DomainName ServiceName = new DomainName("_services._dns-sd._udp.local"); + readonly bool ownsMdns; List profiles = new List(); @@ -101,7 +104,7 @@ public ServiceDiscovery(MulticastService mdns) /// Use to initiate a DNS-SD question. /// /// - public event EventHandler ServiceDiscovered; + public event EventHandler ServiceDiscovered; /// /// Raised when a servive instance is discovered. @@ -162,9 +165,9 @@ public void QueryUnicastAllServices() /// When an answer is received the event is raised. /// /// - public void QueryServiceInstances(string service) + public void QueryServiceInstances(DomainName service) { - Mdns.SendQuery(service + ".local", type: DnsType.PTR); + Mdns.SendQuery(DomainName.Join(service, LocalDomain), type: DnsType.PTR); } /// @@ -180,9 +183,14 @@ public void QueryServiceInstances(string service) /// When an answer is received the event is raised. /// /// - public void QueryServiceInstances(string service, string subtype) + public void QueryServiceInstances(DomainName service, string subtype) { - Mdns.SendQuery($"{subtype}._sub.{service}.local", type: DnsType.PTR); + var name = DomainName.Join( + new DomainName(subtype), + SubName, + service, + LocalDomain); + Mdns.SendQuery(name, type: DnsType.PTR); } /// @@ -196,9 +204,9 @@ public void QueryServiceInstances(string service, string subtype) /// When an answer is received the event is raised. /// /// - public void QueryUnicastServiceInstances(string service) + public void QueryUnicastServiceInstances(DomainName service) { - Mdns.SendUnicastQuery(service + ".local", type: DnsType.PTR); + Mdns.SendUnicastQuery(DomainName.Join(service, LocalDomain), type: DnsType.PTR); } /// @@ -231,7 +239,10 @@ public void Advertise(ServiceProfile service) { var ptr = new PTRRecord { - Name = $"{subtype}._sub.{service.QualifiedServiceName}", + Name = DomainName.Join( + new DomainName(subtype), + SubName, + service.QualifiedServiceName), DomainName = service.FullyQualifiedName }; catalog.Add(ptr, authoritative: true); @@ -289,9 +300,10 @@ void OnAnswer(object sender, MessageEventArgs e) } // Any DNS-SD answers? + var sd = msg.Answers .OfType() - .Where(ptr => ptr.Name.EndsWith(".local")); + .Where(ptr => ptr.Name.IsSubdomainOf(LocalDomain)); foreach (var ptr in sd) { if (ptr.Name == ServiceName) @@ -364,6 +376,11 @@ void OnQuery(object sender, MessageEventArgs e) response.AdditionalRecords.Clear(); } + if (!response.Answers.Any(a => a.Name == ServiceName)) + { + ; + } + if (QU) { // TODO: Send a Unicast response if required. diff --git a/src/ServiceInstanceDiscoveryEventArgs.cs b/src/ServiceInstanceDiscoveryEventArgs.cs index 0724fde..3d70251 100644 --- a/src/ServiceInstanceDiscoveryEventArgs.cs +++ b/src/ServiceInstanceDiscoveryEventArgs.cs @@ -17,7 +17,7 @@ public class ServiceInstanceDiscoveryEventArgs : MessageEventArgs /// Typically of the form "instance._service._tcp.local". /// /// - public string ServiceInstanceName { get; set; } + public DomainName ServiceInstanceName { get; set; } } } diff --git a/src/ServiceInstanceShutdownEventArgs.cs b/src/ServiceInstanceShutdownEventArgs.cs index 20e12c2..b116418 100644 --- a/src/ServiceInstanceShutdownEventArgs.cs +++ b/src/ServiceInstanceShutdownEventArgs.cs @@ -17,7 +17,7 @@ public class ServiceInstanceShutdownEventArgs : MessageEventArgs /// Typically of the form "instance._service._tcp.local". /// /// - public string ServiceInstanceName { get; set; } + public DomainName ServiceInstanceName { get; set; } } } diff --git a/src/ServiceProfile.cs b/src/ServiceProfile.cs index 27f1e2e..3fdf652 100644 --- a/src/ServiceProfile.cs +++ b/src/ServiceProfile.cs @@ -50,17 +50,18 @@ public ServiceProfile() /// /// The SRV, TXT and A/AAAA resoruce records are added to the . /// - public ServiceProfile(string instanceName, string serviceName, ushort port, IEnumerable addresses = null) + public ServiceProfile(DomainName instanceName, DomainName serviceName, ushort port, IEnumerable addresses = null) { InstanceName = instanceName; ServiceName = serviceName; var fqn = FullyQualifiedName; - var simpleServiceName = ServiceName + var simpleServiceName = new DomainName(ServiceName.ToString() .Replace("._tcp", "") .Replace("._udp", "") - .TrimStart('_'); - HostName = $"{InstanceName}.{simpleServiceName}.{Domain}"; + .Trim('_') + .Replace("_", "-")); + HostName = DomainName.Join(InstanceName, simpleServiceName, Domain); Resources.Add(new SRVRecord { Name = fqn, @@ -85,7 +86,7 @@ public ServiceProfile(string instanceName, string serviceName, ushort port, IEnu /// /// Always "local". /// - public string Domain { get; } = "local"; + public DomainName Domain { get; } = "local"; /// /// A unique name for the service. @@ -101,7 +102,7 @@ public ServiceProfile(string instanceName, string serviceName, ushort port, IEnu /// The second label is either "_tcp" (for application /// protocols that run over TCP) or "_udp" (for all others). /// - public string ServiceName { get; set; } + public DomainName ServiceName { get; set; } /// /// A unique identifier for the service instance. @@ -109,7 +110,7 @@ public ServiceProfile(string instanceName, string serviceName, ushort port, IEnu /// /// Some unique value. /// - public string InstanceName { get; set; } + public DomainName InstanceName { get; set; } /// /// The service name and domain. @@ -117,7 +118,7 @@ public ServiceProfile(string instanceName, string serviceName, ushort port, IEnu /// /// Typically of the form "_service._tcp.local". /// - public string QualifiedServiceName => $"{ServiceName}.{Domain}"; + public DomainName QualifiedServiceName => DomainName.Join(ServiceName, Domain); /// /// The fully qualified name of the instance's host. @@ -126,7 +127,7 @@ public ServiceProfile(string instanceName, string serviceName, ushort port, IEnu /// This can be used to query the address records (A and AAAA) /// of the service instance. /// - public string HostName { get; set; } + public DomainName HostName { get; set; } /// /// The instance name, service name and domain. @@ -134,7 +135,8 @@ public ServiceProfile(string instanceName, string serviceName, ushort port, IEnu /// /// .. /// - public string FullyQualifiedName => $"{InstanceName}.{QualifiedServiceName}"; + public DomainName FullyQualifiedName => + DomainName.Join(InstanceName, ServiceName, Domain); /// /// DNS resource records that are used to locate the service instance. diff --git a/test/MulticastServiceTest.cs b/test/MulticastServiceTest.cs index d136cfa..d868ab9 100644 --- a/test/MulticastServiceTest.cs +++ b/test/MulticastServiceTest.cs @@ -169,6 +169,7 @@ public async Task ReceiveLegacyUnicastAnswer() mdns.Start(); Assert.IsTrue(ready.WaitOne(TimeSpan.FromSeconds(1)), "ready timeout"); await client.SendAsync(packet, packet.Length, "224.0.0.251", 5353); + var r = await client.ReceiveAsync(); var response = new Message(); response.Read(r.Buffer, 0, r.Buffer.Length); diff --git a/test/ServiceProfileTest.cs b/test/ServiceProfileTest.cs index 4f0f95e..9a7f009 100644 --- a/test/ServiceProfileTest.cs +++ b/test/ServiceProfileTest.cs @@ -102,5 +102,15 @@ public void Subtypes() var service = new ServiceProfile("x", "_sdtest._udp", 1024); Assert.AreEqual(0, service.Subtypes.Count); } + + [TestMethod] + 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); + } } }