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