VirtualBox

source: vbox/trunk/src/libs/curl-7.87.0/lib/vtls/rustls.c@ 98929

Last change on this file since 98929 was 98326, checked in by vboxsync, 2 years ago

curl-7.87.0: Applied and adjusted our curl changes to 7.83.1. bugref:10356

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette