+/* An SSL object and associated read-write buffers. */
+typedef struct peer_st {
+ SSL *ssl;
+ /* Buffer lengths are int to match the SSL read/write API. */
+ unsigned char *write_buf;
+ int write_buf_len;
+ unsigned char *read_buf;
+ int read_buf_len;
+ int bytes_to_write;
+ int bytes_to_read;
+ peer_status_t status;
+} PEER;
+
+static void create_peer(PEER *peer, SSL_CTX *ctx)
+{
+ static const int peer_buffer_size = 64 * 1024;
+
+ peer->ssl = SSL_new(ctx);
+ TEST_check(peer->ssl != NULL);
+ peer->write_buf = OPENSSL_zalloc(peer_buffer_size);
+ TEST_check(peer->write_buf != NULL);
+ peer->read_buf = OPENSSL_zalloc(peer_buffer_size);
+ TEST_check(peer->read_buf != NULL);
+ peer->write_buf_len = peer->read_buf_len = peer_buffer_size;
+}
+
+static void peer_free_data(PEER *peer)
+{
+ SSL_free(peer->ssl);
+ OPENSSL_free(peer->write_buf);
+ OPENSSL_free(peer->read_buf);
+}
+
+/*
+ * Note that we could do the handshake transparently under an SSL_write,
+ * but separating the steps is more helpful for debugging test failures.
+ */
+static void do_handshake_step(PEER *peer)
+{
+ int ret;
+
+ TEST_check(peer->status == PEER_RETRY);
+ ret = SSL_do_handshake(peer->ssl);
+
+ if (ret == 1) {
+ peer->status = PEER_SUCCESS;
+ } else if (ret == 0) {
+ peer->status = PEER_ERROR;
+ } else {
+ int error = SSL_get_error(peer->ssl, ret);
+ /* Memory bios should never block with SSL_ERROR_WANT_WRITE. */
+ if (error != SSL_ERROR_WANT_READ)
+ peer->status = PEER_ERROR;
+ }
+}
+
+/*-
+ * Send/receive some application data. The read-write sequence is
+ * Peer A: (R) W - first read will yield no data
+ * Peer B: R W
+ * ...
+ * Peer A: R W
+ * Peer B: R W
+ * Peer A: R
+ */
+static void do_app_data_step(PEER *peer)
+{
+ int ret = 1, write_bytes;
+
+ TEST_check(peer->status == PEER_RETRY);
+
+ /* We read everything available... */
+ while (ret > 0 && peer->bytes_to_read) {
+ ret = SSL_read(peer->ssl, peer->read_buf, peer->read_buf_len);
+ if (ret > 0) {
+ TEST_check(ret <= peer->bytes_to_read);
+ peer->bytes_to_read -= ret;
+ } else if (ret == 0) {
+ peer->status = PEER_ERROR;
+ return;
+ } else {
+ int error = SSL_get_error(peer->ssl, ret);
+ if (error != SSL_ERROR_WANT_READ) {
+ peer->status = PEER_ERROR;
+ return;
+ } /* Else continue with write. */
+ }
+ }
+
+ /* ... but we only write one write-buffer-full of data. */
+ write_bytes = peer->bytes_to_write < peer->write_buf_len ? peer->bytes_to_write :
+ peer->write_buf_len;
+ if (write_bytes) {
+ ret = SSL_write(peer->ssl, peer->write_buf, write_bytes);
+ if (ret > 0) {
+ /* SSL_write will only succeed with a complete write. */
+ TEST_check(ret == write_bytes);
+ peer->bytes_to_write -= ret;
+ } else {
+ /*
+ * We should perhaps check for SSL_ERROR_WANT_READ/WRITE here
+ * but this doesn't yet occur with current app data sizes.
+ */
+ peer->status = PEER_ERROR;
+ return;
+ }
+ }
+
+ /*
+ * We could simply finish when there was nothing to read, and we have
+ * nothing left to write. But keeping track of the expected number of bytes
+ * to read gives us somewhat better guarantees that all data sent is in fact
+ * received.
+ */
+ if (!peer->bytes_to_write && !peer->bytes_to_read) {
+ peer->status = PEER_SUCCESS;
+ }
+}
+
+static void do_reneg_setup_step(const SSL_TEST_CTX *test_ctx, PEER *peer)
+{
+ int ret;
+ char buf;
+
+ TEST_check(peer->status == PEER_RETRY);
+ TEST_check(test_ctx->handshake_mode == SSL_TEST_HANDSHAKE_RENEG_SERVER
+ || test_ctx->handshake_mode == SSL_TEST_HANDSHAKE_RENEG_CLIENT);
+
+ /* Check if we are the peer that is going to initiate */
+ if ((test_ctx->handshake_mode == SSL_TEST_HANDSHAKE_RENEG_SERVER
+ && SSL_is_server(peer->ssl))
+ || (test_ctx->handshake_mode == SSL_TEST_HANDSHAKE_RENEG_CLIENT
+ && !SSL_is_server(peer->ssl))) {
+ /*
+ * If we already asked for a renegotiation then fall through to the
+ * SSL_read() below.
+ */
+ if (!SSL_renegotiate_pending(peer->ssl)) {
+ /*
+ * If we are the client we will always attempt to resume the
+ * session. The server may or may not resume dependant on the
+ * setting of SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
+ */
+ if (SSL_is_server(peer->ssl))
+ ret = SSL_renegotiate(peer->ssl);
+ else
+ ret = SSL_renegotiate_abbreviated(peer->ssl);
+ if (!ret) {
+ peer->status = PEER_ERROR;
+ return;
+ }
+ do_handshake_step(peer);
+ /*
+ * If status is PEER_RETRY it means we're waiting on the peer to
+ * continue the handshake. As far as setting up the renegotiation is
+ * concerned that is a success. The next step will continue the
+ * handshake to its conclusion.
+ *
+ * If status is PEER_SUCCESS then we are the server and we have
+ * successfully sent the HelloRequest. We need to continue to wait
+ * until the handshake arrives from the client.
+ */
+ if (peer->status == PEER_RETRY)
+ peer->status = PEER_SUCCESS;
+ else if (peer->status == PEER_SUCCESS)
+ peer->status = PEER_RETRY;
+ return;
+ }
+ }
+
+ /*
+ * The SSL object is still expecting app data, even though it's going to
+ * get a handshake message. We try to read, and it should fail - after which
+ * we should be in a handshake
+ */
+ ret = SSL_read(peer->ssl, &buf, sizeof(buf));
+ if (ret >= 0) {
+ /*
+ * We're not actually expecting data - we're expecting a reneg to
+ * start
+ */
+ peer->status = PEER_ERROR;
+ return;
+ } else {
+ int error = SSL_get_error(peer->ssl, ret);
+ if (error != SSL_ERROR_WANT_READ) {
+ peer->status = PEER_ERROR;
+ return;
+ }
+ /* If we're no in init yet then we're not done with setup yet */
+ if (!SSL_in_init(peer->ssl))
+ return;
+ }
+
+ peer->status = PEER_SUCCESS;
+}
+
+