blob: 4ba1574ca8839cc5b9d331892a32d4541134b02d [file] [log] [blame]
/* _ _
** _ __ ___ ___ __| | ___ ___| | mod_ssl
** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL
** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org
** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org
** |_____|
** ssl_engine_io.c
** I/O Functions
*/
/* ====================================================================
* Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by
* Ralf S. Engelschall <rse@engelschall.com> for use in the
* mod_ssl project (http://www.modssl.org/)."
*
* 4. The names "mod_ssl" must not be used to endorse or promote
* products derived from this software without prior written
* permission. For written permission, please contact
* rse@engelschall.com.
*
* 5. Products derived from this software may not be called "mod_ssl"
* nor may "mod_ssl" appear in their names without prior
* written permission of Ralf S. Engelschall.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by
* Ralf S. Engelschall <rse@engelschall.com> for use in the
* mod_ssl project (http://www.modssl.org/)."
*
* THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
* HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*/
/* ``MY HACK: This universe.
Just one little problem:
core keeps dumping.''
-- Unknown */
#include "mod_ssl.h"
/* _________________________________________________________________
**
** I/O Request Body Sucking and Re-Injection
** _________________________________________________________________
*/
#ifndef SSL_CONSERVATIVE
/*
* Background:
*
* 1. When the client sends a HTTP/HTTPS request, Apache's core code
* reads only the request line ("METHOD /path HTTP/x.y") and the
* attached MIME headers ("Foo: bar") up to the terminating line ("CR
* LF"). An attached request body (for instance the data of a POST
* method) is _NOT_ read. Instead it is read by mod_cgi's content
* handler and directly passed to the CGI script.
*
* 2. mod_ssl supports per-directory re-configuration of SSL parameters.
* This is implemented by performing an SSL renegotiation of the
* re-configured parameters after the request is read, but before the
* response is sent. In more detail: the renegotiation happens after the
* request line and MIME headers were read, but _before_ the attached
* request body is read. The reason simply is that in the HTTP protocol
* usually there is no acknowledgment step between the headers and the
* body (there is the 100-continue feature and the chunking facility
* only), so Apache has no API hook for this step.
*
* 3. the problem now occurs when the client sends a POST request for
* URL /foo via HTTPS the server and the server has SSL parameters
* re-configured on a per-URL basis for /foo. Then mod_ssl has to
* perform an SSL renegotiation after the request was read and before
* the response is sent. But the problem is the pending POST body data
* in the receive buffer of SSL (which Apache still has not read - it's
* pending until mod_cgi sucks it in). When mod_ssl now tries to perform
* the renegotiation the pending data leads to an I/O error.
*
* Solution Idea:
*
* There are only two solutions: Either to simply state that POST
* requests to URLs with SSL re-configurations are not allowed, or to
* renegotiate really after the _complete_ request (i.e. including
* the POST body) was read. Obviously the latter would be preferred,
* but it cannot be done easily inside Apache, because as already
* mentioned, there is no API step between the body reading and the body
* processing. And even when we mod_ssl would hook directly into the
* loop of mod_cgi, we wouldn't solve the problem for other handlers, of
* course. So the only general solution is to suck in the pending data
* of the request body from the OpenSSL BIO into the Apache BUFF. Then
* the renegotiation can be done and after this step Apache can proceed
* processing the request as before.
*
* Solution Implementation:
*
* We cannot simply suck in the data via an SSL_read-based loop because of
* HTTP chunking. Instead we _have_ to use the Apache API for this step which
* is aware of HTTP chunking. So the trick is to suck in the pending request
* data via the Apache API (which uses Apache's BUFF code and in the
* background mod_ssl's I/O glue code) and re-inject it later into the Apache
* BUFF code again. This way the data flows twice through the Apache BUFF, of
* course. But this way the solution doesn't depend on any Apache specifics
* and is fully transparent to Apache modules.
*/
struct ssl_io_suck_st {
BOOL active;
char *bufptr;
int buflen;
char *pendptr;
int pendlen;
};
/* prepare request_rec structure for input sucking */
static void ssl_io_suck_start(request_rec *r)
{
struct ssl_io_suck_st *ss;
ss = ap_ctx_get(r->ctx, "ssl::io::suck");
if (ss == NULL) {
ss = ap_palloc(r->pool, sizeof(struct ssl_io_suck_st));
ap_ctx_set(r->ctx, "ssl::io::suck", ss);
ss->buflen = 8192;
ss->bufptr = ap_palloc(r->pool, ss->buflen);
}
ss->pendptr = ss->bufptr;
ss->pendlen = 0;
ss->active = FALSE;
return;
}
/* record a sucked input chunk */
static void ssl_io_suck_record(request_rec *r, char *buf, int len)
{
struct ssl_io_suck_st *ss;
if ((ss = ap_ctx_get(r->ctx, "ssl::io::suck")) == NULL)
return;
if (((ss->bufptr + ss->buflen) - (ss->pendptr + ss->pendlen)) < len) {
/* "expand" buffer: actually we cannot really expand the buffer
here, because Apache's pool system doesn't support expanding chunks
of memory. Instead we have to either reuse processed data or
allocate a new chunk of memory in advance if we really need more
memory. */
int newlen;
char *newptr;
if (( (ss->pendptr - ss->bufptr)
+ ((ss->bufptr + ss->buflen) - (ss->pendptr + ss->pendlen)) ) >= len) {
/* make memory available by reusing already processed data */
memmove(ss->bufptr, ss->pendptr, ss->pendlen);
ss->pendptr = ss->bufptr;
}
else {
/* too bad, we have to allocate a new larger buffer */
newlen = (ss->buflen * 2) + len;
newptr = ap_palloc(r->pool, newlen);
ss->bufptr = newptr;
ss->buflen = newlen;
memcpy(ss->bufptr, ss->pendptr, ss->pendlen);
ss->pendptr = ss->bufptr;
}
}
memcpy(ss->pendptr+ss->pendlen, buf, len);
ss->pendlen += len;
return;
}
/* finish request_rec after input sucking */
static void ssl_io_suck_end(request_rec *r)
{
struct ssl_io_suck_st *ss;
if ((ss = ap_ctx_get(r->ctx, "ssl::io::suck")) == NULL)
return;
ss->active = TRUE;
r->read_body = REQUEST_NO_BODY;
r->read_length = 0;
r->read_chunked = 0;
r->remaining = 0;
ap_bsetflag(r->connection->client, B_CHUNK, 0);
return;
}
void ssl_io_suck(request_rec *r, SSL *ssl)
{
int rc;
int len;
char *buf;
int buflen;
char c;
int sucked;
if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)) == OK) {
if (ap_should_client_block(r)) {
/* read client request block through Apache API */
buflen = HUGE_STRING_LEN;
buf = ap_palloc(r->pool, buflen);
ap_hard_timeout("SSL I/O request body pre-sucking", r);
sucked = 0;
ssl_io_suck_start(r);
while ((len = ap_get_client_block(r, buf, buflen)) > 0) {
ssl_io_suck_record(r, buf, len);
sucked += len;
}
ssl_io_suck_end(r);
ap_kill_timeout(r);
/* suck trailing data (usually CR LF) which
is still in the Apache BUFF layer */
while (ap_bpeekc(r->connection->client) != EOF) {
c = ap_bgetc(r->connection->client);
ssl_io_suck_record(r, &c, 1);
sucked++;
}
ssl_log(r->server, SSL_LOG_TRACE,
"I/O: sucked %d bytes of input data from SSL/TLS I/O layer "
"for delayed injection into Apache I/O layer", sucked);
}
}
return;
}
/* the SSL_read replacement routine which knows about the suck buffer */
static int ssl_io_suck_read(SSL *ssl, char *buf, int len)
{
ap_ctx *actx;
struct ssl_io_suck_st *ss;
request_rec *r = NULL;
int rv;
actx = (ap_ctx *)SSL_get_app_data2(ssl);
if (actx != NULL)
r = (request_rec *)ap_ctx_get(actx, "ssl::request_rec");
rv = -1;
if (r != NULL) {
ss = ap_ctx_get(r->ctx, "ssl::io::suck");
if (ss != NULL) {
if (ss->active && ss->pendlen > 0) {
/* ok, there is pre-sucked data */
len = (ss->pendlen > len ? len : ss->pendlen);
memcpy(buf, ss->pendptr, len);
ss->pendptr += len;
ss->pendlen -= len;
ssl_log(r->server, SSL_LOG_TRACE,
"I/O: injecting %d bytes of pre-sucked data "
"into Apache I/O layer", len);
rv = len;
}
}
}
if (rv == -1)
rv = SSL_read(ssl, buf, len);
return rv;
}
/* override SSL_read in the following code... */
#define SSL_read ssl_io_suck_read
#endif /* !SSL_CONSERVATIVE */
/* _________________________________________________________________
**
** I/O Hooks
** _________________________________________________________________
*/
#ifndef NO_WRITEV
#include <sys/types.h>
#include <sys/uio.h>
#endif
static int ssl_io_hook_read(BUFF *fb, char *buf, int len);
static int ssl_io_hook_write(BUFF *fb, char *buf, int len);
#ifndef NO_WRITEV
static int ssl_io_hook_writev(BUFF *fb, const struct iovec *iov, int iovcnt);
#endif
#ifdef WIN32
static int ssl_io_hook_recvwithtimeout(BUFF *fb, char *buf, int len);
static int ssl_io_hook_sendwithtimeout(BUFF *fb, const char *buf, int len);
#endif /* WIN32 */
void ssl_io_register(void)
{
ap_hook_register("ap::buff::read", ssl_io_hook_read, AP_HOOK_NOCTX);
ap_hook_register("ap::buff::write", ssl_io_hook_write, AP_HOOK_NOCTX);
#ifndef NO_WRITEV
ap_hook_register("ap::buff::writev", ssl_io_hook_writev, AP_HOOK_NOCTX);
#endif
#ifdef WIN32
ap_hook_register("ap::buff::recvwithtimeout",
ssl_io_hook_recvwithtimeout, AP_HOOK_NOCTX);
ap_hook_register("ap::buff::sendwithtimeout",
ssl_io_hook_sendwithtimeout, AP_HOOK_NOCTX);
#endif
return;
}
void ssl_io_unregister(void)
{
ap_hook_unregister("ap::buff::read", ssl_io_hook_read);
ap_hook_unregister("ap::buff::write", ssl_io_hook_write);
#ifndef NO_WRITEV
ap_hook_unregister("ap::buff::writev", ssl_io_hook_writev);
#endif
#ifdef WIN32
ap_hook_unregister("ap::buff::recvwithtimeout", ssl_io_hook_recvwithtimeout);
ap_hook_unregister("ap::buff::sendwithtimeout", ssl_io_hook_sendwithtimeout);
#endif
return;
}
static int ssl_io_hook_read(BUFF *fb, char *buf, int len)
{
SSL *ssl;
conn_rec *c;
int rc;
if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL) {
rc = SSL_read(ssl, buf, len);
/*
* Simulate an EINTR in case OpenSSL wants to read more.
* (This is usually the case when the client forces an SSL
* renegotation which is handled implicitly by OpenSSL.)
*/
if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_WANT_READ)
errno = EINTR;
/*
* Log SSL errors
*/
if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_SSL) {
c = (conn_rec *)SSL_get_app_data(ssl);
ssl_log(c->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
"SSL error on reading data");
}
/*
* read(2) returns only the generic error number -1
*/
if (rc < 0)
rc = -1;
}
else
rc = read(fb->fd_in, buf, len);
return rc;
}
static int ssl_io_hook_write(BUFF *fb, char *buf, int len)
{
SSL *ssl;
conn_rec *c;
int rc;
if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL) {
rc = SSL_write(ssl, buf, len);
/*
* Simulate an EINTR in case OpenSSL wants to write more.
*/
if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_WANT_WRITE)
errno = EINTR;
/*
* Log SSL errors
*/
if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_SSL) {
c = (conn_rec *)SSL_get_app_data(ssl);
ssl_log(c->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
"SSL error on writing data");
}
/*
* write(2) returns only the generic error number -1
*/
if (rc < 0)
rc = -1;
}
else
rc = write(fb->fd, buf, len);
return rc;
}
#ifndef NO_WRITEV
/* the prototype for our own SSL_writev() */
static int SSL_writev(SSL *, const struct iovec *, int);
static int ssl_io_hook_writev(BUFF *fb, const struct iovec *iov, int iovcnt)
{
SSL *ssl;
conn_rec *c;
int rc;
if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL) {
rc = SSL_writev(ssl, iov, iovcnt);
/*
* Simulate an EINTR in case OpenSSL wants to write more.
*/
if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_WANT_WRITE)
errno = EINTR;
/*
* Log SSL errors
*/
if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_SSL) {
c = (conn_rec *)SSL_get_app_data(ssl);
ssl_log(c->server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
"SSL error on writing data");
}
/*
* writev(2) returns only the generic error number -1
*/
if (rc < 0)
rc = -1;
}
else
rc = writev(fb->fd, iov, iovcnt);
return rc;
}
#endif
#ifdef WIN32
/* these two functions are exported from buff.c under WIN32 */
API_EXPORT(int) sendwithtimeout(int sock, const char *buf, int len, int flags);
API_EXPORT(int) recvwithtimeout(int sock, char *buf, int len, int flags);
/* and the prototypes for our SSL_xxx variants */
static int SSL_sendwithtimeout(BUFF *fb, const char *buf, int len);
static int SSL_recvwithtimeout(BUFF *fb, char *buf, int len);
static int ssl_io_hook_recvwithtimeout(BUFF *fb, char *buf, int len)
{
SSL *ssl;
int rc;
if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL)
rc = SSL_recvwithtimeout(fb, buf, len);
else
rc = recvwithtimeout(fb->fd, buf, len, 0);
return rc;
}
static int ssl_io_hook_sendwithtimeout(BUFF *fb, const char *buf, int len)
{
SSL *ssl;
int rc;
if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL)
rc = SSL_sendwithtimeout(fb, buf, len);
else
rc = sendwithtimeout(fb->fd, buf, len, 0);
return rc;
}
#endif /* WIN32 */
/* _________________________________________________________________
**
** Special Functions for OpenSSL
** _________________________________________________________________
*/
#ifdef WIN32
static int SSL_sendwithtimeout(BUFF *fb, const char *buf, int len)
{
int iostate = 1;
fd_set fdset;
struct timeval tv;
int err = WSAEWOULDBLOCK;
int rv;
int retry;
int sock = fb->fd;
SSL *ssl;
ssl = ap_ctx_get(fb->ctx, "ssl");
if (!(tv.tv_sec = ap_check_alarm()))
return (SSL_write(ssl, (char*)buf, len));
rv = ioctlsocket(sock, FIONBIO, &iostate);
iostate = 0;
if (rv) {
err = WSAGetLastError();
ap_assert(0);
}
rv = SSL_write(ssl, (char*)buf, len);
if (rv <= 0) {
if (BIO_sock_should_retry(rv)) {
do {
retry = 0;
FD_ZERO(&fdset);
FD_SET((unsigned int)sock, &fdset);
tv.tv_usec = 0;
rv = select(FD_SETSIZE, NULL, &fdset, NULL, &tv);
if (rv == SOCKET_ERROR)
err = WSAGetLastError();
else if (rv == 0) {
ioctlsocket(sock, FIONBIO, &iostate);
if(ap_check_alarm() < 0) {
WSASetLastError(EINTR); /* Simulate an alarm() */
return (SOCKET_ERROR);
}
}
else {
rv = SSL_write(ssl, (char*)buf, len);
if (BIO_sock_should_retry(rv)) {
ap_log_error(APLOG_MARK,APLOG_DEBUG, NULL,
"select claimed we could write, "
"but in fact we couldn't. "
"This is a bug in Windows.");
retry = 1;
Sleep(100);
}
}
} while(retry);
}
}
ioctlsocket(sock, FIONBIO, &iostate);
if (rv == SOCKET_ERROR)
WSASetLastError(err);
return (rv);
}
static int SSL_recvwithtimeout(BUFF *fb, char *buf, int len)
{
int iostate = 1;
fd_set fdset;
struct timeval tv;
int err = WSAEWOULDBLOCK;
int rv;
int sock = fb->fd_in;
SSL *ssl;
int retry;
ssl = ap_ctx_get(fb->ctx, "ssl");
if (!(tv.tv_sec = ap_check_alarm()))
return (SSL_read(ssl, buf, len));
rv = ioctlsocket(sock, FIONBIO, &iostate);
iostate = 0;
ap_assert(!rv);
rv = SSL_read(ssl, buf, len);
if (rv <= 0) {
if (BIO_sock_should_retry(rv)) {
do {
retry = 0;
FD_ZERO(&fdset);
FD_SET((unsigned int)sock, &fdset);
tv.tv_usec = 0;
rv = select(FD_SETSIZE, &fdset, NULL, NULL, &tv);
if (rv == SOCKET_ERROR)
err = WSAGetLastError();
else if (rv == 0) {
ioctlsocket(sock, FIONBIO, &iostate);
ap_check_alarm();
WSASetLastError(WSAEWOULDBLOCK);
return (SOCKET_ERROR);
}
else {
rv = SSL_read(ssl, buf, len);
if (rv == SOCKET_ERROR) {
if (BIO_sock_should_retry(rv)) {
ap_log_error(APLOG_MARK,APLOG_DEBUG, NULL,
"select claimed we could read, "
"but in fact we couldn't. "
"This is a bug in Windows.");
retry = 1;
Sleep(100);
}
else {
err = WSAGetLastError();
}
}
}
} while(retry);
}
}
ioctlsocket(sock, FIONBIO, &iostate);
if (rv == SOCKET_ERROR)
WSASetLastError(err);
return (rv);
}
#endif /*WIN32*/
/*
* There is no SSL_writev() provided by OpenSSL. The reason is mainly because
* OpenSSL has to fragment the data itself again for the SSL record layer, so a
* writev() like interface makes not much sense. What we do is to emulate it
* to at least being able to use the write() like interface. But keep in mind
* that the network I/O performance is not write() like, of course.
*/
#ifndef NO_WRITEV
static int SSL_writev(SSL *ssl, const struct iovec *iov, int iovcnt)
{
int i;
int n;
int rc;
rc = 0;
for (i = 0; i < iovcnt; i++) {
if ((n = SSL_write(ssl, iov[i].iov_base, iov[i].iov_len)) == -1) {
rc = -1;
break;
}
rc += n;
}
return rc;
}
#endif
/* _________________________________________________________________
**
** I/O Data Debugging
** _________________________________________________________________
*/
#define DUMP_WIDTH 16
static void ssl_io_data_dump(server_rec *srvr, const char *s, long len)
{
char buf[256];
char tmp[64];
int i, j, rows, trunc;
unsigned char ch;
trunc = 0;
for(; (len > 0) && ((s[len-1] == ' ') || (s[len-1] == '\0')); len--)
trunc++;
rows = (len / DUMP_WIDTH);
if ((rows * DUMP_WIDTH) < len)
rows++;
ssl_log(srvr, SSL_LOG_DEBUG|SSL_NO_TIMESTAMP|SSL_NO_LEVELID,
"+-------------------------------------------------------------------------+");
for(i = 0 ; i< rows; i++) {
ap_snprintf(tmp, sizeof(tmp), "| %04x: ", i * DUMP_WIDTH);
ap_cpystrn(buf, tmp, sizeof(buf));
for (j = 0; j < DUMP_WIDTH; j++) {
if (((i * DUMP_WIDTH) + j) >= len)
ap_cpystrn(buf+strlen(buf), " ", sizeof(buf)-strlen(buf));
else {
ch = ((unsigned char)*((char *)(s) + i * DUMP_WIDTH + j)) & 0xff;
ap_snprintf(tmp, sizeof(tmp), "%02x%c", ch , j==7 ? '-' : ' ');
ap_cpystrn(buf+strlen(buf), tmp, sizeof(buf)-strlen(buf));
}
}
ap_cpystrn(buf+strlen(buf), " ", sizeof(buf)-strlen(buf));
for (j = 0; j < DUMP_WIDTH; j++) {
if (((i * DUMP_WIDTH) + j) >= len)
ap_cpystrn(buf+strlen(buf), " ", sizeof(buf)-strlen(buf));
else {
ch = ((unsigned char)*((char *)(s) + i * DUMP_WIDTH + j)) & 0xff;
ap_snprintf(tmp, sizeof(tmp), "%c", ((ch >= ' ') && (ch <= '~')) ? ch : '.');
ap_cpystrn(buf+strlen(buf), tmp, sizeof(buf)-strlen(buf));
}
}
ap_cpystrn(buf+strlen(buf), " |", sizeof(buf)-strlen(buf));
ssl_log(srvr, SSL_LOG_DEBUG|SSL_NO_TIMESTAMP|SSL_NO_LEVELID, "%s", buf);
}
if (trunc > 0)
ssl_log(srvr, SSL_LOG_DEBUG|SSL_NO_TIMESTAMP|SSL_NO_LEVELID,
"| %04x - <SPACES/NULS>", len + trunc);
ssl_log(srvr, SSL_LOG_DEBUG|SSL_NO_TIMESTAMP|SSL_NO_LEVELID,
"+-------------------------------------------------------------------------+");
return;
}
long ssl_io_data_cb(BIO *bio, int cmd, const char *argp, int argi, long argl, long rc)
{
SSL *ssl;
conn_rec *c;
server_rec *s;
if ((ssl = (SSL *)BIO_get_callback_arg(bio)) == NULL)
return rc;
if ((c = (conn_rec *)SSL_get_app_data(ssl)) == NULL)
return rc;
s = c->server;
if ( cmd == (BIO_CB_WRITE|BIO_CB_RETURN)
|| cmd == (BIO_CB_READ |BIO_CB_RETURN) ) {
if (rc >= 0) {
ssl_log(s, SSL_LOG_DEBUG,
"%s: %s %ld/%d bytes %s BIO#%08X [mem: %08lX] %s",
SSL_LIBRARY_NAME,
(cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "write" : "read"),
rc, argi, (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "to" : "from"),
bio, argp,
(argp != NULL ? "(BIO dump follows)" : "(Ops, no memory buffer?)"));
if (argp != NULL)
ssl_io_data_dump(s, argp, rc);
}
else {
ssl_log(s, SSL_LOG_DEBUG,
"%s: I/O error, %d bytes expected to %s on BIO#%08X [mem: %08lX]",
SSL_LIBRARY_NAME, argi,
(cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "write" : "read"),
bio, argp);
}
}
return rc;
}