0

目前我们正在开发域名注册商 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 提供问候消息,而不是有关登录请求的状态代码。这种阅读回复的方法对吗?没有从服务器获得正确答案的原因可能是什么?谢谢

4

1 回答 1

2

EPP 不像你想象的那样工作。首先,我建议您花一些时间详细阅读 RFC5730 和 5734,它们将在下面讨论。完成这项工作后,您将需要全面掌握 RFC 5731 5732 和 5733 中的所有 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 帧的长度(帧 = 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 个字节的上述问题,getFramesendFrame参见https://github.com/centralnic/php-epp/blob/master/Net/EPP/Protocol.php

于 2020-04-07T03:18:45.837 回答