VirtualBox

source: vbox/trunk/src/libs/curl-8.0.1/lib/vtls/rustls.c@ 99344

Last change on this file since 99344 was 99344, checked in by vboxsync, 22 months ago

curl-8.0.1: Applied and adjusted our curl changes to 7.87.0 bugref:10417

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