Thanks for the response this helps immensely!

Bodo Moeller wrote:
> 
> On Mon, Feb 28, 2000 at 03:54:39PM -0800, Jeremy Bennett wrote:
> 
> > 1) I see that SSL_write and SSL_read can result in errors
> > SSL_ERROR_WANT_READ/WRITE. Since this is the case, can I have
> > simultaneous outstanding SSL_reads and SSL_writes? That is, if I call
> > SSL_read and it results in SSL_ERROR_WANT_WRITE can I go ahead and call
> > SSL_write or should I wait for SSL_read to return success? (and vice
> > versa)
> 
> Handling such situations is a little complicated, but it can be done.
> The difficulty is that after you've called SSL_read or SSL_write, the
> result code of the previous call to on of these functions may no
> longer be valid.  So if the second function reports success, you
> should just retry the first one, because clearly something happened.
> If both functions result in an SSL_ERROR_WANT_READ/WRITE result code,
> then it's not obvious what should be done -- just retrying everything
> may lead to busy waiting, but if you want to call select() instead you
> don't know what you should really select() for.

We have an event handler that will call a specific function when a
socket is readable or writable. I was planning on registering the
appropriate callback on these errors. What I needed to know was whether
the underlying code could cope with this behavior. It sounds like it
can.
Question: if I call SSL_write, get WANT_READ, register a callback for
the socket being readable, then call SSL_read, get WANT_READ, and
register another callback, does it matter if I recall them in the
opposite order? ie, when the socket is readable call SSL_read then
SSL_write?
> 
> The solution is to look at the underlying BIO layer.  Before trying
> SSL_read and SSL_write, record BIO_numer_read(rbio) and
> BIO_number_written(wbio), where rbio and wbio are initialized
> as SSL_get_rbio(ssl) and SSL_get_wbio(ssl), respectively.
> Then call SSL_read and SSL_write.  If it is not obvious whether
> there was any protocol progress (i.e. if you get
> SSL_ERROR_WANT_READ/WRITE for both calls), then by calling
> BIO_number_read(rbio) and BIO_number_written(wbio) and comparing
> these figures with the previous values you can find out whether
> anything has happened.  If one of the values has changed,
> you can just enter another iteration of the loop without
> risking busy waiting.  If, on the other hand, both values
> are the same as before SSL_read and SSL_write, then you can
> be sure that the SSL_ERROR_WANT_READ/WRITE from the first of
> these SSL_... operations is still valid; so you can select()
> for the combination of the two SSL_ERROR_WANT_... codes.
> 
> Looking at the source code of other existing TLS proxies and
> finding out how they can fail is left as an exercise.
> If they don't evaluate BIO_number_read() and BIO_number_written(),
> then probably they're buggy (or their use is restricted to
> protocols where concurrent I/O in both directions is not
> possible).
> 
Could you point me at one of these opensource proxies? 

> > 2) Can SSL_shutdown result in WANT_READ/WRITE?
> 
> SSL_shutdown still has a weird interface, it will never return -1, and
> when it returns 0 this does not indicate an EOF at the network, it
> only means that the shutdown has not yet completed.   So its return
> values are not even suitable for SSL_get_error, i.e. those
> SSL_ERROR_WANT_... codes are meaningless in this context.
> The strange interface plus bugs in various browsers make this
> rather difficult to handle ...  Here's what I use in one program
> for the case where the closure is initiated by that program
> (if the peer initiates the closure, i.e. if SSL_get_error returns
> SSL_ERROR_ZERO_RETURN, then calling SSL_shutdown once should suffice):

