xref: /unit/src/nxt_gnutls.c (revision 2078)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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