rustls_ffi/
io.rs

1use std::io::{Error, IoSlice, Read, Result, Write};
2
3use libc::{c_void, size_t};
4
5use crate::error::rustls_io_result;
6
7/// A callback for `rustls_connection_read_tls`.
8///
9/// An implementation of this callback should attempt to read up to n bytes from the
10/// network, storing them in `buf`. If any bytes were stored, the implementation should
11/// set out_n to the number of bytes stored and return 0.
12///
13/// If there was an error, the implementation should return a nonzero rustls_io_result,
14/// which will be passed through to the caller.
15///
16/// On POSIX systems, returning `errno` is convenient.
17///
18/// On other systems, any appropriate error code works.
19///
20/// It's best to make one read attempt to the network per call. Additional reads will
21/// be triggered by subsequent calls to one of the `_read_tls` methods.
22///
23/// `userdata` is set to the value provided to `rustls_connection_set_userdata`.
24/// In most cases that should be a struct that contains, at a minimum, a file descriptor.
25///
26/// The buf and out_n pointers are borrowed and should not be retained across calls.
27pub type rustls_read_callback = Option<
28    unsafe extern "C" fn(
29        userdata: *mut c_void,
30        buf: *mut u8,
31        n: size_t,
32        out_n: *mut size_t,
33    ) -> rustls_io_result,
34>;
35
36pub(crate) type ReadCallback = unsafe extern "C" fn(
37    userdata: *mut c_void,
38    buf: *mut u8,
39    n: size_t,
40    out_n: *mut size_t,
41) -> rustls_io_result;
42
43pub(crate) struct CallbackReader {
44    pub callback: ReadCallback,
45    pub userdata: *mut c_void,
46}
47
48impl Read for CallbackReader {
49    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
50        let mut out_n = 0;
51        let cb = self.callback;
52        let result = unsafe { cb(self.userdata, buf.as_mut_ptr(), buf.len(), &mut out_n) };
53        match result.0 {
54            0 => Ok(out_n),
55            e => Err(Error::from_raw_os_error(e)),
56        }
57    }
58}
59
60/// A callback for `rustls_connection_write_tls` or `rustls_accepted_alert_write_tls`.
61///
62/// An implementation of this callback should attempt to write the `n` bytes in buf
63/// to the network.
64///
65/// If any bytes were written, the implementation should set `out_n` to the number of
66/// bytes stored and return 0.
67///
68/// If there was an error, the implementation should return a nonzero `rustls_io_result`,
69/// which will be passed through to the caller.
70///
71/// On POSIX systems, returning `errno` is convenient.
72///
73/// On other systems, any appropriate error code works.
74///
75/// It's best to make one write attempt to the network per call. Additional writes will
76/// be triggered by subsequent calls to rustls_connection_write_tls.
77///
78/// `userdata` is set to the value provided to `rustls_connection_set_userdata`. In most
79/// cases that should be a struct that contains, at a minimum, a file descriptor.
80///
81/// The buf and out_n pointers are borrowed and should not be retained across calls.
82pub type rustls_write_callback = Option<
83    unsafe extern "C" fn(
84        userdata: *mut c_void,
85        buf: *const u8,
86        n: size_t,
87        out_n: *mut size_t,
88    ) -> rustls_io_result,
89>;
90
91pub(crate) type WriteCallback = unsafe extern "C" fn(
92    userdata: *mut c_void,
93    buf: *const u8,
94    n: size_t,
95    out_n: *mut size_t,
96) -> rustls_io_result;
97
98pub(crate) struct CallbackWriter {
99    pub callback: WriteCallback,
100    pub userdata: *mut c_void,
101}
102
103impl Write for CallbackWriter {
104    fn write(&mut self, buf: &[u8]) -> Result<usize> {
105        let mut out_n = 0;
106        let cb = self.callback;
107        let result = unsafe { cb(self.userdata, buf.as_ptr(), buf.len(), &mut out_n) };
108        match result.0 {
109            0 => Ok(out_n),
110            e => Err(Error::from_raw_os_error(e)),
111        }
112    }
113
114    fn flush(&mut self) -> Result<()> {
115        Ok(())
116    }
117}
118
119/// An alias for `struct iovec` from uio.h (on Unix) or `WSABUF` on Windows.
120///
121/// You should cast `const struct rustls_iovec *` to `const struct iovec *` on
122/// Unix, or `const *LPWSABUF` on Windows. See [`std::io::IoSlice`] for details
123/// on interoperability with platform specific vectored IO.
124pub struct rustls_iovec {
125    _private: [u8; 0],
126}
127
128/// A callback for `rustls_connection_write_tls_vectored`.
129///
130/// An implementation of this callback should attempt to write the bytes in
131/// the given `count` iovecs to the network.
132///
133/// If any bytes were written, the implementation should set out_n to the number of
134/// bytes written and return 0.
135///
136/// If there was an error, the implementation should return a nonzero rustls_io_result,
137/// which will be passed through to the caller.
138///
139/// On POSIX systems, returning `errno` is convenient.
140///
141/// On other systems, any appropriate error code works.
142///
143/// It's best to make one write attempt to the network per call. Additional write will
144/// be triggered by subsequent calls to one of the `_write_tls` methods.
145///
146/// `userdata` is set to the value provided to `rustls_*_session_set_userdata`. In most
147/// cases that should be a struct that contains, at a minimum, a file descriptor.
148///
149/// The iov and out_n pointers are borrowed and should not be retained across calls.
150pub type rustls_write_vectored_callback = Option<
151    unsafe extern "C" fn(
152        userdata: *mut c_void,
153        iov: *const rustls_iovec,
154        count: size_t,
155        out_n: *mut size_t,
156    ) -> rustls_io_result,
157>;
158
159pub(crate) type VectoredWriteCallback = unsafe extern "C" fn(
160    userdata: *mut c_void,
161    iov: *const rustls_iovec,
162    count: size_t,
163    out_n: *mut size_t,
164) -> rustls_io_result;
165
166pub(crate) struct VectoredCallbackWriter {
167    pub callback: VectoredWriteCallback,
168    pub userdata: *mut c_void,
169}
170
171impl Write for VectoredCallbackWriter {
172    fn write(&mut self, buf: &[u8]) -> Result<usize> {
173        self.write_vectored(&[IoSlice::new(buf)])
174    }
175
176    fn flush(&mut self) -> Result<()> {
177        Ok(())
178    }
179
180    fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> {
181        let mut out_n = 0;
182        let cb = self.callback;
183        let result = unsafe {
184            cb(
185                self.userdata,
186                // This cast is sound because IoSlice is documented to by ABI-compatible with
187                // iovec on Unix, and with WSABUF on Windows.
188                bufs.as_ptr() as *const rustls_iovec,
189                bufs.len(),
190                &mut out_n,
191            )
192        };
193        match result.0 {
194            0 => Ok(out_n),
195            e => Err(Error::from_raw_os_error(e)),
196        }
197    }
198}