The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Net::EPP::Server - A simple EPP server implementation.

VERSION

version 0.001

SYNOPSIS

    use Net::EPP::Server;
    use Net::EPP::ResponseCodes;

    #
    # these are the objects we want to support
    #
    my @OBJECTS = qw(domain host contact);

    #
    # these are the extensions we want to support
    #
    my @EXTENSIONS = qw(secDNS rgp loginSec allocationToken launch);

    #
    # You can pass any arguments supported by Net::Server::Proto::SSL, but
    # by default the server will listen on localhost port 7000 using a
    # self-signed certificate.
    #
    Net::EPP::Server->new->run(

        #
        # this defines callbacks that will be invoked when an EPP frame is
        # received
        #
        handlers => {
            hello   => \&hello_handler,
            login   => \&login_handler,
            check   => \&check_handler,
            info    => \&info_handler,
            create  => \&create_handler,

            # add more here
        }
    );

    #
    # The <hello> handler is special and just needs
    # to return a hashref containing server metadata.
    #
    sub hello_handler {
        return {
            # this is the server ID and is optional, if not provided the system
            # hostname will be used
            svID => 'epp.example.com',

            # this is optional
            lang => [ qw(en fr de) ],

            # these are arrayrefs of namespace URIs
            objects => [
                map { Net::EPP::Frame::ObjectSpec->xmlns($_) } @OBJECTS
            ],

            extensions => [
                map { Net::EPP::Frame::ObjectSpec->xmlns($_) } @EXTENSIONS
            ],
        };
    }

    #
    # All other handlers work the same. They are passed a hash of arguments and
    # can return a simple result code, a result code and message, a
    # XML::LibXML::Document object, or a result code and an array of
    # XML::LibXML::Element objects.
    #
    sub login_handler {
        my %args = @_;

        my $frame = $args{'frame'};

        my $clid = $frame->getElementsByTagName('clid')->item(0)->textContent;
        my $pw = $frame->getElementsByTagName('pw')->item(0)->textContent;

        if (!validate_credentials($clid, $pw)) {
            return AUTHENTICATION_FAILED;

        } else {
            return OK;

        }  
    }

INTRODUCTION

Net::EPP::Server provides a high-level framework for developing Extensible Provisioning Protocol (EPP) servers.

It implements the TLS/TCP transport described in RFC 5734, and the EPP Server State Machine described in Section 2 of RFC 5730.

SERVER CONFIGURATION

Net::EPP::Server inherits from Net::Server (specifically Net::Server::PreFork), and so the run() method accepts all the parameters supported by that module, plus the following:

  • handlers, which is a hashref which maps events (including EPP commands) to callback functions. See below for details.

  • timeout (optional), which is how long (in seconds) to wait for a client to send a command before dropping the connection. This parameter may be a decimal (e.g. 3.14) or an integer (e.g. 42). The default timeout is 30 seconds.

  • client_ca_file (optional), which is the location on disk of a file which can be use to validate client certificates. If this parameter is not provided, clients will not be required to use a certificate.

  • xsd_file (optional), which is the location on disk of an XSD file which should be used to validate all frames received from clients. This XSD file can include other XSD files using <import>.

EVENT HANDLERS

You implement the business logic of your EPP server by specifying callbacks that are invoked for certain events. These come in two flavours: events and commands.

frame_received

Called when a frame has been successfully parsed and validated, but before it has been processed. The input frame will be passed as the frame argument.

response_prepared

Called when a response has been generated, but before it has been sent back to the client. The response will be passed as the response argument, while the input frame will be passed as the frame argument. It is not called for <hello> and <logout>commands.

session_closed

Net::EPP::Server takes care of handling session management, but this event handler will be called once a <logout> command has been successfully processed, but before the client connection has been closed. The session argument will contain a hashref of the session (see below).

hello

The hello event handler is called when a new client connects, or a <hello> frame is received.

Unlike the other event handlers, this handler MUST respond with a hashref which contains the following entries:

  • svID (OPTIONAL) - the server ID. If not provided, the system hostname will be used.

  • lang (OPTIONAL) - an arrayref containing language codes. It not provided, en will be used as the only supported language.

  • objects (REQUIRED) - an arrayref of namespace URIs for

COMMAND HANDLERS

The standard EPP command repertoire is:

  • login

  • logout

  • poll

  • check

  • info

  • create

  • delete

  • renew

  • transfer

  • delete

A command handler may be specified for all of these commands except logout, since Net::EPP::Server handles this itself.

