ANN: net-mdns, multicast DNS and service discovery (aka "Rendezvous")

Hi,

net-mdns is an extension to the standard 'resolv' resolver library that
adds support for multicast DNS. mDNS is an extension of hierarchical,
unicast DNS to link-local unicast. It is most widely known because it is
part of Apple's OS X "Rendezvous" system, where it is used to do service
discovery over local networks.

I've been planning to do this awhile, but a post by Ben Giddings made me
think that I should do it sooner rather than later. Thanks!

It's beta, but it works (for me and my Alpha tester, Ben, anyhow).

RAA - http://raa.ruby-lang.org/project/net-mdns/

The rdoc overview is below, to get an idea what its about.

Cheers,
Sam

main:Resolv::MDNS
title:mDNS - multicast DNS and service discovery (aka "Rendezvous")

Author:: Sam Roberts <sroberts@uniserve.com>
Copyright:: Copyright (C) 2005 Sam Roberts
License:: May be distributed under the same terms as Ruby
Version:: 0.0
Homepage:: http://vpim.rubyforge.org/mdns
Download:: http://vpim.rubyforge.org/mdns/mdns.tgz

== Summary
An extension to the standard 'resolv' resolver library that adds support
for multicast DNS. mDNS is an extension of hierarchical, unicast DNS to
link-local multicast. It is most widely known because it is part of Apple's
OS X "Rendezvous" system, where it is used to do service discovery over
local networks.

MDNS can be used for:
- name to address lookups on local networks
- address to name lookups on local networks (only for link-local addresses)
- discovery of services on local networks

= Example
  require 'net/http'
  require 'net/dns/mdns-resolv'
  require 'resolv-replace'
  
  # Address lookup
  
  begin
  puts Resolv.getaddress('example.local')
  rescue Resolv::ResolvError
    puts "no such address!"
  end
  
  # Service discovery
  
  mdns = Resolv::MDNS.new
  
  mdns.each_resource('_http._tcp.local', Resolv::DNS::Resource::IN::PTR) do |rrhttp|
    service = rrhttp.name
    host = nil
    port = nil
    path = '/'
  
    rrsrv = mdns.getresource(rrhttp.name, Resolv::DNS::Resource::IN::SRV)
    host, port = rrsrv.target.to_s, rrsrv.port
    rrtxt = mdns.getresource(rrhttp.name, Resolv::DNS::Resource::IN::TXT)
    if rrtxt.data =~ /path=(.*)/
      path = $1
    end
  
    http = Net::HTTP.new(host, port)
  
    headers = http.head(path)
  
    puts "#{service[0]} on #{host}:#{port}#{path} was last-modified #{headers['last-modified']}"
  end
  
== Address Lookups
When used for name lookups, it is most useful to add MDNS to the default
set of resolvers queried when using the 'resolv' module methods. This is
done by doing:
  require 'net/dns/mdns-resolv'
  Resolv.getaddress('localhost') # resolved using Resolv::Hosts("/etc/hosts")
  Resolv.getaddress('www.example.com') # resolved using Resolv::DNS
  Resolv.getaddress('example.local') # resolved using Resolv::MDNS
Using this approach means that both global DNS names and local names can be
resolved. When doing this, you may also consider doing:
  require 'resolv-replace'
This has the effect of replacing the default ruby implementation of address
lookup in IPSocket, TCPSocket, UDPSocket, and SOCKSocket with
Resolv.getaddress, so (if 'net/dns/mdns-resolv' has been required) the
standard libraries TCP/IP classes will use mDNS for name lookups in the .local domain.

== Service Discovery

Service discovery consists of 2 stages:
- enumerating the names of the instances of the service
- resolving the instance names

= Service Enumeration

To do this query the pointer records (Resolv::DNS::Resource::IN::PTR) for
names of the form _svc._prot.local. The values of svc and prot for common
services can be found at http://www.dns-sd.org/ServiceTypes.html.
The first label of the name returned is suitable for display to peoplem, and
should be unique in the network.

