boost asio SSL async_shutdown总是以错误结束
boost asio ssl async_shutdown always finishes with an error?
我有一个小ssl客户端,我已经在boost 1.55 asio编程,我试图找出为什么boost::asio::ssl::stream::async_shutdown()
总是失败。该客户端与boost文档中的ssl客户端示例非常相似(几乎相同),因为它经历了一个boost::asio::ip::tcp::resolver::async_resolve()
->boost::asio::ssl::stream::async_connect()
->boost::asio::ssl::stream::async_handshake()
回调序列。所有这些都按预期工作,async_handshake()
回调得到一个完全清除的boost::system::error_code
。
从async_handshake()
回调,我调用async_shutdown()
(我不传输任何数据-这个对象更多的是为了测试握手):
void ClientCertificateFinder::handle_handshake(const boost::system::error_code& e)
{
if ( !e )
{
m_socket.async_shutdown( boost::bind( &ClientCertificateFinder::handle_shutdown_after_success,
this,
boost::asio::placeholders::error ) );
}
else
{
m_handler( e, IssuerNameList() );
}
}
然后调用handle_shutdown_after_success()
,但总是有一个错误?错误是asio.misc
中的value=2,这是"文件结束"。我已经尝试过各种ssl服务器,我似乎总是得到这个asio.misc
错误。这不是一个潜在的openssl错误,这表明我可能在某种程度上滥用asio…?
有人知道为什么会发生这种情况吗?我的印象是关闭与async_shutdown()
的连接是正确的事情,但我想我可以调用boost::asio::ssl::stream.lowestlayer().close()
从openssl下关闭套接字,如果这是预期的方式的话(实际上asio ssl示例似乎表明这是关闭的正确方式)。
对于加密安全关闭,双方必须通过调用shutdown()
或async_shutdown()
并运行io_service
来对boost::asio::ssl::stream
执行关闭操作。如果操作在没有SSL类别的error_code
上完成,并且在部分关闭发生之前没有被取消,则连接被安全关闭,底层传输可以被重用或关闭。简单地关闭最低层可能使会话容易受到截断攻击。
The Protocol and Boost。Asio API
在标准化的TLS协议和非标准化的SSLv3协议中,安全关闭涉及各方交换close_notify
消息。在助推方面。在Asio API中,任何一方都可以通过调用shutdown()
或async_shutdown()
发起关闭,导致close_notify
消息发送给另一方,通知接收者发起者将不再在SSL连接上发送更多消息。根据规范,接收方必须使用close_notify
消息进行响应。提振。Asio不会自动执行此行为,并要求接收方显式调用shutdown()
或async_shutdown()
。
规范允许shutdown的发起者在收到close_notify
响应之前关闭他们的读端连接。这在应用程序协议不希望重用底层协议的情况下使用。不幸的是,提振。Asio目前(1.56)没有直接支持此功能。在提高。Asio,如果出现错误或一方已经发送和接收了close_notify
消息,则认为shutdown()
操作完成。一旦操作完成,应用程序可以重用底层协议或关闭它。
场景和错误码
一旦建立了SSL连接,在关闭时将出现以下错误代码:
- 一方发起关闭,远程方关闭或已经关闭底层传输,而没有关闭协议:
- 启动器的
shutdown()
操作将失败,SSL短读错误。
- 启动器的
- 一方发起关闭,等待远端关闭协议:
- 启动器的关机操作将以错误值
boost::asio::error::eof
完成。 - 远端
shutdown()
操作成功完成。
- 启动器的关机操作将以错误值
- 一方发起关闭,然后关闭底层协议,而不等待远程方关闭协议:
- 启动器的
shutdown()
操作将被取消,导致boost::asio::error::operation_aborted
错误。这是下面详细说明的解决方案的结果。 - 远端
shutdown()
操作成功完成。
- 启动器的
下面详细描述了这些不同的场景。每个场景都用一个类似于泳线的图表来说明,表明每一方在同一时间点正在做什么。
parta在partb未协商关闭连接后调用shutdown()
。
在这个场景中,PartyB违反了关闭过程,在没有首先调用流上的shutdown()
的情况下关闭了底层传输。一旦底层传输被关闭,方a尝试发起一个shutdown()
。
PartyA | PartyB
-------------------------------------+----------------------------------------
ssl_stream.handshake(...); | ssl_stream.handshake(...);
... | ssl_stream.lowest_layer().close();
ssl_stream.shutdown(); |
parta将尝试发送close_notify
消息,但是写入到底层传输的boost::asio::error::eof
将失败。提振。Asio将显式地将底层传输的eof
错误映射为SSL短读错误,因为乙方违反了SSL关闭过程。
if ((error.category() == boost::asio::error::get_ssl_category())
&& (ERR_GET_REASON(error.value()) == SSL_R_SHORT_READ))
{
// Remote peer failed to send a close_notify message.
}
parta调用shutdown()
,然后partb关闭连接,没有协商关闭。
在此场景中,parta发起关机。然而,当乙方收到close_notify
消息时,乙方违反了关闭过程,在关闭底层传输之前从未显式地响应shutdown()
。
PartyA | PartyB
-------------------------------------+---------------------------------------
ssl_stream.handshake(...); | ssl_stream.handshake(...);
ssl_stream.shutdown(); | ...
| ssl_stream.lowest_layer().close();
增加。Asio的shutdown()
操作被认为完成,一旦close_notify
发送和接收或发生错误,parta将发送close_notify
然后等待响应。乙方关闭底层传输,没有发送close_notify
,违反SSL协议。parta的读取将在boost::asio::error::eof
和Boost下失败。Asio将它映射到SSL短读错误。
甲方发起shutdown()
,等待乙方响应shutdown()
。
在此场景中,parta将发起关机,并等待partb响应关机。
PartyA | PartyB
-------------------------------------+----------------------------------------
ssl_stream.handshake(...); | ssl_stream.handshake(...);
ssl_stream.shutdown(); | ...
... | ssl_stream.shutdown();
这是一个相当基本的关闭,其中双方发送和接收close_notify
消息。一旦双方协商关闭,底层传输要么被重用,要么被关闭。
- parta的关机操作将完成,错误值为
boost::asio::error::eof
。 - 乙方关机操作将成功完成。
parta发起shutdown()
,但未等待partb响应。
在此场景中,parta将启动关闭,然后在发送close_notify
后立即关闭底层传输。甲方不等待乙方响应close_notify
消息。这种类型的协商关闭是规范允许的,并且在实现中相当常见。
如上所述,Boost。Asio不直接支持这种类型的关闭。提振。Asio的shutdown()
操作将等待远端对等端发送其close_notify
。然而,在仍然支持规范的情况下实现一个变通方法是可能的。
PartyA | PartyB
-------------------------------------+---------------------------------------
ssl_stream.handshake(...); | ssl_stream.handshake(...)
ssl_stream.async_shutdown(...); | ...
const char buffer[] = ""; | ...
async_write(ssl_stream, buffer, | ...
[](...) { ssl_stream.close(); }) | ...
io_service.run(); | ...
... | ssl_stream.shutdown();
parta将启动异步关机操作,然后启动异步写操作。用于写操作的缓冲区长度必须为非零(上面使用了null字符);否则,提振。Asio将把写操作优化为无操作。当shutdown()
操作运行时,将close_notify
发送给PartyB,导致SSL关闭parta的SSL流的写端,然后异步等待PartyB的close_notify
。然而,由于parta的SSL流的写端已经关闭,async_write()
操作将失败,并显示一个SSL错误,表明协议已关闭。
if ((error.category() == boost::asio::error::get_ssl_category())
&& (SSL_R_PROTOCOL_IS_SHUTDOWN == ERR_GET_REASON(error.value())))
{
ssl_stream.lowest_layer().close();
}
失败的async_write()
操作将显式关闭底层传输,导致正在等待乙方的close_notify
的async_shutdown()
操作被取消。
- 尽管parta执行了SSL规范允许的关闭过程,但当底层传输关闭时,
shutdown()
操作被显式取消。因此,shutdown()
操作的错误码的值将为boost::asio::error::operation_aborted
。 - 乙方关机操作将成功完成。
综上所述,Asio的SSL关闭操作有点棘手。在适当关闭期间,启动器和远程对等体的错误代码之间的不一致可能会使处理变得有点尴尬。作为一般规则,只要错误代码的类别不是SSL类别,则协议被安全关闭。
- 删除映射和分割错误中的一个过去结束元素
- 全局向量导致 C++ 程序结束时出现段错误
- 程序在VS2017上以"abort() has been called"错误结束
- 矩阵类运算符 + 和 * 以段错误结束
- 如何在输入结束时修复预期}错误
- 输入文件读取错误的值,eof 控制循环不会结束
- 寻求结束 std::cin 返回 -1 错误和错误位
- C 功能:读取直到文件结束 - 查找代码中的错误
- winsock2:recvfrom()函数以错误10022(参数无效)结束
- 控制台在结束时崩溃(循环)未超出限制错误
- 错误:"结束索引"中没有名为"rank_"的成员
- 是C 中的语法错误,以结束与A}的类定义中的函数
- 错误:预期的“}”输入结束时 - 有一个
- 在制作过程结束时显示警告/错误的数量
- 通过引用传递以错误结束
- 结束进程错误代码 -1,如果访问结构的字符串字段
- 如何修复此错误?CRT检测到应用程序在堆缓冲区结束后写入内存
- boost asio SSL async_shutdown总是以错误结束
- 简单的CUDA应用程序,cudaMalloc以错误结束:未指定的驱动程序错误
- c++ Codeblocks + libcurl以链接错误结束