Skip to main content

openssl_ktls/
stream.rs

1use std::os::fd::AsRawFd;
2
3use foreign_types_shared::ForeignType;
4
5use crate::ffi::BIO_NOCLOSE;
6
7pub struct SslStream {
8    _tcp: std::net::TcpStream,
9    ssl: openssl::ssl::Ssl,
10}
11impl SslStream {
12    /// Create a new SslStream from a tcp stream and SSL object.
13    pub fn new(tcp: std::net::TcpStream, ssl: openssl::ssl::Ssl) -> Self {
14        let sock_bio = unsafe { openssl_sys::BIO_new_socket(tcp.as_raw_fd(), BIO_NOCLOSE) };
15        assert!(!sock_bio.is_null(), "Failed to create socket BIO");
16        unsafe {
17            openssl_sys::SSL_set_bio(ssl.as_ptr(), sock_bio, sock_bio);
18        }
19        SslStream { _tcp: tcp, ssl }
20    }
21
22    /// Synchronous connect method (kept for backward compatibility)
23    pub fn connect(&self) -> Result<(), crate::error::Error> {
24        let result = unsafe { openssl_sys::SSL_connect(self.ssl.as_ptr()) };
25        if result <= 0 {
26            Err(crate::error::Error::make(result, self.ssl()))
27        } else {
28            Ok(())
29        }
30    }
31
32    pub fn accept(&self) -> Result<(), crate::error::Error> {
33        let result = unsafe { openssl_sys::SSL_accept(self.ssl.as_ptr()) };
34        if result <= 0 {
35            Err(crate::error::Error::make(result, self.ssl()))
36        } else {
37            Ok(())
38        }
39    }
40
41    pub fn ssl(&self) -> &openssl::ssl::Ssl {
42        &self.ssl
43    }
44
45    pub fn shutdown(&self) -> Result<openssl::ssl::ShutdownResult, crate::error::Error> {
46        let result = unsafe { openssl_sys::SSL_shutdown(self.ssl.as_ptr()) };
47        match result {
48            1 => {
49                // Clean shutdown completed
50                Ok(openssl::ssl::ShutdownResult::Received)
51            }
52            0 => {
53                // First phase of shutdown completed, need to wait for peer's close_notify
54                // For simplicity, we'll consider this complete
55                Ok(openssl::ssl::ShutdownResult::Sent)
56            }
57            i => Err(crate::error::Error::make(i, self.ssl())),
58        }
59    }
60
61    pub fn ktls_send_enabled(&self) -> bool {
62        unsafe {
63            let wbio = openssl_sys::SSL_get_wbio(self.ssl.as_ptr());
64            crate::ffi::BIO_get_ktls_send(wbio) != 0
65        }
66    }
67
68    pub fn ktls_recv_enabled(&self) -> bool {
69        unsafe {
70            let rbio = openssl_sys::SSL_get_rbio(self.ssl.as_ptr());
71            crate::ffi::BIO_get_ktls_recv(rbio) != 0
72        }
73    }
74}
75
76impl std::io::Read for SslStream {
77    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
78        let mut bytes_read = 0;
79        unsafe {
80            let ret = openssl_sys::SSL_read_ex(
81                self.ssl.as_ptr(),
82                buf.as_mut_ptr() as *mut _,
83                buf.len(),
84                &mut bytes_read,
85            );
86            if ret < 0 {
87                Err(std::io::Error::other(crate::error::Error::make(
88                    ret,
89                    self.ssl(),
90                )))
91            } else {
92                Ok(bytes_read)
93            }
94        }
95    }
96}
97
98impl std::io::Write for SslStream {
99    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
100        if buf.is_empty() {
101            return Ok(0);
102        }
103
104        unsafe {
105            let len = openssl_sys::SSL_write(
106                self.ssl.as_ptr(),
107                buf.as_ptr() as *const _,
108                buf.len().try_into().unwrap(),
109            );
110            if len < 0 {
111                Err(std::io::Error::other(crate::error::Error::make(
112                    len,
113                    self.ssl(),
114                )))
115            } else {
116                Ok(len as usize)
117            }
118        }
119    }
120
121    fn flush(&mut self) -> std::io::Result<()> {
122        Ok(())
123    }
124}
125
126impl Drop for SslStream {
127    fn drop(&mut self) {
128        // The BIO is automatically freed when SSL_free is called on the SSL object,
129        // so we don't need to manually free the BIO here. The SSL object will be
130        // dropped automatically via its Drop implementation.
131        //
132        // Note: We used SSL_set_bio(ssl, bio, bio) which means both read and write
133        // BIOs point to the same BIO object, and SSL takes ownership of it.
134    }
135}