• 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 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. */
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   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 
78 static CURLcode
cr_connect(struct Curl_cfilter * cf UNUSED_PARAM,struct Curl_easy * data UNUSED_PARAM)79 cr_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 
86 struct io_ctx {
87   struct Curl_cfilter *cf;
88   struct Curl_easy *data;
89 };
90 
91 static int
read_cb(void * userdata,uint8_t * buf,uintptr_t len,uintptr_t * out_n)92 read_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 
110 static int
write_cb(void * userdata,const uint8_t * buf,uintptr_t len,uintptr_t * out_n)111 write_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 
tls_recv_more(struct Curl_cfilter * cf,struct Curl_easy * data,CURLcode * err)133 static 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  */
188 static ssize_t
cr_recv(struct Curl_cfilter * cf,struct Curl_easy * data,char * plainbuf,size_t plainlen,CURLcode * err)189 cr_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 
265 out:
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  */
281 static ssize_t
cr_send(struct Curl_cfilter * cf,struct Curl_easy * data,const void * plainbuf,size_t plainlen,CURLcode * err)282 cr_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. */
352 static enum rustls_result
cr_verify_none(void * userdata UNUSED_PARAM,const rustls_verify_server_cert_params * params UNUSED_PARAM)353 cr_verify_none(void *userdata UNUSED_PARAM,
354                const rustls_verify_server_cert_params *params UNUSED_PARAM)
355 {
356   return RUSTLS_RESULT_OK;
357 }
358 
359 static bool
cr_hostname_is_ip(const char * hostname)360 cr_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 
375 static CURLcode
cr_init_backend(struct Curl_cfilter * cf,struct Curl_easy * data,struct rustls_ssl_backend_data * const backend)376 cr_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 
478 static void
cr_set_negotiated_alpn(struct Curl_cfilter * cf,struct Curl_easy * data,const struct rustls_connection * rconn)479 cr_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 
489 static CURLcode
cr_connect_nonblocking(struct Curl_cfilter * cf,struct Curl_easy * data,bool * done)490 cr_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 */
594 static int
cr_get_select_socks(struct Curl_cfilter * cf,struct Curl_easy * data,curl_socket_t * socks)595 cr_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 
620 static void *
cr_get_internals(struct ssl_connect_data * connssl,CURLINFO info UNUSED_PARAM)621 cr_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 
630 static void
cr_close(struct Curl_cfilter * cf,struct Curl_easy * data)631 cr_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 
cr_version(char * buffer,size_t size)657 static 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 
663 const 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