Module: ZeroConf

Defined in:
lib/zeroconf.rb,
lib/zeroconf/utils.rb,
lib/zeroconf/client.rb,
lib/zeroconf/browser.rb,
lib/zeroconf/service.rb,
lib/zeroconf/version.rb,
lib/zeroconf/resolver.rb,
lib/zeroconf/discoverer.rb

Defined Under Namespace

Modules: MDNS, Utils Classes: A, ANY, Browser, Client, Discoverer, PTR, Resolver, SRV, Service

Constant Summary collapse

MDNS_CACHE_FLUSH =
0x8000
VERSION =
"1.0.1"

Class Method Summary collapse

Class Method Details

.browse(name, interfaces: self.interfaces, timeout: 3, &blk) ⇒ Object

ZeroConf.browse

Call this method to find server information for a particular service. For example, to find server information for servers advertising ‘_elg._tcp.local`, do this:

ZeroConf.browse("_elg._tcp.local") { |r| p r }

Yields info it finds to the provided block as it is received. Pass a list of interfaces you want to use, or just use the default. Also takes a timeout parameter to specify the length of the timeout.

Parameters:

  • interfaces (Array<Socket::Ifaddr>) (defaults to: self.interfaces)

    list of interfaces to query

  • timeout (Numeric) (defaults to: 3)

    number of seconds before returning



26
27
28
29
# File 'lib/zeroconf.rb', line 26

def self.browse name, interfaces: self.interfaces, timeout: 3, &blk
  browser = ZeroConf::Browser.new(name, interfaces:)
  browser.run(timeout:, &blk)
end

.discover(interfaces: self.interfaces, timeout: 3, &blk) ⇒ Object

ZeroConf.discover

Call this method to discover services on your network! Yields services it finds to the provided block as it finds them. Pass a list of interfaces you want to use, or just use the default. Also takes a timeout parameter to specify the length of the timeout.

Parameters:

  • interfaces (Array<Socket::Ifaddr>) (defaults to: self.interfaces)

    list of interfaces to query

  • timeout (Numeric) (defaults to: 3)

    number of seconds before returning



110
111
112
113
# File 'lib/zeroconf.rb', line 110

def self.discover interfaces: self.interfaces, timeout: 3, &blk
  discoverer = ZeroConf::Discoverer.new(interfaces:)
  discoverer.run(timeout:, &blk)
end

.find_addrinfos(name, interfaces: self.interfaces, timeout: 3) ⇒ Object

Get a list tuples with host name and Addrinfo objects for a particular service name.

For example:

pp ZeroConf.find_addrinfos("_elg._tcp.local")
  # [["elgato-key-light-2d93.local", #<Addrinfo: 10.0.1.249:9123 (elgato-key-light-2d93.local)>],
  #  ["elgato-key-light-2d93.local", #<Addrinfo: [fe80::3e6a:9dff:fe19:b313]:9123 (elgato-key-light-2d93.local)>],
  #  ["elgato-key-light-48c6.local", #<Addrinfo: 10.0.1.151:9123 (elgato-key-light-48c6.local)>],
  #  ["elgato-key-light-48c6.local", #<Addrinfo: [fe80::3e6a:9dff:fe19:3a99]:9123 (elgato-key-light-48c6.local)>]]


43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/zeroconf.rb', line 43

def self.find_addrinfos name, interfaces: self.interfaces, timeout: 3
  browse(name, interfaces:, timeout:).flat_map { |r|
    host = nil
    port = nil
    ipv4 = []
    ipv6 = []
    r.additional.each { |name, ttl, data|
      case data
      when Resolv::DNS::Resource::IN::SRV
        host = data.target.to_s
        port = data.port
      when Resolv::DNS::Resource::IN::A
        ipv4 << data.address
      when Resolv::DNS::Resource::IN::AAAA
        ipv6 << data.address
      end
    }
    ipv4.map { |x| [host, ["AF_INET", port, host, x.to_s]] } +
      ipv6.map { |x| [host, ["AF_INET6", port, host, x.to_s]] }
  }.uniq.map { |host, x| [host, Addrinfo.new(x)] }
end

.find_services(interfaces: self.interfaces, timeout: 3) ⇒ Object

ZeroConf.find_services

Get a list of services being advertised on the network!

This method will yield the services as it finds them, or it will return a list of unique service names if no block is given.

Parameters:

  • interfaces (Array<Socket::Ifaddr>) (defaults to: self.interfaces)

    list of interfaces to query

  • timeout (Numeric) (defaults to: 3)

    number of seconds before returning



85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/zeroconf.rb', line 85

def self.find_services interfaces: self.interfaces, timeout: 3
  if block_given?
    discover(interfaces:, timeout:) do |res|
      res.answer.map(&:last).map(&:name).map(&:to_s).each { yield _1 }
    end
  else
    discover(interfaces:, timeout:)
      .flat_map(&:answer)
      .map(&:last)
      .map(&:name)
      .map(&:to_s)
      .uniq
  end
end

.interfacesObject



115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/zeroconf.rb', line 115

def self.interfaces
  addrs = Socket.getifaddrs
  addrs.select { |ifa|
    addr = ifa.addr
    addr &&
      (ifa.flags & Socket::IFF_UP > 0) &&            # must be up
      (ifa.flags & Socket::IFF_MULTICAST > 0) &&     # must have multicast
      (ifa.flags & Socket::IFF_POINTOPOINT == 0) &&  # must not be pointopoint
      (ifa.flags & Socket::IFF_LOOPBACK == 0) &&     # must not be loopback
      (addr.ipv4? ||                                 # must be ipv4 *or*
       (addr.ipv6? && !addr.ipv6_linklocal?))        # must be ipv6 and not link local
  }
end

.resolve(name, interfaces: self.interfaces, timeout: 3, &blk) ⇒ Object



65
66
67
68
# File 'lib/zeroconf.rb', line 65

def self.resolve name, interfaces: self.interfaces, timeout: 3, &blk
  resolver = ZeroConf::Resolver.new(name, interfaces:)
  resolver.run(timeout:, &blk)
end

.service(service, service_port, hostname = Socket.gethostname, service_interfaces: self.service_interfaces, text: [""]) ⇒ Object



70
71
72
73
# File 'lib/zeroconf.rb', line 70

def self.service service, service_port, hostname = Socket.gethostname, service_interfaces: self.service_interfaces, text: [""]
  s = Service.new(service, service_port, hostname, service_interfaces:, text:)
  s.start
end

.service_interfacesObject



129
130
131
132
# File 'lib/zeroconf.rb', line 129

def self.service_interfaces
  ipv4, ipv6 = interfaces.partition { |ifa| ifa.addr.ipv4? }
  [ipv4.first, ipv6&.first].compact
end