[openssl-dev] How to use BIO_do_connect(), blocking and non-blocking with timeout, coping with errors

David von Oheimb David.von.Oheimb at siemens.com
Fri Sep 1 21:12:04 UTC 2017


On 29.08.2017 16:15, Salz, Rich via openssl-dev wrote:
>>     Getting the client connect right appears surprisingly messy when one
>>     needs to cope with all kinds of network error situations including
>>     domain name resolution issues and temporarily unreachable servers.
>>     Both indefinitely blocking and non-blocking behavior (i.e., connection
>>     attempts with and without a timeout) should be supported.
> It is a complicated issue and hard to get right for all definitions of right for all applications ☺
Hmm - on the one hand, good to get confirmation that I did not just miss
a simple way of out the maze, ...
> A set of API’s that set up all the TLS “metadata”, and took a connected socket might be a way through the maze.  For example:
>     SSL *SSL_connection(int socket, const char *servername, …whatever…)
... on the other hand, it's a pity that such a high-level API does not
(yet) exist, at least not in OpenSSL.

How about adding at least some clearly useful abstractions like the
below socket_wait() function,
which would reduce code duplication in the OpenSSL crypto lib and apps
and help application developers?

Maybe other OpenSSL users have specific experience on error and timeout
handling for BIO_do_connect() etc.
and can comment in more detail on the (approximate) solution,
bio_connect(), that I gave below?

On 28.08.2017 13:46, David von Oheimb wrote:
> Hi all,
>
> I'm currently enhancing HTTP(S) clients based on OpenSSL in several
> flavors, in particular a CMP client, which in turn uses simple HTTP
> clients for contacting CRL distribution points or OCSP responders.
>
> Getting the client connect right appears surprisingly messy when one
> needs to cope with all kinds of network error situations including
> domain name resolution issues and temporarily unreachable servers.
> Both indefinitely blocking and non-blocking behavior (i.e., connection
> attempts with and without a timeout) should be supported.
>
> Since these are pretty general problems I wonder why there there is
> rather limited support via generic higher-level OpenSSL or C library
> functions, or at least I was unable to find it. Instead, the OpenSSL
> apps contain code that calls BIO_do_connect directly (or the equivalent
> BIO_do_handshake), in particular query_responder() in apps/ocsp.c.
> (The situation is similar for the subsequent exchange of data via the
> BIO, optionally with a timeout).
>
> So I constructed my own abstraction, called bio_connect, which took
> quite some effort testing network error situations. Please see below its
> code including comments on some strange behavior I experienced and my
> workarounds for that. Does this code make sense, or do I miss anything?
>
> How about adding such a function for instance to crypto/bio/bio_lib.c?
>
> BTW, my code uses a handy generic helper function, socket_wait, for
> waiting for read/write form/to a socket, with a given timeout. Since
> several instances of that pretty common code pattern using select() are
> spread over the OpenSSL apps (and crypto lib), I suggest adding this
> function to the library. Where would be a good place to put it?
>
> Thanks,
> 	David
>> /* returns -1 on error, 0 on timeout, 1 on success */
>> int bio_connect(BIO *bio, int timeout) {
>>     int blocking;
>>     time_t max_time;
>>     int rv;
>>
>>     blocking = timeout == 0;
>>     max_time = timeout != 0 ? time(NULL) + timeout : 0;
>>
>>     if (!blocking)
>>         BIO_set_nbio(bio, 1);
>>  retry:
>>     rv = BIO_do_connect(bio);
>>     if (rv <= 0 && (errno == ETIMEDOUT /* in blocking case,
>>           despite blocking BIO, BIO_do_connect() timed out */ ||
>>           ERR_GET_REASON(ERR_peek_error()) == ETIMEDOUT/* when non-blocking,
>>           BIO_do_connect() timed out early with rv == -1 and errno == 0 */)) {
>>         ERR_clear_error();
>>         (void)BIO_reset(bio); /* otherwise, blocking next connect() may crash
>>                              and non-blocking next BIO_do_connect() will fail */
>>         goto retry;
>>     }
>>     if (!blocking && rv <= 0 && BIO_should_retry(bio)) {
>>         int fd;
>>         if (BIO_get_fd(bio, &fd) <= 0)
>>             return -1;
>>         rv = socket_wait(fd, 1, max_time - time(NULL));
>>         if (rv > 0)
>>             /* for some reason, select() may wrongly have returned success */
>>             goto retry;
>>     }
>>     return rv;
>> }
>> /* returns < 0 on error, 0 on timeout, >0 on success */
>> int socket_wait(int fd, int for_read, int timeout)
>> {
>>     fd_set confds;
>>     struct timeval tv;
>>
>>     if (timeout <= 0)
>>         return 0;
>>
>>     FD_ZERO(&confds);
>>     openssl_fdset(fd, &confds);
>>     tv.tv_usec = 0;
>>     tv.tv_sec = timeout;
>>     return select(fd + 1, for_read ? &confds : NULL,
>>                   for_read ? NULL : &confds, NULL, &tv);
>> }
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mta.openssl.org/pipermail/openssl-dev/attachments/20170901/c7bd4516/attachment.html>


More information about the openssl-dev mailing list