The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

Name

Net::EPP::Registry::Nominet - a simple client interface to the Nominet EPP service

Synopsis

        use strict;
        use warnings;
        use Net::EPP::Registry::Nominet;

        my $epp = Net::EPP::Registry::Nominet->new (
                user  =>  'MYTAG',
                pass  =>  'mypass'
        ) or die ('Could not login to EPP server: ', $Net::EPP::Registry::Nominet::Message);

        my $dom = 'foo.co.uk';
        
        if ($epp->check_domain($dom) == 1) {
                print "Domain $dom is available\n" ;
        } else {
                my $info = $epp->domain_info($dom);
                my $res  = $epp->renew_domain ({
                        name         => $dom,
                        cur_exp_date => $info->{exDate},
                        period       => 5
                });
                if ($res) {
                        print "$dom renewed; new expiry date is $res\n";
                } else {
                        warn "Unable to renew $dom: " . $epp->get_reason;
                }
        }

Description

Nominet is the organisation in charge of domain names under the .uk TLD. Historically it used cryptographically signed email communications with registrars to provision domains. More recently (since 2010) it has instituted an EPP system which is sufficiently different from standard EPP that none of the standard modules will work seamlessly with it.

This module exists to provide a client interface to the Nominet EPP servers. It is a subclass of Net::EPP::Simple and aims to adhere closely to that interface style so as to act as a drop-in replacement.

Constructor

        my $epp = Net::EPP::Registry::Nominet->new (
                user  =>  'MYTAG',
                pass  =>  'mypass'
        ) or die ('Could not login to EPP server: ', $Net::EPP::Registry::Nominet::Message);

The constructor for Net::EPP::Registry::Nominet has the same general form as the one for Net::EPP::Simple, but with the following exceptions:

  • If test or ote is set but testssl is not, port defaults to 8700

  • host will be set to the appropriate endpoint. Specify ote with value 1 to connect to the OT&E endpoint, test with value 1 for the testbed endpoint and none of these for the standard live endpoint.

  • timeout defaults to 5 (seconds).

  • debug specifies the verbosity. 0 = almost silent, 1 = displays warnings/errors, 2 = displays EPP frames in over-ridden methods. Default is 0.

  • def_years changes the default number of years for registrations and renewals from the system default of 2. This is only used if no explicit number of years is given in each registration or renewal command. It must be an integer between 1 and 10 inclusive (but note that renewing for 10 years pre-expiry will always fail because Nominet prohibits it).

  • login_opt is a hashref of options to be passed directly through as the third optional argument to login().

  • There is no facility for a config file but this may be added in future versions.

  • There is no facility for supplying SSL client certificates because there is no support for them in the Nominet EPP server.

Login

The client can perform a standalone EPP Login if required.

        $epp->login ($username, $password, $opt_ref)
                or die ("Could not login: ", $epp->get_reason);

The optional third argument, $opt_ref, is a hash ref of login options. Currently the only supported option is 'tag_list' which should be set to a true value if the user needs to use the list_tags method. Nominet operates a number of mutually exclusive schemas so that the user needs to login again to perform different tasks. At present this module only supports two sets: the standard tasks and the tag list.

Availability checks

The availability checks work similarly to Net::EPP::Simple except that in list context they return an array with two elements. The first element is the availability indicator as before (0 if provisioned, 1 if available, undef on error) and the second element is the abuse counter which shows how many more such checks you may run. This counter is only relevant for check_domain and will always be undef for the other check methods.

        # List context
        my ($avail, $left) = $epp->check_domain ("foo.uk");
        ($avail) = $epp->check_contact ("ABC123");
        ($avail) = $epp->check_host ("ns0.foo.co.uk");

        # Scalar context
        $avail = $epp->check_domain ("foo.uk");
        $avail = $epp->check_contact ("ABC123");
        $avail = $epp->check_host ("ns0.foo.co.uk");

Domain Renewal

You can renew an existing domain with the renew() command.

        my $new_expiry = $epp->renew ({
                name          => $domstr,
                cur_exp_date  => $old_expiry,
                period        => $years
        });

On success, $new_expiry contains the new expiry date in long form. Otherwise returns undef.

$domstr is just the domain as a string, eg. "foo.co.uk".

If you do not specify the old expiry date in your request, the system will attempt to retrieve it from the registry first. It should be in the form YYYY-MM-DD.

$years must be an integer between 1 and 10 inclusive and defaults to any value specified in the constructor or 2 otherwise. 10 year renewals must be post-expiry.

Domain Unrenewal

You can unrenew a list of recently renewed domains with the unrenew() command.

        my $new_expiry = $epp->unrenew ($domstr, $domstr2, ... )

On success, $new_expiry is a hashref with the domain names as keys and the new expiry dates in long form as the values. Otherwise returns an empty hashref or undef on complete failure.

$domstr, $domstr2 are just the domains as a string, eg. "foo.co.uk".

Release domains

To transfer a domain to another registrar, use the release_domain method. Returns 1 on success (including success pending handshake), 0 on failure

        my $res = $epp->release_domain ('foo.co.uk', 'OTHER_TAG');
        if ($res) {
                if ($epp->get_code == 1001) {
                        warn "Handshake pending\n";
                }
        } else {
                warn "Could not release $dom: ", $epp->get_reason;
        }

