socketry/socketry

View on GitHub
lib/socketry/resolver/system.rb

Summary

Maintainability
A
0 mins
Test Coverage
# frozen_string_literal: true

require "timeout"

module Socketry
  module Resolver
    # System DNS resolver backed by the POSIX getaddrinfo(3) function
    module System
      module_function

      # Resolve a domain name using IPSocket.getaddress. This uses getaddrinfo(3)
      # on POSIX operating systems.
      #
      # @param hostname [String] name of the host whose IP address we'd like to obtain
      # @return [IPAddr] resolved IP address
      # @raise [Socketry::Resolver::Error] an error occurred resolving the domain name
      # @raise [Socketry::TimeoutError] a timeout occured before the name could be resolved
      # @raise [Socketry::AddressError] the name was resolved to an unsupported address
      def resolve(hostname, timeout: nil)
        raise TypeError, "expected String, got #{hostname.class}" unless hostname.is_a?(String)

        begin
          case timeout
          when Integer, Float
            # NOTE: ::Timeout is not thread safe. For thread safety, use Socketry::Resolver::Resolv
            result = ::Timeout.timeout(timeout) { IPSocket.getaddress(hostname) }
          when NilClass
            result = IPSocket.getaddress(hostname)
          else raise TypeError, "expected Numeric, got #{timeout.class}"
          end
        rescue ::SocketError => ex
          raise Socketry::Resolver::Error, ex.message, ex.backtrace
        rescue ::Timeout::Error => ex
          raise Socketry::TimeoutError, ex.message, ex.backtrace
        end

        begin
          IPAddr.new(result)
        rescue IPAddr::InvalidAddressError => ex
          raise Socketry::AddressError, ex.message, ex.backtrace
        end
      end
    end

    # Use Socketry::Resolver::System as the default resolver
    DEFAULT_RESOLVER = System
  end
end