From: Sam Roberts Date: 2005-02-03T13:00:24+09:00 Subject: 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 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.