rustls_ffi/
connection.rs

1use std::io::{ErrorKind, Read, Write};
2use std::{ffi::c_void, ptr::null};
3use std::{ptr::null_mut, slice};
4
5use libc::{EINVAL, EIO, size_t};
6use rustls::CipherSuite::TLS_NULL_WITH_NULL_NULL;
7use rustls::pki_types::CertificateDer;
8use rustls::{ClientConnection, ServerConnection};
9
10use crate::certificate::rustls_certificate;
11use crate::enums::rustls_handshake_kind;
12use crate::error::{map_error, rustls_io_result, rustls_result};
13use crate::ffi::{
14    box_castable, free_box, try_callback, try_mut_from_ptr, try_ref_from_ptr, try_slice,
15    try_slice_mut,
16};
17use crate::io::{
18    CallbackReader, CallbackWriter, VectoredCallbackWriter, rustls_read_callback,
19    rustls_write_callback, rustls_write_vectored_callback,
20};
21use crate::log::{ensure_log_registered, rustls_log_callback};
22use crate::panic::ffi_panic_boundary;
23use crate::rslice::rustls_str;
24use crate::userdata::userdata_push;
25
26pub(crate) struct Connection {
27    conn: rustls::Connection,
28    userdata: *mut c_void,
29    log_callback: rustls_log_callback,
30}
31
32impl Connection {
33    pub(crate) fn from_client(conn: ClientConnection) -> Self {
34        Connection {
35            conn: conn.into(),
36            userdata: null_mut(),
37            log_callback: None,
38        }
39    }
40
41    pub(crate) fn from_server(conn: ServerConnection) -> Self {
42        Connection {
43            conn: conn.into(),
44            userdata: null_mut(),
45            log_callback: None,
46        }
47    }
48
49    #[allow(dead_code)]
50    pub(crate) fn as_client(&self) -> Option<&ClientConnection> {
51        match &self.conn {
52            rustls::Connection::Client(c) => Some(c),
53            _ => None,
54        }
55    }
56
57    pub(crate) fn as_server(&self) -> Option<&ServerConnection> {
58        match &self.conn {
59            rustls::Connection::Server(s) => Some(s),
60            _ => None,
61        }
62    }
63
64    #[allow(dead_code)]
65    pub(crate) fn as_client_mut(&mut self) -> Option<&mut ClientConnection> {
66        match &mut self.conn {
67            rustls::Connection::Client(c) => Some(c),
68            _ => None,
69        }
70    }
71
72    #[allow(dead_code)]
73    pub(crate) fn as_server_mut(&mut self) -> Option<&mut ServerConnection> {
74        match &mut self.conn {
75            rustls::Connection::Server(s) => Some(s),
76            _ => None,
77        }
78    }
79}
80
81impl std::ops::Deref for Connection {
82    type Target = rustls::Connection;
83
84    fn deref(&self) -> &Self::Target {
85        &self.conn
86    }
87}
88
89impl std::ops::DerefMut for Connection {
90    fn deref_mut(&mut self) -> &mut Self::Target {
91        &mut self.conn
92    }
93}
94
95box_castable! {
96    /// A C representation of a Rustls `Connection`.
97    pub struct rustls_connection(Connection);
98}
99
100impl rustls_connection {
101    /// Set the userdata pointer associated with this connection. This will be passed
102    /// to any callbacks invoked by the connection, if you've set up callbacks in the config.
103    /// The pointed-to data must outlive the connection.
104    #[no_mangle]
105    pub extern "C" fn rustls_connection_set_userdata(
106        conn: *mut rustls_connection,
107        userdata: *mut c_void,
108    ) {
109        try_mut_from_ptr!(conn).userdata = userdata;
110    }
111
112    /// Set the logging callback for this connection. The log callback will be invoked
113    /// with the userdata parameter previously set by rustls_connection_set_userdata, or
114    /// NULL if no userdata was set.
115    #[no_mangle]
116    pub extern "C" fn rustls_connection_set_log_callback(
117        conn: *mut rustls_connection,
118        cb: rustls_log_callback,
119    ) {
120        let conn = try_mut_from_ptr!(conn);
121        ensure_log_registered();
122        conn.log_callback = cb;
123    }
124
125    /// Read some TLS bytes from the network into internal buffers. The actual network
126    /// I/O is performed by `callback`, which you provide. Rustls will invoke your
127    /// callback with a suitable buffer to store the read bytes into. You don't have
128    /// to fill it up, just fill with as many bytes as you get in one syscall.
129    /// The `userdata` parameter is passed through directly to `callback`. Note that
130    /// this is distinct from the `userdata` parameter set with
131    /// `rustls_connection_set_userdata`.
132    /// Returns 0 for success, or an errno value on error. Passes through return values
133    /// from callback. See rustls_read_callback for more details.
134    /// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.read_tls>
135    #[no_mangle]
136    pub extern "C" fn rustls_connection_read_tls(
137        conn: *mut rustls_connection,
138        callback: rustls_read_callback,
139        userdata: *mut c_void,
140        out_n: *mut size_t,
141    ) -> rustls_io_result {
142        ffi_panic_boundary! {
143            let conn = try_mut_from_ptr!(conn);
144            if out_n.is_null() {
145                return rustls_io_result(EINVAL);
146            }
147            let callback = try_callback!(callback);
148
149            let mut reader = CallbackReader { callback, userdata };
150            let n_read = match conn.read_tls(&mut reader) {
151                Ok(n) => n,
152                Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)),
153            };
154            unsafe {
155                *out_n = n_read;
156            }
157
158            rustls_io_result(0)
159        }
160    }
161
162    /// Write some TLS bytes to the network. The actual network I/O is performed by
163    /// `callback`, which you provide. Rustls will invoke your callback with a
164    /// suitable buffer containing TLS bytes to send. You don't have to write them
165    /// all, just as many as you can in one syscall.
166    /// The `userdata` parameter is passed through directly to `callback`. Note that
167    /// this is distinct from the `userdata` parameter set with
168    /// `rustls_connection_set_userdata`.
169    /// Returns 0 for success, or an errno value on error. Passes through return values
170    /// from callback. See rustls_write_callback for more details.
171    /// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.write_tls>
172    #[no_mangle]
173    pub extern "C" fn rustls_connection_write_tls(
174        conn: *mut rustls_connection,
175        callback: rustls_write_callback,
176        userdata: *mut c_void,
177        out_n: *mut size_t,
178    ) -> rustls_io_result {
179        ffi_panic_boundary! {
180            let conn = try_mut_from_ptr!(conn);
181            if out_n.is_null() {
182                return rustls_io_result(EINVAL);
183            }
184            let callback = try_callback!(callback);
185
186            let mut writer = CallbackWriter { callback, userdata };
187            let n_written = match conn.write_tls(&mut writer) {
188                Ok(n) => n,
189                Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)),
190            };
191            unsafe {
192                *out_n = n_written;
193            }
194
195            rustls_io_result(0)
196        }
197    }
198
199    /// Write all available TLS bytes to the network. The actual network I/O is performed by
200    /// `callback`, which you provide. Rustls will invoke your callback with an array
201    /// of rustls_slice_bytes, each containing a buffer with TLS bytes to send.
202    /// You don't have to write them all, just as many as you are willing.
203    /// The `userdata` parameter is passed through directly to `callback`. Note that
204    /// this is distinct from the `userdata` parameter set with
205    /// `rustls_connection_set_userdata`.
206    /// Returns 0 for success, or an errno value on error. Passes through return values
207    /// from callback. See rustls_write_callback for more details.
208    /// <https://docs.rs/rustls/latest/rustls/struct.Writer.html#method.write_vectored>
209    #[no_mangle]
210    pub extern "C" fn rustls_connection_write_tls_vectored(
211        conn: *mut rustls_connection,
212        callback: rustls_write_vectored_callback,
213        userdata: *mut c_void,
214        out_n: *mut size_t,
215    ) -> rustls_io_result {
216        ffi_panic_boundary! {
217            let conn = try_mut_from_ptr!(conn);
218            if out_n.is_null() {
219                return rustls_io_result(EINVAL);
220            }
221            let callback = try_callback!(callback);
222
223            let mut writer = VectoredCallbackWriter { callback, userdata };
224            let n_written = match conn.write_tls(&mut writer) {
225                Ok(n) => n,
226                Err(e) => return rustls_io_result(e.raw_os_error().unwrap_or(EIO)),
227            };
228            unsafe {
229                *out_n = n_written;
230            }
231
232            rustls_io_result(0)
233        }
234    }
235
236    /// Decrypt any available ciphertext from the internal buffer and put it
237    /// into the internal plaintext buffer, potentially making bytes available
238    /// for rustls_connection_read().
239    /// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.process_new_packets>
240    #[no_mangle]
241    pub extern "C" fn rustls_connection_process_new_packets(
242        conn: *mut rustls_connection,
243    ) -> rustls_result {
244        ffi_panic_boundary! {
245            let conn = try_mut_from_ptr!(conn);
246            let guard = match userdata_push(conn.userdata, conn.log_callback) {
247                Ok(g) => g,
248                Err(_) => return rustls_result::Panic,
249            };
250            let result = match conn.process_new_packets() {
251                Ok(_) => rustls_result::Ok,
252                Err(e) => map_error(e),
253            };
254            match guard.try_drop() {
255                Ok(()) => result,
256                Err(_) => rustls_result::Panic,
257            }
258        }
259    }
260
261    /// <https://docs.rs/rustls/latest/rustls/struct.CommonState.html#method.wants_read>
262    #[no_mangle]
263    pub extern "C" fn rustls_connection_wants_read(conn: *const rustls_connection) -> bool {
264        ffi_panic_boundary! {
265            try_ref_from_ptr!(conn).wants_read()
266        }
267    }
268
269    /// <https://docs.rs/rustls/latest/rustls/struct.CommonState.html#method.wants_write>
270    #[no_mangle]
271    pub extern "C" fn rustls_connection_wants_write(conn: *const rustls_connection) -> bool {
272        ffi_panic_boundary! {
273            try_ref_from_ptr!(conn).wants_write()
274        }
275    }
276
277    /// Returns true if the connection is currently performing the TLS handshake.
278    ///
279    /// Note: This may return `false` while there are still handshake packets waiting
280    /// to be extracted and transmitted with `rustls_connection_write_tls()`.
281    ///
282    /// See the rustls documentation for more information.
283    ///
284    /// <https://docs.rs/rustls/latest/rustls/struct.CommonState.html#method.is_handshaking>
285    #[no_mangle]
286    pub extern "C" fn rustls_connection_is_handshaking(conn: *const rustls_connection) -> bool {
287        ffi_panic_boundary! {
288            try_ref_from_ptr!(conn).is_handshaking()
289        }
290    }
291
292    /// Returns a `rustls_handshake_kind` describing the `rustls_connection`.
293    #[no_mangle]
294    pub extern "C" fn rustls_connection_handshake_kind(
295        conn: *const rustls_connection,
296    ) -> rustls_handshake_kind {
297        ffi_panic_boundary! {
298            try_ref_from_ptr!(conn)
299                .handshake_kind()
300                .map(Into::into)
301                .unwrap_or(rustls_handshake_kind::Unknown)
302        }
303    }
304
305    /// Sets a limit on the internal buffers used to buffer unsent plaintext (prior
306    /// to completing the TLS handshake) and unsent TLS records. By default, there
307    /// is no limit. The limit can be set at any time, even if the current buffer
308    /// use is higher.
309    /// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.set_buffer_limit>
310    #[no_mangle]
311    pub extern "C" fn rustls_connection_set_buffer_limit(conn: *mut rustls_connection, n: usize) {
312        ffi_panic_boundary! {
313            try_mut_from_ptr!(conn).set_buffer_limit(Some(n));
314        }
315    }
316
317    /// Queues a close_notify fatal alert to be sent in the next write_tls call.
318    /// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.send_close_notify>
319    #[no_mangle]
320    pub extern "C" fn rustls_connection_send_close_notify(conn: *mut rustls_connection) {
321        ffi_panic_boundary! {
322            try_mut_from_ptr!(conn).send_close_notify();
323        }
324    }
325
326    /// Queues a TLS1.3 key_update message to refresh a connection’s keys.
327    ///
328    /// Rustls internally manages key updates as required and so this function should
329    /// seldom be used. See the Rustls documentation for important caveats and suggestions
330    /// on occasions that merit its use.
331    ///
332    /// <https://docs.rs/rustls/latest/rustls/struct.ConnectionCommon.html#method.refresh_traffic_keys>
333    #[no_mangle]
334    pub extern "C" fn rustls_connection_refresh_traffic_keys(
335        conn: *mut rustls_connection,
336    ) -> rustls_result {
337        ffi_panic_boundary! {
338            match try_mut_from_ptr!(conn).refresh_traffic_keys() {
339                Ok(_) => rustls_result::Ok,
340                Err(e) => map_error(e),
341            }
342        }
343    }
344
345    /// Return the i-th certificate provided by the peer.
346    /// Index 0 is the end entity certificate. Higher indexes are certificates
347    /// in the chain. Requesting an index higher than what is available returns
348    /// NULL.
349    /// The returned pointer is valid until the next mutating function call
350    /// affecting the connection. A mutating function call is one where the
351    /// first argument has type `struct rustls_connection *` (as opposed to
352    ///  `const struct rustls_connection *`).
353    /// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.peer_certificates>
354    #[no_mangle]
355    pub extern "C" fn rustls_connection_get_peer_certificate<'a>(
356        conn: *const rustls_connection,
357        i: size_t,
358    ) -> *const rustls_certificate<'a> {
359        ffi_panic_boundary! {
360            match try_ref_from_ptr!(conn)
361                .peer_certificates()
362                .and_then(|c| c.get(i))
363            {
364                Some(cert) => cert as *const CertificateDer as *const _,
365                None => null(),
366            }
367        }
368    }
369
370    /// Get the ALPN protocol that was negotiated, if any. Stores a pointer to a
371    /// borrowed buffer of bytes, and that buffer's len, in the output parameters.
372    /// The borrow lives as long as the connection.
373    /// If the connection is still handshaking, or no ALPN protocol was negotiated,
374    /// stores NULL and 0 in the output parameters.
375    /// The provided pointer is valid until the next mutating function call
376    /// affecting the connection. A mutating function call is one where the
377    /// first argument has type `struct rustls_connection *` (as opposed to
378    ///  `const struct rustls_connection *`).
379    /// <https://www.iana.org/assignments/tls-parameters/>
380    /// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.alpn_protocol>
381    #[no_mangle]
382    pub extern "C" fn rustls_connection_get_alpn_protocol(
383        conn: *const rustls_connection,
384        protocol_out: *mut *const u8,
385        protocol_out_len: *mut usize,
386    ) {
387        ffi_panic_boundary! {
388            let conn = try_ref_from_ptr!(conn);
389            if protocol_out.is_null() || protocol_out_len.is_null() {
390                return;
391            }
392            match conn.alpn_protocol() {
393                Some(p) => unsafe {
394                    *protocol_out = p.as_ptr();
395                    *protocol_out_len = p.len();
396                },
397                None => unsafe {
398                    *protocol_out = null();
399                    *protocol_out_len = 0;
400                },
401            }
402        }
403    }
404
405    /// Return the TLS protocol version that has been negotiated. Before this
406    /// has been decided during the handshake, this will return 0. Otherwise,
407    /// the u16 version number as defined in the relevant RFC is returned.
408    /// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.protocol_version>
409    /// <https://docs.rs/rustls/latest/rustls/internal/msgs/enums/enum.ProtocolVersion.html>
410    #[no_mangle]
411    pub extern "C" fn rustls_connection_get_protocol_version(
412        conn: *const rustls_connection,
413    ) -> u16 {
414        ffi_panic_boundary! {
415            try_ref_from_ptr!(conn)
416                .protocol_version()
417                .map(u16::from)
418                .unwrap_or_default()
419        }
420    }
421
422    /// Retrieves the [IANA registered cipher suite identifier][IANA] agreed with the peer.
423    ///
424    /// This returns `TLS_NULL_WITH_NULL_NULL` (0x0000) until the ciphersuite is agreed.
425    ///
426    /// [IANA]: <https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4>
427    #[no_mangle]
428    pub extern "C" fn rustls_connection_get_negotiated_ciphersuite(
429        conn: *const rustls_connection,
430    ) -> u16 {
431        ffi_panic_boundary! {
432            try_ref_from_ptr!(conn)
433                .negotiated_cipher_suite()
434                .map(|cs| u16::from(cs.suite()))
435                .unwrap_or(u16::from(TLS_NULL_WITH_NULL_NULL))
436        }
437    }
438
439    /// Retrieves the cipher suite name agreed with the peer.
440    ///
441    /// This returns "" until the ciphersuite is agreed.
442    ///
443    /// The lifetime of the `rustls_str` is the lifetime of the program, it does not
444    /// need to be freed.
445    ///
446    /// <https://docs.rs/rustls/latest/rustls/enum.Connection.html#method.negotiated_cipher_suite>
447    #[no_mangle]
448    pub extern "C" fn rustls_connection_get_negotiated_ciphersuite_name(
449        conn: *const rustls_connection,
450    ) -> rustls_str<'static> {
451        ffi_panic_boundary! {
452            try_ref_from_ptr!(conn)
453                .negotiated_cipher_suite()
454                .and_then(|cs| rustls_str::try_from(cs.suite().as_str().unwrap_or_default()).ok())
455                .unwrap_or(rustls_str::from_str_unchecked(""))
456        }
457    }
458
459    /// Retrieves the [IANA registered supported group identifier][IANA] agreed with the peer.
460    ///
461    /// This returns Reserved (0x0000) until the key exchange group is agreed.
462    ///
463    /// [IANA]: <https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8>
464    #[no_mangle]
465    pub extern "C" fn rustls_connection_get_negotiated_key_exchange_group(
466        conn: *const rustls_connection,
467    ) -> u16 {
468        ffi_panic_boundary! {
469            try_ref_from_ptr!(conn)
470                .negotiated_key_exchange_group()
471                .map(|kxg| u16::from(kxg.name()))
472                .unwrap_or_default()
473        }
474    }
475
476    /// Retrieves the key exchange group name agreed with the peer.
477    ///
478    /// This returns "" until the key exchange group is agreed.
479    ///
480    /// The lifetime of the `rustls_str` is the lifetime of the program, it does not
481    /// need to be freed.
482    #[no_mangle]
483    pub extern "C" fn rustls_connection_get_negotiated_key_exchange_group_name(
484        conn: *const rustls_connection,
485    ) -> rustls_str<'static> {
486        ffi_panic_boundary! {
487            try_ref_from_ptr!(conn)
488                .negotiated_key_exchange_group()
489                .and_then(|kxg| rustls_str::try_from(kxg.name().as_str().unwrap_or_default()).ok())
490                .unwrap_or(rustls_str::from_str_unchecked(""))
491        }
492    }
493
494    /// Write up to `count` plaintext bytes from `buf` into the `rustls_connection`.
495    /// This will increase the number of output bytes available to
496    /// `rustls_connection_write_tls`.
497    /// On success, store the number of bytes actually written in *out_n
498    /// (this may be less than `count`).
499    /// <https://docs.rs/rustls/latest/rustls/struct.Writer.html#method.write>
500    #[no_mangle]
501    pub extern "C" fn rustls_connection_write(
502        conn: *mut rustls_connection,
503        buf: *const u8,
504        count: size_t,
505        out_n: *mut size_t,
506    ) -> rustls_result {
507        ffi_panic_boundary! {
508            let conn = try_mut_from_ptr!(conn);
509            let write_buf = try_slice!(buf, count);
510            if out_n.is_null() {
511                return rustls_result::NullParameter;
512            }
513            let n_written = match conn.writer().write(write_buf) {
514                Ok(n) => n,
515                Err(_) => return rustls_result::Io,
516            };
517            unsafe {
518                *out_n = n_written;
519            }
520            rustls_result::Ok
521        }
522    }
523
524    /// Read up to `count` plaintext bytes from the `rustls_connection` into `buf`.
525    /// On success, store the number of bytes read in *out_n (this may be less
526    /// than `count`). A success with *out_n set to 0 means "all bytes currently
527    /// available have been read, but more bytes may become available after
528    /// subsequent calls to rustls_connection_read_tls and
529    /// rustls_connection_process_new_packets."
530    ///
531    /// Subtle note: Even though this function only writes to `buf` and does not
532    /// read from it, the memory in `buf` must be initialized before the call (for
533    /// Rust-internal reasons). Initializing a buffer once and then using it
534    /// multiple times without zeroizing before each call is fine.
535    /// <https://docs.rs/rustls/latest/rustls/struct.Reader.html#method.read>
536    #[no_mangle]
537    pub extern "C" fn rustls_connection_read(
538        conn: *mut rustls_connection,
539        buf: *mut u8,
540        count: size_t,
541        out_n: *mut size_t,
542    ) -> rustls_result {
543        ffi_panic_boundary! {
544            let conn = try_mut_from_ptr!(conn);
545            if buf.is_null() {
546                return rustls_result::NullParameter;
547            }
548            if out_n.is_null() {
549                return rustls_result::NullParameter;
550            }
551
552            // Safety: the memory pointed at by buf must be initialized
553            // (required by documentation of this function).
554            let read_buf = try_slice_mut!(buf, count);
555
556            let n_read = match conn.reader().read(read_buf) {
557                Ok(n) => n,
558                Err(e) if e.kind() == ErrorKind::UnexpectedEof => {
559                    return rustls_result::UnexpectedEof;
560                }
561                Err(e) if e.kind() == ErrorKind::WouldBlock => {
562                    return rustls_result::PlaintextEmpty;
563                }
564                Err(_) => return rustls_result::Io,
565            };
566            unsafe {
567                *out_n = n_read;
568            }
569            rustls_result::Ok
570        }
571    }
572
573    /// Read up to `count` plaintext bytes from the `rustls_connection` into `buf`.
574    /// On success, store the number of bytes read in *out_n (this may be less
575    /// than `count`). A success with *out_n set to 0 means "all bytes currently
576    /// available have been read, but more bytes may become available after
577    /// subsequent calls to rustls_connection_read_tls and
578    /// rustls_connection_process_new_packets."
579    ///
580    /// This experimental API is only available when using a nightly Rust compiler
581    /// and enabling the `read_buf` Cargo feature. It will be deprecated and later
582    /// removed in future versions.
583    ///
584    /// Unlike with `rustls_connection_read`, this function may be called with `buf`
585    /// pointing to an uninitialized memory buffer.
586    #[cfg(feature = "read_buf")]
587    #[no_mangle]
588    pub extern "C" fn rustls_connection_read_2(
589        conn: *mut rustls_connection,
590        buf: *mut std::mem::MaybeUninit<u8>,
591        count: size_t,
592        out_n: *mut size_t,
593    ) -> rustls_result {
594        ffi_panic_boundary! {
595            let conn = try_mut_from_ptr!(conn);
596            if buf.is_null() || out_n.is_null() {
597                return rustls_result::NullParameter;
598            }
599            let mut read_buf: std::io::BorrowedBuf<'_> = try_slice_mut!(buf, count).into();
600
601            let n_read = match conn.reader().read_buf(read_buf.unfilled()) {
602                Ok(()) => read_buf.filled().len(),
603                Err(e) if e.kind() == ErrorKind::UnexpectedEof => {
604                    return rustls_result::UnexpectedEof;
605                }
606                Err(e) if e.kind() == ErrorKind::WouldBlock => {
607                    return rustls_result::PlaintextEmpty;
608                }
609                Err(_) => return rustls_result::Io,
610            };
611            unsafe {
612                *out_n = n_read;
613            }
614            rustls_result::Ok
615        }
616    }
617
618    /// Returns true if the `rustls_connection` was made with a `rustls_client_config`
619    /// or `rustls_server_config` that is FIPS compatible.
620    ///
621    /// This is different from `rustls_crypto_provider_fips` which is concerned
622    /// only with cryptography, whereas this also covers TLS-level configuration that NIST
623    /// recommends, as well as ECH HPKE suites if applicable.
624    #[no_mangle]
625    pub extern "C" fn rustls_connection_fips(conn: *const rustls_connection) -> bool {
626        ffi_panic_boundary! {
627            let conn = try_ref_from_ptr!(conn);
628            match &conn.conn {
629                rustls::Connection::Client(c) => c.fips(),
630                rustls::Connection::Server(c) => c.fips(),
631            }
632        }
633    }
634
635    /// Free a rustls_connection. Calling with NULL is fine.
636    /// Must not be called twice with the same value.
637    #[no_mangle]
638    pub extern "C" fn rustls_connection_free(conn: *mut rustls_connection) {
639        ffi_panic_boundary! {
640            free_box(conn);
641        }
642    }
643}