wayland_commons/
wire.rs

1//! Types and routines used to manipulate arguments from the wire format
2
3use std::ffi::{CStr, CString};
4use std::os::unix::io::RawFd;
5use std::ptr;
6
7use nix::{Error as NixError, Result as NixResult};
8
9use smallvec::SmallVec;
10
11// The value of 4 is chosen for the following reasons:
12// - almost all messages have 4 arguments or less
13// - there are some potentially spammy events that have 3/4 arguments (wl_touch.move has 4 for example)
14//
15// This brings the size of Message to 11*usize (instead of 4*usize with a regular vec), but eliminates
16// almost all allocations that may occur during the processing of messages, both client-side and server-side.
17const INLINE_ARGS: usize = 4;
18
19/// Wire metadata of a given message
20#[derive(Copy, Clone, Debug)]
21pub struct MessageDesc {
22    /// Name of this message
23    pub name: &'static str,
24    /// Signature of the message
25    pub signature: &'static [ArgumentType],
26    /// Minimum required version of the interface
27    pub since: u32,
28    /// Whether this message is a destructor
29    pub destructor: bool,
30}
31
32/// Enum of possible argument types as recognized by the wire
33#[derive(Copy, Clone, PartialEq, Debug)]
34pub enum ArgumentType {
35    /// i32
36    Int,
37    /// u32
38    Uint,
39    /// fixed point, 1/256 precision
40    Fixed,
41    /// CString
42    Str,
43    /// id of a wayland object
44    Object,
45    /// id of a newly created wayland object
46    NewId,
47    /// Vec<u8>
48    Array,
49    /// RawFd
50    Fd,
51}
52
53/// Enum of possible argument as recognized by the wire, including values
54#[derive(Clone, PartialEq, Debug)]
55#[allow(clippy::box_collection)]
56pub enum Argument {
57    /// i32
58    Int(i32),
59    /// u32
60    Uint(u32),
61    /// fixed point, 1/256 precision
62    Fixed(i32),
63    /// CString
64    ///
65    /// The value is boxed to reduce the stack size of Argument. The performance
66    /// impact is negligible as `string` arguments are pretty rare in the protocol.
67    Str(Box<CString>),
68    /// id of a wayland object
69    Object(u32),
70    /// id of a newly created wayland object
71    NewId(u32),
72    /// Vec<u8>
73    ///
74    /// The value is boxed to reduce the stack size of Argument. The performance
75    /// impact is negligible as `array` arguments are pretty rare in the protocol.
76    Array(Box<Vec<u8>>),
77    /// RawFd
78    Fd(RawFd),
79}
80
81impl Argument {
82    /// Retrieve the type of a given argument instance
83    pub fn get_type(&self) -> ArgumentType {
84        match *self {
85            Argument::Int(_) => ArgumentType::Int,
86            Argument::Uint(_) => ArgumentType::Uint,
87            Argument::Fixed(_) => ArgumentType::Fixed,
88            Argument::Str(_) => ArgumentType::Str,
89            Argument::Object(_) => ArgumentType::Object,
90            Argument::NewId(_) => ArgumentType::NewId,
91            Argument::Array(_) => ArgumentType::Array,
92            Argument::Fd(_) => ArgumentType::Fd,
93        }
94    }
95}
96
97impl std::fmt::Display for Argument {
98    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99        match self {
100            Argument::Int(value) => write!(f, "{}", value),
101            Argument::Uint(value) => write!(f, "{}", value),
102            Argument::Fixed(value) => write!(f, "{}", value),
103            Argument::Str(value) => write!(f, "{:?}", value),
104            Argument::Object(value) => write!(f, "{}", value),
105            Argument::NewId(value) => write!(f, "{}", value),
106            Argument::Array(value) => write!(f, "{:?}", value),
107            Argument::Fd(value) => write!(f, "{}", value),
108        }
109    }
110}
111
112/// A wire message
113#[derive(Debug, Clone, PartialEq)]
114pub struct Message {
115    /// ID of the object sending this message
116    pub sender_id: u32,
117    /// Opcode of the message
118    pub opcode: u16,
119    /// Arguments of the message
120    pub args: SmallVec<[Argument; INLINE_ARGS]>,
121}
122
123/// Error generated when trying to serialize a message into buffers
124#[derive(Debug, Clone)]
125pub enum MessageWriteError {
126    /// The buffer is too small to hold the message contents
127    BufferTooSmall,
128    /// The message contains a FD that could not be dup-ed
129    DupFdFailed(::nix::Error),
130}
131
132impl std::error::Error for MessageWriteError {}
133
134impl std::fmt::Display for MessageWriteError {
135    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
136        match *self {
137            MessageWriteError::BufferTooSmall => {
138                f.write_str("The provided buffer is too small to hold message content.")
139            }
140            MessageWriteError::DupFdFailed(_) => {
141                f.write_str("The message contains a file descriptor that could not be dup()-ed.")
142            }
143        }
144    }
145}
146
147/// Error generated when trying to deserialize a message from buffers
148#[derive(Debug, Clone)]
149pub enum MessageParseError {
150    /// The message references a FD but the buffer FD is empty
151    MissingFD,
152    /// More data is needed to deserialize the message
153    MissingData,
154    /// The message is malformed and cannot be parsed
155    Malformed,
156}
157
158impl std::error::Error for MessageParseError {}
159
160impl std::fmt::Display for MessageParseError {
161    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
162        match *self {
163            MessageParseError::MissingFD => {
164                f.write_str("The message references a FD but the buffer FD is empty.")
165            }
166            MessageParseError::MissingData => {
167                f.write_str("More data is needed to deserialize the message")
168            }
169            MessageParseError::Malformed => {
170                f.write_str("The message is malformed and cannot be parsed")
171            }
172        }
173    }
174}
175
176impl Message {
177    /// Serialize the contents of this message into provided buffers
178    ///
179    /// Returns the number of elements written in each buffer
180    ///
181    /// Any serialized Fd will be `dup()`-ed in the process
182    pub fn write_to_buffers(
183        &self,
184        payload: &mut [u32],
185        mut fds: &mut [RawFd],
186    ) -> Result<(usize, usize), MessageWriteError> {
187        let orig_payload_len = payload.len();
188        let orig_fds_len = fds.len();
189        // Helper function to write a u32 or a RawFd to its buffer
190        fn write_buf<T>(u: T, payload: &mut [T]) -> Result<&mut [T], MessageWriteError> {
191            if let Some((head, tail)) = payload.split_first_mut() {
192                *head = u;
193                Ok(tail)
194            } else {
195                Err(MessageWriteError::BufferTooSmall)
196            }
197        }
198
199        // Helper function to write byte arrays in payload
200        fn write_array_to_payload<'a>(
201            array: &[u8],
202            payload: &'a mut [u32],
203        ) -> Result<&'a mut [u32], MessageWriteError> {
204            let array_len = array.len();
205            let word_len = array_len / 4 + if array_len % 4 != 0 { 1 } else { 0 };
206            // need enough space to store the whole array with padding and a size header
207            if payload.len() < 1 + word_len {
208                return Err(MessageWriteError::BufferTooSmall);
209            }
210            // size header
211            payload[0] = array_len as u32;
212            let (buffer_slice, rest) = payload[1..].split_at_mut(word_len);
213            unsafe {
214                ptr::copy(array.as_ptr(), buffer_slice.as_mut_ptr() as *mut u8, array_len);
215            }
216            Ok(rest)
217        }
218
219        let free_size = payload.len();
220        if free_size < 2 {
221            return Err(MessageWriteError::BufferTooSmall);
222        }
223
224        let (header, mut payload) = payload.split_at_mut(2);
225
226        // we store all fds we dup-ed in this, which will auto-close
227        // them on drop, if any of the `?` early-returns
228        let mut pending_fds = FdStore::new();
229
230        // write the contents in the buffer
231        for arg in &self.args {
232            // Just to make the borrow checker happy
233            let old_payload = payload;
234            match *arg {
235                Argument::Int(i) => payload = write_buf(i as u32, old_payload)?,
236                Argument::Uint(u) => payload = write_buf(u, old_payload)?,
237                Argument::Fixed(f) => payload = write_buf(f as u32, old_payload)?,
238                Argument::Str(ref s) => {
239                    payload = write_array_to_payload(s.as_bytes_with_nul(), old_payload)?;
240                }
241                Argument::Object(o) => payload = write_buf(o, old_payload)?,
242                Argument::NewId(n) => payload = write_buf(n, old_payload)?,
243                Argument::Array(ref a) => {
244                    payload = write_array_to_payload(a, old_payload)?;
245                }
246                Argument::Fd(fd) => {
247                    let old_fds = fds;
248                    let dup_fd = dup_fd_cloexec(fd).map_err(MessageWriteError::DupFdFailed)?;
249                    pending_fds.push(dup_fd);
250                    fds = write_buf(dup_fd, old_fds)?;
251                    payload = old_payload;
252                }
253            }
254        }
255
256        // we reached here, all writing was successful
257        // no FD needs to be closed
258        pending_fds.clear();
259
260        let wrote_size = (free_size - payload.len()) * 4;
261        header[0] = self.sender_id;
262        header[1] = ((wrote_size as u32) << 16) | u32::from(self.opcode);
263        Ok((orig_payload_len - payload.len(), orig_fds_len - fds.len()))
264    }
265
266    /// Attempts to parse a single wayland message with the given signature.
267    ///
268    /// If the buffers contains several messages, only the first one will be parsed,
269    /// and the unused tail of the buffers is returned. If a single message was present,
270    /// the returned slices should thus be empty.
271    ///
272    /// Errors if the message is malformed.
273    pub fn from_raw<'a, 'b>(
274        raw: &'a [u32],
275        signature: &[ArgumentType],
276        fds: &'b [RawFd],
277    ) -> Result<(Message, &'a [u32], &'b [RawFd]), MessageParseError> {
278        // helper function to read arrays
279        fn read_array_from_payload(
280            array_len: usize,
281            payload: &[u32],
282        ) -> Result<(&[u8], &[u32]), MessageParseError> {
283            let word_len = array_len / 4 + if array_len % 4 != 0 { 1 } else { 0 };
284            if word_len > payload.len() {
285                return Err(MessageParseError::MissingData);
286            }
287            let (array_contents, rest) = payload.split_at(word_len);
288            let array = unsafe {
289                ::std::slice::from_raw_parts(array_contents.as_ptr() as *const u8, array_len)
290            };
291            Ok((array, rest))
292        }
293
294        if raw.len() < 2 {
295            return Err(MessageParseError::MissingData);
296        }
297
298        let sender_id = raw[0];
299        let word_2 = raw[1];
300        let opcode = (word_2 & 0x0000_FFFF) as u16;
301        let len = (word_2 >> 16) as usize / 4;
302
303        if len < 2 || len > raw.len() {
304            return Err(MessageParseError::Malformed);
305        }
306
307        let (mut payload, rest) = raw.split_at(len);
308        payload = &payload[2..];
309        let mut fds = fds;
310
311        let arguments = signature
312            .iter()
313            .map(|argtype| {
314                if let ArgumentType::Fd = *argtype {
315                    // don't consume input but fd
316                    if let Some((&front, tail)) = fds.split_first() {
317                        fds = tail;
318                        Ok(Argument::Fd(front))
319                    } else {
320                        Err(MessageParseError::MissingFD)
321                    }
322                } else if let Some((&front, mut tail)) = payload.split_first() {
323                    let arg = match *argtype {
324                        ArgumentType::Int => Ok(Argument::Int(front as i32)),
325                        ArgumentType::Uint => Ok(Argument::Uint(front)),
326                        ArgumentType::Fixed => Ok(Argument::Fixed(front as i32)),
327                        ArgumentType::Str => read_array_from_payload(front as usize, tail)
328                            .and_then(|(v, rest)| {
329                                tail = rest;
330                                match CStr::from_bytes_with_nul(v) {
331                                    Ok(s) => Ok(Argument::Str(Box::new(s.into()))),
332                                    Err(_) => Err(MessageParseError::Malformed),
333                                }
334                            }),
335                        ArgumentType::Object => Ok(Argument::Object(front)),
336                        ArgumentType::NewId => Ok(Argument::NewId(front)),
337                        ArgumentType::Array => {
338                            read_array_from_payload(front as usize, tail).map(|(v, rest)| {
339                                tail = rest;
340                                Argument::Array(Box::new(v.into()))
341                            })
342                        }
343                        ArgumentType::Fd => unreachable!(),
344                    };
345                    payload = tail;
346                    arg
347                } else {
348                    Err(MessageParseError::MissingData)
349                }
350            })
351            .collect::<Result<SmallVec<_>, MessageParseError>>()?;
352
353        let msg = Message { sender_id, opcode, args: arguments };
354        Ok((msg, rest, fds))
355    }
356}
357
358/// Duplicate a `RawFd` and set the CLOEXEC flag on the copy
359pub fn dup_fd_cloexec(fd: RawFd) -> NixResult<RawFd> {
360    use nix::fcntl;
361    match fcntl::fcntl(fd, fcntl::FcntlArg::F_DUPFD_CLOEXEC(0)) {
362        Ok(newfd) => Ok(newfd),
363        Err(NixError::EINVAL) => {
364            // F_DUPFD_CLOEXEC is not recognized, kernel too old, fallback
365            // to setting CLOEXEC manually
366            let newfd = fcntl::fcntl(fd, fcntl::FcntlArg::F_DUPFD(0))?;
367
368            let flags = fcntl::fcntl(newfd, fcntl::FcntlArg::F_GETFD);
369            let result = flags
370                .map(|f| fcntl::FdFlag::from_bits(f).unwrap() | fcntl::FdFlag::FD_CLOEXEC)
371                .and_then(|f| fcntl::fcntl(newfd, fcntl::FcntlArg::F_SETFD(f)));
372            match result {
373                Ok(_) => {
374                    // setting the O_CLOEXEC worked
375                    Ok(newfd)
376                }
377                Err(e) => {
378                    // something went wrong in F_GETFD or F_SETFD
379                    let _ = ::nix::unistd::close(newfd);
380                    Err(e)
381                }
382            }
383        }
384        Err(e) => Err(e),
385    }
386}
387
388/*
389 * utility struct that closes every FD it contains on drop
390 */
391
392struct FdStore {
393    fds: Vec<RawFd>,
394}
395
396impl FdStore {
397    fn new() -> FdStore {
398        FdStore { fds: Vec::new() }
399    }
400    fn push(&mut self, fd: RawFd) {
401        self.fds.push(fd);
402    }
403    fn clear(&mut self) {
404        self.fds.clear();
405    }
406}
407
408impl Drop for FdStore {
409    fn drop(&mut self) {
410        use nix::unistd::close;
411        for fd in self.fds.drain(..) {
412            // not much can be done if we can't close that anyway...
413            let _ = close(fd);
414        }
415    }
416}
417
418#[cfg(test)]
419mod tests {
420    use super::*;
421    use smallvec::smallvec;
422
423    #[test]
424    fn into_from_raw_cycle() {
425        let mut bytes_buffer = vec![0; 1024];
426        let mut fd_buffer = vec![0; 10];
427
428        let msg = Message {
429            sender_id: 42,
430            opcode: 7,
431            args: smallvec![
432                Argument::Uint(3),
433                Argument::Fixed(-89),
434                Argument::Str(Box::new(CString::new(&b"I like trains!"[..]).unwrap())),
435                Argument::Array(vec![1, 2, 3, 4, 5, 6, 7, 8, 9].into()),
436                Argument::Object(88),
437                Argument::NewId(56),
438                Argument::Int(-25),
439            ],
440        };
441        // write the message to the buffers
442        msg.write_to_buffers(&mut bytes_buffer[..], &mut fd_buffer[..]).unwrap();
443        // read them back
444        let (rebuilt, _, _) = Message::from_raw(
445            &bytes_buffer[..],
446            &[
447                ArgumentType::Uint,
448                ArgumentType::Fixed,
449                ArgumentType::Str,
450                ArgumentType::Array,
451                ArgumentType::Object,
452                ArgumentType::NewId,
453                ArgumentType::Int,
454            ],
455            &fd_buffer[..],
456        )
457        .unwrap();
458        assert_eq!(rebuilt, msg);
459    }
460}