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