DNS Service Discovery and multicast DNS
0.9.0 released

Crystal Lang mDNS Support

Build Status

  • Discover services using DNS-SD

DNS-SD is used where there might be multiple devices implementing a service.

  • Lookup devices using mDNS

mDNS is used to find the IP address of device, like a Raspberry Pi


  1. Add the dependency to your shard.yml:

        github: spider-gazelle/mdns
  2. Run shards install

Single Shot usage

As described in the rfc

Simple DNS-SD query

To perform a simple query for a service on the network

require "mdns"

# Look up homekit devices on the network
results = MDNS.one_shot "_hap._tcp.local"

results.each do |(address, _io_memory, response)|
  address # => Socket::IPAddress
  response # => MDNS::Message

  response.is_response # => true
  response.authoritative_answer # => true / false
  response.response_code # => ResponseCode::NoError
  response.answer_count # => 1
  response.answers # => Array(MDNS::Resource)

Multiple service queries

In a single request

# Look up homekit devices and hubs on the network
results = MDNS.one_shot do |message|
  message.query "_hap._tcp.local"
  message.query "_homekit._tcp.local"

# You will need to inspect the answers to differentiate between devices and hubs
results.each do |(address, io_memory, response)|
  is_hap = false
  is_kit = false

  response.answers.each do |answer|
    # NOTE:: when extracting the domain name the whole raw response is required
    # due to domain name compression:
    domain_name = answer.domain_name(io_memory)

    if domain_name == "_hap._tcp.local"
      is_hap = true
    elsif domain_name == "_homekit._tcp.local"
      is_kit = true

mDNS query

If we are looking for the IP address of a device for instance.

# We're requesting the IPv4 address of the phone (A record)
# We could also do a multi-request and get the IPv6 address too (AAAA record)
results = MDNS.one_shot "Steves-iPhone.local", type: MDNS::Type::A

if address_response = results.first?
  # NOTE:: the `_address` here is the device that responded, it might not be an
  # an authoritative answer (i.e. something replying on behalf of the device)
  # so extract the IP address from the payload (mDNS only, for DNS-SD use the `_address`)
  _address, _io_memory, response = address_response

  response.answers[0].address # => ""

Continuous Multicast DNS Querying

This is only the most basic of servers implementing the transport layer. It could definitely be used as the basis for a DNS cache as described by the RFC

require "mdns"

server =
loop do
  break if server.closed?
  address, io_memory, message = server.receive

  if message.query?
    # process a query here
    # process responses here

  # This code prints the details of the message
  puts Time.utc.to_s("%X")
  puts "QUERY:"
  puts "ANSWERS:"
  puts { |answer| do |str|
      str << answer.domain_name(io_memory)
      str << " => "
      case answer.type
      when MDNS::Type::A, MDNS::Type::AAAA
        str << answer.address
      when MDNS::Type::PTR
        str << address.address
        str << " (PTR)"
        str << answer.type.to_s
  puts "\n\n\n"

  github: spider-gazelle/mdns
  version: ~> 0.9.0
Crystal none

Dependencies 1

  • bindata ~> 1.6
    {'github' => 'spider-gazelle/bindata', 'version' => '~> 1.6'}

Development Dependencies 1

  • ameba
    {'github' => 'veelenga/ameba'}

Dependents 0

Last synced .
search fire star recently