[asio] SSL async_shutdown() never finishes if called when async_read_some() is in progress

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
12 messages Options
Reply | Threaded
Open this post in threaded view
|

[asio] SSL async_shutdown() never finishes if called when async_read_some() is in progress

Boost - Users mailing list
Hello,

I'm writing a middleware which basically adds an SSL layer to the connection between a client and a server. The connection is bidirectional, so I need to read from each side and write to the other. The client can also close the connection at any time, in which case async_shutdown() must be called on the server's stream.

The problem is, often the shutdown happens when the program is waiting on the server through async_read_some(), and the shutdown will never finish.

The following code illustrates this issue:

#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>

int main(int argc, char* argv[])
{
  try
  {
    boost::asio::io_context io_context;
    boost::asio::streambuf buffer;

    boost::asio::ssl::context ctx(boost::asio::ssl::context::tlsv12);
    ctx.set_verify_mode(boost::asio::ssl::verify_none);

    boost::asio::ssl::stream<boost::asio::ip::tcp::socket>
    sock(io_context, ctx);

    sock.next_layer().connect(boost::asio::ip::tcp::endpoint(
        boost::asio::ip::make_address("127.0.0.1"), 4433));
    sock.handshake(boost::asio::ssl::stream_base::client);

    /* "async_shutdown" won't appear unless async_read_some()
     * is commented out */
    sock.async_read_some(buffer.prepare(1024),
        [](boost::system::error_code, size_t)
        {std::cout << "async_read_some" << std::endl;});

    sock.async_shutdown([](boost::system::error_code)
        {std::cout << "async_shutdown" << std::endl;});

    io_context.run();
  }
  catch (std::exception& e)
  {
    std::cerr << "Exception: " << e.what() << "\n";
  }

  return 0;
}

To reproduce, start up an SSL server:
  openssl genrsa -out key.pem 2048
  openssl req -new -sha256 -key key.pem -out csr.csr
  openssl req -x509 -sha256 -days 365 -key key.pem -in csr.csr -out server.pem
  openssl s_server -key key.pem -cert server.pem
and run the program. Notice that "async_shutdown" does not finish even if the server is killed.

In practice, this leaks the memory of the shutdown handler and whatever is bound to it. The only solution I can think of would be to cancel the read operation, but there's no portable way in asio to do this.

Can someone point out how to solve this?

Cheers,

Alexander
_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: [asio] SSL async_shutdown() never finishes if called when async_read_some() is in progress

Boost - Users mailing list
On Fri, Apr 6, 2018 at 12:56 AM, Alexander D. via Boost-users
<[hidden email]> wrote:
> The only solution I can think of would be to cancel the read operation, but there's no portable way in asio to do this.

    sock.next_layer().cancel();

?
_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: [asio] SSL async_shutdown() never finishes if called when async_read_some() is in progress

Boost - Users mailing list
In reply to this post by Boost - Users mailing list
>> The only solution I can think of would be to cancel the read operation, but there's no portable way in asio to do this.
>
>    sock.next_layer().cancel();
>
>?

Per ASIO Reference:

"Calls to cancel() will always fail with boost::asio::error::operation_not_supported when run on Windows XP, Windows Server 2003, and earlier versions of Windows, unless BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has two issues that should be considered before enabling its use:
..."

which renders cancel() non-portable.
_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: [asio] SSL async_shutdown() never finishes if called when async_read_some() is in progress

Boost - Users mailing list
On Fri, Apr 6, 2018 at 6:11 AM, Alexander D. via Boost-users
<[hidden email]> wrote:

>>> The only solution I can think of would be to cancel the read operation, but there's no portable way in asio to do this.
>>
>>    sock.next_layer().cancel();
>>
>>?
>
> Per ASIO Reference:
>
> "Calls to cancel() will always fail with boost::asio::error::operation_not_supported when run on Windows XP, Windows Server 2003, and earlier versions of Windows, unless BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has two issues that should be considered before enabling its use:
> ..."
>
> which renders cancel() non-portable.

