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}