rustls_ffi/rslice.rs
1use std::marker::PhantomData;
2use std::ptr::null;
3use std::{fmt, slice, str};
4
5use libc::{c_char, size_t};
6
7/// A read-only view on a Rust byte slice.
8///
9/// This is used to pass data from rustls-ffi to callback functions provided
10/// by the user of the API.
11/// `len` indicates the number of bytes than can be safely read.
12///
13/// The memory exposed is available as specified by the function
14/// using this in its signature. For instance, when this is a parameter to a
15/// callback, the lifetime will usually be the duration of the callback.
16/// Functions that receive one of these must not dereference the data pointer
17/// beyond the allowed lifetime.
18#[repr(C)]
19pub struct rustls_slice_bytes<'a> {
20 pub data: *const u8,
21 pub len: size_t,
22 phantom: PhantomData<&'a [u8]>,
23}
24
25impl<'a> Default for rustls_slice_bytes<'a> {
26 fn default() -> rustls_slice_bytes<'a> {
27 Self {
28 data: &[0u8; 0] as *const u8,
29 len: 0,
30 phantom: PhantomData,
31 }
32 }
33}
34
35impl<'a> From<&'a [u8]> for rustls_slice_bytes<'a> {
36 fn from(s: &[u8]) -> Self {
37 rustls_slice_bytes {
38 data: s.as_ptr(),
39 len: s.len(),
40 phantom: PhantomData,
41 }
42 }
43}
44
45#[test]
46fn test_rustls_slice_bytes() {
47 let bytes = b"abcd";
48 let rsb: rustls_slice_bytes = bytes.as_ref().into();
49 unsafe {
50 assert_eq!(*rsb.data, b'a');
51 assert_eq!(*rsb.data.offset(3), b'd');
52 assert_eq!(rsb.len, 4);
53 }
54}
55
56/// A read-only view of a slice of Rust byte slices.
57///
58/// This is used to pass data from rustls-ffi to callback functions provided
59/// by the user of the API. Because Vec and slice are not `#[repr(C)]`, we
60/// provide access via a pointer to an opaque struct and an accessor method
61/// that acts on that struct to get entries of type `rustls_slice_bytes`.
62/// Internally, the pointee is a `&[&[u8]]`.
63///
64/// The memory exposed is available as specified by the function
65/// using this in its signature. For instance, when this is a parameter to a
66/// callback, the lifetime will usually be the duration of the callback.
67/// Functions that receive one of these must not call its methods beyond the
68/// allowed lifetime.
69pub struct rustls_slice_slice_bytes<'a> {
70 pub(crate) inner: &'a [&'a [u8]],
71}
72
73/// Return the length of the outer slice. If the input pointer is NULL,
74/// returns 0.
75#[no_mangle]
76pub extern "C" fn rustls_slice_slice_bytes_len(input: *const rustls_slice_slice_bytes) -> size_t {
77 match unsafe { input.as_ref() } {
78 Some(c) => c.inner.len(),
79 None => 0,
80 }
81}
82
83/// Retrieve the nth element from the input slice of slices.
84///
85/// If the input pointer is NULL, or n is greater than the length
86/// of the `rustls_slice_slice_bytes`, returns rustls_slice_bytes{NULL, 0}.
87#[no_mangle]
88pub extern "C" fn rustls_slice_slice_bytes_get(
89 input: *const rustls_slice_slice_bytes,
90 n: size_t,
91) -> rustls_slice_bytes {
92 let input = {
93 match unsafe { input.as_ref() } {
94 Some(c) => c,
95 None => {
96 return rustls_slice_bytes {
97 data: null(),
98 len: 0,
99 phantom: PhantomData,
100 };
101 }
102 }
103 };
104 match input.inner.get(n) {
105 Some(rsb) => (*rsb).into(),
106 None => rustls_slice_bytes {
107 data: null(),
108 len: 0,
109 phantom: PhantomData,
110 },
111 }
112}
113
114#[test]
115fn test_rustls_slice_slice_bytes() {
116 let many_bytes: Vec<&[u8]> = vec![b"abcd", b"", b"xyz"];
117 let rssb = rustls_slice_slice_bytes { inner: &many_bytes };
118
119 assert_eq!(rustls_slice_slice_bytes_len(&rssb), 3);
120
121 assert_eq!(rustls_slice_slice_bytes_get(&rssb, 0).len, 4);
122 assert_eq!(rustls_slice_slice_bytes_get(&rssb, 1).len, 0);
123 assert_ne!(rustls_slice_slice_bytes_get(&rssb, 1).data, null());
124 assert_eq!(rustls_slice_slice_bytes_get(&rssb, 2).len, 3);
125 assert_eq!(rustls_slice_slice_bytes_get(&rssb, 3).len, 0);
126 assert_eq!(rustls_slice_slice_bytes_get(&rssb, 3).data, null());
127
128 unsafe {
129 assert_eq!(*rustls_slice_slice_bytes_get(&rssb, 0).data, b'a');
130 assert_eq!(*rustls_slice_slice_bytes_get(&rssb, 0).data.offset(3), b'd');
131 assert_eq!(*rustls_slice_slice_bytes_get(&rssb, 2).data, b'x');
132 assert_eq!(*rustls_slice_slice_bytes_get(&rssb, 2).data.offset(2), b'z');
133 }
134}
135
136/// A read-only view on a Rust `&str`.
137///
138/// The contents are guaranteed to be valid UTF-8.
139///
140/// As an additional guarantee on top of Rust's normal UTF-8 guarantee,
141/// a `rustls_str` is guaranteed not to contain internal NUL bytes, so it is
142/// safe to interpolate into a C string or compare using strncmp. Keep in mind
143/// that it is not NUL-terminated.
144///
145/// The memory exposed is available as specified by the function
146/// using this in its signature. For instance, when this is a parameter to a
147/// callback, the lifetime will usually be the duration of the callback.
148/// Functions that receive one of these must not dereference the data pointer
149/// beyond the allowed lifetime.
150#[repr(C)]
151pub struct rustls_str<'a> {
152 pub data: *const c_char,
153 pub len: size_t,
154 phantom: PhantomData<&'a str>,
155}
156
157/// NulByte represents an error converting `&str` to `rustls_str` when the &str
158/// contains a NUL.
159#[derive(Debug)]
160pub struct NulByte {}
161
162impl<'a> TryFrom<&'a str> for rustls_str<'a> {
163 type Error = NulByte;
164
165 fn try_from(s: &str) -> Result<Self, Self::Error> {
166 if s.contains('\0') {
167 return Err(NulByte {});
168 }
169 Ok(rustls_str {
170 data: s.as_ptr() as *const c_char,
171 len: s.len(),
172 phantom: PhantomData,
173 })
174 }
175}
176
177impl Default for rustls_str<'_> {
178 fn default() -> rustls_str<'static> {
179 Self::from_str_unchecked("")
180 }
181}
182
183/// rustls_str represents a string that can be passed to C code.
184///
185/// The string should not have any internal NUL bytes and is not NUL terminated.
186/// C code should not create rustls_str objects, they should only be created in Rust
187/// code.
188impl rustls_str<'_> {
189 pub fn from_str_unchecked(s: &'static str) -> rustls_str<'static> {
190 rustls_str {
191 data: s.as_ptr() as *const _,
192 len: s.len(),
193 phantom: PhantomData,
194 }
195 }
196
197 /// Change a rustls_str's lifetime to 'static.
198 ///
199 /// This doesn't actually change how long the pointed-to data lives, but
200 /// is necessary when returning a rustls_str (as opposed to passing it
201 /// into a callback), because Rust can't figure out the "real" lifetime.
202 ///
203 /// # Safety
204 ///
205 /// The caller is responsible for requiring (usually via
206 /// documentation) that nothing uses the resulting rustls_str past its
207 /// actual validity period. The validity period is somewhat ill-defined
208 /// at present, but the Stacked Borrows experiment provides one definition,
209 /// by which a shared reference is valid until a mutable reference (to
210 /// the object or a parent object) is created.
211 pub unsafe fn into_static(self) -> rustls_str<'static> {
212 std::mem::transmute(self)
213 }
214
215 /// Change a rustls_str back to a &str.
216 ///
217 /// # Safety
218 ///
219 /// The caller must ensure the rustls_str data is valid utf8
220 pub unsafe fn to_str(&self) -> &str {
221 str::from_utf8_unchecked(slice::from_raw_parts(self.data as *const u8, self.len))
222 }
223}
224
225// If the assertion about Rust code being the only creator of rustls_str objects
226// changes, you must change this Debug impl, since the assertion in it no longer
227// holds.
228impl fmt::Debug for rustls_str<'_> {
229 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230 let raw = unsafe {
231 // Despite the use of "unsafe", we know that this is safe because:
232 // - self.data is a u8, and single bytes are aligned
233 // - the entire memory range is a single allocated object, because
234 // we always build rustls_str objects from a slice (though this may
235 // change if we accept rustls_str objects from C code)
236 // - all values are properly initialized because we only init
237 // rustls_str objects inside of Rust code
238 // - not larger than isize::MAX because, again, it's coming from Rust
239 slice::from_raw_parts(self.data as *const u8, self.len)
240 };
241 let s = str::from_utf8(raw).unwrap_or("%!(ERROR)");
242 f.debug_struct("rustls_str")
243 .field("data", &s)
244 .field("len", &self.len)
245 .finish()
246 }
247}
248
249#[test]
250fn test_rustls_str() {
251 let s = "abcd";
252 let rs: rustls_str = s.try_into().unwrap();
253 assert_eq!(rs.len, 4);
254 unsafe {
255 assert_eq!(*rs.data, 'a' as c_char);
256 assert_eq!(*rs.data.offset(3), 'd' as c_char);
257 }
258 let rs = unsafe { rs.to_str() };
259 assert_eq!(rs, s);
260}
261
262#[cfg(test)]
263mod tests {
264 use crate::rslice::*;
265
266 #[test]
267 fn test_rustls_str_debug() {
268 let s = "abcd";
269 let rs: rustls_str = s.try_into().unwrap();
270 assert_eq!(
271 format!("{:?}", rs),
272 r#"rustls_str { data: "abcd", len: 4 }"#
273 );
274 }
275}
276
277#[test]
278fn test_rustls_str_rejects_nul() {
279 assert!(matches!(rustls_str::try_from("\0"), Err(NulByte {})));
280 assert!(matches!(rustls_str::try_from("abc\0"), Err(NulByte {})));
281 assert!(matches!(rustls_str::try_from("ab\0cd"), Err(NulByte {})));
282}
283
284/// A read-only view of a slice of multiple Rust `&str`'s (that is, multiple
285/// strings).
286///
287/// Like `rustls_str`, this guarantees that each string contains
288/// UTF-8 and no NUL bytes. Strings are not NUL-terminated.
289///
290/// This is used to pass data from rustls-ffi to callback functions provided
291/// by the user of the API. Because Vec and slice are not `#[repr(C)]`, we
292/// can't provide a straightforward `data` and `len` structure. Instead, we
293/// provide access via a pointer to an opaque struct and accessor methods.
294/// Internally, the pointee is a `&[&str]`.
295///
296/// The memory exposed is available as specified by the function
297/// using this in its signature. For instance, when this is a parameter to a
298/// callback, the lifetime will usually be the duration of the callback.
299/// Functions that receive one of these must not call its methods beyond the
300/// allowed lifetime.
301pub struct rustls_slice_str<'a> {
302 pub(crate) inner: &'a [&'a str],
303}
304
305/// Return the length of the outer slice.
306///
307/// If the input pointer is NULL, returns 0.
308#[no_mangle]
309pub extern "C" fn rustls_slice_str_len(input: *const rustls_slice_str) -> size_t {
310 unsafe {
311 match input.as_ref() {
312 Some(c) => c.inner.len(),
313 None => 0,
314 }
315 }
316}
317
318/// Retrieve the nth element from the input slice of `&str`s.
319///
320/// If the input pointer is NULL, or n is greater than the length of the
321/// rustls_slice_str, returns rustls_str{NULL, 0}.
322#[no_mangle]
323pub extern "C" fn rustls_slice_str_get(input: *const rustls_slice_str, n: size_t) -> rustls_str {
324 let input: &rustls_slice_str = unsafe {
325 match input.as_ref() {
326 Some(c) => c,
327 None => {
328 return rustls_str {
329 data: null(),
330 len: 0,
331 phantom: PhantomData,
332 };
333 }
334 }
335 };
336 input
337 .inner
338 .get(n)
339 .and_then(|&s| s.try_into().ok())
340 .unwrap_or(rustls_str {
341 data: null(),
342 len: 0,
343 phantom: PhantomData,
344 })
345}
346
347#[test]
348fn test_rustls_slice_str() {
349 let many_strings = vec!["abcd", "", "xyz"];
350 let rss = rustls_slice_str {
351 inner: &many_strings,
352 };
353
354 assert_eq!(rustls_slice_str_len(&rss), 3);
355
356 assert_eq!(rustls_slice_str_get(&rss, 0).len, 4);
357 assert_eq!(rustls_slice_str_get(&rss, 1).len, 0);
358 assert_ne!(rustls_slice_str_get(&rss, 1).data, null());
359 assert_eq!(rustls_slice_str_get(&rss, 2).len, 3);
360 assert_eq!(rustls_slice_str_get(&rss, 3).len, 0);
361 assert_eq!(rustls_slice_str_get(&rss, 3).data, null());
362
363 unsafe {
364 assert_eq!(*rustls_slice_str_get(&rss, 0).data, 'a' as c_char);
365 assert_eq!(*rustls_slice_str_get(&rss, 0).data.offset(3), 'd' as c_char);
366 assert_eq!(*rustls_slice_str_get(&rss, 2).data, 'x' as c_char);
367 assert_eq!(*rustls_slice_str_get(&rss, 2).data.offset(2), 'z' as c_char);
368 }
369}
370
371/// A read-only view on a Rust slice of 16-bit integers in platform endianness.
372///
373/// This is used to pass data from rustls-ffi to callback functions provided
374/// by the user of the API.
375/// `len` indicates the number of bytes than can be safely read.
376///
377/// The memory exposed is available as specified by the function
378/// using this in its signature. For instance, when this is a parameter to a
379/// callback, the lifetime will usually be the duration of the callback.
380/// Functions that receive one of these must not dereference the data pointer
381/// beyond the allowed lifetime.
382#[repr(C)]
383pub struct rustls_slice_u16<'a> {
384 pub data: *const u16,
385 pub len: size_t,
386 phantom: PhantomData<&'a [u16]>,
387}
388
389impl<'a> From<&'a [u16]> for rustls_slice_u16<'a> {
390 fn from(s: &[u16]) -> Self {
391 rustls_slice_u16 {
392 data: s.as_ptr(),
393 len: s.len(),
394 phantom: PhantomData,
395 }
396 }
397}
398
399#[test]
400fn test_rustls_slice_u16() {
401 let u16s = vec![101, 314, 2718];
402 let rsu: rustls_slice_u16 = (&*u16s).into();
403 assert_eq!(rsu.len, 3);
404 unsafe {
405 assert_eq!(*rsu.data, 101);
406 assert_eq!(*rsu.data.offset(1), 314);
407 assert_eq!(*rsu.data.offset(2), 2718);
408 }
409}