static void encrypt()

in c/src/tls/openssl.c [2011:2089]


static void encrypt(pn_tls_t *tls) {
  assert(tls);
  buff_ptr curr_result = current_encrypted_result(tls);
  pbuffer_t *pending = next_encrypt_pending(tls);
  bool try_shutdown_again = false;

  while (true) {
    // Insert unencrypted data into BIO.
    // This loop must run even if tls->pn_tls_err, in order to extract the error message for the peer.
    // OpenSSL maps each write to a separate TLS record.
    // The SSL can take 16KB + a bit before blocking.
    // TODO: consider allowing application to configure BIO buffer size on encrypt side.

    while (pending && !tls->enc_wblocked && tls->can_shutdown) {
      size_t n = pending->size - tls->encrypt_pending_offset;
      if (n) {
        char *bytes = pending->bytes + pending->offset + tls->encrypt_pending_offset;
        int wcount = SSL_write(tls->ssl, bytes, n);
        if (wcount < (int) n)
          tls->enc_wblocked = true;
        if (wcount > 0) {
          tls->enc_rblocked = false;
          tls->encrypt_pending_offset += wcount;
        }
      }
      pending = next_encrypt_pending(tls);
    }

    // Finished writing?
    if (tls->enc_closed && !pending && !tls->ssl_shutdown) {
      try_shutdown_again = !try_shutdown(tls);  // may need multple tries if BIO buffers need draining
    }

    // Extract encrypted data from other side of BIO.
    while (curr_result && !tls->enc_rblocked) {
      pbuffer_t *result = &tls->eresult_buffers[curr_result-1];
      size_t n = room(result);
      assert(n);
      int rcount = BIO_read(tls->bio_net_io, result->bytes + result->offset + result->size, n);
      if (rcount < (int) n)
        tls->enc_rblocked = true;
      if (rcount > 0) {
        if (!tls->pn_tls_err)
          tls->enc_wblocked = false;
        if (result->size == 0) {
          // first data inserted: convert from blank type to encrypted type
          assert(result->type == buff_eresult_blank);
          blank_eresult_pop(tls, curr_result);
          encrypted_result_add(tls, curr_result);
        }
        result->size += rcount;
        if (room(result) == 0) {
          // Tentatively set a blank buffer for future output without popping.
          curr_result = tls->eresult_first_blank;
        }
        if (try_shutdown_again)
          try_shutdown_again = !try_shutdown(tls);
      } else if (!BIO_should_retry(tls->bio_net_io)) {
        int reason = SSL_get_error( tls->ssl, rcount );
        switch (reason) {
        case SSL_ERROR_ZERO_RETURN:
          // SSL closed cleanly
          ssl_log(NULL, PN_LEVEL_TRACE, "SSL connection has closed");
          // TODO: replacement for:       start_ssl_shutdown(transport);  // KAG: not sure - this may not be necessary
          tls->dec_closed = true;
          tls->ssl_closed = true; // TODO: still true?
          break;
        default:
          tls_fatal(tls, reason, PN_TLS_PROTOCOL_ERR);
        }
      }
    }

    // Done if not possible to move any more bytes from input to output bufs
    if ((!pending || tls->enc_wblocked || !tls->can_shutdown) // write side
        && (!curr_result || tls->enc_rblocked)) // read side
      break;
  }
}