app/lib/net/dns.rb
require "resolv"
require "timeout"
require 'ipaddr'
module Net
module DNS
# Looks up the IP or MAC address. Handles the conversion of a DNS miss
# exception into nil
# [+query+]: IP or hostname
# Returns: a new DNS record object, A, AAAA, PTR4 or PTR6 accordingly
# We query DNS directly, as its faster then to query our own proxy.
def self.lookup(query, options = {})
return nil unless query.present?
proxy = options.fetch(:proxy, nil)
resolver = options.fetch(:resolver, Resolv::DNS.new)
ipfamily = options.fetch(:ipfamily, Socket::AF_INET)
resolver.timeouts = options.fetch(:timeout, Setting[:dns_timeout])
ipquery = IPAddr.new(query) rescue nil
if ipquery&.ipv4?
hostname = resolver.getname(query).to_s
ip = query
type = "PTR4"
elsif ipquery&.ipv6?
hostname = resolver.getname(query).to_s
ip = query
type = "PTR6"
elsif ipfamily == Socket::AF_INET6
ip = resolver.getresource(query, Resolv::DNS::Resource::IN::AAAA).address.to_s
hostname = query
type = "AAAA"
else
ip = resolver.getresource(query, Resolv::DNS::Resource::IN::A).address.to_s
hostname = query
type = "A"
end
attrs = { :hostname => hostname, :ip => ip, :resolver => resolver, :proxy => proxy }
case type
when "A"
ARecord.new attrs
when "AAAA"
AAAARecord.new attrs
when "PTR4"
PTR4Record.new attrs
when "PTR6"
PTR6Record.new attrs
end
rescue Resolv::ResolvError, SocketError
nil
end
class Record < Net::Record
attr_accessor :ip, :resolver, :type, :ipfamily
def initialize(opts = { })
super(opts)
self.resolver ||= Resolv::DNS.new
end
def destroy
logger.info "Delete the DNS #{type} record for #{self}"
end
def create
raise "Must define a hostname" if hostname.blank?
logger.info "Add DNS #{type} record for #{self}"
end
def attrs
raise "Abstract class"
end
def dns_lookup(ip_or_name, ipfamily = nil)
DNS.lookup(ip_or_name, :proxy => proxy, :resolver => resolver, :ipfamily => ipfamily || self.ipfamily)
end
protected
def generate_conflict_error
logger.warn "Conflicting DNS #{type} record for #{self} detected"
e = Net::Conflict.new
e.type = "dns"
e.expected = to_s
e.actual = conflicts
e.message = "DNS conflict detected - expected #{self}, found #{conflicts.map(&:to_s).join(', ')}"
e
end
end
end
end