• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
43 struct 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. */
map_error(rustls_result r)51 static 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 
66 static bool
cr_data_pending(struct Curl_cfilter * cf,const struct Curl_easy * data)67 cr_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 
76 static CURLcode
cr_connect(struct Curl_cfilter * cf UNUSED_PARAM,struct Curl_easy * data UNUSED_PARAM)77 cr_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 
84 struct io_ctx {
85   struct Curl_cfilter *cf;
86   struct Curl_easy *data;
87 };
88 
89 static int
read_cb(void * userdata,uint8_t * buf,uintptr_t len,uintptr_t * out_n)90 read_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 
108 static int
write_cb(void * userdata,const uint8_t * buf,uintptr_t len,uintptr_t * out_n)109 write_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  */
139 static ssize_t
cr_recv(struct Curl_cfilter * cf,struct Curl_easy * data,char * plainbuf,size_t plainlen,CURLcode * err)140 cr_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  */
253 static ssize_t
cr_send(struct Curl_cfilter * cf,struct Curl_easy * data,const void * plainbuf,size_t plainlen,CURLcode * err)254 cr_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. */
323 static enum rustls_result
cr_verify_none(void * userdata UNUSED_PARAM,const rustls_verify_server_cert_params * params UNUSED_PARAM)324 cr_verify_none(void *userdata UNUSED_PARAM,
325                const rustls_verify_server_cert_params *params UNUSED_PARAM)
326 {
327   return RUSTLS_RESULT_OK;
328 }
329 
330 static bool
cr_hostname_is_ip(const char * hostname)331 cr_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 
346 static CURLcode
cr_init_backend(struct Curl_cfilter * cf,struct Curl_easy * data,struct ssl_backend_data * const backend)347 cr_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 
449 static void
cr_set_negotiated_alpn(struct Curl_cfilter * cf,struct Curl_easy * data,const struct rustls_connection * rconn)450 cr_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 
460 static CURLcode
cr_connect_nonblocking(struct Curl_cfilter * cf,struct Curl_easy * data,bool * done)461 cr_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 */
564 static int
cr_get_select_socks(struct Curl_cfilter * cf,struct Curl_easy * data,curl_socket_t * socks)565 cr_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 
589 static void *
cr_get_internals(struct ssl_connect_data * connssl,CURLINFO info UNUSED_PARAM)590 cr_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 
598 static void
cr_close(struct Curl_cfilter * cf,struct Curl_easy * data)599 cr_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 
cr_version(char * buffer,size_t size)624 static 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 
630 const 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 */
665