# frozen_string_literal: true #-- # Ruby Whois # # An intelligent pure Ruby WHOIS client and parser. # # Copyright (c) 2009-2024 Simone Carletti #++ require 'whois/record/part' require 'whois/record' require 'whois/server/socket_handler' module Whois class Server module Adapters class Base class << self def query_handler @query_handler ||= SocketHandler.new end attr_writer :query_handler end # Default WHOIS request port. DEFAULT_WHOIS_PORT = 43 # Default bind hostname. DEFAULT_BIND_HOST = "0.0.0.0" # @return [Symbol] The type of WHOIS server. attr_reader :type # @return [String] The allocation this server is responsible for. attr_reader :allocation # @return [String, nil] The server hostname. attr_reader :host # @return [Hash] Optional adapter properties. attr_reader :options # Temporary internal response buffer. # # @api private # @return [Array] attr_reader :buffer # @param type [Symbol] the type of WHOIS adapter to define. Known values are :tld, :ipv4, :ipv6, # @param allocation [String] the allocation, range or hostname, this server is responsible for. # @param host [String, nil] the server hostname. Use nil if unknown or not available. # @param options [Hash] optional adapter properties def initialize(type, allocation, host, options = {}) @type = type @allocation = allocation @host = host @options = options || {} end # Checks self and other for equality. # # @param other [The Whois::Server::Adapters::Base] # @return [Boolean] true if the other is the same object, # or other attributes matches this object attributes. def ==(other) equal?(other) || ( other.is_a?(self.class) && type == other.type && allocation == other.allocation && host == other.host && options == other.options ) end alias eql? == # Merges given +settings+ into current {#options}. # # @param settings [Hash] # @return [Hash] the updated options for this object def configure(settings) @host = settings[:host] if settings[:host] options.merge!(settings) end # Performs a Whois lookup for string using the current server adapter. # # Internally, this method calls {#request} using the Template Method design pattern. # # server.lookup("google.com") # # => Whois::Record # # @param string [String] the string to query # @return [Whois::Record] def lookup(string) parts = buffer_start { request(string) } Whois::Record.new(self, parts) end # Performs the real WHOIS request. # # This method is not implemented in {Whois::Server::Adapters::Base} class, # it is intended to be overwritten in the concrete subclasses. # This is the heart of the Template Method design pattern. # # @param string [String] the string to query # @return [void] # @raise [NotImplementedError] # @abstract def request(string) raise NotImplementedError end # Gets the current query handler for this class. # # @see Whois::Servers::Adapters::Base.query_handler # # @return [Object] the query handler def query_handler self.class.query_handler end private def buffer_append(body, host) @buffer << Whois::Record::Part.new(body: body, host: host) end def buffer_start @buffer ||= [] yield(@buffer) @buffer.dup ensure @buffer.clear end # Prepares and passes the query to the {#query_handler}. # # @param [String] query # @param [String] host # @param [String] port # @return [String] # def query(query, host, port = nil) args = [] args.push(host) args.push(port || options[:port] || DEFAULT_WHOIS_PORT) # This is a hack to prevent +TCPSocket.new+ to crash # when resolv-replace.rb file is required. # # +TCPSocket.new+ defaults +local_host+ and +local_port+ to nil # but when you require resolv-replace.rb, +local_host+ # is resolved when you pass any local parameter and in case of nil # it raises the following error # # ArgumentError: cannot interpret as DNS name: nil # if options[:bind_host] || options[:bind_port] args.push(options[:bind_host] || DEFAULT_BIND_HOST) args.push(options[:bind_port]) if options[:bind_port] end self.class.query_handler.call(query, *args) end alias query_the_socket query end end end end