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}