[英]boost asio ssl async_shutdown always finishes with an error?
我有一個在 boost 1.55 asio 中編寫的小型 ssl 客戶端,我想弄清楚為什么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()
關閉連接是正確的async_shutdown()
,但我想我可以調用boost::asio::ssl::stream.lowestlayer().close()
來關閉套接字如果這是執行此操作的預期方式,則從 openssl 下退出(實際上 asio ssl 示例似乎表明這是關閉的正確方式)。
對於加密安全關閉,雙方必須通過調用shutdown()
或async_shutdown()
並運行io_service
對boost::asio::ssl::stream
執行關閉操作。 如果操作以沒有SSL 類別的error_code
完成並且在部分關閉可能發生之前沒有被取消,則連接已安全關閉,底層傳輸可能會被重用或關閉。 簡單地關閉最低層可能會使會話容易受到截斷攻擊。
在標准化的TLS協議和非標准化的SSLv3協議中,安全關閉涉及各方交換close_notify
消息。 在Boost.Asio API 方面,任何一方都可以通過調用shutdown()
或async_shutdown()
來發起關閉,導致向close_notify
發送close_notify
消息,通知接收方發起方不會再發送更多消息SSL 連接。 根據規范,接收者必須使用close_notify
消息進行響應。 Boost.Asio 不會自動執行此行為,並要求接收者顯式調用shutdown()
或async_shutdown()
。
該規范允許關閉的發起者在收到close_notify
響應之前關閉連接的讀取端。 這用於應用程序協議不希望重用底層協議的情況。 不幸的是,Boost.Asio 目前 (1.56) 不提供對此功能的直接支持。 在 Boost.Asio 中, shutdown()
操作在出現錯誤或一方已發送和接收close_notify
消息時被認為已完成。 操作完成后,應用程序可以重用底層協議或關閉它。
建立 SSL 連接后,關機期間會出現以下錯誤代碼:
shutdown()
操作將因 SSL 短讀錯誤而失敗。boost::asio::error::eof
。shutdown()
操作成功完成。shutdown()
操作將被取消,導致boost::asio::error::operation_aborted
。 這是以下詳細信息中指出的解決方法的結果。shutdown()
操作成功完成。下面詳細介紹了這些不同的場景。 每個場景都用類似游泳線的圖表來說明,指示每一方在完全相同的時間點正在做什么。
shutdown()
after closes connection without negotiating shutdown.所調用shutdown()
后關閉,不談判關機連接。 violates the shutdown procedure by closing the underlying transport without first invoking shutdown()
on the stream.在這種情況下, 通過關閉底層傳輸而不首先在流上調用shutdown()
來違反關閉程序。 attempts to initiate a shutdown()
.一旦底層傳輸關閉, 嘗試啟動shutdown()
。
PartyA | PartyB
-------------------------------------+----------------------------------------
ssl_stream.handshake(...); | ssl_stream.handshake(...);
... | ssl_stream.lowest_layer().close();
ssl_stream.shutdown(); |
will attempt to send a close_notify
message, but the write to the underlying transport will fail with boost::asio::error::eof
. 將嘗試發送close_notify
消息,但對底層傳輸的寫入將失敗並顯示boost::asio::error::eof
。 violated the SSL shutdown procedure. Boost.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.
}
shutdown()
then closes connection without negotiating shutdown. 調用shutdown()
然后關閉連接而不協商關閉。 initiates a shutdown.在這種情況下,發起關閉。 receives the close_notify
message, violates the shutdown procedure by never explicitly responding with a shutdown()
before closing the underlying transport.然而,當收到close_notify
消息時,在關閉底層傳輸之前從未明確響應shutdown()
,從而違反了關閉程序。
PartyA | PartyB
-------------------------------------+---------------------------------------
ssl_stream.handshake(...); | ssl_stream.handshake(...);
ssl_stream.shutdown(); | ...
| ssl_stream.lowest_layer().close();
will send a close_notify
then wait for a response.作為Boost.Asio的的shutdown()
操作被認為是完整的一次close_notify
已經發送和接收的或發生錯誤時,將發送close_notify
,然后等待響應。 closes the underlying transport without sending a close_notify
, violating the SSL protocol.關閉底層傳輸不發送close_notify
,違反SSL協議。 's read will fail with boost::asio::error::eof
, and Boost.Asio will map it to an SSL short read error. 的讀取將因boost::asio::error::eof
而失敗,並且 Boost.Asio 會將其映射到 SSL 短讀取錯誤。
shutdown()
and waits for to respond with a shutdown()
. 啟動shutdown()
並等待響應shutdown()
。will initiate a shutdown and wait for to respond with a shutdown.在這種情況下,會發起關機,等待響應關機。
PartyA | PartyB
-------------------------------------+----------------------------------------
ssl_stream.handshake(...); | ssl_stream.handshake(...);
ssl_stream.shutdown(); | ...
... | ssl_stream.shutdown();
這是一個相當基本的關閉,雙方發送和接收close_notify
消息。 一旦雙方協商關閉,底層傳輸可以重新使用或關閉。
boost::asio::error::eof
. 的關閉操作將完成,錯誤值為boost::asio::error::eof
。shutdown()
but does not wait for to responsd.發起shutdown()
但不等待響應。 will initiate a shutdown and then immediately close the underlying transport once close_notify
has been sent.在這種情況下, 將啟動關閉,然后在發送close_notify
后立即關閉底層傳輸。 does not wait for to respond with a close_notify
message.不等待回應了close_notify
消息。 這種類型的協商關閉是根據規范允許的,並且在實現中相當普遍。
如上所述,Boost.Asio 不直接支持這種類型的關機。 Boost.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();
will initiate an asynchronous shutdown operation and then initiate an asynchronous write operation. 會先發起異步關機操作,然后再發起異步寫操作。 用於寫入的緩沖區必須是非零長度(上面使用了空字符); 否則,Boost.Asio 會將寫入優化為無操作。 , causing SSL to close the write side of 's SSL stream, and then asynchronously wait for 's close_notify
.當shutdown()
操作運行時,它會發送close_notify
給,從而導致SSL關閉的寫入端的SSL流,然后異步地等待的close_notify
。 's SSL stream has closed, the async_write()
operation will fail with an SSL error indicating the protocol has been shutdown.但是,由於的 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();
}
's close_notify
to be cancelled.失敗的async_write()
操作則顯式關閉底層傳輸,造成async_shutdown()
正在等待的操作close_notify
被取消。
shutdown()
operation was explicitly cancelled when underlying transport was closed.盡管執行了 SSL 規范允許的關閉過程,但在shutdown()
底層傳輸時, shutdown()
操作被明確取消。 因此, shutdown()
操作的錯誤代碼的值為boost::asio::error::operation_aborted
。綜上所述,Boost.Asio 的 SSL 關閉操作有點棘手。 正確關閉期間發起方和遠程對等方的錯誤代碼之間的不一致可能會使處理變得有點尷尬。 作為一般規則,只要錯誤代碼的類別不是 SSL 類別,協議就會被安全關閉。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.