Create objects

Standard EPP allows the creation of domains, contacts and hosts (nameservers). The same is true of Nominet's version, with several differences.

Register domains

To register a domain, there must already be a registrant in the system. You will need to create a hashref of the domain like this to perform the registration.

        my $domain = {
                name         => "foo.co.uk",
                period       => "5",
                registrant   => "ABC123",
                nameservers  => {
                        'nsname0'  => "ns1.bar.co.uk",
                        'nsname1'  => "ns2.bar.co.uk"
                },
                secDNS       => [
                        {
                                keyTag     => 123,
                                alg        => 5,
                                digestType => 1,
                                digest     => '8A9CEBB665B78E0142F1CEF47CC9F4205F600685'
                        }
                ]
        };
        my ($res) = $epp->create_domain ($domain);
        if ($res) {
                print "Expiry date of new domain: $res->{expiry}\n";
        } else {
                warn "Domain not registered: ", $epp->get_reason, "\n";
        }

It returns undef on failure, 1 on success in scalar context and a hashref on success in list context. Only the keys "expiry" and "regid" in this hashref are populated so far.

To register a new domain to a new registrant you can either create the registrant first to get the ID or you can replace the 'registrant' value in the $domain with a hashref of the registrant and create_domain() will create the registrant first as a handy shortcut.

The alias register() can be used in place of create_domain().

Register accounts

To register an account, you will need to create a hashref of the account like this to perform the registration.

        my $registrant = {
                'id'          => "ABC123",
                'name'        => 'Example Company',
                'trad-name'   => 'Examples4u',
                'type'        => 'LTD',
                'co-no'       => '12345678',
                'disclose'    => {
                        'org'  => 1,
                        'addr' => 0
                },
                'postalInfo'  => { loc => {
                        'name'  => 'Arnold N Other',
                        'org'   => 'Example Company',
                        'addr'  => {
                                'street'  => ['555 Carlton Heights'],
                                'city'    => 'Testington',
                                'sp'      => 'Testshire',
                                'pc'      => 'XL99 9XL',
                                'cc'      => 'GB'
                        }
                }},
                'voice'  => '+44.1234567890',
                'email'  => 'a.n.other@example.com'
        };
        my $res = $epp->create_contact ($registrant) or die $epp->get_reason;

It returns undef on failure, 1 on success. The new id must be unique (across the entire registry) otherwise the creation will fail. If no id is specified a random one will be used instead and can subsequently be extracted as $registrant->{id} in the calling code.

Register nameservers

To register a nameserver:

        my $host = {
                name   => "ns1.foo.co.uk",
                addrs  => [
                        { ip => '10.2.2.1', version => 'v4' },
                ],
        };
        my ($res) = $epp->create_host ($host);

It returns undef on failure or 1 on success.

Modify objects

The domains, contacts and hosts once created can be modified using these methods.

Modify domains

To modify a domain, you will need to create a hashref of the changes like this:

        my $changes = {
                'name'         => 'foo.co.uk',
                'add'          => { ns => ['ns1.newhost.com', 'ns2.newhost.com'] },
                'rem'          => { ns => ['ns1.oldhost.net', 'ns2.oldhost.net'] },
                'chg'          => {},
                'auto-bill'    => 21,
                'auto-period'  => 5,
                'next-bill'    => '',
                'notes'        => ['A first note', 'The second note']
        };
        my $res = $epp->update_domain ($changes) or die $epp->get_reason;

This example adds and removes nameservers using the add and rem groups. You cannot use chg to change nameservers or extension fields. The chg entry is only used to move a domain between registrants with the same name.

The add and rem groups are also used to add and remove DS records. eg:

        my $changes = {
                'name'         => 'foo.co.uk',
                'add'          => {
                        secDNS => [{ 
                                keyTag     => 25103,
                                alg        => 5,
                                digestType => 1,
                                digest     => '8A9CEBB665B78E0142F1CEF47CC9F4205F600685'
                        }]
                },
                'rem'          => {}
        };

The extension fields can only be set outside of the add, rem and chg fields. The supported extensions in this module are: auto-bill, auto-period, next-bill, next-period, renew-not-required and notes. All of these are scalars aside from notes which is an array ref.

update_domain() returns undef on failure, 1 on success.

There is also a convenience method modify_domain() which takes the domain name as the first argument and the hashref of changes as the second argument.

Modify contacts

To modify a contact, which includes aspects of the registrant such as the disclose flags etc., you will again need to create a hashref of the changes like this:

        my $changes = {
                'id'          =>  'ABC123',
                'type'        =>  'FCORP',
                'trad-name'   =>  'American Industries',
                'co-no'       =>  '99998888',
                'postalInfo'  => {
                        'loc' => {
                                'name' => 'James Johnston',
                                'addr' => {
                                        'street'  => ['7500 Test Plaza', 'Testingburg'],
                                        'city'    => 'Testsville',
                                        'sp'      => 'Testifornia',
                                        'pc'      => '99999',
                                        'cc'      => 'US',
                                }
                        }
                },
                'voice' =>      '+1.77777776666',
                'email' =>      'jj@example.com',
                'disclose' => {
                        'addr' => 1
                }
        };
        my $res = $epp->update_contact ($changes) or die $epp->get_reason;

