class MDNS

Constants

MDNS_PORT
MULTICAST_IP
VERSION

Public Class Methods

add_record(host, ttl, ip) click to toggle source
# File lib/mdns.rb, line 70
def add_record(host, ttl, ip)
  records[host] = Record.new(host, ttl, ip)
  respond(records[host])
end
hosts() click to toggle source
# File lib/mdns.rb, line 79
def hosts
  records.keys
end
records() click to toggle source
# File lib/mdns.rb, line 75
def records
  @records ||= {}
end
reset() click to toggle source
# File lib/mdns.rb, line 83
def reset
  @records = nil
  stop
end
respond(record, query = nil, ip = MULTICAST_IP, port = MDNS_PORT) click to toggle source
# File lib/mdns.rb, line 53
def respond(record, query = nil, ip = MULTICAST_IP, port = MDNS_PORT)
  return if !@socket || @socket.closed?
  # I have no idea what I'm doing
  response        = Resolv::DNS::Message.new(query ? query.id : 0)
  response.qr     = 1
  response.opcode = 0
  response.aa     = 1
  response.rd     = 0
  response.ra     = 0
  response.rcode  = 0
  if query
    response.add_question(*query.question.first)
  end
  response.add_answer(record.host, record.ttl, Resolv::DNS::Resource::IN::A.new(record.ip))
  @socket.send(response.encode, 0, ip, port)
end
start() click to toggle source
# File lib/mdns.rb, line 12
def start
  @socket = UDPSocket.new
  @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
  @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEPORT, true) if Socket.const_defined?("SO_REUSEPORT")
  ip_mreq = IPAddr.new(MULTICAST_IP).hton + IPAddr.new('0.0.0.0').hton
  @socket.setsockopt(Socket::IPPROTO_IP, Socket::IP_ADD_MEMBERSHIP, ip_mreq)
  @socket.setsockopt(Socket::IPPROTO_IP, Socket::IP_TTL, 255)
  @socket.setsockopt(Socket::IPPROTO_IP, Socket::IP_MULTICAST_TTL, 255)
  @socket.bind(Socket::INADDR_ANY, MDNS_PORT)
  Thread.abort_on_exception = true
  @thr = Thread.new do
    loop do
      data, address = @socket.recvfrom(1024)
      packet = begin
        Resolv::DNS::Message.decode(data)
      rescue Resolv::DNS::DecodeError
        nil # Invalid DNS data
      rescue
        nil # Sometimes it errors out due to non DNS data
      end
      next if packet.nil?
      if packet.qr == 0
        hosts = packet.question.map(&:first).map(&:to_s)
        matches = self.hosts & hosts
        matches.each do |host|
          respond(records[host], packet, address[3], address[1])
        end
      end
    end
  end

  # Broadcast records
  records.values.each do |record|
    respond(record)
  end

  at_exit do
    stop
  end
end
stop() click to toggle source
# File lib/mdns.rb, line 88
def stop
  if @thr
    @thr.kill
    @thr = nil
  end
  if @socket && !@socket.closed?
    @socket.close
  end
end