/lib/dnssd/service.rb
Ruby | 265 lines | 106 code | 58 blank | 101 comment | 14 complexity | 2d04e6c75abf579a3bddab4e365561d7 MD5 | raw file
- require 'thread'
- ##
- # A DNSSD::Service may be used for one DNS-SD call at a time. Between calls
- # the service must be stopped. A single service can be reused multiple times.
- #
- # DNSSD::Service provides the raw DNS-SD functions via the _ variants.
- class DNSSD::Service
- # :stopdoc:
- IPv4 = 1 unless const_defined? :IPv4
- IPv6 = 2 unless const_defined? :IPv6
- # :startdoc:
- ##
- # Creates a new DNSSD::Service
- def initialize
- @replies = []
- @continue = true
- @thread = nil
- @type = nil
- end
- ##
- # 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
- 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 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
- 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
- ##
- # Raises an ArgumentError if +domain+ is too long including NULL terminator
- # and trailing '.'
- 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 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
- 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
- ##
- # 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
- # http://avahi.org/wiki/AvahiAndUnicastDotLocal for details
- 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
- def inspect # :nodoc:
- stopped = stopped? ? 'stopped' : 'running'
- "#<%s:0x%x %s>" % [self.class, object_id, stopped]
- end
- ##
- # Yields results from the mDNS daemon, blocking until data is available.
- 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
- end
- ##
- # 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
- 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 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
- 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 a service discovered via #browse.
- #
- # +name+ may be either the name of the service found or a DNSSD::Reply from
- # DNSSD::Service#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.
- #
- # The returned service can be used to control when to stop resolving the
- # service (see DNSSD::Service#stop).
- #
- # service.resolve "foo bar", "_http._tcp", "local" do |r|
- # p r
- # end
- 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
- ##
- # Returns true if the service has been started.
- def started?
- not stopped?
- end
- end