I consider the issues with cancel present in Windows XP and Windows
Server 2003 to be sufficiently problematic that it is not worth taking
them into account when designing robust network applications (I do
develop mostly on Windows). Unless you have a specific business need
to support those defective operating systems, I would just ignore
them.
_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: [asio] SSL async_shutdown() never finishes if called when async_read_some() is in progress

Boost - Users mailing list
In reply to this post by Boost - Users mailing list
On Fri, 2018-04-06 at 09:11 -0400, Alexander D. via Boost-users wrote:

> > > The only solution I can think of would be to cancel the read
> > > operation, but there's no portable way in asio to do this.
> >
> >    sock.next_layer().cancel();
> >
> > ?
>
> Per ASIO Reference:
>
> "Calls to cancel() will always fail with
> boost::asio::error::operation_not_supported when run on Windows XP,
> Windows Server 2003, and earlier versions of Windows, unless
> BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function
> has two issues that should be considered before enabling its use:
> ..."
>
> which renders cancel() non-portable.
Is anyone still developing for windows xp? It's portable enough in
reality, surely?

> _______________________________________________
> Boost-users mailing list
> [hidden email]
> https://lists.boost.org/mailman/listinfo.cgi/boost-users
_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users

signature.asc (849 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [asio] SSL async_shutdown() never finishes if called when async_read_some() is in progress

Boost - Users mailing list
In reply to this post by Boost - Users mailing list
On April 6, 2018 9:55 PM, Vinnie Falco via Boost-users <[hidden email]> wrote:

> On Fri, Apr 6, 2018 at 6:11 AM, Alexander D. via Boost-users
> <[hidden email]> wrote:
>>>> The only solution I can think of would be to cancel the read operation, but there's no portable way in asio to do this.
>>>
>>>     sock.next_layer().cancel();
>>>
>>> ?
>>
>> Per ASIO Reference:
>>
>> "Calls to cancel() will always fail with boost::asio::error::operation_not_supported when run on Windows XP, Windows Server 2003, and earlier versions of Windows, unless BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has two issues that should be considered before enabling its use:
>> ..."
>>
>> which renders cancel() non-portable.
>
> I consider the issues with cancel present in Windows XP and Windows
> Server 2003 to be sufficiently problematic that it is not worth taking
> them into account when designing robust network applications (I do
> develop mostly on Windows). Unless you have a specific business need
> to support those defective operating systems, I would just ignore
> them.

Yes, I do need the program to work on at least Windows XP.

However, even if cancel() is used, it is a workaround, not a solution. If you run the code I supplied, you'll notice that async_read_some() is actually finished with a "stream truncated" error, quite similar to "canceling" the read. The problem is on async_shutdown()'s handler not being called, and it's not mentioned anywhere in the reference. Why can't we shutdown a stream while reading from it (since we can close a socket while reading)?
_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: [asio] SSL async_shutdown() never finishes if called when async_read_some() is in progress

Boost - Users mailing list


On 6 April 2018 at 16:29, Alexander D. via Boost-users <[hidden email]> wrote:
On April 6, 2018 9:55 PM, Vinnie Falco via Boost-users <[hidden email]> wrote:
> On Fri, Apr 6, 2018 at 6:11 AM, Alexander D. via Boost-users
> <[hidden email]> wrote:
>>>> The only solution I can think of would be to cancel the read operation, but there's no portable way in asio to do this.
>>>
>>>     sock.next_layer().cancel();
>>>
>>> ?
>>
>> Per ASIO Reference:
>>
>> "Calls to cancel() will always fail with boost::asio::error::operation_not_supported when run on Windows XP, Windows Server 2003, and earlier versions of Windows, unless BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has two issues that should be considered before enabling its use:
>> ..."
>>
>> which renders cancel() non-portable.
>
> I consider the issues with cancel present in Windows XP and Windows
> Server 2003 to be sufficiently problematic that it is not worth taking
> them into account when designing robust network applications (I do
> develop mostly on Windows). Unless you have a specific business need
> to support those defective operating systems, I would just ignore
> them.

Yes, I do need the program to work on at least Windows XP.

However, even if cancel() is used, it is a workaround, not a solution. If you run the code I supplied, you'll notice that async_read_some() is actually finished with a "stream truncated" error, quite similar to "canceling" the read. The problem is on async_shutdown()'s handler not being called, and it's not mentioned anywhere in the reference. Why can't we shutdown a stream while reading from it (since we can close a socket while reading)?

In the past, when I have had queries about the inner workings and design decisions behind ASIO, I have emailed the author, Chris Karloff. I am sure he is a busy guy but he has taken the trouble to answer helpfully in the past. It may be worth dropping him a line and asking for a suggestion.

He didn't charge me but having received help twice now, I think it would be rude of me not to offer him a few hours consultancy if I needed his help again. 



 
_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users


_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: [asio] SSL async_shutdown() never finishes if called when async_read_some() is in progress

Boost - Users mailing list
It's Boris Karloff but Chris(topher) Kohlhoff :-)


Am 06.04.2018 um 16:37 schrieb Richard Hodges via Boost-users:


On 6 April 2018 at 16:29, Alexander D. via Boost-users <[hidden email]> wrote:
On April 6, 2018 9:55 PM, Vinnie Falco via Boost-users <[hidden email]> wrote:
> On Fri, Apr 6, 2018 at 6:11 AM, Alexander D. via Boost-users
> <[hidden email]> wrote:
>>>> The only solution I can think of would be to cancel the read operation, but there's no portable way in asio to do this.
>>>
>>>     sock.next_layer().cancel();
>>>
>>> ?
>>
>> Per ASIO Reference:
>>
>> "Calls to cancel() will always fail with boost::asio::error::operation_not_supported when run on Windows XP, Windows Server 2003, and earlier versions of Windows, unless BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has two issues that should be considered before enabling its use:
>> ..."
>>
>> which renders cancel() non-portable.
>
> I consider the issues with cancel present in Windows XP and Windows
> Server 2003 to be sufficiently problematic that it is not worth taking
> them into account when designing robust network applications (I do
> develop mostly on Windows). Unless you have a specific business need
> to support those defective operating systems, I would just ignore
> them.

Yes, I do need the program to work on at least Windows XP.

However, even if cancel() is used, it is a workaround, not a solution. If you run the code I supplied, you'll notice that async_read_some() is actually finished with a "stream truncated" error, quite similar to "canceling" the read. The problem is on async_shutdown()'s handler not being called, and it's not mentioned anywhere in the reference. Why can't we shutdown a stream while reading from it (since we can close a socket while reading)?

In the past, when I have had queries about the inner workings and design decisions behind ASIO, I have emailed the author, Chris Karloff. I am sure he is a busy guy but he has taken the trouble to answer helpfully in the past. It may be worth dropping him a line and asking for a suggestion.

He didn't charge me but having received help twice now, I think it would be rude of me not to offer him a few hours consultancy if I needed his help again. 



 
_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users



_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users


_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: [asio] SSL async_shutdown() never finishes if called when async_read_some() is in progress

Boost - Users mailing list


On Fri, 6 Apr 2018, 19:50 Franz Fehringer via Boost-users, <[hidden email]> wrote:
It's Boris Karloff but Chris(topher) Kohlhoff :-)

Ah yes, important difference. :) 


