static long conn_ctrl(BIO *b, int cmd, long num, void *ptr)
{
    BIO *dbio;
    int *ip;
    const char **pptr = NULL;
    long ret = 1;
    BIO_CONNECT *data;

    data = (BIO_CONNECT *)b->ptr;

    switch (cmd) {
    case BIO_CTRL_RESET:
        ret = 0;
        data->state = BIO_CONN_S_BEFORE;
        conn_close_socket(b);
        b->flags = 0;
        break;
    case BIO_C_DO_STATE_MACHINE:
        /* use this one to start the connection */
        if (data->state != BIO_CONN_S_OK)
            ret = (long)conn_state(b, data);
        else
            ret = 1;
        break;
    case BIO_C_GET_CONNECT:
        if (ptr != NULL) {
            pptr = (const char **)ptr;
        }

        if (b->init) {
            if (pptr != NULL) {
                ret = 1;
                if (num == 0) {
                    *pptr = data->param_hostname;
                } else if (num == 1) {
                    *pptr = data->param_port;
                } else if (num == 2) {
                    *pptr = (char *)&(data->ip[0]);
                } else {
                    ret = 0;
                }
            }
            if (num == 3) {
                ret = data->port;
            }
        } else {
            if (pptr != NULL)
                *pptr = "not initialized";
            ret = 0;
        }
        break;
    case BIO_C_SET_CONNECT:
        if (ptr != NULL) {
            b->init = 1;
            if (num == 0) {
                OPENSSL_free(data->param_hostname);
                data->param_hostname = OPENSSL_strdup(ptr);
            } else if (num == 1) {
                OPENSSL_free(data->param_port);
                data->param_port = OPENSSL_strdup(ptr);
            } else if (num == 2) {
                char buf[16];
                unsigned char *p = ptr;

                BIO_snprintf(buf, sizeof buf, "%d.%d.%d.%d",
                             p[0], p[1], p[2], p[3]);
                OPENSSL_free(data->param_hostname);
                data->param_hostname = OPENSSL_strdup(buf);
                memcpy(&(data->ip[0]), ptr, 4);
            } else if (num == 3) {
                char buf[DECIMAL_SIZE(int) + 1];

                BIO_snprintf(buf, sizeof buf, "%d", *(int *)ptr);
                OPENSSL_free(data->param_port);
                data->param_port = OPENSSL_strdup(buf);
                data->port = *(int *)ptr;
            }
        }
        break;
    case BIO_C_SET_NBIO:
        data->nbio = (int)num;
        break;
    case BIO_C_GET_FD:
        if (b->init) {
            ip = (int *)ptr;
            if (ip != NULL)
                *ip = b->num;
            ret = b->num;
        } else
            ret = -1;
        break;
    case BIO_CTRL_GET_CLOSE:
        ret = b->shutdown;
        break;
    case BIO_CTRL_SET_CLOSE:
        b->shutdown = (int)num;
        break;
    case BIO_CTRL_PENDING:
    case BIO_CTRL_WPENDING:
        ret = 0;
        break;
    case BIO_CTRL_FLUSH:
        break;
    case BIO_CTRL_DUP:
        {
            dbio = (BIO *)ptr;
            if (data->param_port)
                BIO_set_conn_port(dbio, data->param_port);
            if (data->param_hostname)
                BIO_set_conn_hostname(dbio, data->param_hostname);
            BIO_set_nbio(dbio, data->nbio);
            /*
             * FIXME: the cast of the function seems unlikely to be a good
             * idea
             */
            (void)BIO_set_info_callback(dbio,
                                        (bio_info_cb *)data->info_callback);
        }
        break;
    case BIO_CTRL_SET_CALLBACK:
        {
# if 0                          /* FIXME: Should this be used? -- Richard
                                 * Levitte */
            BIOerr(BIO_F_CONN_CTRL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
            ret = -1;
# else
            ret = 0;
# endif
        }
        break;
    case BIO_CTRL_GET_CALLBACK:
        {
            int (**fptr) (const BIO *bio, int state, int xret);

            fptr = (int (**)(const BIO *bio, int state, int xret))ptr;
            *fptr = data->info_callback;
        }
        break;
    default:
        ret = 0;
        break;
    }
    return (ret);
}