Skip to main content

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