tor-android/external/badvpn_dns/nspr_support/BSSLConnection.c

1025 lines
29 KiB
C
Raw Normal View History

2015-01-25 11:08:34 +00:00
/**
* @file BSSLConnection.c
* @author Ambroz Bizjak <ambrop7@gmail.com>
*
* @section LICENSE
*
* 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. Neither the name of the author nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS 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 THE AUTHOR 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.
*/
#include <prerror.h>
#include <ssl.h>
#include <string.h>
#include <stdlib.h>
#include <misc/print_macros.h>
#include <base/BLog.h>
#include "BSSLConnection.h"
#include <generated/blog_channel_BSSLConnection.h>
#define THREADWORK_STATE_NONE 0
#define THREADWORK_STATE_HANDSHAKE 1
#define THREADWORK_STATE_READ 2
#define THREADWORK_STATE_WRITE 3
static void backend_threadwork_start (struct BSSLConnection_backend *b, int op);
static int backend_threadwork_do_io (struct BSSLConnection_backend *b);
static void connection_init_job_handler (BSSLConnection *o);
static void connection_init_up (BSSLConnection *o);
static void connection_try_io (BSSLConnection *o);
static void connection_threadwork_func_work (void *user);
static void connection_threadwork_handler_done (void *user);
static void connection_recv_job_handler (BSSLConnection *o);
static void connection_try_handshake (BSSLConnection *o);
static void connection_try_send (BSSLConnection *o);
static void connection_try_recv (BSSLConnection *o);
static void connection_send_if_handler_send (BSSLConnection *o, uint8_t *data, int data_len);
static void connection_recv_if_handler_recv (BSSLConnection *o, uint8_t *data, int data_len);
int bprconnection_initialized = 0;
PRDescIdentity bprconnection_identity;
static PRFileDesc * get_bottom (PRFileDesc *layer)
{
while (layer->lower) {
layer = layer->lower;
}
return layer;
}
static PRStatus method_close (PRFileDesc *fd)
{
struct BSSLConnection_backend *b = (struct BSSLConnection_backend *)fd->secret;
ASSERT(!b->con)
ASSERT(b->threadwork_state == THREADWORK_STATE_NONE)
// free mutexes
if ((b->flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE) || (b->flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) {
BMutex_Free(&b->recv_buf_mutex);
BMutex_Free(&b->send_buf_mutex);
}
// free backend
free(b);
// set no secret
fd->secret = NULL;
return PR_SUCCESS;
}
static PRInt32 method_read (PRFileDesc *fd, void *buf, PRInt32 amount)
{
struct BSSLConnection_backend *b = (struct BSSLConnection_backend *)fd->secret;
ASSERT(amount > 0)
if (b->threadwork_state != THREADWORK_STATE_NONE) {
BMutex_Lock(&b->recv_buf_mutex);
}
// if we are receiving into buffer or buffer has no data left, refuse recv
if (b->recv_busy || b->recv_pos == b->recv_len) {
if (b->threadwork_state != THREADWORK_STATE_NONE) {
b->threadwork_want_recv = 1;
BMutex_Unlock(&b->recv_buf_mutex);
} else {
// start receiving if not already
if (!b->recv_busy) {
// set recv busy
b->recv_busy = 1;
// receive into buffer
StreamRecvInterface_Receiver_Recv(b->recv_if, b->recv_buf, BSSLCONNECTION_BUF_SIZE);
}
}
PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
return -1;
}
// limit amount to available data
if (amount > b->recv_len - b->recv_pos) {
amount = b->recv_len - b->recv_pos;
}
// copy data
memcpy(buf, b->recv_buf + b->recv_pos, amount);
// update buffer
b->recv_pos += amount;
if (b->threadwork_state != THREADWORK_STATE_NONE) {
BMutex_Unlock(&b->recv_buf_mutex);
}
return amount;
}
static PRInt32 method_write (PRFileDesc *fd, const void *buf, PRInt32 amount)
{
struct BSSLConnection_backend *b = (struct BSSLConnection_backend *)fd->secret;
ASSERT(amount > 0)
if (b->threadwork_state != THREADWORK_STATE_NONE) {
BMutex_Lock(&b->send_buf_mutex);
}
ASSERT(!b->send_busy || b->send_pos < b->send_len)
// if there is data in buffer, refuse send
if (b->send_pos < b->send_len) {
if (b->threadwork_state != THREADWORK_STATE_NONE) {
b->threadwork_want_send = 1;
BMutex_Unlock(&b->send_buf_mutex);
}
PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
return -1;
}
// limit amount to buffer size
if (amount > BSSLCONNECTION_BUF_SIZE) {
amount = BSSLCONNECTION_BUF_SIZE;
}
// init buffer
memcpy(b->send_buf, buf, amount);
b->send_pos = 0;
b->send_len = amount;
if (b->threadwork_state != THREADWORK_STATE_NONE) {
BMutex_Unlock(&b->send_buf_mutex);
} else {
// start sending
b->send_busy = 1;
StreamPassInterface_Sender_Send(b->send_if, b->send_buf + b->send_pos, b->send_len - b->send_pos);
}
return amount;
}
static PRStatus method_shutdown (PRFileDesc *fd, PRIntn how)
{
PR_SetError(PR_INVALID_METHOD_ERROR, 0);
return PR_FAILURE;
}
static PRInt32 method_recv (PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, PRIntervalTime timeout)
{
ASSERT(flags == 0)
return method_read(fd, buf, amount);
}
static PRInt32 method_send (PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, PRIntervalTime timeout)
{
ASSERT(flags == 0)
return method_write(fd, buf, amount);
}
static PRInt16 method_poll (PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
{
*out_flags = 0;
return in_flags;
}
static PRStatus method_getpeername (PRFileDesc *fd, PRNetAddr *addr)
{
memset(addr, 0, sizeof(*addr));
addr->raw.family = PR_AF_INET;
return PR_SUCCESS;
}
static PRStatus method_getsocketoption (PRFileDesc *fd, PRSocketOptionData *data)
{
switch (data->option) {
case PR_SockOpt_Nonblocking:
data->value.non_blocking = PR_TRUE;
return PR_SUCCESS;
}
PR_SetError(PR_UNKNOWN_ERROR, 0);
return PR_FAILURE;
}
static PRStatus method_setsocketoption (PRFileDesc *fd, const PRSocketOptionData *data)
{
PR_SetError(PR_UNKNOWN_ERROR, 0);
return PR_FAILURE;
}
static PRIntn _PR_InvalidIntn (void)
{
ASSERT(0)
PR_SetError(PR_INVALID_METHOD_ERROR, 0);
return -1;
}
static PRInt32 _PR_InvalidInt32 (void)
{
ASSERT(0)
PR_SetError(PR_INVALID_METHOD_ERROR, 0);
return -1;
}
static PRInt64 _PR_InvalidInt64 (void)
{
ASSERT(0)
PR_SetError(PR_INVALID_METHOD_ERROR, 0);
return -1;
}
static PROffset32 _PR_InvalidOffset32 (void)
{
ASSERT(0)
PR_SetError(PR_INVALID_METHOD_ERROR, 0);
return -1;
}
static PROffset64 _PR_InvalidOffset64 (void)
{
ASSERT(0)
PR_SetError(PR_INVALID_METHOD_ERROR, 0);
return -1;
}
static PRStatus _PR_InvalidStatus (void)
{
ASSERT(0)
PR_SetError(PR_INVALID_METHOD_ERROR, 0);
return PR_FAILURE;
}
static PRFileDesc *_PR_InvalidDesc (void)
{
ASSERT(0)
PR_SetError(PR_INVALID_METHOD_ERROR, 0);
return NULL;
}
static PRIOMethods methods = {
(PRDescType)0,
method_close,
method_read,
method_write,
(PRAvailableFN)_PR_InvalidInt32,
(PRAvailable64FN)_PR_InvalidInt64,
(PRFsyncFN)_PR_InvalidStatus,
(PRSeekFN)_PR_InvalidOffset32,
(PRSeek64FN)_PR_InvalidOffset64,
(PRFileInfoFN)_PR_InvalidStatus,
(PRFileInfo64FN)_PR_InvalidStatus,
(PRWritevFN)_PR_InvalidInt32,
(PRConnectFN)_PR_InvalidStatus,
(PRAcceptFN)_PR_InvalidDesc,
(PRBindFN)_PR_InvalidStatus,
(PRListenFN)_PR_InvalidStatus,
method_shutdown,
method_recv,
method_send,
(PRRecvfromFN)_PR_InvalidInt32,
(PRSendtoFN)_PR_InvalidInt32,
method_poll,
(PRAcceptreadFN)_PR_InvalidInt32,
(PRTransmitfileFN)_PR_InvalidInt32,
(PRGetsocknameFN)_PR_InvalidStatus,
method_getpeername,
(PRReservedFN)_PR_InvalidIntn,
(PRReservedFN)_PR_InvalidIntn,
method_getsocketoption,
method_setsocketoption,
(PRSendfileFN)_PR_InvalidInt32,
(PRConnectcontinueFN)_PR_InvalidStatus,
(PRReservedFN)_PR_InvalidIntn,
(PRReservedFN)_PR_InvalidIntn,
(PRReservedFN)_PR_InvalidIntn,
(PRReservedFN)_PR_InvalidIntn
};
static void backend_send_if_handler_done (struct BSSLConnection_backend *b, int data_len)
{
ASSERT(b->send_busy)
ASSERT(b->send_len > 0)
ASSERT(b->send_pos < b->send_len)
ASSERT(data_len > 0)
ASSERT(data_len <= b->send_len - b->send_pos)
if (b->threadwork_state != THREADWORK_STATE_NONE) {
BMutex_Lock(&b->send_buf_mutex);
}
// update buffer
b->send_pos += data_len;
// send more if needed
if (b->send_pos < b->send_len) {
StreamPassInterface_Sender_Send(b->send_if, b->send_buf + b->send_pos, b->send_len - b->send_pos);
if (b->threadwork_state != THREADWORK_STATE_NONE) {
BMutex_Unlock(&b->send_buf_mutex);
}
return;
}
// set send not busy
b->send_busy = 0;
if (b->threadwork_state != THREADWORK_STATE_NONE) {
BMutex_Unlock(&b->send_buf_mutex);
}
// notify connection
if (b->con && !b->con->have_error) {
connection_try_io(b->con);
return;
}
}
static void backend_recv_if_handler_done (struct BSSLConnection_backend *b, int data_len)
{
ASSERT(b->recv_busy)
ASSERT(data_len > 0)
ASSERT(data_len <= BSSLCONNECTION_BUF_SIZE)
if (b->threadwork_state != THREADWORK_STATE_NONE) {
BMutex_Lock(&b->recv_buf_mutex);
}
// init buffer
b->recv_busy = 0;
b->recv_pos = 0;
b->recv_len = data_len;
if (b->threadwork_state != THREADWORK_STATE_NONE) {
BMutex_Unlock(&b->recv_buf_mutex);
}
// notify connection
if (b->con && !b->con->have_error) {
connection_try_io(b->con);
return;
}
}
static void backend_threadwork_start (struct BSSLConnection_backend *b, int op)
{
ASSERT(b->con)
ASSERT(b->threadwork_state == THREADWORK_STATE_NONE)
ASSERT(op == THREADWORK_STATE_HANDSHAKE || op == THREADWORK_STATE_READ || op == THREADWORK_STATE_WRITE)
b->threadwork_state = op;
b->threadwork_want_recv = 0;
b->threadwork_want_send = 0;
BThreadWork_Init(&b->threadwork, b->twd, connection_threadwork_handler_done, b->con, connection_threadwork_func_work, b->con);
}
static int backend_threadwork_do_io (struct BSSLConnection_backend *b)
{
ASSERT(b->con)
ASSERT(b->threadwork_state == THREADWORK_STATE_NONE)
int io_ready = (b->threadwork_want_recv && !b->recv_busy && b->recv_pos < b->recv_len) ||
(b->threadwork_want_send && b->send_pos == b->send_len);
if (b->threadwork_want_recv && b->recv_pos == b->recv_len && !b->recv_busy) {
b->recv_busy = 1;
StreamRecvInterface_Receiver_Recv(b->recv_if, b->recv_buf, BSSLCONNECTION_BUF_SIZE);
}
if (b->send_pos < b->send_len && !b->send_busy) {
b->send_busy = 1;
StreamPassInterface_Sender_Send(b->send_if, b->send_buf + b->send_pos, b->send_len - b->send_pos);
}
return io_ready;
}
static void connection_report_error (BSSLConnection *o)
{
ASSERT(!o->have_error)
// set error
o->have_error = 1;
// report error
DEBUGERROR(&o->d_err, o->handler(o->user, BSSLCONNECTION_EVENT_ERROR));
}
static void connection_init_job_handler (BSSLConnection *o)
{
DebugObject_Access(&o->d_obj);
ASSERT(!o->have_error)
ASSERT(!o->up)
connection_try_handshake(o);
}
static void connection_init_up (BSSLConnection *o)
{
// unset init job
// (just in the impossible case that handshake completed before the init job executed)
BPending_Unset(&o->init_job);
// init send interface
StreamPassInterface_Init(&o->send_if, (StreamPassInterface_handler_send)connection_send_if_handler_send, o, o->pg);
// init recv interface
StreamRecvInterface_Init(&o->recv_if, (StreamRecvInterface_handler_recv)connection_recv_if_handler_recv, o, o->pg);
// init recv job
BPending_Init(&o->recv_job, o->pg, (BPending_handler)connection_recv_job_handler, o);
// set no send data
o->send_len = -1;
// set no recv data
o->recv_avail = -1;
// set up
o->up = 1;
}
static void connection_try_io (BSSLConnection *o)
{
DebugObject_Access(&o->d_obj);
ASSERT(!o->have_error)
if (!o->up) {
connection_try_handshake(o);
return;
}
if (o->send_len > 0) {
if (o->recv_avail > 0) {
BPending_Set(&o->recv_job);
}
connection_try_send(o);
return;
}
if (o->recv_avail > 0) {
connection_try_recv(o);
return;
}
}
static void connection_threadwork_func_work (void *user)
{
BSSLConnection *o = (BSSLConnection *)user;
struct BSSLConnection_backend *b = o->backend;
ASSERT(b->threadwork_state != THREADWORK_STATE_NONE)
switch (b->threadwork_state) {
case THREADWORK_STATE_HANDSHAKE:
b->threadwork_result_sec = SSL_ForceHandshake(o->prfd);
break;
case THREADWORK_STATE_WRITE:
b->threadwork_result_pr = PR_Write(o->prfd, o->send_data, o->send_len);
break;
case THREADWORK_STATE_READ:
b->threadwork_result_pr = PR_Read(o->prfd, o->recv_data, o->recv_avail);
break;
default:
ASSERT(0);
}
b->threadwork_error = PR_GetError();
}
static void connection_threadwork_handler_done (void *user)
{
BSSLConnection *o = (BSSLConnection *)user;
struct BSSLConnection_backend *b = o->backend;
ASSERT(b->threadwork_state != THREADWORK_STATE_NONE)
// remember what operation the threadwork was performing
int op = b->threadwork_state;
// free threadwork
BThreadWork_Free(&b->threadwork);
b->threadwork_state = THREADWORK_STATE_NONE;
// start any necessary backend I/O operations, and determine if any of the requested
// backend I/O that was not available at the time is now available
int io_ready = backend_threadwork_do_io(b);
switch (op) {
case THREADWORK_STATE_HANDSHAKE: {
ASSERT(!o->up)
ASSERT((b->flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE))
if (b->threadwork_result_sec == SECFailure) {
if (b->threadwork_error == PR_WOULD_BLOCK_ERROR) {
if (io_ready) {
// requested backend I/O got ready, try again
backend_threadwork_start(o->backend, THREADWORK_STATE_HANDSHAKE);
}
return;
}
BLog(BLOG_ERROR, "SSL_ForceHandshake failed (%"PRIi32")", b->threadwork_error);
connection_report_error(o);
return;
}
// init up
connection_init_up(o);
// report up
o->handler(o->user, BSSLCONNECTION_EVENT_UP);
return;
} break;
case THREADWORK_STATE_WRITE: {
ASSERT(o->up)
ASSERT((b->flags & BSSLCONNECTION_FLAG_THREADWORK_IO))
ASSERT(o->send_len > 0)
PRInt32 result = b->threadwork_result_pr;
PRErrorCode error = b->threadwork_error;
if (result < 0) {
if (error == PR_WOULD_BLOCK_ERROR) {
if (io_ready) {
// requested backend I/O got ready, try again
backend_threadwork_start(o->backend, THREADWORK_STATE_WRITE);
} else if (o->recv_avail > 0) {
// don't forget about receiving
backend_threadwork_start(o->backend, THREADWORK_STATE_READ);
}
return;
}
BLog(BLOG_ERROR, "PR_Write failed (%"PRIi32")", error);
connection_report_error(o);
return;
}
ASSERT(result > 0)
ASSERT(result <= o->send_len)
// set no send data
o->send_len = -1;
// don't forget about receiving
if (o->recv_avail > 0) {
backend_threadwork_start(o->backend, THREADWORK_STATE_READ);
}
// finish send operation
StreamPassInterface_Done(&o->send_if, result);
} break;
case THREADWORK_STATE_READ: {
ASSERT(o->up)
ASSERT((b->flags & BSSLCONNECTION_FLAG_THREADWORK_IO))
ASSERT(o->recv_avail > 0)
PRInt32 result = b->threadwork_result_pr;
PRErrorCode error = b->threadwork_error;
if (result < 0) {
if (error == PR_WOULD_BLOCK_ERROR) {
if (io_ready) {
// requested backend I/O got ready, try again
backend_threadwork_start(o->backend, THREADWORK_STATE_READ);
} else if (o->send_len > 0) {
// don't forget about sending
backend_threadwork_start(o->backend, THREADWORK_STATE_WRITE);
}
return;
}
BLog(BLOG_ERROR, "PR_Read failed (%"PRIi32")", error);
connection_report_error(o);
return;
}
if (result == 0) {
BLog(BLOG_ERROR, "PR_Read returned 0");
connection_report_error(o);
return;
}
ASSERT(result > 0)
ASSERT(result <= o->recv_avail)
// set no recv data
o->recv_avail = -1;
// don't forget about sending
if (o->send_len > 0) {
backend_threadwork_start(o->backend, THREADWORK_STATE_WRITE);
}
// finish receive operation
StreamRecvInterface_Done(&o->recv_if, result);
} break;
default:
ASSERT(0);
}
return;
}
static void connection_recv_job_handler (BSSLConnection *o)
{
DebugObject_Access(&o->d_obj);
ASSERT(!o->have_error)
ASSERT(o->up)
ASSERT(o->recv_avail > 0)
connection_try_recv(o);
return;
}
static void connection_try_handshake (BSSLConnection *o)
{
ASSERT(!o->have_error)
ASSERT(!o->up)
// continue in threadwork if requested
if ((o->backend->flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE)) {
if (o->backend->threadwork_state == THREADWORK_STATE_NONE) {
backend_threadwork_start(o->backend, THREADWORK_STATE_HANDSHAKE);
}
return;
}
// try handshake
SECStatus res = SSL_ForceHandshake(o->prfd);
if (res == SECFailure) {
PRErrorCode error = PR_GetError();
if (error == PR_WOULD_BLOCK_ERROR) {
return;
}
BLog(BLOG_ERROR, "SSL_ForceHandshake failed (%"PRIi32")", error);
connection_report_error(o);
return;
}
// init up
connection_init_up(o);
// report up
o->handler(o->user, BSSLCONNECTION_EVENT_UP);
return;
}
static void connection_try_send (BSSLConnection *o)
{
ASSERT(!o->have_error)
ASSERT(o->up)
ASSERT(o->send_len > 0)
// continue in threadwork if requested
if ((o->backend->flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) {
if (o->backend->threadwork_state == THREADWORK_STATE_NONE) {
backend_threadwork_start(o->backend, THREADWORK_STATE_WRITE);
}
return;
}
// send
PRInt32 res = PR_Write(o->prfd, o->send_data, o->send_len);
if (res < 0) {
PRErrorCode error = PR_GetError();
if (error == PR_WOULD_BLOCK_ERROR) {
return;
}
BLog(BLOG_ERROR, "PR_Write failed (%"PRIi32")", error);
connection_report_error(o);
return;
}
ASSERT(res > 0)
ASSERT(res <= o->send_len)
// set no send data
o->send_len = -1;
// done
StreamPassInterface_Done(&o->send_if, res);
}
static void connection_try_recv (BSSLConnection *o)
{
ASSERT(!o->have_error)
ASSERT(o->up)
ASSERT(o->recv_avail > 0)
// unset recv job
BPending_Unset(&o->recv_job);
// continue in threadwork if requested
if ((o->backend->flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) {
if (o->backend->threadwork_state == THREADWORK_STATE_NONE) {
backend_threadwork_start(o->backend, THREADWORK_STATE_READ);
}
return;
}
// recv
PRInt32 res = PR_Read(o->prfd, o->recv_data, o->recv_avail);
if (res < 0) {
PRErrorCode error = PR_GetError();
if (error == PR_WOULD_BLOCK_ERROR) {
return;
}
BLog(BLOG_ERROR, "PR_Read failed (%"PRIi32")", error);
connection_report_error(o);
return;
}
if (res == 0) {
BLog(BLOG_ERROR, "PR_Read returned 0");
connection_report_error(o);
return;
}
ASSERT(res > 0)
ASSERT(res <= o->recv_avail)
// set no recv data
o->recv_avail = -1;
// done
StreamRecvInterface_Done(&o->recv_if, res);
}
static void connection_send_if_handler_send (BSSLConnection *o, uint8_t *data, int data_len)
{
DebugObject_Access(&o->d_obj);
ASSERT(!o->have_error)
ASSERT(o->up)
ASSERT(o->send_len == -1)
ASSERT(data_len > 0)
#ifndef NDEBUG
ASSERT(!o->releasebuffers_called)
o->user_io_started = 1;
#endif
// limit amount for PR_Write
if (data_len > INT32_MAX) {
data_len = INT32_MAX;
}
// set send data
o->send_data = data;
o->send_len = data_len;
// start sending
connection_try_send(o);
}
static void connection_recv_if_handler_recv (BSSLConnection *o, uint8_t *data, int data_len)
{
DebugObject_Access(&o->d_obj);
ASSERT(!o->have_error)
ASSERT(o->up)
ASSERT(o->recv_avail == -1)
ASSERT(data_len > 0)
#ifndef NDEBUG
ASSERT(!o->releasebuffers_called)
o->user_io_started = 1;
#endif
// limit amount for PR_Read
if (data_len > INT32_MAX) {
data_len = INT32_MAX;
}
// set recv data
o->recv_data = data;
o->recv_avail = data_len;
// start receiving
connection_try_recv(o);
}
int BSSLConnection_GlobalInit (void)
{
ASSERT(!bprconnection_initialized)
if ((bprconnection_identity = PR_GetUniqueIdentity("BSSLConnection")) == PR_INVALID_IO_LAYER) {
BLog(BLOG_ERROR, "PR_GetUniqueIdentity failed");
return 0;
}
bprconnection_initialized = 1;
return 1;
}
int BSSLConnection_MakeBackend (PRFileDesc *prfd, StreamPassInterface *send_if, StreamRecvInterface *recv_if, BThreadWorkDispatcher *twd, int flags)
{
ASSERT(bprconnection_initialized)
ASSERT(!(flags & ~(BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE | BSSLCONNECTION_FLAG_THREADWORK_IO)))
ASSERT(!(flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE) || twd)
ASSERT(!(flags & BSSLCONNECTION_FLAG_THREADWORK_IO) || twd)
// don't do stuff in threads if threads aren't available
if (((flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE) || (flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) &&
!BThreadWorkDispatcher_UsingThreads(twd)
) {
BLog(BLOG_WARNING, "SSL operations in threads requested but threads are not available");
flags &= ~(BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE | BSSLCONNECTION_FLAG_THREADWORK_IO);
}
// allocate backend
struct BSSLConnection_backend *b = (struct BSSLConnection_backend *)malloc(sizeof(*b));
if (!b) {
BLog(BLOG_ERROR, "malloc failed");
goto fail0;
}
// init mutexes
if ((flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE) || (flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) {
if (!BMutex_Init(&b->send_buf_mutex)) {
BLog(BLOG_ERROR, "BMutex_Init failed");
goto fail1;
}
if (!BMutex_Init(&b->recv_buf_mutex)) {
BLog(BLOG_ERROR, "BMutex_Init failed");
goto fail2;
}
}
// init arguments
b->send_if = send_if;
b->recv_if = recv_if;
b->twd = twd;
b->flags = flags;
// init interfaces
StreamPassInterface_Sender_Init(b->send_if, (StreamPassInterface_handler_done)backend_send_if_handler_done, b);
StreamRecvInterface_Receiver_Init(b->recv_if, (StreamRecvInterface_handler_done)backend_recv_if_handler_done, b);
// set no connection
b->con = NULL;
// init send buffer
b->send_busy = 0;
b->send_len = 0;
b->send_pos = 0;
// init recv buffer
b->recv_busy = 0;
b->recv_pos = 0;
b->recv_len = 0;
// set threadwork state
b->threadwork_state = THREADWORK_STATE_NONE;
// init prfd
memset(prfd, 0, sizeof(*prfd));
prfd->methods = &methods;
prfd->secret = (PRFilePrivate *)b;
prfd->identity = bprconnection_identity;
return 1;
if ((flags & BSSLCONNECTION_FLAG_THREADWORK_HANDSHAKE) || (flags & BSSLCONNECTION_FLAG_THREADWORK_IO)) {
fail2:
BMutex_Free(&b->send_buf_mutex);
}
fail1:
free(b);
fail0:
return 0;
}
void BSSLConnection_Init (BSSLConnection *o, PRFileDesc *prfd, int force_handshake, BPendingGroup *pg, void *user,
BSSLConnection_handler handler)
{
ASSERT(force_handshake == 0 || force_handshake == 1)
ASSERT(handler)
ASSERT(bprconnection_initialized)
ASSERT(get_bottom(prfd)->identity == bprconnection_identity)
ASSERT(!((struct BSSLConnection_backend *)(get_bottom(prfd)->secret))->con)
// init arguments
o->prfd = prfd;
o->pg = pg;
o->user = user;
o->handler = handler;
// set backend
o->backend = (struct BSSLConnection_backend *)(get_bottom(prfd)->secret);
ASSERT(!o->backend->con)
ASSERT(o->backend->threadwork_state == THREADWORK_STATE_NONE)
// set have no error
o->have_error = 0;
// init init job
BPending_Init(&o->init_job, o->pg, (BPending_handler)connection_init_job_handler, o);
if (force_handshake) {
// set not up
o->up = 0;
// set init job
BPending_Set(&o->init_job);
} else {
// init up
connection_init_up(o);
}
// set backend connection
o->backend->con = o;
#ifndef NDEBUG
o->user_io_started = 0;
o->releasebuffers_called = 0;
#endif
DebugError_Init(&o->d_err, o->pg);
DebugObject_Init(&o->d_obj);
}
void BSSLConnection_Free (BSSLConnection *o)
{
DebugObject_Free(&o->d_obj);
DebugError_Free(&o->d_err);
#ifndef NDEBUG
ASSERT(o->releasebuffers_called || !o->user_io_started)
#endif
ASSERT(o->backend->threadwork_state == THREADWORK_STATE_NONE)
if (o->up) {
// free recv job
BPending_Free(&o->recv_job);
// free recv interface
StreamRecvInterface_Free(&o->recv_if);
// free send interface
StreamPassInterface_Free(&o->send_if);
}
// free init job
BPending_Free(&o->init_job);
// unset backend connection
o->backend->con = NULL;
}
void BSSLConnection_ReleaseBuffers (BSSLConnection *o)
{
DebugObject_Access(&o->d_obj);
#ifndef NDEBUG
ASSERT(!o->releasebuffers_called)
#endif
// wait for threadwork to finish
if (o->backend->threadwork_state != THREADWORK_STATE_NONE) {
BThreadWork_Free(&o->backend->threadwork);
o->backend->threadwork_state = THREADWORK_STATE_NONE;
}
#ifndef NDEBUG
o->releasebuffers_called = 1;
#endif
}
StreamPassInterface * BSSLConnection_GetSendIf (BSSLConnection *o)
{
DebugObject_Access(&o->d_obj);
ASSERT(o->up)
return &o->send_if;
}
StreamRecvInterface * BSSLConnection_GetRecvIf (BSSLConnection *o)
{
DebugObject_Access(&o->d_obj);
ASSERT(o->up)
return &o->recv_if;
}