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]