发送XML请求后捕获EPP服务器响应

时间:2020-04-04 06:44:49

标签: php epp

当前,我们正在开发域注册器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);

在重写XML请求之后

<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给出问候消息,而不是有关登录请求的状态码。 这种读取响应的方法正确吗?不能从服务器获得正确答案的原因可能是什么?谢谢

1 个答案:

答案 0 :(得分:2)

EPP无法像您所想的那样工作。首先,我建议您花一些时间阅读RFC5730和5734的详细信息,下面将对它们进行讨论。完成这项工作后,您将需要全面了解RFC 5731 5732和5733中的所有EPP详细信息。不要指望能够在不阅读所有EPP客户端的情况下编写成功的EPP客户端。

现在回到您的问题,以及为什么不遵循EPP规范。

请参见RFC5730的第2节,此处转载:

          |
          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     |
                                       +-----------------+

不同的说法是,客户的行为方式:

  • 建立TLS连接(绝对确保验证远程证书)
  • 服务器首先讲话,并带有问候消息(请参阅同一RFC的2.4节)
  • 您需要阅读此消息,并从中提取各个部分,尤其是objURI和extURI部分
  • 现在客户端发送其登录消息(第2.9.1.1节)
  • 与其他命令一样,该命令将使服务器响应并给出一个结果,告诉您它是否成功(代码1000)或是否失败(任何以2开头的代码)

程序中的错误是您的客户端先讲话,然后向服务器发送内容。根据上述状态架构,不允许这样做,因此请先阅读服务器回复。

因此,您的问题不是专门发送XML(尽管您也对此有疑问,请参见下文),首先,它不尊重谁先讲话的顺序。

也不要这样做:

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

请参阅说明如何传输EPP的RFC 5734。总而言之,也要回答上面关于您的问题的一些评论:

  • EPP使用TLS作为传输方式,没有HTTPS
  • 700是要联系的由IANA分配的标准端口,除非注册表另有说明
  • 每条XML消息(在UTF-8序列化后等)在线路上都以4个字节为前缀,表示整个EPP帧的长度(frame = 4字节长度的头+完整消息)。

因此,当您希望收到服务器发出的消息时,您会像这样:

  • 您读取了前4个字节(甚至可能不是那么简单,您可能需要一个一个地读取它们并将它们整理回去,这在很大程度上取决于用于网络I / O的工具)
  • 现在您确切知道接下来要到达的大小,它是上述4个字节(正确解码)中的长度减去4,以便从全长中删除4个字节。

但是请注意,将内容发送到服务器时,您需要执行相同的操作! 不要仅发送XML内容,而是需要形成适当的EPP框架,这意味着:

  • 计算消息的大小(以字节为单位,而不是以字符为单位,因此在进行UTF-8序列化后,等等)
  • 准备4个字节以网络字节顺序存储该大小
  • 立即发送这4个字节,后跟XML消息。

此外,作为行业中的“老将”(已经存在20年了,既编写了EPP服务器,客户端,也参与了定义它的RFC),请从经验中采纳此建议:如果您的工作是是只连接到一个注册表,那么生活很简单;您甚至可以使用注册表提供的工具箱,只要它是您选择的语言即可。

但是,一旦您需要编写一个能够正确连接到多个注册表的客户端,便会遭受痛苦。即使这是一个标准,您也会在注册表中发现很多变化,涉及许多主题,例如如何报告扩展的错误数据,存在哪些EPP扩展名以及它们如何工作,域响应的内容:检查等,细微差别的列表在这里只列举了很久。

由于上述所有原因,您可能也不想重新发明轮子。 有一些库可以为您完成所有EPP任务,例如https://github.com/centralnic/php-epp 我不以任何方式认可它,因为我不使用PHP,所以我不知道它,但是也许可以帮到您,要么按原样重用,以便您没有编写代码,或者至少看看看看他们是如何解决特定问题的,以便您受到启发。

例如上述有关长度和每个EPP帧开头的4个字节的问题,请参见https://github.com/centralnic/php-epp/blob/master/Net/EPP/Protocol.php中的getFramesendFrame