Whois Parser

The Whois::Record::Parser object, as the name suggest, actually belongs to the Whois::Record namespace. However, this element is such important to deserve its own documentation section.

The parser architecture allows you to access the WHOIS responses as an object. The parsers decompose the response String and offers a standardized API to access its content. For instance, to get the date the record has been created, just call the created_on method, no matter you are querying the .com or .net database.

# Example:  Accessing a WHOIS record, the object oriented way

r = Whois.whois("google.it")
r.created_on
# => Fri Dec 10 00:00:00 +0100 1999
r.expires_on
# => Sat Nov 27 00:00:00 +0100 2010

The Problem with WHOIS Record Parsing

As explained in the Whois::Record section, an record is composed by one or more parts. Each part represents the response from a specific WHOIS server. Because the WHOIS doesn't force WHOIS servers to follow an unique response layout, each server needs its own dedicated parser.

Have a look at the following records to better understand the differences.

*********************************************************************
* Please note that the following result could be a subgroup of      *
* the data contained in the database.                               *
*                                                                   *
* Additional information can be visualized at:                      *
* http://www.nic.it/cgi-bin/Whois/whois.cgi                         *
*********************************************************************

Domain:             google.it
Status:             ACTIVE
Created:            1999-12-10 00:00:00
Last Update:        2009-12-13 00:03:01
Expire Date:        2010-11-27

Registrant
  Name:             Google Ireland Holdings
  Organization:     Google Ireland Holdings
  ContactID:        GOOG175-ITNIC
  Address:          30 Herbert Street
                    Dublin
                    2
                    IE
                    IE
  Created:          2008-11-27 16:47:22
  Last Update:      2008-11-27 16:47:22
Whois Server Version 2.0

Domain names in the .com and .net domains can now be registered
with many different competing registrars. Go to http://www.internic.net
for detailed information.

   Domain Name: AISCUNEO.COM
   Registrar: GODADDY.COM, INC.
   Whois Server: whois.godaddy.com
   Referral URL: http://registrar.godaddy.com
   Name Server: NS1.DREAMHOST.COM
   Name Server: NS2.DREAMHOST.COM
   Name Server: NS3.DREAMHOST.COM
   Status: clientDeleteProhibited
   Status: clientRenewProhibited
   Status: clientTransferProhibited
   Status: clientUpdateProhibited
   Updated Date: 21-jan-2010
   Creation Date: 24-jan-2009
   Expiration Date: 24-jan-2011

>>> Last update of whois database: Fri, 05 Mar 2010 21:53:17 UTC <<<

Moreover, if you consider the Thin data model, a single .com query often requires at least two parsers. One for the Verisign request and one for the final registrar.

I know what you are thinking and the record is yes: in order to support all existing WHOIS servers and registrars, the Whois library should provide more than 500 different parsers. And this is exactly one of the major development goal. Unfortunately, as you might imagine, this requires a huge development effort.

God save the Standards!

Basics

The Whois::Record::Parser object acts as a proxy between the Whois::Record object and the Whois::Record::Part-level parsers.

As explained in the section above, a Whois::Record is composed by one or more parts and each Whois::Record::Part has its own custom parser. Whois::Record::Part-level parsers must implements the Whois::Record::Parser::Base abstract class.

Parsers define a standard API and two parsers can actually overlaps. For instance, if a .com response contains two parts and each parser supports the nameservers property, then you need an element to forward the request to the most appropriate endpoint. That's the Whois::Record::Parser.

The Whois::Record::Parser knows about all Whois::Record::Part-level parsers and automatically returns the most appropriate value for a specific property.

# Example:  Using the Whois::Record::Parser to access the Whois::Record data

r = Whois.whois("google.it")
p = r.parser

p.created_on
# => Fri Dec 10 00:00:00 +0100 1999
p.expires_on
# => Sat Nov 27 00:00:00 +0100 2010

Properties

The Whois::Record::Parser defines a specific list of record properties. The list is designed in order to include the most common information available in a WHOIS response. Whois::Record::Parser::PROPERTIES returns the current list.

# Example:  Getting the list of defined record properties

Whois::Record::Parser::PROPERTIES
=> [:disclaimer, :domain, :domain_id, :referral_whois, :referral_url, :status, :registered?, :available?, :created_on, :updated_on, :expires_on, :registrar, :registrant, :admin, :technical, :nameservers]

Here's the full list of available properties.

You can access each of this property using the corresponding instance method.

# Example:  Accessing record properties using the instance methods

r = Whois.whois("google.it")
p = r.parser

p.disclaimer
# => "..."
p.domain_id
# => nil

The method can either return the value or raise a Whois::ParserError exception, depending whether the value is supported or not.

Returned Values

When you access a property, you can expect two different behaviors:

  • the method value
  • an exception

The first case is the most simple. If at least one Whois::Record::Part-level parser supports the specified property, then the the last parser which implements it wins and the value is returned.

Have a look at the following examples.

# Example:  The Record contains two parts corresponding to the responses
            from the following WHOIS servers:
            whois.foo.com supports #domain, #domain_id
            whois.bar.com supports #domain, #disclaimer

p = Whois.whois("google.it").parser

# both parsers support the property, last value returned
p.domain
# => value from whois.bar.com

# the first parser supports the property, value returned
p.domain_id
# => value from whois.foo.com

# the second parser supports the property, value returned
p.disclaimer
# => value from whois.bar.com

# the property is not supported, exception raised
p.created_on

The last statement raises an Exception because the property is not supported nor not available. There's a slight difference between a not supported and not available property. View the (yet to come) Whois::Record::Parser::Base base for a detailed explanation.

For now, you just need to know that a property might raise 2 different exceptions according to its flag:

  • Whois::AttributeNotImplemented
  • Whois::AttributeNotSupported

All these exceptions inherits from Whois::ParserError.

Record#property vs Parser#property

As you probably already noticed from the examples you read so far, you can either access an record property from the Whois::Record object or from the underlying Whois::Record::Parser proxy.

# Example: Accessing an record properties from
# Whois::Record and Whois::Record::Parser objects

r = Whois.whois("google.it")

r.created_on
# => Fri Dec 10 00:00:00 +0100 1999

r.parser.created_on
# => Fri Dec 10 00:00:00 +0100 1999

The values are absolutely equivalent.

# Example: Demonstrating the equality between
# Whois::Record and Whois::Record::Parser properties

r = Whois.whois("google.it")

r.created_on == a.parser.created_on
# => true

However, there's a technical difference between the two methods.

In the section above you learnt a property can either return a value or raise an exception. You can check whether a property is supported or not using the #property_supported? method.

# Example: Checking if the property is supported

r = Whois.whois("google.it")

r.property_supported?(:domain)
# => false

r.parser.property_supported?(:domain)
# => false

The main difference between accessing a property at Whois::Record level or Whois::Record::Parser level is that the former never raises exceptions. Instead, if a property is not supported the Whois::Record silently returns nil.

# Example: Checking if the property is supported

r = Whois.whois("google.it")

r.domain
# => nil

r.parser.domain
# => AttributeNotSupported

Usually, it's more convenient to access a property at record level unless you need a more granular control.