10Sigor@sysoev.ru
20Sigor@sysoev.ru /*
30Sigor@sysoev.ru * Copyright (C) Igor Sysoev
40Sigor@sysoev.ru * Copyright (C) NGINX, Inc.
50Sigor@sysoev.ru */
60Sigor@sysoev.ru
70Sigor@sysoev.ru #include <nxt_main.h>
80Sigor@sysoev.ru #include <gnutls/gnutls.h>
90Sigor@sysoev.ru
100Sigor@sysoev.ru
110Sigor@sysoev.ru typedef struct {
120Sigor@sysoev.ru gnutls_session_t session;
130Sigor@sysoev.ru
140Sigor@sysoev.ru uint8_t times; /* 2 bits */
150Sigor@sysoev.ru uint8_t no_shutdown; /* 1 bit */
160Sigor@sysoev.ru
170Sigor@sysoev.ru nxt_buf_mem_t buffer;
180Sigor@sysoev.ru } nxt_gnutls_conn_t;
190Sigor@sysoev.ru
200Sigor@sysoev.ru
210Sigor@sysoev.ru typedef struct {
220Sigor@sysoev.ru gnutls_priority_t ciphers;
230Sigor@sysoev.ru gnutls_certificate_credentials_t certificate;
240Sigor@sysoev.ru } nxt_gnutls_ctx_t;
250Sigor@sysoev.ru
260Sigor@sysoev.ru
270Sigor@sysoev.ru
280Sigor@sysoev.ru #if (NXT_HAVE_GNUTLS_SET_TIME)
290Sigor@sysoev.ru time_t nxt_gnutls_time(time_t *tp);
300Sigor@sysoev.ru #endif
310Sigor@sysoev.ru static nxt_int_t nxt_gnutls_server_init(nxt_ssltls_conf_t *conf);
320Sigor@sysoev.ru static nxt_int_t nxt_gnutls_set_ciphers(nxt_ssltls_conf_t *conf);
330Sigor@sysoev.ru
340Sigor@sysoev.ru static void nxt_gnutls_conn_init(nxt_thread_t *thr, nxt_ssltls_conf_t *conf,
350Sigor@sysoev.ru nxt_event_conn_t *c);
360Sigor@sysoev.ru static void nxt_gnutls_session_cleanup(void *data);
370Sigor@sysoev.ru static ssize_t nxt_gnutls_pull(gnutls_transport_ptr_t data, void *buf,
380Sigor@sysoev.ru size_t size);
390Sigor@sysoev.ru static ssize_t nxt_gnutls_push(gnutls_transport_ptr_t data, const void *buf,
400Sigor@sysoev.ru size_t size);
410Sigor@sysoev.ru #if (NXT_HAVE_GNUTLS_VEC_PUSH)
420Sigor@sysoev.ru static ssize_t nxt_gnutls_vec_push(gnutls_transport_ptr_t data,
430Sigor@sysoev.ru const giovec_t *iov, int iovcnt);
440Sigor@sysoev.ru #endif
450Sigor@sysoev.ru static void nxt_gnutls_conn_handshake(nxt_thread_t *thr, void *obj, void *data);
460Sigor@sysoev.ru static void nxt_gnutls_conn_io_read(nxt_thread_t *thr, void *obj, void *data);
470Sigor@sysoev.ru static ssize_t nxt_gnutls_conn_io_write_chunk(nxt_thread_t *thr,
480Sigor@sysoev.ru nxt_event_conn_t *c, nxt_buf_t *b, size_t limit);
490Sigor@sysoev.ru static ssize_t nxt_gnutls_conn_io_send(nxt_event_conn_t *c, void *buf,
500Sigor@sysoev.ru size_t size);
510Sigor@sysoev.ru static void nxt_gnutls_conn_io_shutdown(nxt_thread_t *thr, void *obj,
520Sigor@sysoev.ru void *data);
530Sigor@sysoev.ru static nxt_int_t nxt_gnutls_conn_test_error(nxt_thread_t *thr,
540Sigor@sysoev.ru nxt_event_conn_t *c, ssize_t err, nxt_work_handler_t handler);
550Sigor@sysoev.ru static void nxt_cdecl nxt_gnutls_conn_log_error(nxt_event_conn_t *c,
560Sigor@sysoev.ru ssize_t err, const char *fmt, ...);
570Sigor@sysoev.ru static nxt_uint_t nxt_gnutls_log_error_level(nxt_event_conn_t *c, ssize_t err);
580Sigor@sysoev.ru static void nxt_cdecl nxt_gnutls_log_error(nxt_uint_t level, nxt_log_t *log,
590Sigor@sysoev.ru int err, const char *fmt, ...);
600Sigor@sysoev.ru
610Sigor@sysoev.ru
620Sigor@sysoev.ru const nxt_ssltls_lib_t nxt_gnutls_lib = {
630Sigor@sysoev.ru nxt_gnutls_server_init,
640Sigor@sysoev.ru NULL,
650Sigor@sysoev.ru };
660Sigor@sysoev.ru
670Sigor@sysoev.ru
680Sigor@sysoev.ru static nxt_event_conn_io_t nxt_gnutls_event_conn_io = {
690Sigor@sysoev.ru NULL,
700Sigor@sysoev.ru NULL,
710Sigor@sysoev.ru
720Sigor@sysoev.ru nxt_gnutls_conn_io_read,
730Sigor@sysoev.ru NULL,
740Sigor@sysoev.ru NULL,
750Sigor@sysoev.ru
760Sigor@sysoev.ru nxt_event_conn_io_write,
770Sigor@sysoev.ru nxt_gnutls_conn_io_write_chunk,
780Sigor@sysoev.ru NULL,
790Sigor@sysoev.ru NULL,
800Sigor@sysoev.ru nxt_gnutls_conn_io_send,
810Sigor@sysoev.ru
820Sigor@sysoev.ru nxt_gnutls_conn_io_shutdown,
830Sigor@sysoev.ru };
840Sigor@sysoev.ru
850Sigor@sysoev.ru
860Sigor@sysoev.ru static nxt_int_t
nxt_gnutls_start(void)870Sigor@sysoev.ru nxt_gnutls_start(void)
880Sigor@sysoev.ru {
890Sigor@sysoev.ru int ret;
900Sigor@sysoev.ru static nxt_bool_t started;
910Sigor@sysoev.ru
920Sigor@sysoev.ru if (nxt_fast_path(started)) {
930Sigor@sysoev.ru return NXT_OK;
940Sigor@sysoev.ru }
950Sigor@sysoev.ru
960Sigor@sysoev.ru started = 1;
970Sigor@sysoev.ru
980Sigor@sysoev.ru /* TODO: gnutls_global_deinit */
990Sigor@sysoev.ru
1000Sigor@sysoev.ru ret = gnutls_global_init();
1010Sigor@sysoev.ru if (ret != GNUTLS_E_SUCCESS) {
102564Svbart@nginx.com nxt_gnutls_log_error(NXT_LOG_ALERT, nxt_thread_log(), ret,
1030Sigor@sysoev.ru "gnutls_global_init() failed");
1040Sigor@sysoev.ru return NXT_ERROR;
1050Sigor@sysoev.ru }
1060Sigor@sysoev.ru
1070Sigor@sysoev.ru nxt_thread_log_error(NXT_LOG_INFO, "GnuTLS version: %s",
1080Sigor@sysoev.ru gnutls_check_version(NULL));
1090Sigor@sysoev.ru
1100Sigor@sysoev.ru #if (NXT_HAVE_GNUTLS_SET_TIME)
1110Sigor@sysoev.ru gnutls_global_set_time_function(nxt_gnutls_time);
1120Sigor@sysoev.ru #endif
1130Sigor@sysoev.ru
1140Sigor@sysoev.ru return NXT_OK;
1150Sigor@sysoev.ru }
1160Sigor@sysoev.ru
1170Sigor@sysoev.ru
1180Sigor@sysoev.ru #if (NXT_HAVE_GNUTLS_SET_TIME)
1190Sigor@sysoev.ru
1200Sigor@sysoev.ru /* GnuTLS 2.12.0 */
1210Sigor@sysoev.ru
1220Sigor@sysoev.ru time_t
nxt_gnutls_time(time_t * tp)1230Sigor@sysoev.ru nxt_gnutls_time(time_t *tp)
1240Sigor@sysoev.ru {
1250Sigor@sysoev.ru time_t t;
1260Sigor@sysoev.ru nxt_thread_t *thr;
1270Sigor@sysoev.ru
1280Sigor@sysoev.ru thr = nxt_thread();
1290Sigor@sysoev.ru nxt_log_debug(thr->log, "gnutls time");
1300Sigor@sysoev.ru
1310Sigor@sysoev.ru t = (time_t) nxt_thread_time(thr);
1320Sigor@sysoev.ru
1330Sigor@sysoev.ru if (tp != NULL) {
1340Sigor@sysoev.ru *tp = t;
1350Sigor@sysoev.ru }
1360Sigor@sysoev.ru
1370Sigor@sysoev.ru return t;
1380Sigor@sysoev.ru }
1390Sigor@sysoev.ru
1400Sigor@sysoev.ru #endif
1410Sigor@sysoev.ru
1420Sigor@sysoev.ru
1430Sigor@sysoev.ru static nxt_int_t
nxt_gnutls_server_init(nxt_ssltls_conf_t * conf)1440Sigor@sysoev.ru nxt_gnutls_server_init(nxt_ssltls_conf_t *conf)
1450Sigor@sysoev.ru {
1460Sigor@sysoev.ru int ret;
1470Sigor@sysoev.ru char *certificate, *key, *ca_certificate;
1480Sigor@sysoev.ru nxt_thread_t *thr;
1490Sigor@sysoev.ru nxt_gnutls_ctx_t *ctx;
1500Sigor@sysoev.ru
1510Sigor@sysoev.ru if (nxt_slow_path(nxt_gnutls_start() != NXT_OK)) {
1520Sigor@sysoev.ru return NXT_ERROR;
1530Sigor@sysoev.ru }
1540Sigor@sysoev.ru
1550Sigor@sysoev.ru /* TODO: mem_pool, cleanup: gnutls_certificate_free_credentials,
1560Sigor@sysoev.ru gnutls_priority_deinit */
1570Sigor@sysoev.ru
1580Sigor@sysoev.ru ctx = nxt_zalloc(sizeof(nxt_gnutls_ctx_t));
1590Sigor@sysoev.ru if (ctx == NULL) {
1600Sigor@sysoev.ru return NXT_ERROR;
1610Sigor@sysoev.ru }
1620Sigor@sysoev.ru
1630Sigor@sysoev.ru conf->ctx = ctx;
1640Sigor@sysoev.ru conf->conn_init = nxt_gnutls_conn_init;
1650Sigor@sysoev.ru
1660Sigor@sysoev.ru thr = nxt_thread();
1670Sigor@sysoev.ru
1680Sigor@sysoev.ru ret = gnutls_certificate_allocate_credentials(&ctx->certificate);
1690Sigor@sysoev.ru if (ret != GNUTLS_E_SUCCESS) {
170564Svbart@nginx.com nxt_gnutls_log_error(NXT_LOG_ALERT, thr->log, ret,
1710Sigor@sysoev.ru "gnutls_certificate_allocate_credentials() failed");
1720Sigor@sysoev.ru return NXT_ERROR;
1730Sigor@sysoev.ru }
1740Sigor@sysoev.ru
1750Sigor@sysoev.ru certificate = conf->certificate;
1760Sigor@sysoev.ru key = conf->certificate_key;
1770Sigor@sysoev.ru
1780Sigor@sysoev.ru ret = gnutls_certificate_set_x509_key_file(ctx->certificate, certificate,
1790Sigor@sysoev.ru key, GNUTLS_X509_FMT_PEM);
1800Sigor@sysoev.ru if (ret != GNUTLS_E_SUCCESS) {
181564Svbart@nginx.com nxt_gnutls_log_error(NXT_LOG_ALERT, thr->log, ret,
1820Sigor@sysoev.ru "gnutls_certificate_set_x509_key_file(\"%s\", \"%s\") failed",
1830Sigor@sysoev.ru certificate, key);
1840Sigor@sysoev.ru goto certificate_fail;
1850Sigor@sysoev.ru }
1860Sigor@sysoev.ru
1870Sigor@sysoev.ru if (nxt_gnutls_set_ciphers(conf) != NXT_OK) {
1880Sigor@sysoev.ru goto ciphers_fail;
1890Sigor@sysoev.ru }
1900Sigor@sysoev.ru
1910Sigor@sysoev.ru if (conf->ca_certificate != NULL) {
1920Sigor@sysoev.ru ca_certificate = conf->ca_certificate;
1930Sigor@sysoev.ru
1940Sigor@sysoev.ru ret = gnutls_certificate_set_x509_trust_file(ctx->certificate,
1950Sigor@sysoev.ru ca_certificate,
1960Sigor@sysoev.ru GNUTLS_X509_FMT_PEM);
1970Sigor@sysoev.ru if (ret < 0) {
198564Svbart@nginx.com nxt_gnutls_log_error(NXT_LOG_ALERT, thr->log, ret,
1990Sigor@sysoev.ru "gnutls_certificate_set_x509_trust_file(\"%s\") failed",
2000Sigor@sysoev.ru ca_certificate);
2010Sigor@sysoev.ru goto ca_certificate_fail;
2020Sigor@sysoev.ru }
2030Sigor@sysoev.ru }
2040Sigor@sysoev.ru
2050Sigor@sysoev.ru return NXT_OK;
2060Sigor@sysoev.ru
2070Sigor@sysoev.ru ca_certificate_fail:
2080Sigor@sysoev.ru
2090Sigor@sysoev.ru gnutls_priority_deinit(ctx->ciphers);
2100Sigor@sysoev.ru
2110Sigor@sysoev.ru ciphers_fail:
2120Sigor@sysoev.ru
2130Sigor@sysoev.ru certificate_fail:
2140Sigor@sysoev.ru
2150Sigor@sysoev.ru gnutls_certificate_free_credentials(ctx->certificate);
2160Sigor@sysoev.ru
2170Sigor@sysoev.ru return NXT_ERROR;
2180Sigor@sysoev.ru }
2190Sigor@sysoev.ru
2200Sigor@sysoev.ru
2210Sigor@sysoev.ru static nxt_int_t
nxt_gnutls_set_ciphers(nxt_ssltls_conf_t * conf)2220Sigor@sysoev.ru nxt_gnutls_set_ciphers(nxt_ssltls_conf_t *conf)
2230Sigor@sysoev.ru {
2240Sigor@sysoev.ru int ret;
2250Sigor@sysoev.ru const char *ciphers;
2260Sigor@sysoev.ru const char *err;
2270Sigor@sysoev.ru nxt_gnutls_ctx_t *ctx;
2280Sigor@sysoev.ru
2290Sigor@sysoev.ru ciphers = (conf->ciphers != NULL) ? conf->ciphers : "NORMAL:!COMP-DEFLATE";
2300Sigor@sysoev.ru ctx = conf->ctx;
2310Sigor@sysoev.ru
2320Sigor@sysoev.ru ret = gnutls_priority_init(&ctx->ciphers, ciphers, &err);
2330Sigor@sysoev.ru
2340Sigor@sysoev.ru switch (ret) {
2350Sigor@sysoev.ru
2360Sigor@sysoev.ru case GNUTLS_E_SUCCESS:
2370Sigor@sysoev.ru return NXT_OK;
2380Sigor@sysoev.ru
2390Sigor@sysoev.ru case GNUTLS_E_INVALID_REQUEST:
240564Svbart@nginx.com nxt_gnutls_log_error(NXT_LOG_ALERT, nxt_thread_log(), ret,
2410Sigor@sysoev.ru "gnutls_priority_init(\"%s\") failed at \"%s\"",
2420Sigor@sysoev.ru ciphers, err);
2430Sigor@sysoev.ru return NXT_ERROR;
2440Sigor@sysoev.ru
2450Sigor@sysoev.ru default:
246564Svbart@nginx.com nxt_gnutls_log_error(NXT_LOG_ALERT, nxt_thread_log(), ret,
2470Sigor@sysoev.ru "gnutls_priority_init() failed");
2480Sigor@sysoev.ru return NXT_ERROR;
2490Sigor@sysoev.ru }
2500Sigor@sysoev.ru }
2510Sigor@sysoev.ru
2520Sigor@sysoev.ru
2530Sigor@sysoev.ru static void
nxt_gnutls_conn_init(nxt_thread_t * thr,nxt_ssltls_conf_t * conf,nxt_event_conn_t * c)2540Sigor@sysoev.ru nxt_gnutls_conn_init(nxt_thread_t *thr, nxt_ssltls_conf_t *conf,
2550Sigor@sysoev.ru nxt_event_conn_t *c)
2560Sigor@sysoev.ru {
2570Sigor@sysoev.ru int ret;
2580Sigor@sysoev.ru gnutls_session_t sess;
2590Sigor@sysoev.ru nxt_gnutls_ctx_t *ctx;
2600Sigor@sysoev.ru nxt_gnutls_conn_t *ssltls;
2610Sigor@sysoev.ru nxt_mem_pool_cleanup_t *mpcl;
2620Sigor@sysoev.ru
2630Sigor@sysoev.ru nxt_log_debug(c->socket.log, "gnutls conn init");
2640Sigor@sysoev.ru
26565Sigor@sysoev.ru ssltls = nxt_mp_zget(c->mem_pool, sizeof(nxt_gnutls_conn_t));
2660Sigor@sysoev.ru if (ssltls == NULL) {
2670Sigor@sysoev.ru goto fail;
2680Sigor@sysoev.ru }
2690Sigor@sysoev.ru
2700Sigor@sysoev.ru c->u.ssltls = ssltls;
2710Sigor@sysoev.ru nxt_buf_mem_set_size(&ssltls->buffer, conf->buffer_size);
2720Sigor@sysoev.ru
2730Sigor@sysoev.ru mpcl = nxt_mem_pool_cleanup(c->mem_pool, 0);
2740Sigor@sysoev.ru if (mpcl == NULL) {
2750Sigor@sysoev.ru goto fail;
2760Sigor@sysoev.ru }
2770Sigor@sysoev.ru
2780Sigor@sysoev.ru ret = gnutls_init(&ssltls->session, GNUTLS_SERVER);
2790Sigor@sysoev.ru if (ret != GNUTLS_E_SUCCESS) {
280564Svbart@nginx.com nxt_gnutls_log_error(NXT_LOG_ALERT, c->socket.log, ret,
2810Sigor@sysoev.ru "gnutls_init() failed");
2820Sigor@sysoev.ru goto fail;
2830Sigor@sysoev.ru }
2840Sigor@sysoev.ru
2850Sigor@sysoev.ru sess = ssltls->session;
2860Sigor@sysoev.ru mpcl->handler = nxt_gnutls_session_cleanup;
2870Sigor@sysoev.ru mpcl->data = ssltls;
2880Sigor@sysoev.ru
2890Sigor@sysoev.ru ctx = conf->ctx;
2900Sigor@sysoev.ru
2910Sigor@sysoev.ru ret = gnutls_priority_set(sess, ctx->ciphers);
2920Sigor@sysoev.ru if (ret != GNUTLS_E_SUCCESS) {
293564Svbart@nginx.com nxt_gnutls_log_error(NXT_LOG_ALERT, c->socket.log, ret,
2940Sigor@sysoev.ru "gnutls_priority_set() failed");
2950Sigor@sysoev.ru goto fail;
2960Sigor@sysoev.ru }
2970Sigor@sysoev.ru
2980Sigor@sysoev.ru /*
2990Sigor@sysoev.ru * Disable TLS random padding of records in CBC ciphers,
3000Sigor@sysoev.ru * which may be up to 255 bytes.
3010Sigor@sysoev.ru */
3020Sigor@sysoev.ru gnutls_record_disable_padding(sess);
3030Sigor@sysoev.ru
3040Sigor@sysoev.ru ret = gnutls_credentials_set(sess, GNUTLS_CRD_CERTIFICATE,
3050Sigor@sysoev.ru ctx->certificate);
3060Sigor@sysoev.ru if (ret != GNUTLS_E_SUCCESS) {
307564Svbart@nginx.com nxt_gnutls_log_error(NXT_LOG_ALERT, c->socket.log, ret,
3080Sigor@sysoev.ru "gnutls_credentials_set() failed");
3090Sigor@sysoev.ru goto fail;
3100Sigor@sysoev.ru }
3110Sigor@sysoev.ru
3120Sigor@sysoev.ru if (conf->ca_certificate != NULL) {
3130Sigor@sysoev.ru gnutls_certificate_server_set_request(sess, GNUTLS_CERT_REQUEST);
3140Sigor@sysoev.ru }
3150Sigor@sysoev.ru
3160Sigor@sysoev.ru gnutls_transport_set_ptr(sess, (gnutls_transport_ptr_t) c);
3170Sigor@sysoev.ru gnutls_transport_set_pull_function(sess, nxt_gnutls_pull);
3180Sigor@sysoev.ru gnutls_transport_set_push_function(sess, nxt_gnutls_push);
3190Sigor@sysoev.ru #if (NXT_HAVE_GNUTLS_VEC_PUSH)
3200Sigor@sysoev.ru gnutls_transport_set_vec_push_function(sess, nxt_gnutls_vec_push);
3210Sigor@sysoev.ru #endif
3220Sigor@sysoev.ru
3230Sigor@sysoev.ru c->io = &nxt_gnutls_event_conn_io;
3240Sigor@sysoev.ru c->sendfile = NXT_CONN_SENDFILE_OFF;
3250Sigor@sysoev.ru
3260Sigor@sysoev.ru nxt_gnutls_conn_handshake(thr, c, c->socket.data);
3270Sigor@sysoev.ru return;
3280Sigor@sysoev.ru
3290Sigor@sysoev.ru fail:
3300Sigor@sysoev.ru
3310Sigor@sysoev.ru nxt_event_conn_io_handle(thr, c->read_work_queue,
3320Sigor@sysoev.ru c->read_state->error_handler, c, c->socket.data);
3330Sigor@sysoev.ru }
3340Sigor@sysoev.ru
3350Sigor@sysoev.ru
3360Sigor@sysoev.ru static void
nxt_gnutls_session_cleanup(void * data)3370Sigor@sysoev.ru nxt_gnutls_session_cleanup(void *data)
3380Sigor@sysoev.ru {
3390Sigor@sysoev.ru nxt_gnutls_conn_t *ssltls;
3400Sigor@sysoev.ru
3410Sigor@sysoev.ru ssltls = data;
3420Sigor@sysoev.ru
3430Sigor@sysoev.ru nxt_thread_log_debug("gnutls session cleanup");
3440Sigor@sysoev.ru
3450Sigor@sysoev.ru nxt_free(ssltls->buffer.start);
3460Sigor@sysoev.ru
3470Sigor@sysoev.ru gnutls_deinit(ssltls->session);
3480Sigor@sysoev.ru }
3490Sigor@sysoev.ru
3500Sigor@sysoev.ru
3510Sigor@sysoev.ru static ssize_t
nxt_gnutls_pull(gnutls_transport_ptr_t data,void * buf,size_t size)3520Sigor@sysoev.ru nxt_gnutls_pull(gnutls_transport_ptr_t data, void *buf, size_t size)
3530Sigor@sysoev.ru {
3540Sigor@sysoev.ru ssize_t n;
3550Sigor@sysoev.ru nxt_thread_t *thr;
3560Sigor@sysoev.ru nxt_event_conn_t *c;
3570Sigor@sysoev.ru
3580Sigor@sysoev.ru c = data;
3590Sigor@sysoev.ru thr = nxt_thread();
3600Sigor@sysoev.ru
3610Sigor@sysoev.ru n = thr->engine->event->io->recv(c, buf, size, 0);
3620Sigor@sysoev.ru
3630Sigor@sysoev.ru if (n == NXT_AGAIN) {
3640Sigor@sysoev.ru nxt_set_errno(NXT_EAGAIN);
3650Sigor@sysoev.ru return -1;
3660Sigor@sysoev.ru }
3670Sigor@sysoev.ru
3680Sigor@sysoev.ru return n;
3690Sigor@sysoev.ru }
3700Sigor@sysoev.ru
3710Sigor@sysoev.ru
3720Sigor@sysoev.ru static ssize_t
nxt_gnutls_push(gnutls_transport_ptr_t data,const void * buf,size_t size)3730Sigor@sysoev.ru nxt_gnutls_push(gnutls_transport_ptr_t data, const void *buf, size_t size)
3740Sigor@sysoev.ru {
3750Sigor@sysoev.ru ssize_t n;
3760Sigor@sysoev.ru nxt_thread_t *thr;
3770Sigor@sysoev.ru nxt_event_conn_t *c;
3780Sigor@sysoev.ru
3790Sigor@sysoev.ru c = data;
3800Sigor@sysoev.ru thr = nxt_thread();
3810Sigor@sysoev.ru
3820Sigor@sysoev.ru n = thr->engine->event->io->send(c, (u_char *) buf, size);
3830Sigor@sysoev.ru
3840Sigor@sysoev.ru if (n == NXT_AGAIN) {
3850Sigor@sysoev.ru nxt_set_errno(NXT_EAGAIN);
3860Sigor@sysoev.ru return -1;
3870Sigor@sysoev.ru }
3880Sigor@sysoev.ru
3890Sigor@sysoev.ru return n;
3900Sigor@sysoev.ru }
3910Sigor@sysoev.ru
3920Sigor@sysoev.ru
3930Sigor@sysoev.ru #if (NXT_HAVE_GNUTLS_VEC_PUSH)
3940Sigor@sysoev.ru
3950Sigor@sysoev.ru /* GnuTLS 2.12.0 */
3960Sigor@sysoev.ru
3970Sigor@sysoev.ru static ssize_t
nxt_gnutls_vec_push(gnutls_transport_ptr_t data,const giovec_t * iov,int iovcnt)3980Sigor@sysoev.ru nxt_gnutls_vec_push(gnutls_transport_ptr_t data, const giovec_t *iov,
3990Sigor@sysoev.ru int iovcnt)
4000Sigor@sysoev.ru {
4010Sigor@sysoev.ru ssize_t n;
4020Sigor@sysoev.ru nxt_thread_t *thr;
4030Sigor@sysoev.ru nxt_event_conn_t *c;
4040Sigor@sysoev.ru
4050Sigor@sysoev.ru c = data;
4060Sigor@sysoev.ru thr = nxt_thread();
4070Sigor@sysoev.ru
4080Sigor@sysoev.ru /*
4090Sigor@sysoev.ru * This code assumes that giovec_t is the same as "struct iovec"
4100Sigor@sysoev.ru * and nxt_iobuf_t. It is not true for Windows.
4110Sigor@sysoev.ru */
4120Sigor@sysoev.ru n = thr->engine->event->io->writev(c, (nxt_iobuf_t *) iov, iovcnt);
4130Sigor@sysoev.ru
4140Sigor@sysoev.ru if (n == NXT_AGAIN) {
4150Sigor@sysoev.ru nxt_set_errno(NXT_EAGAIN);
4160Sigor@sysoev.ru return -1;
4170Sigor@sysoev.ru }
4180Sigor@sysoev.ru
4190Sigor@sysoev.ru return n;
4200Sigor@sysoev.ru }
4210Sigor@sysoev.ru
4220Sigor@sysoev.ru #endif
4230Sigor@sysoev.ru
4240Sigor@sysoev.ru
4250Sigor@sysoev.ru static void
nxt_gnutls_conn_handshake(nxt_thread_t * thr,void * obj,void * data)4260Sigor@sysoev.ru nxt_gnutls_conn_handshake(nxt_thread_t *thr, void *obj, void *data)
4270Sigor@sysoev.ru {
4280Sigor@sysoev.ru int err;
4290Sigor@sysoev.ru nxt_int_t ret;
4300Sigor@sysoev.ru nxt_event_conn_t *c;
4310Sigor@sysoev.ru nxt_gnutls_conn_t *ssltls;
4320Sigor@sysoev.ru
4330Sigor@sysoev.ru c = obj;
4340Sigor@sysoev.ru ssltls = c->u.ssltls;
4350Sigor@sysoev.ru
4360Sigor@sysoev.ru nxt_log_debug(thr->log, "gnutls conn handshake: %d", ssltls->times);
4370Sigor@sysoev.ru
4380Sigor@sysoev.ru /* "ssltls->times == 1" is suitable to run gnutls_handshake() in job. */
4390Sigor@sysoev.ru
4400Sigor@sysoev.ru err = gnutls_handshake(ssltls->session);
4410Sigor@sysoev.ru
4420Sigor@sysoev.ru nxt_thread_time_debug_update(thr);
4430Sigor@sysoev.ru
4440Sigor@sysoev.ru nxt_log_debug(thr->log, "gnutls_handshake(): %d", err);
4450Sigor@sysoev.ru
4460Sigor@sysoev.ru if (err == GNUTLS_E_SUCCESS) {
4470Sigor@sysoev.ru nxt_gnutls_conn_io_read(thr, c, data);
4480Sigor@sysoev.ru return;
4490Sigor@sysoev.ru }
4500Sigor@sysoev.ru
4510Sigor@sysoev.ru ret = nxt_gnutls_conn_test_error(thr, c, err, nxt_gnutls_conn_handshake);
4520Sigor@sysoev.ru
4530Sigor@sysoev.ru if (ret == NXT_ERROR) {
4540Sigor@sysoev.ru nxt_gnutls_conn_log_error(c, err, "gnutls_handshake() failed");
4550Sigor@sysoev.ru
4560Sigor@sysoev.ru nxt_event_conn_io_handle(thr, c->read_work_queue,
4570Sigor@sysoev.ru c->read_state->error_handler, c, data);
4580Sigor@sysoev.ru
4590Sigor@sysoev.ru } else if (err == GNUTLS_E_AGAIN
4600Sigor@sysoev.ru && ssltls->times < 2
4610Sigor@sysoev.ru && gnutls_record_get_direction(ssltls->session) == 0)
4620Sigor@sysoev.ru {
4630Sigor@sysoev.ru ssltls->times++;
4640Sigor@sysoev.ru }
4650Sigor@sysoev.ru }
4660Sigor@sysoev.ru
4670Sigor@sysoev.ru
4680Sigor@sysoev.ru static void
nxt_gnutls_conn_io_read(nxt_thread_t * thr,void * obj,void * data)4690Sigor@sysoev.ru nxt_gnutls_conn_io_read(nxt_thread_t *thr, void *obj, void *data)
4700Sigor@sysoev.ru {
4710Sigor@sysoev.ru ssize_t n;
4720Sigor@sysoev.ru nxt_buf_t *b;
4730Sigor@sysoev.ru nxt_int_t ret;
4740Sigor@sysoev.ru nxt_event_conn_t *c;
4750Sigor@sysoev.ru nxt_gnutls_conn_t *ssltls;
4760Sigor@sysoev.ru nxt_work_handler_t handler;
4770Sigor@sysoev.ru
4780Sigor@sysoev.ru c = obj;
4790Sigor@sysoev.ru
4800Sigor@sysoev.ru nxt_log_debug(thr->log, "gnutls conn read");
4810Sigor@sysoev.ru
4820Sigor@sysoev.ru handler = c->read_state->ready_handler;
4830Sigor@sysoev.ru b = c->read;
4840Sigor@sysoev.ru
4850Sigor@sysoev.ru /* b == NULL is used to test descriptor readiness. */
4860Sigor@sysoev.ru
4870Sigor@sysoev.ru if (b != NULL) {
4880Sigor@sysoev.ru ssltls = c->u.ssltls;
4890Sigor@sysoev.ru
4900Sigor@sysoev.ru n = gnutls_record_recv(ssltls->session, b->mem.free,
4910Sigor@sysoev.ru b->mem.end - b->mem.free);
4920Sigor@sysoev.ru
4930Sigor@sysoev.ru nxt_log_debug(thr->log, "gnutls_record_recv(%d, %p, %uz): %z",
4940Sigor@sysoev.ru c->socket.fd, b->mem.free, b->mem.end - b->mem.free, n);
4950Sigor@sysoev.ru
4960Sigor@sysoev.ru if (n > 0) {
4970Sigor@sysoev.ru /* c->socket.read_ready is kept. */
4980Sigor@sysoev.ru b->mem.free += n;
4990Sigor@sysoev.ru handler = c->read_state->ready_handler;
5000Sigor@sysoev.ru
5010Sigor@sysoev.ru } else if (n == 0) {
5020Sigor@sysoev.ru handler = c->read_state->close_handler;
5030Sigor@sysoev.ru
5040Sigor@sysoev.ru } else {
5050Sigor@sysoev.ru ret = nxt_gnutls_conn_test_error(thr, c, n,
5060Sigor@sysoev.ru nxt_gnutls_conn_io_read);
5070Sigor@sysoev.ru
5080Sigor@sysoev.ru if (nxt_fast_path(ret != NXT_ERROR)) {
5090Sigor@sysoev.ru return;
5100Sigor@sysoev.ru }
5110Sigor@sysoev.ru
5120Sigor@sysoev.ru nxt_gnutls_conn_log_error(c, n,
5130Sigor@sysoev.ru "gnutls_record_recv(%d, %p, %uz): failed",
5140Sigor@sysoev.ru c->socket.fd, b->mem.free,
5150Sigor@sysoev.ru b->mem.end - b->mem.free);
5160Sigor@sysoev.ru
5170Sigor@sysoev.ru handler = c->read_state->error_handler;
5180Sigor@sysoev.ru }
5190Sigor@sysoev.ru }
5200Sigor@sysoev.ru
5210Sigor@sysoev.ru nxt_event_conn_io_handle(thr, c->read_work_queue, handler, c, data);
5220Sigor@sysoev.ru }
5230Sigor@sysoev.ru
5240Sigor@sysoev.ru
5250Sigor@sysoev.ru 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)5260Sigor@sysoev.ru nxt_gnutls_conn_io_write_chunk(nxt_thread_t *thr, nxt_event_conn_t *c,
5270Sigor@sysoev.ru nxt_buf_t *b, size_t limit)
5280Sigor@sysoev.ru {
5290Sigor@sysoev.ru nxt_gnutls_conn_t *ssltls;
5300Sigor@sysoev.ru
5310Sigor@sysoev.ru nxt_log_debug(thr->log, "gnutls conn write chunk");
5320Sigor@sysoev.ru
5330Sigor@sysoev.ru ssltls = c->u.ssltls;
5340Sigor@sysoev.ru
5350Sigor@sysoev.ru return nxt_sendbuf_copy_coalesce(c, &ssltls->buffer, b, limit);
5360Sigor@sysoev.ru }
5370Sigor@sysoev.ru
5380Sigor@sysoev.ru
5390Sigor@sysoev.ru static ssize_t
nxt_gnutls_conn_io_send(nxt_event_conn_t * c,void * buf,size_t size)5400Sigor@sysoev.ru nxt_gnutls_conn_io_send(nxt_event_conn_t *c, void *buf, size_t size)
5410Sigor@sysoev.ru {
5420Sigor@sysoev.ru ssize_t n;
5430Sigor@sysoev.ru nxt_int_t ret;
5440Sigor@sysoev.ru nxt_gnutls_conn_t *ssltls;
5450Sigor@sysoev.ru
5460Sigor@sysoev.ru ssltls = c->u.ssltls;
5470Sigor@sysoev.ru
5480Sigor@sysoev.ru n = gnutls_record_send(ssltls->session, buf, size);
5490Sigor@sysoev.ru
5500Sigor@sysoev.ru nxt_log_debug(c->socket.log, "gnutls_record_send(%d, %p, %uz): %z",
5510Sigor@sysoev.ru c->socket.fd, buf, size, n);
5520Sigor@sysoev.ru
5530Sigor@sysoev.ru if (n > 0) {
5540Sigor@sysoev.ru return n;
5550Sigor@sysoev.ru }
5560Sigor@sysoev.ru
5570Sigor@sysoev.ru ret = nxt_gnutls_conn_test_error(nxt_thread(), c, n,
5580Sigor@sysoev.ru nxt_event_conn_io_write);
5590Sigor@sysoev.ru
5600Sigor@sysoev.ru if (nxt_slow_path(ret == NXT_ERROR)) {
5610Sigor@sysoev.ru nxt_gnutls_conn_log_error(c, n,
5620Sigor@sysoev.ru "gnutls_record_send(%d, %p, %uz): failed",
5630Sigor@sysoev.ru c->socket.fd, buf, size);
5640Sigor@sysoev.ru }
5650Sigor@sysoev.ru
5660Sigor@sysoev.ru return ret;
5670Sigor@sysoev.ru }
5680Sigor@sysoev.ru
5690Sigor@sysoev.ru
5700Sigor@sysoev.ru static void
nxt_gnutls_conn_io_shutdown(nxt_thread_t * thr,void * obj,void * data)5710Sigor@sysoev.ru nxt_gnutls_conn_io_shutdown(nxt_thread_t *thr, void *obj, void *data)
5720Sigor@sysoev.ru {
5730Sigor@sysoev.ru int err;
5740Sigor@sysoev.ru nxt_int_t ret;
5750Sigor@sysoev.ru nxt_event_conn_t *c;
5760Sigor@sysoev.ru nxt_gnutls_conn_t *ssltls;
5770Sigor@sysoev.ru nxt_work_handler_t handler;
5780Sigor@sysoev.ru gnutls_close_request_t how;
5790Sigor@sysoev.ru
5800Sigor@sysoev.ru c = obj;
5810Sigor@sysoev.ru ssltls = c->u.ssltls;
5820Sigor@sysoev.ru
5830Sigor@sysoev.ru if (ssltls->session == NULL || ssltls->no_shutdown) {
5840Sigor@sysoev.ru handler = c->write_state->close_handler;
5850Sigor@sysoev.ru goto done;
5860Sigor@sysoev.ru }
5870Sigor@sysoev.ru
5880Sigor@sysoev.ru nxt_log_debug(c->socket.log, "gnutls conn shutdown");
5890Sigor@sysoev.ru
5900Sigor@sysoev.ru if (c->socket.timedout || c->socket.error != 0) {
5910Sigor@sysoev.ru how = GNUTLS_SHUT_WR;
5920Sigor@sysoev.ru
5930Sigor@sysoev.ru } else if (c->socket.closed) {
5940Sigor@sysoev.ru how = GNUTLS_SHUT_RDWR;
5950Sigor@sysoev.ru
5960Sigor@sysoev.ru } else {
5970Sigor@sysoev.ru how = GNUTLS_SHUT_RDWR;
5980Sigor@sysoev.ru }
5990Sigor@sysoev.ru
6000Sigor@sysoev.ru err = gnutls_bye(ssltls->session, how);
6010Sigor@sysoev.ru
6020Sigor@sysoev.ru nxt_log_debug(c->socket.log, "gnutls_bye(%d, %d): %d",
6030Sigor@sysoev.ru c->socket.fd, how, err);
6040Sigor@sysoev.ru
6050Sigor@sysoev.ru if (err == GNUTLS_E_SUCCESS) {
6060Sigor@sysoev.ru handler = c->write_state->close_handler;
6070Sigor@sysoev.ru
6080Sigor@sysoev.ru } else {
6090Sigor@sysoev.ru ret = nxt_gnutls_conn_test_error(thr, c, err,
6100Sigor@sysoev.ru nxt_gnutls_conn_io_shutdown);
6110Sigor@sysoev.ru
6120Sigor@sysoev.ru if (ret != NXT_ERROR) { /* ret == NXT_AGAIN */
6130Sigor@sysoev.ru c->socket.error_handler = c->read_state->error_handler;
6140Sigor@sysoev.ru nxt_event_timer_add(thr->engine, &c->read_timer, 5000);
6150Sigor@sysoev.ru return;
6160Sigor@sysoev.ru }
6170Sigor@sysoev.ru
6180Sigor@sysoev.ru nxt_gnutls_conn_log_error(c, err, "gnutls_bye(%d) failed",
6190Sigor@sysoev.ru c->socket.fd);
6200Sigor@sysoev.ru
6210Sigor@sysoev.ru handler = c->write_state->error_handler;
6220Sigor@sysoev.ru }
6230Sigor@sysoev.ru
6240Sigor@sysoev.ru done:
6250Sigor@sysoev.ru
6260Sigor@sysoev.ru nxt_event_conn_io_handle(thr, c->write_work_queue, handler, c, data);
6270Sigor@sysoev.ru }
6280Sigor@sysoev.ru
6290Sigor@sysoev.ru
6300Sigor@sysoev.ru 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)6310Sigor@sysoev.ru nxt_gnutls_conn_test_error(nxt_thread_t *thr, nxt_event_conn_t *c, ssize_t err,
6320Sigor@sysoev.ru nxt_work_handler_t handler)
6330Sigor@sysoev.ru {
6340Sigor@sysoev.ru int ret;
6350Sigor@sysoev.ru nxt_gnutls_conn_t *ssltls;
6360Sigor@sysoev.ru
6370Sigor@sysoev.ru switch (err) {
6380Sigor@sysoev.ru
6390Sigor@sysoev.ru case GNUTLS_E_REHANDSHAKE:
6400Sigor@sysoev.ru case GNUTLS_E_AGAIN:
6410Sigor@sysoev.ru ssltls = c->u.ssltls;
6420Sigor@sysoev.ru ret = gnutls_record_get_direction(ssltls->session);
6430Sigor@sysoev.ru
6440Sigor@sysoev.ru nxt_log_debug(thr->log, "gnutls_record_get_direction(): %d", ret);
6450Sigor@sysoev.ru
6460Sigor@sysoev.ru if (ret == 0) {
6470Sigor@sysoev.ru /* A read direction. */
6480Sigor@sysoev.ru
6490Sigor@sysoev.ru nxt_event_fd_block_write(thr->engine, &c->socket);
6500Sigor@sysoev.ru
6510Sigor@sysoev.ru c->socket.read_ready = 0;
6520Sigor@sysoev.ru c->socket.read_handler = handler;
6530Sigor@sysoev.ru
6540Sigor@sysoev.ru if (nxt_event_fd_is_disabled(c->socket.read)) {
6550Sigor@sysoev.ru nxt_event_fd_enable_read(thr->engine, &c->socket);
6560Sigor@sysoev.ru }
6570Sigor@sysoev.ru
6580Sigor@sysoev.ru } else {
6590Sigor@sysoev.ru /* A write direction. */
6600Sigor@sysoev.ru
6610Sigor@sysoev.ru nxt_event_fd_block_read(thr->engine, &c->socket);
6620Sigor@sysoev.ru
6630Sigor@sysoev.ru c->socket.write_ready = 0;
6640Sigor@sysoev.ru c->socket.write_handler = handler;
6650Sigor@sysoev.ru
6660Sigor@sysoev.ru if (nxt_event_fd_is_disabled(c->socket.write)) {
6670Sigor@sysoev.ru nxt_event_fd_enable_write(thr->engine, &c->socket);
6680Sigor@sysoev.ru }
6690Sigor@sysoev.ru }
6700Sigor@sysoev.ru
6710Sigor@sysoev.ru return NXT_AGAIN;
6720Sigor@sysoev.ru
6730Sigor@sysoev.ru default:
6740Sigor@sysoev.ru c->socket.error = 1000; /* Nonexistent errno code. */
6750Sigor@sysoev.ru return NXT_ERROR;
6760Sigor@sysoev.ru }
6770Sigor@sysoev.ru }
6780Sigor@sysoev.ru
6790Sigor@sysoev.ru
6800Sigor@sysoev.ru static void
nxt_gnutls_conn_log_error(nxt_event_conn_t * c,ssize_t err,const char * fmt,...)6810Sigor@sysoev.ru nxt_gnutls_conn_log_error(nxt_event_conn_t *c, ssize_t err,
6820Sigor@sysoev.ru const char *fmt, ...)
6830Sigor@sysoev.ru {
6840Sigor@sysoev.ru va_list args;
6850Sigor@sysoev.ru nxt_uint_t level;
6860Sigor@sysoev.ru u_char *p, msg[NXT_MAX_ERROR_STR];
6870Sigor@sysoev.ru
6880Sigor@sysoev.ru level = nxt_gnutls_log_error_level(c, err);
6890Sigor@sysoev.ru
6900Sigor@sysoev.ru if (nxt_log_level_enough(c->socket.log, level)) {
6910Sigor@sysoev.ru
6920Sigor@sysoev.ru va_start(args, fmt);
6930Sigor@sysoev.ru p = nxt_vsprintf(msg, msg + sizeof(msg), fmt, args);
6940Sigor@sysoev.ru va_end(args);
6950Sigor@sysoev.ru
6960Sigor@sysoev.ru nxt_log_error(level, c->socket.log, "%*s (%d: %s)",
6970Sigor@sysoev.ru p - msg, msg, err, gnutls_strerror(err));
6980Sigor@sysoev.ru }
6990Sigor@sysoev.ru }
7000Sigor@sysoev.ru
7010Sigor@sysoev.ru
7020Sigor@sysoev.ru static nxt_uint_t
nxt_gnutls_log_error_level(nxt_event_conn_t * c,ssize_t err)7030Sigor@sysoev.ru nxt_gnutls_log_error_level(nxt_event_conn_t *c, ssize_t err)
7040Sigor@sysoev.ru {
7050Sigor@sysoev.ru nxt_gnutls_conn_t *ssltls;
7060Sigor@sysoev.ru
7070Sigor@sysoev.ru switch (err) {
7080Sigor@sysoev.ru
7090Sigor@sysoev.ru case GNUTLS_E_UNKNOWN_CIPHER_SUITE: /* -21 */
7100Sigor@sysoev.ru
711*2078Salx.manpages@gmail.com /* Disable gnutls_bye(), because it returns GNUTLS_E_INTERNAL_ERROR. */
7120Sigor@sysoev.ru ssltls = c->u.ssltls;
7130Sigor@sysoev.ru ssltls->no_shutdown = 1;
7140Sigor@sysoev.ru
715*2078Salx.manpages@gmail.com /* Fall through. */
7160Sigor@sysoev.ru
7170Sigor@sysoev.ru case GNUTLS_E_UNEXPECTED_PACKET_LENGTH: /* -9 */
7180Sigor@sysoev.ru c->socket.error = 1000; /* Nonexistent errno code. */
7190Sigor@sysoev.ru break;
7200Sigor@sysoev.ru
7210Sigor@sysoev.ru default:
722564Svbart@nginx.com return NXT_LOG_ALERT;
7230Sigor@sysoev.ru }
7240Sigor@sysoev.ru
7250Sigor@sysoev.ru return NXT_LOG_INFO;
7260Sigor@sysoev.ru }
7270Sigor@sysoev.ru
7280Sigor@sysoev.ru
7290Sigor@sysoev.ru static void
nxt_gnutls_log_error(nxt_uint_t level,nxt_log_t * log,int err,const char * fmt,...)7300Sigor@sysoev.ru nxt_gnutls_log_error(nxt_uint_t level, nxt_log_t *log, int err,
7310Sigor@sysoev.ru const char *fmt, ...)
7320Sigor@sysoev.ru {
7330Sigor@sysoev.ru va_list args;
7340Sigor@sysoev.ru u_char *p, msg[NXT_MAX_ERROR_STR];
7350Sigor@sysoev.ru
7360Sigor@sysoev.ru va_start(args, fmt);
7370Sigor@sysoev.ru p = nxt_vsprintf(msg, msg + sizeof(msg), fmt, args);
7380Sigor@sysoev.ru va_end(args);
7390Sigor@sysoev.ru
7400Sigor@sysoev.ru nxt_log_error(level, log, "%*s (%d: %s)",
7410Sigor@sysoev.ru p - msg, msg, err, gnutls_strerror(err));
7420Sigor@sysoev.ru }
743