我尝试编写函数来处理 SSL 关闭。下面是服务器代码的一部分,我使用非块模式。
int r = SSL_shutdown(ssl);
if (r == 1)
{
return Success;
}
// according to man/SSL_shutdown, 0 means "in progress"
if (r == 0)
{
switch ((r = SSL_get_error(ssl, r)))
{
case SSL_ERROR_WANT_READ:
std::cout << "shutdown want read" << std::endl;
...
return ...;
case SSL_ERROR_WANT_WRITE:
std::cout << "shutdown want write" << std::endl;
...
return ...;
default:
std::cout << r << " " << strerror(errno) << std::endl;
...
return ...;
}
}
这是客户端代码的一部分,我在其中使用阻塞模式。根据男人的说法,我应该调用两次。
if (SSL_shutdown(ssl) == 0)
{
if (SSL_shutdown(ssl) != 1)
{
report_error_and_exit("SSL_shutdown");
}
}
在服务器中,SSL_shutdown 返回 0。根据手册页,SSL_get_error 应该返回 SSL_ERROR_WANT_READ 或 SSL_ERROR_WANT_WRITE。
引用:https://www.openssl.org/docs/manmaster/man3/SSL_shutdown.html
如果底层 BIO 是非阻塞的并且关闭过程尚未完成(例如,因为尚未从对等方收到 close_notify 警报消息,或者因为需要发送 close_notify 警报消息但当前会阻塞),则 SSL_shutdown ()返回0表示关闭过程仍在进行中;在这种情况下,调用 SSL_get_error(3) 将产生 SSL_ERROR_WANT_READ 或 SSL_ERROR_WANT_WRITE。
但是,我从默认情况下打印 r = 5 (SSL_ERROR_SYSCALL)。在这种情况下,该人说应该检查 errno,但 strerror 返回“成功”。一切都是那么的扑朔迷离。
好的,我想我现在可以回答这个问题了,因为我找到了一些更好的文档(好的旧Linux):
https://linux.die.net/man/3/ssl_shutdown
所以,引用那里:
关闭过程包括2个步骤:发送“关闭通知”关闭警报和接收对端的“关闭通知”关闭警报。
根据 TLS 标准,应用程序可以只发送关闭警报,然后关闭底层连接,而不等待对等方的响应(这样可以节省资源,因为进程已经可以终止或服务另一个连接) 。当底层连接用于更多通信时,必须执行完整的关闭过程(双向“关闭通知”警报),以便对等点保持同步。
所以 SSL_shutdown 确实是一个两步过程。然后它接着说(我的评论和细微的澄清编辑):
当应用程序是第一个发送“关闭通知”警报的一方时[我认为,这里就是这种情况],
将仅发送警报,然后设置SSL_shutdown()
标志(以便会话被认为是好的并将保存在缓存中)。SSL_SENT_SHUTDOWN
随后将返回SSL_shutdown()
。如果单向关闭就足够了(即底层连接无论如何都应该关闭),第一次调用0
就足够了SSL_shutdown()
为了完成双向关闭握手,必须再次调用
。第二个调用将使SSL_shutdown()
等待对等方的“关闭通知”关闭警报。成功后,第二次调用SSL_shutdown()
将返回SSL_shutdown()
。1
换句话说,如果您打算处置
ssl
(在发布的代码中),那么调用 SSL_shutdown
一次就足够了(这就是我所做的)。 OTOH,如果您打算再次使用 ssl
,您可能需要调用它两次。
还有一些其他的障碍,但归根结底是,如果
SSL_shutdown
返回 0
,并且你想重用 ssl
,那么你必须再次调用它。如果它返回 1
,则您不会(无论您未来的计划如何 ssl
)。
最后,它这样说:
如果底层 BIO 阻塞,
仅在握手步骤完成或发生错误后才会返回。SSL_shutdown()
我的意思是,对于阻塞套接字,您需要调用它一次。对于非阻塞套接字,您需要更加小心。但是,我对文档的阅读告诉我,对此进行编码的最可靠的方法(如果您打算重用
ssl
)将是:
int rc = SSL_shutdown (ssl);
if (rc == 0)
rc = SSL_shutdown (ssl);
...
但是我永远不会在 OpenSSL 中使用非阻塞套接字,因为有太多陷阱。
很高兴得到明智的头脑对上述任何问题的纠正。谢谢。