class DNSSD::Service

A DNSSD::Service may be used for one DNS-SD call at a time. The service is automatically stopped after calling. A single service can not be reused multiple times.

DNSSD::Service provides the raw DNS-SD functions via the _ variants.

Public Class Methods

new() click to toggle source

Creates a new DNSSD::Service

# File lib/dnssd/service.rb, line 20
def initialize
  @replies = []
  @continue = true
  @thread = nil
  @type = nil
end

Public Instance Methods

add_record(type, data, ttl = 0, flags = 0) click to toggle source

Adds an extra DNS record of type containing data. ttl is in seconds, use 0 for the default value. flags are currently ignored.

Must be called on a service only after register.

Returns the added DNSSD::Record

# File lib/dnssd/service.rb, line 35
def add_record(type, data, ttl = 0, flags = 0)
  raise TypeError, 'must be called after register' unless @type == :register
  @records ||= []

  _add_record flags.to_i, type, data, ttl
end
browse(type, domain = nil, flags = 0, interface = DNSSD::InterfaceAny, &block) click to toggle source

Browse for services.

For each service found a DNSSD::Reply object is yielded.

service = DNSSD::Service.new
timeout 6 do
  service.browse '_http._tcp' do |r|
    puts "Found HTTP service: #{r.name}"
  end
rescue Timeout::Error
end
# File lib/dnssd/service.rb, line 55
def browse(type, domain = nil, flags = 0, interface = DNSSD::InterfaceAny,
           &block)
  check_domain domain
  interface = DNSSD.interface_index interface unless Integer === interface

  raise DNSSD::Error, 'service in progress' if started?

  @type = :browse

  _browse flags.to_i, interface, type, domain

  process(&block)
end
check_domain(domain) click to toggle source

Raises an ArgumentError if domain is too long including NULL terminator and trailing '.'

# File lib/dnssd/service.rb, line 73
def check_domain(domain)
  return unless domain
  raise ArgumentError, 'domain name string is too long' if
    domain.length >= MAX_DOMAIN_NAME - 1
end
enumerate_domains(flags = DNSSD::Flags::BrowseDomains, interface = DNSSD::InterfaceAny, &block) click to toggle source

Enumerate domains available for browsing and registration.

For each domain found a DNSSD::Reply object is passed to block with domain set to the enumerated domain.

available_domains = []

service.enumerate_domains! do |r|
  available_domains << r.domain
  break unless r.flags.more_coming?
end

p available_domains
# File lib/dnssd/service.rb, line 94
def enumerate_domains(flags = DNSSD::Flags::BrowseDomains,
                      interface = DNSSD::InterfaceAny, &block)
  interface = DNSSD.interface_index interface unless Integer === interface

  raise DNSSD::Error, 'service in progress' if started?

  _enumerate_domains flags.to_i, interface

  @type = :enumerate_domains

  process(&block)
end
getaddrinfo(host, protocol = 0, flags = 0, interface = DNSSD::InterfaceAny, &block) click to toggle source

Retrieve address information for host on protocol

addresses = []
service.getaddrinfo reply.target do |addrinfo|
  addresses << addrinfo.address
  break unless addrinfo.flags.more_coming?
end

When using DNSSD on top of the Avahi compatibilty shim you'll need to setup your /etc/nsswitch.conf correctly. See avahi.org/wiki/AvahiAndUnicastDotLocal for details

# File lib/dnssd/service.rb, line 120
def getaddrinfo(host, protocol = 0, flags = 0,
                interface = DNSSD::InterfaceAny, &block)
  interface = DNSSD.interface_index interface unless Integer === interface

  if respond_to? :_getaddrinfo then
    raise DNSSD::Error, 'service in progress' if started?

    _getaddrinfo flags.to_i, interface, protocol, host

    @type = :getaddrinfo

    process(&block)
  else
    family = case protocol
             when IPv4 then Socket::AF_INET
             when IPv6 then Socket::AF_INET6
             else protocol
             end

    addrinfo = Socket.getaddrinfo host, nil, family

    addrinfo.each do |_, _, a_host, ip, _|
      sockaddr = Socket.pack_sockaddr_in 0, ip
      @replies << DNSSD::Reply::AddrInfo.new(self, 0, 0, a_host, sockaddr, 0)
    end
  end
