+/* 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 int create_peer(PEER *peer, SSL_CTX *ctx)
+{
+ static const int peer_buffer_size = 64 * 1024;
+ SSL *ssl = NULL;
+ unsigned char *read_buf = NULL, *write_buf = NULL;
+
+ if (!TEST_ptr(ssl = SSL_new(ctx))
+ || !TEST_ptr(write_buf = OPENSSL_zalloc(peer_buffer_size))
+ || !TEST_ptr(read_buf = OPENSSL_zalloc(peer_buffer_size)))
+ goto err;
+
+ peer->ssl = ssl;
+ peer->write_buf = write_buf;
+ peer->read_buf = read_buf;
+ peer->write_buf_len = peer->read_buf_len = peer_buffer_size;
+ return 1;
+err:
+ SSL_free(ssl);
+ OPENSSL_free(write_buf);
+ OPENSSL_free(read_buf);
+ return 0;
+}
+
+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)
+{
+ if (!TEST_int_eq(peer->status, PEER_RETRY)) {
+ peer->status = PEER_TEST_FAILURE;
+ } else {
+ int 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;
+
+ if (!TEST_int_eq(peer->status, PEER_RETRY)) {
+ peer->status = PEER_TEST_FAILURE;
+ return;
+ }
+
+ /* 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) {
+ if (!TEST_int_le(ret, peer->bytes_to_read)) {
+ peer->status = PEER_TEST_FAILURE;
+ return;
+ }
+ 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. */
+ if (!TEST_int_eq(ret, write_bytes)) {
+ peer->status = PEER_TEST_FAILURE;
+ return;
+ }
+ 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)