xref: /unit/src/nxt_gnutls.c (revision 0)
1*0Sigor@sysoev.ru 
2*0Sigor@sysoev.ru /*
3*0Sigor@sysoev.ru  * Copyright (C) Igor Sysoev
4*0Sigor@sysoev.ru  * Copyright (C) NGINX, Inc.
5*0Sigor@sysoev.ru  */
6*0Sigor@sysoev.ru 
7*0Sigor@sysoev.ru #include <nxt_main.h>
8*0Sigor@sysoev.ru #include <gnutls/gnutls.h>
9*0Sigor@sysoev.ru 
10*0Sigor@sysoev.ru 
11*0Sigor@sysoev.ru typedef struct {
12*0Sigor@sysoev.ru     gnutls_session_t                  session;
13*0Sigor@sysoev.ru 
14*0Sigor@sysoev.ru     uint8_t                           times;        /* 2 bits */
15*0Sigor@sysoev.ru     uint8_t                           no_shutdown;  /* 1 bit */
16*0Sigor@sysoev.ru 
17*0Sigor@sysoev.ru     nxt_buf_mem_t                     buffer;
18*0Sigor@sysoev.ru } nxt_gnutls_conn_t;
19*0Sigor@sysoev.ru 
20*0Sigor@sysoev.ru 
21*0Sigor@sysoev.ru typedef struct {
22*0Sigor@sysoev.ru     gnutls_priority_t                 ciphers;
23*0Sigor@sysoev.ru     gnutls_certificate_credentials_t  certificate;
24*0Sigor@sysoev.ru } nxt_gnutls_ctx_t;
25*0Sigor@sysoev.ru 
26*0Sigor@sysoev.ru 
27*0Sigor@sysoev.ru 
28*0Sigor@sysoev.ru #if (NXT_HAVE_GNUTLS_SET_TIME)
29*0Sigor@sysoev.ru time_t nxt_gnutls_time(time_t *tp);
30*0Sigor@sysoev.ru #endif
31*0Sigor@sysoev.ru static nxt_int_t nxt_gnutls_server_init(nxt_ssltls_conf_t *conf);
32*0Sigor@sysoev.ru static nxt_int_t nxt_gnutls_set_ciphers(nxt_ssltls_conf_t *conf);
33*0Sigor@sysoev.ru 
34*0Sigor@sysoev.ru static void nxt_gnutls_conn_init(nxt_thread_t *thr, nxt_ssltls_conf_t *conf,
35*0Sigor@sysoev.ru     nxt_event_conn_t *c);
36*0Sigor@sysoev.ru static void nxt_gnutls_session_cleanup(void *data);
37*0Sigor@sysoev.ru static ssize_t nxt_gnutls_pull(gnutls_transport_ptr_t data, void *buf,
38*0Sigor@sysoev.ru     size_t size);
39*0Sigor@sysoev.ru static ssize_t nxt_gnutls_push(gnutls_transport_ptr_t data, const void *buf,
40*0Sigor@sysoev.ru     size_t size);
41*0Sigor@sysoev.ru #if (NXT_HAVE_GNUTLS_VEC_PUSH)
42*0Sigor@sysoev.ru static ssize_t nxt_gnutls_vec_push(gnutls_transport_ptr_t data,
43*0Sigor@sysoev.ru     const giovec_t *iov, int iovcnt);
44*0Sigor@sysoev.ru #endif
45*0Sigor@sysoev.ru static void nxt_gnutls_conn_handshake(nxt_thread_t *thr, void *obj, void *data);
46*0Sigor@sysoev.ru static void nxt_gnutls_conn_io_read(nxt_thread_t *thr, void *obj, void *data);
47*0Sigor@sysoev.ru static ssize_t nxt_gnutls_conn_io_write_chunk(nxt_thread_t *thr,
48*0Sigor@sysoev.ru     nxt_event_conn_t *c, nxt_buf_t *b, size_t limit);
49*0Sigor@sysoev.ru static ssize_t nxt_gnutls_conn_io_send(nxt_event_conn_t *c, void *buf,
50*0Sigor@sysoev.ru     size_t size);
51*0Sigor@sysoev.ru static void nxt_gnutls_conn_io_shutdown(nxt_thread_t *thr, void *obj,
52*0Sigor@sysoev.ru     void *data);
53*0Sigor@sysoev.ru static nxt_int_t nxt_gnutls_conn_test_error(nxt_thread_t *thr,
54*0Sigor@sysoev.ru     nxt_event_conn_t *c, ssize_t err, nxt_work_handler_t handler);
55*0Sigor@sysoev.ru static void nxt_cdecl nxt_gnutls_conn_log_error(nxt_event_conn_t *c,
56*0Sigor@sysoev.ru     ssize_t err, const char *fmt, ...);
57*0Sigor@sysoev.ru static nxt_uint_t nxt_gnutls_log_error_level(nxt_event_conn_t *c, ssize_t err);
58*0Sigor@sysoev.ru static void nxt_cdecl nxt_gnutls_log_error(nxt_uint_t level, nxt_log_t *log,
59*0Sigor@sysoev.ru     int err, const char *fmt, ...);
60*0Sigor@sysoev.ru 
61*0Sigor@sysoev.ru 
62*0Sigor@sysoev.ru const nxt_ssltls_lib_t  nxt_gnutls_lib = {
63*0Sigor@sysoev.ru     nxt_gnutls_server_init,
64*0Sigor@sysoev.ru     NULL,
65*0Sigor@sysoev.ru };
66*0Sigor@sysoev.ru 
67*0Sigor@sysoev.ru 
68*0Sigor@sysoev.ru static nxt_event_conn_io_t  nxt_gnutls_event_conn_io = {
69*0Sigor@sysoev.ru     NULL,
70*0Sigor@sysoev.ru     NULL,
71*0Sigor@sysoev.ru 
72*0Sigor@sysoev.ru     nxt_gnutls_conn_io_read,
73*0Sigor@sysoev.ru     NULL,
74*0Sigor@sysoev.ru     NULL,
75*0Sigor@sysoev.ru 
76*0Sigor@sysoev.ru     nxt_event_conn_io_write,
77*0Sigor@sysoev.ru     nxt_gnutls_conn_io_write_chunk,
78*0Sigor@sysoev.ru     NULL,
79*0Sigor@sysoev.ru     NULL,
80*0Sigor@sysoev.ru     nxt_gnutls_conn_io_send,
81*0Sigor@sysoev.ru 
82*0Sigor@sysoev.ru     nxt_gnutls_conn_io_shutdown,
83*0Sigor@sysoev.ru };
84*0Sigor@sysoev.ru 
85*0Sigor@sysoev.ru 
86*0Sigor@sysoev.ru static nxt_int_t
87*0Sigor@sysoev.ru nxt_gnutls_start(void)
88*0Sigor@sysoev.ru {
89*0Sigor@sysoev.ru     int                ret;
90*0Sigor@sysoev.ru     static nxt_bool_t  started;
91*0Sigor@sysoev.ru 
92*0Sigor@sysoev.ru     if (nxt_fast_path(started)) {
93*0Sigor@sysoev.ru         return NXT_OK;
94*0Sigor@sysoev.ru     }
95*0Sigor@sysoev.ru 
96*0Sigor@sysoev.ru     started = 1;
97*0Sigor@sysoev.ru 
98*0Sigor@sysoev.ru     /* TODO: gnutls_global_deinit */
99*0Sigor@sysoev.ru 
100*0Sigor@sysoev.ru     ret = gnutls_global_init();
101*0Sigor@sysoev.ru     if (ret != GNUTLS_E_SUCCESS) {
102*0Sigor@sysoev.ru         nxt_gnutls_log_error(NXT_LOG_CRIT, nxt_thread_log(), ret,
103*0Sigor@sysoev.ru                              "gnutls_global_init() failed");
104*0Sigor@sysoev.ru         return NXT_ERROR;
105*0Sigor@sysoev.ru     }
106*0Sigor@sysoev.ru 
107*0Sigor@sysoev.ru     nxt_thread_log_error(NXT_LOG_INFO, "GnuTLS version: %s",
108*0Sigor@sysoev.ru                          gnutls_check_version(NULL));
109*0Sigor@sysoev.ru 
110*0Sigor@sysoev.ru #if (NXT_HAVE_GNUTLS_SET_TIME)
111*0Sigor@sysoev.ru     gnutls_global_set_time_function(nxt_gnutls_time);
112*0Sigor@sysoev.ru #endif
113*0Sigor@sysoev.ru 
114*0Sigor@sysoev.ru     return NXT_OK;
115*0Sigor@sysoev.ru }
116*0Sigor@sysoev.ru 
117*0Sigor@sysoev.ru 
118*0Sigor@sysoev.ru #if (NXT_HAVE_GNUTLS_SET_TIME)
119*0Sigor@sysoev.ru 
120*0Sigor@sysoev.ru /* GnuTLS 2.12.0 */
121*0Sigor@sysoev.ru 
122*0Sigor@sysoev.ru time_t
123*0Sigor@sysoev.ru nxt_gnutls_time(time_t *tp)
124*0Sigor@sysoev.ru {
125*0Sigor@sysoev.ru     time_t        t;
126*0Sigor@sysoev.ru     nxt_thread_t  *thr;
127*0Sigor@sysoev.ru 
128*0Sigor@sysoev.ru     thr = nxt_thread();
129*0Sigor@sysoev.ru     nxt_log_debug(thr->log, "gnutls time");
130*0Sigor@sysoev.ru 
131*0Sigor@sysoev.ru     t = (time_t) nxt_thread_time(thr);
132*0Sigor@sysoev.ru 
133*0Sigor@sysoev.ru     if (tp != NULL) {
134*0Sigor@sysoev.ru         *tp = t;
135*0Sigor@sysoev.ru     }
136*0Sigor@sysoev.ru 
137*0Sigor@sysoev.ru     return t;
138*0Sigor@sysoev.ru }
139*0Sigor@sysoev.ru 
140*0Sigor@sysoev.ru #endif
141*0Sigor@sysoev.ru 
142*0Sigor@sysoev.ru 
143*0Sigor@sysoev.ru static nxt_int_t
144*0Sigor@sysoev.ru nxt_gnutls_server_init(nxt_ssltls_conf_t *conf)
145*0Sigor@sysoev.ru {
146*0Sigor@sysoev.ru     int                ret;
147*0Sigor@sysoev.ru     char               *certificate, *key, *ca_certificate;
148*0Sigor@sysoev.ru     nxt_thread_t       *thr;
149*0Sigor@sysoev.ru     nxt_gnutls_ctx_t   *ctx;
150*0Sigor@sysoev.ru 
151*0Sigor@sysoev.ru     if (nxt_slow_path(nxt_gnutls_start() != NXT_OK)) {
152*0Sigor@sysoev.ru         return NXT_ERROR;
153*0Sigor@sysoev.ru     }
154*0Sigor@sysoev.ru 
155*0Sigor@sysoev.ru     /* TODO: mem_pool, cleanup: gnutls_certificate_free_credentials,
156*0Sigor@sysoev.ru              gnutls_priority_deinit */
157*0Sigor@sysoev.ru 
158*0Sigor@sysoev.ru     ctx = nxt_zalloc(sizeof(nxt_gnutls_ctx_t));
159*0Sigor@sysoev.ru     if (ctx == NULL) {
160*0Sigor@sysoev.ru         return NXT_ERROR;
161*0Sigor@sysoev.ru     }
162*0Sigor@sysoev.ru 
163*0Sigor@sysoev.ru     conf->ctx = ctx;
164*0Sigor@sysoev.ru     conf->conn_init = nxt_gnutls_conn_init;
165*0Sigor@sysoev.ru 
166*0Sigor@sysoev.ru     thr = nxt_thread();
167*0Sigor@sysoev.ru 
168*0Sigor@sysoev.ru     ret = gnutls_certificate_allocate_credentials(&ctx->certificate);
169*0Sigor@sysoev.ru     if (ret != GNUTLS_E_SUCCESS) {
170*0Sigor@sysoev.ru         nxt_gnutls_log_error(NXT_LOG_CRIT, thr->log, ret,
171*0Sigor@sysoev.ru                 "gnutls_certificate_allocate_credentials() failed");
172*0Sigor@sysoev.ru         return NXT_ERROR;
173*0Sigor@sysoev.ru     }
174*0Sigor@sysoev.ru 
175*0Sigor@sysoev.ru     certificate = conf->certificate;
176*0Sigor@sysoev.ru     key = conf->certificate_key;
177*0Sigor@sysoev.ru 
178*0Sigor@sysoev.ru     ret = gnutls_certificate_set_x509_key_file(ctx->certificate, certificate,
179*0Sigor@sysoev.ru                                                key, GNUTLS_X509_FMT_PEM);
180*0Sigor@sysoev.ru     if (ret != GNUTLS_E_SUCCESS) {
181*0Sigor@sysoev.ru         nxt_gnutls_log_error(NXT_LOG_CRIT, thr->log, ret,
182*0Sigor@sysoev.ru                 "gnutls_certificate_set_x509_key_file(\"%s\", \"%s\") failed",
183*0Sigor@sysoev.ru                 certificate, key);
184*0Sigor@sysoev.ru         goto certificate_fail;
185*0Sigor@sysoev.ru     }
186*0Sigor@sysoev.ru 
187*0Sigor@sysoev.ru     if (nxt_gnutls_set_ciphers(conf) != NXT_OK) {
188*0Sigor@sysoev.ru         goto ciphers_fail;
189*0Sigor@sysoev.ru     }
190*0Sigor@sysoev.ru 
191*0Sigor@sysoev.ru     if (conf->ca_certificate != NULL) {
192*0Sigor@sysoev.ru         ca_certificate = conf->ca_certificate;
193*0Sigor@sysoev.ru 
194*0Sigor@sysoev.ru         ret = gnutls_certificate_set_x509_trust_file(ctx->certificate,
195*0Sigor@sysoev.ru                                                      ca_certificate,
196*0Sigor@sysoev.ru                                                      GNUTLS_X509_FMT_PEM);
197*0Sigor@sysoev.ru         if (ret < 0) {
198*0Sigor@sysoev.ru             nxt_gnutls_log_error(NXT_LOG_CRIT, thr->log, ret,
199*0Sigor@sysoev.ru                 "gnutls_certificate_set_x509_trust_file(\"%s\") failed",
200*0Sigor@sysoev.ru                 ca_certificate);
201*0Sigor@sysoev.ru             goto ca_certificate_fail;
202*0Sigor@sysoev.ru         }
203*0Sigor@sysoev.ru     }
204*0Sigor@sysoev.ru 
205*0Sigor@sysoev.ru     return NXT_OK;
206*0Sigor@sysoev.ru 
207*0Sigor@sysoev.ru ca_certificate_fail:
208*0Sigor@sysoev.ru 
209*0Sigor@sysoev.ru     gnutls_priority_deinit(ctx->ciphers);
210*0Sigor@sysoev.ru 
211*0Sigor@sysoev.ru ciphers_fail:
212*0Sigor@sysoev.ru 
213*0Sigor@sysoev.ru certificate_fail:
214*0Sigor@sysoev.ru 
215*0Sigor@sysoev.ru     gnutls_certificate_free_credentials(ctx->certificate);
216*0Sigor@sysoev.ru 
217*0Sigor@sysoev.ru     return NXT_ERROR;
218*0Sigor@sysoev.ru }
219*0Sigor@sysoev.ru 
220*0Sigor@sysoev.ru 
221*0Sigor@sysoev.ru static nxt_int_t
222*0Sigor@sysoev.ru nxt_gnutls_set_ciphers(nxt_ssltls_conf_t *conf)
223*0Sigor@sysoev.ru {
224*0Sigor@sysoev.ru     int                ret;
225*0Sigor@sysoev.ru     const char         *ciphers;
226*0Sigor@sysoev.ru     const char         *err;
227*0Sigor@sysoev.ru     nxt_gnutls_ctx_t   *ctx;
228*0Sigor@sysoev.ru 
229*0Sigor@sysoev.ru     ciphers = (conf->ciphers != NULL) ? conf->ciphers : "NORMAL:!COMP-DEFLATE";
230*0Sigor@sysoev.ru     ctx = conf->ctx;
231*0Sigor@sysoev.ru 
232*0Sigor@sysoev.ru     ret = gnutls_priority_init(&ctx->ciphers, ciphers, &err);
233*0Sigor@sysoev.ru 
234*0Sigor@sysoev.ru     switch (ret) {
235*0Sigor@sysoev.ru 
236*0Sigor@sysoev.ru     case GNUTLS_E_SUCCESS:
237*0Sigor@sysoev.ru         return NXT_OK;
238*0Sigor@sysoev.ru 
239*0Sigor@sysoev.ru     case GNUTLS_E_INVALID_REQUEST:
240*0Sigor@sysoev.ru         nxt_gnutls_log_error(NXT_LOG_CRIT, nxt_thread_log(), ret,
241*0Sigor@sysoev.ru                              "gnutls_priority_init(\"%s\") failed at \"%s\"",
242*0Sigor@sysoev.ru                              ciphers, err);
243*0Sigor@sysoev.ru         return NXT_ERROR;
244*0Sigor@sysoev.ru 
245*0Sigor@sysoev.ru     default:
246*0Sigor@sysoev.ru         nxt_gnutls_log_error(NXT_LOG_CRIT, nxt_thread_log(), ret,
247*0Sigor@sysoev.ru                              "gnutls_priority_init() failed");
248*0Sigor@sysoev.ru         return NXT_ERROR;
249*0Sigor@sysoev.ru     }
250*0Sigor@sysoev.ru }
251*0Sigor@sysoev.ru 
252*0Sigor@sysoev.ru 
253*0Sigor@sysoev.ru static void
254*0Sigor@sysoev.ru nxt_gnutls_conn_init(nxt_thread_t *thr, nxt_ssltls_conf_t *conf,
255*0Sigor@sysoev.ru     nxt_event_conn_t *c)
256*0Sigor@sysoev.ru {
257*0Sigor@sysoev.ru     int                     ret;
258*0Sigor@sysoev.ru     gnutls_session_t        sess;
259*0Sigor@sysoev.ru     nxt_gnutls_ctx_t        *ctx;
260*0Sigor@sysoev.ru     nxt_gnutls_conn_t       *ssltls;
261*0Sigor@sysoev.ru     nxt_mem_pool_cleanup_t  *mpcl;
262*0Sigor@sysoev.ru 
263*0Sigor@sysoev.ru     nxt_log_debug(c->socket.log, "gnutls conn init");
264*0Sigor@sysoev.ru 
265*0Sigor@sysoev.ru     ssltls = nxt_mem_zalloc(c->mem_pool, sizeof(nxt_gnutls_conn_t));
266*0Sigor@sysoev.ru     if (ssltls == NULL) {
267*0Sigor@sysoev.ru         goto fail;
268*0Sigor@sysoev.ru     }
269*0Sigor@sysoev.ru 
270*0Sigor@sysoev.ru     c->u.ssltls = ssltls;
271*0Sigor@sysoev.ru     nxt_buf_mem_set_size(&ssltls->buffer, conf->buffer_size);
272*0Sigor@sysoev.ru 
273*0Sigor@sysoev.ru     mpcl = nxt_mem_pool_cleanup(c->mem_pool, 0);
274*0Sigor@sysoev.ru     if (mpcl == NULL) {
275*0Sigor@sysoev.ru         goto fail;
276*0Sigor@sysoev.ru     }
277*0Sigor@sysoev.ru 
278*0Sigor@sysoev.ru     ret = gnutls_init(&ssltls->session, GNUTLS_SERVER);
279*0Sigor@sysoev.ru     if (ret != GNUTLS_E_SUCCESS) {
280*0Sigor@sysoev.ru         nxt_gnutls_log_error(NXT_LOG_CRIT, c->socket.log, ret,
281*0Sigor@sysoev.ru                              "gnutls_init() failed");
282*0Sigor@sysoev.ru         goto fail;
283*0Sigor@sysoev.ru     }
284*0Sigor@sysoev.ru 
285*0Sigor@sysoev.ru     sess = ssltls->session;
286*0Sigor@sysoev.ru     mpcl->handler = nxt_gnutls_session_cleanup;
287*0Sigor@sysoev.ru     mpcl->data = ssltls;
288*0Sigor@sysoev.ru 
289*0Sigor@sysoev.ru     ctx = conf->ctx;
290*0Sigor@sysoev.ru 
291*0Sigor@sysoev.ru     ret = gnutls_priority_set(sess, ctx->ciphers);
292*0Sigor@sysoev.ru     if (ret != GNUTLS_E_SUCCESS) {
293*0Sigor@sysoev.ru         nxt_gnutls_log_error(NXT_LOG_CRIT, c->socket.log, ret,
294*0Sigor@sysoev.ru                              "gnutls_priority_set() failed");
295*0Sigor@sysoev.ru         goto fail;
296*0Sigor@sysoev.ru     }
297*0Sigor@sysoev.ru 
298*0Sigor@sysoev.ru     /*
299*0Sigor@sysoev.ru      * Disable TLS random padding of records in CBC ciphers,
300*0Sigor@sysoev.ru      * which may be up to 255 bytes.
301*0Sigor@sysoev.ru      */
302*0Sigor@sysoev.ru     gnutls_record_disable_padding(sess);
303*0Sigor@sysoev.ru 
304*0Sigor@sysoev.ru     ret = gnutls_credentials_set(sess, GNUTLS_CRD_CERTIFICATE,
305*0Sigor@sysoev.ru                                  ctx->certificate);
306*0Sigor@sysoev.ru     if (ret != GNUTLS_E_SUCCESS) {
307*0Sigor@sysoev.ru         nxt_gnutls_log_error(NXT_LOG_CRIT, c->socket.log, ret,
308*0Sigor@sysoev.ru                              "gnutls_credentials_set() failed");
309*0Sigor@sysoev.ru         goto fail;
310*0Sigor@sysoev.ru     }
311*0Sigor@sysoev.ru 
312*0Sigor@sysoev.ru     if (conf->ca_certificate != NULL) {
313*0Sigor@sysoev.ru         gnutls_certificate_server_set_request(sess, GNUTLS_CERT_REQUEST);
314*0Sigor@sysoev.ru     }
315*0Sigor@sysoev.ru 
316*0Sigor@sysoev.ru     gnutls_transport_set_ptr(sess, (gnutls_transport_ptr_t) c);
317*0Sigor@sysoev.ru     gnutls_transport_set_pull_function(sess, nxt_gnutls_pull);
318*0Sigor@sysoev.ru     gnutls_transport_set_push_function(sess, nxt_gnutls_push);
319*0Sigor@sysoev.ru #if (NXT_HAVE_GNUTLS_VEC_PUSH)
320*0Sigor@sysoev.ru     gnutls_transport_set_vec_push_function(sess, nxt_gnutls_vec_push);
321*0Sigor@sysoev.ru #endif
322*0Sigor@sysoev.ru 
323*0Sigor@sysoev.ru     c->io = &nxt_gnutls_event_conn_io;
324*0Sigor@sysoev.ru     c->sendfile = NXT_CONN_SENDFILE_OFF;
325*0Sigor@sysoev.ru 
326*0Sigor@sysoev.ru     nxt_gnutls_conn_handshake(thr, c, c->socket.data);
327*0Sigor@sysoev.ru     return;
328*0Sigor@sysoev.ru 
329*0Sigor@sysoev.ru fail:
330*0Sigor@sysoev.ru 
331*0Sigor@sysoev.ru     nxt_event_conn_io_handle(thr, c->read_work_queue,
332*0Sigor@sysoev.ru                              c->read_state->error_handler, c, c->socket.data);
333*0Sigor@sysoev.ru }
334*0Sigor@sysoev.ru 
335*0Sigor@sysoev.ru 
336*0Sigor@sysoev.ru static void
337*0Sigor@sysoev.ru nxt_gnutls_session_cleanup(void *data)
338*0Sigor@sysoev.ru {
339*0Sigor@sysoev.ru     nxt_gnutls_conn_t  *ssltls;
340*0Sigor@sysoev.ru 
341*0Sigor@sysoev.ru     ssltls = data;
342*0Sigor@sysoev.ru 
343*0Sigor@sysoev.ru     nxt_thread_log_debug("gnutls session cleanup");
344*0Sigor@sysoev.ru 
345*0Sigor@sysoev.ru     nxt_free(ssltls->buffer.start);
346*0Sigor@sysoev.ru 
347*0Sigor@sysoev.ru     gnutls_deinit(ssltls->session);
348*0Sigor@sysoev.ru }
349*0Sigor@sysoev.ru 
350*0Sigor@sysoev.ru 
351*0Sigor@sysoev.ru static ssize_t
352*0Sigor@sysoev.ru nxt_gnutls_pull(gnutls_transport_ptr_t data, void *buf, size_t size)
353*0Sigor@sysoev.ru {
354*0Sigor@sysoev.ru     ssize_t           n;
355*0Sigor@sysoev.ru     nxt_thread_t      *thr;
356*0Sigor@sysoev.ru     nxt_event_conn_t  *c;
357*0Sigor@sysoev.ru 
358*0Sigor@sysoev.ru     c = data;
359*0Sigor@sysoev.ru     thr = nxt_thread();
360*0Sigor@sysoev.ru 
361*0Sigor@sysoev.ru     n = thr->engine->event->io->recv(c, buf, size, 0);
362*0Sigor@sysoev.ru 
363*0Sigor@sysoev.ru     if (n == NXT_AGAIN) {
364*0Sigor@sysoev.ru         nxt_set_errno(NXT_EAGAIN);
365*0Sigor@sysoev.ru         return -1;
366*0Sigor@sysoev.ru     }
367*0Sigor@sysoev.ru 
368*0Sigor@sysoev.ru     return n;
369*0Sigor@sysoev.ru }
370*0Sigor@sysoev.ru 
371*0Sigor@sysoev.ru 
372*0Sigor@sysoev.ru static ssize_t
373*0Sigor@sysoev.ru nxt_gnutls_push(gnutls_transport_ptr_t data, const void *buf, size_t size)
374*0Sigor@sysoev.ru {
375*0Sigor@sysoev.ru     ssize_t           n;
376*0Sigor@sysoev.ru     nxt_thread_t      *thr;
377*0Sigor@sysoev.ru     nxt_event_conn_t  *c;
378*0Sigor@sysoev.ru 
379*0Sigor@sysoev.ru     c = data;
380*0Sigor@sysoev.ru     thr = nxt_thread();
381*0Sigor@sysoev.ru 
382*0Sigor@sysoev.ru     n = thr->engine->event->io->send(c, (u_char *) buf, size);
383*0Sigor@sysoev.ru 
384*0Sigor@sysoev.ru     if (n == NXT_AGAIN) {
385*0Sigor@sysoev.ru         nxt_set_errno(NXT_EAGAIN);
386*0Sigor@sysoev.ru         return -1;
387*0Sigor@sysoev.ru     }
388*0Sigor@sysoev.ru 
389*0Sigor@sysoev.ru     return n;
390*0Sigor@sysoev.ru }
391*0Sigor@sysoev.ru 
392*0Sigor@sysoev.ru 
393*0Sigor@sysoev.ru #if (NXT_HAVE_GNUTLS_VEC_PUSH)
394*0Sigor@sysoev.ru 
395*0Sigor@sysoev.ru /* GnuTLS 2.12.0 */
396*0Sigor@sysoev.ru 
397*0Sigor@sysoev.ru static ssize_t
398*0Sigor@sysoev.ru nxt_gnutls_vec_push(gnutls_transport_ptr_t data, const giovec_t *iov,
399*0Sigor@sysoev.ru     int iovcnt)
400*0Sigor@sysoev.ru {
401*0Sigor@sysoev.ru     ssize_t           n;
402*0Sigor@sysoev.ru     nxt_thread_t      *thr;
403*0Sigor@sysoev.ru     nxt_event_conn_t  *c;
404*0Sigor@sysoev.ru 
405*0Sigor@sysoev.ru     c = data;
406*0Sigor@sysoev.ru     thr = nxt_thread();
407*0Sigor@sysoev.ru 
408*0Sigor@sysoev.ru     /*
409*0Sigor@sysoev.ru      * This code assumes that giovec_t is the same as "struct iovec"
410*0Sigor@sysoev.ru      * and nxt_iobuf_t.  It is not true for Windows.
411*0Sigor@sysoev.ru      */
412*0Sigor@sysoev.ru     n = thr->engine->event->io->writev(c, (nxt_iobuf_t *) iov, iovcnt);
413*0Sigor@sysoev.ru 
414*0Sigor@sysoev.ru     if (n == NXT_AGAIN) {
415*0Sigor@sysoev.ru         nxt_set_errno(NXT_EAGAIN);
416*0Sigor@sysoev.ru         return -1;
417*0Sigor@sysoev.ru     }
418*0Sigor@sysoev.ru 
419*0Sigor@sysoev.ru     return n;
420*0Sigor@sysoev.ru }
421*0Sigor@sysoev.ru 
422*0Sigor@sysoev.ru #endif
423*0Sigor@sysoev.ru 
424*0Sigor@sysoev.ru 
425*0Sigor@sysoev.ru static void
426*0Sigor@sysoev.ru nxt_gnutls_conn_handshake(nxt_thread_t *thr, void *obj, void *data)
427*0Sigor@sysoev.ru {
428*0Sigor@sysoev.ru     int                err;
429*0Sigor@sysoev.ru     nxt_int_t          ret;
430*0Sigor@sysoev.ru     nxt_event_conn_t   *c;
431*0Sigor@sysoev.ru     nxt_gnutls_conn_t  *ssltls;
432*0Sigor@sysoev.ru 
433*0Sigor@sysoev.ru     c = obj;
434*0Sigor@sysoev.ru     ssltls = c->u.ssltls;
435*0Sigor@sysoev.ru 
436*0Sigor@sysoev.ru     nxt_log_debug(thr->log, "gnutls conn handshake: %d", ssltls->times);
437*0Sigor@sysoev.ru 
438*0Sigor@sysoev.ru     /* "ssltls->times == 1" is suitable to run gnutls_handshake() in job. */
439*0Sigor@sysoev.ru 
440*0Sigor@sysoev.ru     err = gnutls_handshake(ssltls->session);
441*0Sigor@sysoev.ru 
442*0Sigor@sysoev.ru     nxt_thread_time_debug_update(thr);
443*0Sigor@sysoev.ru 
444*0Sigor@sysoev.ru     nxt_log_debug(thr->log, "gnutls_handshake(): %d", err);
445*0Sigor@sysoev.ru 
446*0Sigor@sysoev.ru     if (err == GNUTLS_E_SUCCESS) {
447*0Sigor@sysoev.ru         nxt_gnutls_conn_io_read(thr, c, data);
448*0Sigor@sysoev.ru         return;
449*0Sigor@sysoev.ru     }
450*0Sigor@sysoev.ru 
451*0Sigor@sysoev.ru     ret = nxt_gnutls_conn_test_error(thr, c, err, nxt_gnutls_conn_handshake);
452*0Sigor@sysoev.ru 
453*0Sigor@sysoev.ru     if (ret == NXT_ERROR) {
454*0Sigor@sysoev.ru         nxt_gnutls_conn_log_error(c, err, "gnutls_handshake() failed");
455*0Sigor@sysoev.ru 
456*0Sigor@sysoev.ru         nxt_event_conn_io_handle(thr, c->read_work_queue,
457*0Sigor@sysoev.ru                                  c->read_state->error_handler, c, data);
458*0Sigor@sysoev.ru 
459*0Sigor@sysoev.ru     } else if (err == GNUTLS_E_AGAIN
460*0Sigor@sysoev.ru                && ssltls->times < 2
461*0Sigor@sysoev.ru                && gnutls_record_get_direction(ssltls->session) == 0)
462*0Sigor@sysoev.ru     {
463*0Sigor@sysoev.ru         ssltls->times++;
464*0Sigor@sysoev.ru     }
465*0Sigor@sysoev.ru }
466*0Sigor@sysoev.ru 
467*0Sigor@sysoev.ru 
468*0Sigor@sysoev.ru static void
469*0Sigor@sysoev.ru nxt_gnutls_conn_io_read(nxt_thread_t *thr, void *obj, void *data)
470*0Sigor@sysoev.ru {
471*0Sigor@sysoev.ru     ssize_t             n;
472*0Sigor@sysoev.ru     nxt_buf_t           *b;
473*0Sigor@sysoev.ru     nxt_int_t           ret;
474*0Sigor@sysoev.ru     nxt_event_conn_t    *c;
475*0Sigor@sysoev.ru     nxt_gnutls_conn_t   *ssltls;
476*0Sigor@sysoev.ru     nxt_work_handler_t  handler;
477*0Sigor@sysoev.ru 
478*0Sigor@sysoev.ru     c = obj;
479*0Sigor@sysoev.ru 
480*0Sigor@sysoev.ru     nxt_log_debug(thr->log, "gnutls conn read");
481*0Sigor@sysoev.ru 
482*0Sigor@sysoev.ru     handler = c->read_state->ready_handler;
483*0Sigor@sysoev.ru     b = c->read;
484*0Sigor@sysoev.ru 
485*0Sigor@sysoev.ru     /* b == NULL is used to test descriptor readiness. */
486*0Sigor@sysoev.ru 
487*0Sigor@sysoev.ru     if (b != NULL) {
488*0Sigor@sysoev.ru         ssltls = c->u.ssltls;
489*0Sigor@sysoev.ru 
490*0Sigor@sysoev.ru         n = gnutls_record_recv(ssltls->session, b->mem.free,
491*0Sigor@sysoev.ru                                b->mem.end - b->mem.free);
492*0Sigor@sysoev.ru 
493*0Sigor@sysoev.ru         nxt_log_debug(thr->log, "gnutls_record_recv(%d, %p, %uz): %z",
494*0Sigor@sysoev.ru                       c->socket.fd, b->mem.free, b->mem.end - b->mem.free, n);
495*0Sigor@sysoev.ru 
496*0Sigor@sysoev.ru         if (n > 0) {
497*0Sigor@sysoev.ru             /* c->socket.read_ready is kept. */
498*0Sigor@sysoev.ru             b->mem.free += n;
499*0Sigor@sysoev.ru             handler = c->read_state->ready_handler;
500*0Sigor@sysoev.ru 
501*0Sigor@sysoev.ru         } else if (n == 0) {
502*0Sigor@sysoev.ru             handler = c->read_state->close_handler;
503*0Sigor@sysoev.ru 
504*0Sigor@sysoev.ru         } else {
505*0Sigor@sysoev.ru             ret = nxt_gnutls_conn_test_error(thr, c, n,
506*0Sigor@sysoev.ru                                              nxt_gnutls_conn_io_read);
507*0Sigor@sysoev.ru 
508*0Sigor@sysoev.ru             if (nxt_fast_path(ret != NXT_ERROR)) {
509*0Sigor@sysoev.ru                 return;
510*0Sigor@sysoev.ru             }
511*0Sigor@sysoev.ru 
512*0Sigor@sysoev.ru             nxt_gnutls_conn_log_error(c, n,
513*0Sigor@sysoev.ru                                       "gnutls_record_recv(%d, %p, %uz): failed",
514*0Sigor@sysoev.ru                                       c->socket.fd, b->mem.free,
515*0Sigor@sysoev.ru                                       b->mem.end - b->mem.free);
516*0Sigor@sysoev.ru 
517*0Sigor@sysoev.ru             handler = c->read_state->error_handler;
518*0Sigor@sysoev.ru         }
519*0Sigor@sysoev.ru     }
520*0Sigor@sysoev.ru 
521*0Sigor@sysoev.ru     nxt_event_conn_io_handle(thr, c->read_work_queue, handler, c, data);
522*0Sigor@sysoev.ru }
523*0Sigor@sysoev.ru 
524*0Sigor@sysoev.ru 
525*0Sigor@sysoev.ru static ssize_t
526*0Sigor@sysoev.ru nxt_gnutls_conn_io_write_chunk(nxt_thread_t *thr, nxt_event_conn_t *c,
527*0Sigor@sysoev.ru     nxt_buf_t *b, size_t limit)
528*0Sigor@sysoev.ru {
529*0Sigor@sysoev.ru     nxt_gnutls_conn_t  *ssltls;
530*0Sigor@sysoev.ru 
531*0Sigor@sysoev.ru     nxt_log_debug(thr->log, "gnutls conn write chunk");
532*0Sigor@sysoev.ru 
533*0Sigor@sysoev.ru     ssltls = c->u.ssltls;
534*0Sigor@sysoev.ru 
535*0Sigor@sysoev.ru     return nxt_sendbuf_copy_coalesce(c, &ssltls->buffer, b, limit);
536*0Sigor@sysoev.ru }
537*0Sigor@sysoev.ru 
538*0Sigor@sysoev.ru 
539*0Sigor@sysoev.ru static ssize_t
540*0Sigor@sysoev.ru nxt_gnutls_conn_io_send(nxt_event_conn_t *c, void *buf, size_t size)
541*0Sigor@sysoev.ru {
542*0Sigor@sysoev.ru     ssize_t            n;
543*0Sigor@sysoev.ru     nxt_int_t          ret;
544*0Sigor@sysoev.ru     nxt_gnutls_conn_t  *ssltls;
545*0Sigor@sysoev.ru 
546*0Sigor@sysoev.ru     ssltls = c->u.ssltls;
547*0Sigor@sysoev.ru 
548*0Sigor@sysoev.ru     n = gnutls_record_send(ssltls->session, buf, size);
549*0Sigor@sysoev.ru 
550*0Sigor@sysoev.ru     nxt_log_debug(c->socket.log, "gnutls_record_send(%d, %p, %uz): %z",
551*0Sigor@sysoev.ru                   c->socket.fd, buf, size, n);
552*0Sigor@sysoev.ru 
553*0Sigor@sysoev.ru     if (n > 0) {
554*0Sigor@sysoev.ru         return n;
555*0Sigor@sysoev.ru     }
556*0Sigor@sysoev.ru 
557*0Sigor@sysoev.ru     ret = nxt_gnutls_conn_test_error(nxt_thread(), c, n,
558*0Sigor@sysoev.ru                                      nxt_event_conn_io_write);
559*0Sigor@sysoev.ru 
560*0Sigor@sysoev.ru     if (nxt_slow_path(ret == NXT_ERROR)) {
561*0Sigor@sysoev.ru         nxt_gnutls_conn_log_error(c, n,
562*0Sigor@sysoev.ru                                   "gnutls_record_send(%d, %p, %uz): failed",
563*0Sigor@sysoev.ru                                   c->socket.fd, buf, size);
564*0Sigor@sysoev.ru     }
565*0Sigor@sysoev.ru 
566*0Sigor@sysoev.ru     return ret;
567*0Sigor@sysoev.ru }
568*0Sigor@sysoev.ru 
569*0Sigor@sysoev.ru 
570*0Sigor@sysoev.ru static void
571*0Sigor@sysoev.ru nxt_gnutls_conn_io_shutdown(nxt_thread_t *thr, void *obj, void *data)
572*0Sigor@sysoev.ru {
573*0Sigor@sysoev.ru     int                     err;
574*0Sigor@sysoev.ru     nxt_int_t               ret;
575*0Sigor@sysoev.ru     nxt_event_conn_t        *c;
576*0Sigor@sysoev.ru     nxt_gnutls_conn_t       *ssltls;
577*0Sigor@sysoev.ru     nxt_work_handler_t      handler;
578*0Sigor@sysoev.ru     gnutls_close_request_t  how;
579*0Sigor@sysoev.ru 
580*0Sigor@sysoev.ru     c = obj;
581*0Sigor@sysoev.ru     ssltls = c->u.ssltls;
582*0Sigor@sysoev.ru 
583*0Sigor@sysoev.ru     if (ssltls->session == NULL || ssltls->no_shutdown) {
584*0Sigor@sysoev.ru         handler = c->write_state->close_handler;
585*0Sigor@sysoev.ru         goto done;
586*0Sigor@sysoev.ru     }
587*0Sigor@sysoev.ru 
588*0Sigor@sysoev.ru     nxt_log_debug(c->socket.log, "gnutls conn shutdown");
589*0Sigor@sysoev.ru 
590*0Sigor@sysoev.ru     if (c->socket.timedout || c->socket.error != 0) {
591*0Sigor@sysoev.ru         how = GNUTLS_SHUT_WR;
592*0Sigor@sysoev.ru 
593*0Sigor@sysoev.ru     } else if (c->socket.closed) {
594*0Sigor@sysoev.ru         how = GNUTLS_SHUT_RDWR;
595*0Sigor@sysoev.ru 
596*0Sigor@sysoev.ru     } else {
597*0Sigor@sysoev.ru         how = GNUTLS_SHUT_RDWR;
598*0Sigor@sysoev.ru     }
599*0Sigor@sysoev.ru 
600*0Sigor@sysoev.ru     err = gnutls_bye(ssltls->session, how);
601*0Sigor@sysoev.ru 
602*0Sigor@sysoev.ru     nxt_log_debug(c->socket.log, "gnutls_bye(%d, %d): %d",
603*0Sigor@sysoev.ru                   c->socket.fd, how, err);
604*0Sigor@sysoev.ru 
605*0Sigor@sysoev.ru     if (err == GNUTLS_E_SUCCESS) {
606*0Sigor@sysoev.ru         handler = c->write_state->close_handler;
607*0Sigor@sysoev.ru 
608*0Sigor@sysoev.ru     } else {
609*0Sigor@sysoev.ru         ret = nxt_gnutls_conn_test_error(thr, c, err,
610*0Sigor@sysoev.ru                                          nxt_gnutls_conn_io_shutdown);
611*0Sigor@sysoev.ru 
612*0Sigor@sysoev.ru         if (ret != NXT_ERROR) {  /* ret == NXT_AGAIN */
613*0Sigor@sysoev.ru             c->socket.error_handler = c->read_state->error_handler;
614*0Sigor@sysoev.ru             nxt_event_timer_add(thr->engine, &c->read_timer, 5000);
615*0Sigor@sysoev.ru             return;
616*0Sigor@sysoev.ru         }
617*0Sigor@sysoev.ru 
618*0Sigor@sysoev.ru         nxt_gnutls_conn_log_error(c, err, "gnutls_bye(%d) failed",
619*0Sigor@sysoev.ru                                   c->socket.fd);
620*0Sigor@sysoev.ru 
621*0Sigor@sysoev.ru         handler = c->write_state->error_handler;
622*0Sigor@sysoev.ru     }
623*0Sigor@sysoev.ru 
624*0Sigor@sysoev.ru done:
625*0Sigor@sysoev.ru 
626*0Sigor@sysoev.ru     nxt_event_conn_io_handle(thr, c->write_work_queue, handler, c, data);
627*0Sigor@sysoev.ru }
628*0Sigor@sysoev.ru 
629*0Sigor@sysoev.ru 
630*0Sigor@sysoev.ru static nxt_int_t
631*0Sigor@sysoev.ru nxt_gnutls_conn_test_error(nxt_thread_t *thr, nxt_event_conn_t *c, ssize_t err,
632*0Sigor@sysoev.ru     nxt_work_handler_t handler)
633*0Sigor@sysoev.ru {
634*0Sigor@sysoev.ru     int                ret;
635*0Sigor@sysoev.ru     nxt_gnutls_conn_t  *ssltls;
636*0Sigor@sysoev.ru 
637*0Sigor@sysoev.ru     switch (err) {
638*0Sigor@sysoev.ru 
639*0Sigor@sysoev.ru     case GNUTLS_E_REHANDSHAKE:
640*0Sigor@sysoev.ru     case GNUTLS_E_AGAIN:
641*0Sigor@sysoev.ru         ssltls = c->u.ssltls;
642*0Sigor@sysoev.ru         ret = gnutls_record_get_direction(ssltls->session);
643*0Sigor@sysoev.ru 
644*0Sigor@sysoev.ru         nxt_log_debug(thr->log, "gnutls_record_get_direction(): %d", ret);
645*0Sigor@sysoev.ru 
646*0Sigor@sysoev.ru         if (ret == 0) {
647*0Sigor@sysoev.ru             /* A read direction. */
648*0Sigor@sysoev.ru 
649*0Sigor@sysoev.ru             nxt_event_fd_block_write(thr->engine, &c->socket);
650*0Sigor@sysoev.ru 
651*0Sigor@sysoev.ru             c->socket.read_ready = 0;
652*0Sigor@sysoev.ru             c->socket.read_handler = handler;
653*0Sigor@sysoev.ru 
654*0Sigor@sysoev.ru             if (nxt_event_fd_is_disabled(c->socket.read)) {
655*0Sigor@sysoev.ru                 nxt_event_fd_enable_read(thr->engine, &c->socket);
656*0Sigor@sysoev.ru             }
657*0Sigor@sysoev.ru 
658*0Sigor@sysoev.ru         } else {
659*0Sigor@sysoev.ru             /* A write direction. */
660*0Sigor@sysoev.ru 
661*0Sigor@sysoev.ru             nxt_event_fd_block_read(thr->engine, &c->socket);
662*0Sigor@sysoev.ru 
663*0Sigor@sysoev.ru             c->socket.write_ready = 0;
664*0Sigor@sysoev.ru             c->socket.write_handler = handler;
665*0Sigor@sysoev.ru 
666*0Sigor@sysoev.ru             if (nxt_event_fd_is_disabled(c->socket.write)) {
667*0Sigor@sysoev.ru                 nxt_event_fd_enable_write(thr->engine, &c->socket);
668*0Sigor@sysoev.ru             }
669*0Sigor@sysoev.ru         }
670*0Sigor@sysoev.ru 
671*0Sigor@sysoev.ru         return NXT_AGAIN;
672*0Sigor@sysoev.ru 
673*0Sigor@sysoev.ru     default:
674*0Sigor@sysoev.ru         c->socket.error = 1000;  /* Nonexistent errno code. */
675*0Sigor@sysoev.ru         return NXT_ERROR;
676*0Sigor@sysoev.ru     }
677*0Sigor@sysoev.ru }
678*0Sigor@sysoev.ru 
679*0Sigor@sysoev.ru 
680*0Sigor@sysoev.ru static void
681*0Sigor@sysoev.ru nxt_gnutls_conn_log_error(nxt_event_conn_t *c, ssize_t err,
682*0Sigor@sysoev.ru     const char *fmt, ...)
683*0Sigor@sysoev.ru {
684*0Sigor@sysoev.ru     va_list      args;
685*0Sigor@sysoev.ru     nxt_uint_t   level;
686*0Sigor@sysoev.ru     u_char       *p, msg[NXT_MAX_ERROR_STR];
687*0Sigor@sysoev.ru 
688*0Sigor@sysoev.ru     level = nxt_gnutls_log_error_level(c, err);
689*0Sigor@sysoev.ru 
690*0Sigor@sysoev.ru     if (nxt_log_level_enough(c->socket.log, level)) {
691*0Sigor@sysoev.ru 
692*0Sigor@sysoev.ru         va_start(args, fmt);
693*0Sigor@sysoev.ru         p = nxt_vsprintf(msg, msg + sizeof(msg), fmt, args);
694*0Sigor@sysoev.ru         va_end(args);
695*0Sigor@sysoev.ru 
696*0Sigor@sysoev.ru         nxt_log_error(level, c->socket.log, "%*s (%d: %s)",
697*0Sigor@sysoev.ru                       p - msg, msg, err, gnutls_strerror(err));
698*0Sigor@sysoev.ru     }
699*0Sigor@sysoev.ru }
700*0Sigor@sysoev.ru 
701*0Sigor@sysoev.ru 
702*0Sigor@sysoev.ru static nxt_uint_t
703*0Sigor@sysoev.ru nxt_gnutls_log_error_level(nxt_event_conn_t *c, ssize_t err)
704*0Sigor@sysoev.ru {
705*0Sigor@sysoev.ru     nxt_gnutls_conn_t  *ssltls;
706*0Sigor@sysoev.ru 
707*0Sigor@sysoev.ru     switch (err) {
708*0Sigor@sysoev.ru 
709*0Sigor@sysoev.ru     case GNUTLS_E_UNKNOWN_CIPHER_SUITE:                      /*  -21 */
710*0Sigor@sysoev.ru 
711*0Sigor@sysoev.ru          /* Disable gnutls_bye(), because it returns GNUTLS_E_INTERNAL_ERROR. */
712*0Sigor@sysoev.ru         ssltls = c->u.ssltls;
713*0Sigor@sysoev.ru         ssltls->no_shutdown = 1;
714*0Sigor@sysoev.ru 
715*0Sigor@sysoev.ru          /* Fall through. */
716*0Sigor@sysoev.ru 
717*0Sigor@sysoev.ru     case GNUTLS_E_UNEXPECTED_PACKET_LENGTH:                  /*   -9 */
718*0Sigor@sysoev.ru         c->socket.error = 1000;  /* Nonexistent errno code. */
719*0Sigor@sysoev.ru         break;
720*0Sigor@sysoev.ru 
721*0Sigor@sysoev.ru     default:
722*0Sigor@sysoev.ru         return NXT_LOG_CRIT;
723*0Sigor@sysoev.ru     }
724*0Sigor@sysoev.ru 
725*0Sigor@sysoev.ru     return NXT_LOG_INFO;
726*0Sigor@sysoev.ru }
727*0Sigor@sysoev.ru 
728*0Sigor@sysoev.ru 
729*0Sigor@sysoev.ru static void
730*0Sigor@sysoev.ru nxt_gnutls_log_error(nxt_uint_t level, nxt_log_t *log, int err,
731*0Sigor@sysoev.ru     const char *fmt, ...)
732*0Sigor@sysoev.ru {
733*0Sigor@sysoev.ru     va_list  args;
734*0Sigor@sysoev.ru     u_char   *p, msg[NXT_MAX_ERROR_STR];
735*0Sigor@sysoev.ru 
736*0Sigor@sysoev.ru     va_start(args, fmt);
737*0Sigor@sysoev.ru     p = nxt_vsprintf(msg, msg + sizeof(msg), fmt, args);
738*0Sigor@sysoev.ru     va_end(args);
739*0Sigor@sysoev.ru 
740*0Sigor@sysoev.ru     nxt_log_error(level, log, "%*s (%d: %s)",
741*0Sigor@sysoev.ru                   p - msg, msg, err, gnutls_strerror(err));
742*0Sigor@sysoev.ru }
743