1use 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
11const INLINE_ARGS: usize = 4;
18
19#[derive(Copy, Clone, Debug)]
21pub struct MessageDesc {
22 pub name: &'static str,
24 pub signature: &'static [ArgumentType],
26 pub since: u32,
28 pub destructor: bool,
30}
31
32#[derive(Copy, Clone, PartialEq, Debug)]
34pub enum ArgumentType {
35 Int,
37 Uint,
39 Fixed,
41 Str,
43 Object,
45 NewId,
47 Array,
49 Fd,
51}
52
53#[derive(Clone, PartialEq, Debug)]
55#[allow(clippy::box_collection)]
56pub enum Argument {
57 Int(i32),
59 Uint(u32),
61 Fixed(i32),
63 Str(Box<CString>),
68 Object(u32),
70 NewId(u32),
72 Array(Box<Vec<u8>>),
77 Fd(RawFd),
79}
80
81impl Argument {
82 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#[derive(Debug, Clone, PartialEq)]
114pub struct Message {
115 pub sender_id: u32,
117 pub opcode: u16,
119 pub args: SmallVec<[Argument; INLINE_ARGS]>,
121}
122
123#[derive(Debug, Clone)]
125pub enum MessageWriteError {
126 BufferTooSmall,
128 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#[derive(Debug, Clone)]
149pub enum MessageParseError {
150 MissingFD,
152 MissingData,
154 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 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 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 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 if payload.len() < 1 + word_len {
208 return Err(MessageWriteError::BufferTooSmall);
209 }
210 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 let mut pending_fds = FdStore::new();
229
230 for arg in &self.args {
232 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 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 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 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 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
358pub 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 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 Ok(newfd)
376 }
377 Err(e) => {
378 let _ = ::nix::unistd::close(newfd);
380 Err(e)
381 }
382 }
383 }
384 Err(e) => Err(e),
385 }
386}
387
388struct 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 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 msg.write_to_buffers(&mut bytes_buffer[..], &mut fd_buffer[..]).unwrap();
443 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}