Note that this differs from the syntax of Net::EPP::Simple where that takes the stock add, rem and chg elements.

It returns undef on failure, 1 on success.

There is also a convenience method modify_contact() which takes the contact id as the first argument and the hashref of changes as the second argument.

Note that due to an undocumented restriction in Nominet's EPP servers it is not possible to modify the disclose flags for both addr and org to different values in one request.

Modify nameservers

To modify a nameserver, you will need to create a hashref of the changes like this:

        my $changes = {
                name =>  'ns1.foo.co.uk',
                add  =>  { 'addr' => [ { ip => '192.168.0.51', version => 'v4' } ] },
                rem  =>  { 'addr' => [ { ip => '192.168.0.50', version => 'v4' } ] },
        };
        my $res = $epp->update_host ($changes) or die $epp->get_reason;

This operation can only be used to add and remove ip addresses. The chg element is not permitted to change addresses, so it is likely that only the add and rem elements will ever be needed.

It returns undef on failure, 1 on success.

There is also a convenience method modify_host() which takes the host name as the first argument and the hashref of changes as the second argument.

Querying objects

The interface for querying domains, contacts and hosts is the same as for Net::EPP::Simple with the addendum that authinfo is not used at Nominet so can be ignored. The interface is simply:

        my $domhash = $epp->domain_info($domainname);
        my $fulldomhash = $epp->domain_info($domainname, undef, $follow);
        my $conthash = $epp->contact_info ($contid);
        my $hosthash = $epp->host_info ($hostname);

List Domains

Nominet allows listing domains either by registration date (ie. creation date) or expiry date. The date must be a month in the form YYYY-MM. eg.

        my $domlist = $epp->list_domains ('2019-01', 'expiry');

will list all the domains expiring in January 2019 as an arrayref. It will return an empty array ref if there are no matches and undef on error. The second argument can only be 'expiry' or 'month' (for creation date). If it is not supplied, the default is 'expiry'.

List Tags

When transferring domains it may be useful to have a list of possible tag names. This method returns the full list of tags as an array ref. Each entry in the arrayref is itself a hashref with these keys:

registrar-tag is the tag name to use in release actions, etc.
name is the name of the registrar for display purposes
trad-name is the trading name of the registrar (may be empty string)
handshake is "Y" if they require handshakes on transfer or "N" otherwise
        my $taglist = $epp->list_tags;

It accepts no arguments and returns undef on error.

Note that you must have passed the tag_list option to login() in order to use this method.

Hello

EPP allows the use of a "hello" operation which effectively tests that the connection to the EPP server is still active and also serves to reset any inactivity timeout which the server might apply. Nominet's documentation seems to indicate a 60 minute timeout (as at August 2013).

        my $res = $epp->hello ();
        

The hello method takes no arguments. It returns 1 on success, undef otherwise.

This performs much the same function as the ping method of Net::EPP::Simple (which could be used instead) but provides more extensive error handling.

Utility methods

The following utility methods are used internally but are described here in case they are useful for other purposes.

spec

This utility method takes a 'type' argument and returns a three-valued array of type, XMLNS and XSI for use with various frame and XML routines. It is not expected to be called independently by the user but is here if you need it.

Type can currently be one of: domain, contact, contact-ext, host, l (for list), u (for unrenew), r (for release)

        my @spec = $epp->spec ('domain');

valid_voice

The valid_voice method takes one argument which is a string representing a telephone number and returns 1 if it is a valid string for the "voice" field of a contact or undef otherwise.

        unless ($epp->valid_voice ($phone)) {
                die "The phone number $phone is not in a valid format.";
        }

random_id

The random_id method takes an integer as its optional argument and returns a random string suitable for use as an ID. When creating a new contact an ID must be supplied and it must not be globally unique within the registry (not just within the TAG). This method is used to generate one of 26339361174458854765907679379456 possible 16-character IDs, rendering clashes less likely that winning the Lottery two weeks running (ie. good enough FAPP).

        my $almost_unique_id = $epp->random_id (16);

The length defaults to 16 if not supplied. RFC 5730 specifies that this is the maximum length for a contact ID.

Accessors

The following accessors may be used to extract diagnostic information from the EPP object:

        my $code    = $epp->get_code;
        my $error   = $epp->get_error;
        my $msg     = $epp->get_message;
        my $reason  = $epp->get_reason;

The first three of these just provide an OO interface to $Code, $Error and $Message respectively. The user should use these in preference to the explicit variable names except in the specific instance of a login or connection failure when no epp object will be returned.

TODO

  • The poll, fork, handshake, lock and reseller operations are not yet supported.

  • Much more extensive tests should be performed.

See Also

Author

Pete Houston <cpan@openstrike.co.uk>

Licence

This software is copyright © 2013-2019 by Pete Houston. It is released under the Artistic Licence (version 2) and the GNU General Public Licence (version 2).