class Whois::Server

The {Whois::Server} class has two important roles:

  1. it acts as a database for the WHOIS server definitions

  2. it is responsible for selecting the right adapter used to handle the query to the WHOIS server(s).

Public Class Methods

define(type, allocation, host, options = {}) click to toggle source

Defines a new server for :type queries.

@param [Symbol] type

The type of WHOIS server to define.
Known values are :tld, :ipv4, :ipv6.

@param [String] allocation

The allocation, range or hostname, this server is responsible for.

@param [String, nil] host

The server hostname. Use nil if unknown or not available.

@param [Hash] options Optional definition properties. @option options [Class] :adapter (Whois::Server::Adapters::Standard)

This option has a special meaning and determines the adapter Class to use.
Defaults to {Whois::Server::Adapters::Standard} unless specified.
All the other options are passed directly to the adapter which can decide how to use them.

@return [void]

@example

# Define a server for the .it extension
Whois::Server.define :tld, ".it", "whois.nic.it"

# Define a new server for an range of IPv4 addresses
Whois::Server.define :ipv4, "61.192.0.0/12", "whois.nic.ad.jp"

# Define a new server for an range of IPv6 addresses
Whois::Server.define :ipv6, "2001:2000::/19", "whois.ripe.net"

# Define a new server with a custom adapter
Whois::Server.define :tld, ".test", nil,
  :adapter => Whois::Server::Adapter::None

# Define a new server with a custom adapter and options
Whois::Server.define :tld, ".ar", nil,
  :adapter => Whois::Server::Adapters::Web,
  :url => "http://www.nic.ar/"
# File lib/whois/server.rb, line 140
def self.define(type, allocation, host, options = {})
  @@definitions[type] ||= []
  @@definitions[type] <<  [allocation, host, options]
end
definitions(type = nil) click to toggle source

Lookup and returns the definition list for given type, or all definitions if type is nil.

@param [Symbol] type The type of WHOIS server to lookup.

Known values are :tld, :ipv4, :ipv6.

@return [{ Symbol => Array }]

The definition Hash if +type+ is +nil+.

@return [Array<Hash>]

The definitions for given +type+ if +type+ is not +nil+ and +type+ exists.

@return [nil]

The definitions for given +type+ if +type+ is not +nil+ and +type+ doesn't exist.

@example Return the definition database.

Whois::Server.definitions
# => { :tld => [...], :ipv4 => [], ... }

@example Return the definitions for given key.

Whois::Server.definitions(:tld)
# => [...]

Whois::Server.definitions(:invalid)
# => nil
# File lib/whois/server.rb, line 95
def self.definitions(type = nil)
  if type.nil?
    @@definitions
  else
    @@definitions[type]
  end
end
factory(type, allocation, host, options = {}) click to toggle source

Creates a new server adapter from given arguments and returns the server instance.

By default, returns a new {Whois::Server::Adapters::Standard} instance. You can customize the behavior passing a custom adapter class as :adapter option.

Whois::Server.factory :tld, ".it", "whois.nic.it"
# => #<Whois::Servers::Adapter::Standard>

Whois::Server.factory :tld, ".it", "whois.nic.it",
  :option => Whois::Servers::Adapter::Custom
# => #<Whois::Servers::Adapter::Custom>

Please note that any adapter is responsible for a limited set of queries, which should be included in the range of the allocation parameter. Use {Whois::Server.guess} if you are not sure which adapter is the right one for a specific string.

@param [Symbol] type

The type of WHOIS server to define.
Known values are :tld, :ipv4, :ipv6.

@param [String] allocation

The allocation, range or hostname, this server is responsible for.

@param [String, nil] host

The server hostname. Use nil if unknown or not available.

@param [Hash] options Optional definition properties. @option options [Class] :adapter (Whois::Server::Adapters::Standard)

This option has a special meaning and determines the adapter Class to use.
Defaults to {Whois::Server::Adapters::Standard} unless specified.
All the other options are passed directly to the adapter which can decide how to use them.

@return [Whois::Server::Adapters::Standard]

An adapter that can be used to perform queries.
# File lib/whois/server.rb, line 180
def self.factory(type, allocation, host, options = {})
  options = options.dup
  adapter = options.delete(:adapter) || Adapters::Standard
  adapter = Adapters.const_get(camelize(adapter)) unless adapter.respond_to?(:new)
  adapter.new(type, allocation, host, options)
end
guess(string) click to toggle source

Parses string and tries to guess the right server.

It successfully detects the following query types:

  • ipv6

  • ipv4

  • top level domains (e.g. .com, .net, .it)

  • domain names (e.g. google.com, google.net, google.it)

  • emails

Note that not all query types actually have a corresponding adapter. For instance, the following request will result in a {Whois::ServerNotSupported} exception.

Whois::Server.guess "mail@example.com"

@param [String] string @return [Whois::Server::Adapters::Base]

The adapter that can be used to perform
WHOIS queries for <tt>string</tt>.

