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