Bonjour,

I'm running into something strange.
Here's an extract of my code:

-----
[...]
{
  time_t start;
  BIO *cbio = NULL;
  
  if ((cbio = BIO_new(BIO_s_connect())) == NULL)
  {  
    result = ERR(CMCLIENTERR_CONNEXION);
    goto done;
  }

  BIO_set_conn_hostname(cbio, server_name);
  BIO_set_conn_port(cbio, server_port);

  /* Configure the BIO as a non-blocking one */
  BIO_set_nbio(cbio, 1);
  
  /* We'll mesure time from now */
  start = time(NULL);
  
  /* Let's try to connect, deal with retries and timeout */
  while (difftime(time(NULL), start) < mytimeout)
  {
    res = BIO_do_connect(cbio);
  
    /* If BIO_do_connect() said it's OK, then get out of this loop */
    if (res > 0)
      break;
  
    /* If it failed, check if retrying can be useful */
    if ((res <= 0) && !BIO_should_retry(cbio))
    {
      LOGMSG(LOG_ERR, "Unable to connect to CM server");
      result = ERR(CMCLIENTERR_CANTCONNECT);
      goto done;
    }
  }
  [... do something useful, since it's OK now ...]
done:
  [my cleanup code];
}
-----

In the normal case, everything works OK. But I found myself trying to
connect to a host behind a blocking firewall (with a DROP policy), and
the BIO_do_connect() call is performed exactly twice.
The first time it returns -1, cbio->flags is set to 12
(BIO_FLAGS_SHOULD_RETRY|BIO_FLAGS_IO_SPECIAL), indicating that I
should retry the connect, and cbio->ptr.state is set to 7
(BIO_CONN_S_BLOCKED_CONNECT).
The second time it returns something > 0, indicating it's OK, but the
connection hasn't been performed, and subsequent reads and writes
fail.

Reading the bss_conn.c source file, I can track down the
BIO_do_connect() code down to conn_state(), and the following code:

-----
        for (;;)
                {
                switch (c->state)
                        {
                [...]
                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_ERROR);
                                ret=0;
                                goto exit_loop;
                                }
                        else
                                c->state=BIO_CONN_S_OK;
                        break;

                case BIO_CONN_S_OK:
                        ret=1;
                        goto exit_loop;
                default:
                        /* abort(); */
                        goto exit_loop;
                        }
                 [...]
-----

That means that if the BIO is in BIO_CONN_S_BLOCKED_CONNECT state (and
in my case it is), any subsequent call to BIO_do_connect() returns OK
(state changes to BIO_CONN_S_OK, for() loop is executed once more,
and the function return with ret=1).

Is that really intended to work like this?

I know I could check for BIO_should_io_special() for this, but:
 - there's no standard action to do (if I want to read or write, I can
   do a select() call, but here?)
 - even if I call BIO_should_io_special(), the state of the BIO is
   already set to BIO_CONN_S_BLOCKED_CONNECT, and the next call to
   BIO_do_connect() will return 1, I loose.

If I don't call BIO_set_nbio(), the call to BIO_do_connect() is
blocked at connect() (that's normal), and eventually it'll return back
to me. Using blocking BIOs is not an option:
 - I can't control the timeouts
 - BIO_set_nbio() should be called before connecting (as per the
   manpage)

-- 
Erwann ABALEA <[EMAIL PROTECTED]>
______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
User Support Mailing List                    openssl-users@openssl.org
Automated List Manager                           [EMAIL PROTECTED]

Reply via email to