Am 06.04.2018 um 16:37 schrieb Richard Hodges via Boost-users:


On 6 April 2018 at 16:29, Alexander D. via Boost-users <[hidden email]> wrote:
On April 6, 2018 9:55 PM, Vinnie Falco via Boost-users <[hidden email]> wrote:
> On Fri, Apr 6, 2018 at 6:11 AM, Alexander D. via Boost-users
> <[hidden email]> wrote:
>>>> The only solution I can think of would be to cancel the read operation, but there's no portable way in asio to do this.
>>>
>>>     sock.next_layer().cancel();
>>>
>>> ?
>>
>> Per ASIO Reference:
>>
>> "Calls to cancel() will always fail with boost::asio::error::operation_not_supported when run on Windows XP, Windows Server 2003, and earlier versions of Windows, unless BOOST_ASIO_ENABLE_CANCELIO is defined. However, the CancelIo function has two issues that should be considered before enabling its use:
>> ..."
>>
>> which renders cancel() non-portable.
>
> I consider the issues with cancel present in Windows XP and Windows
> Server 2003 to be sufficiently problematic that it is not worth taking
> them into account when designing robust network applications (I do
> develop mostly on Windows). Unless you have a specific business need
> to support those defective operating systems, I would just ignore
> them.

Yes, I do need the program to work on at least Windows XP.

