xref: /third_party/curl/lib/vtls/rustls.c (revision 13498266)
1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Jacob Hoffman-Andrews,
9 * <github@hoffman-andrews.com>
10 *
11 * This software is licensed as described in the file COPYING, which
12 * you should have received as part of this distribution. The terms
13 * are also available at https://curl.se/docs/copyright.html.
14 *
15 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
16 * copies of the Software, and permit persons to whom the Software is
17 * furnished to do so, under the terms of the COPYING file.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 * SPDX-License-Identifier: curl
23 *
24 ***************************************************************************/
25#include "curl_setup.h"
26
27#ifdef USE_RUSTLS
28
29#include "curl_printf.h"
30
31#include <errno.h>
32#include <rustls.h>
33
34#include "inet_pton.h"
35#include "urldata.h"
36#include "sendf.h"
37#include "vtls.h"
38#include "vtls_int.h"
39#include "select.h"
40#include "strerror.h"
41#include "multiif.h"
42#include "connect.h" /* for the connect timeout */
43
44struct rustls_ssl_backend_data
45{
46  const struct rustls_client_config *config;
47  struct rustls_connection *conn;
48  bool data_pending;
49};
50
51/* For a given rustls_result error code, return the best-matching CURLcode. */
52static CURLcode map_error(rustls_result r)
53{
54  if(rustls_result_is_cert_error(r)) {
55    return CURLE_PEER_FAILED_VERIFICATION;
56  }
57  switch(r) {
58    case RUSTLS_RESULT_OK:
59      return CURLE_OK;
60    case RUSTLS_RESULT_NULL_PARAMETER:
61      return CURLE_BAD_FUNCTION_ARGUMENT;
62    default:
63      return CURLE_READ_ERROR;
64  }
65}
66
67static bool
68cr_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data)
69{
70  struct ssl_connect_data *ctx = cf->ctx;
71  struct rustls_ssl_backend_data *backend;
72
73  (void)data;
74  DEBUGASSERT(ctx && ctx->backend);
75  backend = (struct rustls_ssl_backend_data *)ctx->backend;
76  return backend->data_pending;
77}
78
79struct io_ctx {
80  struct Curl_cfilter *cf;
81  struct Curl_easy *data;
82};
83
84static int
85read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n)
86{
87  struct io_ctx *io_ctx = userdata;
88  CURLcode result;
89  int ret = 0;
90  ssize_t nread = Curl_conn_cf_recv(io_ctx->cf->next, io_ctx->data,
91                                    (char *)buf, len, &result);
92  if(nread < 0) {
93    nread = 0;
94    if(CURLE_AGAIN == result)
95      ret = EAGAIN;
96    else
97      ret = EINVAL;
98  }
99  *out_n = (int)nread;
100  return ret;
101}
102
103static int
104write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n)
105{
106  struct io_ctx *io_ctx = userdata;
107  CURLcode result;
108  int ret = 0;
109  ssize_t nwritten = Curl_conn_cf_send(io_ctx->cf->next, io_ctx->data,
110                                       (const char *)buf, len, &result);
111  if(nwritten < 0) {
112    nwritten = 0;
113    if(CURLE_AGAIN == result)
114      ret = EAGAIN;
115    else
116      ret = EINVAL;
117  }
118  *out_n = (int)nwritten;
119  /*
120  CURL_TRC_CFX(io_ctx->data, io_ctx->cf, "cf->next send(len=%zu) -> %zd, %d",
121                len, nwritten, result));
122  */
123  return ret;
124}
125
126static ssize_t tls_recv_more(struct Curl_cfilter *cf,
127                             struct Curl_easy *data, CURLcode *err)
128{
129  struct ssl_connect_data *const connssl = cf->ctx;
130  struct rustls_ssl_backend_data *const backend =
131    (struct rustls_ssl_backend_data *)connssl->backend;
132  struct io_ctx io_ctx;
133  size_t tls_bytes_read = 0;
134  rustls_io_result io_error;
135  rustls_result rresult = 0;
136
137  io_ctx.cf = cf;
138  io_ctx.data = data;
139  io_error = rustls_connection_read_tls(backend->conn, read_cb, &io_ctx,
140                                        &tls_bytes_read);
141  if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
142    *err = CURLE_AGAIN;
143    return -1;
144  }
145  else if(io_error) {
146    char buffer[STRERROR_LEN];
147    failf(data, "reading from socket: %s",
148          Curl_strerror(io_error, buffer, sizeof(buffer)));
149    *err = CURLE_READ_ERROR;
150    return -1;
151  }
152
153  rresult = rustls_connection_process_new_packets(backend->conn);
154  if(rresult != RUSTLS_RESULT_OK) {
155    char errorbuf[255];
156    size_t errorlen;
157    rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
158    failf(data, "rustls_connection_process_new_packets: %.*s",
159      (int)errorlen, errorbuf);
160    *err = map_error(rresult);
161    return -1;
162  }
163
164  backend->data_pending = TRUE;
165  *err = CURLE_OK;
166  return (ssize_t)tls_bytes_read;
167}
168
169/*
170 * On each run:
171 *  - Read a chunk of bytes from the socket into rustls' TLS input buffer.
172 *  - Tell rustls to process any new packets.
173 *  - Read out as many plaintext bytes from rustls as possible, until hitting
174 *    error, EOF, or EAGAIN/EWOULDBLOCK, or plainbuf/plainlen is filled up.
175 *
176 * It's okay to call this function with plainbuf == NULL and plainlen == 0.
177 * In that case, it will copy bytes from the socket into rustls' TLS input
178 * buffer, and process packets, but won't consume bytes from rustls' plaintext
179 * output buffer.
180 */
181static ssize_t
182cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
183            char *plainbuf, size_t plainlen, CURLcode *err)
184{
185  struct ssl_connect_data *const connssl = cf->ctx;
186  struct rustls_ssl_backend_data *const backend =
187    (struct rustls_ssl_backend_data *)connssl->backend;
188  struct rustls_connection *rconn = NULL;
189  size_t n = 0;
190  size_t plain_bytes_copied = 0;
191  rustls_result rresult = 0;
192  ssize_t nread;
193  bool eof = FALSE;
194
195  DEBUGASSERT(backend);
196  rconn = backend->conn;
197
198  while(plain_bytes_copied < plainlen) {
199    if(!backend->data_pending) {
200      if(tls_recv_more(cf, data, err) < 0) {
201        if(*err != CURLE_AGAIN) {
202          nread = -1;
203          goto out;
204        }
205        break;
206      }
207    }
208
209    rresult = rustls_connection_read(rconn,
210      (uint8_t *)plainbuf + plain_bytes_copied,
211      plainlen - plain_bytes_copied,
212      &n);
213    if(rresult == RUSTLS_RESULT_PLAINTEXT_EMPTY) {
214      backend->data_pending = FALSE;
215    }
216    else if(rresult == RUSTLS_RESULT_UNEXPECTED_EOF) {
217      failf(data, "rustls: peer closed TCP connection "
218        "without first closing TLS connection");
219      *err = CURLE_READ_ERROR;
220      nread = -1;
221      goto out;
222    }
223    else if(rresult != RUSTLS_RESULT_OK) {
224      /* n always equals 0 in this case, don't need to check it */
225      char errorbuf[255];
226      size_t errorlen;
227      rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
228      failf(data, "rustls_connection_read: %.*s", (int)errorlen, errorbuf);
229      *err = CURLE_READ_ERROR;
230      nread = -1;
231      goto out;
232    }
233    else if(n == 0) {
234      /* n == 0 indicates clean EOF, but we may have read some other
235         plaintext bytes before we reached this. Break out of the loop
236         so we can figure out whether to return success or EOF. */
237      eof = TRUE;
238      break;
239    }
240    else {
241      plain_bytes_copied += n;
242    }
243  }
244
245  if(plain_bytes_copied) {
246    *err = CURLE_OK;
247    nread = (ssize_t)plain_bytes_copied;
248  }
249  else if(eof) {
250    *err = CURLE_OK;
251    nread = 0;
252  }
253  else {
254    *err = CURLE_AGAIN;
255    nread = -1;
256  }
257
258out:
259  CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d",
260              plainlen, nread, *err);
261  return nread;
262}
263
264/*
265 * On each call:
266 *  - Copy `plainlen` bytes into rustls' plaintext input buffer (if > 0).
267 *  - Fully drain rustls' plaintext output buffer into the socket until
268 *    we get either an error or EAGAIN/EWOULDBLOCK.
269 *
270 * It's okay to call this function with plainbuf == NULL and plainlen == 0.
271 * In that case, it won't read anything into rustls' plaintext input buffer.
272 * It will only drain rustls' plaintext output buffer into the socket.
273 */
274static ssize_t
275cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
276        const void *plainbuf, size_t plainlen, CURLcode *err)
277{
278  struct ssl_connect_data *const connssl = cf->ctx;
279  struct rustls_ssl_backend_data *const backend =
280    (struct rustls_ssl_backend_data *)connssl->backend;
281  struct rustls_connection *rconn = NULL;
282  struct io_ctx io_ctx;
283  size_t plainwritten = 0;
284  size_t tlswritten = 0;
285  size_t tlswritten_total = 0;
286  rustls_result rresult;
287  rustls_io_result io_error;
288  char errorbuf[256];
289  size_t errorlen;
290
291  DEBUGASSERT(backend);
292  rconn = backend->conn;
293
294  CURL_TRC_CF(data, cf, "cf_send: %ld plain bytes", plainlen);
295
296  io_ctx.cf = cf;
297  io_ctx.data = data;
298
299  if(plainlen > 0) {
300    rresult = rustls_connection_write(rconn, plainbuf, plainlen,
301                                      &plainwritten);
302    if(rresult != RUSTLS_RESULT_OK) {
303      rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
304      failf(data, "rustls_connection_write: %.*s", (int)errorlen, errorbuf);
305      *err = CURLE_WRITE_ERROR;
306      return -1;
307    }
308    else if(plainwritten == 0) {
309      failf(data, "rustls_connection_write: EOF");
310      *err = CURLE_WRITE_ERROR;
311      return -1;
312    }
313  }
314
315  while(rustls_connection_wants_write(rconn)) {
316    io_error = rustls_connection_write_tls(rconn, write_cb, &io_ctx,
317                                           &tlswritten);
318    if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
319      CURL_TRC_CF(data, cf, "cf_send: EAGAIN after %zu bytes",
320                  tlswritten_total);
321      *err = CURLE_AGAIN;
322      return -1;
323    }
324    else if(io_error) {
325      char buffer[STRERROR_LEN];
326      failf(data, "writing to socket: %s",
327            Curl_strerror(io_error, buffer, sizeof(buffer)));
328      *err = CURLE_WRITE_ERROR;
329      return -1;
330    }
331    if(tlswritten == 0) {
332      failf(data, "EOF in swrite");
333      *err = CURLE_WRITE_ERROR;
334      return -1;
335    }
336    CURL_TRC_CF(data, cf, "cf_send: wrote %zu TLS bytes", tlswritten);
337    tlswritten_total += tlswritten;
338  }
339
340  return plainwritten;
341}
342
343/* A server certificate verify callback for rustls that always returns
344   RUSTLS_RESULT_OK, or in other words disable certificate verification. */
345static enum rustls_result
346cr_verify_none(void *userdata UNUSED_PARAM,
347               const rustls_verify_server_cert_params *params UNUSED_PARAM)
348{
349  return RUSTLS_RESULT_OK;
350}
351
352static bool
353cr_hostname_is_ip(const char *hostname)
354{
355  struct in_addr in;
356#ifdef ENABLE_IPV6
357  struct in6_addr in6;
358  if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) {
359    return true;
360  }
361#endif /* ENABLE_IPV6 */
362  if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
363    return true;
364  }
365  return false;
366}
367
368static CURLcode
369cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
370                struct rustls_ssl_backend_data *const backend)
371{
372  struct ssl_connect_data *connssl = cf->ctx;
373  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
374  struct rustls_connection *rconn = NULL;
375  struct rustls_client_config_builder *config_builder = NULL;
376  struct rustls_root_cert_store *roots = NULL;
377  const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
378  const char * const ssl_cafile =
379    /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
380    (ca_info_blob ? NULL : conn_config->CAfile);
381  const bool verifypeer = conn_config->verifypeer;
382  const char *hostname = connssl->peer.hostname;
383  char errorbuf[256];
384  size_t errorlen;
385  int result;
386
387  DEBUGASSERT(backend);
388  rconn = backend->conn;
389
390  config_builder = rustls_client_config_builder_new();
391  if(connssl->alpn) {
392    struct alpn_proto_buf proto;
393    rustls_slice_bytes alpn[ALPN_ENTRIES_MAX];
394    size_t i;
395
396    for(i = 0; i < connssl->alpn->count; ++i) {
397      alpn[i].data = (const uint8_t *)connssl->alpn->entries[i];
398      alpn[i].len = strlen(connssl->alpn->entries[i]);
399    }
400    rustls_client_config_builder_set_alpn_protocols(config_builder, alpn,
401                                                    connssl->alpn->count);
402    Curl_alpn_to_proto_str(&proto, connssl->alpn);
403    infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
404  }
405  if(!verifypeer) {
406    rustls_client_config_builder_dangerous_set_certificate_verifier(
407      config_builder, cr_verify_none);
408    /* rustls doesn't support IP addresses (as of 0.19.0), and will reject
409     * connections created with an IP address, even when certificate
410     * verification is turned off. Set a placeholder hostname and disable
411     * SNI. */
412    if(cr_hostname_is_ip(hostname)) {
413      rustls_client_config_builder_set_enable_sni(config_builder, false);
414      hostname = "example.invalid";
415    }
416  }
417  else if(ca_info_blob) {
418    roots = rustls_root_cert_store_new();
419
420    /* Enable strict parsing only if verification isn't disabled. */
421    result = rustls_root_cert_store_add_pem(roots, ca_info_blob->data,
422                                            ca_info_blob->len, verifypeer);
423    if(result != RUSTLS_RESULT_OK) {
424      failf(data, "rustls: failed to parse trusted certificates from blob");
425      rustls_root_cert_store_free(roots);
426      rustls_client_config_free(
427        rustls_client_config_builder_build(config_builder));
428      return CURLE_SSL_CACERT_BADFILE;
429    }
430
431    result = rustls_client_config_builder_use_roots(config_builder, roots);
432    rustls_root_cert_store_free(roots);
433    if(result != RUSTLS_RESULT_OK) {
434      failf(data, "rustls: failed to load trusted certificates");
435      rustls_client_config_free(
436        rustls_client_config_builder_build(config_builder));
437      return CURLE_SSL_CACERT_BADFILE;
438    }
439  }
440  else if(ssl_cafile) {
441    result = rustls_client_config_builder_load_roots_from_file(
442      config_builder, ssl_cafile);
443    if(result != RUSTLS_RESULT_OK) {
444      failf(data, "rustls: failed to load trusted certificates");
445      rustls_client_config_free(
446        rustls_client_config_builder_build(config_builder));
447      return CURLE_SSL_CACERT_BADFILE;
448    }
449  }
450
451  backend->config = rustls_client_config_builder_build(config_builder);
452  DEBUGASSERT(rconn == NULL);
453  {
454    /* rustls claims to manage ip address hostnames as well here. So,
455     * if we have an SNI, we use it, otherwise we pass the hostname */
456    char *server = connssl->peer.sni?
457                   connssl->peer.sni : connssl->peer.hostname;
458    result = rustls_client_connection_new(backend->config, server, &rconn);
459  }
460  if(result != RUSTLS_RESULT_OK) {
461    rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen);
462    failf(data, "rustls_client_connection_new: %.*s", (int)errorlen, errorbuf);
463    return CURLE_COULDNT_CONNECT;
464  }
465  rustls_connection_set_userdata(rconn, backend);
466  backend->conn = rconn;
467  return CURLE_OK;
468}
469
470static void
471cr_set_negotiated_alpn(struct Curl_cfilter *cf, struct Curl_easy *data,
472  const struct rustls_connection *rconn)
473{
474  const uint8_t *protocol = NULL;
475  size_t len = 0;
476
477  rustls_connection_get_alpn_protocol(rconn, &protocol, &len);
478  Curl_alpn_set_negotiated(cf, data, protocol, len);
479}
480
481/* Given an established network connection, do a TLS handshake.
482 *
483 * If `blocking` is true, this function will block until the handshake is
484 * complete. Otherwise it will return as soon as I/O would block.
485 *
486 * For the non-blocking I/O case, this function will set `*done` to true
487 * once the handshake is complete. This function never reads the value of
488 * `*done*`.
489 */
490static CURLcode
491cr_connect_common(struct Curl_cfilter *cf,
492                  struct Curl_easy *data,
493                  bool blocking,
494                  bool *done)
495{
496  struct ssl_connect_data *const connssl = cf->ctx;
497  curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
498  struct rustls_ssl_backend_data *const backend =
499    (struct rustls_ssl_backend_data *)connssl->backend;
500  struct rustls_connection *rconn = NULL;
501  CURLcode tmperr = CURLE_OK;
502  int result;
503  int what;
504  bool wants_read;
505  bool wants_write;
506  curl_socket_t writefd;
507  curl_socket_t readfd;
508  timediff_t timeout_ms;
509  timediff_t socket_check_timeout;
510
511  DEBUGASSERT(backend);
512
513  if(ssl_connection_none == connssl->state) {
514    result = cr_init_backend(cf, data,
515               (struct rustls_ssl_backend_data *)connssl->backend);
516    if(result != CURLE_OK) {
517      return result;
518    }
519    connssl->state = ssl_connection_negotiating;
520  }
521
522  rconn = backend->conn;
523
524  /* Read/write data until the handshake is done or the socket would block. */
525  for(;;) {
526    /*
527    * Connection has been established according to rustls. Set send/recv
528    * handlers, and update the state machine.
529    */
530    if(!rustls_connection_is_handshaking(rconn)) {
531      infof(data, "Done handshaking");
532      /* Done with the handshake. Set up callbacks to send/receive data. */
533      connssl->state = ssl_connection_complete;
534
535      cr_set_negotiated_alpn(cf, data, rconn);
536
537      *done = TRUE;
538      return CURLE_OK;
539    }
540
541    wants_read = rustls_connection_wants_read(rconn);
542    wants_write = rustls_connection_wants_write(rconn);
543    DEBUGASSERT(wants_read || wants_write);
544    writefd = wants_write?sockfd:CURL_SOCKET_BAD;
545    readfd = wants_read?sockfd:CURL_SOCKET_BAD;
546
547    /* check allowed time left */
548    timeout_ms = Curl_timeleft(data, NULL, TRUE);
549
550    if(timeout_ms < 0) {
551      /* no need to continue if time already is up */
552      failf(data, "rustls: operation timed out before socket check");
553      return CURLE_OPERATION_TIMEDOUT;
554    }
555
556    socket_check_timeout = blocking?timeout_ms:0;
557
558    what = Curl_socket_check(
559      readfd, CURL_SOCKET_BAD, writefd, socket_check_timeout);
560    if(what < 0) {
561      /* fatal error */
562      failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
563      return CURLE_SSL_CONNECT_ERROR;
564    }
565    if(blocking && 0 == what) {
566      failf(data, "rustls connection timeout after %"
567        CURL_FORMAT_TIMEDIFF_T " ms", socket_check_timeout);
568      return CURLE_OPERATION_TIMEDOUT;
569    }
570    if(0 == what) {
571      infof(data, "Curl_socket_check: %s would block",
572            wants_read&&wants_write ? "writing and reading" :
573            wants_write ? "writing" : "reading");
574      *done = FALSE;
575      return CURLE_OK;
576    }
577    /* socket is readable or writable */
578
579    if(wants_write) {
580      infof(data, "rustls_connection wants us to write_tls.");
581      cr_send(cf, data, NULL, 0, &tmperr);
582      if(tmperr == CURLE_AGAIN) {
583        infof(data, "writing would block");
584        /* fall through */
585      }
586      else if(tmperr != CURLE_OK) {
587        return tmperr;
588      }
589    }
590
591    if(wants_read) {
592      infof(data, "rustls_connection wants us to read_tls.");
593
594      if(tls_recv_more(cf, data, &tmperr) < 0) {
595        if(tmperr == CURLE_AGAIN) {
596          infof(data, "reading would block");
597          /* fall through */
598        }
599        else if(tmperr == CURLE_READ_ERROR) {
600          return CURLE_SSL_CONNECT_ERROR;
601        }
602        else {
603          return tmperr;
604        }
605      }
606    }
607  }
608
609  /* We should never fall through the loop. We should return either because
610     the handshake is done or because we can't read/write without blocking. */
611  DEBUGASSERT(false);
612}
613
614static CURLcode
615cr_connect_nonblocking(struct Curl_cfilter *cf,
616                       struct Curl_easy *data, bool *done)
617{
618  return cr_connect_common(cf, data, false, done);
619}
620
621static CURLcode
622cr_connect_blocking(struct Curl_cfilter *cf UNUSED_PARAM,
623           struct Curl_easy *data UNUSED_PARAM)
624{
625  bool done; /* unused */
626  return cr_connect_common(cf, data, true, &done);
627}
628
629static void cr_adjust_pollset(struct Curl_cfilter *cf,
630                              struct Curl_easy *data,
631                              struct easy_pollset *ps)
632{
633  if(!cf->connected) {
634    curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data);
635    struct ssl_connect_data *const connssl = cf->ctx;
636    struct rustls_ssl_backend_data *const backend =
637      (struct rustls_ssl_backend_data *)connssl->backend;
638    struct rustls_connection *rconn = NULL;
639
640    (void)data;
641    DEBUGASSERT(backend);
642    rconn = backend->conn;
643
644    if(rustls_connection_wants_write(rconn)) {
645      Curl_pollset_add_out(data, ps, sock);
646    }
647    if(rustls_connection_wants_read(rconn)) {
648      Curl_pollset_add_in(data, ps, sock);
649    }
650  }
651}
652
653static void *
654cr_get_internals(struct ssl_connect_data *connssl,
655                 CURLINFO info UNUSED_PARAM)
656{
657  struct rustls_ssl_backend_data *backend =
658    (struct rustls_ssl_backend_data *)connssl->backend;
659  DEBUGASSERT(backend);
660  return &backend->conn;
661}
662
663static void
664cr_close(struct Curl_cfilter *cf, struct Curl_easy *data)
665{
666  struct ssl_connect_data *connssl = cf->ctx;
667  struct rustls_ssl_backend_data *backend =
668    (struct rustls_ssl_backend_data *)connssl->backend;
669  CURLcode tmperr = CURLE_OK;
670  ssize_t n = 0;
671
672  DEBUGASSERT(backend);
673
674  if(backend->conn) {
675    rustls_connection_send_close_notify(backend->conn);
676    n = cr_send(cf, data, NULL, 0, &tmperr);
677    if(n < 0) {
678      failf(data, "rustls: error sending close_notify: %d", tmperr);
679    }
680
681    rustls_connection_free(backend->conn);
682    backend->conn = NULL;
683  }
684  if(backend->config) {
685    rustls_client_config_free(backend->config);
686    backend->config = NULL;
687  }
688}
689
690static size_t cr_version(char *buffer, size_t size)
691{
692  struct rustls_str ver = rustls_version();
693  return msnprintf(buffer, size, "%.*s", (int)ver.len, ver.data);
694}
695
696const struct Curl_ssl Curl_ssl_rustls = {
697  { CURLSSLBACKEND_RUSTLS, "rustls" },
698  SSLSUPP_CAINFO_BLOB |            /* supports */
699  SSLSUPP_TLS13_CIPHERSUITES |
700  SSLSUPP_HTTPS_PROXY,
701  sizeof(struct rustls_ssl_backend_data),
702
703  Curl_none_init,                  /* init */
704  Curl_none_cleanup,               /* cleanup */
705  cr_version,                      /* version */
706  Curl_none_check_cxn,             /* check_cxn */
707  Curl_none_shutdown,              /* shutdown */
708  cr_data_pending,                 /* data_pending */
709  Curl_none_random,                /* random */
710  Curl_none_cert_status_request,   /* cert_status_request */
711  cr_connect_blocking,             /* connect */
712  cr_connect_nonblocking,          /* connect_nonblocking */
713  cr_adjust_pollset,               /* adjust_pollset */
714  cr_get_internals,                /* get_internals */
715  cr_close,                        /* close_one */
716  Curl_none_close_all,             /* close_all */
717  Curl_none_session_free,          /* session_free */
718  Curl_none_set_engine,            /* set_engine */
719  Curl_none_set_engine_default,    /* set_engine_default */
720  Curl_none_engines_list,          /* engines_list */
721  Curl_none_false_start,           /* false_start */
722  NULL,                            /* sha256sum */
723  NULL,                            /* associate_connection */
724  NULL,                            /* disassociate_connection */
725  NULL,                            /* free_multi_ssl_backend_data */
726  cr_recv,                         /* recv decrypted data */
727  cr_send,                         /* send data to encrypt */
728};
729
730#endif /* USE_RUSTLS */
731