@raise [Whois::ServerNotFound]

When unable to find an appropriate WHOIS adapter
for <tt>string</tt>. Most of the cases, the <tt>string</tt>
haven't been recognised as one of the supported query types.

@raise [Whois::ServerNotSupported]

When the <tt>string</tt> type is detected,
but the object type doesn't have any supported WHOIS adapter associated.
# File lib/whois/server.rb, line 217
def self.guess(string)
  # Top Level Domain match
  if matches_tld?(string)
    return factory(:tld, ".", "whois.iana.org")
  end

  # IP address (secure match)
  if matches_ip?(string)
    return find_for_ip(string)
  end

  # Email Address (secure match)
  if matches_email?(string)
    return find_for_email(string)
  end

  # Domain Name match
  if server = find_for_domain(string)
    return server
  end

  # ASN match
  if matches_asn?(string)
    return find_for_asn(string)
  end

  # Game Over
  raise ServerNotFound, "Unable to find a WHOIS server for `#{string}'"
end
load_definitions() click to toggle source

Searches the /definitions folder for definition files and loads them. This method is automatically invoked when this file is parsed by the Ruby interpreter (scroll down to the bottom of this file).

@return [void]

# File lib/whois/server.rb, line 52
def self.load_definitions
  Dir[File.expand_path("../../../data/*.json", __FILE__)].each { |f| load_json(f) }
end
load_json(file) click to toggle source

Loads the definitions from a JSON file.

@param [String] file The path to the definition file.

@return [void]

# File lib/whois/server.rb, line 61
def self.load_json(file)
  type = File.basename(file, File.extname(file)).to_sym
  JSON.load(File.read(file)).each do |allocation, settings|
    define(type, allocation, settings.delete("host"), Hash[settings.map { |k,v| [k.to_sym, v] }])
  end
end

Private Class Methods

camelize(string) click to toggle source
# File lib/whois/server.rb, line 250
def self.camelize(string)
  string.to_s.split("_").collect(&:capitalize).join
end
find_for_asn(string) click to toggle source
# File lib/whois/server.rb, line 297
def self.find_for_asn(string)
  asn = string[/\d+/].to_i
  asn_type = asn <= 65535 ? :asn16 : :asn32
  definitions(asn_type).each do |definition|
    if (range = definition.first.split.map(&:to_i)) && asn >= range.first && asn <= range.last
      return factory(asn_type, *definition)
    end
  end
  raise AllocationUnknown, "Unknown AS number - `#{asn}'."
end
find_for_domain(string) click to toggle source
# File lib/whois/server.rb, line 290
def self.find_for_domain(string)
  definitions(:tld).each do |definition|
    return factory(:tld, *definition) if /#{Regexp.escape(definition.first)}$/ =~ string
  end
  nil
end
find_for_email(string) click to toggle source
# File lib/whois/server.rb, line 286
def self.find_for_email(string)
  raise ServerNotSupported, "No WHOIS server is known for email objects"
end
find_for_ip(string) click to toggle source
# File lib/whois/server.rb, line 271
def self.find_for_ip(string)
  begin
    ip = IPAddr.new(string)
    type = ip.ipv4? ? :ipv4 : :ipv6
    definitions(type).each do |definition|
      if IPAddr.new(definition.first).include?(ip)
        return factory(type, *definition)
      end
    end
  rescue ArgumentError
    # continue
  end
  raise AllocationUnknown, "IP Allocation for `#{string}' unknown. Server definitions might be outdated."
end
matches_asn?(string) click to toggle source
# File lib/whois/server.rb, line 267
def self.matches_asn?(string)
  string =~ /^as\d+$/i
end
matches_email?(string) click to toggle source
# File lib/whois/server.rb, line 263
def self.matches_email?(string)
  string =~ /@/
end
matches_ip?(string) click to toggle source
# File lib/whois/server.rb, line 259
def self.matches_ip?(string)
  valid_ipv4?(string) || valid_ipv6?(string)
end
matches_tld?(string) click to toggle source
# File lib/whois/server.rb, line 255
def self.matches_tld?(string)
  string =~ /^\.(xn--)?[a-z0-9]+$/
end
valid_ipv4?(addr) click to toggle source
# File lib/whois/server.rb, line 309
def self.valid_ipv4?(addr)
  if /\A(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\Z/ =~ addr
    return $~.captures.all? {|i| i.to_i < 256}
  end
  false
end
valid_ipv6?(addr) click to toggle source
# File lib/whois/server.rb, line 316
def self.valid_ipv6?(addr)
  # IPv6 (normal)
  return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*\Z/ =~ addr
  return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr
  return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*)?\Z/ =~ addr
  # IPv6 (IPv4 compat)
  return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:/ =~ addr && valid_ipv4?($')
  return true if /\A[\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_ipv4?($')
  return true if /\A::([\dA-Fa-f]{1,4}(:[\dA-Fa-f]{1,4})*:)?/ =~ addr && valid_ipv4?($')
  false
end