Gabriel Soto wrote:

> {
> // Create BIO with some random nonexistent host.
> BIO *bio = BIO_new_connect("192.168.9.9:9999");
>
> if (bio == NULL) {
> // Failed to obtain BIO.
> return false;
> }
>
> // Set as non-blocking.
> BIO_set_nbio(bio, 1);
>
> // Attempt to connect.
> printf("BIO_do_connect: %ld\n", BIO_do_connect(bio));
> printf("BIO_should_retry: %d\n", BIO_should_retry(bio));
>
> // Try again. Not much sense in this, but let's see what happens.
> printf("BIO_do_connect: %ld\n", BIO_do_connect(bio));
> printf("BIO_should_retry: %d\n", BIO_should_retry(bio));
> }
>
> Output:
> BIO_do_connect: -1
> BIO_should_retry: 8
> BIO_do_connect: 1
> BIO_should_retry: 8
>
> Does this make sense?
> Why does BIO_do_connect() return 1 the second time?


> Can anybody just confirm that this is a strange behavior. Maybe I'm
> getting things wrong.
> Any example code of a client using non-blocking sockets will be
> greatly appreciated too.


There's a bug in bss_conn.c. It assumes that with a pending non-blocking
connection attempt, the absence of an error (SO_ERROR is 0) indicates that
the connection completed successfully. The absence of an error *right* *now*
does mean that the connection has or will complete successfully:

case BIO_CONN_S_BLOCKED_CONNECT:
i=BIO_sock_error(b->num);
if (i)
{
BIO_clear_retry_flags(b);
SYSerr(SYS_F_CONNECT,i);
ERR_add_error_data(4,"host=",
c->param_hostname,
":",c->param_port);
BIOerr(BIO_F_CONN_STATE,BIO_R_NBIO_CONNECT_ERRO
R);
ret=0;
goto exit_loop;
}
else
c->state=BIO_CONN_S_OK;
break;

Notice how this assumes that if BIO_sock_error returns zero, the connection
completed? This is a bogus inference. The absence of an error just means the
connection attempt has not failed *yet* and tells you nothing about how it
will ultimately turn out.

Until OpenSSL is fixed, you must avoid retrying the connect until the socket
becomes writable or errored. Calling 'connect' in the window between when a
connection is attempted and when it succeeds or fails is unsafe, due to this
bug.

I'm not sure what the optimal fix is. One possible fix is to change the
'else' to call 'getpeername'. If the return is an 'ENOTCONN' error, then
indicate that a retry is needed. Only set the state to 'BIO_CONN_S_OK' if
'getpeername' returns zero.

DS


__________________________________________________ ____________________
OpenSSL Project http://www.openssl.org
Development Mailing List openssl-dev@openssl.org
Automated List Manager majordomo@openssl.org