However, even if cancel() is used, it is a workaround, not a solution. If you run the code I supplied, you'll notice that async_read_some() is actually finished with a "stream truncated" error, quite similar to "canceling" the read. The problem is on async_shutdown()'s handler not being called, and it's not mentioned anywhere in the reference. Why can't we shutdown a stream while reading from it (since we can close a socket while reading)?

In the past, when I have had queries about the inner workings and design decisions behind ASIO, I have emailed the author, Chris Karloff. I am sure he is a busy guy but he has taken the trouble to answer helpfully in the past. It may be worth dropping him a line and asking for a suggestion.

He didn't charge me but having received help twice now, I think it would be rude of me not to offer him a few hours consultancy if I needed his help again. 



 
_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users



_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users

_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users

_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: [asio] SSL async_shutdown() never finishes if called when async_read_some() is in progress

Boost - Users mailing list
In reply to this post by Boost - Users mailing list
On Fri, Apr 6, 2018 at 7:29 AM, Alexander D. via Boost-users
<[hidden email]> wrote:
>
> Yes, I do need the program to work on at least Windows XP.

This restriction described in the Boost.Asio documentation places
constrains on how your code may be designed:

"[CancelIO] will only cancel asynchronous operations that were
initiated in the current thread."

This means you must always use an implicit strand (only one thread
calling io_service::run), and always call initiating functions from
it, especially the call to the first initiating function (which is
often made from a foreign thread). The call to cancel() must of course
be made from the io_service thread (asio sockets are not thread safe),
which can be done by using boost::asio::post if you are on a foreign
thread. If you need your application service connections using more
than one thread you will need to use one io_service per thread.

> Why can't we shutdown a stream while reading from it (since we can close a socket while reading)?

If you insist on supporting a defective operating system, then do not
be surprised if things don't work the way you want. From the
Boost.Asio documentation (RE: CancelIO on Windows XP):

"[CancelIO] can appear to complete without error, but the request to
cancel the unfinished operations may be silently ignored by the
operating system. Whether it works or not seems to depend on the
drivers that are installed."

So, IF you make sure only one thread is used on each instance of
io_service , AND your user has cooperative drivers, then MAYBE your
program will function correctly.

Regards
_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: [asio] SSL async_shutdown() never finishes if called when async_read_some() is in progress

Boost - Users mailing list
In reply to this post by Boost - Users mailing list
OK, after delving into the asio::ssl code, I finally managed to do this by closing the underlying socket when async_read_some() finishes with an error.

The crux of this problem is that, when the client sends a "close alert" to the TLS server, the server subsequently closes the connection without sending back a "close alert" (as permitted by TLS standard), causing async_read_some() to catch the error and fail. Meanwhile, async_shutdown() is still blindly waiting for the server to respond, which never happens (ASIO always assumes that the server will respond with a "close alert": https://github.com/boostorg/asio/blob/886839c/include/boost/asio/ssl/detail/impl/engine.ipp#L301), unless we propagate the error by closing the socket.

IMO, asio should probably "remember" the error state of the stream and make all the pending operations fail. At least this avoids some memory leak.
_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: [asio] SSL async_shutdown() never finishes if called when async_read_some() is in progress

Boost - Users mailing list
On 7/04/2018 19:02, Alexander D. wrote:
> OK, after delving into the asio::ssl code, I finally managed to do
this by closing the underlying socket when async_read_some() finishes
with an error.

Closing the socket on read error is fairly standard practice.  Otherwise
you have a socket that you're not reading from (which is generally
undesirable) or will be endlessly spinning with read errors (if you try
to restart the read, since most errors are non-recoverable).

_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users