+int doit_biopair(SSL *s_ssl, SSL *c_ssl, long count,
+ clock_t *s_time, clock_t *c_time)
+ {
+ long cw_num = count, cr_num = count, sw_num = count, sr_num = count;
+ BIO *s_ssl_bio = NULL, *c_ssl_bio = NULL;
+ BIO *server = NULL, *server_io = NULL, *client = NULL, *client_io = NULL;
+ int ret = 1;
+
+ size_t bufsiz = 256; /* small buffer for testing */
+
+ if (!BIO_new_bio_pair(&server, bufsiz, &server_io, bufsiz))
+ goto err;
+ if (!BIO_new_bio_pair(&client, bufsiz, &client_io, bufsiz))
+ goto err;
+
+ s_ssl_bio = BIO_new(BIO_f_ssl());
+ if (!s_ssl_bio)
+ goto err;
+
+ c_ssl_bio = BIO_new(BIO_f_ssl());
+ if (!c_ssl_bio)
+ goto err;
+
+ SSL_set_connect_state(c_ssl);
+ SSL_set_bio(c_ssl, client, client);
+ (void)BIO_set_ssl(c_ssl_bio, c_ssl, BIO_NOCLOSE);
+
+ SSL_set_accept_state(s_ssl);
+ SSL_set_bio(s_ssl, server, server);
+ (void)BIO_set_ssl(s_ssl_bio, s_ssl, BIO_NOCLOSE);
+
+ do
+ {
+ /* c_ssl_bio: SSL filter BIO
+ *
+ * client: pseudo-I/O for SSL library
+ *
+ * client_io: client's SSL communication; usually to be
+ * relayed over some I/O facility, but in this
+ * test program, we're the server, too:
+ *
+ * server_io: server's SSL communication
+ *
+ * server: pseudo-I/O for SSL library
+ *
+ * s_ssl_bio: SSL filter BIO
+ *
+ * The client and the server each employ a "BIO pair":
+ * client + client_io, server + server_io.
+ * BIO pairs are symmetric. A BIO pair behaves similar
+ * to a non-blocking socketpair (but both endpoints must
+ * be handled by the same thread).
+ * [Here we could connect client and server to the ends
+ * of a single BIO pair, but then this code would be less
+ * suitable as an example for BIO pairs in general.]
+ *
+ * Useful functions for querying the state of BIO pair endpoints:
+ *
+ * BIO_ctrl_pending(bio) number of bytes we can read now
+ * BIO_ctrl_get_read_request(bio) number of bytes needed to fulfil
+ * other side's read attempt
+ * BIO_ctrl_get_write_guarantee(bio) number of bytes we can write now
+ *
+ * ..._read_request is never more than ..._write_guarantee;
+ * it depends on the application which one you should use.
+ */
+
+ /* We have non-blocking behaviour throughout this test program, but
+ * can be sure that there is *some* progress in each iteration; so
+ * we don't have to worry about ..._SHOULD_READ or ..._SHOULD_WRITE
+ * -- we just try everything in each iteration
+ */
+
+ {
+ /* CLIENT */
+
+ MS_STATIC char cbuf[1024*8];
+ int i, r;
+ clock_t c_clock = clock();
+
+ if (debug)
+ if (SSL_in_init(c_ssl))
+ printf("client waiting in SSL_connect - %s\n",
+ SSL_state_string_long(c_ssl));
+
+ if (cw_num > 0)
+ {
+ /* Write to server. */
+
+ if (cw_num > (long)sizeof cbuf)
+ i = sizeof cbuf;
+ else
+ i = (int)cw_num;
+ r = BIO_write(c_ssl_bio, cbuf, i);
+ if (r < 0)
+ {
+ if (!BIO_should_retry(c_ssl_bio))
+ {
+ fprintf(stderr,"ERROR in CLIENT\n");
+ goto err;
+ }
+ /* BIO_should_retry(...) can just be ignored here.
+ * The library expects us to call BIO_write with
+ * the same arguments again, and that's what we will
+ * do in the next iteration. */
+ }
+ else if (r == 0)
+ {
+ fprintf(stderr,"SSL CLIENT STARTUP FAILED\n");
+ goto err;
+ }
+ else
+ {
+ if (debug)
+ printf("client wrote %d\n", r);
+ cw_num -= r;
+ }
+ }
+
+ if (cr_num > 0)
+ {
+ /* Read from server. */
+
+ r = BIO_read(c_ssl_bio, cbuf, sizeof(cbuf));
+ if (r < 0)
+ {
+ if (!BIO_should_retry(c_ssl_bio))
+ {
+ fprintf(stderr,"ERROR in CLIENT\n");
+ goto err;
+ }
+ /* Again, "BIO_should_retry" can be ignored. */
+ }
+ else if (r == 0)
+ {
+ fprintf(stderr,"SSL CLIENT STARTUP FAILED\n");
+ goto err;
+ }
+ else
+ {
+ if (debug)
+ printf("client read %d\n", r);
+ cr_num -= r;
+ }
+ }
+
+ /* c_time and s_time increments will typically be very small
+ * (depending on machine speed and clock tick intervals),
+ * but sampling over a large number of connections should
+ * result in fairly accurate figures. We cannot guarantee
+ * a lot, however -- if each connection lasts for exactly
+ * one clock tick, it will be counted only for the client
+ * or only for the server or even not at all.
+ */
+ *c_time += (clock() - c_clock);
+ }
+
+ {
+ /* SERVER */
+
+ MS_STATIC char sbuf[1024*8];
+ int i, r;
+ clock_t s_clock = clock();
+
+ if (debug)
+ if (SSL_in_init(s_ssl))
+ printf("server waiting in SSL_accept - %s\n",
+ SSL_state_string_long(s_ssl));
+
+ if (sw_num > 0)
+ {
+ /* Write to client. */
+
+ if (sw_num > (long)sizeof sbuf)
+ i = sizeof sbuf;
+ else
+ i = (int)sw_num;
+ r = BIO_write(s_ssl_bio, sbuf, i);
+ if (r < 0)
+ {
+ if (!BIO_should_retry(s_ssl_bio))
+ {
+ fprintf(stderr,"ERROR in SERVER\n");
+ goto err;
+ }
+ /* Ignore "BIO_should_retry". */
+ }
+ else if (r == 0)
+ {
+ fprintf(stderr,"SSL SERVER STARTUP FAILED\n");
+ goto err;
+ }
+ else
+ {
+ if (debug)
+ printf("server wrote %d\n", r);
+ sw_num -= r;
+ }
+ }
+
+ if (sr_num > 0)
+ {
+ /* Read from client. */
+
+ r = BIO_read(s_ssl_bio, sbuf, sizeof(sbuf));
+ if (r < 0)
+ {
+ if (!BIO_should_retry(s_ssl_bio))
+ {
+ fprintf(stderr,"ERROR in SERVER\n");
+ goto err;
+ }
+ /* blah, blah */
+ }
+ else if (r == 0)
+ {
+ fprintf(stderr,"SSL SERVER STARTUP FAILED\n");
+ goto err;
+ }
+ else
+ {
+ if (debug)
+ printf("server read %d\n", r);
+ sr_num -= r;
+ }
+ }
+
+ *s_time += (clock() - s_clock);
+ }
+
+ {
+ /* "I/O" BETWEEN CLIENT AND SERVER. */
+
+ size_t r1, r2;
+ BIO *io1 = server_io, *io2 = client_io;
+ /* we use the non-copying interface for io1
+ * and the standard BIO_write/BIO_read interface for io2
+ */
+
+ static int prev_progress = 1;
+ int progress = 0;
+
+ /* io1 to io2 */
+ do
+ {
+ size_t num;
+ int r;
+
+ r1 = BIO_ctrl_pending(io1);
+ r2 = BIO_ctrl_get_write_guarantee(io2);
+
+ num = r1;
+ if (r2 < num)
+ num = r2;
+ if (num)
+ {
+ char *dataptr;
+
+ if (INT_MAX < num) /* yeah, right */
+ num = INT_MAX;
+
+ r = BIO_nread(io1, &dataptr, (int)num);
+ assert(r > 0);
+ assert(r <= (int)num);
+ /* possibly r < num (non-contiguous data) */
+ num = r;
+ r = BIO_write(io2, dataptr, (int)num);
+ if (r != (int)num) /* can't happen */
+ {
+ fprintf(stderr, "ERROR: BIO_write could not write "
+ "BIO_ctrl_get_write_guarantee() bytes");
+ goto err;
+ }
+ progress = 1;
+
+ if (debug)
+ printf((io1 == client_io) ?
+ "C->S relaying: %d bytes\n" :
+ "S->C relaying: %d bytes\n",
+ (int)num);
+ }
+ }
+ while (r1 && r2);
+
+ /* io2 to io1 */
+ {
+ size_t num;
+ int r;
+
+ r1 = BIO_ctrl_pending(io2);
+ r2 = BIO_ctrl_get_read_request(io1);
+ /* here we could use ..._get_write_guarantee instead of
+ * ..._get_read_request, but by using the latter
+ * we test restartability of the SSL implementation
+ * more thoroughly */
+ num = r1;
+ if (r2 < num)
+ num = r2;
+ if (num)
+ {
+ char *dataptr;
+
+ if (INT_MAX < num)
+ num = INT_MAX;
+
+ if (num > 1)
+ --num; /* test restartability even more thoroughly */
+
+ r = BIO_nwrite(io1, &dataptr, (int)num);
+ assert(r > 0);
+ assert(r <= (int)num);
+ num = r;
+ r = BIO_read(io2, dataptr, (int)num);
+ if (r != (int)num) /* can't happen */
+ {
+ fprintf(stderr, "ERROR: BIO_read could not read "
+ "BIO_ctrl_pending() bytes");
+ goto err;
+ }
+ progress = 1;
+
+ if (debug)
+ printf((io2 == client_io) ?
+ "C->S relaying: %d bytes\n" :
+ "S->C relaying: %d bytes\n",
+ (int)num);
+ }
+ } /* no loop, BIO_ctrl_get_read_request now returns 0 anyway */
+
+ if (!progress && !prev_progress)
+ if (cw_num > 0 || cr_num > 0 || sw_num > 0 || sr_num > 0)
+ {
+ fprintf(stderr, "ERROR: got stuck\n");
+ if (strcmp("SSLv2", SSL_get_version(c_ssl)) == 0)
+ {
+ fprintf(stderr, "This can happen for SSL2 because "
+ "CLIENT-FINISHED and SERVER-VERIFY are written \n"
+ "concurrently ...");
+ if (strncmp("2SCF", SSL_state_string(c_ssl), 4) == 0
+ && strncmp("2SSV", SSL_state_string(s_ssl), 4) == 0)
+ {
+ fprintf(stderr, " ok.\n");
+ goto end;
+ }
+ }
+ fprintf(stderr, " ERROR.\n");
+ goto err;
+ }
+ prev_progress = progress;
+ }
+ }
+ while (cw_num > 0 || cr_num > 0 || sw_num > 0 || sr_num > 0);
+
+ if (verbose)
+ print_details(c_ssl, "DONE via BIO pair: ");
+end:
+ ret = 0;
+
+ err:
+ ERR_print_errors(bio_err);
+
+ if (server)
+ BIO_free(server);
+ if (server_io)
+ BIO_free(server_io);
+ if (client)
+ BIO_free(client);
+ if (client_io)
+ BIO_free(client_io);
+ if (s_ssl_bio)
+ BIO_free(s_ssl_bio);
+ if (c_ssl_bio)
+ BIO_free(c_ssl_bio);
+
+ return ret;
+ }
+
+