Initial version.
This commit is contained in:
742
src/nxt_gnutls.c
Normal file
742
src/nxt_gnutls.c
Normal file
@@ -0,0 +1,742 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) NGINX, Inc.
|
||||
*/
|
||||
|
||||
#include <nxt_main.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
gnutls_session_t session;
|
||||
|
||||
uint8_t times; /* 2 bits */
|
||||
uint8_t no_shutdown; /* 1 bit */
|
||||
|
||||
nxt_buf_mem_t buffer;
|
||||
} nxt_gnutls_conn_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
gnutls_priority_t ciphers;
|
||||
gnutls_certificate_credentials_t certificate;
|
||||
} nxt_gnutls_ctx_t;
|
||||
|
||||
|
||||
|
||||
#if (NXT_HAVE_GNUTLS_SET_TIME)
|
||||
time_t nxt_gnutls_time(time_t *tp);
|
||||
#endif
|
||||
static nxt_int_t nxt_gnutls_server_init(nxt_ssltls_conf_t *conf);
|
||||
static nxt_int_t nxt_gnutls_set_ciphers(nxt_ssltls_conf_t *conf);
|
||||
|
||||
static void nxt_gnutls_conn_init(nxt_thread_t *thr, nxt_ssltls_conf_t *conf,
|
||||
nxt_event_conn_t *c);
|
||||
static void nxt_gnutls_session_cleanup(void *data);
|
||||
static ssize_t nxt_gnutls_pull(gnutls_transport_ptr_t data, void *buf,
|
||||
size_t size);
|
||||
static ssize_t nxt_gnutls_push(gnutls_transport_ptr_t data, const void *buf,
|
||||
size_t size);
|
||||
#if (NXT_HAVE_GNUTLS_VEC_PUSH)
|
||||
static ssize_t nxt_gnutls_vec_push(gnutls_transport_ptr_t data,
|
||||
const giovec_t *iov, int iovcnt);
|
||||
#endif
|
||||
static void nxt_gnutls_conn_handshake(nxt_thread_t *thr, void *obj, void *data);
|
||||
static void nxt_gnutls_conn_io_read(nxt_thread_t *thr, void *obj, void *data);
|
||||
static ssize_t nxt_gnutls_conn_io_write_chunk(nxt_thread_t *thr,
|
||||
nxt_event_conn_t *c, nxt_buf_t *b, size_t limit);
|
||||
static ssize_t nxt_gnutls_conn_io_send(nxt_event_conn_t *c, void *buf,
|
||||
size_t size);
|
||||
static void nxt_gnutls_conn_io_shutdown(nxt_thread_t *thr, void *obj,
|
||||
void *data);
|
||||
static nxt_int_t nxt_gnutls_conn_test_error(nxt_thread_t *thr,
|
||||
nxt_event_conn_t *c, ssize_t err, nxt_work_handler_t handler);
|
||||
static void nxt_cdecl nxt_gnutls_conn_log_error(nxt_event_conn_t *c,
|
||||
ssize_t err, const char *fmt, ...);
|
||||
static nxt_uint_t nxt_gnutls_log_error_level(nxt_event_conn_t *c, ssize_t err);
|
||||
static void nxt_cdecl nxt_gnutls_log_error(nxt_uint_t level, nxt_log_t *log,
|
||||
int err, const char *fmt, ...);
|
||||
|
||||
|
||||
const nxt_ssltls_lib_t nxt_gnutls_lib = {
|
||||
nxt_gnutls_server_init,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
static nxt_event_conn_io_t nxt_gnutls_event_conn_io = {
|
||||
NULL,
|
||||
NULL,
|
||||
|
||||
nxt_gnutls_conn_io_read,
|
||||
NULL,
|
||||
NULL,
|
||||
|
||||
nxt_event_conn_io_write,
|
||||
nxt_gnutls_conn_io_write_chunk,
|
||||
NULL,
|
||||
NULL,
|
||||
nxt_gnutls_conn_io_send,
|
||||
|
||||
nxt_gnutls_conn_io_shutdown,
|
||||
};
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_gnutls_start(void)
|
||||
{
|
||||
int ret;
|
||||
static nxt_bool_t started;
|
||||
|
||||
if (nxt_fast_path(started)) {
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
started = 1;
|
||||
|
||||
/* TODO: gnutls_global_deinit */
|
||||
|
||||
ret = gnutls_global_init();
|
||||
if (ret != GNUTLS_E_SUCCESS) {
|
||||
nxt_gnutls_log_error(NXT_LOG_CRIT, nxt_thread_log(), ret,
|
||||
"gnutls_global_init() failed");
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
nxt_thread_log_error(NXT_LOG_INFO, "GnuTLS version: %s",
|
||||
gnutls_check_version(NULL));
|
||||
|
||||
#if (NXT_HAVE_GNUTLS_SET_TIME)
|
||||
gnutls_global_set_time_function(nxt_gnutls_time);
|
||||
#endif
|
||||
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
|
||||
#if (NXT_HAVE_GNUTLS_SET_TIME)
|
||||
|
||||
/* GnuTLS 2.12.0 */
|
||||
|
||||
time_t
|
||||
nxt_gnutls_time(time_t *tp)
|
||||
{
|
||||
time_t t;
|
||||
nxt_thread_t *thr;
|
||||
|
||||
thr = nxt_thread();
|
||||
nxt_log_debug(thr->log, "gnutls time");
|
||||
|
||||
t = (time_t) nxt_thread_time(thr);
|
||||
|
||||
if (tp != NULL) {
|
||||
*tp = t;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_gnutls_server_init(nxt_ssltls_conf_t *conf)
|
||||
{
|
||||
int ret;
|
||||
char *certificate, *key, *ca_certificate;
|
||||
nxt_thread_t *thr;
|
||||
nxt_gnutls_ctx_t *ctx;
|
||||
|
||||
if (nxt_slow_path(nxt_gnutls_start() != NXT_OK)) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
/* TODO: mem_pool, cleanup: gnutls_certificate_free_credentials,
|
||||
gnutls_priority_deinit */
|
||||
|
||||
ctx = nxt_zalloc(sizeof(nxt_gnutls_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
conf->ctx = ctx;
|
||||
conf->conn_init = nxt_gnutls_conn_init;
|
||||
|
||||
thr = nxt_thread();
|
||||
|
||||
ret = gnutls_certificate_allocate_credentials(&ctx->certificate);
|
||||
if (ret != GNUTLS_E_SUCCESS) {
|
||||
nxt_gnutls_log_error(NXT_LOG_CRIT, thr->log, ret,
|
||||
"gnutls_certificate_allocate_credentials() failed");
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
certificate = conf->certificate;
|
||||
key = conf->certificate_key;
|
||||
|
||||
ret = gnutls_certificate_set_x509_key_file(ctx->certificate, certificate,
|
||||
key, GNUTLS_X509_FMT_PEM);
|
||||
if (ret != GNUTLS_E_SUCCESS) {
|
||||
nxt_gnutls_log_error(NXT_LOG_CRIT, thr->log, ret,
|
||||
"gnutls_certificate_set_x509_key_file(\"%s\", \"%s\") failed",
|
||||
certificate, key);
|
||||
goto certificate_fail;
|
||||
}
|
||||
|
||||
if (nxt_gnutls_set_ciphers(conf) != NXT_OK) {
|
||||
goto ciphers_fail;
|
||||
}
|
||||
|
||||
if (conf->ca_certificate != NULL) {
|
||||
ca_certificate = conf->ca_certificate;
|
||||
|
||||
ret = gnutls_certificate_set_x509_trust_file(ctx->certificate,
|
||||
ca_certificate,
|
||||
GNUTLS_X509_FMT_PEM);
|
||||
if (ret < 0) {
|
||||
nxt_gnutls_log_error(NXT_LOG_CRIT, thr->log, ret,
|
||||
"gnutls_certificate_set_x509_trust_file(\"%s\") failed",
|
||||
ca_certificate);
|
||||
goto ca_certificate_fail;
|
||||
}
|
||||
}
|
||||
|
||||
return NXT_OK;
|
||||
|
||||
ca_certificate_fail:
|
||||
|
||||
gnutls_priority_deinit(ctx->ciphers);
|
||||
|
||||
ciphers_fail:
|
||||
|
||||
certificate_fail:
|
||||
|
||||
gnutls_certificate_free_credentials(ctx->certificate);
|
||||
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_gnutls_set_ciphers(nxt_ssltls_conf_t *conf)
|
||||
{
|
||||
int ret;
|
||||
const char *ciphers;
|
||||
const char *err;
|
||||
nxt_gnutls_ctx_t *ctx;
|
||||
|
||||
ciphers = (conf->ciphers != NULL) ? conf->ciphers : "NORMAL:!COMP-DEFLATE";
|
||||
ctx = conf->ctx;
|
||||
|
||||
ret = gnutls_priority_init(&ctx->ciphers, ciphers, &err);
|
||||
|
||||
switch (ret) {
|
||||
|
||||
case GNUTLS_E_SUCCESS:
|
||||
return NXT_OK;
|
||||
|
||||
case GNUTLS_E_INVALID_REQUEST:
|
||||
nxt_gnutls_log_error(NXT_LOG_CRIT, nxt_thread_log(), ret,
|
||||
"gnutls_priority_init(\"%s\") failed at \"%s\"",
|
||||
ciphers, err);
|
||||
return NXT_ERROR;
|
||||
|
||||
default:
|
||||
nxt_gnutls_log_error(NXT_LOG_CRIT, nxt_thread_log(), ret,
|
||||
"gnutls_priority_init() failed");
|
||||
return NXT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
nxt_gnutls_conn_init(nxt_thread_t *thr, nxt_ssltls_conf_t *conf,
|
||||
nxt_event_conn_t *c)
|
||||
{
|
||||
int ret;
|
||||
gnutls_session_t sess;
|
||||
nxt_gnutls_ctx_t *ctx;
|
||||
nxt_gnutls_conn_t *ssltls;
|
||||
nxt_mem_pool_cleanup_t *mpcl;
|
||||
|
||||
nxt_log_debug(c->socket.log, "gnutls conn init");
|
||||
|
||||
ssltls = nxt_mem_zalloc(c->mem_pool, sizeof(nxt_gnutls_conn_t));
|
||||
if (ssltls == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
c->u.ssltls = ssltls;
|
||||
nxt_buf_mem_set_size(&ssltls->buffer, conf->buffer_size);
|
||||
|
||||
mpcl = nxt_mem_pool_cleanup(c->mem_pool, 0);
|
||||
if (mpcl == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = gnutls_init(&ssltls->session, GNUTLS_SERVER);
|
||||
if (ret != GNUTLS_E_SUCCESS) {
|
||||
nxt_gnutls_log_error(NXT_LOG_CRIT, c->socket.log, ret,
|
||||
"gnutls_init() failed");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
sess = ssltls->session;
|
||||
mpcl->handler = nxt_gnutls_session_cleanup;
|
||||
mpcl->data = ssltls;
|
||||
|
||||
ctx = conf->ctx;
|
||||
|
||||
ret = gnutls_priority_set(sess, ctx->ciphers);
|
||||
if (ret != GNUTLS_E_SUCCESS) {
|
||||
nxt_gnutls_log_error(NXT_LOG_CRIT, c->socket.log, ret,
|
||||
"gnutls_priority_set() failed");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable TLS random padding of records in CBC ciphers,
|
||||
* which may be up to 255 bytes.
|
||||
*/
|
||||
gnutls_record_disable_padding(sess);
|
||||
|
||||
ret = gnutls_credentials_set(sess, GNUTLS_CRD_CERTIFICATE,
|
||||
ctx->certificate);
|
||||
if (ret != GNUTLS_E_SUCCESS) {
|
||||
nxt_gnutls_log_error(NXT_LOG_CRIT, c->socket.log, ret,
|
||||
"gnutls_credentials_set() failed");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (conf->ca_certificate != NULL) {
|
||||
gnutls_certificate_server_set_request(sess, GNUTLS_CERT_REQUEST);
|
||||
}
|
||||
|
||||
gnutls_transport_set_ptr(sess, (gnutls_transport_ptr_t) c);
|
||||
gnutls_transport_set_pull_function(sess, nxt_gnutls_pull);
|
||||
gnutls_transport_set_push_function(sess, nxt_gnutls_push);
|
||||
#if (NXT_HAVE_GNUTLS_VEC_PUSH)
|
||||
gnutls_transport_set_vec_push_function(sess, nxt_gnutls_vec_push);
|
||||
#endif
|
||||
|
||||
c->io = &nxt_gnutls_event_conn_io;
|
||||
c->sendfile = NXT_CONN_SENDFILE_OFF;
|
||||
|
||||
nxt_gnutls_conn_handshake(thr, c, c->socket.data);
|
||||
return;
|
||||
|
||||
fail:
|
||||
|
||||
nxt_event_conn_io_handle(thr, c->read_work_queue,
|
||||
c->read_state->error_handler, c, c->socket.data);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
nxt_gnutls_session_cleanup(void *data)
|
||||
{
|
||||
nxt_gnutls_conn_t *ssltls;
|
||||
|
||||
ssltls = data;
|
||||
|
||||
nxt_thread_log_debug("gnutls session cleanup");
|
||||
|
||||
nxt_free(ssltls->buffer.start);
|
||||
|
||||
gnutls_deinit(ssltls->session);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t
|
||||
nxt_gnutls_pull(gnutls_transport_ptr_t data, void *buf, size_t size)
|
||||
{
|
||||
ssize_t n;
|
||||
nxt_thread_t *thr;
|
||||
nxt_event_conn_t *c;
|
||||
|
||||
c = data;
|
||||
thr = nxt_thread();
|
||||
|
||||
n = thr->engine->event->io->recv(c, buf, size, 0);
|
||||
|
||||
if (n == NXT_AGAIN) {
|
||||
nxt_set_errno(NXT_EAGAIN);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t
|
||||
nxt_gnutls_push(gnutls_transport_ptr_t data, const void *buf, size_t size)
|
||||
{
|
||||
ssize_t n;
|
||||
nxt_thread_t *thr;
|
||||
nxt_event_conn_t *c;
|
||||
|
||||
c = data;
|
||||
thr = nxt_thread();
|
||||
|
||||
n = thr->engine->event->io->send(c, (u_char *) buf, size);
|
||||
|
||||
if (n == NXT_AGAIN) {
|
||||
nxt_set_errno(NXT_EAGAIN);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
#if (NXT_HAVE_GNUTLS_VEC_PUSH)
|
||||
|
||||
/* GnuTLS 2.12.0 */
|
||||
|
||||
static ssize_t
|
||||
nxt_gnutls_vec_push(gnutls_transport_ptr_t data, const giovec_t *iov,
|
||||
int iovcnt)
|
||||
{
|
||||
ssize_t n;
|
||||
nxt_thread_t *thr;
|
||||
nxt_event_conn_t *c;
|
||||
|
||||
c = data;
|
||||
thr = nxt_thread();
|
||||
|
||||
/*
|
||||
* This code assumes that giovec_t is the same as "struct iovec"
|
||||
* and nxt_iobuf_t. It is not true for Windows.
|
||||
*/
|
||||
n = thr->engine->event->io->writev(c, (nxt_iobuf_t *) iov, iovcnt);
|
||||
|
||||
if (n == NXT_AGAIN) {
|
||||
nxt_set_errno(NXT_EAGAIN);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static void
|
||||
nxt_gnutls_conn_handshake(nxt_thread_t *thr, void *obj, void *data)
|
||||
{
|
||||
int err;
|
||||
nxt_int_t ret;
|
||||
nxt_event_conn_t *c;
|
||||
nxt_gnutls_conn_t *ssltls;
|
||||
|
||||
c = obj;
|
||||
ssltls = c->u.ssltls;
|
||||
|
||||
nxt_log_debug(thr->log, "gnutls conn handshake: %d", ssltls->times);
|
||||
|
||||
/* "ssltls->times == 1" is suitable to run gnutls_handshake() in job. */
|
||||
|
||||
err = gnutls_handshake(ssltls->session);
|
||||
|
||||
nxt_thread_time_debug_update(thr);
|
||||
|
||||
nxt_log_debug(thr->log, "gnutls_handshake(): %d", err);
|
||||
|
||||
if (err == GNUTLS_E_SUCCESS) {
|
||||
nxt_gnutls_conn_io_read(thr, c, data);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = nxt_gnutls_conn_test_error(thr, c, err, nxt_gnutls_conn_handshake);
|
||||
|
||||
if (ret == NXT_ERROR) {
|
||||
nxt_gnutls_conn_log_error(c, err, "gnutls_handshake() failed");
|
||||
|
||||
nxt_event_conn_io_handle(thr, c->read_work_queue,
|
||||
c->read_state->error_handler, c, data);
|
||||
|
||||
} else if (err == GNUTLS_E_AGAIN
|
||||
&& ssltls->times < 2
|
||||
&& gnutls_record_get_direction(ssltls->session) == 0)
|
||||
{
|
||||
ssltls->times++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
nxt_gnutls_conn_io_read(nxt_thread_t *thr, void *obj, void *data)
|
||||
{
|
||||
ssize_t n;
|
||||
nxt_buf_t *b;
|
||||
nxt_int_t ret;
|
||||
nxt_event_conn_t *c;
|
||||
nxt_gnutls_conn_t *ssltls;
|
||||
nxt_work_handler_t handler;
|
||||
|
||||
c = obj;
|
||||
|
||||
nxt_log_debug(thr->log, "gnutls conn read");
|
||||
|
||||
handler = c->read_state->ready_handler;
|
||||
b = c->read;
|
||||
|
||||
/* b == NULL is used to test descriptor readiness. */
|
||||
|
||||
if (b != NULL) {
|
||||
ssltls = c->u.ssltls;
|
||||
|
||||
n = gnutls_record_recv(ssltls->session, b->mem.free,
|
||||
b->mem.end - b->mem.free);
|
||||
|
||||
nxt_log_debug(thr->log, "gnutls_record_recv(%d, %p, %uz): %z",
|
||||
c->socket.fd, b->mem.free, b->mem.end - b->mem.free, n);
|
||||
|
||||
if (n > 0) {
|
||||
/* c->socket.read_ready is kept. */
|
||||
b->mem.free += n;
|
||||
handler = c->read_state->ready_handler;
|
||||
|
||||
} else if (n == 0) {
|
||||
handler = c->read_state->close_handler;
|
||||
|
||||
} else {
|
||||
ret = nxt_gnutls_conn_test_error(thr, c, n,
|
||||
nxt_gnutls_conn_io_read);
|
||||
|
||||
if (nxt_fast_path(ret != NXT_ERROR)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nxt_gnutls_conn_log_error(c, n,
|
||||
"gnutls_record_recv(%d, %p, %uz): failed",
|
||||
c->socket.fd, b->mem.free,
|
||||
b->mem.end - b->mem.free);
|
||||
|
||||
handler = c->read_state->error_handler;
|
||||
}
|
||||
}
|
||||
|
||||
nxt_event_conn_io_handle(thr, c->read_work_queue, handler, c, data);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t
|
||||
nxt_gnutls_conn_io_write_chunk(nxt_thread_t *thr, nxt_event_conn_t *c,
|
||||
nxt_buf_t *b, size_t limit)
|
||||
{
|
||||
nxt_gnutls_conn_t *ssltls;
|
||||
|
||||
nxt_log_debug(thr->log, "gnutls conn write chunk");
|
||||
|
||||
ssltls = c->u.ssltls;
|
||||
|
||||
return nxt_sendbuf_copy_coalesce(c, &ssltls->buffer, b, limit);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t
|
||||
nxt_gnutls_conn_io_send(nxt_event_conn_t *c, void *buf, size_t size)
|
||||
{
|
||||
ssize_t n;
|
||||
nxt_int_t ret;
|
||||
nxt_gnutls_conn_t *ssltls;
|
||||
|
||||
ssltls = c->u.ssltls;
|
||||
|
||||
n = gnutls_record_send(ssltls->session, buf, size);
|
||||
|
||||
nxt_log_debug(c->socket.log, "gnutls_record_send(%d, %p, %uz): %z",
|
||||
c->socket.fd, buf, size, n);
|
||||
|
||||
if (n > 0) {
|
||||
return n;
|
||||
}
|
||||
|
||||
ret = nxt_gnutls_conn_test_error(nxt_thread(), c, n,
|
||||
nxt_event_conn_io_write);
|
||||
|
||||
if (nxt_slow_path(ret == NXT_ERROR)) {
|
||||
nxt_gnutls_conn_log_error(c, n,
|
||||
"gnutls_record_send(%d, %p, %uz): failed",
|
||||
c->socket.fd, buf, size);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
nxt_gnutls_conn_io_shutdown(nxt_thread_t *thr, void *obj, void *data)
|
||||
{
|
||||
int err;
|
||||
nxt_int_t ret;
|
||||
nxt_event_conn_t *c;
|
||||
nxt_gnutls_conn_t *ssltls;
|
||||
nxt_work_handler_t handler;
|
||||
gnutls_close_request_t how;
|
||||
|
||||
c = obj;
|
||||
ssltls = c->u.ssltls;
|
||||
|
||||
if (ssltls->session == NULL || ssltls->no_shutdown) {
|
||||
handler = c->write_state->close_handler;
|
||||
goto done;
|
||||
}
|
||||
|
||||
nxt_log_debug(c->socket.log, "gnutls conn shutdown");
|
||||
|
||||
if (c->socket.timedout || c->socket.error != 0) {
|
||||
how = GNUTLS_SHUT_WR;
|
||||
|
||||
} else if (c->socket.closed) {
|
||||
how = GNUTLS_SHUT_RDWR;
|
||||
|
||||
} else {
|
||||
how = GNUTLS_SHUT_RDWR;
|
||||
}
|
||||
|
||||
err = gnutls_bye(ssltls->session, how);
|
||||
|
||||
nxt_log_debug(c->socket.log, "gnutls_bye(%d, %d): %d",
|
||||
c->socket.fd, how, err);
|
||||
|
||||
if (err == GNUTLS_E_SUCCESS) {
|
||||
handler = c->write_state->close_handler;
|
||||
|
||||
} else {
|
||||
ret = nxt_gnutls_conn_test_error(thr, c, err,
|
||||
nxt_gnutls_conn_io_shutdown);
|
||||
|
||||
if (ret != NXT_ERROR) { /* ret == NXT_AGAIN */
|
||||
c->socket.error_handler = c->read_state->error_handler;
|
||||
nxt_event_timer_add(thr->engine, &c->read_timer, 5000);
|
||||
return;
|
||||
}
|
||||
|
||||
nxt_gnutls_conn_log_error(c, err, "gnutls_bye(%d) failed",
|
||||
c->socket.fd);
|
||||
|
||||
handler = c->write_state->error_handler;
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
nxt_event_conn_io_handle(thr, c->write_work_queue, handler, c, data);
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_gnutls_conn_test_error(nxt_thread_t *thr, nxt_event_conn_t *c, ssize_t err,
|
||||
nxt_work_handler_t handler)
|
||||
{
|
||||
int ret;
|
||||
nxt_gnutls_conn_t *ssltls;
|
||||
|
||||
switch (err) {
|
||||
|
||||
case GNUTLS_E_REHANDSHAKE:
|
||||
case GNUTLS_E_AGAIN:
|
||||
ssltls = c->u.ssltls;
|
||||
ret = gnutls_record_get_direction(ssltls->session);
|
||||
|
||||
nxt_log_debug(thr->log, "gnutls_record_get_direction(): %d", ret);
|
||||
|
||||
if (ret == 0) {
|
||||
/* A read direction. */
|
||||
|
||||
nxt_event_fd_block_write(thr->engine, &c->socket);
|
||||
|
||||
c->socket.read_ready = 0;
|
||||
c->socket.read_handler = handler;
|
||||
|
||||
if (nxt_event_fd_is_disabled(c->socket.read)) {
|
||||
nxt_event_fd_enable_read(thr->engine, &c->socket);
|
||||
}
|
||||
|
||||
} else {
|
||||
/* A write direction. */
|
||||
|
||||
nxt_event_fd_block_read(thr->engine, &c->socket);
|
||||
|
||||
c->socket.write_ready = 0;
|
||||
c->socket.write_handler = handler;
|
||||
|
||||
if (nxt_event_fd_is_disabled(c->socket.write)) {
|
||||
nxt_event_fd_enable_write(thr->engine, &c->socket);
|
||||
}
|
||||
}
|
||||
|
||||
return NXT_AGAIN;
|
||||
|
||||
default:
|
||||
c->socket.error = 1000; /* Nonexistent errno code. */
|
||||
return NXT_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
nxt_gnutls_conn_log_error(nxt_event_conn_t *c, ssize_t err,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
nxt_uint_t level;
|
||||
u_char *p, msg[NXT_MAX_ERROR_STR];
|
||||
|
||||
level = nxt_gnutls_log_error_level(c, err);
|
||||
|
||||
if (nxt_log_level_enough(c->socket.log, level)) {
|
||||
|
||||
va_start(args, fmt);
|
||||
p = nxt_vsprintf(msg, msg + sizeof(msg), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
nxt_log_error(level, c->socket.log, "%*s (%d: %s)",
|
||||
p - msg, msg, err, gnutls_strerror(err));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static nxt_uint_t
|
||||
nxt_gnutls_log_error_level(nxt_event_conn_t *c, ssize_t err)
|
||||
{
|
||||
nxt_gnutls_conn_t *ssltls;
|
||||
|
||||
switch (err) {
|
||||
|
||||
case GNUTLS_E_UNKNOWN_CIPHER_SUITE: /* -21 */
|
||||
|
||||
/* Disable gnutls_bye(), because it returns GNUTLS_E_INTERNAL_ERROR. */
|
||||
ssltls = c->u.ssltls;
|
||||
ssltls->no_shutdown = 1;
|
||||
|
||||
/* Fall through. */
|
||||
|
||||
case GNUTLS_E_UNEXPECTED_PACKET_LENGTH: /* -9 */
|
||||
c->socket.error = 1000; /* Nonexistent errno code. */
|
||||
break;
|
||||
|
||||
default:
|
||||
return NXT_LOG_CRIT;
|
||||
}
|
||||
|
||||
return NXT_LOG_INFO;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
nxt_gnutls_log_error(nxt_uint_t level, nxt_log_t *log, int err,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
u_char *p, msg[NXT_MAX_ERROR_STR];
|
||||
|
||||
va_start(args, fmt);
|
||||
p = nxt_vsprintf(msg, msg + sizeof(msg), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
nxt_log_error(level, log, "%*s (%d: %s)",
|
||||
p - msg, msg, err, gnutls_strerror(err));
|
||||
}
|
||||
Reference in New Issue
Block a user