end
process() { |Result| ... } click to toggle source

Yields results from the mDNS daemon, blocking until data is available. Use break or return when you wish to stop receiving results.

The service is automatically stopped after calling this method.

# File lib/dnssd/service.rb, line 159
def process # :yields: DNSSD::Result
  @thread = Thread.current

  while @continue do
    _process if @replies.empty?
    yield @replies.shift until @replies.empty?
  end

  @thread = nil

  self
rescue DNSSD::ServiceNotRunningError
  # raised when we jump out of DNSServiceProcess() while it's waiting for a
  # response
  self
ensure
  stop unless stopped?
end
query_record(fullname, record_type, record_class = DNSSD::Record::IN, flags = 0, interface = DNSSD::InterfaceAny, &block) click to toggle source

Retrieves an arbitrary DNS record

fullname is the full name of the resource record. record_type is the type of the resource record (see DNSSD::Resource).

flags may be either DNSSD::Flags::ForceMulticast or DNSSD::Flags::LongLivedQuery

service.query_record "hostname._afpovertcp._tcp.local",
                     DNSService::Record::SRV do |record|
  p record
end
# File lib/dnssd/service.rb, line 192
def query_record(fullname, record_type, record_class = DNSSD::Record::IN,
                 flags = 0, interface = DNSSD::InterfaceAny, &block)
  interface = DNSSD.interface_index interface unless Integer === interface

  raise DNSSD::Error, 'service in progress' if started?

  _query_record flags.to_i, interface, fullname, record_type, record_class

  @type = :query_record

  process(&block)
end
register(name, type, domain, port, host = nil, text_record = nil, flags = 0, interface = DNSSD::InterfaceAny, &block) click to toggle source

Register a service. A DNSSD::Reply object is passed to the optional block when the registration completes.

service.register "My Files", "_http._tcp", nil, 8080 do |r|
  puts "successfully registered: #{r.inspect}"
end
# File lib/dnssd/service.rb, line 213
def register(name, type, domain, port, host = nil, text_record = nil,
             flags = 0, interface = DNSSD::InterfaceAny, &block)
  check_domain domain
  interface = DNSSD.interface_index interface unless Integer === interface
  text_record = text_record.encode if text_record

  raise DNSSD::Error, 'service in progress' if started?

  _register flags.to_i, interface, name, type, domain, host, port,
            text_record, &block

  @type = :register

  process(&block) if block
end
resolve(name, type = name.type, domain = name.domain, flags = 0, interface = DNSSD::InterfaceAny, &block) click to toggle source

Resolve a service discovered via browse.

name may be either the name of the service found or a DNSSD::Reply from #browse. When name is a DNSSD::Reply, type and domain are automatically filled in, otherwise the service type and domain must be supplied.

The service is resolved to a target host name, port number, and text record, all contained in the DNSSD::Reply object passed to the required block.

service.resolve "foo bar", "_http._tcp", "local" do |r|
  p r
end
# File lib/dnssd/service.rb, line 245
def resolve(name, type = name.type, domain = name.domain, flags = 0,
            interface = DNSSD::InterfaceAny, &block)
  name = name.name if DNSSD::Reply === name
  check_domain domain
  interface = DNSSD.interface_index interface unless Integer === interface

  raise DNSSD::Error, 'service in progress' if started?

  _resolve flags.to_i, interface, name, type, domain

  @type = :resolve

  process(&block)
end
started?() click to toggle source

Returns true if the service has been started.

# File lib/dnssd/service.rb, line 263
def started?
  not stopped?
end