Since EPP allows the command repertoire to be extended (by omitting the <command> element and using the <extension> element only), Net::EPP::Server also supports the other event which will be called when processing such frames.

All command handlers receive a hash containing the following arguments:

  • $server - the server.

  • event - the name of the command.

  • frame - an XML::LibXML::Document object representing the frame received from the client.

  • session - a hashref containing the session information.

  • clTRID - the value of the <clTRID> element taken from the frame received from the client.

  • svTRID - a value suitable for inclusion in the <clTRID> element of the response.

SESSION PARAMETERS

As mentioned above, the $args{session} parameter is a hashref which contains information about the session. It contains the following:

  • session_id - a unique session ID.

  • remote_addr - the client's remote IP address (IPv4 or IPv6).

  • remote_port - the client's remote port.

  • clid - the client ID used to log in.

  • lang - the language specified at login.

  • objects - the object URI(s) specified at login.

  • lang - the extension URI(s) specified at login.

  • client_cert - information about the client certificate (if any). This is a hashref which looks something like this:

        {
          'issuer' => $dnref,
          'common_name' => 'example.com',
          'subject' => $dnref,
        }

    $dnref is a hashref representing the Distinguished Name of the issuer or subject and looks like this:

        {
            'O' => 'Example Inc.',
            'OU' => 'Registry Services',
            'emailAddress' => 'registry@example.com',
            'CN' => 'EPP Server Private CA',
        }

    Other members, such as C (country), ST (state/province), and L (city) may also be present.

RETURN VALUES

Command handlers can return result information in four different ways that are explained below.

1. SIMPLE RESULT CODE

Command handlers can signal the result of a command by simply passing a single integer value. Net::EPP::ResponseCodes may be used to avoid literal integers.

Example:

    sub delete_handler {
        my %args = @_;

        # business logic here

        if ($success) {
            return OK;

        } else {
            return COMMAND_FAILED;

        }
    }

Net::EPP::Server will construct a standard EPP response frame using the result code and send it to the client.

2. RESULT CODE + MESSAGE

If the command handler returns two values, and the first is a valid result code, then the second can be a message. Example:

    sub delete_handler {
        my %args = @_;

        # business logic here

        if ($success) {
            return (OK, 'object deleted');

        } else {
            return (COMMAND_FAILED, 'object not deleted');

        }
    }

Net::EPP::Server will construct a standard EPP response frame using the result code and message, and send it to the client.

3. RESULT CODE + XML ELEMENTS

The command handler may return a result code followed by an array of between one and three XML::LibXML::Element objects, in any order, representing the <resData>, <msgQ> and <extension> elements. Example:

    sub delete_handler {
        my %args = @_;

        # business logic here

        return (
            OK,
            $resData_element,
            $msgQ_element,
            $extension_element,
        );
    }

Net::EPP::Server will construct a standard EPP response frame using the result code and supplied elements which will be imported and inserted into the appropriate positions, and send it to the client.

4. XML::LibXML::Document OBJECT

A return value that is a single XML::LibXML::Document object will be sent back to the client verbatim.

EXCEPTIONS

Net::EPP::Server will catch any exceptions thrown by the command handler, will carp($@), and then send a 2400 result code back to the client.

UTILITY METHODS

generate_response(%args)

This method returns a XML::LibXML::Document object representing the response described by %args, which should contain the following:

  • code (OPTIONAL) - the result code. See Net::EPP::ResponseCodes. If not provided, 1000 will be used.

  • msg - a human-readable error message. If not provided, the string "Command completed successfully." will be used if code is less than 2000, and "Command failed." if code is 2000 or higher.

  • resData (OPTIONAL) - if defined, an empty <resData> element will be added to the frame.

  • clTRID (OPTIONAL) - the client transaction ID.

  • svTRID (OPTIONAL) - the server's transaction ID.

Once created, it is straightforward to modify the object to add, remove or change its contents as needed.

generate_error(%args)

This method is identical to generate_response() except the default value for the code parameter is 2400, indicating that the command failed for unspecified reasons.

generate_svTRID()

This method returns a unique string suitable for use in the <svTRID> and similar elements.

parse_frame($xml)

Attempts to parse $xml and returns a XML::LibXML::Document if successful.

is_valid($frame)

Returns a result code and optionally a message if $frame cannot be validated against the XSD file provided in the xsd_file parameter.

is_result_code($value)

Returns true if $value is a recognised EPP result code.

AUTHOR

Gavin Brown <gavin.brown@icann.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2024 by Internet Corporation for Assigned Names and Number (ICANN).

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.