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.
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).
> 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):
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
/* ... */
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]