Catch EPP server response after sending XML request

557 views Asked by At

Currently we are developing domain registrar API.

    $options = [
        'ssl' => [
            'verify_peer' => true,
            'local_cert' => __DIR__ . '/Domain.pem',
            'local_pk' => __DIR__ . '/Domain.pem',
            'allow_self_signed' => true,
        ]
    ];
    $context = stream_context_create($options);
    $ch = stream_socket_client($serverPath.':'.$parentClass->port, $errorNumber, $errorString, 60, STREAM_CLIENT_CONNECT, $context);
    stream_set_timeout($ch, 60);

    fwrite($ch, $command);

    $data = '';

    while (!feof($ch)) {
        $data .= fread($ch, 1024);
    }

    fclose($ch);

After fwriting XML request

<epp xsi:schemaLocation="urn:ietf:params:xml:ns:epp-1.0 epp-1.0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<login>
<clID>User</clID>
<pw>Password</pw>
<options>
<version>1.0</version>
<lang>en</lang>
</options>
<svcs>
<objURI>urn:ietf:params:xml:ns:obj1</objURI>
<objURI>urn:ietf:params:xml:ns:obj2</objURI>
<objURI>urn:ietf:params:xml:ns:obj3</objURI>
<svcExtension>
<extURI>http://custom/obj1ext-1.0</extURI>
</svcExtension>
</svcs>
</login>
<clTRID>ABC-12345</clTRID>
</command>
</epp>

fread gives greeting message, not status code about login request. Is this method of reading the response right? What can be a reason not getting the right answer from the server? Thanks

1

There are 1 answers

0
Patrick Mevzek On BEST ANSWER

EPP does not work like you think. As a start, I recommend you take some time to read RFC5730 and 5734 in full details, they are discussed below. After you made that work, you will need a full grasp of all EPP details in RFCs 5731 5732 and 5733. Do not expect to be able to write a successful EPP client without having read all of them.

Now back to your problem, and why you are not following EPP specifications.

See section 2 of RFC5730, reproduced here:

          |
          V
  +-----------------+                  +-----------------+
  |   Waiting for   |     Connected    |     Prepare     |
  |      Client     |----------------->|     Greeting    |
  +-----------------+    or <hello>    +-----------------+
     ^                                           |
     | Close Connection                     Send |
     |     or Idle                      Greeting |
  +-----------------+                            V
  |       End       |     Timeout      +-----------------+
  |     Session     |<-----------------|   Waiting for   |
  +-----------------+                  |      Client     |
     ^    ^    ^        Send +-------->|  Authentication |
     |    |    |    Response |         +-----------------+
     |    |    |     +--------------+            |
     |    |    |     | Prepare Fail |            | <login>
     |    |    +-----|   Response   |            | Received
     |    |    Send  +--------------+            V
     |    |    2501          ^         +-----------------+
     |    |   Response       |         |   Processing    |
     |    |                  +---------|     <login>     |
     |    |                  Auth Fail +-----------------+
     |    |       Timeout                         |
     |    +-------------------------------+       | Auth OK
     |                                    |       V
     |   +-----------------+  <hello>  +-----------------+
     |   |     Prepare     |<----------|   Waiting for   |
     |   |     Greeting    |---------->|   Command or    |
     |   +-----------------+   Send    |     <hello>     |
     | Send x5xx             Greeting  +-----------------+
     | Response  +-----------------+  Send    ^  |
     +-----------|     Prepare     | Response |  | Command
                 |     Response    |----------+  | Received
                 +-----------------+             V
                            ^          +-----------------+
                    Command |          |   Processing    |
                  Processed +----------|     Command     |
                                       +-----------------+

Said differently here is how your client should behave:

  • establish the TLS connection (make absolutely sure to verify the remote certificate)
  • the server speaks first, with a greeting message (see section 2.4 of same RFC)
  • you need to read this message, and extract various parts from it, specifically the objURI and extURI part
  • now the client sends its login message (section 2.9.1.1)
  • this command, like others, will make the server reply with a result telling you if it succeeded (code 1000) or if it failed (any code starting with 2)

The error in your program is that your client is speaking first, sending something to the server. This is not allowed per the above state schema, so please read the server reply first.

So your problem is not specifically in sending XML (while you also have a problem on that, see below), first it is not respecting the order of who speaks first.

Also do not do this:

    while (!feof($ch)) {
        $data .= fread($ch, 1024);
    }

See RFC 5734 that explains how EPP is transported. In summary and to reply also to some comments above on your question:

  • EPP uses TLS as a transport, there is no HTTPS
  • 700 is the standard IANA assigned port to contact, except if the registry tells you otherwise
  • each XML message (after UTF-8 serialization, etc.) is prefixed on the wire by 4 bytes that denote the length for the full EPP frame (frame = 4 bytes header of length + full message).

So when you expect a message from server you do it like that:

  • you read the first 4 bytes (this may be not so simple even, you may need to read them one by one and collating them back together, depends a lot on the tools used for the network I/O)
  • now you know exactly the size of what you expect to arrive next, it is the length in the above 4 bytes (decoded properly) minus 4 to remove the 4 bytes themselves from the full length.

But note that you need to do the same when you send content to the server! Do not send just the XML content, you need to form a proper EPP frame which means:

  • count the size of the message (in bytes, not in characters, so after UTF-8 serialization, etc.)
  • prepare 4 bytes to store that size as network byte order
  • send those 4 bytes immediately followed by the XML message.

Also, as a "veteran" in the industry (been there for 20 years, having written both EPP servers, clients, and having participated in the RFCs that define it), please take this advice out of experience: if your job is to connect only to a single registry, then life is simple; you may even be able to use a toolkit provided by the registry, as long as it is in a language of your choice.

However, as soon as you need to write a client being able to properly connect to multiple registries, you will suffer pain. Even if it is a standard you will find A LOT of variations among registries, on many topics such how extended error data is reported, what EPP extensions exist and how they work, the content of responses for a domain:check, and so on, the list of small differences is just to long to enumerate here.

For all the above reasons, you may want not to reinvent the wheel either. There are libraries that do all the EPP stuff for you, for example https://github.com/centralnic/php-epp I am not endorsing it in any way as I do not know it as I do not use PHP, but maybe it could help you, either to just reuse as is so that you do not have code to write, or at least have a look at it to see how they solved specific issues so that you can be inspired.

For example the above issue on the length and the 4 bytes at beginning of each EPP frame, see getFrame and sendFrame in https://github.com/centralnic/php-epp/blob/master/Net/EPP/Protocol.php