the upcoming 2.0 version
https://redmine.lighttpd.net/projects/lighttpd2
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
670 lines
18 KiB
670 lines
18 KiB
|
|
#include "gnutls_filter.h" |
|
|
|
struct liGnuTLSFilter { |
|
int refcount; |
|
const liGnuTLSFilterCallbacks *callbacks; |
|
gpointer callback_data; |
|
|
|
liServer *srv; |
|
liWorker *wrk; |
|
liLogContext *log_context; /* TODO: support setting this to VR context */ |
|
|
|
gnutls_session_t session; |
|
liStream crypt_source; |
|
liStream crypt_drain; |
|
liStream plain_source; |
|
liStream plain_drain; |
|
|
|
liBuffer *raw_in_buffer; /* for gnutls_record_recv */ |
|
liBuffer *raw_out_buffer; /* stream_pushv */ |
|
|
|
unsigned int initial_handshaked_finished:1; |
|
unsigned int closing:1, aborted:1; |
|
unsigned int write_wants_read:1; |
|
}; |
|
|
|
#if GNUTLS_VERSION_NUMBER >= 0x020c00 |
|
#define HAVE_GIOVEC |
|
typedef giovec_t li_iovec_t; |
|
#else |
|
typedef struct { |
|
void *iov_base; |
|
size_t iov_len; |
|
} li_iovec_t; |
|
#endif |
|
|
|
#if GNUTLS_VERSION_NUMBER >= 0x020a00 |
|
#define HAVE_SAVE_RENEGOTIATION |
|
#endif |
|
|
|
static ssize_t stream_push(gnutls_transport_ptr_t, const void*, size_t); |
|
static ssize_t stream_pushv(gnutls_transport_ptr_t, const li_iovec_t * iov, int iovcnt); |
|
static ssize_t stream_pull(gnutls_transport_ptr_t, void*, size_t); |
|
|
|
static ssize_t stream_push(gnutls_transport_ptr_t trans, const void *buf, size_t len) { |
|
li_iovec_t vec; |
|
vec.iov_base = (void *) buf; |
|
vec.iov_len = len; |
|
return stream_pushv(trans, &vec, 1); |
|
} |
|
static ssize_t stream_pushv(gnutls_transport_ptr_t trans, const li_iovec_t * iov, int iovcnt) { |
|
const ssize_t blocksize = 16*1024; /* 16k */ |
|
liGnuTLSFilter *f = (liGnuTLSFilter*) trans; |
|
liChunkQueue *cq; |
|
int i; |
|
liBuffer *buf; |
|
gboolean cq_buf_append; |
|
ssize_t written = 0; |
|
|
|
errno = ECONNRESET; |
|
|
|
if (NULL == f || NULL == f->crypt_source.out) return -1; |
|
cq = f->crypt_source.out; |
|
if (cq->is_closed) return -1; |
|
|
|
buf = f->raw_out_buffer; |
|
cq_buf_append = (buf != NULL && buf == li_chunkqueue_get_last_buffer(cq, 1024)); |
|
for (i = 0; i < iovcnt; ++i) { |
|
const char *data = iov[i].iov_base; |
|
size_t len = iov[i].iov_len; |
|
|
|
while (len > 0) { |
|
size_t bufsize, do_write; |
|
if (NULL == buf) buf = li_buffer_new(blocksize); |
|
|
|
bufsize = buf->alloc_size - buf->used; |
|
do_write = (bufsize > len) ? len : bufsize; |
|
memcpy(buf->addr + buf->used, data, do_write); |
|
len -= do_write; |
|
data += do_write; |
|
if (cq_buf_append) { |
|
/* also updates buf->used */ |
|
li_chunkqueue_update_last_buffer_size(cq, do_write); |
|
} else { |
|
gsize offset = buf->used; |
|
buf->used += do_write; |
|
li_buffer_acquire(buf); |
|
li_chunkqueue_append_buffer2(cq, buf, offset, do_write); |
|
cq_buf_append = TRUE; |
|
} |
|
if (buf->used == buf->alloc_size) { |
|
li_buffer_release(buf); |
|
buf = NULL; |
|
cq_buf_append = FALSE; |
|
} |
|
written += do_write; |
|
} |
|
} |
|
if (NULL != buf && buf->alloc_size - buf->used < 1024) { |
|
li_buffer_release(buf); |
|
f->raw_out_buffer = buf = NULL; |
|
} else { |
|
f->raw_out_buffer = buf; |
|
} |
|
|
|
li_stream_notify_later(&f->crypt_source); |
|
|
|
errno = 0; |
|
return written; |
|
} |
|
static ssize_t stream_pull(gnutls_transport_ptr_t trans, void *buf, size_t len) { |
|
liGnuTLSFilter *f = (liGnuTLSFilter*) trans; |
|
liChunkQueue *cq; |
|
|
|
errno = ECONNRESET; |
|
if (NULL == f || NULL == f->crypt_drain.out) return -1; |
|
|
|
cq = f->crypt_drain.out; |
|
|
|
if (0 == cq->length) { |
|
if (cq->is_closed) { |
|
errno = 0; |
|
return 0; |
|
} else { |
|
errno = EAGAIN; |
|
return -1; |
|
} |
|
} |
|
|
|
if (len > (size_t) cq->length) len = cq->length; |
|
if (!li_chunkqueue_extract_to_memory(cq, len, buf, NULL)) return -1; |
|
li_chunkqueue_skip(cq, len); |
|
|
|
errno = 0; |
|
return len; |
|
} |
|
|
|
static void f_close_gnutls(liGnuTLSFilter *f) { |
|
if (NULL != f->session && !f->closing) { |
|
liCQLimit *limit; |
|
f->closing = TRUE; |
|
f->session = NULL; |
|
|
|
assert(NULL != f->crypt_source.out); |
|
assert(NULL != f->crypt_source.out->limit); |
|
limit = f->crypt_source.out->limit; |
|
limit->notify = NULL; |
|
limit->context = NULL; |
|
|
|
li_stream_disconnect(&f->plain_source); /* crypt in -> plain out */ |
|
|
|
li_stream_disconnect(&f->plain_drain); /* app -> plain in */ |
|
li_stream_disconnect_dest(&f->plain_source); /* plain out -> app */ |
|
|
|
f->log_context = NULL; |
|
if (NULL != f->callbacks && NULL != f->callbacks->closed_cb) { |
|
f->callbacks->closed_cb(f, f->callback_data); |
|
} |
|
} |
|
} |
|
static void f_acquire(liGnuTLSFilter *f) { |
|
assert(f->refcount > 0); |
|
++f->refcount; |
|
} |
|
static void f_release(liGnuTLSFilter *f) { |
|
assert(f->refcount > 0); |
|
if (0 == --f->refcount) { |
|
f->refcount = 1; |
|
f_close_gnutls(f); |
|
if (NULL != f->raw_in_buffer) { |
|
li_buffer_release(f->raw_in_buffer); |
|
f->raw_in_buffer = NULL; |
|
} |
|
if (NULL != f->raw_out_buffer) { |
|
li_buffer_release(f->raw_out_buffer); |
|
f->raw_out_buffer = NULL; |
|
} |
|
|
|
g_slice_free(liGnuTLSFilter, f); |
|
} |
|
} |
|
static void f_abort_gnutls(liGnuTLSFilter *f) { |
|
if (f->aborted) return; |
|
f->aborted = TRUE; |
|
f_acquire(f); |
|
f_close_gnutls(f); |
|
li_stream_disconnect(&f->crypt_source); /* plain in -> crypt out */ |
|
li_stream_disconnect(&f->crypt_drain); /* io -> crypt in */ |
|
li_stream_disconnect_dest(&f->crypt_source); /* crypt out -> io */ |
|
f_release(f); |
|
} |
|
|
|
static void f_close_with_alert(liGnuTLSFilter *f, int r) { |
|
if (f->closing || f->aborted) return; |
|
|
|
if (GNUTLS_E_SUCCESS != gnutls_alert_send_appropriate(f->session, r)) { |
|
f_abort_gnutls(f); |
|
return; |
|
} |
|
|
|
/* push alert to io */ |
|
li_stream_notify(&f->crypt_source); |
|
f->plain_source.out->is_closed = TRUE; |
|
f->crypt_drain.out->is_closed = TRUE; |
|
f_close_gnutls(f); |
|
} |
|
|
|
static void do_handle_error(liGnuTLSFilter *f, const char *gnutlsfunc, int r, gboolean writing) { |
|
switch (r) { |
|
case GNUTLS_E_AGAIN: |
|
if (writing) f->write_wants_read = TRUE; |
|
break; |
|
case GNUTLS_E_REHANDSHAKE: |
|
#ifdef HAVE_SAVE_RENEGOTIATION |
|
if (f->initial_handshaked_finished && !gnutls_safe_renegotiation_status(f->session)) { |
|
_ERROR(f->srv, f->wrk, f->log_context, "%s: client initiated unsafe renegotitation, closing connection", gnutlsfunc); |
|
f_close_with_alert(f, r); |
|
} else { |
|
_DEBUG(f->srv, f->wrk, f->log_context, "%s: client initiated renegotitation", gnutlsfunc); |
|
} |
|
#else |
|
if (f->initial_handshaked_finished) { |
|
_ERROR(f->srv, f->wrk, f->log_context, "%s: client initiated renegotitation, closing connection", gnutlsfunc); |
|
f_close_with_alert(f, r); |
|
} |
|
#endif |
|
break; |
|
case GNUTLS_E_UNEXPECTED_PACKET_LENGTH: |
|
f_close_with_alert(f, r); |
|
break; |
|
case GNUTLS_E_UNKNOWN_CIPHER_SUITE: |
|
case GNUTLS_E_UNSUPPORTED_VERSION_PACKET: |
|
_DEBUG(f->srv, f->wrk, f->log_context, "%s (%s): %s", gnutlsfunc, |
|
gnutls_strerror_name(r), gnutls_strerror(r)); |
|
f_close_with_alert(f, r); |
|
break; |
|
default: |
|
if (gnutls_error_is_fatal(r)) { |
|
_ERROR(f->srv, f->wrk, f->log_context, "%s (%s): %s", gnutlsfunc, |
|
gnutls_strerror_name(r), gnutls_strerror(r)); |
|
if (f->initial_handshaked_finished) { |
|
f_close_with_alert(f, r); |
|
} else { |
|
f_abort_gnutls(f); |
|
} |
|
} else { |
|
_ERROR(f->srv, f->wrk, f->log_context, "%s non fatal (%s): %s", gnutlsfunc, |
|
gnutls_strerror_name(r), gnutls_strerror(r)); |
|
} |
|
} |
|
} |
|
|
|
#define TIMEDIFF_MS(ts1, ts2) (((ts2).tv_sec - (ts1).tv_sec)*1.0e3 + ((ts2).tv_nsec - (ts1).tv_nsec)*1.0e-6) |
|
|
|
static gboolean do_gnutls_handshake(liGnuTLSFilter *f, gboolean writing) { |
|
int r; |
|
|
|
assert(!f->initial_handshaked_finished); |
|
|
|
r = gnutls_handshake(f->session); |
|
if (GNUTLS_E_SUCCESS == r) { |
|
f->initial_handshaked_finished = 1; |
|
li_stream_acquire(&f->plain_source); |
|
li_stream_acquire(&f->plain_drain); |
|
f->callbacks->handshake_cb(f, f->callback_data, &f->plain_source, &f->plain_drain); |
|
li_stream_release(&f->plain_source); |
|
li_stream_release(&f->plain_drain); |
|
return TRUE; |
|
} else { |
|
do_handle_error(f, "gnutls_handshake", r, writing); |
|
return FALSE; |
|
} |
|
} |
|
|
|
static void do_gnutls_read(liGnuTLSFilter *f) { |
|
const ssize_t blocksize = 16*1024; /* 16k */ |
|
off_t max_read = 4 * blocksize; /* 64k */ |
|
ssize_t r; |
|
off_t len = 0; |
|
liChunkQueue *cq = f->plain_source.out; |
|
|
|
f_acquire(f); |
|
|
|
if (NULL != f->session && !f->initial_handshaked_finished && !do_gnutls_handshake(f, FALSE)) goto out; |
|
if (NULL == f->session) { |
|
f_abort_gnutls(f); |
|
goto out; |
|
} |
|
|
|
do { |
|
liBuffer *buf; |
|
gboolean cq_buf_append; |
|
|
|
buf = li_chunkqueue_get_last_buffer(cq, 1024); |
|
cq_buf_append = (buf != NULL); |
|
|
|
if (buf != NULL) { |
|
/* use last buffer as raw_in_buffer; they should be the same anyway */ |
|
if (G_UNLIKELY(buf != f->raw_in_buffer)) { |
|
li_buffer_acquire(buf); |
|
li_buffer_release(f->raw_in_buffer); |
|
f->raw_in_buffer = buf; |
|
} |
|
} else { |
|
buf = f->raw_in_buffer; |
|
if (buf != NULL && buf->alloc_size - buf->used < 1024) { |
|
/* release *buffer */ |
|
li_buffer_release(buf); |
|
f->raw_in_buffer = buf = NULL; |
|
} |
|
if (buf == NULL) { |
|
f->raw_in_buffer = buf = li_buffer_new(blocksize); |
|
} |
|
} |
|
assert(f->raw_in_buffer == buf); |
|
|
|
r = gnutls_record_recv(f->session, buf->addr + buf->used, buf->alloc_size - buf->used); |
|
if (r < 0) { |
|
do_handle_error(f, "gnutls_record_recv", r, FALSE); |
|
goto out; |
|
} else if (r == 0) { |
|
/* clean shutdown? */ |
|
f->plain_source.out->is_closed = TRUE; |
|
f->plain_drain.out->is_closed = TRUE; |
|
f->crypt_source.out->is_closed = TRUE; |
|
f->crypt_drain.out->is_closed = TRUE; |
|
li_stream_disconnect(&f->crypt_drain); /* io -> crypt in */ |
|
li_stream_disconnect_dest(&f->crypt_source); /* crypt out -> io */ |
|
li_stream_disconnect(&f->crypt_source); /* plain in -> crypt out */ |
|
f_close_gnutls(f); |
|
goto out; |
|
} |
|
|
|
if (cq_buf_append) { |
|
li_chunkqueue_update_last_buffer_size(cq, r); |
|
} else { |
|
gsize offset; |
|
|
|
li_buffer_acquire(buf); |
|
|
|
offset = buf->used; |
|
buf->used += r; |
|
li_chunkqueue_append_buffer2(cq, buf, offset, r); |
|
} |
|
if (buf->alloc_size - buf->used < 1024) { |
|
/* release *buffer */ |
|
li_buffer_release(buf); |
|
f->raw_in_buffer = buf = NULL; |
|
} |
|
len += r; |
|
} while (len < max_read); |
|
|
|
out: |
|
f_release(f); |
|
} |
|
|
|
#if GNUTLS_VERSION_NUMBER >= 0x030109 |
|
/* gnutls_record_cork / gnutls_record_uncork available since 3.1.9 */ |
|
#define USE_CORK |
|
#endif |
|
|
|
static void do_gnutls_write(liGnuTLSFilter *f) { |
|
const ssize_t blocksize = 16*1024; /* 16k */ |
|
char *block_data; |
|
off_t block_len; |
|
ssize_t r; |
|
off_t write_max; |
|
#ifdef USE_CORK |
|
gboolean corked = FALSE; |
|
#endif |
|
liChunkQueue *cq = f->plain_drain.out; |
|
|
|
f_acquire(f); |
|
|
|
f->write_wants_read = FALSE; |
|
|
|
/* use space in (encrypted) outgoing buffer as amounts of bytes we try to write from (plain) output |
|
* don't care if we write a little bit more than the limit allowed */ |
|
write_max = li_chunkqueue_limit_available(f->crypt_source.out); |
|
assert(write_max >= 0); /* we set a limit! */ |
|
if (0 == write_max) goto out; |
|
/* if we start writing, try to write at least blocksize bytes */ |
|
if (write_max < blocksize) write_max = blocksize; |
|
|
|
if (NULL != f->session && !f->initial_handshaked_finished && !do_gnutls_handshake(f, TRUE)) goto out; |
|
if (NULL == f->session) { |
|
f_abort_gnutls(f); |
|
goto out; |
|
} |
|
|
|
#ifdef USE_CORK |
|
if (0 != cq->length && cq->queue.length > 1) { |
|
corked = TRUE; |
|
gnutls_record_cork(f->session); |
|
} |
|
#endif |
|
|
|
do { |
|
GError *err = NULL; |
|
liChunkIter ci; |
|
|
|
if (0 == cq->length) break; |
|
|
|
ci = li_chunkqueue_iter(cq); |
|
switch (li_chunkiter_read(ci, 0, blocksize, &block_data, &block_len, &err)) { |
|
case LI_HANDLER_GO_ON: |
|
break; |
|
case LI_HANDLER_ERROR: |
|
if (NULL != err) { |
|
_ERROR(f->srv, f->wrk, f->log_context, "Couldn't read data from chunkqueue: %s", err->message); |
|
g_error_free(err); |
|
} |
|
/* fall through */ |
|
default: |
|
f_abort_gnutls(f); |
|
goto out; |
|
} |
|
|
|
r = gnutls_record_send(f->session, block_data, block_len); |
|
if (r <= 0) { |
|
do_handle_error(f, "gnutls_record_send", r, TRUE); |
|
goto out; |
|
} |
|
|
|
li_chunkqueue_skip(cq, r); |
|
write_max -= r; |
|
} while (r == block_len && write_max > 0); |
|
|
|
if (cq->is_closed && 0 == cq->length) { |
|
r = gnutls_bye(f->session, GNUTLS_SHUT_RDWR); |
|
switch (r) { |
|
case GNUTLS_E_SUCCESS: |
|
case GNUTLS_E_AGAIN: |
|
case GNUTLS_E_INTERRUPTED: |
|
f->plain_source.out->is_closed = TRUE; |
|
f->crypt_source.out->is_closed = TRUE; |
|
f->crypt_drain.out->is_closed = TRUE; |
|
li_stream_disconnect(&f->crypt_source); /* plain in -> crypt out */ |
|
f_close_gnutls(f); |
|
break; |
|
default: |
|
do_handle_error(f, "gnutls_bye", r, TRUE); |
|
f_abort_gnutls(f); |
|
break; |
|
} |
|
} else if (0 < cq->length && 0 != li_chunkqueue_limit_available(f->crypt_source.out)) { |
|
li_stream_again_later(&f->plain_drain); |
|
} |
|
|
|
out: |
|
#ifdef USE_CORK |
|
if (NULL != f->session && corked) { |
|
corked = TRUE; |
|
gnutls_record_uncork(f->session, 0); |
|
} |
|
#endif |
|
|
|
f_release(f); |
|
} |
|
|
|
/* ssl crypted out -> io */ |
|
static void stream_crypt_source_cb(liStream *stream, liStreamEvent event) { |
|
liGnuTLSFilter *f = LI_CONTAINER_OF(stream, liGnuTLSFilter, crypt_source); |
|
switch (event) { |
|
case LI_STREAM_NEW_DATA: |
|
/* data comes through SSL */ |
|
break; |
|
case LI_STREAM_NEW_CQLIMIT: |
|
break; |
|
case LI_STREAM_CONNECTED_DEST: /* io out */ |
|
break; |
|
case LI_STREAM_CONNECTED_SOURCE: /* plain_drain */ |
|
break; |
|
case LI_STREAM_DISCONNECTED_DEST: /* io out disconnect */ |
|
if (!stream->out->is_closed || 0 != stream->out->length) { |
|
f_abort_gnutls(f); /* didn't read everything */ |
|
} |
|
break; |
|
case LI_STREAM_DISCONNECTED_SOURCE: /* plain_drain */ |
|
if (!stream->out->is_closed) { /* f_close_ssl before we were ready */ |
|
f_abort_gnutls(f); |
|
} |
|
break; |
|
case LI_STREAM_DESTROY: |
|
f_release(f); |
|
break; |
|
} |
|
} |
|
/* io -> ssl crypted in */ |
|
static void stream_crypt_drain_cb(liStream *stream, liStreamEvent event) { |
|
liGnuTLSFilter *f = LI_CONTAINER_OF(stream, liGnuTLSFilter, crypt_drain); |
|
switch (event) { |
|
case LI_STREAM_NEW_DATA: |
|
if (!stream->out->is_closed && NULL != stream->source) { |
|
li_chunkqueue_steal_all(stream->out, stream->source->out); |
|
stream->out->is_closed = stream->out->is_closed || stream->source->out->is_closed; |
|
li_stream_notify(stream); /* tell plain_source to do SSL_read */ |
|
} |
|
if (stream->out->is_closed) { |
|
li_stream_disconnect(stream); |
|
} |
|
break; |
|
case LI_STREAM_NEW_CQLIMIT: |
|
break; |
|
case LI_STREAM_CONNECTED_DEST: /* plain_source */ |
|
break; |
|
case LI_STREAM_CONNECTED_SOURCE: /* io in */ |
|
break; |
|
case LI_STREAM_DISCONNECTED_DEST: /* plain_source */ |
|
if (!stream->out->is_closed || 0 != stream->out->length) { |
|
f_abort_gnutls(f); /* didn't read everything */ |
|
} |
|
break; |
|
case LI_STREAM_DISCONNECTED_SOURCE: /* io in disconnect */ |
|
if (!stream->out->is_closed) { |
|
f_abort_gnutls(f); /* conn aborted */ |
|
} |
|
break; |
|
case LI_STREAM_DESTROY: |
|
f_release(f); |
|
break; |
|
} |
|
} |
|
/* ssl (plain) -> app */ |
|
static void stream_plain_source_cb(liStream *stream, liStreamEvent event) { |
|
liGnuTLSFilter *f = LI_CONTAINER_OF(stream, liGnuTLSFilter, plain_source); |
|
switch (event) { |
|
case LI_STREAM_NEW_DATA: |
|
do_gnutls_read(f); |
|
if (f->write_wants_read) do_gnutls_write(f); |
|
li_stream_notify(stream); |
|
break; |
|
case LI_STREAM_NEW_CQLIMIT: |
|
break; |
|
case LI_STREAM_CONNECTED_DEST: /* app */ |
|
break; |
|
case LI_STREAM_CONNECTED_SOURCE: /* crypt_drain */ |
|
break; |
|
case LI_STREAM_DISCONNECTED_DEST: /* app */ |
|
if (!stream->out->is_closed || 0 != stream->out->length) { |
|
f_abort_gnutls(f); /* didn't read everything */ |
|
} |
|
break; |
|
case LI_STREAM_DISCONNECTED_SOURCE: /* crypt_drain */ |
|
if (!stream->out->is_closed) { |
|
f_abort_gnutls(f); /* didn't get everything */ |
|
} |
|
break; |
|
case LI_STREAM_DESTROY: |
|
f_release(f); |
|
break; |
|
} |
|
} |
|
/* app -> ssl (plain) */ |
|
static void stream_plain_drain_cb(liStream *stream, liStreamEvent event) { |
|
liGnuTLSFilter *f = LI_CONTAINER_OF(stream, liGnuTLSFilter, plain_drain); |
|
switch (event) { |
|
case LI_STREAM_NEW_DATA: |
|
if (!stream->out->is_closed && NULL != stream->source) { |
|
li_chunkqueue_steal_all(stream->out, stream->source->out); |
|
stream->out->is_closed = stream->out->is_closed || stream->source->out->is_closed; |
|
} |
|
do_gnutls_write(f); |
|
if (stream->out->is_closed) { |
|
li_stream_disconnect(stream); |
|
stream->out->is_closed = FALSE; |
|
} |
|
break; |
|
case LI_STREAM_NEW_CQLIMIT: |
|
break; |
|
case LI_STREAM_CONNECTED_DEST: /* crypt_source */ |
|
break; |
|
case LI_STREAM_CONNECTED_SOURCE: /* app */ |
|
break; |
|
case LI_STREAM_DISCONNECTED_DEST: |
|
if (!stream->out->is_closed || 0 != stream->out->length) { |
|
f_abort_gnutls(f); /* didn't read everything */ |
|
} |
|
break; |
|
case LI_STREAM_DISCONNECTED_SOURCE: |
|
if (!stream->out->is_closed) { |
|
f_abort_gnutls(f); /* didn't get everything */ |
|
} |
|
break; |
|
case LI_STREAM_DESTROY: |
|
f_release(f); |
|
break; |
|
} |
|
} |
|
static void stream_crypt_source_limit_notify_cb(gpointer context, gboolean locked) { |
|
liGnuTLSFilter *f = context; |
|
if (!locked) li_stream_again_later(&f->plain_drain); |
|
} |
|
|
|
static int post_client_hello_cb(gnutls_session_t session) { |
|
liGnuTLSFilter *f = gnutls_session_get_ptr(session); |
|
return f->callbacks->post_client_hello_cb(f, f->callback_data); |
|
} |
|
|
|
liGnuTLSFilter* li_gnutls_filter_new( |
|
liServer *srv, liWorker *wrk, |
|
const liGnuTLSFilterCallbacks *callbacks, gpointer data, |
|
gnutls_session_t session, liStream *crypt_source, liStream *crypt_drain |
|
) { |
|
liEventLoop *loop = crypt_source->loop; |
|
liGnuTLSFilter *f; |
|
liCQLimit *out_limit; |
|
|
|
f = g_slice_new0(liGnuTLSFilter); |
|
f->refcount = 5; /* 1 + 4 streams */ |
|
f->callbacks = callbacks; |
|
f->callback_data = data; |
|
f->srv = srv; |
|
f->wrk = wrk; |
|
|
|
f->session = session; |
|
gnutls_transport_set_ptr(f->session, (gnutls_transport_ptr_t) f); |
|
gnutls_transport_set_push_function(f->session, stream_push); |
|
#ifdef HAVE_GIOVEC |
|
gnutls_transport_set_vec_push_function(f->session, stream_pushv); |
|
#endif |
|
gnutls_transport_set_pull_function(f->session, stream_pull); |
|
|
|
gnutls_session_set_ptr(f->session, f); |
|
gnutls_handshake_set_post_client_hello_function(f->session, post_client_hello_cb); |
|
|
|
f->initial_handshaked_finished = 0; |
|
f->closing = f->aborted = 0; |
|
f->write_wants_read = 0; |
|
|
|
li_stream_init(&f->crypt_source, loop, stream_crypt_source_cb); |
|
li_stream_init(&f->crypt_drain, loop, stream_crypt_drain_cb); |
|
li_stream_init(&f->plain_source, loop, stream_plain_source_cb); |
|
li_stream_init(&f->plain_drain, loop, stream_plain_drain_cb); |
|
|
|
/* "virtual" connections - the content goes through SSL */ |
|
li_stream_connect(&f->plain_drain, &f->crypt_source); |
|
li_stream_connect(&f->crypt_drain, &f->plain_source); |
|
|
|
li_stream_connect(crypt_source, &f->crypt_drain); |
|
li_stream_connect(&f->crypt_source, crypt_drain); |
|
|
|
/* separate limit for buffer of encrypted data |
|
* |
|
* f->plain_drain is already connected to f->crypt_source, |
|
* so they won't share the same limit */ |
|
out_limit = li_cqlimit_new(); |
|
out_limit->notify = stream_crypt_source_limit_notify_cb; |
|
out_limit->context = f; |
|
li_cqlimit_set_limit(out_limit, 32*1024); |
|
li_chunkqueue_set_limit(crypt_drain->out, out_limit); |
|
li_chunkqueue_set_limit(f->crypt_source.out, out_limit); |
|
li_cqlimit_release(out_limit); |
|
|
|
return f; |
|
} |
|
|
|
void li_gnutls_filter_free(liGnuTLSFilter *f) { |
|
assert(NULL != f->callbacks); |
|
f->callbacks = NULL; |
|
f->callback_data = NULL; |
|
|
|
f_close_gnutls(f); |
|
|
|
li_stream_release(&f->crypt_source); |
|
li_stream_release(&f->crypt_drain); |
|
li_stream_release(&f->plain_source); |
|
li_stream_release(&f->plain_drain); |
|
f_release(f); |
|
}
|
|
|