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}