+/* 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;
+ }
+}
+