Class: Resolv::MDNS

Inherits:
Object
  • Object
show all
Defined in:
lib/net/dns/resolv-mdns.rb

Overview

Address Lookups

Requiring ‘net/dns/mdns-resolv’ causes a Resolv::MDNS resolver to be added to list of default resolvers queried when using Resolv#getaddress, and the other Resolv module methods.

It can be used by doing:

require 'net/dns/resolv-mdns'
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 'net/dns/resolv-mdns'
require 'net/dns/resolv-replace'

This has the effect of replacing the default ruby implementation of address lookup using the C library in IPSocket, TCPSocket, UDPSocket, and SOCKSocket with Resolv.getaddress. Since ‘net/dns/resolv-mdns’ has been required Resolv.getaddress and the standard libraries TCP/IP classes will use mDNS for name lookups in the .local mDNS domain, without even knowing it themselves.

NOTE: the version of resolv.rb and resolv-replace.rb in net-mdns are based on the head of ruby 1.8.x cvs + bug fixes required by net-mdns and not present in the cvs. They must be used in place of the standard library’s resolv implementation!

Service Discovery (DNS-SD)

Service discovery consists of 2 stages:

  • enumerating the names of the instances of the service

  • resolving the instance names

The Net::DNS::MDNSSD API is better documented and easier to use for DNS-SD. Still, here’s some information on using the Resolv APIs for DNS-SD, and examples of doing so are:

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 www.dns-sd.org/ServiceTypes.html. The first label of the name returned is suitable for display to people, 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 has 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.

Constant Summary collapse

DefaultTimeout =

How many seconds to wait before assuming all responses have been seen.

2
Default =
Resolv::MDNS.new

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config_info = nil) ⇒ MDNS

See Resolv::DNS#new.



79
80
81
82
83
# File 'lib/net/dns/resolv-mdns.rb', line 79

def initialize(config_info=nil)
  @mutex = Mutex.new
  @config = DNS::Config.new(config_info)
  @initialized = nil
end

Class Method Details

.defaultObject

Return the default MDNS Resolver. This is what is used when Resolv.getaddress and friends are called. Use it unless you need to specify config_info to Resolv::MDNS.new.



222
223
224
# File 'lib/net/dns/resolv-mdns.rb', line 222

def self.default
  Default
end

Instance Method Details

#each_address(name) ⇒ Object

See Resolv::DNS#each_address.



108
109
110
# File 'lib/net/dns/resolv-mdns.rb', line 108

def each_address(name)
  each_resource(name, DNS::Resource::IN::A) {|resource| yield resource.address}
end

#each_name(address) ⇒ Object

See Resolv::DNS#each_name.



126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/net/dns/resolv-mdns.rb', line 126

def each_name(address)
  case address
  when DNS::Name
    ptr = address
  when IPv4::Regex
    ptr = IPv4.create(address).to_name
  when IPv6::Regex
    ptr = IPv6.create(address).to_name
  else
    raise ResolvError.new("cannot interpret as address: #{address}")
  end
  each_resource(ptr, DNS::Resource::IN::PTR) {|resource| yield resource.name}
end

#each_resource(name, typeclass) ⇒ Object

See Resolv::DNS#eachresource.



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/net/dns/resolv-mdns.rb', line 195

def each_resource(name, typeclass)
  name = generate_candidates(name)

  query = Net::DNS::MDNS::Query.new(name, typeclass)

  begin
    # We want all the answers we can get, within the timeout period.
    begin
      timeout(DefaultTimeout) do
        query.each do |answers|
          answers.each do |an|
            yield an.data
          end
        end
      end
    rescue TimeoutError
    end
  ensure
    query.stop
  end
end

#generate_candidates(name) ⇒ Object

:nodoc:



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/net/dns/resolv-mdns.rb', line 153

def generate_candidates(name) # :nodoc:
  # Names ending in .local MUST be resolved using mDNS. Other names may be, but
  # SHOULD NOT be, so a local machine can't spoof a non-local address.
  #
  # Reverse lookups in the domain '.254.169.in-addr.arpa' should also be resolved
  # using mDNS.
  #
  # TODO - those are the IPs auto-allocated with ZeroConf. In my (common)
  # situation, I have a net of OS X machines behind and ADSL firewall box,
  # and all IPs were allocated in 192.168.123.*. I can do mDNS queries to
  # get these addrs, but I can't do an mDNS query to reverse lookup the
  # addrs. There are security reasons to not allow all addrs to be reversed
  # on the local network, but maybe it wouldn't be so bad if MDNS was after
  # DNS, so it only did it for addrs that were unmatched by DNS?
  #
  # Or perhaps IP addrs in the netmask of the ifx should be considered local,
  # and mDNS allowed on them?
  #
  # If the search domains includes .local, we can add .local to it only if
  # it has no dots and wasn't absolute.
  lazy_initialize
  dotlocal = DNS::Name.create('local')
  search_dotlocal = @config.search.map.include?( dotlocal.to_a )
  name = DNS::Name.create(name)
  if name.absolute?
    name = name
  elsif name.length == 1 && search_dotlocal
    name = name + dotlocal
  elsif name.length > 1
    name = name
  else
    name = nil
  end
  if name.subdomain_of?('local') || name.subdomain_of?('254.169.in-addr.arpa')
    name.absolute = true
    name
  else
    nil
  end
end

#getaddress(name) ⇒ Object

See Resolv::DNS#getaddress.

Raises:



95
96
97
98
# File 'lib/net/dns/resolv-mdns.rb', line 95

def getaddress(name)
  each_address(name) {|address| return address}
  raise ResolvError.new("mDNS result has no information for #{name}")
end

#getaddresses(name) ⇒ Object

See Resolv::DNS#getaddresss.



101
102
103
104
105
# File 'lib/net/dns/resolv-mdns.rb', line 101

def getaddresses(name)
  ret = []
  each_address(name) {|address| ret << address}
  return ret
end

#getname(address) ⇒ Object

See Resolv::DNS#getname.

Raises:



113
114
115
116
# File 'lib/net/dns/resolv-mdns.rb', line 113

def getname(address)
  each_name(address) {|name| return name}
  raise ResolvError.new("mDNS result has no information for #{address}")
end

#getnames(address) ⇒ Object

See Resolv::DNS#getnames.



119
120
121
122
123
# File 'lib/net/dns/resolv-mdns.rb', line 119

def getnames(address)
  ret = []
  each_name(address) {|name| ret << name}
  return ret
end

#getresource(name, typeclass) ⇒ Object

See Resolv::DNS#getresource.

Raises:



141
142
143
144
# File 'lib/net/dns/resolv-mdns.rb', line 141

def getresource(name, typeclass)
  each_resource(name, typeclass) {|resource| return resource}
  raise ResolvError.new("mDNS result has no information for #{name}")
end

#getresources(name, typeclass) ⇒ Object

See Resolv::DNS#getresources.



147
148
149
150
151
# File 'lib/net/dns/resolv-mdns.rb', line 147

def getresources(name, typeclass)
  ret = []
  each_resource(name, typeclass) {|resource| ret << resource}
  return ret
end

#lazy_initializeObject

:nodoc:



85
86
87
88
89
90
91
92
# File 'lib/net/dns/resolv-mdns.rb', line 85

def lazy_initialize # :nodoc:
  @mutex.synchronize do
    unless @initialized
      @config.lazy_initialize
      @initialized = true
    end
  end
end