Thanks! This is perfect!
> 
> >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> 
> /* ... */
> 
> some_function()
> {
>     /* ... */
> 
>     {
>         /* The TLS case.
>          * Send close_notify to "out", and try to obtain the peer's
>          * close_notify. */
> 
>         assert(out != NULL);
> #ifdef DEBUG_CLOSE
>         fprintf(stdout, "%s%s: Trying SSL_shutdown at %s.\n",
>             Myname, childid, tdef->forward_to_tcp_port.printable_name);
> #endif
>         r = do_tls_shutdown(out, outfd, childid);
> 
>         if (r == 0) {
>         /* Clean shutdown */
>         kill_outfd = 0;
>         clean_closure = 1;
>         } else {
>         tls_output_OpenSSL_errors(" while shutting down connection to ",
>                       tdef->forward_to_tcp_port.printable_name,
>                       childid, "unclean closure");
>         }
>     }
>     /* ... */
>     /* If kill_outfd has not been set to 0, send TCP RST,
>      * otherwise just close */
> }
> 
> /* Returns 0 if the shutdown succeeded, 1 in case of failure.
>  * Does not close the socket; does not report TLS level failure to stderr
>  * (but may report lower level problems, i.e. with sockets). */
> static int
> do_tls_shutdown(SSL *ssl, int sslfd, const char *childid)
> {
>     int r;
>     int shut;
> 
>     assert(ssl != NULL);
> 
> #if 0 /* In theory, things would work this way.  But Netscape (Navigator 4.5)
>        * does not handle connection closure correctly: When the server has sent
>        * closure alerts, it does not answer with closure alerts and close
>        * the connection -- instead, it keeps the connections and tries
>        * do use them for further requests, even though the server has sent
>        * closure alerts.  Then it just hangs, ignoring the server's alerts,
>        * and waiting for something to read. */
> 
>     r = sockets_set_blocking(sslfd, "");
>     if (r != 0)
>     return 1; /* didn't work */
> 
>     shut = SSL_shutdown(ssl) || SSL_shutdown(ssl);
>     /* SSL_shutdown is repeated because of the silly state machine
>      * in ssl3_shutdown -- calling it once is never enough even
>      * with blocking sockets (I think with non-blocking sockets it
>      * probably wouldn't work at all).  The first call should send
>      * a closure alert, the second one should received one. */
> 
>     if (shut)
>     return 0; /* success */
>     else
>     return 1;
> #else
>     /* This should work: Send a TCP shutdown after the SSL/TLS shutdown
>      * has been sent. */
> 
>     r = sockets_set_blocking(sslfd, "");
>     if (r != 0)
>     return 1;
>     shut = SSL_shutdown(ssl); /* should send out closure alert */
> #ifdef DEBUG_CLOSE
>     fprintf(stdout, "%s%s: First SSL_shutdown returned %i.\n",
>         Myname, childid, shut);
> #endif
> 
>     r = sockets_set_nonblocking(sslfd, "");
>     if (r != 0)
>     return 1;
>     /* We write our own close_nofity in blocking mode to make sure that
>      * we really write something; but we try to read the peer's one in
>      * non-blocking mode.  Note that if the peer sends its close_notify in
>      * more than one TCP packet, it could confuse us in that we
>      * may not notice that it's a clean closure; but that problem
>      * is pretty irrelevant in practice. */
> 
>     if (!shut) /* if r != 0, probably the peer already initiated shutdown */ {
> #ifndef SHUT_WR
> # define SHUT_WR 1
> #endif
> 
>     /* Now we can expect a closure alert from the peer;
>      * but many browsers don't react to the close_notify (and even
>      * a TCP shutdown) until they want to reuse the connection, so we
>      * use a time-out.
>      * Note that an unclean closure destroys the session (reuse
>      * is not allowed according to the TLS specification),
>      * so we should be a bit generous with timeouts because
>      * otherwise we may need new handshakes for the same peer,
>      * which are quite costly.
>      */
> 
>     sockets_select(sslfd, -1, -1, -1, 60 /* seconds */, childid);
>     /* Maybe the peer responded by now -- */
>     shut = SSL_shutdown(ssl);
> #ifdef DEBUG_CLOSE
>     fprintf(stdout, "%s%s: After timeout, SSL_shutdown returned %i.\n",
>         Myname, childid, shut);
> #endif
>     if (!shut) {
>         /* Either the peer was too slow, or sockets_select returned
>          * because the peer just tried to send new data (ignoring
>          * our close_notify).  The latter is quite likely with many
>          * browsers ... We give them another chance; this time,
>          * the timeout must be very short because those browsers
>          * will hang, hoping for us to send data. */
> 
>         sockets_select(sslfd, -1, -1, -1, 2 /* seconds */, childid);
>         shut = SSL_shutdown(ssl);
> #ifdef DEBUG_CLOSE
>         fprintf(stdout, "%s%s: After second timeout, SSL_shutdown returned %i.\n",
>         Myname, childid, shut);
> #endif
> 
>         /* Finally, now send a TCP FIN; maybe then the peer will get
>          * the idea :-) (We could have done so immediately, but it
>          * doesn't quite fit into our own world-view that both directions
>          * of any connection are closed simultaneously ... */
> 
>         r = shutdown(sslfd, SHUT_WR);
>         if (r != 0) {
>         assert(r == -1);
>         fprintf(stderr, "%s%s: Cannot shutdown TCP socket: %s\n",
>             Myname, childid, strerror(errno));
>         return 1;
>         }
> 
>         sockets_select(sslfd, -1, -1, -1, 30 /* seconds */, childid);
>         /* Last chance for the peer to respond. */
>         shut = SSL_shutdown(ssl);
> 
> #ifdef DEBUG_CLOSE
>         fprintf(stdout, "%s%s: After TCP FIN and timeout, "
>             "SSL_shutdown returned %i.\n",
>             Myname, childid, shut);
> #endif
>     }
>     }
> 
>     if (shut)
>     return 0; /* success */
>     else
>     return 1;
> #endif
> }
> 
> /* Arguments are fd's or -1 (if not needed). */
> void
> sockets_select(int read_select_1, int read_select_2,
>            int write_select_1, int write_select_2,
>            int seconds /* timeout -- -1 means no timeout */,
>            const char *extraprefix)
> {
>     /* ... */
> }
> 
> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
> ______________________________________________________________________
> OpenSSL Project                                 http://www.openssl.org
> Development Mailing List                       [EMAIL PROTECTED]
> Automated List Manager                           [EMAIL PROTECTED]
______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
Development Mailing List                       [EMAIL PROTECTED]
Automated List Manager                           [EMAIL PROTECTED]

Reply via email to