= Service Resolution

In order to resolve a service name query the service record
(Resolv::DNS::Resource::IN::SRV) for the name. The service record contains
a host and port to connect to. The host name will have to be resolved to
an address. This can be done explicitly using mDNS or, if resolv-replace
and mdns-default have been required, it will be done by the standard library.
In addition, some services put "extra" information about the service in a
text (Resolv::DNS::Resource::IN::TXT) record associated with the service name.
The format of the text record is service-specific.

== For More Information

See the following:
- draft-cheshire-dnsext-multicastdns-04.txt for a description of mDNS
- RFC 2782 for a description of DNS SRV records
- draft-cheshire-dnsext-dns-sd-02.txt for a description of how to
  use SRV, PTR, and TXT records for service discovery
- http://www.dns-sd.org

== Comparison to the DNS-SD Extension

The DNS-SD project at http://dnssd.rubyforge.org/wiki/wiki.pl is another
approach to mDNS and service discovery.

DNS-SD is a compiled ruby extension implemented on top of the dns_sd.h APIs
published by Apple. These APIs work by contacting a local mDNS daemon
(through unix domain sockets) and should be more efficient (they can
take advantage of the daemon's cache) and likely a better way of doing
mDNS queries than using pure ruby. Also, the mDNS daemon is capable of
advertising services over the network, and Resolv::MDNS can't do that,
though I'm working on it. There's some technical obstacles.

Currently, the only thing I'm aware of Resolv::MDNS doing that DNS-SD
doesn't is integrate into the standard library so that link-local domain
names can be used throughout the standard networking classes. There is no
reason DNS-SD can't do this, and I'll try and add that capability as soon as I
find a way to install and use DNS-SD, which leads to why you might be
interested in Resolv::MDNS.

The DNS-SD extension requires the dns_sd.h C language APIs for the Apple
mDNS daemon. Installing the Apple responder can be quite difficult, and
requires a running daemon. It also requires compiling the extension. If
you need a pure ruby implementation, or if building DNS-SD turns out to be
difficult for you, Resolv::MDNS may be useful to you.

== Samples

There are a few samples in the samples/ directory:
- mdns.rb is useful for finding out as much as possible about services on .local
- mdns_demo.rb is a sample provided by Ben Giddings, with better docs, showing
  the call sequence he uses when resolving services. Thanks, Ben!

== TODO

- Implement a Resolv:: object that uses the DNS-SD project, so the standard
  library will use it for .local name lookups.
- Implement response cacheing and service advertising in Resolv::MDNS.
- Implement a higher level service discovery API that will work with either
  DNS-SD or Resolv::MDNS, so either can be used (as available) without code
  changes.
- Various API improvements, testing, ...

== Author

Any feedback, questions, problems, etc., please contact me, Sam Roberts,
via dnssd-developers@rubyforge.org, or directly.

Hi Sam,

Thanks for putting this up!

Just to let you folks know, this may be alpha software, and may still have bugs, but it works for what I need, and works well. You may be interested to know that it is in much better shape than Python's zeroconf implementation.

Ben

Rendezvous is a ( good ) Tibco messaging product, don't get their
lawyers fired up again :wink:

···

On Thu, 3 Feb 2005 13:00:24 +0900, Sam Roberts <sroberts@uniserve.com> wrote:

Hi,

net-mdns is an extension to the standard 'resolv' resolver library that
adds support for multicast DNS. mDNS is an extension of hierarchical,
unicast DNS to link-local unicast. It is most widely known because it is
part of Apple's OS X "Rendezvous" system, where it is used to do service
discovery over local networks.

I've been planning to do this awhile, but a post by Ben Giddings made me
think that I should do it sooner rather than later. Thanks!

It's beta, but it works (for me and my Alpha tester, Ben, anyhow).

RAA - http://raa.ruby-lang.org/project/net-mdns/

The rdoc overview is below, to get an idea what its about.

Cheers,
Sam

main:Resolv::MDNS
title:mDNS - multicast DNS and service discovery (aka "Rendezvous")

Author:: Sam Roberts <sroberts@uniserve.com>
Copyright:: Copyright (C) 2005 Sam Roberts
License:: May be distributed under the same terms as Ruby
Version:: 0.0
Homepage:: http://vpim.rubyforge.org/mdns
Download:: http://vpim.rubyforge.org/mdns/mdns.tgz

== Summary
An extension to the standard 'resolv' resolver library that adds support
for multicast DNS. mDNS is an extension of hierarchical, unicast DNS to
link-local multicast. It is most widely known because it is part of Apple's
OS X "Rendezvous" system, where it is used to do service discovery over
local networks.

MDNS can be used for:
- name to address lookups on local networks
- address to name lookups on local networks (only for link-local addresses)
- discovery of services on local networks

= Example
  require 'net/http'
  require 'net/dns/mdns-resolv'
  require 'resolv-replace'

  # Address lookup

  begin
  puts Resolv.getaddress('example.local')
  rescue Resolv::ResolvError
    puts "no such address!"
  end

  # Service discovery

  mdns = Resolv::MDNS.new

  mdns.each_resource('_http._tcp.local', Resolv::DNS::Resource::IN::PTR) do |rrhttp|
    service = rrhttp.name
    host = nil
    port = nil
    path = '/'

    rrsrv = mdns.getresource(rrhttp.name, Resolv::DNS::Resource::IN::SRV)
    host, port = rrsrv.target.to_s, rrsrv.port
    rrtxt = mdns.getresource(rrhttp.name, Resolv::DNS::Resource::IN::TXT)
    if rrtxt.data =~ /path=(.*)/
      path = $1
    end

    http = Net::HTTP.new(host, port)

    headers = http.head(path)

    puts "#{service[0]} on #{host}:#{port}#{path} was last-modified #{headers['last-modified']}"
  end

== Address Lookups
When used for name lookups, it is most useful to add MDNS to the default
set of resolvers queried when using the 'resolv' module methods. This is
done by doing:
  require 'net/dns/mdns-resolv'
  Resolv.getaddress('localhost') # resolved using Resolv::Hosts("/etc/hosts")
  Resolv.getaddress('www.example.com') # resolved using Resolv::DNS
  Resolv.getaddress('example.local') # resolved using Resolv::MDNS
Using this approach means that both global DNS names and local names can be
resolved. When doing this, you may also consider doing:
  require 'resolv-replace'
This has the effect of replacing the default ruby implementation of address
lookup in IPSocket, TCPSocket, UDPSocket, and SOCKSocket with
Resolv.getaddress, so (if 'net/dns/mdns-resolv' has been required) the
standard libraries TCP/IP classes will use mDNS for name lookups in the .local domain.

== Service Discovery

Service discovery consists of 2 stages:
- enumerating the names of the instances of the service
- resolving the instance names

= Service Enumeration

To do this query the pointer records (Resolv::DNS::Resource::IN::PTR) for
names of the form _svc._prot.local. The values of svc and prot for common
services can be found at http://www.dns-sd.org/ServiceTypes.html\.
The first label of the name returned is suitable for display to peoplem, and
should be unique in the network.

= Service Resolution

In order to resolve a service name query the service record
(Resolv::DNS::Resource::IN::SRV) for the name. The service record contains
a host and port to connect to. The host name will have to be resolved to
an address. This can be done explicitly using mDNS or, if resolv-replace
and mdns-default have been required, it will be done by the standard library.
In addition, some services put "extra" information about the service in a
text (Resolv::DNS::Resource::IN::TXT) record associated with the service name.
The format of the text record is service-specific.

== For More Information

See the following:
- draft-cheshire-dnsext-multicastdns-04.txt for a description of mDNS
- RFC 2782 for a description of DNS SRV records
- draft-cheshire-dnsext-dns-sd-02.txt for a description of how to
  use SRV, PTR, and TXT records for service discovery
- http://www.dns-sd.org

== Comparison to the DNS-SD Extension

The DNS-SD project at http://dnssd.rubyforge.org/wiki/wiki.pl is another
approach to mDNS and service discovery.

DNS-SD is a compiled ruby extension implemented on top of the dns_sd.h APIs
published by Apple. These APIs work by contacting a local mDNS daemon
(through unix domain sockets) and should be more efficient (they can
take advantage of the daemon's cache) and likely a better way of doing
mDNS queries than using pure ruby. Also, the mDNS daemon is capable of
advertising services over the network, and Resolv::MDNS can't do that,
though I'm working on it. There's some technical obstacles.

Currently, the only thing I'm aware of Resolv::MDNS doing that DNS-SD
doesn't is integrate into the standard library so that link-local domain
names can be used throughout the standard networking classes. There is no
reason DNS-SD can't do this, and I'll try and add that capability as soon as I
find a way to install and use DNS-SD, which leads to why you might be
interested in Resolv::MDNS.

The DNS-SD extension requires the dns_sd.h C language APIs for the Apple
mDNS daemon. Installing the Apple responder can be quite difficult, and
requires a running daemon. It also requires compiling the extension. If
you need a pure ruby implementation, or if building DNS-SD turns out to be
difficult for you, Resolv::MDNS may be useful to you.

== Samples

There are a few samples in the samples/ directory:
- mdns.rb is useful for finding out as much as possible about services on .local
- mdns_demo.rb is a sample provided by Ben Giddings, with better docs, showing
  the call sequence he uses when resolving services. Thanks, Ben!

== TODO

- Implement a Resolv:: object that uses the DNS-SD project, so the standard
  library will use it for .local name lookups.
- Implement response cacheing and service advertising in Resolv::MDNS.
- Implement a higher level service discovery API that will work with either
  DNS-SD or Resolv::MDNS, so either can be used (as available) without code
  changes.
- Various API improvements, testing, ...

== Author

Any feedback, questions, problems, etc., please contact me, Sam Roberts,
via dnssd-developers@rubyforge.org, or directly.

--
Into RFID? www.rfidnewsupdate.com Simple, fast, news.

Um, google sez Apple wins. Apple has called their implementation of zeroconf Rendezvous forever. If there's anybody's lawyers we have to worry about it's, Apple's. I think they trademarked "Rendezvous", the IETF name is "zeroconf". Meh, whatever. It's great software, we can figure out the names later. :slight_smile:

Ben

···

On Feb 3, 2005, at 17:33, Lyndon Samson wrote:

Rendezvous is a ( good ) Tibco messaging product, don't get their
lawyers fired up again :wink:

Ben Giddings wrote:

···

On Feb 3, 2005, at 17:33, Lyndon Samson wrote:

Rendezvous is a ( good ) Tibco messaging product, don't get their
lawyers fired up again :wink:

Um, google sez Apple wins. Apple has called their implementation of zeroconf Rendezvous forever. If there's anybody's lawyers we have to worry about it's, Apple's. I think they trademarked "Rendezvous", the IETF name is "zeroconf". Meh, whatever. It's great software, we can figure out the names later. :slight_smile:

Actually, Apple is phasing out the name "Rendezvous" because of the Tibco lawsuit. Regardless, just call it MDNS (multicast DNS) or DNSSD (DNS service discovery), depending on what you're actually talking about and you're fine.

-garrett

Wrote Garrett Rooney <rooneg@electricjellyfish.net>, on Sat, Feb 05, 2005 at 12:45:47AM +0900:

Actually, Apple is phasing out the name "Rendezvous" because of the
Tibco lawsuit. Regardless, just call it MDNS (multicast DNS) or DNSSD
(DNS service discovery), depending on what you're actually talking about
and you're fine.

Note that the lib is called net-mdns, and the only mention of
"Rendezous" is one note that Apple calls it that. Which they still do,
its smeared across their web site, and the minds of their users.

Cheers,
Sam

···

--
Sam Roberts <sroberts@certicom.com>