Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.0k views
in Technique[技术] by (71.8m points)

c++ - How to gracefully shutdown a boost asio ssl client?

The client does some ssl::stream<tcp_socket>::async_read_some()/ssl::stream<tcp_socket>::async_write() calls and at some point needs to exit, i.e. it needs to shutdown the connection.

Calling ssl::stream<tcp_socket>::lowest_layer().close() works, but (as it is expected) the server (a openssl s_server -state ... command) reports an error on closing the connection.

Looking at the API the right way seems to be to call ssl::stream<tcp_socket>::async_shutdown().

Now there are basically 2 situation where a shutdown is needed:

1) Client is in the async_read_some() callback and reacts on a 'quit' command from the server. Calling from there async_shutdown() yields a 'short read' error in the shutdown callback.

This is surprising but after googling around this seems to be normal behaviour - one seem to have to check if it is a real error or not like this:

// const boost::system::error_code &ec
if (ec.category() == asio::error::get_ssl_category() &&
  ec.value() == ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SHORT_READ)) {
  // -> not a real error, just a normal TLS shutdown
}

The TLS server seems to be happy, though - it reports:

DONE
shutting down SSL
CONNECTION CLOSED

2) A async_read_some() is active - but a user decides to exit the client (e.g. via a command from stdin). When calling async_shutdown() from that context following happens:

  • the async_read_some() callback is executed with a 'short read' error code - kind of expected now
  • the async_shutdown() callback is executed with a decryption failed or bad record mac error code - this is unexpected

The server side does not report an error.

Thus my question how to properly shutdown a TLS client with boost asio.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

One way to resolve the 'decryption failed or bad record mac' error code from the 2nd context is:

a) from inside the stdin handler call:

ssl::stream<tcp_socket>::lowest_layer()::shutdown(tcp::socket::shutdown_receive)

b) this results in the async_read_some() callback getting executed with a 'short read' 'error' code

c) in that callback under that 'error' condition async_shutdown() is called:

// const boost::system::error_code &ec
if (ec.category() == asio::error::get_ssl_category() &&
    ec.value()    == ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SHORT_READ)) {
  // -> not a real error:
  do_ssl_async_shutdown();
}

d) the async_shutdown() callback is executed with a 'short read' error code, from where we finally call:

ssl::stream::lowest_layer()::close()

These steps result in a connection shutdown without any weird error messages on the client or server side.

For example, when using openssl s_server -state ... as server it reports on sutdown:

SSL3 alert read:warning:close notify
DONE
shutting down SSL
CONNECTION CLOSED
ACCEPT

(the last line is because the command accepts new connections)

Alternative

Instead of lowest_layer()::shutdown(tcp::socket::shutdown_receive) we can also call

ssl::stream<tcp_socket>::lowest_layer()::cancel()

to initiate a proper shutdown. It has the same effect, i.e. it yields the execution of the scheduled async_read_some() callback (but with operation_aborted error code). Thus, one can call async_shutdown() from there:

if (ec.value() == asio::error::operation_aborted) {
  cout << "(not really an error)
";
  do_async_ssl_shutdown();
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...