class Resolve::Hostname

Constants

ADDRESS_VERSIONS
DEFAULT_ENABLE_SYSTEM_RESOLVER
DEFAULT_EXPIRATION_SECONDS
DEFAULT_PERMIT_SECONDARY_ADDRESS_VERSION
DEFAULT_PRIMARY_ADDRESS_VERSION
DEFAULT_RAISE_NOTFOUND
DEFAULT_RESOLVER_TTL
VERSION

Attributes

cache[R]
resolver_expires[RW]
resolver_ttl[RW]
ttl[RW]

Public Class Methods

new(opts={}) click to toggle source

TODO: DNS RoundRobin with resolv

DEFAULT_SUPPORTS_DNS_RR = false
# File lib/resolve/hostname.rb, line 35
def initialize(opts={})
  @primary_ip_version = opts[:version] || DEFAULT_PRIMARY_ADDRESS_VERSION
  unless ADDRESS_VERSIONS.include? @primary_ip_version
    raise ArgumentError, "unknown version of ip address: #{opts[:version]}"
  end

  @ttl = opts[:ttl] || DEFAULT_EXPIRATION_SECONDS
  @resolver_ttl = opts[:resolver_ttl] || DEFAULT_RESOLVER_TTL

  @system_resolver_enabled = opts.fetch(:system_resolver, DEFAULT_ENABLE_SYSTEM_RESOLVER)
  @permit_secondary_address_version = opts.fetch(:permit_other_version, DEFAULT_PERMIT_SECONDARY_ADDRESS_VERSION)
  @raise_notfound = opts.fetch(:raise_notfound, DEFAULT_RAISE_NOTFOUND)

  @cache = {}
  @mutex = Mutex.new

  @resolver = nil
  @resolver_expires = nil

  @invalid_address_error = if IPAddr.const_defined?('InvalidAddressError')
                             IPAddr::InvalidAddressError
                           else
                             ArgumentError
                           end
end

Public Instance Methods

getaddress(name) click to toggle source
# File lib/resolve/hostname.rb, line 61
def getaddress(name)
  unless @cache[name]
    @mutex.synchronize do
      @cache[name] ||= CachedValue.new(@ttl)
    end
  end
  @cache[name].get_or_refresh{ resolve(name) }
end
primary_ip_version() click to toggle source
# File lib/resolve/hostname.rb, line 70
def primary_ip_version
  @primary_ip_version
end
primary_version_address?(str) click to toggle source
# File lib/resolve/hostname.rb, line 78
def primary_version_address?(str)
  if @primary_ip_version == :ipv4
    IPAddr.new(str).ipv4?
  else
    IPAddr.new(str).ipv6?
  end
end
resolv_instance() click to toggle source
# File lib/resolve/hostname.rb, line 129
def resolv_instance
  return @resolver if @resolver && @resolver_expires >= Time.now

  @resolver_expires = Time.now + @resolver_ttl
  @resolver = Resolv::DNS.new

  @resolver
end
resolve(name) click to toggle source
# File lib/resolve/hostname.rb, line 86
def resolve(name)
  secondary = nil

  is_address = false
  begin
    IPAddr.new(name)
    is_address = true
  rescue @invalid_address_error => e
    # ignore
  end
  return name if is_address

  if @system_resolver_enabled
    addr = resolve_builtin(name)
    if addr
      return addr if primary_version_address?(addr)
      secondary = addr
    end
  end

  addr = resolve_resolv(name, primary_ip_version)
  if addr
    return addr if primary_version_address?(addr)
    secondary ||= addr
  end

  if secondary.nil? && @permit_secondary_address_version
    secondary = resolve_resolv(name, secondary_ip_version)
  end

  addr = resolve_magic(name)
  if addr
    return addr if primary_version_address?(addr)
    secondary ||= addr
  end

  return secondary if secondary && @permit_secondary_address_version

  raise NotFoundError, "cannot resolve hostname #{name}" if @raise_notfound

  nil
end
resolve_builtin(name) click to toggle source
# File lib/resolve/hostname.rb, line 155
def resolve_builtin(name)
  begin
    IPSocket.getaddress(name)
  rescue SocketError => e
    raise unless e.message.start_with?('getaddrinfo: nodename nor servname provided, or not known')
    nil
  end
end
resolve_magic(name) click to toggle source
# File lib/resolve/hostname.rb, line 164
def resolve_magic(name)
  if name =~ /^localhost$/
    return @primary_ip_version == :ipv4 ? '127.0.0.1' : '::1'
  end
  nil
end
resolve_resolv(name, version) click to toggle source
# File lib/resolve/hostname.rb, line 138
def resolve_resolv(name, version)
  t = case version
      when :ipv4
        Resolv::DNS::Resource::IN::A
      when :ipv6
        Resolv::DNS::Resource::IN::AAAA
      else
        raise ArgumentError, "invalid ip address version:#{version}"
      end
  begin
    resolv_instance.getresource(name, t).address.to_s
  rescue Resolv::ResolvError => e
    raise unless e.message.start_with?('DNS result has no information for')
    nil
  end
end
secondary_ip_version() click to toggle source
# File lib/resolve/hostname.rb, line 74
def secondary_ip_version
  @primary_ip_version == :ipv4 ? :ipv6 : :ipv4
end